From 82f5d551aada69720617b8edc61b507a64b65f77 Mon Sep 17 00:00:00 2001 From: Lars Asplund Date: Thu, 30 May 2019 22:00:45 +0200 Subject: [PATCH] Fixed line endings --- .gitattributes | 29 + docs/release_notes/0.52.0.rst | 2 +- docs/release_notes/0.53.0.rst | 6 +- docs/release_notes/0.54.0.rst | 4 +- docs/release_notes/0.56.0.rst | 2 +- docs/release_notes/0.57.0.rst | 4 +- docs/release_notes/0.58.0.rst | 4 +- docs/release_notes/0.59.0.rst | 6 +- docs/release_notes/0.60.0.rst | 6 +- docs/release_notes/0.60.1.rst | 2 +- docs/release_notes/0.61.0.rst | 6 +- docs/release_notes/0.62.0.rst | 4 +- docs/release_notes/0.63.0.rst | 2 +- docs/release_notes/0.64.0.rst | 4 +- docs/release_notes/0.65.0.rst | 18 +- docs/release_notes/0.66.0.rst | 4 +- docs/release_notes/0.67.0.rst | 32 +- docs/release_notes/0.68.0.rst | 2 +- docs/release_notes/0.68.1.rst | 2 +- docs/release_notes/0.69.0.rst | 2 +- docs/release_notes/0.70.0.rst | 6 +- docs/release_notes/0.71.0.rst | 2 +- docs/release_notes/1.0.0.rst | 12 +- docs/release_notes/1.1.1.rst | 6 +- docs/release_notes/1.2.0.rst | 2 +- docs/release_notes/1.3.0.rst | 4 +- docs/release_notes/1.3.1.rst | 2 +- docs/release_notes/1.4.0.rst | 8 +- docs/release_notes/2.0.0.rst | 78 +- examples/verilog/uart/run.py | 40 +- examples/verilog/uart/src/test/tb_uart_rx.sv | 180 +- examples/verilog/uart/src/test/tb_uart_tx.sv | 174 +- examples/verilog/uart/src/uart_rx.sv | 148 +- examples/verilog/uart/src/uart_tx.sv | 120 +- examples/verilog/user_guide/run.py | 30 +- examples/verilog/user_guide/tb_example.sv | 152 +- .../verilog/user_guide/tb_example_basic.sv | 36 +- examples/verilog/verilog_ams/run.py | 32 +- examples/verilog/verilog_ams/tb_dut.sv | 54 +- examples/vhdl/array/run.py | 36 +- examples/vhdl/array/src/sobel_x.vhd | 136 +- examples/vhdl/array/src/test/tb_sobel_x.vhd | 336 +- examples/vhdl/array_axis_vcs/run.py | 48 +- .../vhdl/array_axis_vcs/src/axis_buffer.vhd | 172 +- examples/vhdl/array_axis_vcs/src/fifo.vhd | 208 +- .../array_axis_vcs/src/test/tb_axis_loop.vhd | 372 +- examples/vhdl/axi_dma/run.py | 40 +- examples/vhdl/axi_dma/src/axi_burst_gen.vhd | 204 +- examples/vhdl/axi_dma/src/axi_dma.vhd | 632 +- examples/vhdl/axi_dma/src/axi_dma_regs.vhd | 246 +- .../vhdl/axi_dma/src/axi_dma_regs_pkg.vhd | 52 +- examples/vhdl/axi_dma/src/axi_pkg.vhd | 232 +- examples/vhdl/axi_dma/src/axil_pkg.vhd | 182 +- examples/vhdl/axi_dma/src/test/tb_axi_dma.vhd | 674 +- .../vhdl/axi_dma/src/test/tb_axi_dma_regs.vhd | 340 +- .../vhdl/axi_dma/src/test/tb_util_pkg.vhd | 98 +- examples/vhdl/axi_dma/src/util_pkg.vhd | 66 +- examples/vhdl/check/run.py | 46 +- examples/vhdl/check/tb_example.vhd | 698 +- examples/vhdl/com/run.py | 42 +- examples/vhdl/com/src/adder.vhd | 60 +- examples/vhdl/com/test/memory_bfm.vhd | 138 +- examples/vhdl/com/test/memory_bfm_pkg.vhd | 224 +- examples/vhdl/com/test/tb_user_guide.vhd | 642 +- examples/vhdl/composite_generics/run.py | 58 +- .../test/tb_composite_generics.vhd | 146 +- examples/vhdl/coverage/run.py | 50 +- examples/vhdl/coverage/tb_coverage.vhd | 70 +- examples/vhdl/generate_tests/run.py | 150 +- .../vhdl/generate_tests/test/tb_generated.vhd | 98 +- examples/vhdl/json4vhdl/run.py | 46 +- .../vhdl/json4vhdl/src/test/tb_json_gens.vhd | 166 +- examples/vhdl/logging/run.py | 28 +- examples/vhdl/logging/tb_logging_example.vhd | 224 +- examples/vhdl/run/run.py | 34 +- examples/vhdl/run/tb_counting_errors.vhd | 60 +- examples/vhdl/run/tb_magic_paths.vhd | 46 +- examples/vhdl/run/tb_many_ways_to_fail.vhd | 66 +- examples/vhdl/run/tb_minimal.vhd | 44 +- examples/vhdl/run/tb_running_test_case.vhd | 94 +- examples/vhdl/run/tb_standalone.vhd | 64 +- .../vhdl/run/tb_with_lower_level_control.vhd | 44 +- examples/vhdl/run/tb_with_test_cases.vhd | 80 +- examples/vhdl/run/tb_with_watchdog.vhd | 74 +- examples/vhdl/run/test_control.vhd | 62 +- examples/vhdl/third_party_integration/run.py | 30 +- .../tb_external_framework_integration.vhd | 150 +- examples/vhdl/uart/run.py | 44 +- examples/vhdl/uart/src/test/tb_uart_rx.vhd | 228 +- examples/vhdl/uart/src/test/tb_uart_tx.vhd | 208 +- examples/vhdl/uart/src/uart_rx.vhd | 210 +- examples/vhdl/uart/src/uart_tx.vhd | 180 +- examples/vhdl/user_guide/run.py | 30 +- examples/vhdl/user_guide/tb_example.vhd | 44 +- examples/vhdl/user_guide/tb_example_many.vhd | 66 +- .../vhdl/vivado/generate_vivado_project.py | 46 +- examples/vhdl/vivado/run.py | 52 +- examples/vhdl/vivado/src/test/tb_top.vhd | 174 +- examples/vhdl/vivado/src/top.vhd | 102 +- examples/vhdl/vivado/vivado_util.py | 168 +- setup.py | 172 +- tools/build_docs.py | 56 +- tools/create_release_notes.py | 216 +- tools/incisive_vhdl_fixup.py | 180 +- tools/is_new_release.py | 156 +- vunit/__init__.py | 44 +- vunit/about.py | 148 +- vunit/activehdl_interface.py | 834 +- vunit/builtins.py | 470 +- vunit/cached.py | 182 +- vunit/cds_file.py | 112 +- vunit/check_preprocessor.py | 430 +- vunit/color_printer.py | 404 +- vunit/com/__init__.py | 10 +- vunit/com/codec_datatype_template.py | 94 +- vunit/com/codec_generator.py | 154 +- vunit/com/codec_vhdl_array_type.py | 966 +- vunit/com/codec_vhdl_enumeration_type.py | 198 +- vunit/com/codec_vhdl_package.py | 628 +- vunit/com/codec_vhdl_record_type.py | 220 +- vunit/configuration.py | 540 +- vunit/csv_logs.py | 90 +- vunit/database.py | 286 +- vunit/dependency_graph.py | 252 +- vunit/design_unit.py | 200 +- vunit/exceptions.py | 30 +- vunit/ghdl_interface.py | 562 +- vunit/hashing.py | 36 +- vunit/incisive_interface.py | 730 +- vunit/json4vhdl.py | 82 +- vunit/location_preprocessor.py | 234 +- vunit/modelsim_interface.py | 818 +- vunit/ostools.py | 720 +- vunit/parsing/__init__.py | 10 +- vunit/parsing/encodings.py | 26 +- vunit/parsing/tokenizer.py | 546 +- vunit/parsing/verilog/__init__.py | 10 +- vunit/parsing/verilog/parser.py | 678 +- vunit/parsing/verilog/preprocess.py | 1046 +- vunit/parsing/verilog/tokenizer.py | 288 +- vunit/parsing/verilog/tokens.py | 614 +- vunit/persistent_tcl_shell.py | 264 +- vunit/project.py | 1974 ++-- vunit/rivierapro_interface.py | 778 +- vunit/simulator_factory.py | 308 +- vunit/simulator_interface.py | 780 +- vunit/tcl_read_eval_loop.tcl | 24 +- vunit/test/__init__.py | 10 +- vunit/test/acceptance/__init__.py | 10 +- .../artificial/verilog/other_file_tests.sv | 46 +- .../test/acceptance/artificial/verilog/run.py | 110 +- .../tb_fail_on_fatal_and_early_finish.sv | 56 +- .../artificial/verilog/tb_fail_on_warning.sv | 34 +- .../artificial/verilog/tb_magic_paths.sv | 58 +- .../artificial/verilog/tb_other_file_tests.sv | 20 +- .../verilog/tb_same_sim_all_pass.sv | 86 +- .../verilog/tb_same_sim_some_fail.sv | 60 +- .../artificial/verilog/tb_with_define.sv | 42 +- .../verilog/tb_with_parameter_config.sv | 84 +- .../artificial/verilog/tb_with_runner.sv | 46 +- .../artificial/vhdl/bool_driver.vhd | 38 +- .../artificial/vhdl/other_file_tests.vhd | 62 +- vunit/test/acceptance/artificial/vhdl/run.py | 168 +- .../artificial/vhdl/tb_assert_stop_level.vhd | 90 +- .../artificial/vhdl/tb_elab_fail.vhd | 46 +- .../acceptance/artificial/vhdl/tb_fail.vhd | 46 +- .../artificial/vhdl/tb_fail_on_warning.vhd | 50 +- .../artificial/vhdl/tb_ieee_warning.vhd | 78 +- .../artificial/vhdl/tb_infinite_events.vhd | 60 +- .../artificial/vhdl/tb_magic_paths.vhd | 62 +- .../vhdl/tb_no_fail_after_cleanup.vhd | 46 +- .../artificial/vhdl/tb_no_fail_on_warning.vhd | 46 +- .../vhdl/tb_no_generic_override.vhd | 62 +- .../artificial/vhdl/tb_other_file_tests.vhd | 40 +- .../acceptance/artificial/vhdl/tb_pass.vhd | 44 +- .../artificial/vhdl/tb_same_sim_all_pass.vhd | 106 +- .../artificial/vhdl/tb_same_sim_some_fail.vhd | 80 +- .../artificial/vhdl/tb_set_generic.vhd | 90 +- .../artificial/vhdl/tb_with_checks.vhd | 78 +- .../vhdl/tb_with_generic_config.vhd | 114 +- .../artificial/vhdl/tb_with_vhdl_runner.vhd | 82 +- vunit/test/acceptance/dependencies/pkg.vhd | 18 +- .../acceptance/dependencies/pkg_body1.vhd | 24 +- .../acceptance/dependencies/pkg_body2.vhd | 24 +- vunit/test/acceptance/dependencies/tb_pkg.vhd | 54 +- vunit/test/acceptance/test_artificial.py | 458 +- vunit/test/acceptance/test_dependencies.py | 120 +- .../acceptance/test_external_run_scripts.py | 478 +- vunit/test/common.py | 462 +- vunit/test/lint/__init__.py | 10 +- vunit/test/lint/test_license.py | 298 +- vunit/test/lint/test_pycodestyle.py | 82 +- vunit/test/lint/test_pylint.py | 54 +- vunit/test/lint/test_readme.py | 54 +- vunit/test/mock_2or3.py | 34 +- vunit/test/unit/__init__.py | 10 +- vunit/test/unit/non_utf8_printer.py | 34 +- vunit/test/unit/test_activehdl_interface.py | 466 +- vunit/test/unit/test_builtins.py | 116 +- vunit/test/unit/test_cds_file.py | 220 +- vunit/test/unit/test_check_preprocessor.py | 382 +- vunit/test/unit/test_configuration.py | 486 +- vunit/test/unit/test_csv_logs.py | 282 +- vunit/test/unit/test_database.py | 208 +- vunit/test/unit/test_dependency_graph.py | 308 +- vunit/test/unit/test_ghdl_interface.py | 406 +- vunit/test/unit/test_incisive_interface.py | 1688 +-- vunit/test/unit/test_location_preprocessor.py | 392 +- vunit/test/unit/test_modelsim_interface.py | 572 +- vunit/test/unit/test_ostools.py | 190 +- vunit/test/unit/test_project.py | 3144 +++--- vunit/test/unit/test_rivierapro_interface.py | 430 +- vunit/test/unit/test_simulator_interface.py | 560 +- vunit/test/unit/test_test_bench.py | 1694 +-- vunit/test/unit/test_test_bench_list.py | 198 +- vunit/test/unit/test_test_report.py | 616 +- vunit/test/unit/test_test_runner.py | 442 +- vunit/test/unit/test_test_suites.py | 308 +- vunit/test/unit/test_tokenizer.py | 248 +- vunit/test/unit/test_ui.py | 2236 ++-- vunit/test/unit/test_verilog_parser.py | 714 +- vunit/test/unit/test_verilog_preprocessor.py | 1762 +-- vunit/test/unit/test_verilog_tokenizer.py | 312 +- vunit/test/unit/test_vhdl_parser.py | 1040 +- vunit/test_bench.py | 1254 +-- vunit/test_bench_list.py | 242 +- vunit/test_list.py | 204 +- vunit/test_report.py | 636 +- vunit/test_runner.py | 922 +- vunit/test_suites.py | 628 +- vunit/ui.py | 4038 +++---- vunit/verilog.py | 46 +- vunit/verilog/vunit_pkg.sv | 468 +- vunit/version_check.py | 62 +- vunit/vhdl/array/run.py | 34 +- vunit/vhdl/array/src/array_pkg.vhd | 504 +- vunit/vhdl/array/test/tb_array.vhd | 766 +- vunit/vhdl/check/run.py | 82 +- vunit/vhdl/check/src/check.vhd | 9576 ++++++++--------- vunit/vhdl/check/src/check_api.vhd | 4356 ++++---- vunit/vhdl/check/src/check_deprecated_pkg.vhd | 408 +- vunit/vhdl/check/src/checker_pkg-body.vhd | 322 +- vunit/vhdl/check/src/checker_pkg.vhd | 136 +- vunit/vhdl/check/test/tb_check.vhd | 630 +- vunit/vhdl/check/test/tb_check_equal_real.vhd | 140 +- vunit/vhdl/check/test/tb_check_failed.vhd | 154 +- vunit/vhdl/check/test/tb_check_false.vhd | 450 +- .../vhdl/check/test/tb_check_implication.vhd | 488 +- vunit/vhdl/check/test/tb_check_next.vhd | 614 +- .../vhdl/check/test/tb_check_not_unknown.vhd | 564 +- vunit/vhdl/check/test/tb_check_one_hot.vhd | 648 +- vunit/vhdl/check/test/tb_check_passed.vhd | 140 +- vunit/vhdl/check/test/tb_check_relation.vhd | 430 +- .../check/test/tb_check_relation_2008.vhd | 154 +- vunit/vhdl/check/test/tb_check_sequence.vhd | 562 +- vunit/vhdl/check/test/tb_check_stable.vhd | 1318 +-- .../vhdl/check/test/tb_check_zero_one_hot.vhd | 596 +- vunit/vhdl/check/test/tb_checker.vhd | 216 +- vunit/vhdl/check/test/tb_deprecated.vhd | 348 +- vunit/vhdl/check/test/tb_result.vhd | 120 +- vunit/vhdl/check/test/test_support.vhd | 402 +- .../vhdl/check/tools/generate_check_equal.py | 1254 +-- .../vhdl/check/tools/generate_check_match.py | 820 +- vunit/vhdl/com/run.py | 38 +- vunit/vhdl/com/src/com.vhd | 1398 +-- vunit/vhdl/com/src/com_api.vhd | 562 +- vunit/vhdl/com/src/com_common.vhd | 76 +- vunit/vhdl/com/src/com_context.vhd | 38 +- .../vhdl/com/src/com_debug_codec_builder.vhd | 588 +- vunit/vhdl/com/src/com_deprecated.vhd | 1834 ++-- vunit/vhdl/com/src/com_messenger.vhd | 2424 ++--- vunit/vhdl/com/src/com_string.vhd | 614 +- vunit/vhdl/com/src/com_support.vhd | 142 +- vunit/vhdl/com/src/com_types.vhd | 1714 +-- vunit/vhdl/com/test/constants.vhd | 22 +- vunit/vhdl/com/test/custom_types.vhd | 224 +- vunit/vhdl/com/test/more_constants.vhd | 22 +- vunit/vhdl/com/test/tb_com.vhd | 2940 ++--- vunit/vhdl/com/test/tb_com_codec.vhd | 490 +- vunit/vhdl/com/test/tb_com_deprecated.vhd | 908 +- vunit/vhdl/com/test/tb_com_msg_building.vhd | 602 +- vunit/vhdl/compile_vunit_lib.py | 20 +- vunit/vhdl/core/src/core_pkg.vhd | 266 +- vunit/vhdl/core/src/stop_body_2008.vhd | 30 +- vunit/vhdl/core/src/stop_body_93-2002.vhd | 24 +- vunit/vhdl/core/src/stop_pkg.vhd | 18 +- vunit/vhdl/data_types/run.py | 44 +- vunit/vhdl/data_types/src/codec-2008.vhd | 506 +- vunit/vhdl/data_types/src/codec.vhd | 1356 +-- .../data_types/src/codec_builder-2008.vhd | 256 +- vunit/vhdl/data_types/src/codec_builder.vhd | 868 +- .../data_types/src/data_types_context.vhd | 36 +- vunit/vhdl/data_types/src/dict_pkg.vhd | 666 +- .../data_types/src/integer_array_pkg-body.vhd | 912 +- .../vhdl/data_types/src/integer_array_pkg.vhd | 362 +- .../src/integer_vector_ptr_pkg-body-200x.vhd | 450 +- .../src/integer_vector_ptr_pkg-body-93.vhd | 258 +- .../data_types/src/integer_vector_ptr_pkg.vhd | 192 +- .../src/integer_vector_ptr_pool_pkg.vhd | 138 +- vunit/vhdl/data_types/src/queue_pkg-2008.vhd | 412 +- vunit/vhdl/data_types/src/queue_pkg-body.vhd | 1208 +-- vunit/vhdl/data_types/src/queue_pkg.vhd | 786 +- vunit/vhdl/data_types/src/queue_pool_pkg.vhd | 124 +- .../src/string_ptr_pkg-body-200x.vhd | 538 +- .../data_types/src/string_ptr_pkg-body-93.vhd | 316 +- vunit/vhdl/data_types/src/string_ptr_pkg.vhd | 208 +- .../data_types/src/string_ptr_pool_pkg.vhd | 180 +- vunit/vhdl/data_types/test/tb_codec-2008.vhd | 268 +- vunit/vhdl/data_types/test/tb_codec.vhd | 516 +- vunit/vhdl/data_types/test/tb_dict.vhd | 208 +- .../vhdl/data_types/test/tb_integer_array.vhd | 732 +- .../data_types/test/tb_integer_vector_ptr.vhd | 228 +- .../test/tb_integer_vector_ptr_pool.vhd | 112 +- vunit/vhdl/data_types/test/tb_queue-2008.vhd | 168 +- vunit/vhdl/data_types/test/tb_queue.vhd | 676 +- vunit/vhdl/data_types/test/tb_queue_pool.vhd | 94 +- vunit/vhdl/data_types/test/tb_string_ptr.vhd | 262 +- .../data_types/test/tb_string_ptr_pool.vhd | 132 +- vunit/vhdl/dictionary/run.py | 30 +- vunit/vhdl/dictionary/src/dictionary.vhd | 264 +- vunit/vhdl/dictionary/test/tb_dictionary.vhd | 172 +- vunit/vhdl/logging/run.py | 28 +- vunit/vhdl/logging/src/ansi_pkg.vhd | 466 +- vunit/vhdl/logging/src/file_pkg.vhd | 528 +- vunit/vhdl/logging/src/log_deprecated_pkg.vhd | 416 +- .../vhdl/logging/src/log_handler_pkg-body.vhd | 662 +- vunit/vhdl/logging/src/log_handler_pkg.vhd | 164 +- .../vhdl/logging/src/log_levels_pkg-body.vhd | 262 +- vunit/vhdl/logging/src/log_levels_pkg.vhd | 100 +- vunit/vhdl/logging/src/logger_pkg-body.vhd | 2502 ++--- vunit/vhdl/logging/src/logger_pkg.vhd | 774 +- vunit/vhdl/logging/src/print_pkg-body.vhd | 88 +- vunit/vhdl/logging/src/print_pkg.vhd | 44 +- vunit/vhdl/logging/test/tb_deprecated.vhd | 366 +- vunit/vhdl/logging/test/tb_log.vhd | 1796 ++-- vunit/vhdl/logging/test/tb_log_levels.vhd | 160 +- vunit/vhdl/logging/test/test_support_pkg.vhd | 224 +- vunit/vhdl/path/run.py | 28 +- vunit/vhdl/path/src/path.vhd | 128 +- vunit/vhdl/path/test/tb_path.vhd | 82 +- vunit/vhdl/random/run.py | 32 +- vunit/vhdl/random/src/random_pkg.vhd | 398 +- vunit/vhdl/random/test/tb_random_pkg.vhd | 204 +- vunit/vhdl/run/run.py | 30 +- vunit/vhdl/run/src/run.vhd | 974 +- vunit/vhdl/run/src/run_api.vhd | 326 +- vunit/vhdl/run/src/run_deprecated_pkg.vhd | 66 +- vunit/vhdl/run/src/run_types.vhd | 106 +- vunit/vhdl/run/src/runner_pkg.vhd | 1096 +- vunit/vhdl/run/test/run_tests.vhd | 1762 +-- vunit/vhdl/run/test/tb_run.vhd | 46 +- vunit/vhdl/run/test/tb_watchdog.vhd | 130 +- vunit/vhdl/string_ops/run.py | 34 +- vunit/vhdl/string_ops/src/string_ops.vhd | 1416 +-- vunit/vhdl/string_ops/test/tb_string_ops.vhd | 746 +- vunit/vhdl/verification_components/run.py | 210 +- .../src/avalon_master.vhd | 370 +- .../src/avalon_pkg.vhd | 112 +- .../src/avalon_sink.vhd | 170 +- .../src/avalon_slave.vhd | 232 +- .../src/avalon_source.vhd | 158 +- .../src/avalon_stream_pkg.vhd | 450 +- .../src/axi_lite_master.vhd | 308 +- .../src/axi_lite_master_pkg.vhd | 328 +- .../verification_components/src/axi_pkg.vhd | 50 +- .../src/axi_read_slave.vhd | 332 +- .../src/axi_slave_pkg.vhd | 596 +- .../src/axi_slave_private_pkg.vhd | 1160 +- .../src/axi_statistics_pkg.vhd | 256 +- .../src/axi_stream_master.vhd | 346 +- .../src/axi_stream_monitor.vhd | 166 +- .../src/axi_stream_pkg.vhd | 1550 +-- .../src/axi_stream_protocol_checker.vhd | 526 +- .../src/axi_stream_slave.vhd | 362 +- .../src/axi_write_slave.vhd | 528 +- .../src/bus2memory.vhd | 122 +- .../src/bus_master_pkg-body.vhd | 682 +- .../src/bus_master_pkg.vhd | 378 +- .../src/memory_pkg-body.vhd | 982 +- .../src/memory_pkg.vhd | 390 +- .../src/memory_utils_pkg.vhd | 534 +- .../src/ram_master.vhd | 180 +- .../src/signal_checker_pkg.vhd | 150 +- .../src/std_logic_checker.vhd | 188 +- .../src/stream_master_pkg-body.vhd | 62 +- .../src/stream_master_pkg.vhd | 64 +- .../src/stream_slave_pkg-body.vhd | 152 +- .../src/stream_slave_pkg.vhd | 122 +- .../src/sync_pkg-body.vhd | 126 +- .../verification_components/src/sync_pkg.vhd | 90 +- .../src/uart_master.vhd | 134 +- .../verification_components/src/uart_pkg.vhd | 238 +- .../src/uart_slave.vhd | 170 +- .../src/vc_context.vhd | 48 +- .../src/wishbone_master.vhd | 314 +- .../src/wishbone_pkg.vhd | 108 +- .../src/wishbone_slave.vhd | 232 +- .../test/tb_avalon_master.vhd | 658 +- .../test/tb_avalon_slave.vhd | 348 +- .../test/tb_avalon_stream.vhd | 262 +- .../test/tb_avalon_stream_pkg.vhd | 174 +- .../test/tb_axi_lite_master.vhd | 796 +- .../test/tb_axi_read_slave.vhd | 836 +- .../test/tb_axi_slave_private_pkg.vhd | 504 +- .../test/tb_axi_statistics_pkg.vhd | 174 +- .../test/tb_axi_stream.vhd | 1002 +- .../test/tb_axi_stream_protocol_checker.vhd | 1238 +-- .../test/tb_axi_write_slave.vhd | 1278 +-- .../test/tb_bus_master_pkg.vhd | 210 +- .../test/tb_memory.vhd | 754 +- .../test/tb_memory_utils_pkg.vhd | 352 +- .../test/tb_ram_master.vhd | 316 +- .../test/tb_std_logic_checker.vhd | 276 +- .../test/tb_sync_pkg.vhd | 96 +- .../verification_components/test/tb_uart.vhd | 208 +- .../test/tb_wishbone_master.vhd | 408 +- .../test/tb_wishbone_slave.vhd | 302 +- vunit/vhdl/vunit_context.vhd | 52 +- vunit/vhdl/vunit_run_context.vhd | 30 +- vunit/vhdl_parser.py | 2014 ++-- vunit/vivado/__init__.py | 26 +- vunit/vivado/vivado.py | 242 +- vunit/vsim_simulator_mixin.py | 684 +- vunit/vunit_cli.py | 424 +- 424 files changed, 79464 insertions(+), 79435 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..99ff308d7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,29 @@ +* text=auto + +*.css text +*.csv text +*.do text +*.gitattributes text +*.gitignore text +*.gitmodules text +*.gtkw text +*.html text +*.ini text +*.json text +*.md text +*.py text +*.rst text +*.sh text +*.sv text +*.svg text +*.svh text +*.tcl text +*.txt text +*.vams text +*.vhd text +*.yml text + +*.gif binary +*.ico binary +*.jpeg binary +*.png binary diff --git a/docs/release_notes/0.52.0.rst b/docs/release_notes/0.52.0.rst index f1de20d0c..d1be33a17 100644 --- a/docs/release_notes/0.52.0.rst +++ b/docs/release_notes/0.52.0.rst @@ -1 +1 @@ -Added function to get the number of messages missed by a com package actor. +Added function to get the number of messages missed by a com package actor. diff --git a/docs/release_notes/0.53.0.rst b/docs/release_notes/0.53.0.rst index 9ff3e80e2..c7a1e9110 100644 --- a/docs/release_notes/0.53.0.rst +++ b/docs/release_notes/0.53.0.rst @@ -1,3 +1,3 @@ -- ``add_source_files`` accepts a list of files -- Added ``-f/--files`` command line flag to list all files in compile order -- Verilog parser improvements in robustness and error messages. +- ``add_source_files`` accepts a list of files +- Added ``-f/--files`` command line flag to list all files in compile order +- Verilog parser improvements in robustness and error messages. diff --git a/docs/release_notes/0.54.0.rst b/docs/release_notes/0.54.0.rst index 1eed7890c..8a1397dc6 100644 --- a/docs/release_notes/0.54.0.rst +++ b/docs/release_notes/0.54.0.rst @@ -1,2 +1,2 @@ -- Adds support for Verilog preprocessor ifdef/ifndef/elsif/else/endif -- Fixes regression in modelsim persistent mode. Makes many short tests faster. +- Adds support for Verilog preprocessor ifdef/ifndef/elsif/else/endif +- Fixes regression in modelsim persistent mode. Makes many short tests faster. diff --git a/docs/release_notes/0.56.0.rst b/docs/release_notes/0.56.0.rst index 7c6838f5c..03253747c 100644 --- a/docs/release_notes/0.56.0.rst +++ b/docs/release_notes/0.56.0.rst @@ -1 +1 @@ -- Verilog preprocessing of resetall / undefineall / undef +- Verilog preprocessing of resetall / undefineall / undef diff --git a/docs/release_notes/0.57.0.rst b/docs/release_notes/0.57.0.rst index 711fed8a7..22cc3ac40 100644 --- a/docs/release_notes/0.57.0.rst +++ b/docs/release_notes/0.57.0.rst @@ -1,2 +1,2 @@ -- Adds ``include_dirs`` argument also to ``Library`` add_source_file(s) -- Ignores more builtin Verilog preprocessor directives. +- Adds ``include_dirs`` argument also to ``Library`` add_source_file(s) +- Ignores more builtin Verilog preprocessor directives. diff --git a/docs/release_notes/0.58.0.rst b/docs/release_notes/0.58.0.rst index a1d1e7186..be491c905 100644 --- a/docs/release_notes/0.58.0.rst +++ b/docs/release_notes/0.58.0.rst @@ -1,2 +1,2 @@ -- Parsing Verilog package references. :vunit_issue:`119` -- Added ``scan_tests_from_file`` public method. :vunit_issue:`121`. +- Parsing Verilog package references. :vunit_issue:`119` +- Added ``scan_tests_from_file`` public method. :vunit_issue:`121`. diff --git a/docs/release_notes/0.59.0.rst b/docs/release_notes/0.59.0.rst index dc6bf3377..8a77799dc 100644 --- a/docs/release_notes/0.59.0.rst +++ b/docs/release_notes/0.59.0.rst @@ -1,3 +1,3 @@ -- Covered a miss in circular dependency detection. -- Added detection of circular includes and macro expansions in verilog preprocessing. -- Added caching of verilog parse results for significant speed when running run.py more than once. +- Covered a miss in circular dependency detection. +- Added detection of circular includes and macro expansions in verilog preprocessing. +- Added caching of verilog parse results for significant speed when running run.py more than once. diff --git a/docs/release_notes/0.60.0.rst b/docs/release_notes/0.60.0.rst index b3664a649..a4d88d8ed 100644 --- a/docs/release_notes/0.60.0.rst +++ b/docs/release_notes/0.60.0.rst @@ -1,3 +1,3 @@ -- Better error messages when there are circular dependencies. -- Added ``defines`` argument to add_source_file(s) :vunit_issue:`126` -- Made ``--files`` deterministic with Python 3 :vunit_issue:`116` +- Better error messages when there are circular dependencies. +- Added ``defines`` argument to add_source_file(s) :vunit_issue:`126` +- Made ``--files`` deterministic with Python 3 :vunit_issue:`116` diff --git a/docs/release_notes/0.60.1.rst b/docs/release_notes/0.60.1.rst index 31e7715af..fdfec95f3 100644 --- a/docs/release_notes/0.60.1.rst +++ b/docs/release_notes/0.60.1.rst @@ -1 +1 @@ -- Avoids crash with errors in Verilog defines from Python string in run.py +- Avoids crash with errors in Verilog defines from Python string in run.py diff --git a/docs/release_notes/0.61.0.rst b/docs/release_notes/0.61.0.rst index f211a3e3c..1f36b5807 100644 --- a/docs/release_notes/0.61.0.rst +++ b/docs/release_notes/0.61.0.rst @@ -1,3 +1,3 @@ -- Adds ``.all`` suffix to test benches with no test to better align with XUnit architecture. - - Enables better hierarchical JUnit XML report view in Jenkins. -- Fixes :vunit_issue:`129`. +- Adds ``.all`` suffix to test benches with no test to better align with XUnit architecture. + - Enables better hierarchical JUnit XML report view in Jenkins. +- Fixes :vunit_issue:`129`. diff --git a/docs/release_notes/0.62.0.rst b/docs/release_notes/0.62.0.rst index cd240680a..33877f363 100644 --- a/docs/release_notes/0.62.0.rst +++ b/docs/release_notes/0.62.0.rst @@ -1,2 +1,2 @@ -- Early runtime error when gtkwave is missing. Closes :vunit_issue:`137` -- Added add_compile_option. Closes :vunit_issue:`118` +- Early runtime error when gtkwave is missing. Closes :vunit_issue:`137` +- Added add_compile_option. Closes :vunit_issue:`118` diff --git a/docs/release_notes/0.63.0.rst b/docs/release_notes/0.63.0.rst index dd3b37b72..27f3b7548 100644 --- a/docs/release_notes/0.63.0.rst +++ b/docs/release_notes/0.63.0.rst @@ -1 +1 @@ -- Update test scanner pattern to be based on ``runner_cfg``. :vunit_issue:`138` +- Update test scanner pattern to be based on ``runner_cfg``. :vunit_issue:`138` diff --git a/docs/release_notes/0.64.0.rst b/docs/release_notes/0.64.0.rst index 7de3e7e9a..7b07ce578 100644 --- a/docs/release_notes/0.64.0.rst +++ b/docs/release_notes/0.64.0.rst @@ -1,2 +1,2 @@ -- Added python version check. Closes :vunit_issue:`141`. -- Not adding .all suffix when there are named configurations +- Added python version check. Closes :vunit_issue:`141`. +- Not adding .all suffix when there are named configurations diff --git a/docs/release_notes/0.65.0.rst b/docs/release_notes/0.65.0.rst index 2852dd96c..f0fbac16c 100644 --- a/docs/release_notes/0.65.0.rst +++ b/docs/release_notes/0.65.0.rst @@ -1,9 +1,9 @@ -- Added sim and compile options to set rivierapro/activehdl flags. :vunit_issue:`143`. -- Removed builtin ``-dbg`` flag to vcom for aldec tools. Use set_compile_option instead to set it yourself. -- Fixed a bug with custom relative output_path. -- Documentation fixes & improvements. -- Update rivierapro and activehdl toolchain discovery. :vunit_issue:`148`. -- Added possibility to set ``VUNIT__PATH`` environment - variable to specify simulation executable path. :vunit_issue:`148`. -- Added ``-k/--keep-compiling`` flag. :vunit_issue:`140`. -- Added optional ``output_path`` argument to ``pre_config``. :vunit_issue:`146`. +- Added sim and compile options to set rivierapro/activehdl flags. :vunit_issue:`143`. +- Removed builtin ``-dbg`` flag to vcom for aldec tools. Use set_compile_option instead to set it yourself. +- Fixed a bug with custom relative output_path. +- Documentation fixes & improvements. +- Update rivierapro and activehdl toolchain discovery. :vunit_issue:`148`. +- Added possibility to set ``VUNIT__PATH`` environment + variable to specify simulation executable path. :vunit_issue:`148`. +- Added ``-k/--keep-compiling`` flag. :vunit_issue:`140`. +- Added optional ``output_path`` argument to ``pre_config``. :vunit_issue:`146`. diff --git a/docs/release_notes/0.66.0.rst b/docs/release_notes/0.66.0.rst index 1fc11a5e8..4c550ca87 100644 --- a/docs/release_notes/0.66.0.rst +++ b/docs/release_notes/0.66.0.rst @@ -1,2 +1,2 @@ -- Fixed :vunit_issue:`109`, :vunit_issue:`141`, :vunit_issue:`153`, :vunit_issue:`155`. -- Fixed relative path for multiple drives on windows. +- Fixed :vunit_issue:`109`, :vunit_issue:`141`, :vunit_issue:`153`, :vunit_issue:`155`. +- Fixed relative path for multiple drives on windows. diff --git a/docs/release_notes/0.67.0.rst b/docs/release_notes/0.67.0.rst index 364f704fe..a389c7d2c 100644 --- a/docs/release_notes/0.67.0.rst +++ b/docs/release_notes/0.67.0.rst @@ -1,16 +1,16 @@ -- A number of minor enhancements and bug fixes -- Added vunit_restart TCL procedure to ModelSim -- Print out remaining number of tests when pressing ctrl-c -- Updated OSVVM and made it a git submodule. Run - -.. code-block:: console - - git submodule update --init --recursive - -after updating an existing Git repository or - -.. code-block:: console - - git clone --recursive https://github.com/VUnit/vunit.git - -when creating a new clone to get the OSVVM subdirectory of VUnit populated. Doesn't affect installations made from PyPi +- A number of minor enhancements and bug fixes +- Added vunit_restart TCL procedure to ModelSim +- Print out remaining number of tests when pressing ctrl-c +- Updated OSVVM and made it a git submodule. Run + +.. code-block:: console + + git submodule update --init --recursive + +after updating an existing Git repository or + +.. code-block:: console + + git clone --recursive https://github.com/VUnit/vunit.git + +when creating a new clone to get the OSVVM subdirectory of VUnit populated. Doesn't affect installations made from PyPi diff --git a/docs/release_notes/0.68.0.rst b/docs/release_notes/0.68.0.rst index 4cc295532..68f67aa9d 100644 --- a/docs/release_notes/0.68.0.rst +++ b/docs/release_notes/0.68.0.rst @@ -1 +1 @@ -Added check_equal for time and updated documentation. +Added check_equal for time and updated documentation. diff --git a/docs/release_notes/0.68.1.rst b/docs/release_notes/0.68.1.rst index 572f7344b..f87bee3d1 100644 --- a/docs/release_notes/0.68.1.rst +++ b/docs/release_notes/0.68.1.rst @@ -1 +1 @@ -New version to fix broken PyPi upload +New version to fix broken PyPi upload diff --git a/docs/release_notes/0.69.0.rst b/docs/release_notes/0.69.0.rst index 02f3b77ec..41804a0c6 100644 --- a/docs/release_notes/0.69.0.rst +++ b/docs/release_notes/0.69.0.rst @@ -1 +1 @@ -Added check_equal for strings. +Added check_equal for strings. diff --git a/docs/release_notes/0.70.0.rst b/docs/release_notes/0.70.0.rst index bf1d85ecf..c7b00f30e 100644 --- a/docs/release_notes/0.70.0.rst +++ b/docs/release_notes/0.70.0.rst @@ -1,3 +1,3 @@ -- Hashing test output_path to protect against special characters and long paths on Windows. -- Added ``.vo`` as recognized Verilog file ending. -- Enable setting vhdl_standard per file. +- Hashing test output_path to protect against special characters and long paths on Windows. +- Added ``.vo`` as recognized Verilog file ending. +- Enable setting vhdl_standard per file. diff --git a/docs/release_notes/0.71.0.rst b/docs/release_notes/0.71.0.rst index 60e67363f..44df81a14 100644 --- a/docs/release_notes/0.71.0.rst +++ b/docs/release_notes/0.71.0.rst @@ -1 +1 @@ -- Improved location preprocessing control +- Improved location preprocessing control diff --git a/docs/release_notes/1.0.0.rst b/docs/release_notes/1.0.0.rst index f14a600aa..7f3ab2eff 100644 --- a/docs/release_notes/1.0.0.rst +++ b/docs/release_notes/1.0.0.rst @@ -1,6 +1,6 @@ -- Adds ActiveHDL custom simulation flags support -- Made library simulator flag argument deterministic and same as the order added to VUnit -- Added check_equal between std_logic_vector and natural for unsigned comparison -- Can now set vhdl_standard on an external library -- Added no_parse argument to add_source_files(s) to inhibit any dependency or test scanning -- Renamed public method depends_on to add_dependency_on +- Adds ActiveHDL custom simulation flags support +- Made library simulator flag argument deterministic and same as the order added to VUnit +- Added check_equal between std_logic_vector and natural for unsigned comparison +- Can now set vhdl_standard on an external library +- Added no_parse argument to add_source_files(s) to inhibit any dependency or test scanning +- Renamed public method depends_on to add_dependency_on diff --git a/docs/release_notes/1.1.1.rst b/docs/release_notes/1.1.1.rst index ec961482d..1b6ba9c1c 100644 --- a/docs/release_notes/1.1.1.rst +++ b/docs/release_notes/1.1.1.rst @@ -1,3 +1,3 @@ -- Adds vunit_restart and vunit_compile TCL commands for both ModelSim and RivieraPro -- Also support persistent simulator to save startup overhead for RivieraPro. -- Changes --new-vsim into -u/--unique-sim which also works for riviera +- Adds vunit_restart and vunit_compile TCL commands for both ModelSim and RivieraPro +- Also support persistent simulator to save startup overhead for RivieraPro. +- Changes --new-vsim into -u/--unique-sim which also works for riviera diff --git a/docs/release_notes/1.2.0.rst b/docs/release_notes/1.2.0.rst index 42548df83..158d39ee5 100644 --- a/docs/release_notes/1.2.0.rst +++ b/docs/release_notes/1.2.0.rst @@ -1 +1 @@ -- Updated OSVVM submodule +- Updated OSVVM submodule diff --git a/docs/release_notes/1.3.0.rst b/docs/release_notes/1.3.0.rst index 8c5258691..3c67d42e8 100644 --- a/docs/release_notes/1.3.0.rst +++ b/docs/release_notes/1.3.0.rst @@ -1,2 +1,2 @@ -- Added support for pass acknowledge messages for check subprograms. -- Made design unit duplication a warning instead of runtime error again. +- Added support for pass acknowledge messages for check subprograms. +- Made design unit duplication a warning instead of runtime error again. diff --git a/docs/release_notes/1.3.1.rst b/docs/release_notes/1.3.1.rst index d2796770e..013d554b3 100644 --- a/docs/release_notes/1.3.1.rst +++ b/docs/release_notes/1.3.1.rst @@ -1 +1 @@ -- Fixed compile errors with GHDL 0.33 +- Fixed compile errors with GHDL 0.33 diff --git a/docs/release_notes/1.4.0.rst b/docs/release_notes/1.4.0.rst index d3bb33f84..bacf90f47 100644 --- a/docs/release_notes/1.4.0.rst +++ b/docs/release_notes/1.4.0.rst @@ -1,4 +1,4 @@ -- Removed bug when compiling Verilog with Active-HDL -- Updated array package -- Added support for simulation init script -- Added support for setting VHDL asserts stop level from run script +- Removed bug when compiling Verilog with Active-HDL +- Updated array package +- Added support for simulation init script +- Added support for setting VHDL asserts stop level from run script diff --git a/docs/release_notes/2.0.0.rst b/docs/release_notes/2.0.0.rst index 6ca684552..62ddb2dee 100644 --- a/docs/release_notes/2.0.0.rst +++ b/docs/release_notes/2.0.0.rst @@ -1,39 +1,39 @@ - -Public interface changes -~~~~~~~~~~~~~~~~~~~~~~~~ - -Some ``run.py`` scripts can be broken by this. Both ``set_generic`` -and ``add_config`` works differently internally. - -``set_generic`` and ``set_sim_option`` now only affects files added -before the call so reordering within the ``run.py`` can be needed. - -``add_config`` on the test case level will no longer discard -configurations added on the test bench level. This affects users -mixing adding configurations on both test and test case level for the -same test bench. Adding a configuration on the test bench level is now -seen as a shorthand for adding the configuration to all test cases -within the test bench. Configurations are only held at the test case -level now. Before there could be configurations on multiple levels -where the most specific level ignored all others. I now recommend -writing a for loop over test_bench.get_tests() adding configurations -to each test individually, see the updated generate_tests example. - -We have also forbidden to have configurations without name (""), this -is since the default configuration of all test cases has no name. The -``post_check`` and ``pre_config`` can now be set using -``set_pre_config`` also without using ``add_config`` removing the need -to add a single unnamed configuration and instead setting these in the -default configuration. - -This internal restructuring has been made to allow a sane data model -of configurations where they are attached to test cases. This allows -us to expose configurations objects on the public API in the future -allowing users more control and visibility. The current behavior of -configurations is also better documented than it ever was. - -I suggest reading the section on :ref:`configurations ` in the docs. - -- Replace ``disable_ieee_warnings`` and ``set_pli`` with corresponding simulation options. -- Adds ``--version`` flag -- Added ``--gui`` flag for GHDL to open gtkwave. Also allows saving waveform without opening gui with ``--gtkwave-fmt`` flag. + +Public interface changes +~~~~~~~~~~~~~~~~~~~~~~~~ + +Some ``run.py`` scripts can be broken by this. Both ``set_generic`` +and ``add_config`` works differently internally. + +``set_generic`` and ``set_sim_option`` now only affects files added +before the call so reordering within the ``run.py`` can be needed. + +``add_config`` on the test case level will no longer discard +configurations added on the test bench level. This affects users +mixing adding configurations on both test and test case level for the +same test bench. Adding a configuration on the test bench level is now +seen as a shorthand for adding the configuration to all test cases +within the test bench. Configurations are only held at the test case +level now. Before there could be configurations on multiple levels +where the most specific level ignored all others. I now recommend +writing a for loop over test_bench.get_tests() adding configurations +to each test individually, see the updated generate_tests example. + +We have also forbidden to have configurations without name (""), this +is since the default configuration of all test cases has no name. The +``post_check`` and ``pre_config`` can now be set using +``set_pre_config`` also without using ``add_config`` removing the need +to add a single unnamed configuration and instead setting these in the +default configuration. + +This internal restructuring has been made to allow a sane data model +of configurations where they are attached to test cases. This allows +us to expose configurations objects on the public API in the future +allowing users more control and visibility. The current behavior of +configurations is also better documented than it ever was. + +I suggest reading the section on :ref:`configurations ` in the docs. + +- Replace ``disable_ieee_warnings`` and ``set_pli`` with corresponding simulation options. +- Adds ``--version`` flag +- Added ``--gui`` flag for GHDL to open gtkwave. Also allows saving waveform without opening gui with ``--gtkwave-fmt`` flag. diff --git a/examples/verilog/uart/run.py b/examples/verilog/uart/run.py index b02f90913..c56301317 100644 --- a/examples/verilog/uart/run.py +++ b/examples/verilog/uart/run.py @@ -1,20 +1,20 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit.verilog import VUnit - -ui = VUnit.from_argv() - -src_path = join(dirname(__file__), "src") - -uart_lib = ui.add_library("uart_lib") -uart_lib.add_source_files(join(src_path, "*.sv")) - -tb_uart_lib = ui.add_library("tb_uart_lib") -tb_uart_lib.add_source_files(join(src_path, "test", "*.sv")) - -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit.verilog import VUnit + +ui = VUnit.from_argv() + +src_path = join(dirname(__file__), "src") + +uart_lib = ui.add_library("uart_lib") +uart_lib.add_source_files(join(src_path, "*.sv")) + +tb_uart_lib = ui.add_library("tb_uart_lib") +tb_uart_lib.add_source_files(join(src_path, "test", "*.sv")) + +ui.main() diff --git a/examples/verilog/uart/src/test/tb_uart_rx.sv b/examples/verilog/uart/src/test/tb_uart_rx.sv index 30d9447e1..fbc7d249a 100644 --- a/examples/verilog/uart/src/test/tb_uart_rx.sv +++ b/examples/verilog/uart/src/test/tb_uart_rx.sv @@ -1,90 +1,90 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -`include "vunit_defines.svh" - -module tb_uart_rx; - localparam integer baud_rate = 115200; // bits / s - localparam integer clk_period = 20; // ns - localparam integer cycles_per_bit = 50 * 10**6 / baud_rate; - localparam time_per_bit = (10**9 / baud_rate); - localparam time_per_half_bit = time_per_bit/2; - logic clk = 1'b0; - - // Serial input bit - logic rx = 1'b0; - - // AXI stream for input bytes - logic overflow; - logic tready = 1'b0; - logic tvalid; - logic [7:0]tdata; - integer num_overflows = 0; - - `TEST_SUITE begin - `TEST_CASE("test_tvalid_low_at_start") begin - fork : tvalid_low_check - begin - wait (tvalid == 1'b1); - $error("tvalid should not be high unless data received"); - disable tvalid_low_check; - end - begin - #100ns; - disable tvalid_low_check; - end - join - end - - `TEST_CASE("test_receives_one_byte") begin - uart_send(77, rx, baud_rate); - tready <= 1'b1; - @(posedge clk iff tready == 1'b1 && tvalid == 1'b1); - `CHECK_EQUAL(tdata, 77); - tready <= 1'b0; - `CHECK_EQUAL(num_overflows, 0); - @(posedge clk); - `CHECK_EQUAL(tvalid, 1'b0); - end - - `TEST_CASE("test_two_bytes_cause_overflow") begin - uart_send(77, rx, baud_rate); - @(posedge clk iff tvalid == 1'b1); - `CHECK_EQUAL(num_overflows, 0); - uart_send(77, rx, baud_rate); - `CHECK_EQUAL(num_overflows, 1); - end - end - - `WATCHDOG(10ms); - - task automatic uart_send(input integer data, ref logic rx, input integer baud_rate); - integer time_per_bit; - time_per_bit = (10**9 / baud_rate); - rx = 1'b0; - #(time_per_bit * 1ns); - - for (int i=0; i<8; i++) begin - rx = data[i]; - #(time_per_bit * 1ns); - end - - rx = 1'b1; - #(time_per_bit * 1ns); - endtask - - always begin - #(clk_period/2 * 1ns); - clk <= !clk; - end - - always @(posedge clk iff overflow == 1'b1) begin - num_overflows <= num_overflows + 1; - end - - uart_rx #(.cycles_per_bit(cycles_per_bit)) dut(.*); - -endmodule +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +`include "vunit_defines.svh" + +module tb_uart_rx; + localparam integer baud_rate = 115200; // bits / s + localparam integer clk_period = 20; // ns + localparam integer cycles_per_bit = 50 * 10**6 / baud_rate; + localparam time_per_bit = (10**9 / baud_rate); + localparam time_per_half_bit = time_per_bit/2; + logic clk = 1'b0; + + // Serial input bit + logic rx = 1'b0; + + // AXI stream for input bytes + logic overflow; + logic tready = 1'b0; + logic tvalid; + logic [7:0]tdata; + integer num_overflows = 0; + + `TEST_SUITE begin + `TEST_CASE("test_tvalid_low_at_start") begin + fork : tvalid_low_check + begin + wait (tvalid == 1'b1); + $error("tvalid should not be high unless data received"); + disable tvalid_low_check; + end + begin + #100ns; + disable tvalid_low_check; + end + join + end + + `TEST_CASE("test_receives_one_byte") begin + uart_send(77, rx, baud_rate); + tready <= 1'b1; + @(posedge clk iff tready == 1'b1 && tvalid == 1'b1); + `CHECK_EQUAL(tdata, 77); + tready <= 1'b0; + `CHECK_EQUAL(num_overflows, 0); + @(posedge clk); + `CHECK_EQUAL(tvalid, 1'b0); + end + + `TEST_CASE("test_two_bytes_cause_overflow") begin + uart_send(77, rx, baud_rate); + @(posedge clk iff tvalid == 1'b1); + `CHECK_EQUAL(num_overflows, 0); + uart_send(77, rx, baud_rate); + `CHECK_EQUAL(num_overflows, 1); + end + end + + `WATCHDOG(10ms); + + task automatic uart_send(input integer data, ref logic rx, input integer baud_rate); + integer time_per_bit; + time_per_bit = (10**9 / baud_rate); + rx = 1'b0; + #(time_per_bit * 1ns); + + for (int i=0; i<8; i++) begin + rx = data[i]; + #(time_per_bit * 1ns); + end + + rx = 1'b1; + #(time_per_bit * 1ns); + endtask + + always begin + #(clk_period/2 * 1ns); + clk <= !clk; + end + + always @(posedge clk iff overflow == 1'b1) begin + num_overflows <= num_overflows + 1; + end + + uart_rx #(.cycles_per_bit(cycles_per_bit)) dut(.*); + +endmodule diff --git a/examples/verilog/uart/src/test/tb_uart_tx.sv b/examples/verilog/uart/src/test/tb_uart_tx.sv index 7e37b8eb4..b3e67ad13 100644 --- a/examples/verilog/uart/src/test/tb_uart_tx.sv +++ b/examples/verilog/uart/src/test/tb_uart_tx.sv @@ -1,87 +1,87 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -`include "vunit_defines.svh" - -module tb_uart_tx; - localparam integer baud_rate = 115200; // bits / s - localparam integer clk_period = 20; // ns - localparam integer cycles_per_bit = 50 * 10**6 / baud_rate; - localparam time_per_bit = (10**9 / baud_rate); - localparam time_per_half_bit = time_per_bit/2; - logic clk = 1'b0; - - // Serial output bit - logic tx; - - // AXI stream for input bytes - logic tready; - logic tvalid = 1'b0; - logic [7:0]tdata; - - int words[$]; - int num_recv; - - task automatic send(); - int word = $urandom_range(255); - tvalid <= 1'b1; - tdata <= word; - words.push_back(word); - @(posedge clk iff tvalid == 1'b1 && tready == 1'b1); - $info("AXI: Sent word ", word); - tvalid <= 1'b0; - endtask - - task automatic check_all_was_received(); - wait (words.size() == num_recv); - endtask - - `TEST_SUITE begin - `TEST_CASE("test_send_one_byte") begin - send(); - check_all_was_received(); - end - `TEST_CASE("test_send_many_bytes") begin - for (int i=0; i<7; i++) begin - send(); - end - check_all_was_received(); - end - end - - `WATCHDOG(10ms); - - always begin - #(clk_period/2 * 1ns); - clk <= !clk; - end - - task automatic uart_recv(output integer data); - data = 0; - wait(tx == 1'b0); - #(time_per_half_bit * 1ns); - `CHECK_EQUAL(tx, 1'b0, "Expected low tx"); - #(time_per_bit * 1ns); - for (int i=0; i<8; i++) begin - data[i] = tx; - #(time_per_bit * 1ns); - end - `CHECK_EQUAL(tx, 1'b1, "Expected high tx"); - #(time_per_half_bit * 1ns); - endtask - - always begin - integer data; - uart_recv(data); - $info("WIRE: Received data ", data); - `CHECK_EQUAL(data, words[num_recv]); - num_recv = num_recv + 1; - end - - uart_tx #(.cycles_per_bit(cycles_per_bit)) dut(.*); - - -endmodule +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +`include "vunit_defines.svh" + +module tb_uart_tx; + localparam integer baud_rate = 115200; // bits / s + localparam integer clk_period = 20; // ns + localparam integer cycles_per_bit = 50 * 10**6 / baud_rate; + localparam time_per_bit = (10**9 / baud_rate); + localparam time_per_half_bit = time_per_bit/2; + logic clk = 1'b0; + + // Serial output bit + logic tx; + + // AXI stream for input bytes + logic tready; + logic tvalid = 1'b0; + logic [7:0]tdata; + + int words[$]; + int num_recv; + + task automatic send(); + int word = $urandom_range(255); + tvalid <= 1'b1; + tdata <= word; + words.push_back(word); + @(posedge clk iff tvalid == 1'b1 && tready == 1'b1); + $info("AXI: Sent word ", word); + tvalid <= 1'b0; + endtask + + task automatic check_all_was_received(); + wait (words.size() == num_recv); + endtask + + `TEST_SUITE begin + `TEST_CASE("test_send_one_byte") begin + send(); + check_all_was_received(); + end + `TEST_CASE("test_send_many_bytes") begin + for (int i=0; i<7; i++) begin + send(); + end + check_all_was_received(); + end + end + + `WATCHDOG(10ms); + + always begin + #(clk_period/2 * 1ns); + clk <= !clk; + end + + task automatic uart_recv(output integer data); + data = 0; + wait(tx == 1'b0); + #(time_per_half_bit * 1ns); + `CHECK_EQUAL(tx, 1'b0, "Expected low tx"); + #(time_per_bit * 1ns); + for (int i=0; i<8; i++) begin + data[i] = tx; + #(time_per_bit * 1ns); + end + `CHECK_EQUAL(tx, 1'b1, "Expected high tx"); + #(time_per_half_bit * 1ns); + endtask + + always begin + integer data; + uart_recv(data); + $info("WIRE: Received data ", data); + `CHECK_EQUAL(data, words[num_recv]); + num_recv = num_recv + 1; + end + + uart_tx #(.cycles_per_bit(cycles_per_bit)) dut(.*); + + +endmodule diff --git a/examples/verilog/uart/src/uart_rx.sv b/examples/verilog/uart/src/uart_rx.sv index 7bfa4a8d8..cc0e6a52f 100644 --- a/examples/verilog/uart/src/uart_rx.sv +++ b/examples/verilog/uart/src/uart_rx.sv @@ -1,74 +1,74 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -module uart_rx(clk, rx, overflow, tready, tvalid, tdata); - parameter integer cycles_per_bit = 434; - - input logic clk; - - // Serial input bit - input logic rx; - - output logic overflow = 1'b0; - - // AXI stream for input bytes - input logic tready; - output logic tvalid = 1'b0; - output logic [7:0] tdata; - - typedef enum {idle, receiving, done} state_t; - state_t state = idle; - - logic [7:0] data; - logic [$bits(cycles_per_bit)-1:0] cycles = 0; - logic [$bits($size(data))-1:0] index; - - always @(posedge clk) begin - overflow <= 1'b0; - - case (state) - idle : begin - if (rx == 1'b0) begin - if (cycles == cycles_per_bit/2 - 1) begin - state = receiving; - cycles <= 0; - index <= 0; - end else begin - cycles <= cycles + 1; - end - end else begin - cycles <= 0; - end - end - - receiving : begin - if (cycles == cycles_per_bit - 1) begin - data <= {rx, data[$size(data)-1:1]}; - cycles <= 0; - - if (index == $size(data) - 1) begin - state <= done; - end else begin - index <= index + 1; - end - end else begin - cycles <= cycles + 1; - end - end - - done : begin - overflow <= tvalid && !tready; - tvalid <= 1'b1; - tdata <= data; - state <= idle; - end - endcase - - if (tvalid == 1'b1 && tready == 1'b1) begin - tvalid <= 1'b0; - end - end -endmodule +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +module uart_rx(clk, rx, overflow, tready, tvalid, tdata); + parameter integer cycles_per_bit = 434; + + input logic clk; + + // Serial input bit + input logic rx; + + output logic overflow = 1'b0; + + // AXI stream for input bytes + input logic tready; + output logic tvalid = 1'b0; + output logic [7:0] tdata; + + typedef enum {idle, receiving, done} state_t; + state_t state = idle; + + logic [7:0] data; + logic [$bits(cycles_per_bit)-1:0] cycles = 0; + logic [$bits($size(data))-1:0] index; + + always @(posedge clk) begin + overflow <= 1'b0; + + case (state) + idle : begin + if (rx == 1'b0) begin + if (cycles == cycles_per_bit/2 - 1) begin + state = receiving; + cycles <= 0; + index <= 0; + end else begin + cycles <= cycles + 1; + end + end else begin + cycles <= 0; + end + end + + receiving : begin + if (cycles == cycles_per_bit - 1) begin + data <= {rx, data[$size(data)-1:1]}; + cycles <= 0; + + if (index == $size(data) - 1) begin + state <= done; + end else begin + index <= index + 1; + end + end else begin + cycles <= cycles + 1; + end + end + + done : begin + overflow <= tvalid && !tready; + tvalid <= 1'b1; + tdata <= data; + state <= idle; + end + endcase + + if (tvalid == 1'b1 && tready == 1'b1) begin + tvalid <= 1'b0; + end + end +endmodule diff --git a/examples/verilog/uart/src/uart_tx.sv b/examples/verilog/uart/src/uart_tx.sv index 81a9909ca..6a44d3ad8 100644 --- a/examples/verilog/uart/src/uart_tx.sv +++ b/examples/verilog/uart/src/uart_tx.sv @@ -1,60 +1,60 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -module uart_tx(clk, tx, tready, tvalid, tdata); - parameter integer cycles_per_bit = 434; - - input logic clk; - - // Serial output bit - output logic tx = 1'b1; - - // AXI stream for input bytes - output logic tready = 1'b1; - input logic tvalid; - input logic [7:0] tdata; - - typedef enum {idle, sending} state_t; - state_t state = idle; - - logic [9:0] data; - logic [$bits(cycles_per_bit)-1:0] cycles; - logic [$bits($size(data))-1:0] index; - - - always @(posedge clk) - begin - case (state) - idle : begin - tx <= 1'b1; - if (tvalid == 1'b1 && tready == 1'b1) begin - state <= sending; - tready <= 1'b0; - cycles <= 0; - index <= 0; - data <= {1'b1, tdata, 1'b0}; - end - end - - sending : begin - tx <= data[0]; - - if (cycles == cycles_per_bit - 1) begin - if (index == $size(data)-1) begin - state <= idle; - tready <= 1'b1; - end else begin - index <= index + 1; - end - data <= {1'b0, data[$size(data)-1:1]}; - cycles <= 0; - end else begin - cycles <= cycles + 1; - end - end - endcase - end -endmodule +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +module uart_tx(clk, tx, tready, tvalid, tdata); + parameter integer cycles_per_bit = 434; + + input logic clk; + + // Serial output bit + output logic tx = 1'b1; + + // AXI stream for input bytes + output logic tready = 1'b1; + input logic tvalid; + input logic [7:0] tdata; + + typedef enum {idle, sending} state_t; + state_t state = idle; + + logic [9:0] data; + logic [$bits(cycles_per_bit)-1:0] cycles; + logic [$bits($size(data))-1:0] index; + + + always @(posedge clk) + begin + case (state) + idle : begin + tx <= 1'b1; + if (tvalid == 1'b1 && tready == 1'b1) begin + state <= sending; + tready <= 1'b0; + cycles <= 0; + index <= 0; + data <= {1'b1, tdata, 1'b0}; + end + end + + sending : begin + tx <= data[0]; + + if (cycles == cycles_per_bit - 1) begin + if (index == $size(data)-1) begin + state <= idle; + tready <= 1'b1; + end else begin + index <= index + 1; + end + data <= {1'b0, data[$size(data)-1:1]}; + cycles <= 0; + end else begin + cycles <= cycles + 1; + end + end + endcase + end +endmodule diff --git a/examples/verilog/user_guide/run.py b/examples/verilog/user_guide/run.py index 0772aa6f0..9a0d87bf9 100644 --- a/examples/verilog/user_guide/run.py +++ b/examples/verilog/user_guide/run.py @@ -1,15 +1,15 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit.verilog import VUnit - -root = dirname(__file__) - -ui = VUnit.from_argv() -lib = ui.add_library("lib") -lib.add_source_files(join(root, "*.sv")) -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit.verilog import VUnit + +root = dirname(__file__) + +ui = VUnit.from_argv() +lib = ui.add_library("lib") +lib.add_source_files(join(root, "*.sv")) +ui.main() diff --git a/examples/verilog/user_guide/tb_example.sv b/examples/verilog/user_guide/tb_example.sv index babfb9766..b13dc5547 100644 --- a/examples/verilog/user_guide/tb_example.sv +++ b/examples/verilog/user_guide/tb_example.sv @@ -1,76 +1,76 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -// You do not need to worry about adding vunit_defines.svh to your -// include path, VUnit will automatically do that for you if VUnit is -// correctly installed (and your python run-script is correct). -`include "vunit_defines.svh" - -module tb_example; - `TEST_SUITE begin - // Note: Do not place any code here (unless you are debugging - // VUnit internals). - - `TEST_SUITE_SETUP begin - // Here you will typically place things that are common to - // all tests, such as asserting the reset signal and starting - // the clock(s). - $display("Running test suite setup code"); - end - - `TEST_CASE_SETUP begin - // By default VUnit will run each test separately. However, - // advanced users may want to run tests consecutively rather - // than in separate instances of the HDL-simulator. In that - // case the code placed in a TEST_CASE_SETUP block should - // restore the unit under test to the state expected by the - // test cases below. In many cases this block would only - // assert/deassert the reset signal for a couple of - // clock-cycles. - // - // When trying out VUnit for the first time this section - // should probably be left empty. - $display("Running test case setup code"); - end - - `TEST_CASE("Test that a successful test case passes") begin - $display("This test case is expected to pass"); - `CHECK_EQUAL(1, 1); - end - - `TEST_CASE("Test that a failing test case actually fails") begin - $display("This test case is expected to fail"); - `CHECK_EQUAL(0, 1, "You may also optionally add a diagnostic message to CHECK_EQUAL"); - // Note: A test case will also be marked as failing if the - // simulator stops for other reasons before the end of the - // TEST_SUITE block is reached. This means that you don't - // need to use CHECK_EQUAL if the testbench you want to - // convert to VUnit already contains code that for example - // calls $stop if an error-condition is detected. - end - - `TEST_CASE("Test that a test case that takes too long time fails with a timeout") begin - $display("This test is expected to timeout because of the watch dog below."); - #2ns; // - end - - `TEST_CASE_CLEANUP begin - // This section will run after the end of a test case. In - // many cases this section will not be needed. - $display("Cleaning up after a test case"); - end - - `TEST_SUITE_CLEANUP begin - // This section will run last before the TEST_SUITE block - // exits. In many cases this section will not be needed. - $display("Cleaning up after running the complete test suite"); - end - end; - - // The watchdog macro is optional, but recommended. If present, it - // must not be placed inside any initial or always-block. - `WATCHDOG(1ns); -endmodule +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +// You do not need to worry about adding vunit_defines.svh to your +// include path, VUnit will automatically do that for you if VUnit is +// correctly installed (and your python run-script is correct). +`include "vunit_defines.svh" + +module tb_example; + `TEST_SUITE begin + // Note: Do not place any code here (unless you are debugging + // VUnit internals). + + `TEST_SUITE_SETUP begin + // Here you will typically place things that are common to + // all tests, such as asserting the reset signal and starting + // the clock(s). + $display("Running test suite setup code"); + end + + `TEST_CASE_SETUP begin + // By default VUnit will run each test separately. However, + // advanced users may want to run tests consecutively rather + // than in separate instances of the HDL-simulator. In that + // case the code placed in a TEST_CASE_SETUP block should + // restore the unit under test to the state expected by the + // test cases below. In many cases this block would only + // assert/deassert the reset signal for a couple of + // clock-cycles. + // + // When trying out VUnit for the first time this section + // should probably be left empty. + $display("Running test case setup code"); + end + + `TEST_CASE("Test that a successful test case passes") begin + $display("This test case is expected to pass"); + `CHECK_EQUAL(1, 1); + end + + `TEST_CASE("Test that a failing test case actually fails") begin + $display("This test case is expected to fail"); + `CHECK_EQUAL(0, 1, "You may also optionally add a diagnostic message to CHECK_EQUAL"); + // Note: A test case will also be marked as failing if the + // simulator stops for other reasons before the end of the + // TEST_SUITE block is reached. This means that you don't + // need to use CHECK_EQUAL if the testbench you want to + // convert to VUnit already contains code that for example + // calls $stop if an error-condition is detected. + end + + `TEST_CASE("Test that a test case that takes too long time fails with a timeout") begin + $display("This test is expected to timeout because of the watch dog below."); + #2ns; // + end + + `TEST_CASE_CLEANUP begin + // This section will run after the end of a test case. In + // many cases this section will not be needed. + $display("Cleaning up after a test case"); + end + + `TEST_SUITE_CLEANUP begin + // This section will run last before the TEST_SUITE block + // exits. In many cases this section will not be needed. + $display("Cleaning up after running the complete test suite"); + end + end; + + // The watchdog macro is optional, but recommended. If present, it + // must not be placed inside any initial or always-block. + `WATCHDOG(1ns); +endmodule diff --git a/examples/verilog/user_guide/tb_example_basic.sv b/examples/verilog/user_guide/tb_example_basic.sv index b919fba73..371a03dce 100644 --- a/examples/verilog/user_guide/tb_example_basic.sv +++ b/examples/verilog/user_guide/tb_example_basic.sv @@ -1,18 +1,18 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -// You do not need to worry about adding vunit_defines.svh to your -// include path, VUnit will automatically do that for you if VUnit is -// correctly installed (and your python run-script is correct). -`include "vunit_defines.svh" - -module tb_example_basic; - `TEST_SUITE begin - // It is possible to create a basic test bench without any test cases - $display("Hello world"); - end; - -endmodule +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +// You do not need to worry about adding vunit_defines.svh to your +// include path, VUnit will automatically do that for you if VUnit is +// correctly installed (and your python run-script is correct). +`include "vunit_defines.svh" + +module tb_example_basic; + `TEST_SUITE begin + // It is possible to create a basic test bench without any test cases + $display("Hello world"); + end; + +endmodule diff --git a/examples/verilog/verilog_ams/run.py b/examples/verilog/verilog_ams/run.py index 65ae0e6b2..00ad25c45 100644 --- a/examples/verilog/verilog_ams/run.py +++ b/examples/verilog/verilog_ams/run.py @@ -1,16 +1,16 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit.verilog import VUnit - -root = dirname(__file__) - -ui = VUnit.from_argv() -lib = ui.add_library("lib") -lib.add_source_files(join(root, "*.sv")) -lib.add_source_files(join(root, "*.vams")).set_compile_option("modelsim.vlog_flags", ["-ams"]) -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit.verilog import VUnit + +root = dirname(__file__) + +ui = VUnit.from_argv() +lib = ui.add_library("lib") +lib.add_source_files(join(root, "*.sv")) +lib.add_source_files(join(root, "*.vams")).set_compile_option("modelsim.vlog_flags", ["-ams"]) +ui.main() diff --git a/examples/verilog/verilog_ams/tb_dut.sv b/examples/verilog/verilog_ams/tb_dut.sv index 779bf2900..98a00211f 100644 --- a/examples/verilog/verilog_ams/tb_dut.sv +++ b/examples/verilog/verilog_ams/tb_dut.sv @@ -1,27 +1,27 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -`include "vunit_defines.svh" - -module tb_dut; - var real vout; - - `TEST_SUITE begin - `TEST_CASE("Test that pass") begin - @(vout); - `CHECK_EQUAL(vout, 3.0); - end - - `TEST_CASE("Test that fail") begin - @(vout); - `CHECK_EQUAL(vout, 2.0); - end - end; - - dut dut_inst(.vout(vout)); - - `WATCHDOG(1ms); -endmodule +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +`include "vunit_defines.svh" + +module tb_dut; + var real vout; + + `TEST_SUITE begin + `TEST_CASE("Test that pass") begin + @(vout); + `CHECK_EQUAL(vout, 3.0); + end + + `TEST_CASE("Test that fail") begin + @(vout); + `CHECK_EQUAL(vout, 2.0); + end + end; + + dut dut_inst(.vout(vout)); + + `WATCHDOG(1ms); +endmodule diff --git a/examples/vhdl/array/run.py b/examples/vhdl/array/run.py index 573ad8fc1..db240eb95 100644 --- a/examples/vhdl/array/run.py +++ b/examples/vhdl/array/run.py @@ -1,18 +1,18 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -root = dirname(__file__) - -ui = VUnit.from_argv() -ui.add_osvvm() -ui.add_array_util() -lib = ui.add_library("lib") -lib.add_source_files(join(root, "src", "*.vhd")) -lib.add_source_files(join(root, "src", "test", "*.vhd")) -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +root = dirname(__file__) + +ui = VUnit.from_argv() +ui.add_osvvm() +ui.add_array_util() +lib = ui.add_library("lib") +lib.add_source_files(join(root, "src", "*.vhd")) +lib.add_source_files(join(root, "src", "test", "*.vhd")) +ui.main() diff --git a/examples/vhdl/array/src/sobel_x.vhd b/examples/vhdl/array/src/sobel_x.vhd index 926936396..11f1a3b5f 100644 --- a/examples/vhdl/array/src/sobel_x.vhd +++ b/examples/vhdl/array/src/sobel_x.vhd @@ -1,68 +1,68 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -entity sobel_x is - generic ( - data_width : natural); - port ( - signal clk : in std_logic; - signal input_tvalid : in std_logic; - signal input_tlast : in std_logic; - signal input_tdata : in unsigned(data_width-1 downto 0); - - signal output_tvalid : out std_logic := '0'; - signal output_tlast : out std_logic; - signal output_tdata : out signed(data_width downto 0)); -end entity; - -architecture a of sobel_x is -begin - - main : process - variable input_tdata_p1, input_tdata_p2 : unsigned(input_tdata'range) := (others => '0'); - variable input_tvalid_p1 : std_logic := '0'; - variable input_tlast_p1 : std_logic := '0'; - variable first : boolean := true; - variable pos, neg : unsigned(input_tdata'range) := (others => '0'); - begin - wait until rising_edge(clk); - output_tvalid <= input_tvalid_p1; - output_tlast <= input_tlast_p1; - - if first then - neg := input_tdata_p1; - else - neg := input_tdata_p2; - end if; - - if input_tlast_p1 = '1' then - pos := input_tdata_p1; - else - pos := input_tdata; - end if; - - output_tdata <= signed(resize(pos, output_tdata'length) - - resize(neg, output_tdata'length)); - - if input_tvalid_p1 = '1' then - if input_tlast_p1 ='1' then - first := true; - else - first := false; - end if; - end if; - - input_tlast_p1 := input_tlast; - input_tvalid_p1 := input_tvalid; - - input_tdata_p2 := input_tdata_p1; - input_tdata_p1 := input_tdata; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity sobel_x is + generic ( + data_width : natural); + port ( + signal clk : in std_logic; + signal input_tvalid : in std_logic; + signal input_tlast : in std_logic; + signal input_tdata : in unsigned(data_width-1 downto 0); + + signal output_tvalid : out std_logic := '0'; + signal output_tlast : out std_logic; + signal output_tdata : out signed(data_width downto 0)); +end entity; + +architecture a of sobel_x is +begin + + main : process + variable input_tdata_p1, input_tdata_p2 : unsigned(input_tdata'range) := (others => '0'); + variable input_tvalid_p1 : std_logic := '0'; + variable input_tlast_p1 : std_logic := '0'; + variable first : boolean := true; + variable pos, neg : unsigned(input_tdata'range) := (others => '0'); + begin + wait until rising_edge(clk); + output_tvalid <= input_tvalid_p1; + output_tlast <= input_tlast_p1; + + if first then + neg := input_tdata_p1; + else + neg := input_tdata_p2; + end if; + + if input_tlast_p1 = '1' then + pos := input_tdata_p1; + else + pos := input_tdata; + end if; + + output_tdata <= signed(resize(pos, output_tdata'length) - + resize(neg, output_tdata'length)); + + if input_tvalid_p1 = '1' then + if input_tlast_p1 ='1' then + first := true; + else + first := false; + end if; + end if; + + input_tlast_p1 := input_tlast; + input_tvalid_p1 := input_tvalid; + + input_tdata_p2 := input_tdata_p1; + input_tdata_p1 := input_tdata; + end process; +end architecture; diff --git a/examples/vhdl/array/src/test/tb_sobel_x.vhd b/examples/vhdl/array/src/test/tb_sobel_x.vhd index 6b376d205..08e8edb7c 100644 --- a/examples/vhdl/array/src/test/tb_sobel_x.vhd +++ b/examples/vhdl/array/src/test/tb_sobel_x.vhd @@ -1,168 +1,168 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library vunit_lib; -context vunit_lib.vunit_context; -use vunit_lib.array_pkg.all; - - -library osvvm; -use osvvm.RandomPkg.all; - -entity tb_sobel_x is - generic ( - runner_cfg : string; - tb_path : string); -end entity; - -architecture tb of tb_sobel_x is - signal clk : std_logic := '0'; - signal input_tvalid : std_logic := '0'; - signal input_tlast : std_logic := '0'; - signal input_tdata : unsigned(14-1 downto 0) := (others => '0'); - signal output_tvalid : std_logic; - signal output_tlast : std_logic; - signal output_tdata : signed(input_tdata'length downto 0); - - shared variable image : array_t; - shared variable reference_image : array_t; - signal start, data_check_done, stimuli_done : boolean := false; -begin - - main : process - procedure sobel_x(variable image : inout array_t; - variable result : inout array_t) is - begin - result.init_2d(width => image.width, - height => image.height, - bit_width => image.bit_width+1, - is_signed => true); - - for y in 0 to image.height-1 loop - for x in 0 to image.width-1 loop - result.set(x => x, y => y, - value => (image.get(minimum(x+1, image.width-1),y) - - image.get(maximum(x-1, 0), y))); - end loop; - end loop; - - end procedure; - - variable rnd : RandomPType; - - procedure randomize(variable arr : inout array_t) is - begin - for idx in 0 to arr.length-1 loop - arr.set(idx, value => rnd.RandInt(arr.lower_limit, arr.upper_limit)); - end loop; - end procedure; - - procedure run_test is - begin - wait until rising_edge(clk); - start <= true; - wait until rising_edge(clk); - start <= false; - - wait until (stimuli_done and - data_check_done and - rising_edge(clk)); - end procedure; - - procedure test_random_image(width, height : natural) is - begin - image.init_2d(width => width, height => height, - bit_width => input_tdata'length, - is_signed => false); - randomize(image); - sobel_x(image, result => reference_image); - run_test; - end procedure; - - begin - test_runner_setup(runner, runner_cfg); - rnd.InitSeed(rnd'instance_name); - while test_suite loop - if run("test_random_data_against_model") then - test_random_image(128, 64); - test_random_image(1, 13); - test_random_image(16, 1); - test_random_image(1, 1); - elsif run("test_input_file_against_output_file") then - image.load_csv(tb_path & "input.csv"); - reference_image.load_csv(tb_path & "output.csv"); - run_test; - end if; - end loop; - test_runner_cleanup(runner); - wait; - end process; - - stimuli_process : process - begin - wait until start and rising_edge(clk); - stimuli_done <= false; - - report ("Sending image of size " & - to_string(image.width) & "x" & - to_string(image.height)); - - for y in 0 to image.height-1 loop - for x in 0 to image.width-1 loop - wait until rising_edge(clk); - input_tvalid <= '1'; - if x = image.width-1 then - input_tlast <= '1'; - else - input_tlast <= '0'; - end if; - input_tdata <= to_unsigned(image.get(x,y), input_tdata'length); - end loop; - end loop; - - wait until rising_edge(clk); - input_tvalid <= '0'; - - stimuli_done <= true; - end process; - - data_check_process : process - begin - wait until start and rising_edge(clk); - data_check_done <= false; - for y in 0 to reference_image.height-1 loop - for x in 0 to reference_image.width-1 loop - wait until output_tvalid = '1' and rising_edge(clk); - check_equal(output_tlast, x = reference_image.width-1); - check_equal(output_tdata, reference_image.get(x, y), - "x=" & to_string(x) & " y=" & to_string(y)); - end loop; - end loop; - report ("Done checking image of size " & - to_string(reference_image.width) & "x" & - to_string(reference_image.height)); - data_check_done <= true; - end process; - - clk <= not clk after 1 ns; - - dut : entity work.sobel_x - generic map ( - data_width => input_tdata'length) - port map ( - clk => clk, - input_tvalid => input_tvalid, - input_tlast => input_tlast, - input_tdata => input_tdata, - output_tvalid => output_tvalid, - output_tlast => output_tlast, - output_tdata => output_tdata); - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library vunit_lib; +context vunit_lib.vunit_context; +use vunit_lib.array_pkg.all; + + +library osvvm; +use osvvm.RandomPkg.all; + +entity tb_sobel_x is + generic ( + runner_cfg : string; + tb_path : string); +end entity; + +architecture tb of tb_sobel_x is + signal clk : std_logic := '0'; + signal input_tvalid : std_logic := '0'; + signal input_tlast : std_logic := '0'; + signal input_tdata : unsigned(14-1 downto 0) := (others => '0'); + signal output_tvalid : std_logic; + signal output_tlast : std_logic; + signal output_tdata : signed(input_tdata'length downto 0); + + shared variable image : array_t; + shared variable reference_image : array_t; + signal start, data_check_done, stimuli_done : boolean := false; +begin + + main : process + procedure sobel_x(variable image : inout array_t; + variable result : inout array_t) is + begin + result.init_2d(width => image.width, + height => image.height, + bit_width => image.bit_width+1, + is_signed => true); + + for y in 0 to image.height-1 loop + for x in 0 to image.width-1 loop + result.set(x => x, y => y, + value => (image.get(minimum(x+1, image.width-1),y) - + image.get(maximum(x-1, 0), y))); + end loop; + end loop; + + end procedure; + + variable rnd : RandomPType; + + procedure randomize(variable arr : inout array_t) is + begin + for idx in 0 to arr.length-1 loop + arr.set(idx, value => rnd.RandInt(arr.lower_limit, arr.upper_limit)); + end loop; + end procedure; + + procedure run_test is + begin + wait until rising_edge(clk); + start <= true; + wait until rising_edge(clk); + start <= false; + + wait until (stimuli_done and + data_check_done and + rising_edge(clk)); + end procedure; + + procedure test_random_image(width, height : natural) is + begin + image.init_2d(width => width, height => height, + bit_width => input_tdata'length, + is_signed => false); + randomize(image); + sobel_x(image, result => reference_image); + run_test; + end procedure; + + begin + test_runner_setup(runner, runner_cfg); + rnd.InitSeed(rnd'instance_name); + while test_suite loop + if run("test_random_data_against_model") then + test_random_image(128, 64); + test_random_image(1, 13); + test_random_image(16, 1); + test_random_image(1, 1); + elsif run("test_input_file_against_output_file") then + image.load_csv(tb_path & "input.csv"); + reference_image.load_csv(tb_path & "output.csv"); + run_test; + end if; + end loop; + test_runner_cleanup(runner); + wait; + end process; + + stimuli_process : process + begin + wait until start and rising_edge(clk); + stimuli_done <= false; + + report ("Sending image of size " & + to_string(image.width) & "x" & + to_string(image.height)); + + for y in 0 to image.height-1 loop + for x in 0 to image.width-1 loop + wait until rising_edge(clk); + input_tvalid <= '1'; + if x = image.width-1 then + input_tlast <= '1'; + else + input_tlast <= '0'; + end if; + input_tdata <= to_unsigned(image.get(x,y), input_tdata'length); + end loop; + end loop; + + wait until rising_edge(clk); + input_tvalid <= '0'; + + stimuli_done <= true; + end process; + + data_check_process : process + begin + wait until start and rising_edge(clk); + data_check_done <= false; + for y in 0 to reference_image.height-1 loop + for x in 0 to reference_image.width-1 loop + wait until output_tvalid = '1' and rising_edge(clk); + check_equal(output_tlast, x = reference_image.width-1); + check_equal(output_tdata, reference_image.get(x, y), + "x=" & to_string(x) & " y=" & to_string(y)); + end loop; + end loop; + report ("Done checking image of size " & + to_string(reference_image.width) & "x" & + to_string(reference_image.height)); + data_check_done <= true; + end process; + + clk <= not clk after 1 ns; + + dut : entity work.sobel_x + generic map ( + data_width => input_tdata'length) + port map ( + clk => clk, + input_tvalid => input_tvalid, + input_tlast => input_tlast, + input_tdata => input_tdata, + output_tvalid => output_tvalid, + output_tlast => output_tlast, + output_tdata => output_tdata); + +end architecture; diff --git a/examples/vhdl/array_axis_vcs/run.py b/examples/vhdl/array_axis_vcs/run.py index eb0e6b7f3..4f0027c00 100644 --- a/examples/vhdl/array_axis_vcs/run.py +++ b/examples/vhdl/array_axis_vcs/run.py @@ -1,24 +1,24 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -root = dirname(__file__) - -vu = VUnit.from_argv() - -vu.add_osvvm() -vu.add_array_util() -vu.add_verification_components() - -lib = vu.add_library("lib") -lib.add_source_files(join(root, "src/*.vhd")) -lib.add_source_files(join(root, "src/**/*.vhd")) - -# vu.set_sim_option('modelsim.init_files.after_load',['runall_addwave.do']) - -vu.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +root = dirname(__file__) + +vu = VUnit.from_argv() + +vu.add_osvvm() +vu.add_array_util() +vu.add_verification_components() + +lib = vu.add_library("lib") +lib.add_source_files(join(root, "src/*.vhd")) +lib.add_source_files(join(root, "src/**/*.vhd")) + +# vu.set_sim_option('modelsim.init_files.after_load',['runall_addwave.do']) + +vu.main() diff --git a/examples/vhdl/array_axis_vcs/src/axis_buffer.vhd b/examples/vhdl/array_axis_vcs/src/axis_buffer.vhd index 17ed2a2e0..63145d14d 100644 --- a/examples/vhdl/array_axis_vcs/src/axis_buffer.vhd +++ b/examples/vhdl/array_axis_vcs/src/axis_buffer.vhd @@ -1,86 +1,86 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -context ieee.ieee_std_context; - -entity axis_buffer is - generic ( - data_width : integer := 32; - fifo_depth : integer := 0 -- ceiling of the log base 2 of the desired FIFO length - ); - port ( - s_axis_clk : in std_logic; - s_axis_rstn : in std_logic; - s_axis_rdy : out std_logic; - s_axis_data : in std_logic_vector(data_width-1 downto 0); - s_axis_valid : in std_logic; - s_axis_strb : in std_logic_vector((data_width/8)-1 downto 0); - s_axis_last : in std_logic; - - m_axis_clk : in std_logic; - m_axis_rstn : in std_logic; - m_axis_valid : out std_logic; - m_axis_data : out std_logic_vector(data_width-1 downto 0); - m_axis_rdy : in std_logic; - m_axis_strb : out std_logic_vector((data_width/8)-1 downto 0); - m_axis_last : out std_logic - ); -end axis_buffer; - -architecture arch of axis_buffer is - - signal r, e, f, wr, rd, valid : std_logic; - signal d, q : std_logic_vector(data_width+data_width/8 downto 0); - -begin - - r <= (s_axis_rstn nand m_axis_rstn); - - fifo: entity work.fifo - generic map ( - fifo_depth => fifo_depth, - data_width => data_width+data_width/8+1 - ) - port map ( - CLKW => s_axis_clk, - CLKR => m_axis_clk, - RST => r, - WR => wr, - RD => rd, - E => e, - F => f, - D => d, - Q => q - ); - --- AXI4 Stream Slave logic - - wr <= s_axis_valid and (not f); - d <= s_axis_last & s_axis_strb & s_axis_data; - - s_axis_rdy <= not f; - --- AXI4 Stream Master logic - - rd <= (not e) and (valid nand (not m_axis_rdy)); - - process(m_axis_clk) begin - if rising_edge(m_axis_clk) then - if ((not m_axis_rstn) or ((valid and E) and m_axis_rdy))='1' then - valid <= '0'; - elsif rd then - valid <= '1'; - end if; - end if; - end process; - - m_axis_valid <= valid; - m_axis_last <= q(d'left); - m_axis_strb <= q(q'left-1 downto data_width); - m_axis_data <= q(data_width-1 downto 0); - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +context ieee.ieee_std_context; + +entity axis_buffer is + generic ( + data_width : integer := 32; + fifo_depth : integer := 0 -- ceiling of the log base 2 of the desired FIFO length + ); + port ( + s_axis_clk : in std_logic; + s_axis_rstn : in std_logic; + s_axis_rdy : out std_logic; + s_axis_data : in std_logic_vector(data_width-1 downto 0); + s_axis_valid : in std_logic; + s_axis_strb : in std_logic_vector((data_width/8)-1 downto 0); + s_axis_last : in std_logic; + + m_axis_clk : in std_logic; + m_axis_rstn : in std_logic; + m_axis_valid : out std_logic; + m_axis_data : out std_logic_vector(data_width-1 downto 0); + m_axis_rdy : in std_logic; + m_axis_strb : out std_logic_vector((data_width/8)-1 downto 0); + m_axis_last : out std_logic + ); +end axis_buffer; + +architecture arch of axis_buffer is + + signal r, e, f, wr, rd, valid : std_logic; + signal d, q : std_logic_vector(data_width+data_width/8 downto 0); + +begin + + r <= (s_axis_rstn nand m_axis_rstn); + + fifo: entity work.fifo + generic map ( + fifo_depth => fifo_depth, + data_width => data_width+data_width/8+1 + ) + port map ( + CLKW => s_axis_clk, + CLKR => m_axis_clk, + RST => r, + WR => wr, + RD => rd, + E => e, + F => f, + D => d, + Q => q + ); + +-- AXI4 Stream Slave logic + + wr <= s_axis_valid and (not f); + d <= s_axis_last & s_axis_strb & s_axis_data; + + s_axis_rdy <= not f; + +-- AXI4 Stream Master logic + + rd <= (not e) and (valid nand (not m_axis_rdy)); + + process(m_axis_clk) begin + if rising_edge(m_axis_clk) then + if ((not m_axis_rstn) or ((valid and E) and m_axis_rdy))='1' then + valid <= '0'; + elsif rd then + valid <= '1'; + end if; + end if; + end process; + + m_axis_valid <= valid; + m_axis_last <= q(d'left); + m_axis_strb <= q(q'left-1 downto data_width); + m_axis_data <= q(data_width-1 downto 0); + +end architecture; diff --git a/examples/vhdl/array_axis_vcs/src/fifo.vhd b/examples/vhdl/array_axis_vcs/src/fifo.vhd index 631de13b9..5e835b4fa 100644 --- a/examples/vhdl/array_axis_vcs/src/fifo.vhd +++ b/examples/vhdl/array_axis_vcs/src/fifo.vhd @@ -1,104 +1,104 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -context ieee.ieee_std_context; - -entity fifo is - generic ( - data_width : positive := 8; - fifo_depth : positive := 8 - ); - port ( - clkw : in std_logic; - clkr : in std_logic; - rst : in std_logic; - wr : in std_logic; - rd : in std_logic; - d : in std_logic_vector(data_width-1 downto 0); - e : out std_logic; - f : out std_logic; - q : out std_logic_vector(data_width-1 downto 0) - ); -end fifo; - -architecture arch of fifo is - - type fifo_t is array (0 to 2**fifo_depth-1) - of std_logic_vector(data_width-1 downto 0); - signal mem : fifo_t; - - signal rdp, wrp : unsigned(fifo_depth downto 0); - -begin - --- Assertions - process(clkw, clkr) - constant dx : std_logic_vector(d'left downto 0) := (others => 'X'); - constant du : std_logic_vector(d'left downto 0) := (others => 'U'); - begin - if rising_edge(clkw) then - if ( wr and ( d?=dx or d?=du ) ) then - assert false report "wrote X|U to fIfO" severity failure; - end if; - if (f and wr) then - assert false report "wrote to fIfO while full" severity failure; - end if; - end if; - if rising_edge(clkr) then - if (e and rd) then - assert false report "Read from fIfO while empty" severity failure; - end if; - end if; - end process; - --- - - process(clkw) begin - if rising_edge(clkw) then - if wr then - mem(to_integer(wrp(fifo_depth-1 downto 0))) <= d; - end if; - end if; - end process; - - process(clkw) begin - if rising_edge(clkw) then - if rst then - wrp <= (others => '0'); - else - if wr then - wrp <= wrp+1; - end if; - end if; - end if; - end process; - - f <= rdp(fifo_depth-1 downto 0)?=wrp(fifo_depth-1 downto 0) - and (rdp(fifo_depth) xor wrp(fifo_depth)); - e <= rdp ?= wrp; - - process(clkr) begin - if rising_edge(clkr) then - if rst then - q <= (others => '0'); - elsif rd then - q <= mem(to_integer(rdp(fifo_depth-1 downto 0))); - end if; - end if; - end process; - - process(clkr) begin - if rising_edge(clkr) then - if rst then - rdp <= (others => '0'); - else - if rd then rdp <= rdp+1; end if; - end if; - end if; - end process; - -end arch; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +context ieee.ieee_std_context; + +entity fifo is + generic ( + data_width : positive := 8; + fifo_depth : positive := 8 + ); + port ( + clkw : in std_logic; + clkr : in std_logic; + rst : in std_logic; + wr : in std_logic; + rd : in std_logic; + d : in std_logic_vector(data_width-1 downto 0); + e : out std_logic; + f : out std_logic; + q : out std_logic_vector(data_width-1 downto 0) + ); +end fifo; + +architecture arch of fifo is + + type fifo_t is array (0 to 2**fifo_depth-1) + of std_logic_vector(data_width-1 downto 0); + signal mem : fifo_t; + + signal rdp, wrp : unsigned(fifo_depth downto 0); + +begin + +-- Assertions + process(clkw, clkr) + constant dx : std_logic_vector(d'left downto 0) := (others => 'X'); + constant du : std_logic_vector(d'left downto 0) := (others => 'U'); + begin + if rising_edge(clkw) then + if ( wr and ( d?=dx or d?=du ) ) then + assert false report "wrote X|U to fIfO" severity failure; + end if; + if (f and wr) then + assert false report "wrote to fIfO while full" severity failure; + end if; + end if; + if rising_edge(clkr) then + if (e and rd) then + assert false report "Read from fIfO while empty" severity failure; + end if; + end if; + end process; + +-- + + process(clkw) begin + if rising_edge(clkw) then + if wr then + mem(to_integer(wrp(fifo_depth-1 downto 0))) <= d; + end if; + end if; + end process; + + process(clkw) begin + if rising_edge(clkw) then + if rst then + wrp <= (others => '0'); + else + if wr then + wrp <= wrp+1; + end if; + end if; + end if; + end process; + + f <= rdp(fifo_depth-1 downto 0)?=wrp(fifo_depth-1 downto 0) + and (rdp(fifo_depth) xor wrp(fifo_depth)); + e <= rdp ?= wrp; + + process(clkr) begin + if rising_edge(clkr) then + if rst then + q <= (others => '0'); + elsif rd then + q <= mem(to_integer(rdp(fifo_depth-1 downto 0))); + end if; + end if; + end process; + + process(clkr) begin + if rising_edge(clkr) then + if rst then + rdp <= (others => '0'); + else + if rd then rdp <= rdp+1; end if; + end if; + end if; + end process; + +end arch; diff --git a/examples/vhdl/array_axis_vcs/src/test/tb_axis_loop.vhd b/examples/vhdl/array_axis_vcs/src/test/tb_axis_loop.vhd index fdf57eb31..5919f4f82 100644 --- a/examples/vhdl/array_axis_vcs/src/test/tb_axis_loop.vhd +++ b/examples/vhdl/array_axis_vcs/src/test/tb_axis_loop.vhd @@ -1,186 +1,186 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- This testbench is a Minimum Working Example (MWE) of VUnit's resources to read/write CSV files and to verify --- AXI4-Stream components. A CSV file that contains comma separated integers is read from `data_path & csv_i`, and it is --- sent row by row to an AXI4-Stream Slave. The AXI4-Stream Slave is expected to be connected to an AXI4-Stream Master --- either directly or (preferredly) through a FIFO, thus composing a loopback. Therefore, as data is pushed to the --- AXI4-Stream Slave interface, the output is read from the AXI4-Stream Master interface and it is saved to --- `data_path & csv_o`. - -library ieee; -context ieee.ieee_std_context; - -library vunit_lib; -context vunit_lib.vunit_context; -context vunit_lib.vc_context; -use vunit_lib.array_pkg.all; - -entity tb_axis_loop is - generic ( - runner_cfg : string; - tb_path : string; - csv_i : string := "data/in.csv"; - csv_o : string := "data/out.csv" - ); -end entity; - -architecture tb of tb_axis_loop is - - -- Simulation constants - - constant clk_period : time := 20 ns; - constant data_width : natural := 32; - - -- AXI4Stream Verification Components - - constant master_axi_stream : axi_stream_master_t := new_axi_stream_master(data_length => data_width); - constant slave_axi_stream : axi_stream_slave_t := new_axi_stream_slave(data_length => data_width); - - -- Signals to/from the UUT from/to the verification components - - signal m_valid, m_ready, m_last, s_valid, s_ready, s_last : std_logic; - signal m_data, s_data : std_logic_vector(data_length(master_axi_stream)-1 downto 0); - - -- tb signals and variables - - signal clk, rst, rstn : std_logic := '0'; - shared variable m_I, m_O : array_t; - signal start, done, saved : boolean := false; - -begin - - clk <= not clk after clk_period/2; - rstn <= not rst; - - main: process - procedure run_test is begin - info("Init test"); - wait until rising_edge(clk); start <= true; - wait until rising_edge(clk); start <= false; - wait until (done and saved and rising_edge(clk)); - info("Test done"); - end procedure; - begin - test_runner_setup(runner, runner_cfg); - while test_suite loop - if run("test") then - rst <= '1'; - wait for 15*clk_period; - rst <= '0'; - run_test; - end if; - end loop; - test_runner_cleanup(runner); - wait; - end process; - - stimuli: process - variable last : std_logic; - begin - wait until start and rising_edge(clk); - done <= false; - wait until rising_edge(clk); - - m_I.load_csv(tb_path & csv_i); - - info("Sending m_I of size " & to_string(m_I.height) & "x" & to_string(m_I.width) & " to UUT..."); - - for y in 0 to m_I.height-1 loop - for x in 0 to m_I.width-1 loop - wait until rising_edge(clk); - if x = m_I.width-1 then last := '1'; else last := '0'; end if; - push_axi_stream(net, master_axi_stream, std_logic_vector(to_signed(m_I.get(x,y), data_width)) , tlast => last); - end loop; - end loop; - - info("m_I sent!"); - - wait until rising_edge(clk); - done <= true; - end process; - - save: process - variable o : std_logic_vector(31 downto 0); - variable last : std_logic:='0'; - begin - wait until start and rising_edge(clk); - saved <= false; - wait for 50*clk_period; - - m_O.init_2d(m_I.width, m_I.height, o'length, true); - - info("Receiving m_O of size " & to_string(m_O.height) & "x" & to_string(m_O.width) & " from UUT..."); - - for y in 0 to m_O.height-1 loop - for x in 0 to m_O.width-1 loop - pop_axi_stream(net, slave_axi_stream, tdata => o, tlast => last); - if (x = m_O.width-1) and (last='0') then - error("Something went wrong. Last misaligned!"); - end if; - m_O.set(x,y,to_integer(signed(o))); - end loop; - end loop; - - info("m_O read!"); - - wait until rising_edge(clk); - m_O.save_csv(tb_path & csv_o); - - info("m_O saved!"); - - wait until rising_edge(clk); - saved <= true; - end process; - --- - - vunit_axism: entity vunit_lib.axi_stream_master - generic map ( - master => master_axi_stream) - port map ( - aclk => clk, - tvalid => m_valid, - tready => m_ready, - tdata => m_data, - tlast => m_last); - - vunit_axiss: entity vunit_lib.axi_stream_slave - generic map ( - slave => slave_axi_stream) - port map ( - aclk => clk, - tvalid => s_valid, - tready => s_ready, - tdata => s_data, - tlast => s_last); - --- - - uut: entity work.axis_buffer - generic map ( - data_width => data_width, - fifo_depth => 4 - ) - port map ( - s_axis_clk => clk, - s_axis_rstn => rstn, - s_axis_rdy => m_ready, - s_axis_data => m_data, - s_axis_valid => m_valid, - s_axis_strb => "1111", - s_axis_last => m_last, - - m_axis_clk => clk, - m_axis_rstn => rstn, - m_axis_valid => s_valid, - m_axis_data => s_data, - m_axis_rdy => s_ready, - m_axis_strb => open, - m_axis_last => s_last - ); - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- This testbench is a Minimum Working Example (MWE) of VUnit's resources to read/write CSV files and to verify +-- AXI4-Stream components. A CSV file that contains comma separated integers is read from `data_path & csv_i`, and it is +-- sent row by row to an AXI4-Stream Slave. The AXI4-Stream Slave is expected to be connected to an AXI4-Stream Master +-- either directly or (preferredly) through a FIFO, thus composing a loopback. Therefore, as data is pushed to the +-- AXI4-Stream Slave interface, the output is read from the AXI4-Stream Master interface and it is saved to +-- `data_path & csv_o`. + +library ieee; +context ieee.ieee_std_context; + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.vc_context; +use vunit_lib.array_pkg.all; + +entity tb_axis_loop is + generic ( + runner_cfg : string; + tb_path : string; + csv_i : string := "data/in.csv"; + csv_o : string := "data/out.csv" + ); +end entity; + +architecture tb of tb_axis_loop is + + -- Simulation constants + + constant clk_period : time := 20 ns; + constant data_width : natural := 32; + + -- AXI4Stream Verification Components + + constant master_axi_stream : axi_stream_master_t := new_axi_stream_master(data_length => data_width); + constant slave_axi_stream : axi_stream_slave_t := new_axi_stream_slave(data_length => data_width); + + -- Signals to/from the UUT from/to the verification components + + signal m_valid, m_ready, m_last, s_valid, s_ready, s_last : std_logic; + signal m_data, s_data : std_logic_vector(data_length(master_axi_stream)-1 downto 0); + + -- tb signals and variables + + signal clk, rst, rstn : std_logic := '0'; + shared variable m_I, m_O : array_t; + signal start, done, saved : boolean := false; + +begin + + clk <= not clk after clk_period/2; + rstn <= not rst; + + main: process + procedure run_test is begin + info("Init test"); + wait until rising_edge(clk); start <= true; + wait until rising_edge(clk); start <= false; + wait until (done and saved and rising_edge(clk)); + info("Test done"); + end procedure; + begin + test_runner_setup(runner, runner_cfg); + while test_suite loop + if run("test") then + rst <= '1'; + wait for 15*clk_period; + rst <= '0'; + run_test; + end if; + end loop; + test_runner_cleanup(runner); + wait; + end process; + + stimuli: process + variable last : std_logic; + begin + wait until start and rising_edge(clk); + done <= false; + wait until rising_edge(clk); + + m_I.load_csv(tb_path & csv_i); + + info("Sending m_I of size " & to_string(m_I.height) & "x" & to_string(m_I.width) & " to UUT..."); + + for y in 0 to m_I.height-1 loop + for x in 0 to m_I.width-1 loop + wait until rising_edge(clk); + if x = m_I.width-1 then last := '1'; else last := '0'; end if; + push_axi_stream(net, master_axi_stream, std_logic_vector(to_signed(m_I.get(x,y), data_width)) , tlast => last); + end loop; + end loop; + + info("m_I sent!"); + + wait until rising_edge(clk); + done <= true; + end process; + + save: process + variable o : std_logic_vector(31 downto 0); + variable last : std_logic:='0'; + begin + wait until start and rising_edge(clk); + saved <= false; + wait for 50*clk_period; + + m_O.init_2d(m_I.width, m_I.height, o'length, true); + + info("Receiving m_O of size " & to_string(m_O.height) & "x" & to_string(m_O.width) & " from UUT..."); + + for y in 0 to m_O.height-1 loop + for x in 0 to m_O.width-1 loop + pop_axi_stream(net, slave_axi_stream, tdata => o, tlast => last); + if (x = m_O.width-1) and (last='0') then + error("Something went wrong. Last misaligned!"); + end if; + m_O.set(x,y,to_integer(signed(o))); + end loop; + end loop; + + info("m_O read!"); + + wait until rising_edge(clk); + m_O.save_csv(tb_path & csv_o); + + info("m_O saved!"); + + wait until rising_edge(clk); + saved <= true; + end process; + +-- + + vunit_axism: entity vunit_lib.axi_stream_master + generic map ( + master => master_axi_stream) + port map ( + aclk => clk, + tvalid => m_valid, + tready => m_ready, + tdata => m_data, + tlast => m_last); + + vunit_axiss: entity vunit_lib.axi_stream_slave + generic map ( + slave => slave_axi_stream) + port map ( + aclk => clk, + tvalid => s_valid, + tready => s_ready, + tdata => s_data, + tlast => s_last); + +-- + + uut: entity work.axis_buffer + generic map ( + data_width => data_width, + fifo_depth => 4 + ) + port map ( + s_axis_clk => clk, + s_axis_rstn => rstn, + s_axis_rdy => m_ready, + s_axis_data => m_data, + s_axis_valid => m_valid, + s_axis_strb => "1111", + s_axis_last => m_last, + + m_axis_clk => clk, + m_axis_rstn => rstn, + m_axis_valid => s_valid, + m_axis_data => s_data, + m_axis_rdy => s_ready, + m_axis_strb => open, + m_axis_last => s_last + ); + +end architecture; diff --git a/examples/vhdl/axi_dma/run.py b/examples/vhdl/axi_dma/run.py index 18d7b8e0d..d350d01b5 100644 --- a/examples/vhdl/axi_dma/run.py +++ b/examples/vhdl/axi_dma/run.py @@ -1,20 +1,20 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -ui = VUnit.from_argv() -ui.add_osvvm() -ui.add_verification_components() - -src_path = join(dirname(__file__), "src") - -axi_dma_lib = ui.add_library("axi_dma_lib") -axi_dma_lib.add_source_files(join(src_path, "*.vhd")) -axi_dma_lib.add_source_files(join(src_path, "test", "*.vhd")) - -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +ui = VUnit.from_argv() +ui.add_osvvm() +ui.add_verification_components() + +src_path = join(dirname(__file__), "src") + +axi_dma_lib = ui.add_library("axi_dma_lib") +axi_dma_lib.add_source_files(join(src_path, "*.vhd")) +axi_dma_lib.add_source_files(join(src_path, "test", "*.vhd")) + +ui.main() diff --git a/examples/vhdl/axi_dma/src/axi_burst_gen.vhd b/examples/vhdl/axi_dma/src/axi_burst_gen.vhd index 266cfe5ce..0fb66057a 100644 --- a/examples/vhdl/axi_dma/src/axi_burst_gen.vhd +++ b/examples/vhdl/axi_dma/src/axi_burst_gen.vhd @@ -1,102 +1,102 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - - -entity axi_burst_gen is - generic ( - max_burst_length : natural range 1 to 256; - bytes_per_beat : natural - ); - port ( - clk : in std_logic; - - -- Start pulse - start : in std_logic; - start_addr : in std_logic_vector(31 downto 0); - num_bytes : in std_logic_vector(31 downto 0); - - -- Output burst stream - burst_valid : out std_logic := '0'; - burst_ready : in std_logic; - burst_addr : out std_logic_vector(31 downto 0) := (others => '0'); - burst_length : out natural range 1 to max_burst_length := 1; - burst_last : out std_logic := '0'); -end entity; - - -architecture a of axi_burst_gen is - constant c4kbyte : natural := 4096; - - type state_t is (idle, - compute_burst_length0, - compute_burst_length1, - compute_is_last, - await_accept); - signal state : state_t := idle; - signal addr, remaining_bytes : unsigned(start_addr'range); -begin - main : process - begin - wait until rising_edge(clk); - - case state is - when idle => - if start = '1' then - addr <= unsigned(start_addr); - remaining_bytes <= unsigned(num_bytes); - state <= compute_burst_length0; - end if; - - when compute_burst_length0 => - if max_burst_length <= to_integer(remaining_bytes) / bytes_per_beat then - burst_length <= max_burst_length; - else - burst_length <= to_integer(remaining_bytes) / bytes_per_beat; - end if; - - state <= compute_burst_length1; - - when compute_burst_length1 => - if (to_integer(addr)/c4kbyte) /= (to_integer(addr) + burst_length * bytes_per_beat - 1)/c4kbyte then - burst_length <= (-(to_integer(addr)/bytes_per_beat)) mod (c4kbyte/bytes_per_beat); - end if; - - state <= compute_is_last; - - when compute_is_last => - burst_valid <= '1'; - - if remaining_bytes = burst_length * bytes_per_beat then - burst_last <= '1'; - else - burst_last <= '0'; - end if; - - state <= await_accept; - - when await_accept => - if burst_ready = '1' then - burst_valid <= '0'; - addr <= addr + burst_length * bytes_per_beat; - remaining_bytes <= remaining_bytes - burst_length * bytes_per_beat; - if burst_last = '1' then - state <= idle; - else - state <= compute_burst_length0; - end if; - end if; - - end case; - - end process; - - burst_addr <= std_logic_vector(addr); - -end; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity axi_burst_gen is + generic ( + max_burst_length : natural range 1 to 256; + bytes_per_beat : natural + ); + port ( + clk : in std_logic; + + -- Start pulse + start : in std_logic; + start_addr : in std_logic_vector(31 downto 0); + num_bytes : in std_logic_vector(31 downto 0); + + -- Output burst stream + burst_valid : out std_logic := '0'; + burst_ready : in std_logic; + burst_addr : out std_logic_vector(31 downto 0) := (others => '0'); + burst_length : out natural range 1 to max_burst_length := 1; + burst_last : out std_logic := '0'); +end entity; + + +architecture a of axi_burst_gen is + constant c4kbyte : natural := 4096; + + type state_t is (idle, + compute_burst_length0, + compute_burst_length1, + compute_is_last, + await_accept); + signal state : state_t := idle; + signal addr, remaining_bytes : unsigned(start_addr'range); +begin + main : process + begin + wait until rising_edge(clk); + + case state is + when idle => + if start = '1' then + addr <= unsigned(start_addr); + remaining_bytes <= unsigned(num_bytes); + state <= compute_burst_length0; + end if; + + when compute_burst_length0 => + if max_burst_length <= to_integer(remaining_bytes) / bytes_per_beat then + burst_length <= max_burst_length; + else + burst_length <= to_integer(remaining_bytes) / bytes_per_beat; + end if; + + state <= compute_burst_length1; + + when compute_burst_length1 => + if (to_integer(addr)/c4kbyte) /= (to_integer(addr) + burst_length * bytes_per_beat - 1)/c4kbyte then + burst_length <= (-(to_integer(addr)/bytes_per_beat)) mod (c4kbyte/bytes_per_beat); + end if; + + state <= compute_is_last; + + when compute_is_last => + burst_valid <= '1'; + + if remaining_bytes = burst_length * bytes_per_beat then + burst_last <= '1'; + else + burst_last <= '0'; + end if; + + state <= await_accept; + + when await_accept => + if burst_ready = '1' then + burst_valid <= '0'; + addr <= addr + burst_length * bytes_per_beat; + remaining_bytes <= remaining_bytes - burst_length * bytes_per_beat; + if burst_last = '1' then + state <= idle; + else + state <= compute_burst_length0; + end if; + end if; + + end case; + + end process; + + burst_addr <= std_logic_vector(addr); + +end; diff --git a/examples/vhdl/axi_dma/src/axi_dma.vhd b/examples/vhdl/axi_dma/src/axi_dma.vhd index 656f137ec..b59959647 100644 --- a/examples/vhdl/axi_dma/src/axi_dma.vhd +++ b/examples/vhdl/axi_dma/src/axi_dma.vhd @@ -1,316 +1,316 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -use work.axil_pkg.all; -use work.axi_pkg.all; -use work.util_pkg.clog2; -use work.util_pkg.is_power_of_two; - -entity axi_dma is - generic ( - max_burst_length : natural range 0 to 256 - ); - port ( - clk : in std_logic; - - -- Control register bus - axils_m2s : in axil_m2s_t; - axils_s2m : out axil_s2m_t := axil_s2m_init; - - -- Read data bus - axi_rd_m2s : out axi_rd_m2s_t := axi_rd_m2s_init; - axi_rd_s2m : in axi_rd_s2m_t; - - -- Write data bus - axi_wr_m2s : out axi_wr_m2s_t := axi_wr_m2s_init; - axi_wr_s2m : in axi_wr_s2m_t - ); - -end entity; - -architecture a of axi_dma is - constant bytes_per_beat : natural := axi_rd_s2m.r.data'length/8; - constant c4kbyte : natural := 4096; - - signal start_transfer : std_logic; - signal transfer_done : std_logic := '0'; - signal src_address : std_logic_vector(31 downto 0); - signal dst_address : std_logic_vector(31 downto 0); - signal num_bytes : std_logic_vector(31 downto 0); - - signal last_beat_written : boolean := false; - - constant max_num_burst_buffered : natural := 2; - constant max_num_beats_buffered : natural := max_num_burst_buffered * max_burst_length; - - -- The maximum difference between two counters can only be - -- max_num_beats_buffered + max_burst_length - -- Thus it is enough to compare counters MOD max_num_beats_buffered - -- We round up to nearest power of two to avoid non power of two MOD - constant max_counter_value : natural := 2**clog2(max_num_beats_buffered + max_burst_length); - - signal num_beats_read : natural range 0 to max_counter_value-1; - signal num_beats_written : natural range 0 to max_counter_value-1; - - constant max_outstanding_write_responses : natural := 7; - signal outstanding_write_responses : natural; - -begin - - ctrl_block : block - type state_t is (idle, wait_for_transfer_done); - signal state : state_t := idle; - begin - main : process - begin - wait until rising_edge(clk); - - - case state is - when idle => - if start_transfer = '1' then - transfer_done <= '0'; - state <= wait_for_transfer_done; - end if; - - when wait_for_transfer_done => - if last_beat_written and outstanding_write_responses = 0 then - transfer_done <= '1'; - state <= idle; - end if; - end case; - - end process; - end block; - - assert is_power_of_two(max_burst_length) - report "max_burst_length shall be a power of two to generate efficient logic" - severity failure; - - read_block : block - type state_t is (wait_for_burst, - wait_for_space, - wait_for_accept); - signal state : state_t := wait_for_burst; - - signal burst_valid : std_logic; - signal burst_ready : std_logic := '0'; - signal burst_length : natural range 1 to max_burst_length; - - signal num_read_beats_ordered : natural range 0 to max_counter_value-1; - begin - - axi_burst_gen_inst: entity work.axi_burst_gen - generic map ( - max_burst_length => max_burst_length, - bytes_per_beat => bytes_per_beat) - port map ( - clk => clk, - start => start_transfer, - start_addr => src_address, - num_bytes => num_bytes, - burst_valid => burst_valid, - burst_ready => burst_ready, - burst_addr => axi_rd_m2s.ar.addr, - burst_length => burst_length, - burst_last => open); - - main : process - begin - wait until rising_edge(clk); - - if start_transfer = '1' then - num_read_beats_ordered <= 0; - end if; - - case state is - when wait_for_burst => - if burst_valid = '1' then - num_read_beats_ordered <= (num_read_beats_ordered + burst_length) mod max_counter_value; - state <= wait_for_space; - end if; - - when wait_for_space => - if (num_read_beats_ordered - num_beats_written) mod max_counter_value < max_num_beats_buffered then - axi_rd_m2s.ar.valid <= '1'; - state <= wait_for_accept; - end if; - - when wait_for_accept => - if axi_rd_m2s.ar.valid = '1' and axi_rd_s2m.ar.ready = '1' then - axi_rd_m2s.ar.valid <= '0'; - state <= wait_for_burst; - end if; - end case; - end process; - - burst_ready <= axi_rd_m2s.ar.valid and axi_rd_s2m.ar.ready; - axi_rd_m2s.ar.id <= (others => '0'); - axi_rd_m2s.ar.burst <= axi_burst_incr; - axi_rd_m2s.ar.len <= std_logic_vector(to_unsigned(burst_length-1, - axi_rd_m2s.ar.len'length)); - axi_rd_m2s.ar.size <= std_logic_vector(to_unsigned(clog2(bytes_per_beat), - axi_rd_m2s.ar.size'length)); - - end block; - - data_buffer : block - type mem_t is array (natural range <>) of std_logic_vector(axi_rd_s2m.r.data'range); - signal mem : mem_t(0 to max_num_beats_buffered - 1) := (others => (others => '0')); - begin - main : process - begin - wait until rising_edge(clk); - - if start_transfer = '1' then - num_beats_read <= 0; - num_beats_written <= 0; - end if; - - if axi_rd_s2m.r.valid = '1' then - mem(num_beats_read mod max_num_beats_buffered) <= axi_rd_s2m.r.data; - num_beats_read <= (num_beats_read + 1) mod max_counter_value; - end if; - - if axi_wr_m2s.w.valid = '1' and axi_wr_s2m.w.ready = '1' then - num_beats_written <= (num_beats_written + 1) mod max_counter_value; - end if; - end process; - - axi_wr_m2s.w.data <= mem(num_beats_written mod max_num_beats_buffered); - axi_wr_m2s.w.strb <= (others => '1'); - axi_rd_m2s.r.ready <= '1'; - end block; - - write_block : block - type state_t is (wait_for_burst, - wait_for_read_data, - wait_for_accept); - signal state : state_t := wait_for_burst; - - signal burst_valid : std_logic; - signal burst_ready : std_logic := '0'; - signal burst_last : std_logic; - signal burst_length : natural range 1 to max_burst_length; - - impure function next_outstanding_write_responses return natural is - variable inc, dec : boolean; - begin - inc := axi_wr_m2s.aw.valid = '1' and axi_wr_s2m.aw.ready = '1'; - dec := axi_wr_s2m.b.valid = '1' and axi_wr_m2s.b.ready = '1'; - - if inc and dec then - return outstanding_write_responses; - elsif inc then - return outstanding_write_responses + 1; - elsif dec then - return outstanding_write_responses - 1; - else - return outstanding_write_responses; - end if; - end; - - signal num_write_beats_ordered : natural range 0 to max_counter_value-1; - signal beat_count : natural range 1 to max_burst_length; - begin - - main : process - begin - wait until rising_edge(clk); - outstanding_write_responses <= next_outstanding_write_responses; - - burst_ready <= '0'; - - if start_transfer = '1' then - num_write_beats_ordered <= max_num_beats_buffered; - outstanding_write_responses <= 0; - last_beat_written <= false; - end if; - - case state is - when wait_for_burst => - if burst_valid = '1' and burst_ready = '0' then - num_write_beats_ordered <= (num_write_beats_ordered + burst_length) mod max_counter_value; - beat_count <= burst_length; - state <= wait_for_read_data; - end if; - - when wait_for_read_data => - if (num_beats_read - num_write_beats_ordered) mod max_counter_value >= max_num_beats_buffered then - if outstanding_write_responses /= max_outstanding_write_responses then - axi_wr_m2s.aw.valid <= '1'; - axi_wr_m2s.w.valid <= '1'; - state <= wait_for_accept; - end if; - end if; - - when wait_for_accept => - if axi_wr_m2s.aw.valid = '1' and axi_wr_s2m.aw.ready = '1' then - axi_wr_m2s.aw.valid <= '0'; - end if; - - if axi_wr_m2s.w.valid = '1' and axi_wr_s2m.w.ready = '1' then - if axi_wr_m2s.w.last = '1' then - axi_wr_m2s.w.valid <= '0'; - else - beat_count <= beat_count - 1; - end if; - end if; - - if axi_wr_m2s.aw.valid = '0' and axi_wr_m2s.w.valid = '0' then - if burst_last = '1' then - last_beat_written <= true; - end if; - burst_ready <= '1'; - state <= wait_for_burst; - end if; - - end case; - end process; - - axi_wr_m2s.w.last <= '1' when beat_count = 1 else '0'; - - axi_burst_gen_inst: entity work.axi_burst_gen - generic map ( - max_burst_length => max_burst_length, - bytes_per_beat => bytes_per_beat) - port map ( - clk => clk, - start => start_transfer, - start_addr => dst_address, - num_bytes => num_bytes, - burst_valid => burst_valid, - burst_ready => burst_ready, - burst_addr => axi_wr_m2s.aw.addr, - burst_length => burst_length, - burst_last => burst_last); - - axi_wr_m2s.b.ready <= '1'; - - axi_wr_m2s.aw.id <= (others => '0'); - axi_wr_m2s.aw.burst <= axi_burst_incr; - axi_wr_m2s.aw.len <= std_logic_vector(to_unsigned(burst_length-1, - axi_wr_m2s.aw.len'length)); - axi_wr_m2s.aw.size <= std_logic_vector(to_unsigned(clog2(bytes_per_beat), - axi_wr_m2s.aw.size'length)); - - end block; - - axi_dma_regs_inst: entity work.axi_dma_regs - port map ( - clk => clk, - axils_m2s => axils_m2s, - axils_s2m => axils_s2m, - start_transfer => start_transfer, - transfer_done => transfer_done, - src_address => src_address, - dst_address => dst_address, - num_bytes => num_bytes); -end; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.axil_pkg.all; +use work.axi_pkg.all; +use work.util_pkg.clog2; +use work.util_pkg.is_power_of_two; + +entity axi_dma is + generic ( + max_burst_length : natural range 0 to 256 + ); + port ( + clk : in std_logic; + + -- Control register bus + axils_m2s : in axil_m2s_t; + axils_s2m : out axil_s2m_t := axil_s2m_init; + + -- Read data bus + axi_rd_m2s : out axi_rd_m2s_t := axi_rd_m2s_init; + axi_rd_s2m : in axi_rd_s2m_t; + + -- Write data bus + axi_wr_m2s : out axi_wr_m2s_t := axi_wr_m2s_init; + axi_wr_s2m : in axi_wr_s2m_t + ); + +end entity; + +architecture a of axi_dma is + constant bytes_per_beat : natural := axi_rd_s2m.r.data'length/8; + constant c4kbyte : natural := 4096; + + signal start_transfer : std_logic; + signal transfer_done : std_logic := '0'; + signal src_address : std_logic_vector(31 downto 0); + signal dst_address : std_logic_vector(31 downto 0); + signal num_bytes : std_logic_vector(31 downto 0); + + signal last_beat_written : boolean := false; + + constant max_num_burst_buffered : natural := 2; + constant max_num_beats_buffered : natural := max_num_burst_buffered * max_burst_length; + + -- The maximum difference between two counters can only be + -- max_num_beats_buffered + max_burst_length + -- Thus it is enough to compare counters MOD max_num_beats_buffered + -- We round up to nearest power of two to avoid non power of two MOD + constant max_counter_value : natural := 2**clog2(max_num_beats_buffered + max_burst_length); + + signal num_beats_read : natural range 0 to max_counter_value-1; + signal num_beats_written : natural range 0 to max_counter_value-1; + + constant max_outstanding_write_responses : natural := 7; + signal outstanding_write_responses : natural; + +begin + + ctrl_block : block + type state_t is (idle, wait_for_transfer_done); + signal state : state_t := idle; + begin + main : process + begin + wait until rising_edge(clk); + + + case state is + when idle => + if start_transfer = '1' then + transfer_done <= '0'; + state <= wait_for_transfer_done; + end if; + + when wait_for_transfer_done => + if last_beat_written and outstanding_write_responses = 0 then + transfer_done <= '1'; + state <= idle; + end if; + end case; + + end process; + end block; + + assert is_power_of_two(max_burst_length) + report "max_burst_length shall be a power of two to generate efficient logic" + severity failure; + + read_block : block + type state_t is (wait_for_burst, + wait_for_space, + wait_for_accept); + signal state : state_t := wait_for_burst; + + signal burst_valid : std_logic; + signal burst_ready : std_logic := '0'; + signal burst_length : natural range 1 to max_burst_length; + + signal num_read_beats_ordered : natural range 0 to max_counter_value-1; + begin + + axi_burst_gen_inst: entity work.axi_burst_gen + generic map ( + max_burst_length => max_burst_length, + bytes_per_beat => bytes_per_beat) + port map ( + clk => clk, + start => start_transfer, + start_addr => src_address, + num_bytes => num_bytes, + burst_valid => burst_valid, + burst_ready => burst_ready, + burst_addr => axi_rd_m2s.ar.addr, + burst_length => burst_length, + burst_last => open); + + main : process + begin + wait until rising_edge(clk); + + if start_transfer = '1' then + num_read_beats_ordered <= 0; + end if; + + case state is + when wait_for_burst => + if burst_valid = '1' then + num_read_beats_ordered <= (num_read_beats_ordered + burst_length) mod max_counter_value; + state <= wait_for_space; + end if; + + when wait_for_space => + if (num_read_beats_ordered - num_beats_written) mod max_counter_value < max_num_beats_buffered then + axi_rd_m2s.ar.valid <= '1'; + state <= wait_for_accept; + end if; + + when wait_for_accept => + if axi_rd_m2s.ar.valid = '1' and axi_rd_s2m.ar.ready = '1' then + axi_rd_m2s.ar.valid <= '0'; + state <= wait_for_burst; + end if; + end case; + end process; + + burst_ready <= axi_rd_m2s.ar.valid and axi_rd_s2m.ar.ready; + axi_rd_m2s.ar.id <= (others => '0'); + axi_rd_m2s.ar.burst <= axi_burst_incr; + axi_rd_m2s.ar.len <= std_logic_vector(to_unsigned(burst_length-1, + axi_rd_m2s.ar.len'length)); + axi_rd_m2s.ar.size <= std_logic_vector(to_unsigned(clog2(bytes_per_beat), + axi_rd_m2s.ar.size'length)); + + end block; + + data_buffer : block + type mem_t is array (natural range <>) of std_logic_vector(axi_rd_s2m.r.data'range); + signal mem : mem_t(0 to max_num_beats_buffered - 1) := (others => (others => '0')); + begin + main : process + begin + wait until rising_edge(clk); + + if start_transfer = '1' then + num_beats_read <= 0; + num_beats_written <= 0; + end if; + + if axi_rd_s2m.r.valid = '1' then + mem(num_beats_read mod max_num_beats_buffered) <= axi_rd_s2m.r.data; + num_beats_read <= (num_beats_read + 1) mod max_counter_value; + end if; + + if axi_wr_m2s.w.valid = '1' and axi_wr_s2m.w.ready = '1' then + num_beats_written <= (num_beats_written + 1) mod max_counter_value; + end if; + end process; + + axi_wr_m2s.w.data <= mem(num_beats_written mod max_num_beats_buffered); + axi_wr_m2s.w.strb <= (others => '1'); + axi_rd_m2s.r.ready <= '1'; + end block; + + write_block : block + type state_t is (wait_for_burst, + wait_for_read_data, + wait_for_accept); + signal state : state_t := wait_for_burst; + + signal burst_valid : std_logic; + signal burst_ready : std_logic := '0'; + signal burst_last : std_logic; + signal burst_length : natural range 1 to max_burst_length; + + impure function next_outstanding_write_responses return natural is + variable inc, dec : boolean; + begin + inc := axi_wr_m2s.aw.valid = '1' and axi_wr_s2m.aw.ready = '1'; + dec := axi_wr_s2m.b.valid = '1' and axi_wr_m2s.b.ready = '1'; + + if inc and dec then + return outstanding_write_responses; + elsif inc then + return outstanding_write_responses + 1; + elsif dec then + return outstanding_write_responses - 1; + else + return outstanding_write_responses; + end if; + end; + + signal num_write_beats_ordered : natural range 0 to max_counter_value-1; + signal beat_count : natural range 1 to max_burst_length; + begin + + main : process + begin + wait until rising_edge(clk); + outstanding_write_responses <= next_outstanding_write_responses; + + burst_ready <= '0'; + + if start_transfer = '1' then + num_write_beats_ordered <= max_num_beats_buffered; + outstanding_write_responses <= 0; + last_beat_written <= false; + end if; + + case state is + when wait_for_burst => + if burst_valid = '1' and burst_ready = '0' then + num_write_beats_ordered <= (num_write_beats_ordered + burst_length) mod max_counter_value; + beat_count <= burst_length; + state <= wait_for_read_data; + end if; + + when wait_for_read_data => + if (num_beats_read - num_write_beats_ordered) mod max_counter_value >= max_num_beats_buffered then + if outstanding_write_responses /= max_outstanding_write_responses then + axi_wr_m2s.aw.valid <= '1'; + axi_wr_m2s.w.valid <= '1'; + state <= wait_for_accept; + end if; + end if; + + when wait_for_accept => + if axi_wr_m2s.aw.valid = '1' and axi_wr_s2m.aw.ready = '1' then + axi_wr_m2s.aw.valid <= '0'; + end if; + + if axi_wr_m2s.w.valid = '1' and axi_wr_s2m.w.ready = '1' then + if axi_wr_m2s.w.last = '1' then + axi_wr_m2s.w.valid <= '0'; + else + beat_count <= beat_count - 1; + end if; + end if; + + if axi_wr_m2s.aw.valid = '0' and axi_wr_m2s.w.valid = '0' then + if burst_last = '1' then + last_beat_written <= true; + end if; + burst_ready <= '1'; + state <= wait_for_burst; + end if; + + end case; + end process; + + axi_wr_m2s.w.last <= '1' when beat_count = 1 else '0'; + + axi_burst_gen_inst: entity work.axi_burst_gen + generic map ( + max_burst_length => max_burst_length, + bytes_per_beat => bytes_per_beat) + port map ( + clk => clk, + start => start_transfer, + start_addr => dst_address, + num_bytes => num_bytes, + burst_valid => burst_valid, + burst_ready => burst_ready, + burst_addr => axi_wr_m2s.aw.addr, + burst_length => burst_length, + burst_last => burst_last); + + axi_wr_m2s.b.ready <= '1'; + + axi_wr_m2s.aw.id <= (others => '0'); + axi_wr_m2s.aw.burst <= axi_burst_incr; + axi_wr_m2s.aw.len <= std_logic_vector(to_unsigned(burst_length-1, + axi_wr_m2s.aw.len'length)); + axi_wr_m2s.aw.size <= std_logic_vector(to_unsigned(clog2(bytes_per_beat), + axi_wr_m2s.aw.size'length)); + + end block; + + axi_dma_regs_inst: entity work.axi_dma_regs + port map ( + clk => clk, + axils_m2s => axils_m2s, + axils_s2m => axils_s2m, + start_transfer => start_transfer, + transfer_done => transfer_done, + src_address => src_address, + dst_address => dst_address, + num_bytes => num_bytes); +end; diff --git a/examples/vhdl/axi_dma/src/axi_dma_regs.vhd b/examples/vhdl/axi_dma/src/axi_dma_regs.vhd index f543f5e72..224ba6652 100644 --- a/examples/vhdl/axi_dma/src/axi_dma_regs.vhd +++ b/examples/vhdl/axi_dma/src/axi_dma_regs.vhd @@ -1,123 +1,123 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -use work.axil_pkg.all; -use work.axi_pkg.axi_response_ok; -use work.axi_pkg.axi_response_decerr; -use work.axi_dma_regs_pkg.all; - -entity axi_dma_regs is - port ( - clk : in std_logic; - - axils_m2s : in axil_m2s_t; - axils_s2m : out axil_s2m_t := axil_s2m_init; - - start_transfer : out std_logic := '0'; - transfer_done : in std_logic; - - src_address : out std_logic_vector(31 downto 0); - dst_address : out std_logic_vector(31 downto 0); - num_bytes : out std_logic_vector(31 downto 0) -); - -end entity; - -architecture a of axi_dma_regs is - type state_t is (idle, - writing, - write_response, - reading); - signal state : state_t := idle; - - signal addr : std_logic_vector(axils_m2s.ar.addr'range); - - -- Compare addresses of 32-bit words discarding byte address - function cmp_word_address(byte_addr : std_logic_vector; - word_addr : natural) return boolean is - begin - return to_integer(unsigned(byte_addr(byte_addr'left downto 2))) = word_addr/4; - end; - -begin - - main : process - begin - wait until rising_edge(clk); - - axils_s2m.ar.ready <= '0'; - axils_s2m.aw.ready <= '0'; - axils_s2m.w.ready <= '0'; - axils_s2m.r.valid <= '0'; - axils_s2m.r.data <= (others => '0'); - - start_transfer <= '0'; - - case state is - when idle => - - if axils_m2s.ar.valid = '1' then - axils_s2m.ar.ready <= '1'; - addr <= axils_m2s.ar.addr; - state <= reading; - - elsif axils_m2s.aw.valid = '1' then - axils_s2m.aw.ready <= '1'; - addr <= axils_m2s.aw.addr; - state <= writing; - end if; - - when writing => - if axils_m2s.w.valid = '1' then - axils_s2m.w.ready <= '1'; - - axils_s2m.b.valid <= '1'; - axils_s2m.b.resp <= axi_response_ok; - - state <= write_response; - - -- Ignore byte write enable - if cmp_word_address(addr, command_reg_addr) then - start_transfer <= axils_m2s.w.data(start_transfer_command_bit); - elsif cmp_word_address(addr, src_address_reg_addr) then - src_address <= axils_m2s.w.data; - elsif cmp_word_address(addr, dst_address_reg_addr) then - dst_address <= axils_m2s.w.data; - elsif cmp_word_address(addr, num_bytes_reg_addr) then - num_bytes <= axils_m2s.w.data; - else - axils_s2m.b.resp <= axi_response_decerr; - end if; - end if; - - when write_response => - if axils_m2s.b.ready = '1' then - axils_s2m.b.valid <= '0'; - state <= idle; - end if; - - when reading => - if axils_s2m.r.valid = '0' then - axils_s2m.r.valid <= '1'; - axils_s2m.r.resp <= axi_response_ok; - - if cmp_word_address(addr, status_reg_addr) then - axils_s2m.r.data(transfer_done_status_bit) <= transfer_done; - else - axils_s2m.r.resp <= axi_response_decerr; - end if; - - elsif axils_m2s.r.ready = '1' then - state <= idle; - end if; - end case; - - end process; -end; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.axil_pkg.all; +use work.axi_pkg.axi_response_ok; +use work.axi_pkg.axi_response_decerr; +use work.axi_dma_regs_pkg.all; + +entity axi_dma_regs is + port ( + clk : in std_logic; + + axils_m2s : in axil_m2s_t; + axils_s2m : out axil_s2m_t := axil_s2m_init; + + start_transfer : out std_logic := '0'; + transfer_done : in std_logic; + + src_address : out std_logic_vector(31 downto 0); + dst_address : out std_logic_vector(31 downto 0); + num_bytes : out std_logic_vector(31 downto 0) +); + +end entity; + +architecture a of axi_dma_regs is + type state_t is (idle, + writing, + write_response, + reading); + signal state : state_t := idle; + + signal addr : std_logic_vector(axils_m2s.ar.addr'range); + + -- Compare addresses of 32-bit words discarding byte address + function cmp_word_address(byte_addr : std_logic_vector; + word_addr : natural) return boolean is + begin + return to_integer(unsigned(byte_addr(byte_addr'left downto 2))) = word_addr/4; + end; + +begin + + main : process + begin + wait until rising_edge(clk); + + axils_s2m.ar.ready <= '0'; + axils_s2m.aw.ready <= '0'; + axils_s2m.w.ready <= '0'; + axils_s2m.r.valid <= '0'; + axils_s2m.r.data <= (others => '0'); + + start_transfer <= '0'; + + case state is + when idle => + + if axils_m2s.ar.valid = '1' then + axils_s2m.ar.ready <= '1'; + addr <= axils_m2s.ar.addr; + state <= reading; + + elsif axils_m2s.aw.valid = '1' then + axils_s2m.aw.ready <= '1'; + addr <= axils_m2s.aw.addr; + state <= writing; + end if; + + when writing => + if axils_m2s.w.valid = '1' then + axils_s2m.w.ready <= '1'; + + axils_s2m.b.valid <= '1'; + axils_s2m.b.resp <= axi_response_ok; + + state <= write_response; + + -- Ignore byte write enable + if cmp_word_address(addr, command_reg_addr) then + start_transfer <= axils_m2s.w.data(start_transfer_command_bit); + elsif cmp_word_address(addr, src_address_reg_addr) then + src_address <= axils_m2s.w.data; + elsif cmp_word_address(addr, dst_address_reg_addr) then + dst_address <= axils_m2s.w.data; + elsif cmp_word_address(addr, num_bytes_reg_addr) then + num_bytes <= axils_m2s.w.data; + else + axils_s2m.b.resp <= axi_response_decerr; + end if; + end if; + + when write_response => + if axils_m2s.b.ready = '1' then + axils_s2m.b.valid <= '0'; + state <= idle; + end if; + + when reading => + if axils_s2m.r.valid = '0' then + axils_s2m.r.valid <= '1'; + axils_s2m.r.resp <= axi_response_ok; + + if cmp_word_address(addr, status_reg_addr) then + axils_s2m.r.data(transfer_done_status_bit) <= transfer_done; + else + axils_s2m.r.resp <= axi_response_decerr; + end if; + + elsif axils_m2s.r.ready = '1' then + state <= idle; + end if; + end case; + + end process; +end; diff --git a/examples/vhdl/axi_dma/src/axi_dma_regs_pkg.vhd b/examples/vhdl/axi_dma/src/axi_dma_regs_pkg.vhd index e16d26232..d5e44df14 100644 --- a/examples/vhdl/axi_dma/src/axi_dma_regs_pkg.vhd +++ b/examples/vhdl/axi_dma/src/axi_dma_regs_pkg.vhd @@ -1,26 +1,26 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -package axi_dma_regs_pkg is - constant command_reg_addr : natural := 0; - constant status_reg_addr : natural := 4; - constant src_address_reg_addr : natural := 8; - constant dst_address_reg_addr : natural := 12; - constant num_bytes_reg_addr : natural := 16; - - constant start_transfer_command_bit : natural := 0; - constant start_transfer_command : std_logic_vector(31 downto 0) := ( - start_transfer_command_bit => '1', - others => '0'); - - constant transfer_done_status_bit : natural := 0; - constant transfer_done_status : std_logic_vector(31 downto 0) := ( - transfer_done_status_bit => '1', - others => '0'); -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +package axi_dma_regs_pkg is + constant command_reg_addr : natural := 0; + constant status_reg_addr : natural := 4; + constant src_address_reg_addr : natural := 8; + constant dst_address_reg_addr : natural := 12; + constant num_bytes_reg_addr : natural := 16; + + constant start_transfer_command_bit : natural := 0; + constant start_transfer_command : std_logic_vector(31 downto 0) := ( + start_transfer_command_bit => '1', + others => '0'); + + constant transfer_done_status_bit : natural := 0; + constant transfer_done_status : std_logic_vector(31 downto 0) := ( + transfer_done_status_bit => '1', + others => '0'); +end package; diff --git a/examples/vhdl/axi_dma/src/axi_pkg.vhd b/examples/vhdl/axi_dma/src/axi_pkg.vhd index e2b368d26..605b0433e 100644 --- a/examples/vhdl/axi_dma/src/axi_pkg.vhd +++ b/examples/vhdl/axi_dma/src/axi_pkg.vhd @@ -1,116 +1,116 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -package axi_pkg is - - type axi_addr_m2s_t is record - valid : std_logic; - id : std_logic_vector(1 downto 0); - addr : std_logic_vector(32-1 downto 0); - len : std_logic_vector(7 downto 0); - size : std_logic_vector(2 downto 0); - burst : std_logic_vector(1 downto 0); - end record; - constant axi_addr_m2s_init : axi_addr_m2s_t := (valid => '0', - id => (others => '0'), - addr => (others => '0'), - len => (others => '0'), - size => (others => '0'), - burst => (others => '0')); - - type axi_addr_s2m_t is record - ready : std_logic; - end record; - constant axi_addr_s2m_init : axi_addr_s2m_t := (ready => '0'); - - type axi_read_m2s_t is record - ready : std_logic; - end record; - constant axi_read_m2s_init : axi_read_m2s_t := (ready => '0'); - - type axi_read_s2m_t is record - valid : std_logic; - id : std_logic_vector(1 downto 0); - data : std_logic_vector(128-1 downto 0); - resp : std_logic_vector(1 downto 0); - last : std_logic; - end record; - constant axi_read_s2m_init : axi_read_s2m_t := (valid => '0', - id => (others => '0'), - data => (others => '0'), - resp => (others => '0'), - last => '0'); - - type axi_write_m2s_t is record - valid : std_logic; - data : std_logic_vector(128-1 downto 0); - strb : std_logic_vector(16-1 downto 0); - last : std_logic; - end record; - constant axi_write_m2s_init : axi_write_m2s_t := (valid => '0', - data => (others => '0'), - strb => (others => '0'), - last => '0'); - - type axi_write_s2m_t is record - ready : std_logic; - end record; - constant axi_write_s2m_init : axi_write_s2m_t := (ready => '0'); - - type axi_wresp_m2s_t is record - ready : std_logic; - end record; - constant axi_wresp_m2s_init : axi_wresp_m2s_t := (ready => '0'); - - type axi_wresp_s2m_t is record - valid : std_logic; - id : std_logic_vector(1 downto 0); - resp : std_logic_vector(1 downto 0); - end record; - constant axi_wresp_s2m_init : axi_wresp_s2m_t := (valid => '0', - id => (others => '0'), - resp => (others => '0')); - - type axi_rd_m2s_t is record - ar : axi_addr_m2s_t; - r : axi_read_m2s_t; - end record; - constant axi_rd_m2s_init : axi_rd_m2s_t := (ar => axi_addr_m2s_init, - r => axi_read_m2s_init); - - type axi_rd_s2m_t is record - ar : axi_addr_s2m_t; - r : axi_read_s2m_t; - end record; - constant axi_rd_s2m_init : axi_rd_s2m_t := (ar => axi_addr_s2m_init, - r => axi_read_s2m_init); - - type axi_wr_m2s_t is record - aw : axi_addr_m2s_t; - w : axi_write_m2s_t; - b : axi_wresp_m2s_t; - end record; - constant axi_wr_m2s_init : axi_wr_m2s_t := (aw => axi_addr_m2s_init, - w => axi_write_m2s_init, - b => axi_wresp_m2s_init); - - type axi_wr_s2m_t is record - aw : axi_addr_s2m_t; - w : axi_write_s2m_t; - b : axi_wresp_s2m_t; - end record; - constant axi_wr_s2m_init : axi_wr_s2m_t := (aw => axi_addr_s2m_init, - w => axi_write_s2m_init, - b => axi_wresp_s2m_init); - - constant axi_response_ok : std_logic_vector(1 downto 0) := "00"; - constant axi_response_decerr : std_logic_vector(1 downto 0) := "11"; - constant axi_burst_incr : std_logic_vector(1 downto 0) := "01"; - -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +package axi_pkg is + + type axi_addr_m2s_t is record + valid : std_logic; + id : std_logic_vector(1 downto 0); + addr : std_logic_vector(32-1 downto 0); + len : std_logic_vector(7 downto 0); + size : std_logic_vector(2 downto 0); + burst : std_logic_vector(1 downto 0); + end record; + constant axi_addr_m2s_init : axi_addr_m2s_t := (valid => '0', + id => (others => '0'), + addr => (others => '0'), + len => (others => '0'), + size => (others => '0'), + burst => (others => '0')); + + type axi_addr_s2m_t is record + ready : std_logic; + end record; + constant axi_addr_s2m_init : axi_addr_s2m_t := (ready => '0'); + + type axi_read_m2s_t is record + ready : std_logic; + end record; + constant axi_read_m2s_init : axi_read_m2s_t := (ready => '0'); + + type axi_read_s2m_t is record + valid : std_logic; + id : std_logic_vector(1 downto 0); + data : std_logic_vector(128-1 downto 0); + resp : std_logic_vector(1 downto 0); + last : std_logic; + end record; + constant axi_read_s2m_init : axi_read_s2m_t := (valid => '0', + id => (others => '0'), + data => (others => '0'), + resp => (others => '0'), + last => '0'); + + type axi_write_m2s_t is record + valid : std_logic; + data : std_logic_vector(128-1 downto 0); + strb : std_logic_vector(16-1 downto 0); + last : std_logic; + end record; + constant axi_write_m2s_init : axi_write_m2s_t := (valid => '0', + data => (others => '0'), + strb => (others => '0'), + last => '0'); + + type axi_write_s2m_t is record + ready : std_logic; + end record; + constant axi_write_s2m_init : axi_write_s2m_t := (ready => '0'); + + type axi_wresp_m2s_t is record + ready : std_logic; + end record; + constant axi_wresp_m2s_init : axi_wresp_m2s_t := (ready => '0'); + + type axi_wresp_s2m_t is record + valid : std_logic; + id : std_logic_vector(1 downto 0); + resp : std_logic_vector(1 downto 0); + end record; + constant axi_wresp_s2m_init : axi_wresp_s2m_t := (valid => '0', + id => (others => '0'), + resp => (others => '0')); + + type axi_rd_m2s_t is record + ar : axi_addr_m2s_t; + r : axi_read_m2s_t; + end record; + constant axi_rd_m2s_init : axi_rd_m2s_t := (ar => axi_addr_m2s_init, + r => axi_read_m2s_init); + + type axi_rd_s2m_t is record + ar : axi_addr_s2m_t; + r : axi_read_s2m_t; + end record; + constant axi_rd_s2m_init : axi_rd_s2m_t := (ar => axi_addr_s2m_init, + r => axi_read_s2m_init); + + type axi_wr_m2s_t is record + aw : axi_addr_m2s_t; + w : axi_write_m2s_t; + b : axi_wresp_m2s_t; + end record; + constant axi_wr_m2s_init : axi_wr_m2s_t := (aw => axi_addr_m2s_init, + w => axi_write_m2s_init, + b => axi_wresp_m2s_init); + + type axi_wr_s2m_t is record + aw : axi_addr_s2m_t; + w : axi_write_s2m_t; + b : axi_wresp_s2m_t; + end record; + constant axi_wr_s2m_init : axi_wr_s2m_t := (aw => axi_addr_s2m_init, + w => axi_write_s2m_init, + b => axi_wresp_s2m_init); + + constant axi_response_ok : std_logic_vector(1 downto 0) := "00"; + constant axi_response_decerr : std_logic_vector(1 downto 0) := "11"; + constant axi_burst_incr : std_logic_vector(1 downto 0) := "01"; + +end package; diff --git a/examples/vhdl/axi_dma/src/axil_pkg.vhd b/examples/vhdl/axi_dma/src/axil_pkg.vhd index d503431cf..66ffee27d 100644 --- a/examples/vhdl/axi_dma/src/axil_pkg.vhd +++ b/examples/vhdl/axi_dma/src/axil_pkg.vhd @@ -1,91 +1,91 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- Defines AXI4-lite data bus types - -library ieee; -use ieee.std_logic_1164.all; - -package axil_pkg is - - type axil_addr_m2s_t is record - valid : std_logic; - addr : std_logic_vector(32-1 downto 0); - end record; - constant axil_addr_m2s_init : axil_addr_m2s_t := (valid => '0', - addr => (others => '0')); - - type axil_addr_s2m_t is record - ready : std_logic; - end record; - constant axil_addr_s2m_init : axil_addr_s2m_t := (ready => '0'); - - type axil_read_m2s_t is record - ready : std_logic; - end record; - constant axil_read_m2s_init : axil_read_m2s_t := (ready => '0'); - - type axil_read_s2m_t is record - valid : std_logic; - data : std_logic_vector(32-1 downto 0); - resp : std_logic_vector(1 downto 0); - end record; - constant axil_read_s2m_init : axil_read_s2m_t := (valid => '0', - data => (others => '0'), - resp => (others => '0')); - - type axil_write_m2s_t is record - valid : std_logic; - data : std_logic_vector(32-1 downto 0); - strb : std_logic_vector(4-1 downto 0); - end record; - constant axil_write_m2s_init : axil_write_m2s_t := (valid => '0', - data => (others => '0'), - strb => (others => '0')); - - type axil_write_s2m_t is record - ready : std_logic; - end record; - constant axil_write_s2m_init : axil_write_s2m_t := (ready => '0'); - - type axil_wresp_m2s_t is record - ready : std_logic; - end record; - constant axil_wresp_m2s_init : axil_wresp_m2s_t := (ready => '0'); - - type axil_wresp_s2m_t is record - valid : std_logic; - resp : std_logic_vector(1 downto 0); - end record; - constant axil_wresp_s2m_init : axil_wresp_s2m_t := (valid => '0', - resp => (others => '0')); - - type axil_m2s_t is record - ar : axil_addr_m2s_t; - aw : axil_addr_m2s_t; - r : axil_read_m2s_t; - w : axil_write_m2s_t; - b : axil_wresp_m2s_t; - end record; - constant axil_m2s_init : axil_m2s_t := (ar => axil_addr_m2s_init, - aw => axil_addr_m2s_init, - r => axil_read_m2s_init, - w => axil_write_m2s_init, - b => axil_wresp_m2s_init); - - type axil_s2m_t is record - ar : axil_addr_s2m_t; - aw : axil_addr_s2m_t; - r : axil_read_s2m_t; - w : axil_write_s2m_t; - b : axil_wresp_s2m_t; - end record; - constant axil_s2m_init : axil_s2m_t := (ar => axil_addr_s2m_init, - aw => axil_addr_s2m_init, - r => axil_read_s2m_init, - w => axil_write_s2m_init, - b => axil_wresp_s2m_init); -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- Defines AXI4-lite data bus types + +library ieee; +use ieee.std_logic_1164.all; + +package axil_pkg is + + type axil_addr_m2s_t is record + valid : std_logic; + addr : std_logic_vector(32-1 downto 0); + end record; + constant axil_addr_m2s_init : axil_addr_m2s_t := (valid => '0', + addr => (others => '0')); + + type axil_addr_s2m_t is record + ready : std_logic; + end record; + constant axil_addr_s2m_init : axil_addr_s2m_t := (ready => '0'); + + type axil_read_m2s_t is record + ready : std_logic; + end record; + constant axil_read_m2s_init : axil_read_m2s_t := (ready => '0'); + + type axil_read_s2m_t is record + valid : std_logic; + data : std_logic_vector(32-1 downto 0); + resp : std_logic_vector(1 downto 0); + end record; + constant axil_read_s2m_init : axil_read_s2m_t := (valid => '0', + data => (others => '0'), + resp => (others => '0')); + + type axil_write_m2s_t is record + valid : std_logic; + data : std_logic_vector(32-1 downto 0); + strb : std_logic_vector(4-1 downto 0); + end record; + constant axil_write_m2s_init : axil_write_m2s_t := (valid => '0', + data => (others => '0'), + strb => (others => '0')); + + type axil_write_s2m_t is record + ready : std_logic; + end record; + constant axil_write_s2m_init : axil_write_s2m_t := (ready => '0'); + + type axil_wresp_m2s_t is record + ready : std_logic; + end record; + constant axil_wresp_m2s_init : axil_wresp_m2s_t := (ready => '0'); + + type axil_wresp_s2m_t is record + valid : std_logic; + resp : std_logic_vector(1 downto 0); + end record; + constant axil_wresp_s2m_init : axil_wresp_s2m_t := (valid => '0', + resp => (others => '0')); + + type axil_m2s_t is record + ar : axil_addr_m2s_t; + aw : axil_addr_m2s_t; + r : axil_read_m2s_t; + w : axil_write_m2s_t; + b : axil_wresp_m2s_t; + end record; + constant axil_m2s_init : axil_m2s_t := (ar => axil_addr_m2s_init, + aw => axil_addr_m2s_init, + r => axil_read_m2s_init, + w => axil_write_m2s_init, + b => axil_wresp_m2s_init); + + type axil_s2m_t is record + ar : axil_addr_s2m_t; + aw : axil_addr_s2m_t; + r : axil_read_s2m_t; + w : axil_write_s2m_t; + b : axil_wresp_s2m_t; + end record; + constant axil_s2m_init : axil_s2m_t := (ar => axil_addr_s2m_init, + aw => axil_addr_s2m_init, + r => axil_read_s2m_init, + w => axil_write_s2m_init, + b => axil_wresp_s2m_init); +end package; diff --git a/examples/vhdl/axi_dma/src/test/tb_axi_dma.vhd b/examples/vhdl/axi_dma/src/test/tb_axi_dma.vhd index d6fb39404..83883dca3 100644 --- a/examples/vhdl/axi_dma/src/test/tb_axi_dma.vhd +++ b/examples/vhdl/axi_dma/src/test/tb_axi_dma.vhd @@ -1,337 +1,337 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library vunit_lib; -context vunit_lib.vunit_context; -context vunit_lib.vc_context; - -library osvvm; -use osvvm.RandomPkg.all; - -use work.axil_pkg.all; -use work.axi_pkg.all; -use work.axi_dma_regs_pkg.all; - -entity tb_axi_dma is - generic (runner_cfg : string); -end entity; - -architecture tb of tb_axi_dma is - constant clk_period : time := 1 ns; - - constant axil_bus : bus_master_t := new_bus(data_length => 32, - address_length => 32, - logger => get_logger("axil_bus")); - - constant memory : memory_t := new_memory; - constant axi_rd_slave : axi_slave_t := new_axi_slave(memory => memory, - logger => get_logger("axi_rd_slave")); - - constant axi_wr_slave : axi_slave_t := new_axi_slave(memory => memory, - logger => get_logger("axi_wr_slave")); - - signal clk : std_logic := '0'; - signal axil_m2s : axil_m2s_t := axil_m2s_init; - signal axil_s2m : axil_s2m_t; - - signal axi_rd_m2s : axi_rd_m2s_t; - signal axi_rd_s2m : axi_rd_s2m_t := axi_rd_s2m_init; - - signal axi_wr_m2s : axi_wr_m2s_t; - signal axi_wr_s2m : axi_wr_s2m_t := axi_wr_s2m_init; - - constant max_burst_length : natural := 256; - constant bytes_per_beat : natural := axi_rd_s2m.r.data'length / 8; - - impure function to_reg_data(value : natural) return std_logic_vector is - begin - return std_logic_vector(to_unsigned(value, data_length(axil_bus))); - end; -begin - - main : process - variable rnd : RandomPType; - - procedure perform_transfer(rbuffer, wbuffer : buffer_t; - reg_sample_period : delay_length := 100 * clk_period) is - variable rdata : std_logic_vector(axil_s2m.r.data'range); - variable byte : natural; - begin - info("perform_transfer(num_bytes => " & to_string(num_bytes(rbuffer)) & ")"); - assert num_bytes(rbuffer) = num_bytes(wbuffer) report "buffer size must be equal"; - - -- Provide random stimuli to read data buffer - -- Set expected data on write data buffer - for i in 0 to num_bytes(rbuffer)-1 loop - byte := rnd.RandInt(0, 255); - write_byte(memory, base_address(rbuffer) + i, byte); - set_expected_byte(memory, base_address(wbuffer) + i, byte); - end loop; - - write_bus(net, axil_bus, src_address_reg_addr, to_reg_data(base_address(rbuffer))); - write_bus(net, axil_bus, dst_address_reg_addr, to_reg_data(base_address(wbuffer))); - write_bus(net, axil_bus, num_bytes_reg_addr, to_reg_data(num_bytes(rbuffer))); - write_bus(net, axil_bus, command_reg_addr, start_transfer_command); - - loop - read_bus(net, axil_bus, status_reg_addr, rdata); - exit when rdata(transfer_done_status_bit) = '1'; - wait for reg_sample_period; - end loop; - - -- This checks that all data has been correctly written to the write - -- buffer at this point - check_expected_was_written(wbuffer); - end; - - procedure perform_transfer(num_bytes : natural; - reg_sample_period : delay_length := 100 * clk_period) is - variable rbuffer, wbuffer : buffer_t; - begin - rbuffer := allocate(memory, - num_bytes => num_bytes, - name => rbuffer'simple_name, - permissions => read_only, - alignment => 4096); - wbuffer := allocate(memory, - num_bytes => num_bytes, - name => wbuffer'simple_name, - permissions => write_only, - alignment => 4096); - perform_transfer(rbuffer, wbuffer, reg_sample_period); - end; - - variable stat : axi_statistics_t; - variable unnused_buffer, rbuffer, wbuffer : buffer_t; - begin - test_runner_setup(runner, runner_cfg); - rnd.InitSeed(rnd'instance_name); - show(display_handler, debug); - - if run("Perform simple transfers") then - -- Perform transfer that are a multiple of the max_burst_length and - -- do not cross 4k boundaries - perform_transfer(num_bytes => max_burst_length * bytes_per_beat); - perform_transfer(num_bytes => 10 * max_burst_length * bytes_per_beat); - - elsif run("Perform split transfers") then - -- Perform transfers where the max_burst_length cannot be used for the - -- entire transfer - perform_transfer(num_bytes => bytes_per_beat); - get_statistics(net, axi_rd_slave, stat, clear => true); - check_equal(get_num_burst_with_length(stat, 1), 1); - check_equal(num_bursts(stat), 1); - get_statistics(net, axi_wr_slave, stat, clear => true); - check_equal(get_num_burst_with_length(stat, 1), 1); - check_equal(num_bursts(stat), 1); - - clear(memory); - perform_transfer(num_bytes => (max_burst_length - 1) * bytes_per_beat); - get_statistics(net, axi_rd_slave, stat, clear => true); - check_equal(get_num_burst_with_length(stat, max_burst_length - 1), 1); - check_equal(num_bursts(stat), 1); - get_statistics(net, axi_wr_slave, stat, clear => true); - check_equal(get_num_burst_with_length(stat, max_burst_length - 1), 1); - check_equal(num_bursts(stat), 1); - - clear(memory); - perform_transfer(num_bytes => (max_burst_length + 1) * bytes_per_beat); - get_statistics(net, axi_rd_slave, stat, clear => true); - check_equal(get_num_burst_with_length(stat, max_burst_length), 1); - check_equal(get_num_burst_with_length(stat, 1), 1); - check_equal(num_bursts(stat), 2); - get_statistics(net, axi_wr_slave, stat, clear => true); - check_equal(get_num_burst_with_length(stat, max_burst_length), 1); - check_equal(get_num_burst_with_length(stat, 1), 1); - check_equal(num_bursts(stat), 2); - - clear(memory); - perform_transfer(num_bytes => (2*max_burst_length - 1) * bytes_per_beat); - get_statistics(net, axi_rd_slave, stat, clear => true); - check_equal(get_num_burst_with_length(stat, max_burst_length), 1); - check_equal(get_num_burst_with_length(stat, max_burst_length - 1), 1); - check_equal(num_bursts(stat), 2); - get_statistics(net, axi_wr_slave, stat, clear => true); - check_equal(get_num_burst_with_length(stat, max_burst_length), 1); - check_equal(get_num_burst_with_length(stat, max_burst_length - 1), 1); - check_equal(num_bursts(stat), 2); - - elsif run("Check transfer done comes after write response") then - -- Set a very large response latency to ensure that the - -- dut does not signal transfer_done until the write reponse has been received - set_response_latency(net, axi_wr_slave, 100 * clk_period); - perform_transfer(num_bytes => max_burst_length * bytes_per_beat, - reg_sample_period => 10 * clk_period); - - elsif run("Check read burst is split on 4KByte boundary") then - for i in 1 to 5 loop - clear(memory); - unnused_buffer := allocate(memory, num_bytes => 4096 - i * bytes_per_beat); - rbuffer := allocate(memory, - num_bytes => 1024, - name => rbuffer'simple_name, - permissions => read_only); - info("base_address(rbuffer) = " & to_string(base_address(rbuffer))); - check_equal(base_address(rbuffer), 4096 - i * bytes_per_beat); - wbuffer := allocate(memory, - num_bytes => 1024, - name => wbuffer'simple_name, - permissions => write_only, - alignment => 4096); - perform_transfer(rbuffer, wbuffer); - end loop; - - elsif run("Check write burst is split on 4KByte boundary") then - for i in 1 to 5 loop - clear(memory); - unnused_buffer := allocate(memory, num_bytes => 4096 - i * bytes_per_beat); - wbuffer := allocate(memory, - num_bytes => 1024, - name => wbuffer'simple_name, - permissions => write_only); - info("base_address(wbuffer) = " & to_string(base_address(wbuffer))); - check_equal(base_address(wbuffer), 4096 - i * bytes_per_beat); - rbuffer := allocate(memory, - num_bytes => 1024, - name => rbuffer'simple_name, - permissions => read_only, - alignment => 4096); - perform_transfer(rbuffer, wbuffer); - end loop; - - - elsif run("Slow data read") then - set_address_fifo_depth(net, axi_rd_slave, 16); - set_address_stall_probability(net, axi_rd_slave, 0.99); - set_data_stall_probability(net, axi_rd_slave, 0.95); - for i in 0 to 15 loop - perform_transfer(num_bytes => rnd.RandInt(1, 3 * max_burst_length) * bytes_per_beat); - end loop; - - elsif run("Slow data write") then - set_address_fifo_depth(net, axi_wr_slave, 16); - set_address_stall_probability(net, axi_wr_slave, 0.99); - set_data_stall_probability(net, axi_wr_slave, 0.95); - for i in 0 to 15 loop - perform_transfer(num_bytes => rnd.RandInt(1, 3 * max_burst_length) * bytes_per_beat); - end loop; - - elsif run("Random AXI configuration") then - for idx in 0 to 15 loop - set_address_fifo_depth(net, axi_wr_slave, rnd.RandInt(1, 16)); - set_address_stall_probability(net, axi_wr_slave, rnd.Uniform(0.0, 0.99)); - set_data_stall_probability(net, axi_wr_slave, rnd.Uniform(0.0, 0.95)); - set_write_response_fifo_depth(net, axi_wr_slave, rnd.RandInt(1, 16)); - set_write_response_stall_probability(net, axi_wr_slave, rnd.Uniform(0.0, 0.99)); - set_response_latency(net, axi_wr_slave, rnd.Uniform(1.0, 100.0) * 1 ns); - - set_address_fifo_depth(net, axi_rd_slave, rnd.RandInt(1, 16)); - set_address_stall_probability(net, axi_rd_slave, rnd.Uniform(0.0, 0.99)); - set_data_stall_probability(net, axi_rd_slave, rnd.Uniform(0.0, 0.95)); - set_response_latency(net, axi_rd_slave, rnd.Uniform(1.0, 100.0) * 1 ns); - for i in 0 to 3 loop - clear(memory); - perform_transfer(num_bytes => rnd.RandInt(1, 3 * max_burst_length) * bytes_per_beat); - end loop; - end loop; - end if; - - test_runner_cleanup(runner); - end process; - - test_runner_watchdog(runner, 10 ms); - - - dut: entity work.axi_dma - generic map ( - max_burst_length => max_burst_length - ) - port map ( - clk => clk, - - axils_m2s => axil_m2s, - axils_s2m => axil_s2m, - - axi_rd_m2s => axi_rd_m2s, - axi_rd_s2m => axi_rd_s2m, - - axi_wr_m2s => axi_wr_m2s, - axi_wr_s2m => axi_wr_s2m); - - clk <= not clk after clk_period/2; - - axi_lite_master_inst: entity vunit_lib.axi_lite_master - generic map ( - bus_handle => axil_bus) - port map ( - aclk => clk, - arready => axil_s2m.ar.ready, - arvalid => axil_m2s.ar.valid, - araddr => axil_m2s.ar.addr, - rready => axil_m2s.r.ready, - rvalid => axil_s2m.r.valid, - rdata => axil_s2m.r.data, - rresp => axil_s2m.r.resp, - awready => axil_s2m.aw.ready, - awvalid => axil_m2s.aw.valid, - awaddr => axil_m2s.aw.addr, - wready => axil_s2m.w.ready, - wvalid => axil_m2s.w.valid, - wdata => axil_m2s.w.data, - wstrb => axil_m2s.w.strb, - bvalid => axil_s2m.b.valid, - bready => axil_m2s.b.ready, - bresp => axil_s2m.b.resp); - - axi_read_slave_inst: entity vunit_lib.axi_read_slave - generic map ( - axi_slave => axi_rd_slave) - port map ( - aclk => clk, - arvalid => axi_rd_m2s.ar.valid, - arready => axi_rd_s2m.ar.ready, - arid => axi_rd_m2s.ar.id, - araddr => axi_rd_m2s.ar.addr, - arlen => axi_rd_m2s.ar.len, - arsize => axi_rd_m2s.ar.size, - arburst => axi_rd_m2s.ar.burst, - rvalid => axi_rd_s2m.r.valid, - rready => axi_rd_m2s.r.ready, - rid => axi_rd_s2m.r.id, - rdata => axi_rd_s2m.r.data, - rresp => axi_rd_s2m.r.resp, - rlast => axi_rd_s2m.r.last); - - axi_write_slave_inst: entity vunit_lib.axi_write_slave - generic map ( - axi_slave => axi_wr_slave) - port map ( - aclk => clk, - awvalid => axi_wr_m2s.aw.valid, - awready => axi_wr_s2m.aw.ready, - awid => axi_wr_m2s.aw.id, - awaddr => axi_wr_m2s.aw.addr, - awlen => axi_wr_m2s.aw.len, - awsize => axi_wr_m2s.aw.size, - awburst => axi_wr_m2s.aw.burst, - - wvalid => axi_wr_m2s.w.valid, - wready => axi_wr_s2m.w.ready, - wdata => axi_wr_m2s.w.data, - wstrb => axi_wr_m2s.w.strb, - wlast => axi_wr_m2s.w.last, - - bvalid => axi_wr_s2m.b.valid, - bready => axi_wr_m2s.b.ready, - bid => axi_wr_s2m.b.id, - bresp => axi_wr_s2m.b.resp); - - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.vc_context; + +library osvvm; +use osvvm.RandomPkg.all; + +use work.axil_pkg.all; +use work.axi_pkg.all; +use work.axi_dma_regs_pkg.all; + +entity tb_axi_dma is + generic (runner_cfg : string); +end entity; + +architecture tb of tb_axi_dma is + constant clk_period : time := 1 ns; + + constant axil_bus : bus_master_t := new_bus(data_length => 32, + address_length => 32, + logger => get_logger("axil_bus")); + + constant memory : memory_t := new_memory; + constant axi_rd_slave : axi_slave_t := new_axi_slave(memory => memory, + logger => get_logger("axi_rd_slave")); + + constant axi_wr_slave : axi_slave_t := new_axi_slave(memory => memory, + logger => get_logger("axi_wr_slave")); + + signal clk : std_logic := '0'; + signal axil_m2s : axil_m2s_t := axil_m2s_init; + signal axil_s2m : axil_s2m_t; + + signal axi_rd_m2s : axi_rd_m2s_t; + signal axi_rd_s2m : axi_rd_s2m_t := axi_rd_s2m_init; + + signal axi_wr_m2s : axi_wr_m2s_t; + signal axi_wr_s2m : axi_wr_s2m_t := axi_wr_s2m_init; + + constant max_burst_length : natural := 256; + constant bytes_per_beat : natural := axi_rd_s2m.r.data'length / 8; + + impure function to_reg_data(value : natural) return std_logic_vector is + begin + return std_logic_vector(to_unsigned(value, data_length(axil_bus))); + end; +begin + + main : process + variable rnd : RandomPType; + + procedure perform_transfer(rbuffer, wbuffer : buffer_t; + reg_sample_period : delay_length := 100 * clk_period) is + variable rdata : std_logic_vector(axil_s2m.r.data'range); + variable byte : natural; + begin + info("perform_transfer(num_bytes => " & to_string(num_bytes(rbuffer)) & ")"); + assert num_bytes(rbuffer) = num_bytes(wbuffer) report "buffer size must be equal"; + + -- Provide random stimuli to read data buffer + -- Set expected data on write data buffer + for i in 0 to num_bytes(rbuffer)-1 loop + byte := rnd.RandInt(0, 255); + write_byte(memory, base_address(rbuffer) + i, byte); + set_expected_byte(memory, base_address(wbuffer) + i, byte); + end loop; + + write_bus(net, axil_bus, src_address_reg_addr, to_reg_data(base_address(rbuffer))); + write_bus(net, axil_bus, dst_address_reg_addr, to_reg_data(base_address(wbuffer))); + write_bus(net, axil_bus, num_bytes_reg_addr, to_reg_data(num_bytes(rbuffer))); + write_bus(net, axil_bus, command_reg_addr, start_transfer_command); + + loop + read_bus(net, axil_bus, status_reg_addr, rdata); + exit when rdata(transfer_done_status_bit) = '1'; + wait for reg_sample_period; + end loop; + + -- This checks that all data has been correctly written to the write + -- buffer at this point + check_expected_was_written(wbuffer); + end; + + procedure perform_transfer(num_bytes : natural; + reg_sample_period : delay_length := 100 * clk_period) is + variable rbuffer, wbuffer : buffer_t; + begin + rbuffer := allocate(memory, + num_bytes => num_bytes, + name => rbuffer'simple_name, + permissions => read_only, + alignment => 4096); + wbuffer := allocate(memory, + num_bytes => num_bytes, + name => wbuffer'simple_name, + permissions => write_only, + alignment => 4096); + perform_transfer(rbuffer, wbuffer, reg_sample_period); + end; + + variable stat : axi_statistics_t; + variable unnused_buffer, rbuffer, wbuffer : buffer_t; + begin + test_runner_setup(runner, runner_cfg); + rnd.InitSeed(rnd'instance_name); + show(display_handler, debug); + + if run("Perform simple transfers") then + -- Perform transfer that are a multiple of the max_burst_length and + -- do not cross 4k boundaries + perform_transfer(num_bytes => max_burst_length * bytes_per_beat); + perform_transfer(num_bytes => 10 * max_burst_length * bytes_per_beat); + + elsif run("Perform split transfers") then + -- Perform transfers where the max_burst_length cannot be used for the + -- entire transfer + perform_transfer(num_bytes => bytes_per_beat); + get_statistics(net, axi_rd_slave, stat, clear => true); + check_equal(get_num_burst_with_length(stat, 1), 1); + check_equal(num_bursts(stat), 1); + get_statistics(net, axi_wr_slave, stat, clear => true); + check_equal(get_num_burst_with_length(stat, 1), 1); + check_equal(num_bursts(stat), 1); + + clear(memory); + perform_transfer(num_bytes => (max_burst_length - 1) * bytes_per_beat); + get_statistics(net, axi_rd_slave, stat, clear => true); + check_equal(get_num_burst_with_length(stat, max_burst_length - 1), 1); + check_equal(num_bursts(stat), 1); + get_statistics(net, axi_wr_slave, stat, clear => true); + check_equal(get_num_burst_with_length(stat, max_burst_length - 1), 1); + check_equal(num_bursts(stat), 1); + + clear(memory); + perform_transfer(num_bytes => (max_burst_length + 1) * bytes_per_beat); + get_statistics(net, axi_rd_slave, stat, clear => true); + check_equal(get_num_burst_with_length(stat, max_burst_length), 1); + check_equal(get_num_burst_with_length(stat, 1), 1); + check_equal(num_bursts(stat), 2); + get_statistics(net, axi_wr_slave, stat, clear => true); + check_equal(get_num_burst_with_length(stat, max_burst_length), 1); + check_equal(get_num_burst_with_length(stat, 1), 1); + check_equal(num_bursts(stat), 2); + + clear(memory); + perform_transfer(num_bytes => (2*max_burst_length - 1) * bytes_per_beat); + get_statistics(net, axi_rd_slave, stat, clear => true); + check_equal(get_num_burst_with_length(stat, max_burst_length), 1); + check_equal(get_num_burst_with_length(stat, max_burst_length - 1), 1); + check_equal(num_bursts(stat), 2); + get_statistics(net, axi_wr_slave, stat, clear => true); + check_equal(get_num_burst_with_length(stat, max_burst_length), 1); + check_equal(get_num_burst_with_length(stat, max_burst_length - 1), 1); + check_equal(num_bursts(stat), 2); + + elsif run("Check transfer done comes after write response") then + -- Set a very large response latency to ensure that the + -- dut does not signal transfer_done until the write reponse has been received + set_response_latency(net, axi_wr_slave, 100 * clk_period); + perform_transfer(num_bytes => max_burst_length * bytes_per_beat, + reg_sample_period => 10 * clk_period); + + elsif run("Check read burst is split on 4KByte boundary") then + for i in 1 to 5 loop + clear(memory); + unnused_buffer := allocate(memory, num_bytes => 4096 - i * bytes_per_beat); + rbuffer := allocate(memory, + num_bytes => 1024, + name => rbuffer'simple_name, + permissions => read_only); + info("base_address(rbuffer) = " & to_string(base_address(rbuffer))); + check_equal(base_address(rbuffer), 4096 - i * bytes_per_beat); + wbuffer := allocate(memory, + num_bytes => 1024, + name => wbuffer'simple_name, + permissions => write_only, + alignment => 4096); + perform_transfer(rbuffer, wbuffer); + end loop; + + elsif run("Check write burst is split on 4KByte boundary") then + for i in 1 to 5 loop + clear(memory); + unnused_buffer := allocate(memory, num_bytes => 4096 - i * bytes_per_beat); + wbuffer := allocate(memory, + num_bytes => 1024, + name => wbuffer'simple_name, + permissions => write_only); + info("base_address(wbuffer) = " & to_string(base_address(wbuffer))); + check_equal(base_address(wbuffer), 4096 - i * bytes_per_beat); + rbuffer := allocate(memory, + num_bytes => 1024, + name => rbuffer'simple_name, + permissions => read_only, + alignment => 4096); + perform_transfer(rbuffer, wbuffer); + end loop; + + + elsif run("Slow data read") then + set_address_fifo_depth(net, axi_rd_slave, 16); + set_address_stall_probability(net, axi_rd_slave, 0.99); + set_data_stall_probability(net, axi_rd_slave, 0.95); + for i in 0 to 15 loop + perform_transfer(num_bytes => rnd.RandInt(1, 3 * max_burst_length) * bytes_per_beat); + end loop; + + elsif run("Slow data write") then + set_address_fifo_depth(net, axi_wr_slave, 16); + set_address_stall_probability(net, axi_wr_slave, 0.99); + set_data_stall_probability(net, axi_wr_slave, 0.95); + for i in 0 to 15 loop + perform_transfer(num_bytes => rnd.RandInt(1, 3 * max_burst_length) * bytes_per_beat); + end loop; + + elsif run("Random AXI configuration") then + for idx in 0 to 15 loop + set_address_fifo_depth(net, axi_wr_slave, rnd.RandInt(1, 16)); + set_address_stall_probability(net, axi_wr_slave, rnd.Uniform(0.0, 0.99)); + set_data_stall_probability(net, axi_wr_slave, rnd.Uniform(0.0, 0.95)); + set_write_response_fifo_depth(net, axi_wr_slave, rnd.RandInt(1, 16)); + set_write_response_stall_probability(net, axi_wr_slave, rnd.Uniform(0.0, 0.99)); + set_response_latency(net, axi_wr_slave, rnd.Uniform(1.0, 100.0) * 1 ns); + + set_address_fifo_depth(net, axi_rd_slave, rnd.RandInt(1, 16)); + set_address_stall_probability(net, axi_rd_slave, rnd.Uniform(0.0, 0.99)); + set_data_stall_probability(net, axi_rd_slave, rnd.Uniform(0.0, 0.95)); + set_response_latency(net, axi_rd_slave, rnd.Uniform(1.0, 100.0) * 1 ns); + for i in 0 to 3 loop + clear(memory); + perform_transfer(num_bytes => rnd.RandInt(1, 3 * max_burst_length) * bytes_per_beat); + end loop; + end loop; + end if; + + test_runner_cleanup(runner); + end process; + + test_runner_watchdog(runner, 10 ms); + + + dut: entity work.axi_dma + generic map ( + max_burst_length => max_burst_length + ) + port map ( + clk => clk, + + axils_m2s => axil_m2s, + axils_s2m => axil_s2m, + + axi_rd_m2s => axi_rd_m2s, + axi_rd_s2m => axi_rd_s2m, + + axi_wr_m2s => axi_wr_m2s, + axi_wr_s2m => axi_wr_s2m); + + clk <= not clk after clk_period/2; + + axi_lite_master_inst: entity vunit_lib.axi_lite_master + generic map ( + bus_handle => axil_bus) + port map ( + aclk => clk, + arready => axil_s2m.ar.ready, + arvalid => axil_m2s.ar.valid, + araddr => axil_m2s.ar.addr, + rready => axil_m2s.r.ready, + rvalid => axil_s2m.r.valid, + rdata => axil_s2m.r.data, + rresp => axil_s2m.r.resp, + awready => axil_s2m.aw.ready, + awvalid => axil_m2s.aw.valid, + awaddr => axil_m2s.aw.addr, + wready => axil_s2m.w.ready, + wvalid => axil_m2s.w.valid, + wdata => axil_m2s.w.data, + wstrb => axil_m2s.w.strb, + bvalid => axil_s2m.b.valid, + bready => axil_m2s.b.ready, + bresp => axil_s2m.b.resp); + + axi_read_slave_inst: entity vunit_lib.axi_read_slave + generic map ( + axi_slave => axi_rd_slave) + port map ( + aclk => clk, + arvalid => axi_rd_m2s.ar.valid, + arready => axi_rd_s2m.ar.ready, + arid => axi_rd_m2s.ar.id, + araddr => axi_rd_m2s.ar.addr, + arlen => axi_rd_m2s.ar.len, + arsize => axi_rd_m2s.ar.size, + arburst => axi_rd_m2s.ar.burst, + rvalid => axi_rd_s2m.r.valid, + rready => axi_rd_m2s.r.ready, + rid => axi_rd_s2m.r.id, + rdata => axi_rd_s2m.r.data, + rresp => axi_rd_s2m.r.resp, + rlast => axi_rd_s2m.r.last); + + axi_write_slave_inst: entity vunit_lib.axi_write_slave + generic map ( + axi_slave => axi_wr_slave) + port map ( + aclk => clk, + awvalid => axi_wr_m2s.aw.valid, + awready => axi_wr_s2m.aw.ready, + awid => axi_wr_m2s.aw.id, + awaddr => axi_wr_m2s.aw.addr, + awlen => axi_wr_m2s.aw.len, + awsize => axi_wr_m2s.aw.size, + awburst => axi_wr_m2s.aw.burst, + + wvalid => axi_wr_m2s.w.valid, + wready => axi_wr_s2m.w.ready, + wdata => axi_wr_m2s.w.data, + wstrb => axi_wr_m2s.w.strb, + wlast => axi_wr_m2s.w.last, + + bvalid => axi_wr_s2m.b.valid, + bready => axi_wr_m2s.b.ready, + bid => axi_wr_s2m.b.id, + bresp => axi_wr_s2m.b.resp); + + +end architecture; diff --git a/examples/vhdl/axi_dma/src/test/tb_axi_dma_regs.vhd b/examples/vhdl/axi_dma/src/test/tb_axi_dma_regs.vhd index a204f71ef..66621fc00 100644 --- a/examples/vhdl/axi_dma/src/test/tb_axi_dma_regs.vhd +++ b/examples/vhdl/axi_dma/src/test/tb_axi_dma_regs.vhd @@ -1,170 +1,170 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -library vunit_lib; -context vunit_lib.vunit_context; -context vunit_lib.vc_context; -use vunit_lib.signal_checker_pkg.all; - -use work.axil_pkg.all; -use work.axi_dma_regs_pkg.all; - -library osvvm; -use osvvm.RandomPkg.all; - -entity tb_axi_dma_regs is - generic (runner_cfg : string); -end entity; - -architecture tb of tb_axi_dma_regs is - constant axil_bus : bus_master_t := new_bus(data_length => 32, address_length => 32); - - constant clk_period : time := 1 ns; - - signal clk : std_logic := '1'; - signal axil_m2s : axil_m2s_t := axil_m2s_init; - signal axil_s2m : axil_s2m_t; - - signal start_transfer : std_logic; - signal transfer_done : std_logic := '0'; - signal src_address : std_logic_vector(31 downto 0); - signal dst_address : std_logic_vector(31 downto 0); - signal num_bytes : std_logic_vector(31 downto 0); - - constant src_address_checker : signal_checker_t := new_signal_checker( - logger => get_logger("src_address_checker")); - - constant dst_address_checker : signal_checker_t := new_signal_checker( - logger => get_logger("dst_address_checker")); - - constant num_bytes_checker : signal_checker_t := new_signal_checker( - logger => get_logger("num_bytes_checker")); - - constant start_transfer_checker : signal_checker_t := new_signal_checker( - logger => get_logger("start_transfer_checker")); - -begin - - main : process - variable rnd : RandomPType; - variable nbytes, src_addr, dst_addr, rdata : std_logic_vector(axil_s2m.r.data'range); - begin - test_runner_setup(runner, runner_cfg); - rnd.InitSeed(rnd'instance_name); - - if run("Test source address") then - src_addr := rnd.RandSlv(src_addr'length); - expect(net, src_address_checker, src_addr, now + 3 * clk_period); - write_bus(net, axil_bus, src_address_reg_addr, src_addr); - wait_until_idle(net, axil_bus); - wait_until_idle(net, src_address_checker); - - elsif run("Test destination address") then - dst_addr := rnd.RandSlv(dst_addr'length); - expect(net, dst_address_checker, dst_addr, now + 3 * clk_period); - write_bus(net, axil_bus, dst_address_reg_addr, dst_addr); - wait_until_idle(net, axil_bus); - wait_until_idle(net, dst_address_checker); - - elsif run("Test num bytes") then - nbytes := rnd.RandSlv(nbytes'length); - expect(net, num_bytes_checker, nbytes, now + 3 * clk_period); - write_bus(net, axil_bus, num_bytes_reg_addr, nbytes); - wait_until_idle(net, axil_bus); - wait_until_idle(net, num_bytes_checker); - - elsif run("Test start transfer command") then - expect(net, start_transfer_checker, "1", now + 3 * clk_period); - expect(net, start_transfer_checker, "0", now + 4 * clk_period); - write_bus(net, axil_bus, command_reg_addr, start_transfer_command); - wait_until_idle(net, axil_bus); - wait_until_idle(net, start_transfer_checker); - - elsif run("Test status register") then - check_bus(net, axil_bus, status_reg_addr, (transfer_done_status'range => '0'), - msg => "Transfer done low initially"); - transfer_done <= '1'; - wait for 0 ns; - check_bus(net, axil_bus, status_reg_addr, transfer_done_status, - msg => "Transfer done high when set"); - transfer_done <= '0'; - wait for 0 ns; - check_bus(net, axil_bus, status_reg_addr, (transfer_done_status'range => '0'), - msg => "Transfer done low when cleared"); - end if; - - -- Avoid unexpected change of data after test - wait for 100 * clk_period; - - test_runner_cleanup(runner); - end process; - - test_runner_watchdog(runner, 1 ms); - - dut: entity work.axi_dma_regs - port map ( - clk => clk, - axils_m2s => axil_m2s, - axils_s2m => axil_s2m, - start_transfer => start_transfer, - transfer_done => transfer_done, - src_address => src_address, - dst_address => dst_address, - num_bytes => num_bytes); - - clk <= not clk after clk_period / 2; - - axi_lite_master_inst: entity vunit_lib.axi_lite_master - generic map ( - bus_handle => axil_bus) - port map ( - aclk => clk, - arready => axil_s2m.ar.ready, - arvalid => axil_m2s.ar.valid, - araddr => axil_m2s.ar.addr, - rready => axil_m2s.r.ready, - rvalid => axil_s2m.r.valid, - rdata => axil_s2m.r.data, - rresp => axil_s2m.r.resp, - awready => axil_s2m.aw.ready, - awvalid => axil_m2s.aw.valid, - awaddr => axil_m2s.aw.addr, - wready => axil_s2m.w.ready, - wvalid => axil_m2s.w.valid, - wdata => axil_m2s.w.data, - wstrb => axil_m2s.w.strb, - bvalid => axil_s2m.b.valid, - bready => axil_m2s.b.ready, - bresp => axil_s2m.b.resp); - - src_address_checker_inst: entity vunit_lib.std_logic_checker - generic map ( - signal_checker => src_address_checker) - port map ( - value => src_address); - - dst_address_checker_inst: entity vunit_lib.std_logic_checker - generic map ( - signal_checker => dst_address_checker) - port map ( - value => dst_address); - - num_bytes_checker_inst: entity vunit_lib.std_logic_checker - generic map ( - signal_checker => num_bytes_checker) - port map ( - value => num_bytes); - - start_transfer_checker_inst: entity vunit_lib.std_logic_checker - generic map ( - signal_checker => start_transfer_checker) - port map ( - value(0) => start_transfer); - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.vc_context; +use vunit_lib.signal_checker_pkg.all; + +use work.axil_pkg.all; +use work.axi_dma_regs_pkg.all; + +library osvvm; +use osvvm.RandomPkg.all; + +entity tb_axi_dma_regs is + generic (runner_cfg : string); +end entity; + +architecture tb of tb_axi_dma_regs is + constant axil_bus : bus_master_t := new_bus(data_length => 32, address_length => 32); + + constant clk_period : time := 1 ns; + + signal clk : std_logic := '1'; + signal axil_m2s : axil_m2s_t := axil_m2s_init; + signal axil_s2m : axil_s2m_t; + + signal start_transfer : std_logic; + signal transfer_done : std_logic := '0'; + signal src_address : std_logic_vector(31 downto 0); + signal dst_address : std_logic_vector(31 downto 0); + signal num_bytes : std_logic_vector(31 downto 0); + + constant src_address_checker : signal_checker_t := new_signal_checker( + logger => get_logger("src_address_checker")); + + constant dst_address_checker : signal_checker_t := new_signal_checker( + logger => get_logger("dst_address_checker")); + + constant num_bytes_checker : signal_checker_t := new_signal_checker( + logger => get_logger("num_bytes_checker")); + + constant start_transfer_checker : signal_checker_t := new_signal_checker( + logger => get_logger("start_transfer_checker")); + +begin + + main : process + variable rnd : RandomPType; + variable nbytes, src_addr, dst_addr, rdata : std_logic_vector(axil_s2m.r.data'range); + begin + test_runner_setup(runner, runner_cfg); + rnd.InitSeed(rnd'instance_name); + + if run("Test source address") then + src_addr := rnd.RandSlv(src_addr'length); + expect(net, src_address_checker, src_addr, now + 3 * clk_period); + write_bus(net, axil_bus, src_address_reg_addr, src_addr); + wait_until_idle(net, axil_bus); + wait_until_idle(net, src_address_checker); + + elsif run("Test destination address") then + dst_addr := rnd.RandSlv(dst_addr'length); + expect(net, dst_address_checker, dst_addr, now + 3 * clk_period); + write_bus(net, axil_bus, dst_address_reg_addr, dst_addr); + wait_until_idle(net, axil_bus); + wait_until_idle(net, dst_address_checker); + + elsif run("Test num bytes") then + nbytes := rnd.RandSlv(nbytes'length); + expect(net, num_bytes_checker, nbytes, now + 3 * clk_period); + write_bus(net, axil_bus, num_bytes_reg_addr, nbytes); + wait_until_idle(net, axil_bus); + wait_until_idle(net, num_bytes_checker); + + elsif run("Test start transfer command") then + expect(net, start_transfer_checker, "1", now + 3 * clk_period); + expect(net, start_transfer_checker, "0", now + 4 * clk_period); + write_bus(net, axil_bus, command_reg_addr, start_transfer_command); + wait_until_idle(net, axil_bus); + wait_until_idle(net, start_transfer_checker); + + elsif run("Test status register") then + check_bus(net, axil_bus, status_reg_addr, (transfer_done_status'range => '0'), + msg => "Transfer done low initially"); + transfer_done <= '1'; + wait for 0 ns; + check_bus(net, axil_bus, status_reg_addr, transfer_done_status, + msg => "Transfer done high when set"); + transfer_done <= '0'; + wait for 0 ns; + check_bus(net, axil_bus, status_reg_addr, (transfer_done_status'range => '0'), + msg => "Transfer done low when cleared"); + end if; + + -- Avoid unexpected change of data after test + wait for 100 * clk_period; + + test_runner_cleanup(runner); + end process; + + test_runner_watchdog(runner, 1 ms); + + dut: entity work.axi_dma_regs + port map ( + clk => clk, + axils_m2s => axil_m2s, + axils_s2m => axil_s2m, + start_transfer => start_transfer, + transfer_done => transfer_done, + src_address => src_address, + dst_address => dst_address, + num_bytes => num_bytes); + + clk <= not clk after clk_period / 2; + + axi_lite_master_inst: entity vunit_lib.axi_lite_master + generic map ( + bus_handle => axil_bus) + port map ( + aclk => clk, + arready => axil_s2m.ar.ready, + arvalid => axil_m2s.ar.valid, + araddr => axil_m2s.ar.addr, + rready => axil_m2s.r.ready, + rvalid => axil_s2m.r.valid, + rdata => axil_s2m.r.data, + rresp => axil_s2m.r.resp, + awready => axil_s2m.aw.ready, + awvalid => axil_m2s.aw.valid, + awaddr => axil_m2s.aw.addr, + wready => axil_s2m.w.ready, + wvalid => axil_m2s.w.valid, + wdata => axil_m2s.w.data, + wstrb => axil_m2s.w.strb, + bvalid => axil_s2m.b.valid, + bready => axil_m2s.b.ready, + bresp => axil_s2m.b.resp); + + src_address_checker_inst: entity vunit_lib.std_logic_checker + generic map ( + signal_checker => src_address_checker) + port map ( + value => src_address); + + dst_address_checker_inst: entity vunit_lib.std_logic_checker + generic map ( + signal_checker => dst_address_checker) + port map ( + value => dst_address); + + num_bytes_checker_inst: entity vunit_lib.std_logic_checker + generic map ( + signal_checker => num_bytes_checker) + port map ( + value => num_bytes); + + start_transfer_checker_inst: entity vunit_lib.std_logic_checker + generic map ( + signal_checker => start_transfer_checker) + port map ( + value(0) => start_transfer); + +end architecture; diff --git a/examples/vhdl/axi_dma/src/test/tb_util_pkg.vhd b/examples/vhdl/axi_dma/src/test/tb_util_pkg.vhd index 7bcc55d41..c6f564cd5 100644 --- a/examples/vhdl/axi_dma/src/test/tb_util_pkg.vhd +++ b/examples/vhdl/axi_dma/src/test/tb_util_pkg.vhd @@ -1,49 +1,49 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library vunit_lib; -context vunit_lib.vunit_context; - -use work.util_pkg.all; - -entity tb_util_pkg is - generic (runner_cfg : string); -end entity; - -architecture tb of tb_util_pkg is -begin - main : process - begin - test_runner_setup(runner, runner_cfg); - show(display_handler, pass); - while test_suite loop - if run("test clog2") then - check_equal(clog2(1), 0); - check_equal(clog2(2), 1); - check_equal(clog2(3), 2); - check_equal(clog2(4), 2); - check_equal(clog2(5), 3); - check_equal(clog2(8), 3); - check_equal(clog2(127), 7); - check_equal(clog2(128), 7); - check_equal(clog2(129), 8); - check_equal(clog2(2**30), 30); - elsif run("test is_power_of_two") then - check(is_power_of_two(1)); - check(is_power_of_two(2)); - check(is_power_of_two(4)); - check(is_power_of_two(2**30)); - check_false(is_power_of_two(3)); - check_false(is_power_of_two(2**30-1)); - check_false(is_power_of_two(2**30+1)); - check_false(is_power_of_two(integer'high)); - end if; - end loop; - test_runner_cleanup(runner); - end process; -end; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library vunit_lib; +context vunit_lib.vunit_context; + +use work.util_pkg.all; + +entity tb_util_pkg is + generic (runner_cfg : string); +end entity; + +architecture tb of tb_util_pkg is +begin + main : process + begin + test_runner_setup(runner, runner_cfg); + show(display_handler, pass); + while test_suite loop + if run("test clog2") then + check_equal(clog2(1), 0); + check_equal(clog2(2), 1); + check_equal(clog2(3), 2); + check_equal(clog2(4), 2); + check_equal(clog2(5), 3); + check_equal(clog2(8), 3); + check_equal(clog2(127), 7); + check_equal(clog2(128), 7); + check_equal(clog2(129), 8); + check_equal(clog2(2**30), 30); + elsif run("test is_power_of_two") then + check(is_power_of_two(1)); + check(is_power_of_two(2)); + check(is_power_of_two(4)); + check(is_power_of_two(2**30)); + check_false(is_power_of_two(3)); + check_false(is_power_of_two(2**30-1)); + check_false(is_power_of_two(2**30+1)); + check_false(is_power_of_two(integer'high)); + end if; + end loop; + test_runner_cleanup(runner); + end process; +end; diff --git a/examples/vhdl/axi_dma/src/util_pkg.vhd b/examples/vhdl/axi_dma/src/util_pkg.vhd index 8a87dcc60..b4f1fe66a 100644 --- a/examples/vhdl/axi_dma/src/util_pkg.vhd +++ b/examples/vhdl/axi_dma/src/util_pkg.vhd @@ -1,33 +1,33 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.math_real.all; - -package util_pkg is - -- ceiling of 2:s logarithm of value - function clog2(value : natural) return natural; - - -- return true if value is an exact power of two - function is_power_of_two(value : natural) return boolean; -end package; - -package body util_pkg is - function clog2(value : natural) return natural is - begin - return integer(ceil(log2(real(value)))); - end; - - function log2(value : natural) return natural is - begin - return integer(trunc(log2(real(value)))); - end; - - function is_power_of_two(value : natural) return boolean is - begin - return clog2(value) = log2(value); - end; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.math_real.all; + +package util_pkg is + -- ceiling of 2:s logarithm of value + function clog2(value : natural) return natural; + + -- return true if value is an exact power of two + function is_power_of_two(value : natural) return boolean; +end package; + +package body util_pkg is + function clog2(value : natural) return natural is + begin + return integer(ceil(log2(real(value)))); + end; + + function log2(value : natural) return natural is + begin + return integer(trunc(log2(real(value)))); + end; + + function is_power_of_two(value : natural) return boolean is + begin + return clog2(value) = log2(value); + end; +end package body; diff --git a/examples/vhdl/check/run.py b/examples/vhdl/check/run.py index 36055b9b2..786ca437d 100644 --- a/examples/vhdl/check/run.py +++ b/examples/vhdl/check/run.py @@ -1,23 +1,23 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -ui = VUnit.from_argv() - -# Enable location preprocessing but exclude all but check_false to make the example less bloated -ui.enable_location_preprocessing( - exclude_subprograms=['debug', 'info', 'check', 'check_failed', 'check_true', 'check_implication', - 'check_stable', 'check_equal', 'check_not_unknown', 'check_zero_one_hot', - 'check_one_hot', 'check_next', 'check_sequence', 'check_relation']) - -ui.enable_check_preprocessing() - -lib = ui.add_library("lib") -lib.add_source_files(join(dirname(__file__), "tb_example.vhd")) - -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +ui = VUnit.from_argv() + +# Enable location preprocessing but exclude all but check_false to make the example less bloated +ui.enable_location_preprocessing( + exclude_subprograms=['debug', 'info', 'check', 'check_failed', 'check_true', 'check_implication', + 'check_stable', 'check_equal', 'check_not_unknown', 'check_zero_one_hot', + 'check_one_hot', 'check_next', 'check_sequence', 'check_relation']) + +ui.enable_check_preprocessing() + +lib = ui.add_library("lib") +lib.add_source_files(join(dirname(__file__), "tb_example.vhd")) + +ui.main() diff --git a/examples/vhdl/check/tb_example.vhd b/examples/vhdl/check/tb_example.vhd index 3f50af849..fe540ab24 100644 --- a/examples/vhdl/check/tb_example.vhd +++ b/examples/vhdl/check/tb_example.vhd @@ -1,349 +1,349 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_example is - generic (runner_cfg : string := runner_cfg_default); -end entity tb_example; - -architecture test of tb_example is - constant some_true_condition : boolean := true; - constant some_false_condition, operation_completed : boolean := false; - signal status_ok : std_logic; - signal clk : std_logic := '0'; - signal check_en : std_logic := '0'; - signal start_event, end_event : std_logic := '0'; - signal stability_en, next_en, sequence_en : std_logic := '0'; - signal stability_signal, next_signal : std_logic := '0'; - signal event_sequence : std_logic_vector(1 to 4) := (others => '0'); -begin - example_process: process is - constant my_data, reference_value : integer := 17; - variable stat, expected_stat : checker_stat_t; - variable my_checker : checker_t; - variable check_ok, found_errors : boolean; - begin - test_runner_setup(runner, runner_cfg); - - -- Introduction - -- This file contains a number of runnable examples that you can step through. Supporting information - -- is brief. It is assumed that you've already read the user guide. - - -- The default settings is to stop on error but for these examples I want to continue - -- on errors so I'm going to raise the stop level. - set_stop_level(failure); - - -- Check - -- The basic check is like a VHDL assert. The difference is that error messages are reported using the - -- VUnit logging library. - check(some_true_condition, "Expect to pass so this should not be displayed"); - check(some_false_condition, "Expected to fail"); - - -- The default log level of error can also be overridden in a specific check call. - check(some_false_condition, "This is not very good", warning); - - -- Note that every failing check will be regarded as a failure from a testing point of view regardless - -- of which level that was used for reporting. - info("Error statistics before warning: " & LF & to_string(get_checker_stat)); - check(some_false_condition, "This warning is also collected in the error statistsics.", warning); - info("Error statistics after warning: " & LF & to_string(get_checker_stat)); - - -- Logging Passing Checks - -- You can also have the check message logged on a passing check to create a debug trace. If you use - -- the result function the message becomes nice in both the passing and failing case. - show(get_logger(default_checker), display_handler, pass); - - check(some_false_condition, result("for error status flag")); - check(some_true_condition, result("for error status flag")); - - -- Message Format - -- Some checks provide a context in addition to the user message in order to help debugging. - -- A context is only given if it can provide information not already known from the type of - -- check used. For example, a failing basic check is caused by the input expression being false. - -- No need to bloat the output with such information. - check_equal(my_data - 1, reference_value, result("for my_data")); - check_equal(my_data, reference_value, result("for my_data")); - - -- Check Location - -- Check calls are also detected by the location preprocessor such that ""anonymous"" checks can be - -- more easily traced. Location preprocessing has been disabled for all checks but check_false to - -- make this example file cleaner. - check_false(some_true_condition, "Something is wrong somewhere."); - - -- Many Checkers - -- As with loggers it's possible to create many checkers, so far we've used the default one. - my_checker := new_checker("my_checker"); - - -- The default checker is not affected by my_checker errors as shown in this after - before diff. - stat := get_checker_stat; - check(my_checker, some_false_condition); - info("Changes in default checker stats after a my_checker error:" & LF & to_string(get_checker_stat - stat)); - get_checker_stat(my_checker, stat); - info("my_checker has its own stats:" & LF & to_string(stat)); - - -- Acting on Failing Checks - -- You can act on the result of a single check. Calls to the default checker are implemented with - -- both procedures and functions while calls to custom checkers have to be procedures (the checker - -- parameter is a protected type). - if check(some_true_condition) then - info("Expected to be here."); - else - info("This was not expected."); - end if; - - check(my_checker, check_ok, some_true_condition); - if check_ok then - info("Expected to be here."); - else - info("This was not expected."); - end if; - - -- You can also ask if a checker has detected any errors. - stat := get_checker_stat(default_checker); - if stat.n_failed > 0 then - info("Expected to be here."); - else - info("This was not expected."); - end if; - - if stat.n_failed > 0 then - info("Expected to be here."); - else - info("This was not expected."); - end if; - - -- Concurrent checks - -- Checks can be called concurrently as well (see bottom of file). The concurrent call for check - -- takes a std_logic condition ('1' = true) and checks that on every enabled clock_edge - -- (either rising, falling, or both). - wait until rising_edge(clk); - check_en <= '1'; - wait until falling_edge(clk); -- NOTE. Falling edge. - wait for 1 ns; - - -- Don't expect the concurrent status check to report any errors yet since it's setup to check - -- on positive edges. - - wait until rising_edge(clk); - wait for 1 ns; - - -- Now you should have seen an error report."); - check_en <= '0'; - - -- Point Checks - -- check is a point check checking a condition at a specific point in time. Here are some other - -- point checks which have the same type of subprograms as check. Only one type of each is shown here. - - ---- True Check - ---- check_true does the same thing as check but has a more verbose name and result message. - check_true(false); - check_true(true); - - ---- False Check - ---- check_false is the inverse of check_true. - check_false(true); - check_false(false); - - ---- Implication Check - ---- check_implication checks if a logical implication holds. - check_implication(true, false); - check_implication(false, true); - - ---- Not Unknown Check - ---- check_not_unknown fails when meta values are present. - check_not_unknown("00001U0"); - check_not_unknown("0000110"); - - ---- Zero One-Hot Check - ---- check_zero_one_hot requires zero or one bit to be active. - check_zero_one_hot("10001"); - check_zero_one_hot("10000"); - - ---- One-Hot Check - ---- check_one_hot requires exactly one bit to be active. - check_one_hot("00000"); - check_one_hot("10000"); - - -- Relation Checks - -- Checks are used to verify a relation, e.g. a = b. The following checks are designed to - -- target such relations. - - ---- Equality Check - ---- check_equal checks the equality between common or similar types. - check_equal(true, '0'); - check_equal(true, '1'); - check_equal(unsigned'("1000"), 7); - check_equal(-3, signed'("11100")); - check_equal(std_logic_vector'("101010"), std_logic_vector'("--1010"), - "Don't care ('-') only equals don't care."); - check_equal(17, 17); - - ---- Match Check - ---- check_match is like check_equal with the difference that don't care ('-') equals anything. - check_match(std_logic_vector'("101010"), std_logic_vector'("--1001")); - check_match(std_logic_vector'("101010"), std_logic_vector'("--1010")); - - ---- Relation Check - ---- check_relation together with the check_preprocessor will check any relation between any - ---- types and present an error context as long as the relation operator and the to_string - ---- function are defined for those types. - stat := get_checker_stat; - expected_stat := (0, 0, 0); - check_relation(stat = expected_stat, result("for default checker statistics")); - expected_stat := stat + (1, 1, 0); - check_relation(get_checker_stat = expected_stat, result("for default checker statistics")); - - -- Sequence Checks - -- Sequence checks need several clock cycles to determine if the check passes or fails. - -- The following types are provided: - - ---- Stability Check - ---- check_stable checks the stability of a signal within a window. The concurrent procedure call - ---- is at the bottom of this file. - - stability_signal <= '1'; - stability_en <= '1'; - - ------ Unstable Case - start_event <= '1'; - wait until rising_edge(clk); - start_event <= '0'; - - wait until rising_edge(clk); - stability_signal <= '0'; - wait until rising_edge(clk); - - end_event <= '1'; - wait until rising_edge(clk); - end_event <= '0'; - - ------ Stable Case - start_event <= '1'; - wait until rising_edge(clk); - start_event <= '0'; - - wait until rising_edge(clk); - wait until rising_edge(clk); - - end_event <= '1'; - wait until rising_edge(clk); - end_event <= '0'; - stability_en <= '0'; - wait until rising_edge(clk); - - ---- Next Check - ---- check_next checks that an event happens with a specified delay from a starting point. The - ---- concurrent procedure call is at the bottom of this file. - - ------ Too long delay - next_en <= '1'; - start_event <= '1'; - wait until rising_edge(clk); - start_event <= '0'; - - wait until rising_edge(clk); - wait until rising_edge(clk); - - next_signal <= '1'; - wait until rising_edge(clk); - next_signal <= '0'; - wait until rising_edge(clk); - - ------ Expected delay - start_event <= '1'; - wait until rising_edge(clk); - start_event <= '0'; - - wait until rising_edge(clk); - - next_signal <= '1'; - wait until rising_edge(clk); - next_signal <= '0'; - next_en <= '0'; - wait until rising_edge(clk); - - ---- Sequence Check - ---- check_sequence checks that a set of events, represented by the bits in a std_logic_vector, - ---- are activated in consecutive clock cycles. check_sequence has several modes of operation. - ---- This example uses the first_pipe mode. The concurrent procedure call is at the bottom of - ---- this file. - - ------ Broken Sequence - sequence_en <= '1'; - event_sequence <= "1000"; - wait until rising_edge(clk); - event_sequence <= "0100"; - wait until rising_edge(clk); - event_sequence <= "0000"; - wait until rising_edge(clk); - event_sequence <= "0001"; - wait until rising_edge(clk); - event_sequence <= "0000"; - wait until rising_edge(clk); - - ------ Complete Sequence - event_sequence <= "1000"; - wait until rising_edge(clk); - event_sequence <= "0100"; - wait until rising_edge(clk); - event_sequence <= "0010"; - wait until rising_edge(clk); - event_sequence <= "0001"; - wait until rising_edge(clk); - event_sequence <= "0000"; - sequence_en <= '0'; - wait until rising_edge(clk); - - -- Unconditional Checks - -- check_failed and check_passed are useful when the pass/fail status is already given by the - -- normal program flow. - if operation_completed then - check_passed("Some operation completed successfully"); - else - debug("Log some interesting information for debugging"); - check_failed("This was not expected. Read the log file for more debug information."); - end if; - - -- Wrap-Up - -- In this testbench I'm expecting failing checks and will suppress these such that the testbench pass. - -- VUnit automatically tracks the errors for the default checker but if you create your own checker - -- you have to insert its pass/fail status into the test_runner_cleanup call for those to be recognized. - expected_stat := (41, 24, 17); - stat := get_checker_stat; - if stat = expected_stat then - reset_checker_stat; - else - check_failed("Not the expected number of failing checks for the default checker:" & LF & to_string(stat)); - end if; - - expected_stat := (2, 1, 1); - get_checker_stat(my_checker, stat); - if stat = expected_stat then - reset_checker_stat(my_checker); - else - check_failed("Not the expected number of failing checks for my_checker:" & LF & to_string(stat)); - end if; - - -- We reset the log count of the checkers to avoid test suite error in this - -- example - reset_log_count(get_logger(my_checker), error); - reset_log_count(get_logger(default_checker), error); - - test_runner_cleanup(runner); - end process example_process; - - clk <= not clk after 5 ns; - - status_check: check(clk, check_en, status_ok, "Concurrent status check failed."); - stability_check : check_stable(clk, stability_en, start_event, end_event, stability_signal, result("for stability_signal.")); - next_check : check_next(clk, next_en, start_event, next_signal, result("for next_signal"), num_cks => 2); - sequence_check: check_sequence(clk, sequence_en, event_sequence, result("for event_sequence"), first_pipe); -end architecture test; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_example is + generic (runner_cfg : string := runner_cfg_default); +end entity tb_example; + +architecture test of tb_example is + constant some_true_condition : boolean := true; + constant some_false_condition, operation_completed : boolean := false; + signal status_ok : std_logic; + signal clk : std_logic := '0'; + signal check_en : std_logic := '0'; + signal start_event, end_event : std_logic := '0'; + signal stability_en, next_en, sequence_en : std_logic := '0'; + signal stability_signal, next_signal : std_logic := '0'; + signal event_sequence : std_logic_vector(1 to 4) := (others => '0'); +begin + example_process: process is + constant my_data, reference_value : integer := 17; + variable stat, expected_stat : checker_stat_t; + variable my_checker : checker_t; + variable check_ok, found_errors : boolean; + begin + test_runner_setup(runner, runner_cfg); + + -- Introduction + -- This file contains a number of runnable examples that you can step through. Supporting information + -- is brief. It is assumed that you've already read the user guide. + + -- The default settings is to stop on error but for these examples I want to continue + -- on errors so I'm going to raise the stop level. + set_stop_level(failure); + + -- Check + -- The basic check is like a VHDL assert. The difference is that error messages are reported using the + -- VUnit logging library. + check(some_true_condition, "Expect to pass so this should not be displayed"); + check(some_false_condition, "Expected to fail"); + + -- The default log level of error can also be overridden in a specific check call. + check(some_false_condition, "This is not very good", warning); + + -- Note that every failing check will be regarded as a failure from a testing point of view regardless + -- of which level that was used for reporting. + info("Error statistics before warning: " & LF & to_string(get_checker_stat)); + check(some_false_condition, "This warning is also collected in the error statistsics.", warning); + info("Error statistics after warning: " & LF & to_string(get_checker_stat)); + + -- Logging Passing Checks + -- You can also have the check message logged on a passing check to create a debug trace. If you use + -- the result function the message becomes nice in both the passing and failing case. + show(get_logger(default_checker), display_handler, pass); + + check(some_false_condition, result("for error status flag")); + check(some_true_condition, result("for error status flag")); + + -- Message Format + -- Some checks provide a context in addition to the user message in order to help debugging. + -- A context is only given if it can provide information not already known from the type of + -- check used. For example, a failing basic check is caused by the input expression being false. + -- No need to bloat the output with such information. + check_equal(my_data - 1, reference_value, result("for my_data")); + check_equal(my_data, reference_value, result("for my_data")); + + -- Check Location + -- Check calls are also detected by the location preprocessor such that ""anonymous"" checks can be + -- more easily traced. Location preprocessing has been disabled for all checks but check_false to + -- make this example file cleaner. + check_false(some_true_condition, "Something is wrong somewhere."); + + -- Many Checkers + -- As with loggers it's possible to create many checkers, so far we've used the default one. + my_checker := new_checker("my_checker"); + + -- The default checker is not affected by my_checker errors as shown in this after - before diff. + stat := get_checker_stat; + check(my_checker, some_false_condition); + info("Changes in default checker stats after a my_checker error:" & LF & to_string(get_checker_stat - stat)); + get_checker_stat(my_checker, stat); + info("my_checker has its own stats:" & LF & to_string(stat)); + + -- Acting on Failing Checks + -- You can act on the result of a single check. Calls to the default checker are implemented with + -- both procedures and functions while calls to custom checkers have to be procedures (the checker + -- parameter is a protected type). + if check(some_true_condition) then + info("Expected to be here."); + else + info("This was not expected."); + end if; + + check(my_checker, check_ok, some_true_condition); + if check_ok then + info("Expected to be here."); + else + info("This was not expected."); + end if; + + -- You can also ask if a checker has detected any errors. + stat := get_checker_stat(default_checker); + if stat.n_failed > 0 then + info("Expected to be here."); + else + info("This was not expected."); + end if; + + if stat.n_failed > 0 then + info("Expected to be here."); + else + info("This was not expected."); + end if; + + -- Concurrent checks + -- Checks can be called concurrently as well (see bottom of file). The concurrent call for check + -- takes a std_logic condition ('1' = true) and checks that on every enabled clock_edge + -- (either rising, falling, or both). + wait until rising_edge(clk); + check_en <= '1'; + wait until falling_edge(clk); -- NOTE. Falling edge. + wait for 1 ns; + + -- Don't expect the concurrent status check to report any errors yet since it's setup to check + -- on positive edges. + + wait until rising_edge(clk); + wait for 1 ns; + + -- Now you should have seen an error report."); + check_en <= '0'; + + -- Point Checks + -- check is a point check checking a condition at a specific point in time. Here are some other + -- point checks which have the same type of subprograms as check. Only one type of each is shown here. + + ---- True Check + ---- check_true does the same thing as check but has a more verbose name and result message. + check_true(false); + check_true(true); + + ---- False Check + ---- check_false is the inverse of check_true. + check_false(true); + check_false(false); + + ---- Implication Check + ---- check_implication checks if a logical implication holds. + check_implication(true, false); + check_implication(false, true); + + ---- Not Unknown Check + ---- check_not_unknown fails when meta values are present. + check_not_unknown("00001U0"); + check_not_unknown("0000110"); + + ---- Zero One-Hot Check + ---- check_zero_one_hot requires zero or one bit to be active. + check_zero_one_hot("10001"); + check_zero_one_hot("10000"); + + ---- One-Hot Check + ---- check_one_hot requires exactly one bit to be active. + check_one_hot("00000"); + check_one_hot("10000"); + + -- Relation Checks + -- Checks are used to verify a relation, e.g. a = b. The following checks are designed to + -- target such relations. + + ---- Equality Check + ---- check_equal checks the equality between common or similar types. + check_equal(true, '0'); + check_equal(true, '1'); + check_equal(unsigned'("1000"), 7); + check_equal(-3, signed'("11100")); + check_equal(std_logic_vector'("101010"), std_logic_vector'("--1010"), + "Don't care ('-') only equals don't care."); + check_equal(17, 17); + + ---- Match Check + ---- check_match is like check_equal with the difference that don't care ('-') equals anything. + check_match(std_logic_vector'("101010"), std_logic_vector'("--1001")); + check_match(std_logic_vector'("101010"), std_logic_vector'("--1010")); + + ---- Relation Check + ---- check_relation together with the check_preprocessor will check any relation between any + ---- types and present an error context as long as the relation operator and the to_string + ---- function are defined for those types. + stat := get_checker_stat; + expected_stat := (0, 0, 0); + check_relation(stat = expected_stat, result("for default checker statistics")); + expected_stat := stat + (1, 1, 0); + check_relation(get_checker_stat = expected_stat, result("for default checker statistics")); + + -- Sequence Checks + -- Sequence checks need several clock cycles to determine if the check passes or fails. + -- The following types are provided: + + ---- Stability Check + ---- check_stable checks the stability of a signal within a window. The concurrent procedure call + ---- is at the bottom of this file. + + stability_signal <= '1'; + stability_en <= '1'; + + ------ Unstable Case + start_event <= '1'; + wait until rising_edge(clk); + start_event <= '0'; + + wait until rising_edge(clk); + stability_signal <= '0'; + wait until rising_edge(clk); + + end_event <= '1'; + wait until rising_edge(clk); + end_event <= '0'; + + ------ Stable Case + start_event <= '1'; + wait until rising_edge(clk); + start_event <= '0'; + + wait until rising_edge(clk); + wait until rising_edge(clk); + + end_event <= '1'; + wait until rising_edge(clk); + end_event <= '0'; + stability_en <= '0'; + wait until rising_edge(clk); + + ---- Next Check + ---- check_next checks that an event happens with a specified delay from a starting point. The + ---- concurrent procedure call is at the bottom of this file. + + ------ Too long delay + next_en <= '1'; + start_event <= '1'; + wait until rising_edge(clk); + start_event <= '0'; + + wait until rising_edge(clk); + wait until rising_edge(clk); + + next_signal <= '1'; + wait until rising_edge(clk); + next_signal <= '0'; + wait until rising_edge(clk); + + ------ Expected delay + start_event <= '1'; + wait until rising_edge(clk); + start_event <= '0'; + + wait until rising_edge(clk); + + next_signal <= '1'; + wait until rising_edge(clk); + next_signal <= '0'; + next_en <= '0'; + wait until rising_edge(clk); + + ---- Sequence Check + ---- check_sequence checks that a set of events, represented by the bits in a std_logic_vector, + ---- are activated in consecutive clock cycles. check_sequence has several modes of operation. + ---- This example uses the first_pipe mode. The concurrent procedure call is at the bottom of + ---- this file. + + ------ Broken Sequence + sequence_en <= '1'; + event_sequence <= "1000"; + wait until rising_edge(clk); + event_sequence <= "0100"; + wait until rising_edge(clk); + event_sequence <= "0000"; + wait until rising_edge(clk); + event_sequence <= "0001"; + wait until rising_edge(clk); + event_sequence <= "0000"; + wait until rising_edge(clk); + + ------ Complete Sequence + event_sequence <= "1000"; + wait until rising_edge(clk); + event_sequence <= "0100"; + wait until rising_edge(clk); + event_sequence <= "0010"; + wait until rising_edge(clk); + event_sequence <= "0001"; + wait until rising_edge(clk); + event_sequence <= "0000"; + sequence_en <= '0'; + wait until rising_edge(clk); + + -- Unconditional Checks + -- check_failed and check_passed are useful when the pass/fail status is already given by the + -- normal program flow. + if operation_completed then + check_passed("Some operation completed successfully"); + else + debug("Log some interesting information for debugging"); + check_failed("This was not expected. Read the log file for more debug information."); + end if; + + -- Wrap-Up + -- In this testbench I'm expecting failing checks and will suppress these such that the testbench pass. + -- VUnit automatically tracks the errors for the default checker but if you create your own checker + -- you have to insert its pass/fail status into the test_runner_cleanup call for those to be recognized. + expected_stat := (41, 24, 17); + stat := get_checker_stat; + if stat = expected_stat then + reset_checker_stat; + else + check_failed("Not the expected number of failing checks for the default checker:" & LF & to_string(stat)); + end if; + + expected_stat := (2, 1, 1); + get_checker_stat(my_checker, stat); + if stat = expected_stat then + reset_checker_stat(my_checker); + else + check_failed("Not the expected number of failing checks for my_checker:" & LF & to_string(stat)); + end if; + + -- We reset the log count of the checkers to avoid test suite error in this + -- example + reset_log_count(get_logger(my_checker), error); + reset_log_count(get_logger(default_checker), error); + + test_runner_cleanup(runner); + end process example_process; + + clk <= not clk after 5 ns; + + status_check: check(clk, check_en, status_ok, "Concurrent status check failed."); + stability_check : check_stable(clk, stability_en, start_event, end_event, stability_signal, result("for stability_signal.")); + next_check : check_next(clk, next_en, start_event, next_signal, result("for next_signal"), num_cks => 2); + sequence_check: check_sequence(clk, sequence_en, event_sequence, result("for event_sequence"), first_pipe); +end architecture test; diff --git a/examples/vhdl/com/run.py b/examples/vhdl/com/run.py index c068131e5..4a23aea93 100644 --- a/examples/vhdl/com/run.py +++ b/examples/vhdl/com/run.py @@ -1,21 +1,21 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -prj = VUnit.from_argv() -prj.add_com() -prj.add_verification_components() -prj.add_osvvm() - -lib = prj.add_library('lib') -lib.add_source_files(join(dirname(__file__), 'src', '*.vhd')) - -tb_lib = prj.add_library('tb_lib') -tb_lib.add_source_files(join(dirname(__file__), 'test', '*.vhd')) - -prj.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +prj = VUnit.from_argv() +prj.add_com() +prj.add_verification_components() +prj.add_osvvm() + +lib = prj.add_library('lib') +lib.add_source_files(join(dirname(__file__), 'src', '*.vhd')) + +tb_lib = prj.add_library('tb_lib') +tb_lib.add_source_files(join(dirname(__file__), 'test', '*.vhd')) + +prj.main() diff --git a/examples/vhdl/com/src/adder.vhd b/examples/vhdl/com/src/adder.vhd index 82d457db5..5c5cc1c2d 100644 --- a/examples/vhdl/com/src/adder.vhd +++ b/examples/vhdl/com/src/adder.vhd @@ -1,30 +1,30 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -entity adder is - port( - clk : in std_logic; - op_a : in unsigned(7 downto 0); - op_b : in unsigned(7 downto 0); - dv_in : in std_logic; - sum : out unsigned(8 downto 0); - dv_out : out std_logic - ); -end entity adder; - -architecture a of adder is -begin - main : process is - begin - wait until rising_edge(clk); - sum <= resize(op_a, sum) + resize(op_b, sum); - dv_out <= dv_in; - end process main; -end architecture a; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity adder is + port( + clk : in std_logic; + op_a : in unsigned(7 downto 0); + op_b : in unsigned(7 downto 0); + dv_in : in std_logic; + sum : out unsigned(8 downto 0); + dv_out : out std_logic + ); +end entity adder; + +architecture a of adder is +begin + main : process is + begin + wait until rising_edge(clk); + sum <= resize(op_a, sum) + resize(op_b, sum); + dv_out <= dv_in; + end process main; +end architecture a; diff --git a/examples/vhdl/com/test/memory_bfm.vhd b/examples/vhdl/com/test/memory_bfm.vhd index fa459934f..5fc001d9f 100644 --- a/examples/vhdl/com/test/memory_bfm.vhd +++ b/examples/vhdl/com/test/memory_bfm.vhd @@ -1,69 +1,69 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; -context vunit_lib.com_context; -use vunit_lib.sync_pkg.all; - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use ieee.numeric_std_unsigned.all; - -use work.memory_bfm_pkg.all; - -entity memory_bfm is -end entity memory_bfm; - -architecture a of memory_bfm is -begin - message_handler : process is - variable request_msg, reply_msg : msg_t; - variable msg_type : msg_type_t; - variable address : unsigned(7 downto 0); - variable data : std_logic_vector(7 downto 0); - variable memory : integer_vector(0 to 255) := (others => 0); - - procedure emulate_memory_access_delay is - begin - wait for 10 ns; - end procedure; - begin - receive(net, actor, request_msg); - msg_type := message_type(request_msg); - - handle_sync_message(net, msg_type, request_msg); - - if msg_type = write_msg then - address := pop(request_msg); - data := pop(request_msg); - debug(memory_bfm_logger, "Writing x""" & to_hstring(data) & """ to address x""" & to_hstring(address) & """"); - emulate_memory_access_delay; - memory(to_integer(address)) := to_integer(data); - - elsif msg_type = write_with_acknowledge_msg then - address := pop(request_msg); - data := pop(request_msg); - debug(memory_bfm_logger, "Writing x""" & to_hstring(data) & """ to address x""" & to_hstring(address) & """"); - emulate_memory_access_delay; - memory(to_integer(address)) := to_integer(data); - acknowledge(net, request_msg, true); - - elsif msg_type = read_msg then - address := pop(request_msg); - emulate_memory_access_delay; - data := to_std_logic_vector(memory(to_integer(address)), 8); - debug(memory_bfm_logger, "Reading x""" & to_hstring(data) & """ from address x""" & to_hstring(address) & """"); - reply_msg := new_msg(read_reply_msg); - push(reply_msg, data); - reply(net, request_msg, reply_msg); - - else - unexpected_msg_type(msg_type); - end if; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +use vunit_lib.sync_pkg.all; + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.numeric_std_unsigned.all; + +use work.memory_bfm_pkg.all; + +entity memory_bfm is +end entity memory_bfm; + +architecture a of memory_bfm is +begin + message_handler : process is + variable request_msg, reply_msg : msg_t; + variable msg_type : msg_type_t; + variable address : unsigned(7 downto 0); + variable data : std_logic_vector(7 downto 0); + variable memory : integer_vector(0 to 255) := (others => 0); + + procedure emulate_memory_access_delay is + begin + wait for 10 ns; + end procedure; + begin + receive(net, actor, request_msg); + msg_type := message_type(request_msg); + + handle_sync_message(net, msg_type, request_msg); + + if msg_type = write_msg then + address := pop(request_msg); + data := pop(request_msg); + debug(memory_bfm_logger, "Writing x""" & to_hstring(data) & """ to address x""" & to_hstring(address) & """"); + emulate_memory_access_delay; + memory(to_integer(address)) := to_integer(data); + + elsif msg_type = write_with_acknowledge_msg then + address := pop(request_msg); + data := pop(request_msg); + debug(memory_bfm_logger, "Writing x""" & to_hstring(data) & """ to address x""" & to_hstring(address) & """"); + emulate_memory_access_delay; + memory(to_integer(address)) := to_integer(data); + acknowledge(net, request_msg, true); + + elsif msg_type = read_msg then + address := pop(request_msg); + emulate_memory_access_delay; + data := to_std_logic_vector(memory(to_integer(address)), 8); + debug(memory_bfm_logger, "Reading x""" & to_hstring(data) & """ from address x""" & to_hstring(address) & """"); + reply_msg := new_msg(read_reply_msg); + push(reply_msg, data); + reply(net, request_msg, reply_msg); + + else + unexpected_msg_type(msg_type); + end if; + end process; +end architecture; diff --git a/examples/vhdl/com/test/memory_bfm_pkg.vhd b/examples/vhdl/com/test/memory_bfm_pkg.vhd index a52c82fee..0f43e080e 100644 --- a/examples/vhdl/com/test/memory_bfm_pkg.vhd +++ b/examples/vhdl/com/test/memory_bfm_pkg.vhd @@ -1,112 +1,112 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; -context vunit_lib.com_context; - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -package memory_bfm_pkg is - constant write_msg : msg_type_t := new_msg_type("write"); - constant write_with_acknowledge_msg : msg_type_t := new_msg_type("write with acknowledge"); - constant read_msg : msg_type_t := new_msg_type("read"); - constant read_reply_msg : msg_type_t := new_msg_type("read reply"); - constant actor : actor_t := new_actor("memory BFM"); - constant memory_bfm_logger : logger_t := get_logger("memory BFM"); - - procedure write( - signal net : inout network_t; - constant address : in unsigned(7 downto 0); - constant data : in std_logic_vector(7 downto 0)); - - procedure blocking_write( - signal net : inout network_t; - constant address : in unsigned(7 downto 0); - constant data : in std_logic_vector(7 downto 0)); - - procedure read( - signal net : inout network_t; - constant address : in unsigned(7 downto 0); - variable future : out msg_t); - alias non_blocking_read is read[network_t, unsigned, msg_t]; - - procedure get( - signal net : inout network_t; - variable future : inout msg_t; - variable data : out std_logic_vector(7 downto 0)); - - procedure read( - signal net : inout network_t; - constant address : in unsigned(7 downto 0); - variable data : out std_logic_vector(7 downto 0)); - alias blocking_read is read[network_t, unsigned, std_logic_vector]; - -end package memory_bfm_pkg; - -package body memory_bfm_pkg is - procedure write( - signal net : inout network_t; - constant address : in unsigned(7 downto 0); - constant data : in std_logic_vector(7 downto 0)) is - variable msg : msg_t := new_msg(write_msg); - begin - push(msg, address); - push(msg, data); - send(net, actor, msg); - end; - - procedure blocking_write( - signal net : inout network_t; - constant address : in unsigned(7 downto 0); - constant data : in std_logic_vector(7 downto 0)) is - variable msg : msg_t := new_msg(write_with_acknowledge_msg); - variable positive_ack : boolean; - begin - push(msg, address); - push(msg, data); - request(net, actor, msg, positive_ack); - check(positive_ack, "Write failed"); - end; - - procedure read( - signal net : inout network_t; - constant address : in unsigned(7 downto 0); - variable future : out msg_t) is - variable request_msg : msg_t := new_msg(read_msg); - variable reply_msg : msg_t; - begin - push(request_msg, address); - send(net, actor, request_msg); - future := request_msg; - end; - - procedure get( - signal net : inout network_t; - variable future : inout msg_t; - variable data : out std_logic_vector(7 downto 0)) is - variable reply_msg : msg_t; - variable msg_type : msg_type_t; - begin - receive_reply(net, future, reply_msg); - msg_type := message_type(reply_msg); - data := pop(reply_msg); - delete(reply_msg); - end; - - procedure read( - signal net : inout network_t; - constant address : in unsigned(7 downto 0); - variable data : out std_logic_vector(7 downto 0)) is - variable future : msg_t; - begin - read(net, address, future); - get(net, future, data); - end; - -end package body memory_bfm_pkg; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +package memory_bfm_pkg is + constant write_msg : msg_type_t := new_msg_type("write"); + constant write_with_acknowledge_msg : msg_type_t := new_msg_type("write with acknowledge"); + constant read_msg : msg_type_t := new_msg_type("read"); + constant read_reply_msg : msg_type_t := new_msg_type("read reply"); + constant actor : actor_t := new_actor("memory BFM"); + constant memory_bfm_logger : logger_t := get_logger("memory BFM"); + + procedure write( + signal net : inout network_t; + constant address : in unsigned(7 downto 0); + constant data : in std_logic_vector(7 downto 0)); + + procedure blocking_write( + signal net : inout network_t; + constant address : in unsigned(7 downto 0); + constant data : in std_logic_vector(7 downto 0)); + + procedure read( + signal net : inout network_t; + constant address : in unsigned(7 downto 0); + variable future : out msg_t); + alias non_blocking_read is read[network_t, unsigned, msg_t]; + + procedure get( + signal net : inout network_t; + variable future : inout msg_t; + variable data : out std_logic_vector(7 downto 0)); + + procedure read( + signal net : inout network_t; + constant address : in unsigned(7 downto 0); + variable data : out std_logic_vector(7 downto 0)); + alias blocking_read is read[network_t, unsigned, std_logic_vector]; + +end package memory_bfm_pkg; + +package body memory_bfm_pkg is + procedure write( + signal net : inout network_t; + constant address : in unsigned(7 downto 0); + constant data : in std_logic_vector(7 downto 0)) is + variable msg : msg_t := new_msg(write_msg); + begin + push(msg, address); + push(msg, data); + send(net, actor, msg); + end; + + procedure blocking_write( + signal net : inout network_t; + constant address : in unsigned(7 downto 0); + constant data : in std_logic_vector(7 downto 0)) is + variable msg : msg_t := new_msg(write_with_acknowledge_msg); + variable positive_ack : boolean; + begin + push(msg, address); + push(msg, data); + request(net, actor, msg, positive_ack); + check(positive_ack, "Write failed"); + end; + + procedure read( + signal net : inout network_t; + constant address : in unsigned(7 downto 0); + variable future : out msg_t) is + variable request_msg : msg_t := new_msg(read_msg); + variable reply_msg : msg_t; + begin + push(request_msg, address); + send(net, actor, request_msg); + future := request_msg; + end; + + procedure get( + signal net : inout network_t; + variable future : inout msg_t; + variable data : out std_logic_vector(7 downto 0)) is + variable reply_msg : msg_t; + variable msg_type : msg_type_t; + begin + receive_reply(net, future, reply_msg); + msg_type := message_type(reply_msg); + data := pop(reply_msg); + delete(reply_msg); + end; + + procedure read( + signal net : inout network_t; + constant address : in unsigned(7 downto 0); + variable data : out std_logic_vector(7 downto 0)) is + variable future : msg_t; + begin + read(net, address, future); + get(net, future, data); + end; + +end package body memory_bfm_pkg; diff --git a/examples/vhdl/com/test/tb_user_guide.vhd b/examples/vhdl/com/test/tb_user_guide.vhd index a9902fde6..fc1831ea2 100644 --- a/examples/vhdl/com/test/tb_user_guide.vhd +++ b/examples/vhdl/com/test/tb_user_guide.vhd @@ -1,321 +1,321 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; -context vunit_lib.com_context; -use vunit_lib.sync_pkg.all; - -library lib; - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -use work.memory_bfm_pkg; - -library osvvm; -use osvvm.RandomPkg.all; - -entity tb_user_guide is - generic ( - runner_cfg : string); -end entity tb_user_guide; - -architecture a of tb_user_guide is - signal clk : std_logic := '0'; - signal op_a, op_b : unsigned(7 downto 0); - signal sum : unsigned(8 downto 0); - signal dv_in, dv_out : std_logic := '0'; - - constant driver : actor_t := new_actor("driver"); - constant monitor : actor_t := new_actor("monitor"); - constant master_channel : actor_t := new_actor("driver channel"); - constant slave_channel : actor_t := new_actor("monitor channel"); - constant add_msg : msg_type_t := new_msg_type("add"); - constant sum_msg : msg_type_t := new_msg_type("sum"); - constant my_receiver : actor_t := new_actor("my receiver"); - constant test_sequencer : actor_t := new_actor("test sequencer"); - constant channels : actor_vec_t(1 to 2) := (new_actor("channel 1"), new_actor("channel 2")); - constant clk_period : time := 10 ns; -begin - test_runner : process - variable msg, request_msg, reply_msg : msg_t; - variable rnd : RandomPType; - constant found_receiver : actor_t := find("my receiver"); - constant my_integer : integer := 17; - constant my_unsigned_address : unsigned(7 downto 0) := x"80"; - constant my_natural_address : natural := 17; - constant my_std_logic_vector_data : std_logic_vector(7 downto 0) := x"21"; - variable data : std_logic_vector(7 downto 0); - variable future : msg_t; - constant sending_actor : actor_t := new_actor("Sending actor"); - variable status : com_status_t; - variable start : time; - constant my_actor : actor_t := new_actor("My actor"); - variable msg_type : msg_type_t; - variable mailbox_state : mailbox_state_t; - variable actor_state : actor_state_t; - variable messenger_state : messenger_state_t; - begin - test_runner_setup(runner, runner_cfg); - show(display_handler, pass); - - while test_suite loop - if run("Test sending a message to a known actor") then - msg := new_msg; - push_string(msg, "10101010"); - push(msg, my_integer); - send(net, my_receiver, msg); - - elsif run("Test sending a message to a found actor") then - msg := new_msg; - push_string(msg, "10101010"); - push(msg, my_integer); - send(net, found_receiver, msg); - - elsif run("Test sending messages with a message type with a name identical to another message type") then - msg := new_msg(memory_bfm_pkg.write_msg); - push(msg, my_unsigned_address); - push(msg, my_std_logic_vector_data); - send(net, memory_bfm_pkg.actor, msg); - - elsif run("Test encapsulating message passing details in transaction procedures") then - memory_bfm_pkg.write(net, address => x"80", data => x"21"); - - elsif run("Test requesting information") then - memory_bfm_pkg.write(net, address => x"80", data => x"21"); - request_msg := new_msg(memory_bfm_pkg.read_msg); - push(request_msg, unsigned'(x"80")); - request(net, memory_bfm_pkg.actor, request_msg, reply_msg); - msg_type := message_type(reply_msg); - data := pop(reply_msg); - check_equal(data, std_logic_vector'(x"21")); - - elsif run("Test sending/receiving a reply") then - memory_bfm_pkg.write(net, address => x"80", data => x"21"); - memory_bfm_pkg.read(net, address => x"80", data => data); - check_equal(data, std_logic_vector'(x"21")); - - elsif run("Test blocking and non-blocking transactions") then - memory_bfm_pkg.write(net, address => x"80", data => x"21"); - memory_bfm_pkg.write(net, address => x"84", data => x"17"); - memory_bfm_pkg.non_blocking_read(net, address => x"80", future => future); - memory_bfm_pkg.blocking_read(net, address => x"84", data => data); - check_equal(data, std_logic_vector'(x"17")); - memory_bfm_pkg.get(net, future, data); - check_equal(data, std_logic_vector'(x"21")); - - elsif run("Test blocking on acknowledge") then - start := now; - memory_bfm_pkg.write(net, address => x"80", data => x"21"); - check_equal(now, start); - start := now; - memory_bfm_pkg.blocking_write(net, address => x"84", data => x"17"); - check(now > start); - - elsif run("Synchronize with a rendezvous") then - memory_bfm_pkg.write(net, address => x"80", data => x"21"); - info("Synchronizing at " & to_string(now)); - wait_until_idle(net, memory_bfm_pkg.actor); - info("Synchronized at " & to_string(now)); - - elsif run("Test timeout") then - wait_for_message(net, my_actor, status, timeout => 10 ns); - check(status = timeout, result("for status = timeout when no messages have been sent")); - check(not has_message(my_actor), result("for no presence of messages when no messages have been sent")); - - msg := new_msg; - push_string(msg, "hello"); - send(net, my_actor, msg); - - wait_for_message(net, my_actor, status, timeout => 10 ns); - check(status = ok, result("for status = ok when a message has been sent")); - check(has_message(my_actor), result("for presence of messages when a message has been sent")); - get_message(net, my_actor, msg); - check_equal(pop_string(msg), "hello"); - - elsif run("Test actor with multiple channels (actors)") then - msg := new_msg; - push_string(msg, "alpha"); - send(net, channels(1), msg); - wait for 10 ns; - msg := new_msg; - push_string(msg, "beta"); - send(net, channels(2), msg); - - elsif run("Signing messages") then - memory_bfm_pkg.write(net, address => x"80", data => x"21"); - memory_bfm_pkg.non_blocking_read(net, address => x"80", future => future); - - wait_for_message(net, sending_actor, status, timeout => 1 ns); - check(status = timeout, "The read request reply shouldn't be able to find it's way to the sender's inbox"); - - memory_bfm_pkg.write(net, address => x"84", data => x"17"); - msg := new_msg(memory_bfm_pkg.read_msg, sending_actor); - push(msg, unsigned'(x"84")); - send(net, memory_bfm_pkg.actor, msg); - receive(net, sending_actor, msg, timeout => 100 ns); - msg_type := message_type(msg); - data := pop(msg); - check_equal(data, std_logic_vector'(x"17")); - - elsif run("Test publisher/subscriber") then - for i in 1 to 10 loop - msg := new_msg(add_msg); - push(msg, rnd.RandInt(0, 255)); - push(msg, rnd.RandInt(0, 255)); - send(net, driver, msg); - wait_for_time(net, driver, rnd.RandTime(0 ns, 10 * clk_period)); - end loop; - wait_until_idle(net, master_channel); - - elsif run("Test debugging features") then - show(com_logger, display_handler, trace); - show(display_handler, debug); - - msg := new_msg(memory_bfm_pkg.write_msg); - push(msg, my_unsigned_address); - push(msg, my_std_logic_vector_data); - send(net, memory_bfm_pkg.actor, msg); - request_msg := new_msg(memory_bfm_pkg.read_msg, test_sequencer); - push(request_msg, unsigned'(x"80")); - send(net, memory_bfm_pkg.actor, request_msg); - wait for 30 ns; - receive_reply(net, request_msg, reply_msg); - - memory_bfm_pkg.write(net, address => x"80", data => x"17"); - memory_bfm_pkg.write(net, address => x"84", data => x"21"); - memory_bfm_pkg.non_blocking_read(net, address => x"80", future => future); - - mailbox_state := get_mailbox_state(memory_bfm_pkg.actor, inbox); - debug(get_mailbox_state_string(memory_bfm_pkg.actor, inbox)); - - memory_bfm_pkg.get(net, future, data); - - for i in 1 to 3 loop - msg := new_msg(add_msg); - push(msg, rnd.RandInt(0, 255)); - push(msg, rnd.RandInt(0, 255)); - send(net, driver, msg); - end loop; - actor_state := get_actor_state(driver); - debug(get_actor_state_string(driver)); - - messenger_state := get_messenger_state; - debug(get_messenger_state_string); - end if; - end loop; - - wait for 100 ns; - test_runner_cleanup(runner); - wait; - end process; - - my_receiver_process : process - variable msg : msg_t; - variable my_integer : integer; - begin - receive(net, my_receiver, msg); - check_equal(pop_string(msg), "10101010"); - my_integer := pop(msg); - check_equal(my_integer, 17); - end process; - - multiple_channel_process : process is - variable status : com_status_t; - variable msg : msg_t; - begin - wait_for_message(net, channels, status); - if status = ok then - for i in channels'range loop - if has_message(channels(i)) then - get_message(net, channels(i), msg); - info("Received " & pop_string(msg) & " on " & name(channels(i))); - end if; - end loop; - end if; - end process; - - memory_bfm : entity work.memory_bfm; - - test_runner_watchdog(runner, 500 ms); - - clk <= not clk after clk_period / 2; - - driver_process : process is - variable request_msg : msg_t; - variable msg_type : msg_type_t; - begin - receive(net, driver, request_msg); - msg_type := message_type(request_msg); - - handle_wait_for_time(net, msg_type, request_msg); - - if msg_type = add_msg then - op_a <= to_unsigned(pop(request_msg), op_a); - op_b <= to_unsigned(pop(request_msg), op_a); - dv_in <= '1'; - wait until rising_edge(clk); - dv_in <= '0'; - else - unexpected_msg_type(msg_type); - end if; - end process; - - monitor_process : process is - variable msg : msg_t; - begin - wait until rising_edge(clk) and (dv_out = '1'); - msg := new_msg(sum_msg); - push(msg, to_integer(sum)); - publish(net, monitor, msg); - end process; - - scoreboard_process : process is - variable master_msg, slave_msg : msg_t; - variable msg_type : msg_type_t; - - procedure do_model_check(indata, outdata : msg_t) is - variable op_a, op_b, sum : natural; - begin - op_a := pop(indata); - op_b := pop(indata); - sum := pop(outdata); - check_equal(sum, op_a + op_b); - end; - begin - subscribe(master_channel, driver, inbound); - subscribe(slave_channel, monitor); - loop - receive(net, master_channel, master_msg); - msg_type := message_type(master_msg); - - if receiver(master_msg) = master_channel then - handle_wait_until_idle(net, msg_type, master_msg); - end if; - - if msg_type = add_msg then - receive(net, slave_channel, slave_msg); - - if message_type(slave_msg) = sum_msg then - do_model_check(master_msg, slave_msg); - end if; - end if; - end loop; - end process; - - adder : entity lib.adder - port map( - clk => clk, - op_a => op_a, - op_b => op_b, - dv_in => dv_in, - sum => sum, - dv_out => dv_out - ); - -end; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +use vunit_lib.sync_pkg.all; + +library lib; + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.memory_bfm_pkg; + +library osvvm; +use osvvm.RandomPkg.all; + +entity tb_user_guide is + generic ( + runner_cfg : string); +end entity tb_user_guide; + +architecture a of tb_user_guide is + signal clk : std_logic := '0'; + signal op_a, op_b : unsigned(7 downto 0); + signal sum : unsigned(8 downto 0); + signal dv_in, dv_out : std_logic := '0'; + + constant driver : actor_t := new_actor("driver"); + constant monitor : actor_t := new_actor("monitor"); + constant master_channel : actor_t := new_actor("driver channel"); + constant slave_channel : actor_t := new_actor("monitor channel"); + constant add_msg : msg_type_t := new_msg_type("add"); + constant sum_msg : msg_type_t := new_msg_type("sum"); + constant my_receiver : actor_t := new_actor("my receiver"); + constant test_sequencer : actor_t := new_actor("test sequencer"); + constant channels : actor_vec_t(1 to 2) := (new_actor("channel 1"), new_actor("channel 2")); + constant clk_period : time := 10 ns; +begin + test_runner : process + variable msg, request_msg, reply_msg : msg_t; + variable rnd : RandomPType; + constant found_receiver : actor_t := find("my receiver"); + constant my_integer : integer := 17; + constant my_unsigned_address : unsigned(7 downto 0) := x"80"; + constant my_natural_address : natural := 17; + constant my_std_logic_vector_data : std_logic_vector(7 downto 0) := x"21"; + variable data : std_logic_vector(7 downto 0); + variable future : msg_t; + constant sending_actor : actor_t := new_actor("Sending actor"); + variable status : com_status_t; + variable start : time; + constant my_actor : actor_t := new_actor("My actor"); + variable msg_type : msg_type_t; + variable mailbox_state : mailbox_state_t; + variable actor_state : actor_state_t; + variable messenger_state : messenger_state_t; + begin + test_runner_setup(runner, runner_cfg); + show(display_handler, pass); + + while test_suite loop + if run("Test sending a message to a known actor") then + msg := new_msg; + push_string(msg, "10101010"); + push(msg, my_integer); + send(net, my_receiver, msg); + + elsif run("Test sending a message to a found actor") then + msg := new_msg; + push_string(msg, "10101010"); + push(msg, my_integer); + send(net, found_receiver, msg); + + elsif run("Test sending messages with a message type with a name identical to another message type") then + msg := new_msg(memory_bfm_pkg.write_msg); + push(msg, my_unsigned_address); + push(msg, my_std_logic_vector_data); + send(net, memory_bfm_pkg.actor, msg); + + elsif run("Test encapsulating message passing details in transaction procedures") then + memory_bfm_pkg.write(net, address => x"80", data => x"21"); + + elsif run("Test requesting information") then + memory_bfm_pkg.write(net, address => x"80", data => x"21"); + request_msg := new_msg(memory_bfm_pkg.read_msg); + push(request_msg, unsigned'(x"80")); + request(net, memory_bfm_pkg.actor, request_msg, reply_msg); + msg_type := message_type(reply_msg); + data := pop(reply_msg); + check_equal(data, std_logic_vector'(x"21")); + + elsif run("Test sending/receiving a reply") then + memory_bfm_pkg.write(net, address => x"80", data => x"21"); + memory_bfm_pkg.read(net, address => x"80", data => data); + check_equal(data, std_logic_vector'(x"21")); + + elsif run("Test blocking and non-blocking transactions") then + memory_bfm_pkg.write(net, address => x"80", data => x"21"); + memory_bfm_pkg.write(net, address => x"84", data => x"17"); + memory_bfm_pkg.non_blocking_read(net, address => x"80", future => future); + memory_bfm_pkg.blocking_read(net, address => x"84", data => data); + check_equal(data, std_logic_vector'(x"17")); + memory_bfm_pkg.get(net, future, data); + check_equal(data, std_logic_vector'(x"21")); + + elsif run("Test blocking on acknowledge") then + start := now; + memory_bfm_pkg.write(net, address => x"80", data => x"21"); + check_equal(now, start); + start := now; + memory_bfm_pkg.blocking_write(net, address => x"84", data => x"17"); + check(now > start); + + elsif run("Synchronize with a rendezvous") then + memory_bfm_pkg.write(net, address => x"80", data => x"21"); + info("Synchronizing at " & to_string(now)); + wait_until_idle(net, memory_bfm_pkg.actor); + info("Synchronized at " & to_string(now)); + + elsif run("Test timeout") then + wait_for_message(net, my_actor, status, timeout => 10 ns); + check(status = timeout, result("for status = timeout when no messages have been sent")); + check(not has_message(my_actor), result("for no presence of messages when no messages have been sent")); + + msg := new_msg; + push_string(msg, "hello"); + send(net, my_actor, msg); + + wait_for_message(net, my_actor, status, timeout => 10 ns); + check(status = ok, result("for status = ok when a message has been sent")); + check(has_message(my_actor), result("for presence of messages when a message has been sent")); + get_message(net, my_actor, msg); + check_equal(pop_string(msg), "hello"); + + elsif run("Test actor with multiple channels (actors)") then + msg := new_msg; + push_string(msg, "alpha"); + send(net, channels(1), msg); + wait for 10 ns; + msg := new_msg; + push_string(msg, "beta"); + send(net, channels(2), msg); + + elsif run("Signing messages") then + memory_bfm_pkg.write(net, address => x"80", data => x"21"); + memory_bfm_pkg.non_blocking_read(net, address => x"80", future => future); + + wait_for_message(net, sending_actor, status, timeout => 1 ns); + check(status = timeout, "The read request reply shouldn't be able to find it's way to the sender's inbox"); + + memory_bfm_pkg.write(net, address => x"84", data => x"17"); + msg := new_msg(memory_bfm_pkg.read_msg, sending_actor); + push(msg, unsigned'(x"84")); + send(net, memory_bfm_pkg.actor, msg); + receive(net, sending_actor, msg, timeout => 100 ns); + msg_type := message_type(msg); + data := pop(msg); + check_equal(data, std_logic_vector'(x"17")); + + elsif run("Test publisher/subscriber") then + for i in 1 to 10 loop + msg := new_msg(add_msg); + push(msg, rnd.RandInt(0, 255)); + push(msg, rnd.RandInt(0, 255)); + send(net, driver, msg); + wait_for_time(net, driver, rnd.RandTime(0 ns, 10 * clk_period)); + end loop; + wait_until_idle(net, master_channel); + + elsif run("Test debugging features") then + show(com_logger, display_handler, trace); + show(display_handler, debug); + + msg := new_msg(memory_bfm_pkg.write_msg); + push(msg, my_unsigned_address); + push(msg, my_std_logic_vector_data); + send(net, memory_bfm_pkg.actor, msg); + request_msg := new_msg(memory_bfm_pkg.read_msg, test_sequencer); + push(request_msg, unsigned'(x"80")); + send(net, memory_bfm_pkg.actor, request_msg); + wait for 30 ns; + receive_reply(net, request_msg, reply_msg); + + memory_bfm_pkg.write(net, address => x"80", data => x"17"); + memory_bfm_pkg.write(net, address => x"84", data => x"21"); + memory_bfm_pkg.non_blocking_read(net, address => x"80", future => future); + + mailbox_state := get_mailbox_state(memory_bfm_pkg.actor, inbox); + debug(get_mailbox_state_string(memory_bfm_pkg.actor, inbox)); + + memory_bfm_pkg.get(net, future, data); + + for i in 1 to 3 loop + msg := new_msg(add_msg); + push(msg, rnd.RandInt(0, 255)); + push(msg, rnd.RandInt(0, 255)); + send(net, driver, msg); + end loop; + actor_state := get_actor_state(driver); + debug(get_actor_state_string(driver)); + + messenger_state := get_messenger_state; + debug(get_messenger_state_string); + end if; + end loop; + + wait for 100 ns; + test_runner_cleanup(runner); + wait; + end process; + + my_receiver_process : process + variable msg : msg_t; + variable my_integer : integer; + begin + receive(net, my_receiver, msg); + check_equal(pop_string(msg), "10101010"); + my_integer := pop(msg); + check_equal(my_integer, 17); + end process; + + multiple_channel_process : process is + variable status : com_status_t; + variable msg : msg_t; + begin + wait_for_message(net, channels, status); + if status = ok then + for i in channels'range loop + if has_message(channels(i)) then + get_message(net, channels(i), msg); + info("Received " & pop_string(msg) & " on " & name(channels(i))); + end if; + end loop; + end if; + end process; + + memory_bfm : entity work.memory_bfm; + + test_runner_watchdog(runner, 500 ms); + + clk <= not clk after clk_period / 2; + + driver_process : process is + variable request_msg : msg_t; + variable msg_type : msg_type_t; + begin + receive(net, driver, request_msg); + msg_type := message_type(request_msg); + + handle_wait_for_time(net, msg_type, request_msg); + + if msg_type = add_msg then + op_a <= to_unsigned(pop(request_msg), op_a); + op_b <= to_unsigned(pop(request_msg), op_a); + dv_in <= '1'; + wait until rising_edge(clk); + dv_in <= '0'; + else + unexpected_msg_type(msg_type); + end if; + end process; + + monitor_process : process is + variable msg : msg_t; + begin + wait until rising_edge(clk) and (dv_out = '1'); + msg := new_msg(sum_msg); + push(msg, to_integer(sum)); + publish(net, monitor, msg); + end process; + + scoreboard_process : process is + variable master_msg, slave_msg : msg_t; + variable msg_type : msg_type_t; + + procedure do_model_check(indata, outdata : msg_t) is + variable op_a, op_b, sum : natural; + begin + op_a := pop(indata); + op_b := pop(indata); + sum := pop(outdata); + check_equal(sum, op_a + op_b); + end; + begin + subscribe(master_channel, driver, inbound); + subscribe(slave_channel, monitor); + loop + receive(net, master_channel, master_msg); + msg_type := message_type(master_msg); + + if receiver(master_msg) = master_channel then + handle_wait_until_idle(net, msg_type, master_msg); + end if; + + if msg_type = add_msg then + receive(net, slave_channel, slave_msg); + + if message_type(slave_msg) = sum_msg then + do_model_check(master_msg, slave_msg); + end if; + end if; + end loop; + end process; + + adder : entity lib.adder + port map( + clk => clk, + op_a => op_a, + op_b => op_b, + dv_in => dv_in, + sum => sum, + dv_out => dv_out + ); + +end; diff --git a/examples/vhdl/composite_generics/run.py b/examples/vhdl/composite_generics/run.py index 4271e3b1a..c746d2eed 100644 --- a/examples/vhdl/composite_generics/run.py +++ b/examples/vhdl/composite_generics/run.py @@ -1,29 +1,29 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -prj = VUnit.from_argv() - -tb_lib = prj.add_library('tb_lib') -tb_lib.add_source_files(join(dirname(__file__), 'test', '*.vhd')) - -testbench = tb_lib.test_bench("tb_composite_generics") -test_1 = testbench.test("Test 1") - - -def encode(tb_cfg): - return ", ".join(["%s:%s" % (key, str(tb_cfg[key])) for key in tb_cfg]) - - -vga_tb_cfg = dict(image_width=640, image_height=480, dump_debug_data=False) -test_1.add_config(name='VGA', generics=dict(encoded_tb_cfg=encode(vga_tb_cfg))) - -tiny_tb_cfg = dict(image_width=4, image_height=3, dump_debug_data=True) -test_1.add_config(name='tiny', generics=dict(encoded_tb_cfg=encode(tiny_tb_cfg))) - -prj.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +prj = VUnit.from_argv() + +tb_lib = prj.add_library('tb_lib') +tb_lib.add_source_files(join(dirname(__file__), 'test', '*.vhd')) + +testbench = tb_lib.test_bench("tb_composite_generics") +test_1 = testbench.test("Test 1") + + +def encode(tb_cfg): + return ", ".join(["%s:%s" % (key, str(tb_cfg[key])) for key in tb_cfg]) + + +vga_tb_cfg = dict(image_width=640, image_height=480, dump_debug_data=False) +test_1.add_config(name='VGA', generics=dict(encoded_tb_cfg=encode(vga_tb_cfg))) + +tiny_tb_cfg = dict(image_width=4, image_height=3, dump_debug_data=True) +test_1.add_config(name='tiny', generics=dict(encoded_tb_cfg=encode(tiny_tb_cfg))) + +prj.main() diff --git a/examples/vhdl/composite_generics/test/tb_composite_generics.vhd b/examples/vhdl/composite_generics/test/tb_composite_generics.vhd index 015177d6f..912d3c294 100644 --- a/examples/vhdl/composite_generics/test/tb_composite_generics.vhd +++ b/examples/vhdl/composite_generics/test/tb_composite_generics.vhd @@ -1,73 +1,73 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com -library ieee; -use ieee.std_logic_1164.all; - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_composite_generics is - generic ( - encoded_tb_cfg : string; - runner_cfg : string); -end tb_composite_generics; - -architecture tb of tb_composite_generics is - type tb_cfg_t is record - image_width : positive; - image_height : positive; - dump_debug_data : boolean; - end record tb_cfg_t; - - impure function decode(encoded_tb_cfg : string) return tb_cfg_t is - begin - return (image_width => positive'value(get(encoded_tb_cfg, "image_width")), - image_height => positive'value(get(encoded_tb_cfg, "image_height")), - dump_debug_data => boolean'value(get(encoded_tb_cfg, "dump_debug_data"))); - end function decode; - - constant tb_cfg : tb_cfg_t := decode(encoded_tb_cfg); - - signal dumping_done : boolean := not tb_cfg.dump_debug_data; - signal data_valid : std_logic := '1'; - signal clk : std_logic := '0'; - constant clk_period : time := 2 ns; -begin - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test 1") then - check_equal(tb_cfg.image_width/4, tb_cfg.image_height/3, "Failed dummy test"); - end if; - end loop; - - if not dumping_done then - wait until dumping_done; - end if; - - test_runner_cleanup(runner); - wait; - end process test_runner; - - clk <= not clk after clk_period/2; - - dump_debug_data: if tb_cfg.dump_debug_data generate - process is - begin - for y in 0 to tb_cfg.image_height - 1 loop - for x in 0 to tb_cfg.image_width - 1 loop - wait until rising_edge(clk) and data_valid = '1'; - debug("Dumping tons of debug data"); - end loop; - end loop; - - dumping_done <= true; - wait; - end process; - end generate dump_debug_data; -end; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +library ieee; +use ieee.std_logic_1164.all; + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_composite_generics is + generic ( + encoded_tb_cfg : string; + runner_cfg : string); +end tb_composite_generics; + +architecture tb of tb_composite_generics is + type tb_cfg_t is record + image_width : positive; + image_height : positive; + dump_debug_data : boolean; + end record tb_cfg_t; + + impure function decode(encoded_tb_cfg : string) return tb_cfg_t is + begin + return (image_width => positive'value(get(encoded_tb_cfg, "image_width")), + image_height => positive'value(get(encoded_tb_cfg, "image_height")), + dump_debug_data => boolean'value(get(encoded_tb_cfg, "dump_debug_data"))); + end function decode; + + constant tb_cfg : tb_cfg_t := decode(encoded_tb_cfg); + + signal dumping_done : boolean := not tb_cfg.dump_debug_data; + signal data_valid : std_logic := '1'; + signal clk : std_logic := '0'; + constant clk_period : time := 2 ns; +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test 1") then + check_equal(tb_cfg.image_width/4, tb_cfg.image_height/3, "Failed dummy test"); + end if; + end loop; + + if not dumping_done then + wait until dumping_done; + end if; + + test_runner_cleanup(runner); + wait; + end process test_runner; + + clk <= not clk after clk_period/2; + + dump_debug_data: if tb_cfg.dump_debug_data generate + process is + begin + for y in 0 to tb_cfg.image_height - 1 loop + for x in 0 to tb_cfg.image_width - 1 loop + wait until rising_edge(clk) and data_valid = '1'; + debug("Dumping tons of debug data"); + end loop; + end loop; + + dumping_done <= true; + wait; + end process; + end generate dump_debug_data; +end; diff --git a/examples/vhdl/coverage/run.py b/examples/vhdl/coverage/run.py index 921b0e8cd..c4cb54f6a 100644 --- a/examples/vhdl/coverage/run.py +++ b/examples/vhdl/coverage/run.py @@ -1,25 +1,25 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -root = dirname(__file__) - -ui = VUnit.from_argv() -lib = ui.add_library("lib") -lib.add_source_files(join(root, "*.vhd")) - -lib.set_compile_option("rivierapro.vcom_flags", ["-coverage", "bs"]) -lib.set_compile_option("rivierapro.vlog_flags", ["-coverage", "bs"]) -lib.set_compile_option("modelsim.vcom_flags", ["+cover=bs"]) -lib.set_compile_option("modelsim.vlog_flags", ["+cover=bs"]) -lib.set_sim_option("enable_coverage", True) - -def post_run(results): - results.merge_coverage(file_name="coverage_data") - -ui.main(post_run=post_run) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +root = dirname(__file__) + +ui = VUnit.from_argv() +lib = ui.add_library("lib") +lib.add_source_files(join(root, "*.vhd")) + +lib.set_compile_option("rivierapro.vcom_flags", ["-coverage", "bs"]) +lib.set_compile_option("rivierapro.vlog_flags", ["-coverage", "bs"]) +lib.set_compile_option("modelsim.vcom_flags", ["+cover=bs"]) +lib.set_compile_option("modelsim.vlog_flags", ["+cover=bs"]) +lib.set_sim_option("enable_coverage", True) + +def post_run(results): + results.merge_coverage(file_name="coverage_data") + +ui.main(post_run=post_run) diff --git a/examples/vhdl/coverage/tb_coverage.vhd b/examples/vhdl/coverage/tb_coverage.vhd index b103fe209..92d967b30 100644 --- a/examples/vhdl/coverage/tb_coverage.vhd +++ b/examples/vhdl/coverage/tb_coverage.vhd @@ -1,35 +1,35 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -package pkg is - attribute attr : string; -end package; - -library vunit_lib; -context vunit_lib.vunit_context; - - -use work.pkg.attr; - -entity tb_coverage is - generic (runner_cfg : string); -end entity; - -architecture tb of tb_coverage is -begin - main : process - begin - test_runner_setup(runner, runner_cfg); - if false then - report "Never reached"; - end if; - - if true then - report "Hello"; - end if; - test_runner_cleanup(runner); -- Simulation ends here - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +package pkg is + attribute attr : string; +end package; + +library vunit_lib; +context vunit_lib.vunit_context; + + +use work.pkg.attr; + +entity tb_coverage is + generic (runner_cfg : string); +end entity; + +architecture tb of tb_coverage is +begin + main : process + begin + test_runner_setup(runner, runner_cfg); + if false then + report "Never reached"; + end if; + + if true then + report "Hello"; + end if; + test_runner_cleanup(runner); -- Simulation ends here + end process; +end architecture; diff --git a/examples/vhdl/generate_tests/run.py b/examples/vhdl/generate_tests/run.py index be86dabe8..1a8ad027c 100644 --- a/examples/vhdl/generate_tests/run.py +++ b/examples/vhdl/generate_tests/run.py @@ -1,75 +1,75 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from itertools import product -from vunit import VUnit - - -def make_post_check(data_width, sign): - """ - Return a check function to verify test case output - """ - - def post_check(output_path): - """ - This function recives the output_path of the test - """ - - expected = ", ".join([str(data_width), - str(sign).lower()]) + "\n" - - output_file = join(output_path, "generics.txt") - - print("Post check: %s" % output_file) - with open(output_file, "r") as fread: - got = fread.read() - if not got == expected: - print("Content mismatch, got %r expected %r" % (got, expected)) - return False - return True - - return post_check - - -def generate_tests(obj, signs, data_widths): - """ - Generate test by varying the data_width and sign generics - """ - - for sign, data_width in product(signs, data_widths): - # This configuration name is added as a suffix to the test bench name - config_name = "data_width=%i,sign=%s" % (data_width, sign) - - # Add the configuration with a post check function to verify the output - obj.add_config(name=config_name, - generics=dict( - data_width=data_width, - sign=sign), - post_check=make_post_check(data_width, sign)) - - -test_path = join(dirname(__file__), "test") - -ui = VUnit.from_argv() -lib = ui.add_library("lib") -lib.add_source_files(join(test_path, "*.vhd")) - -tb_generated = lib.test_bench("tb_generated") - -# Just set a generic for all configurations within the test bench -tb_generated.set_generic("message", "set-for-entity") - -for test in tb_generated.get_tests(): - if test.name == "Test 2": - # Test 2 should only be run with signed width of 16 - generate_tests(test, [True], [16]) - test.set_generic("message", "set-for-test") - else: - # Run all other tests with signed/unsigned and data width in range [1,5[ - generate_tests(test, [False, True], range(1, 5)) - -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from itertools import product +from vunit import VUnit + + +def make_post_check(data_width, sign): + """ + Return a check function to verify test case output + """ + + def post_check(output_path): + """ + This function recives the output_path of the test + """ + + expected = ", ".join([str(data_width), + str(sign).lower()]) + "\n" + + output_file = join(output_path, "generics.txt") + + print("Post check: %s" % output_file) + with open(output_file, "r") as fread: + got = fread.read() + if not got == expected: + print("Content mismatch, got %r expected %r" % (got, expected)) + return False + return True + + return post_check + + +def generate_tests(obj, signs, data_widths): + """ + Generate test by varying the data_width and sign generics + """ + + for sign, data_width in product(signs, data_widths): + # This configuration name is added as a suffix to the test bench name + config_name = "data_width=%i,sign=%s" % (data_width, sign) + + # Add the configuration with a post check function to verify the output + obj.add_config(name=config_name, + generics=dict( + data_width=data_width, + sign=sign), + post_check=make_post_check(data_width, sign)) + + +test_path = join(dirname(__file__), "test") + +ui = VUnit.from_argv() +lib = ui.add_library("lib") +lib.add_source_files(join(test_path, "*.vhd")) + +tb_generated = lib.test_bench("tb_generated") + +# Just set a generic for all configurations within the test bench +tb_generated.set_generic("message", "set-for-entity") + +for test in tb_generated.get_tests(): + if test.name == "Test 2": + # Test 2 should only be run with signed width of 16 + generate_tests(test, [True], [16]) + test.set_generic("message", "set-for-test") + else: + # Run all other tests with signed/unsigned and data width in range [1,5[ + generate_tests(test, [False, True], range(1, 5)) + +ui.main() diff --git a/examples/vhdl/generate_tests/test/tb_generated.vhd b/examples/vhdl/generate_tests/test/tb_generated.vhd index 25cd48755..17a74619c 100644 --- a/examples/vhdl/generate_tests/test/tb_generated.vhd +++ b/examples/vhdl/generate_tests/test/tb_generated.vhd @@ -1,49 +1,49 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use std.textio.all; - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_generated is - generic ( - runner_cfg : string; - output_path : string; - data_width : natural; - sign : boolean; - message : string); -end entity; - -architecture a of tb_generated is -begin - main : process - procedure dump_generics is - file fwrite : text; - variable l : line; - begin - file_open(fwrite, output_path & "/" & "generics.txt", write_mode); - write(l, to_string(data_width) & ", " & to_string(sign)); - writeline(fwrite, l); - file_close(fwrite); - end procedure; - begin - test_runner_setup(runner, runner_cfg); - while test_suite loop - if run("Test 1") then - assert message = "set-for-entity"; - dump_generics; - elsif run("Test 2") then - assert message = "set-for-test"; - dump_generics; - end if; - end loop; - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 10 ms); -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use std.textio.all; + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_generated is + generic ( + runner_cfg : string; + output_path : string; + data_width : natural; + sign : boolean; + message : string); +end entity; + +architecture a of tb_generated is +begin + main : process + procedure dump_generics is + file fwrite : text; + variable l : line; + begin + file_open(fwrite, output_path & "/" & "generics.txt", write_mode); + write(l, to_string(data_width) & ", " & to_string(sign)); + writeline(fwrite, l); + file_close(fwrite); + end procedure; + begin + test_runner_setup(runner, runner_cfg); + while test_suite loop + if run("Test 1") then + assert message = "set-for-entity"; + dump_generics; + elsif run("Test 2") then + assert message = "set-for-test"; + dump_generics; + end if; + end loop; + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 10 ms); +end architecture; diff --git a/examples/vhdl/json4vhdl/run.py b/examples/vhdl/json4vhdl/run.py index 2162a1a28..84ffaa2dd 100644 --- a/examples/vhdl/json4vhdl/run.py +++ b/examples/vhdl/json4vhdl/run.py @@ -1,23 +1,23 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit, read_json, encode_json - -root = dirname(__file__) - -vu = VUnit.from_argv() - -vu.add_json4vhdl() - -lib = vu.add_library("test") -lib.add_source_files(join(root, "src/test/*.vhd")) - -tb_cfg = read_json(join(root, "src/test/data/data.json")) -tb_cfg["dump_debug_data"]=False -vu.set_generic("tb_cfg", encode_json(tb_cfg)) - -vu.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit, read_json, encode_json + +root = dirname(__file__) + +vu = VUnit.from_argv() + +vu.add_json4vhdl() + +lib = vu.add_library("test") +lib.add_source_files(join(root, "src/test/*.vhd")) + +tb_cfg = read_json(join(root, "src/test/data/data.json")) +tb_cfg["dump_debug_data"]=False +vu.set_generic("tb_cfg", encode_json(tb_cfg)) + +vu.main() diff --git a/examples/vhdl/json4vhdl/src/test/tb_json_gens.vhd b/examples/vhdl/json4vhdl/src/test/tb_json_gens.vhd index a0edfca3c..2a09e0e30 100644 --- a/examples/vhdl/json4vhdl/src/test/tb_json_gens.vhd +++ b/examples/vhdl/json4vhdl/src/test/tb_json_gens.vhd @@ -1,83 +1,83 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com -library vunit_lib; -context vunit_lib.vunit_context; -library JSON; -context JSON.json_ctx; - -entity tb_json_gens is - generic ( - runner_cfg : string; - tb_path : string; - tb_cfg : string; - tb_cfg_file : string := "data/data.json" - ); -end entity; - -architecture tb of tb_json_gens is - - -- tb_cfg contains stringified content - constant JSONContent : T_JSON := jsonLoad(tb_cfg); - - -- tb_cfg is the path of a JSON file - constant JSONFileContent : T_JSON := jsonLoad(tb_path & tb_cfg_file); - - -- record to be filled by function decode - type img_t is record - image_width : positive; - image_height : positive; - dump_debug_data : boolean; - end record img_t; - - -- function to fill img_t with content extracted from a JSON input - impure function decode(Content : T_JSON) return img_t is - begin - return (image_width => positive'value( jsonGetString(Content, "Image/0") ), - image_height => positive'value( jsonGetString(Content, "Image/1") ), - dump_debug_data => jsonGetBoolean(Content, "dump_debug_data") ); - end function decode; - - constant img : img_t := decode(JSONContent); - - -- get array of integers from JSON content - constant img_arr : integer_vector := jsonGetIntegerArray(JSONContent, "Image"); - -begin - main: process - begin - test_runner_setup(runner, runner_cfg); - while test_suite loop - if run("test") then - -- Content extracted from the stringified generic - info("JSONContent: " & lf & JSONContent.Content); - - -- Full path of the JSON file, and extracted content - info("tb_path & tb_cfg_file: " & tb_path & tb_cfg_file); - info("JSONFileContent: " & lf & JSONFileContent.Content); - - -- Image dimensions in a record, filled by function decode with data from the stringified generic - info("Image: " & integer'image(img.image_width) & ',' & integer'image(img.image_height)); - - -- Integer array, extracted by function decode_array with data from the stringified generic - for i in 0 to img_arr'length-1 loop - info("Image array [" & integer'image(i) & "]: " & integer'image(img_arr(i))); - end loop; - - -- Image dimensions as strings, get from the content from the JSON file - info("Image: " & jsonGetString(JSONFileContent, "Image/0") & ',' & jsonGetString(JSONFileContent, "Image/1")); - - -- Some other content, deep in the JSON sources - info("Platform/ML505/FPGA: " & jsonGetString(JSONContent, "Platform/ML505/FPGA")); - info("Platform/ML505/FPGA: " & jsonGetString(JSONFileContent, "Platform/ML505/FPGA")); - - info("Platform/KC705/IIC/0/Devices/0/Name: " & jsonGetString(JSONContent, "Platform/KC705/IIC/0/Devices/0/Name")); - info("Platform/KC705/IIC/0/Devices/0/Name: " & jsonGetString(JSONFileContent, "Platform/KC705/IIC/0/Devices/0/Name")); - end if; - end loop; - test_runner_cleanup(runner); - wait; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +library vunit_lib; +context vunit_lib.vunit_context; +library JSON; +context JSON.json_ctx; + +entity tb_json_gens is + generic ( + runner_cfg : string; + tb_path : string; + tb_cfg : string; + tb_cfg_file : string := "data/data.json" + ); +end entity; + +architecture tb of tb_json_gens is + + -- tb_cfg contains stringified content + constant JSONContent : T_JSON := jsonLoad(tb_cfg); + + -- tb_cfg is the path of a JSON file + constant JSONFileContent : T_JSON := jsonLoad(tb_path & tb_cfg_file); + + -- record to be filled by function decode + type img_t is record + image_width : positive; + image_height : positive; + dump_debug_data : boolean; + end record img_t; + + -- function to fill img_t with content extracted from a JSON input + impure function decode(Content : T_JSON) return img_t is + begin + return (image_width => positive'value( jsonGetString(Content, "Image/0") ), + image_height => positive'value( jsonGetString(Content, "Image/1") ), + dump_debug_data => jsonGetBoolean(Content, "dump_debug_data") ); + end function decode; + + constant img : img_t := decode(JSONContent); + + -- get array of integers from JSON content + constant img_arr : integer_vector := jsonGetIntegerArray(JSONContent, "Image"); + +begin + main: process + begin + test_runner_setup(runner, runner_cfg); + while test_suite loop + if run("test") then + -- Content extracted from the stringified generic + info("JSONContent: " & lf & JSONContent.Content); + + -- Full path of the JSON file, and extracted content + info("tb_path & tb_cfg_file: " & tb_path & tb_cfg_file); + info("JSONFileContent: " & lf & JSONFileContent.Content); + + -- Image dimensions in a record, filled by function decode with data from the stringified generic + info("Image: " & integer'image(img.image_width) & ',' & integer'image(img.image_height)); + + -- Integer array, extracted by function decode_array with data from the stringified generic + for i in 0 to img_arr'length-1 loop + info("Image array [" & integer'image(i) & "]: " & integer'image(img_arr(i))); + end loop; + + -- Image dimensions as strings, get from the content from the JSON file + info("Image: " & jsonGetString(JSONFileContent, "Image/0") & ',' & jsonGetString(JSONFileContent, "Image/1")); + + -- Some other content, deep in the JSON sources + info("Platform/ML505/FPGA: " & jsonGetString(JSONContent, "Platform/ML505/FPGA")); + info("Platform/ML505/FPGA: " & jsonGetString(JSONFileContent, "Platform/ML505/FPGA")); + + info("Platform/KC705/IIC/0/Devices/0/Name: " & jsonGetString(JSONContent, "Platform/KC705/IIC/0/Devices/0/Name")); + info("Platform/KC705/IIC/0/Devices/0/Name: " & jsonGetString(JSONFileContent, "Platform/KC705/IIC/0/Devices/0/Name")); + end if; + end loop; + test_runner_cleanup(runner); + wait; + end process; +end architecture; diff --git a/examples/vhdl/logging/run.py b/examples/vhdl/logging/run.py index d2c708f59..8e78b7807 100644 --- a/examples/vhdl/logging/run.py +++ b/examples/vhdl/logging/run.py @@ -1,14 +1,14 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -ui = VUnit.from_argv() -lib = ui.add_library("lib") -lib.add_source_files(join(dirname(__file__), "*.vhd")) - -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +ui = VUnit.from_argv() +lib = ui.add_library("lib") +lib.add_source_files(join(dirname(__file__), "*.vhd")) + +ui.main() diff --git a/examples/vhdl/logging/tb_logging_example.vhd b/examples/vhdl/logging/tb_logging_example.vhd index 765e618ca..72b927975 100644 --- a/examples/vhdl/logging/tb_logging_example.vhd +++ b/examples/vhdl/logging/tb_logging_example.vhd @@ -1,112 +1,112 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -use vunit_lib.print_pkg.all; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.log_handler_pkg.all; -use vunit_lib.run_pkg.all; - -use std.textio.all; - -entity tb_logging_example is - generic (runner_cfg : string); -end entity; - -architecture test of tb_logging_example is -begin - - example_process: process is - variable my_logger : logger_t := get_logger("logging_example:my_logger"); - constant file_name : string := output_path(runner_cfg) & "log.csv"; - - file fptr : text; - variable status : file_open_status; - begin - test_runner_setup(runner, runner_cfg); - - -- An informative log to the the default logger - info("Hello world"); - - -- Log messages can also be multi line - info("Hello" & LF & "world"); - - -- Trace and debug messages are not written to the display by default - debug("not visible"); - trace("not visible"); - - -- Custom loggers can also be used - info(my_logger, "Message to my_logger"); - - -- Loggers have hierarchy - assert get_parent(my_logger) = get_logger("logging_example"); - assert get_child(get_logger("logging_example"), 0) = my_logger; - - -- Log visibility settings are inherited by all children - show(get_parent(my_logger), display_handler, debug); - debug(my_logger, "This will be shown on stdout"); - debug(get_parent(my_logger), "This will be shown on stdout"); - hide(my_logger, display_handler, debug); - debug(my_logger, "This is no longer shown on stdout"); - debug(get_parent(my_logger), "This is still shown on stdout"); - - -- The log format can be changed - set_format(display_handler, raw); - info("Raw format"); - - set_format(display_handler, csv); - info("CSV format"); - - -- The print procedure is independent of logging - print("Print on stdout"); - print("Print on file using file name", file_name); - file_open(status, fptr, file_name, append_mode); - assert status = open_ok report "Failed to open file " & file_name severity failure; - print("Print on file using file object", fptr); - file_close(fptr); - - -- We disable the simulation stop to show error and failure - disable_stop(default_logger, failure); - disable_stop(default_logger, error); - show_all(display_handler); - set_format(display_handler, level, use_color => true); - trace("Level format"); - debug("Level format"); - info("Level format"); - warning("Level format"); - error("Level format"); - failure("Level format"); - - set_format(display_handler, verbose, use_color => true); - trace("Verbose format"); - debug("Verbose format"); - info("Verbose format"); - warning("Verbose format"); - error("Verbose format"); - failure("Verbose format"); - - -- Loggers can also be mocked - mock(my_logger); - failure(my_logger, "message"); - check_only_log(my_logger, "message", failure); - unmock(my_logger); - - -- Conditionall logging is also possible - warning_if(True, "A warning happened"); - warning_if(False, "A warning did not happen"); - - -- Any log to error or failure causes test failure so we reset those levels - reset_log_count(default_logger, error); - reset_log_count(default_logger, failure); - - test_runner_cleanup(runner); - wait; - end process; - - - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +use vunit_lib.print_pkg.all; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.log_handler_pkg.all; +use vunit_lib.run_pkg.all; + +use std.textio.all; + +entity tb_logging_example is + generic (runner_cfg : string); +end entity; + +architecture test of tb_logging_example is +begin + + example_process: process is + variable my_logger : logger_t := get_logger("logging_example:my_logger"); + constant file_name : string := output_path(runner_cfg) & "log.csv"; + + file fptr : text; + variable status : file_open_status; + begin + test_runner_setup(runner, runner_cfg); + + -- An informative log to the the default logger + info("Hello world"); + + -- Log messages can also be multi line + info("Hello" & LF & "world"); + + -- Trace and debug messages are not written to the display by default + debug("not visible"); + trace("not visible"); + + -- Custom loggers can also be used + info(my_logger, "Message to my_logger"); + + -- Loggers have hierarchy + assert get_parent(my_logger) = get_logger("logging_example"); + assert get_child(get_logger("logging_example"), 0) = my_logger; + + -- Log visibility settings are inherited by all children + show(get_parent(my_logger), display_handler, debug); + debug(my_logger, "This will be shown on stdout"); + debug(get_parent(my_logger), "This will be shown on stdout"); + hide(my_logger, display_handler, debug); + debug(my_logger, "This is no longer shown on stdout"); + debug(get_parent(my_logger), "This is still shown on stdout"); + + -- The log format can be changed + set_format(display_handler, raw); + info("Raw format"); + + set_format(display_handler, csv); + info("CSV format"); + + -- The print procedure is independent of logging + print("Print on stdout"); + print("Print on file using file name", file_name); + file_open(status, fptr, file_name, append_mode); + assert status = open_ok report "Failed to open file " & file_name severity failure; + print("Print on file using file object", fptr); + file_close(fptr); + + -- We disable the simulation stop to show error and failure + disable_stop(default_logger, failure); + disable_stop(default_logger, error); + show_all(display_handler); + set_format(display_handler, level, use_color => true); + trace("Level format"); + debug("Level format"); + info("Level format"); + warning("Level format"); + error("Level format"); + failure("Level format"); + + set_format(display_handler, verbose, use_color => true); + trace("Verbose format"); + debug("Verbose format"); + info("Verbose format"); + warning("Verbose format"); + error("Verbose format"); + failure("Verbose format"); + + -- Loggers can also be mocked + mock(my_logger); + failure(my_logger, "message"); + check_only_log(my_logger, "message", failure); + unmock(my_logger); + + -- Conditionall logging is also possible + warning_if(True, "A warning happened"); + warning_if(False, "A warning did not happen"); + + -- Any log to error or failure causes test failure so we reset those levels + reset_log_count(default_logger, error); + reset_log_count(default_logger, failure); + + test_runner_cleanup(runner); + wait; + end process; + + + +end architecture; diff --git a/examples/vhdl/run/run.py b/examples/vhdl/run/run.py index aa81b8406..fe94b66d8 100644 --- a/examples/vhdl/run/run.py +++ b/examples/vhdl/run/run.py @@ -1,17 +1,17 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -root = dirname(__file__) - -ui = VUnit.from_argv() -lib = ui.add_library("lib") -lib.add_source_files(join(root, "*.vhd")) -tb_with_lower_level_control = lib.entity("tb_with_lower_level_control") -tb_with_lower_level_control.scan_tests_from_file(join(root, "test_control.vhd")) -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +root = dirname(__file__) + +ui = VUnit.from_argv() +lib = ui.add_library("lib") +lib.add_source_files(join(root, "*.vhd")) +tb_with_lower_level_control = lib.entity("tb_with_lower_level_control") +tb_with_lower_level_control.scan_tests_from_file(join(root, "test_control.vhd")) +ui.main() diff --git a/examples/vhdl/run/tb_counting_errors.vhd b/examples/vhdl/run/tb_counting_errors.vhd index 0c231ecf0..354bec57a 100644 --- a/examples/vhdl/run/tb_counting_errors.vhd +++ b/examples/vhdl/run/tb_counting_errors.vhd @@ -1,30 +1,30 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_counting_errors is - generic (runner_cfg : string := runner_cfg_default); -end entity; - -architecture tb of tb_counting_errors is -begin - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - set_stop_level(failure); - - while test_suite loop - if run("Test that fails multiple times but doesn't stop") then - check_equal(17, 18); - check_equal(17, 19); - end if; - end loop; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_counting_errors is + generic (runner_cfg : string := runner_cfg_default); +end entity; + +architecture tb of tb_counting_errors is +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + set_stop_level(failure); + + while test_suite loop + if run("Test that fails multiple times but doesn't stop") then + check_equal(17, 18); + check_equal(17, 19); + end if; + end loop; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/examples/vhdl/run/tb_magic_paths.vhd b/examples/vhdl/run/tb_magic_paths.vhd index c6693333b..b8bcabfba 100644 --- a/examples/vhdl/run/tb_magic_paths.vhd +++ b/examples/vhdl/run/tb_magic_paths.vhd @@ -1,23 +1,23 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_magic_paths is - generic (runner_cfg : string); -end entity; - -architecture tb of tb_magic_paths is -begin - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - info("Directory containing testbench: " & tb_path(runner_cfg)); - info("Test output directory: " & output_path(runner_cfg)); - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_magic_paths is + generic (runner_cfg : string); +end entity; + +architecture tb of tb_magic_paths is +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + info("Directory containing testbench: " & tb_path(runner_cfg)); + info("Test output directory: " & output_path(runner_cfg)); + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/examples/vhdl/run/tb_many_ways_to_fail.vhd b/examples/vhdl/run/tb_many_ways_to_fail.vhd index d51ee331e..3d64675ff 100644 --- a/examples/vhdl/run/tb_many_ways_to_fail.vhd +++ b/examples/vhdl/run/tb_many_ways_to_fail.vhd @@ -1,33 +1,33 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_many_ways_to_fail is - generic (runner_cfg : string := runner_cfg_default); -end entity; - -architecture tb of tb_many_ways_to_fail is -begin - test_runner : process - variable my_vector : integer_vector(1 to 17); - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test that fails on an assert") then - assert false; - elsif run("Test that crashes on boundary problems") then - report to_string(my_vector(runner_cfg'length)); - elsif run("Test that fails on VUnit check procedure") then - check_equal(17, 18); - end if; - end loop; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_many_ways_to_fail is + generic (runner_cfg : string := runner_cfg_default); +end entity; + +architecture tb of tb_many_ways_to_fail is +begin + test_runner : process + variable my_vector : integer_vector(1 to 17); + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test that fails on an assert") then + assert false; + elsif run("Test that crashes on boundary problems") then + report to_string(my_vector(runner_cfg'length)); + elsif run("Test that fails on VUnit check procedure") then + check_equal(17, 18); + end if; + end loop; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/examples/vhdl/run/tb_minimal.vhd b/examples/vhdl/run/tb_minimal.vhd index b3f0bafe4..b209d0112 100644 --- a/examples/vhdl/run/tb_minimal.vhd +++ b/examples/vhdl/run/tb_minimal.vhd @@ -1,22 +1,22 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_minimal is - generic (runner_cfg : string := runner_cfg_default); -end entity; - -architecture tb of tb_minimal is -begin - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - check_equal(to_string(17), "17"); - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_minimal is + generic (runner_cfg : string := runner_cfg_default); +end entity; + +architecture tb of tb_minimal is +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + check_equal(to_string(17), "17"); + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/examples/vhdl/run/tb_running_test_case.vhd b/examples/vhdl/run/tb_running_test_case.vhd index e682542cd..c2d5f022e 100644 --- a/examples/vhdl/run/tb_running_test_case.vhd +++ b/examples/vhdl/run/tb_running_test_case.vhd @@ -1,47 +1,47 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_running_test_case is - generic (runner_cfg : string := runner_cfg_default); -end entity; - -architecture tb of tb_running_test_case is - signal start_stimuli, stimuli_done : boolean := false; -begin - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test scenario A") or run("Test scenario B") then - start_stimuli <= true; - wait until stimuli_done; - elsif run("Test something else") then - info("Testing something else"); - end if; - end loop; - - test_runner_cleanup(runner); - end process; - - stimuli_generator: process is - begin - wait until start_stimuli; - - if running_test_case = "Test scenario A" then - info("Applying stimuli for scenario A"); - elsif running_test_case = "Test scenario B" then - info("Applying stimuli for scenario B"); - end if; - - stimuli_done <= true; - wait; - end process stimuli_generator; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_running_test_case is + generic (runner_cfg : string := runner_cfg_default); +end entity; + +architecture tb of tb_running_test_case is + signal start_stimuli, stimuli_done : boolean := false; +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test scenario A") or run("Test scenario B") then + start_stimuli <= true; + wait until stimuli_done; + elsif run("Test something else") then + info("Testing something else"); + end if; + end loop; + + test_runner_cleanup(runner); + end process; + + stimuli_generator: process is + begin + wait until start_stimuli; + + if running_test_case = "Test scenario A" then + info("Applying stimuli for scenario A"); + elsif running_test_case = "Test scenario B" then + info("Applying stimuli for scenario B"); + end if; + + stimuli_done <= true; + wait; + end process stimuli_generator; + +end architecture; diff --git a/examples/vhdl/run/tb_standalone.vhd b/examples/vhdl/run/tb_standalone.vhd index 9c5e6d198..f257d6c0c 100644 --- a/examples/vhdl/run/tb_standalone.vhd +++ b/examples/vhdl/run/tb_standalone.vhd @@ -1,32 +1,32 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_standalone is - generic (runner_cfg : string := runner_cfg_default); -end entity; - -architecture tb of tb_standalone is -begin - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test that fails on VUnit check procedure") then - check_equal(17, 18); - elsif run("Test to_string for boolean") then - check_equal(to_string(true), "true"); - end if; - end loop; - - info("===Summary===" & LF & to_string(get_checker_stat)); - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_standalone is + generic (runner_cfg : string := runner_cfg_default); +end entity; + +architecture tb of tb_standalone is +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test that fails on VUnit check procedure") then + check_equal(17, 18); + elsif run("Test to_string for boolean") then + check_equal(to_string(true), "true"); + end if; + end loop; + + info("===Summary===" & LF & to_string(get_checker_stat)); + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/examples/vhdl/run/tb_with_lower_level_control.vhd b/examples/vhdl/run/tb_with_lower_level_control.vhd index d7c0eb205..b24327961 100644 --- a/examples/vhdl/run/tb_with_lower_level_control.vhd +++ b/examples/vhdl/run/tb_with_lower_level_control.vhd @@ -1,22 +1,22 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_with_lower_level_control is - generic (runner_cfg : string := runner_cfg_default); -end entity; - -architecture tb of tb_with_lower_level_control is - signal start_stimuli, stimuli_done : boolean := false; -begin - - test_control: entity work.test_control - generic map ( - nested_runner_cfg => runner_cfg); - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_with_lower_level_control is + generic (runner_cfg : string := runner_cfg_default); +end entity; + +architecture tb of tb_with_lower_level_control is + signal start_stimuli, stimuli_done : boolean := false; +begin + + test_control: entity work.test_control + generic map ( + nested_runner_cfg => runner_cfg); + +end architecture; diff --git a/examples/vhdl/run/tb_with_test_cases.vhd b/examples/vhdl/run/tb_with_test_cases.vhd index cf19f069d..d253cc92e 100644 --- a/examples/vhdl/run/tb_with_test_cases.vhd +++ b/examples/vhdl/run/tb_with_test_cases.vhd @@ -1,40 +1,40 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_with_test_cases is - generic (runner_cfg : string := runner_cfg_default); -end entity; - -architecture tb of tb_with_test_cases is -begin - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - - -- Put test suite setup code here - - while test_suite loop - - -- Put common test case setup code here - - if run("Test to_string for integer") then - check_equal(to_string(17), "17"); - elsif run("Test to_string for boolean") then - check_equal(to_string(true), "true"); - end if; - - -- Put common test case cleanup code here - - end loop; - - -- Put test suite cleanup code here - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_with_test_cases is + generic (runner_cfg : string := runner_cfg_default); +end entity; + +architecture tb of tb_with_test_cases is +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + + -- Put test suite setup code here + + while test_suite loop + + -- Put common test case setup code here + + if run("Test to_string for integer") then + check_equal(to_string(17), "17"); + elsif run("Test to_string for boolean") then + check_equal(to_string(true), "true"); + end if; + + -- Put common test case cleanup code here + + end loop; + + -- Put test suite cleanup code here + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/examples/vhdl/run/tb_with_watchdog.vhd b/examples/vhdl/run/tb_with_watchdog.vhd index 6d079774b..e41f960ac 100644 --- a/examples/vhdl/run/tb_with_watchdog.vhd +++ b/examples/vhdl/run/tb_with_watchdog.vhd @@ -1,37 +1,37 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_with_watchdog is - generic (runner_cfg : string := runner_cfg_default); -end entity; - -architecture tb of tb_with_watchdog is -begin - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test that stalls") then - wait; - elsif run("Test to_string for boolean") then - check_equal(to_string(true), "true"); - elsif run("Test that needs longer timeout") then - -- It is also possible to set/re-set the timeout - -- When test cases need separate timeout settings - set_timeout(runner, 2 ms); - wait for 1 ms; - end if; - end loop; - - test_runner_cleanup(runner); - end process; - - test_runner_watchdog(runner, 1 ns); -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_with_watchdog is + generic (runner_cfg : string := runner_cfg_default); +end entity; + +architecture tb of tb_with_watchdog is +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test that stalls") then + wait; + elsif run("Test to_string for boolean") then + check_equal(to_string(true), "true"); + elsif run("Test that needs longer timeout") then + -- It is also possible to set/re-set the timeout + -- When test cases need separate timeout settings + set_timeout(runner, 2 ms); + wait for 1 ms; + end if; + end loop; + + test_runner_cleanup(runner); + end process; + + test_runner_watchdog(runner, 1 ns); +end architecture; diff --git a/examples/vhdl/run/test_control.vhd b/examples/vhdl/run/test_control.vhd index 96338a5b8..e53ff3fed 100644 --- a/examples/vhdl/run/test_control.vhd +++ b/examples/vhdl/run/test_control.vhd @@ -1,31 +1,31 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity test_control is - generic ( - nested_runner_cfg : string); -end entity test_control; - -architecture tb of test_control is -begin - test_runner : process - begin - test_runner_setup(runner, nested_runner_cfg); - - while test_suite loop - if run("Test something") then - info("Testing something"); - elsif run("Test something else") then - info("Testing something else"); - end if; - end loop; - - test_runner_cleanup(runner); - end process; -end architecture tb; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity test_control is + generic ( + nested_runner_cfg : string); +end entity test_control; + +architecture tb of test_control is +begin + test_runner : process + begin + test_runner_setup(runner, nested_runner_cfg); + + while test_suite loop + if run("Test something") then + info("Testing something"); + elsif run("Test something else") then + info("Testing something else"); + end if; + end loop; + + test_runner_cleanup(runner); + end process; +end architecture tb; diff --git a/examples/vhdl/third_party_integration/run.py b/examples/vhdl/third_party_integration/run.py index c373812d5..a2bd47d0d 100644 --- a/examples/vhdl/third_party_integration/run.py +++ b/examples/vhdl/third_party_integration/run.py @@ -1,15 +1,15 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -root = dirname(__file__) -ui = VUnit.from_argv() - -lib = ui.add_library("lib") -lib.add_source_files(join(root, 'test', '*.vhd')) -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +root = dirname(__file__) +ui = VUnit.from_argv() + +lib = ui.add_library("lib") +lib.add_source_files(join(root, 'test', '*.vhd')) +ui.main() diff --git a/examples/vhdl/third_party_integration/test/tb_external_framework_integration.vhd b/examples/vhdl/third_party_integration/test/tb_external_framework_integration.vhd index 48c3bf4ab..ef4c1142d 100644 --- a/examples/vhdl/third_party_integration/test/tb_external_framework_integration.vhd +++ b/examples/vhdl/third_party_integration/test/tb_external_framework_integration.vhd @@ -1,75 +1,75 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com -------------------------------------------------------------------------------- --- This file shows how to integrate external assertion --- libraries into VUnit. It can be a company proprietary --- library or open source libraries such as OSVVM and UVVM -------------------------------------------------------------------------------- - -library vunit_lib; --- The VUnit run context doesn't include the logging and checking functionality --- included in vunit_context. This will avoid some name collisions with other libraries. -context vunit_lib.vunit_run_context; - -use std.textio.all; - -entity tb_external_framework_integration is - generic (runner_cfg : string := runner_cfg_default); -end entity tb_external_framework_integration; - -architecture test_fixture of tb_external_framework_integration is -begin - test_runner: process is - --------------------------------------------------------------------------- - -- Typical external framework functionality - --------------------------------------------------------------------------- - - -- A concept for keeping track of error status - variable error_counter : natural := 0; - - -- Assert procedures which may or may not stop the simulation on an error - procedure external_assert (expr : boolean; msg : string; stop_on_error : boolean := true) is - variable l : line; - begin - if not expr then - write(l, "ERROR - " & msg); - writeline(OUTPUT, l); - - error_counter := error_counter + 1; - - if stop_on_error then - report "Simulation stopped due to errors" severity failure; - end if; - end if; - end procedure external_assert; - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test that pass") then - -- No special considerations for a passing test - external_assert(true, "Error message"); - - elsif run("Test that stops the simulation on first error") then - -- VUnit will detect when an error stops the simulation - external_assert(false, "Error message"); - - elsif run("Test that doesn't stop the simulation on error") then - -- When the test doesn't stop on error you must let VUnit know - -- about the error state. This is done in the final test_runner_cleanup - -- call - external_assert(false, "Error message 1", false); - external_assert(false, "Error message 2", false); - end if; - end loop; - - -- Add failure if external error counter > 0 which is not known to VUnit - assert not (error_counter > 0) report "External failure" severity failure; - - test_runner_cleanup(runner); - - end process test_runner; -end; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +------------------------------------------------------------------------------- +-- This file shows how to integrate external assertion +-- libraries into VUnit. It can be a company proprietary +-- library or open source libraries such as OSVVM and UVVM +------------------------------------------------------------------------------- + +library vunit_lib; +-- The VUnit run context doesn't include the logging and checking functionality +-- included in vunit_context. This will avoid some name collisions with other libraries. +context vunit_lib.vunit_run_context; + +use std.textio.all; + +entity tb_external_framework_integration is + generic (runner_cfg : string := runner_cfg_default); +end entity tb_external_framework_integration; + +architecture test_fixture of tb_external_framework_integration is +begin + test_runner: process is + --------------------------------------------------------------------------- + -- Typical external framework functionality + --------------------------------------------------------------------------- + + -- A concept for keeping track of error status + variable error_counter : natural := 0; + + -- Assert procedures which may or may not stop the simulation on an error + procedure external_assert (expr : boolean; msg : string; stop_on_error : boolean := true) is + variable l : line; + begin + if not expr then + write(l, "ERROR - " & msg); + writeline(OUTPUT, l); + + error_counter := error_counter + 1; + + if stop_on_error then + report "Simulation stopped due to errors" severity failure; + end if; + end if; + end procedure external_assert; + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test that pass") then + -- No special considerations for a passing test + external_assert(true, "Error message"); + + elsif run("Test that stops the simulation on first error") then + -- VUnit will detect when an error stops the simulation + external_assert(false, "Error message"); + + elsif run("Test that doesn't stop the simulation on error") then + -- When the test doesn't stop on error you must let VUnit know + -- about the error state. This is done in the final test_runner_cleanup + -- call + external_assert(false, "Error message 1", false); + external_assert(false, "Error message 2", false); + end if; + end loop; + + -- Add failure if external error counter > 0 which is not known to VUnit + assert not (error_counter > 0) report "External failure" severity failure; + + test_runner_cleanup(runner); + + end process test_runner; +end; diff --git a/examples/vhdl/uart/run.py b/examples/vhdl/uart/run.py index b82103d95..930f561e7 100644 --- a/examples/vhdl/uart/run.py +++ b/examples/vhdl/uart/run.py @@ -1,22 +1,22 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -ui = VUnit.from_argv() -ui.add_osvvm() -ui.add_verification_components() - -src_path = join(dirname(__file__), "src") - -uart_lib = ui.add_library("uart_lib") -uart_lib.add_source_files(join(src_path, "*.vhd")) - -tb_uart_lib = ui.add_library("tb_uart_lib") -tb_uart_lib.add_source_files(join(src_path, "test", "*.vhd")) - -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +ui = VUnit.from_argv() +ui.add_osvvm() +ui.add_verification_components() + +src_path = join(dirname(__file__), "src") + +uart_lib = ui.add_library("uart_lib") +uart_lib.add_source_files(join(src_path, "*.vhd")) + +tb_uart_lib = ui.add_library("tb_uart_lib") +tb_uart_lib.add_source_files(join(src_path, "test", "*.vhd")) + +ui.main() diff --git a/examples/vhdl/uart/src/test/tb_uart_rx.vhd b/examples/vhdl/uart/src/test/tb_uart_rx.vhd index 9ed0a38ce..5044860b6 100644 --- a/examples/vhdl/uart/src/test/tb_uart_rx.vhd +++ b/examples/vhdl/uart/src/test/tb_uart_rx.vhd @@ -1,114 +1,114 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library vunit_lib; -context vunit_lib.vunit_context; -context vunit_lib.vc_context; - -library uart_lib; - -entity tb_uart_rx is - generic ( - runner_cfg : string); -end entity; - -architecture tb of tb_uart_rx is - constant baud_rate : integer := 115200; -- bits / s - constant clk_period : integer := 20; -- ns - constant cycles_per_bit : integer := 50 * 10**6 / baud_rate; - - signal clk : std_logic := '0'; - signal rx : std_logic := '1'; - signal overflow : std_logic; - signal tready : std_logic; - signal tvalid : std_Logic; - signal tdata : std_logic_vector(7 downto 0); - - signal num_overflows : integer := 0; - - constant uart_bfm : uart_master_t := new_uart_master(initial_baud_rate => baud_rate); - constant uart_stream : stream_master_t := as_stream(uart_bfm); - - constant axi_stream_bfm : axi_stream_slave_t := new_axi_stream_slave(data_length => tdata'length); - constant axi_stream : stream_slave_t := as_stream(axi_stream_bfm); -begin - - main : process - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - reset_checker_stat; - if run("test_tvalid_low_at_start") then - wait until tvalid = '1' for 1 ms; - check_equal(tvalid, '0'); - - elsif run("test_receives_one_byte") then - push_stream(net, uart_stream, x"77"); - check_stream(net, axi_stream, x"77",true); - wait until rising_edge(clk); - check_equal(tvalid, '0'); - check_equal(num_overflows, 0); - - elsif run("test_two_bytes_casues_overflow") then - push_stream(net, uart_stream, x"77"); - wait until tvalid = '1' and rising_edge(clk); - check_equal(num_overflows, 0); - wait for 1 ms; - push_stream(net, uart_stream, x"77"); - wait for 1 ms; - wait until num_overflows = 1 and rising_edge(clk); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - test_runner_watchdog(runner, 10 ms); - - overflow_counter : process (clk) - begin - if rising_edge(clk) then - if overflow = '1' then - warning("Overflow"); - num_overflows <= num_overflows + 1; - end if; - end if; - end process; - - clk <= not clk after (clk_period/2) * 1 ns; - - dut : entity uart_lib.uart_rx - generic map ( - cycles_per_bit => cycles_per_bit) - port map ( - clk => clk, - rx => rx, - overflow => overflow, - tready => tready, - tvalid => tvalid, - tdata => tdata); - - uart_master_bfm : entity vunit_lib.uart_master - generic map ( - uart => uart_bfm) - port map ( - tx => rx); - - axi_stream_slave_bfm: entity vunit_lib.axi_stream_slave - generic map ( - slave => axi_stream_bfm) - port map ( - aclk => clk, - tvalid => tvalid, - tready => tready, - tdata => tdata); - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.vc_context; + +library uart_lib; + +entity tb_uart_rx is + generic ( + runner_cfg : string); +end entity; + +architecture tb of tb_uart_rx is + constant baud_rate : integer := 115200; -- bits / s + constant clk_period : integer := 20; -- ns + constant cycles_per_bit : integer := 50 * 10**6 / baud_rate; + + signal clk : std_logic := '0'; + signal rx : std_logic := '1'; + signal overflow : std_logic; + signal tready : std_logic; + signal tvalid : std_Logic; + signal tdata : std_logic_vector(7 downto 0); + + signal num_overflows : integer := 0; + + constant uart_bfm : uart_master_t := new_uart_master(initial_baud_rate => baud_rate); + constant uart_stream : stream_master_t := as_stream(uart_bfm); + + constant axi_stream_bfm : axi_stream_slave_t := new_axi_stream_slave(data_length => tdata'length); + constant axi_stream : stream_slave_t := as_stream(axi_stream_bfm); +begin + + main : process + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + reset_checker_stat; + if run("test_tvalid_low_at_start") then + wait until tvalid = '1' for 1 ms; + check_equal(tvalid, '0'); + + elsif run("test_receives_one_byte") then + push_stream(net, uart_stream, x"77"); + check_stream(net, axi_stream, x"77",true); + wait until rising_edge(clk); + check_equal(tvalid, '0'); + check_equal(num_overflows, 0); + + elsif run("test_two_bytes_casues_overflow") then + push_stream(net, uart_stream, x"77"); + wait until tvalid = '1' and rising_edge(clk); + check_equal(num_overflows, 0); + wait for 1 ms; + push_stream(net, uart_stream, x"77"); + wait for 1 ms; + wait until num_overflows = 1 and rising_edge(clk); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + test_runner_watchdog(runner, 10 ms); + + overflow_counter : process (clk) + begin + if rising_edge(clk) then + if overflow = '1' then + warning("Overflow"); + num_overflows <= num_overflows + 1; + end if; + end if; + end process; + + clk <= not clk after (clk_period/2) * 1 ns; + + dut : entity uart_lib.uart_rx + generic map ( + cycles_per_bit => cycles_per_bit) + port map ( + clk => clk, + rx => rx, + overflow => overflow, + tready => tready, + tvalid => tvalid, + tdata => tdata); + + uart_master_bfm : entity vunit_lib.uart_master + generic map ( + uart => uart_bfm) + port map ( + tx => rx); + + axi_stream_slave_bfm: entity vunit_lib.axi_stream_slave + generic map ( + slave => axi_stream_bfm) + port map ( + aclk => clk, + tvalid => tvalid, + tready => tready, + tdata => tdata); + +end architecture; diff --git a/examples/vhdl/uart/src/test/tb_uart_tx.vhd b/examples/vhdl/uart/src/test/tb_uart_tx.vhd index c7e44e873..14a218bdf 100644 --- a/examples/vhdl/uart/src/test/tb_uart_tx.vhd +++ b/examples/vhdl/uart/src/test/tb_uart_tx.vhd @@ -1,104 +1,104 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library vunit_lib; -context vunit_lib.vunit_context; -context vunit_lib.vc_context; - -library osvvm; -use osvvm.RandomPkg.all; - -library uart_lib; - -entity tb_uart_tx is - generic ( - runner_cfg : string); -end entity; - -architecture tb of tb_uart_tx is - constant baud_rate : integer := 115200; -- bits / s - constant clk_period : integer := 20; -- ns - constant cycles_per_bit : integer := 50 * 10**6 / baud_rate; - - signal clk : std_logic := '0'; - signal tx : std_logic; - signal tready : std_logic; - signal tvalid : std_Logic; - signal tdata : std_logic_vector(7 downto 0); - - shared variable rnd_stimuli, rnd_expected : RandomPType; - constant uart_bfm : uart_slave_t := new_uart_slave(initial_baud_rate => baud_rate, - data_length => tdata'length); - constant uart_stream : stream_slave_t := as_stream(uart_bfm); - - constant axi_stream_bfm : axi_stream_master_t := new_axi_stream_master(data_length => tdata'length); - constant axi_stream : stream_master_t := as_stream(axi_stream_bfm); - -begin - - main : process - begin - test_runner_setup(runner, runner_cfg); - - -- Initialize to same seed to get same sequence - rnd_stimuli.InitSeed(rnd_stimuli'instance_name); - rnd_expected.InitSeed(rnd_stimuli'instance_name); - - while test_suite loop - if run("test_send_one_byte") then - push_stream(net, axi_stream, rnd_stimuli.RandSlv(tdata'length)); - check_stream(net, uart_stream, rnd_expected.RandSlv(tdata'length)); - elsif run("test_send_two_bytes") then - push_stream(net, axi_stream, rnd_stimuli.RandSlv(tdata'length)); - check_stream(net, uart_stream, rnd_expected.RandSlv(tdata'length)); - push_stream(net, axi_stream, rnd_stimuli.RandSlv(tdata'length)); - check_stream(net, uart_stream, rnd_expected.RandSlv(tdata'length)); - elsif run("test_send_many_bytes") then - for i in 0 to 7 loop - push_stream(net, axi_stream, rnd_stimuli.RandSlv(tdata'length)); - end loop; - for i in 0 to 7 loop - check_stream(net, uart_stream, rnd_expected.RandSlv(tdata'length)); - end loop; - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - test_runner_watchdog(runner, 10 ms); - - clk <= not clk after (clk_period/2) * 1 ns; - - dut : entity uart_lib.uart_tx - generic map ( - cycles_per_bit => cycles_per_bit) - port map ( - clk => clk, - tx => tx, - tready => tready, - tvalid => tvalid, - tdata => tdata); - - uart_slave_bfm : entity vunit_lib.uart_slave - generic map ( - uart => uart_bfm) - port map ( - rx => tx); - - axi_stream_master_bfm: entity vunit_lib.axi_stream_master - generic map ( - master => axi_stream_bfm) - port map ( - aclk => clk, - tvalid => tvalid, - tready => tready, - tdata => tdata); -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.vc_context; + +library osvvm; +use osvvm.RandomPkg.all; + +library uart_lib; + +entity tb_uart_tx is + generic ( + runner_cfg : string); +end entity; + +architecture tb of tb_uart_tx is + constant baud_rate : integer := 115200; -- bits / s + constant clk_period : integer := 20; -- ns + constant cycles_per_bit : integer := 50 * 10**6 / baud_rate; + + signal clk : std_logic := '0'; + signal tx : std_logic; + signal tready : std_logic; + signal tvalid : std_Logic; + signal tdata : std_logic_vector(7 downto 0); + + shared variable rnd_stimuli, rnd_expected : RandomPType; + constant uart_bfm : uart_slave_t := new_uart_slave(initial_baud_rate => baud_rate, + data_length => tdata'length); + constant uart_stream : stream_slave_t := as_stream(uart_bfm); + + constant axi_stream_bfm : axi_stream_master_t := new_axi_stream_master(data_length => tdata'length); + constant axi_stream : stream_master_t := as_stream(axi_stream_bfm); + +begin + + main : process + begin + test_runner_setup(runner, runner_cfg); + + -- Initialize to same seed to get same sequence + rnd_stimuli.InitSeed(rnd_stimuli'instance_name); + rnd_expected.InitSeed(rnd_stimuli'instance_name); + + while test_suite loop + if run("test_send_one_byte") then + push_stream(net, axi_stream, rnd_stimuli.RandSlv(tdata'length)); + check_stream(net, uart_stream, rnd_expected.RandSlv(tdata'length)); + elsif run("test_send_two_bytes") then + push_stream(net, axi_stream, rnd_stimuli.RandSlv(tdata'length)); + check_stream(net, uart_stream, rnd_expected.RandSlv(tdata'length)); + push_stream(net, axi_stream, rnd_stimuli.RandSlv(tdata'length)); + check_stream(net, uart_stream, rnd_expected.RandSlv(tdata'length)); + elsif run("test_send_many_bytes") then + for i in 0 to 7 loop + push_stream(net, axi_stream, rnd_stimuli.RandSlv(tdata'length)); + end loop; + for i in 0 to 7 loop + check_stream(net, uart_stream, rnd_expected.RandSlv(tdata'length)); + end loop; + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + test_runner_watchdog(runner, 10 ms); + + clk <= not clk after (clk_period/2) * 1 ns; + + dut : entity uart_lib.uart_tx + generic map ( + cycles_per_bit => cycles_per_bit) + port map ( + clk => clk, + tx => tx, + tready => tready, + tvalid => tvalid, + tdata => tdata); + + uart_slave_bfm : entity vunit_lib.uart_slave + generic map ( + uart => uart_bfm) + port map ( + rx => tx); + + axi_stream_master_bfm: entity vunit_lib.axi_stream_master + generic map ( + master => axi_stream_bfm) + port map ( + aclk => clk, + tvalid => tvalid, + tready => tready, + tdata => tdata); +end architecture; diff --git a/examples/vhdl/uart/src/uart_rx.vhd b/examples/vhdl/uart/src/uart_rx.vhd index c02501e5a..5884a4bd5 100644 --- a/examples/vhdl/uart/src/uart_rx.vhd +++ b/examples/vhdl/uart/src/uart_rx.vhd @@ -1,105 +1,105 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; --- pragma translate_off -library vunit_lib; -use vunit_lib.check_pkg.all; -use vunit_lib.logger_pkg.all; --- pragma translate_on - -entity uart_rx is - generic ( - cycles_per_bit : natural := 434); - port ( - clk : in std_logic; - - -- Serial input bit - rx : in std_logic; - - overflow : out std_logic := '0'; - - -- AXI stream for output bytes - tready : in std_logic; - tvalid : out std_Logic := '0'; - tdata : out std_logic_vector(7 downto 0)); -begin - -- pragma translate_off - check_stable(clk, check_enabled, tvalid, tready, tdata, "tdata must be stable until tready is active"); - check_stable(clk, check_enabled, tvalid, tready, tvalid, "tvalid must be active until tready is active"); - check_not_unknown(clk, check_enabled, tvalid, "tvalid must never be unknown"); - check_not_unknown(clk, check_enabled, tready, "tready must never be unknown"); - check_not_unknown(clk, check_enabled, rx, "rx must never be unknown"); - traffic_logger: process (clk) is - begin - if tvalid = '1' and tready = '1' and rising_edge(clk) then - debug("Received " & to_string(to_integer(unsigned(tdata)))); - end if; - end process traffic_logger; - -- pragma translate_on -end entity; - -architecture a of uart_rx is - signal tvalid_int : std_logic := '0'; -begin - main : process (clk) - type state_t is (idle, receiving, done); - variable state : state_t := idle; - variable cycles : natural range 0 to cycles_per_bit-1 := 0; - variable data : std_logic_vector(7 downto 0); - variable index : natural range 0 to data'length-1 := 0; - begin - if rising_edge(clk) then - overflow <= '0'; - - case state is - when idle => - if rx = '0' then - if cycles = cycles_per_bit/2 - 1 then - state := receiving; - cycles := 0; - index := 0; - else - cycles := cycles + 1; - end if; - else - cycles := 0; - end if; - - when receiving => - if cycles = cycles_per_bit - 1 then - data := rx & data(data'length-1 downto 1); - cycles := 0; - - if index = data'length - 1 then - state := done; - else - index := index + 1; - end if; - else - cycles := cycles + 1; - end if; - - when done => - -- New output overwrites old output - overflow <= tvalid_int and not tready; - tvalid_int <= '1'; - tdata <= data; - state := idle; - end case; - - -- output was read - if tvalid_int = '1' and tready = '1' then - tvalid_int <= '0'; - end if; - - end if; - end process; - - tvalid <= tvalid_int; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +-- pragma translate_off +library vunit_lib; +use vunit_lib.check_pkg.all; +use vunit_lib.logger_pkg.all; +-- pragma translate_on + +entity uart_rx is + generic ( + cycles_per_bit : natural := 434); + port ( + clk : in std_logic; + + -- Serial input bit + rx : in std_logic; + + overflow : out std_logic := '0'; + + -- AXI stream for output bytes + tready : in std_logic; + tvalid : out std_Logic := '0'; + tdata : out std_logic_vector(7 downto 0)); +begin + -- pragma translate_off + check_stable(clk, check_enabled, tvalid, tready, tdata, "tdata must be stable until tready is active"); + check_stable(clk, check_enabled, tvalid, tready, tvalid, "tvalid must be active until tready is active"); + check_not_unknown(clk, check_enabled, tvalid, "tvalid must never be unknown"); + check_not_unknown(clk, check_enabled, tready, "tready must never be unknown"); + check_not_unknown(clk, check_enabled, rx, "rx must never be unknown"); + traffic_logger: process (clk) is + begin + if tvalid = '1' and tready = '1' and rising_edge(clk) then + debug("Received " & to_string(to_integer(unsigned(tdata)))); + end if; + end process traffic_logger; + -- pragma translate_on +end entity; + +architecture a of uart_rx is + signal tvalid_int : std_logic := '0'; +begin + main : process (clk) + type state_t is (idle, receiving, done); + variable state : state_t := idle; + variable cycles : natural range 0 to cycles_per_bit-1 := 0; + variable data : std_logic_vector(7 downto 0); + variable index : natural range 0 to data'length-1 := 0; + begin + if rising_edge(clk) then + overflow <= '0'; + + case state is + when idle => + if rx = '0' then + if cycles = cycles_per_bit/2 - 1 then + state := receiving; + cycles := 0; + index := 0; + else + cycles := cycles + 1; + end if; + else + cycles := 0; + end if; + + when receiving => + if cycles = cycles_per_bit - 1 then + data := rx & data(data'length-1 downto 1); + cycles := 0; + + if index = data'length - 1 then + state := done; + else + index := index + 1; + end if; + else + cycles := cycles + 1; + end if; + + when done => + -- New output overwrites old output + overflow <= tvalid_int and not tready; + tvalid_int <= '1'; + tdata <= data; + state := idle; + end case; + + -- output was read + if tvalid_int = '1' and tready = '1' then + tvalid_int <= '0'; + end if; + + end if; + end process; + + tvalid <= tvalid_int; +end architecture; diff --git a/examples/vhdl/uart/src/uart_tx.vhd b/examples/vhdl/uart/src/uart_tx.vhd index 36b0b9582..5678edc30 100644 --- a/examples/vhdl/uart/src/uart_tx.vhd +++ b/examples/vhdl/uart/src/uart_tx.vhd @@ -1,90 +1,90 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; --- pragma translate_off -library vunit_lib; -use vunit_lib.check_pkg.all; -use vunit_lib.logger_pkg.all; --- pragma translate_on - -entity uart_tx is - generic ( - cycles_per_bit : natural := 434); - port ( - clk : in std_logic; - - -- Serial output bit - tx : out std_logic := '1'; - - -- AXI stream for input bytes - tready : out std_logic := '0'; - tvalid : in std_Logic; - tdata : in std_logic_vector(7 downto 0)); -begin - -- pragma translate_off - check_stable(clk, check_enabled, tvalid, tready, tdata, "tdata must be stable until tready is active"); - check_stable(clk, check_enabled, tvalid, tready, tvalid, "tvalid must be active until tready is active"); - check_not_unknown(clk, check_enabled, tvalid, "tvalid must never be unknown"); - check_not_unknown(clk, check_enabled, tready, "tready must never be unknown"); - check_not_unknown(clk, check_enabled, tx, "tx must never be unknown"); - traffic_logger: process (clk) is - begin - if tvalid = '1' and tready = '1' and rising_edge(clk) then - debug("Sending " & to_string(to_integer(unsigned(tdata)))); - end if; - end process traffic_logger; - -- pragma translate_on -end entity; - -architecture a of uart_tx is - signal tready_int : std_logic := '0'; -begin - main : process (clk) - type state_t is (idle, sending); - variable state : state_t := idle; - variable cycles : natural range 0 to cycles_per_bit-1 := 0; - variable data : std_logic_vector(9 downto 0); - variable index : natural range 0 to data'length-1 := 0; - begin - if rising_edge(clk) then - case state is - when idle => - tx <= '1'; - if tvalid = '1' and tready_int = '1' then - state := sending; - cycles := 0; - index := 0; - data := '1' & tdata & '0'; - end if; - when sending => - tx <= data(0); - - if cycles = cycles_per_bit - 1 then - if index = data'length-1 then - state := idle; - else - index := index + 1; - end if; - data := '0' & data(data'left downto 1); - cycles := 0; - else - cycles := cycles + 1; - end if; - end case; - - if state = idle then - tready_int <= '1'; - else - tready_int <= '0'; - end if; - end if; - end process; - - tready <= tready_int; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +-- pragma translate_off +library vunit_lib; +use vunit_lib.check_pkg.all; +use vunit_lib.logger_pkg.all; +-- pragma translate_on + +entity uart_tx is + generic ( + cycles_per_bit : natural := 434); + port ( + clk : in std_logic; + + -- Serial output bit + tx : out std_logic := '1'; + + -- AXI stream for input bytes + tready : out std_logic := '0'; + tvalid : in std_Logic; + tdata : in std_logic_vector(7 downto 0)); +begin + -- pragma translate_off + check_stable(clk, check_enabled, tvalid, tready, tdata, "tdata must be stable until tready is active"); + check_stable(clk, check_enabled, tvalid, tready, tvalid, "tvalid must be active until tready is active"); + check_not_unknown(clk, check_enabled, tvalid, "tvalid must never be unknown"); + check_not_unknown(clk, check_enabled, tready, "tready must never be unknown"); + check_not_unknown(clk, check_enabled, tx, "tx must never be unknown"); + traffic_logger: process (clk) is + begin + if tvalid = '1' and tready = '1' and rising_edge(clk) then + debug("Sending " & to_string(to_integer(unsigned(tdata)))); + end if; + end process traffic_logger; + -- pragma translate_on +end entity; + +architecture a of uart_tx is + signal tready_int : std_logic := '0'; +begin + main : process (clk) + type state_t is (idle, sending); + variable state : state_t := idle; + variable cycles : natural range 0 to cycles_per_bit-1 := 0; + variable data : std_logic_vector(9 downto 0); + variable index : natural range 0 to data'length-1 := 0; + begin + if rising_edge(clk) then + case state is + when idle => + tx <= '1'; + if tvalid = '1' and tready_int = '1' then + state := sending; + cycles := 0; + index := 0; + data := '1' & tdata & '0'; + end if; + when sending => + tx <= data(0); + + if cycles = cycles_per_bit - 1 then + if index = data'length-1 then + state := idle; + else + index := index + 1; + end if; + data := '0' & data(data'left downto 1); + cycles := 0; + else + cycles := cycles + 1; + end if; + end case; + + if state = idle then + tready_int <= '1'; + else + tready_int <= '0'; + end if; + end if; + end process; + + tready <= tready_int; +end architecture; diff --git a/examples/vhdl/user_guide/run.py b/examples/vhdl/user_guide/run.py index 01cb91e22..87da8ffac 100644 --- a/examples/vhdl/user_guide/run.py +++ b/examples/vhdl/user_guide/run.py @@ -1,15 +1,15 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -root = dirname(__file__) - -ui = VUnit.from_argv() -lib = ui.add_library("lib") -lib.add_source_files(join(root, "*.vhd")) -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +root = dirname(__file__) + +ui = VUnit.from_argv() +lib = ui.add_library("lib") +lib.add_source_files(join(root, "*.vhd")) +ui.main() diff --git a/examples/vhdl/user_guide/tb_example.vhd b/examples/vhdl/user_guide/tb_example.vhd index e2a8683d4..50384f006 100644 --- a/examples/vhdl/user_guide/tb_example.vhd +++ b/examples/vhdl/user_guide/tb_example.vhd @@ -1,22 +1,22 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_example is - generic (runner_cfg : string); -end entity; - -architecture tb of tb_example is -begin - main : process - begin - test_runner_setup(runner, runner_cfg); - report "Hello world!"; - test_runner_cleanup(runner); -- Simulation ends here - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_example is + generic (runner_cfg : string); +end entity; + +architecture tb of tb_example is +begin + main : process + begin + test_runner_setup(runner, runner_cfg); + report "Hello world!"; + test_runner_cleanup(runner); -- Simulation ends here + end process; +end architecture; diff --git a/examples/vhdl/user_guide/tb_example_many.vhd b/examples/vhdl/user_guide/tb_example_many.vhd index 5063f0dee..9d9a36720 100644 --- a/examples/vhdl/user_guide/tb_example_many.vhd +++ b/examples/vhdl/user_guide/tb_example_many.vhd @@ -1,33 +1,33 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_example_many is - generic (runner_cfg : string); -end entity; - -architecture tb of tb_example_many is -begin - main : process - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - - if run("test_pass") then - report "This will pass"; - - elsif run("test_fail") then - assert false report "It fails"; - - end if; - end loop; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_example_many is + generic (runner_cfg : string); +end entity; + +architecture tb of tb_example_many is +begin + main : process + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + + if run("test_pass") then + report "This will pass"; + + elsif run("test_fail") then + assert false report "It fails"; + + end if; + end loop; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/examples/vhdl/vivado/generate_vivado_project.py b/examples/vhdl/vivado/generate_vivado_project.py index 49dbb6c7e..263157739 100644 --- a/examples/vhdl/vivado/generate_vivado_project.py +++ b/examples/vhdl/vivado/generate_vivado_project.py @@ -1,23 +1,23 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from vunit.vivado import run_vivado -from os.path import join, dirname, exists, normpath -from shutil import rmtree - - -def main(): - root = normpath(dirname(__file__)) - project_name = "myproject" - if exists(join(root, project_name)): - rmtree(join(root, project_name)) - - run_vivado(join(root, "tcl", "generate_project.tcl"), - tcl_args=[root, "myproject"]) - - -if __name__ == "__main__": - main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from vunit.vivado import run_vivado +from os.path import join, dirname, exists, normpath +from shutil import rmtree + + +def main(): + root = normpath(dirname(__file__)) + project_name = "myproject" + if exists(join(root, project_name)): + rmtree(join(root, project_name)) + + run_vivado(join(root, "tcl", "generate_project.tcl"), + tcl_args=[root, "myproject"]) + + +if __name__ == "__main__": + main() diff --git a/examples/vhdl/vivado/run.py b/examples/vhdl/vivado/run.py index ea72d926d..ba3af6033 100644 --- a/examples/vhdl/vivado/run.py +++ b/examples/vhdl/vivado/run.py @@ -1,26 +1,26 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit -from vivado_util import add_vivado_ip - -ui = VUnit.from_argv() - -root = dirname(__file__) -src_path = join(root, "src") - -lib = ui.add_library("lib") -lib.add_source_files(join(src_path, "*.vhd")) - -tb_lib = ui.add_library("tb_lib") -tb_lib.add_source_files(join(src_path, "test", "*.vhd")) - -add_vivado_ip(ui, - output_path=join(root, "vivado_libs"), - project_file=join(root, "myproject", "myproject.xpr")) - -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit +from vivado_util import add_vivado_ip + +ui = VUnit.from_argv() + +root = dirname(__file__) +src_path = join(root, "src") + +lib = ui.add_library("lib") +lib.add_source_files(join(src_path, "*.vhd")) + +tb_lib = ui.add_library("tb_lib") +tb_lib.add_source_files(join(src_path, "test", "*.vhd")) + +add_vivado_ip(ui, + output_path=join(root, "vivado_libs"), + project_file=join(root, "myproject", "myproject.xpr")) + +ui.main() diff --git a/examples/vhdl/vivado/src/test/tb_top.vhd b/examples/vhdl/vivado/src/test/tb_top.vhd index 3a91493a4..9c1f1e414 100644 --- a/examples/vhdl/vivado/src/test/tb_top.vhd +++ b/examples/vhdl/vivado/src/test/tb_top.vhd @@ -1,87 +1,87 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library vunit_lib; -context vunit_lib.vunit_context; - -library lib; - -entity tb_top is - generic (runner_cfg : string); -end entity; - -architecture tb of tb_top is - signal clk : std_logic := '0'; - signal rstn : std_logic := '0'; - signal in_valid : std_logic := '0'; - signal in_ready : std_logic; - signal in_data : std_logic_vector(7 downto 0) := (others => '0'); - signal out_valid : std_logic; - signal out_ready : std_logic := '0'; - signal out_data : std_logic_vector(7 downto 0); - - constant num_data : integer := 128; - signal start, done : boolean := false; -begin - main : process - begin - test_runner_setup(runner, runner_cfg); - - wait until rising_edge(clk); - wait until rising_edge(clk); - rstn <= '1'; - wait until rising_edge(clk); - - start <= true; - wait until done; - test_runner_cleanup(runner); - end process; - - stimuli : process - begin - wait until start and rising_edge(clk); - for i in 1 to num_data loop - info("input " & integer'image(i) & " of " & integer'image(num_data)); - in_valid <= '1'; - in_data <= std_logic_vector(to_unsigned(i, in_data'length)); - wait until (in_ready and in_valid) = '1' and rising_edge(clk); - in_valid <= '0'; - end loop; - wait; - end process; - - data_check : process - begin - wait until start and rising_edge(clk); - for i in 1 to num_data loop - info("output " & integer'image(i) & " of " & integer'image(num_data)); - out_ready <= '1'; - wait until (out_valid and out_ready) = '1' and rising_edge(clk); - out_ready <= '0'; - check_equal(unsigned(out_data), i); - end loop; - done <= true; - wait; - end process; - - clk <= not clk after 1 ns; - dut : entity lib.top - port map ( - clk => clk, - rstn => rstn, - in_valid => in_valid, - in_ready => in_ready, - in_data => in_data, - out_valid => out_valid, - out_ready => out_ready, - out_data => out_data); - - test_runner_watchdog(runner, 1 ms); -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library vunit_lib; +context vunit_lib.vunit_context; + +library lib; + +entity tb_top is + generic (runner_cfg : string); +end entity; + +architecture tb of tb_top is + signal clk : std_logic := '0'; + signal rstn : std_logic := '0'; + signal in_valid : std_logic := '0'; + signal in_ready : std_logic; + signal in_data : std_logic_vector(7 downto 0) := (others => '0'); + signal out_valid : std_logic; + signal out_ready : std_logic := '0'; + signal out_data : std_logic_vector(7 downto 0); + + constant num_data : integer := 128; + signal start, done : boolean := false; +begin + main : process + begin + test_runner_setup(runner, runner_cfg); + + wait until rising_edge(clk); + wait until rising_edge(clk); + rstn <= '1'; + wait until rising_edge(clk); + + start <= true; + wait until done; + test_runner_cleanup(runner); + end process; + + stimuli : process + begin + wait until start and rising_edge(clk); + for i in 1 to num_data loop + info("input " & integer'image(i) & " of " & integer'image(num_data)); + in_valid <= '1'; + in_data <= std_logic_vector(to_unsigned(i, in_data'length)); + wait until (in_ready and in_valid) = '1' and rising_edge(clk); + in_valid <= '0'; + end loop; + wait; + end process; + + data_check : process + begin + wait until start and rising_edge(clk); + for i in 1 to num_data loop + info("output " & integer'image(i) & " of " & integer'image(num_data)); + out_ready <= '1'; + wait until (out_valid and out_ready) = '1' and rising_edge(clk); + out_ready <= '0'; + check_equal(unsigned(out_data), i); + end loop; + done <= true; + wait; + end process; + + clk <= not clk after 1 ns; + dut : entity lib.top + port map ( + clk => clk, + rstn => rstn, + in_valid => in_valid, + in_ready => in_ready, + in_data => in_data, + out_valid => out_valid, + out_ready => out_ready, + out_data => out_data); + + test_runner_watchdog(runner, 1 ms); +end architecture; diff --git a/examples/vhdl/vivado/src/top.vhd b/examples/vhdl/vivado/src/top.vhd index 22ef413e2..f548ba3e2 100644 --- a/examples/vhdl/vivado/src/top.vhd +++ b/examples/vhdl/vivado/src/top.vhd @@ -1,51 +1,51 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -library xil_defaultlib; - --- A simple entity just for example -entity top is - port ( - clk : in std_logic; - rstn : in std_logic; - in_valid : in std_logic; - in_ready : out std_logic; - in_data : in std_logic_vector(7 downto 0); - out_valid : out std_logic; - out_ready : in std_logic; - out_data : out std_logic_vector(7 downto 0)); -end entity; - - -architecture arch of top is - component fifo_8b_32w is - port ( - s_aclk : IN STD_LOGIC; - s_aresetn : IN STD_LOGIC; - s_axis_tvalid : IN STD_LOGIC; - s_axis_tready : OUT STD_LOGIC; - s_axis_tdata : IN STD_LOGIC_VECTOR(7 DOWNTO 0); - m_axis_tvalid : OUT STD_LOGIC; - m_axis_tready : IN STD_LOGIC; - m_axis_tdata : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)); - end component fifo_8b_32w; -begin - -- Just pass through data to prove that component is not a black box - -- and that compiled ip simulation model is used - fifo_8b_32w_inst : component fifo_8b_32w - port map ( - s_aclk => clk, - s_aresetn => rstn, - s_axis_tvalid => in_valid, - s_axis_tready => in_ready, - s_axis_tdata => in_data, - m_axis_tvalid => out_valid, - m_axis_tready => out_ready, - m_axis_tdata => out_data); -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +library xil_defaultlib; + +-- A simple entity just for example +entity top is + port ( + clk : in std_logic; + rstn : in std_logic; + in_valid : in std_logic; + in_ready : out std_logic; + in_data : in std_logic_vector(7 downto 0); + out_valid : out std_logic; + out_ready : in std_logic; + out_data : out std_logic_vector(7 downto 0)); +end entity; + + +architecture arch of top is + component fifo_8b_32w is + port ( + s_aclk : IN STD_LOGIC; + s_aresetn : IN STD_LOGIC; + s_axis_tvalid : IN STD_LOGIC; + s_axis_tready : OUT STD_LOGIC; + s_axis_tdata : IN STD_LOGIC_VECTOR(7 DOWNTO 0); + m_axis_tvalid : OUT STD_LOGIC; + m_axis_tready : IN STD_LOGIC; + m_axis_tdata : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)); + end component fifo_8b_32w; +begin + -- Just pass through data to prove that component is not a black box + -- and that compiled ip simulation model is used + fifo_8b_32w_inst : component fifo_8b_32w + port map ( + s_aclk => clk, + s_aresetn => rstn, + s_axis_tvalid => in_valid, + s_axis_tready => in_ready, + s_axis_tdata => in_data, + m_axis_tvalid => out_valid, + m_axis_tready => out_ready, + m_axis_tdata => out_data); +end architecture; diff --git a/examples/vhdl/vivado/vivado_util.py b/examples/vhdl/vivado/vivado_util.py index 1517b19c6..1f05d05ca 100644 --- a/examples/vhdl/vivado/vivado_util.py +++ b/examples/vhdl/vivado/vivado_util.py @@ -1,84 +1,84 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from __future__ import print_function -from subprocess import check_call -import os -from os.path import join, exists, abspath, dirname, basename -from os import makedirs -from vunit.simulator_factory import SIMULATOR_FACTORY -from vunit.vivado import (run_vivado, - add_from_compile_order_file, - create_compile_order_file) - - -def add_vivado_ip(vunit_obj, output_path, project_file): - """ - Add vivado (and compile if necessary) vivado ip to vunit project. - """ - - if not exists(project_file): - print("Could not find vivado project %s" % project_file) - exit(1) - - standard_library_path = join(output_path, "standard") - compile_standard_libraries(vunit_obj, standard_library_path) - - project_ip_path = join(output_path, "project_ip") - add_project_ip(vunit_obj, project_file, project_ip_path) - - -def compile_standard_libraries(vunit_obj, output_path): - """ - Compile Xilinx standard libraries using Vivado TCL command - """ - done_token = join(output_path, "all_done.txt") - - simulator_class = SIMULATOR_FACTORY.select_simulator() - - if not exists(done_token): - print("Compiling standard libraries into %s ..." % abspath(output_path)) - simname = simulator_class.name - - # Vivado calls rivierapro for riviera - if simname == "rivierapro": - simname = "riviera" - - run_vivado(join(dirname(__file__), "tcl", "compile_standard_libs.tcl"), - tcl_args=[simname, - simulator_class.find_prefix().replace("\\", "/"), - output_path]) - - else: - print("Standard libraries already exists in %s, skipping" % abspath(output_path)) - - for library_name in ["unisim", "unimacro", "unifast", "secureip", "xpm"]: - path = join(output_path, library_name) - if exists(path): - vunit_obj.add_external_library(library_name, path) - - with open(done_token, "w") as fptr: - fptr.write("done") - - -def add_project_ip(vunit_obj, project_file, output_path, vivado_path=None, clean=False): - """ - Add all IP files from Vivado project to the vunit project - - Caching is used to save time where Vivado is not called again if the compile order already exists. - If Clean is True the compile order is always re-generated - - returns the list of SourceFile objects added - """ - - compile_order_file = join(output_path, "compile_order.txt") - - if clean or not exists(compile_order_file): - create_compile_order_file(project_file, compile_order_file, vivado_path=vivado_path) - else: - print("Vivado project Compile order already exists, re-using: %s" % abspath(compile_order_file)) - - return add_from_compile_order_file(vunit_obj, compile_order_file) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from __future__ import print_function +from subprocess import check_call +import os +from os.path import join, exists, abspath, dirname, basename +from os import makedirs +from vunit.simulator_factory import SIMULATOR_FACTORY +from vunit.vivado import (run_vivado, + add_from_compile_order_file, + create_compile_order_file) + + +def add_vivado_ip(vunit_obj, output_path, project_file): + """ + Add vivado (and compile if necessary) vivado ip to vunit project. + """ + + if not exists(project_file): + print("Could not find vivado project %s" % project_file) + exit(1) + + standard_library_path = join(output_path, "standard") + compile_standard_libraries(vunit_obj, standard_library_path) + + project_ip_path = join(output_path, "project_ip") + add_project_ip(vunit_obj, project_file, project_ip_path) + + +def compile_standard_libraries(vunit_obj, output_path): + """ + Compile Xilinx standard libraries using Vivado TCL command + """ + done_token = join(output_path, "all_done.txt") + + simulator_class = SIMULATOR_FACTORY.select_simulator() + + if not exists(done_token): + print("Compiling standard libraries into %s ..." % abspath(output_path)) + simname = simulator_class.name + + # Vivado calls rivierapro for riviera + if simname == "rivierapro": + simname = "riviera" + + run_vivado(join(dirname(__file__), "tcl", "compile_standard_libs.tcl"), + tcl_args=[simname, + simulator_class.find_prefix().replace("\\", "/"), + output_path]) + + else: + print("Standard libraries already exists in %s, skipping" % abspath(output_path)) + + for library_name in ["unisim", "unimacro", "unifast", "secureip", "xpm"]: + path = join(output_path, library_name) + if exists(path): + vunit_obj.add_external_library(library_name, path) + + with open(done_token, "w") as fptr: + fptr.write("done") + + +def add_project_ip(vunit_obj, project_file, output_path, vivado_path=None, clean=False): + """ + Add all IP files from Vivado project to the vunit project + + Caching is used to save time where Vivado is not called again if the compile order already exists. + If Clean is True the compile order is always re-generated + + returns the list of SourceFile objects added + """ + + compile_order_file = join(output_path, "compile_order.txt") + + if clean or not exists(compile_order_file): + create_compile_order_file(project_file, compile_order_file, vivado_path=vivado_path) + else: + print("Vivado project Compile order already exists, re-using: %s" % abspath(compile_order_file)) + + return add_from_compile_order_file(vunit_obj, compile_order_file) diff --git a/setup.py b/setup.py index 50fcd1095..db77fd815 100644 --- a/setup.py +++ b/setup.py @@ -1,86 +1,86 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -PyPI setup script -""" - -import os -from logging import warning -from setuptools import setup -from vunit.about import version, doc -from vunit.builtins import osvvm_is_installed - - -def find_all_files(directory, endings=None): - """ - Recursively find all files within directory - """ - result = [] - for root, _, filenames in os.walk(directory): - for filename in filenames: - ending = os.path.splitext(filename)[-1] - if endings is None or ending in endings: - result.append(os.path.join(root, filename)) - return result - - -DATA_FILES = [] -DATA_FILES += find_all_files(os.path.join('vunit'), endings=[".tcl"]) -DATA_FILES += find_all_files(os.path.join('vunit', 'vhdl')) -DATA_FILES += find_all_files(os.path.join('vunit', 'verilog'), - endings=[".v", ".sv", ".svh"]) -DATA_FILES = [os.path.relpath(file_name, 'vunit') for file_name in DATA_FILES] - -setup( - name='vunit_hdl', - version=version(), - packages=['vunit', - 'vunit.com', - 'vunit.test', - 'vunit.parsing', - 'vunit.parsing.verilog', - 'vunit.vivado', - 'vunit.test.lint', - 'vunit.test.unit', - 'vunit.test.acceptance'], - package_data={'vunit': DATA_FILES}, - zip_safe=False, - url='https://github.com/VUnit/vunit', - classifiers=['Development Status :: 5 - Production/Stable', - 'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)', - 'Natural Language :: English', - 'Intended Audience :: Developers', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: POSIX :: Linux', - 'Topic :: Software Development :: Testing'], - install_requires=[ - "colorama", - ], - requires=[ - "colorama", - ], - license=[ - "Mozilla Public License 2.0 (MPL 2.0)", - ], - author='Lars Asplund', - author_email='lars.anders.asplund@gmail.com', - description="VUnit is an open source unit testing framework for VHDL/SystemVerilog.", - long_description=doc()) - -if not osvvm_is_installed(): - warning(""" -Found no OSVVM VHDL files. If you're installing from a Git repository and plan to use VUnit's integration -of OSVVM you should run - -git submodule update --init --recursive - -in your VUnit repository before running setup.py.""") +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +PyPI setup script +""" + +import os +from logging import warning +from setuptools import setup +from vunit.about import version, doc +from vunit.builtins import osvvm_is_installed + + +def find_all_files(directory, endings=None): + """ + Recursively find all files within directory + """ + result = [] + for root, _, filenames in os.walk(directory): + for filename in filenames: + ending = os.path.splitext(filename)[-1] + if endings is None or ending in endings: + result.append(os.path.join(root, filename)) + return result + + +DATA_FILES = [] +DATA_FILES += find_all_files(os.path.join('vunit'), endings=[".tcl"]) +DATA_FILES += find_all_files(os.path.join('vunit', 'vhdl')) +DATA_FILES += find_all_files(os.path.join('vunit', 'verilog'), + endings=[".v", ".sv", ".svh"]) +DATA_FILES = [os.path.relpath(file_name, 'vunit') for file_name in DATA_FILES] + +setup( + name='vunit_hdl', + version=version(), + packages=['vunit', + 'vunit.com', + 'vunit.test', + 'vunit.parsing', + 'vunit.parsing.verilog', + 'vunit.vivado', + 'vunit.test.lint', + 'vunit.test.unit', + 'vunit.test.acceptance'], + package_data={'vunit': DATA_FILES}, + zip_safe=False, + url='https://github.com/VUnit/vunit', + classifiers=['Development Status :: 5 - Production/Stable', + 'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)', + 'Natural Language :: English', + 'Intended Audience :: Developers', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: POSIX :: Linux', + 'Topic :: Software Development :: Testing'], + install_requires=[ + "colorama", + ], + requires=[ + "colorama", + ], + license=[ + "Mozilla Public License 2.0 (MPL 2.0)", + ], + author='Lars Asplund', + author_email='lars.anders.asplund@gmail.com', + description="VUnit is an open source unit testing framework for VHDL/SystemVerilog.", + long_description=doc()) + +if not osvvm_is_installed(): + warning(""" +Found no OSVVM VHDL files. If you're installing from a Git repository and plan to use VUnit's integration +of OSVVM you should run + +git submodule update --init --recursive + +in your VUnit repository before running setup.py.""") diff --git a/tools/build_docs.py b/tools/build_docs.py index 520a4cbb7..0436ab004 100644 --- a/tools/build_docs.py +++ b/tools/build_docs.py @@ -1,28 +1,28 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Command line utility to build documentation/website -""" - -from subprocess import check_call -from os.path import join, dirname -import sys -from create_release_notes import create_release_notes - - -def main(): - """ - Build documentation/website - """ - create_release_notes() - check_call([sys.executable, "-m", "sphinx", - "-T", "-E", "-W", "-a", "-n", "-b", "html", - join(dirname(__file__), "..", "docs"), sys.argv[1]]) - - -if __name__ == "__main__": - main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Command line utility to build documentation/website +""" + +from subprocess import check_call +from os.path import join, dirname +import sys +from create_release_notes import create_release_notes + + +def main(): + """ + Build documentation/website + """ + create_release_notes() + check_call([sys.executable, "-m", "sphinx", + "-T", "-E", "-W", "-a", "-n", "-b", "html", + join(dirname(__file__), "..", "docs"), sys.argv[1]]) + + +if __name__ == "__main__": + main() diff --git a/tools/create_release_notes.py b/tools/create_release_notes.py index fc574160d..dea1f8bf9 100644 --- a/tools/create_release_notes.py +++ b/tools/create_release_notes.py @@ -1,108 +1,108 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Create monolithic release notes file from several input files -""" - -from __future__ import print_function - -from os.path import join, dirname, basename, splitext, relpath -from glob import glob -from subprocess import check_output, CalledProcessError -import datetime - - -def get_releases(source_path): - """ - Get all releases defined by release note files - """ - release_notes = join(source_path, "release_notes") - releases = [] - for idx, file_name in enumerate(sorted(glob(join(release_notes, "*.rst")), reverse=True)): - releases.append(Release(file_name, is_latest=idx == 0)) - return releases - - -def create_release_notes(): - """ - Create monolithic release notes file from several input files - """ - source_path = join(dirname(__file__), "..", "docs") - - releases = get_releases(source_path) - latest_release = releases[0] - - def banner(fptr): - fptr.write("\n" + ("-" * 80) + "\n\n") - - with open(join(source_path, "release_notes.rst"), "w") as fptr: - fptr.write(""" -.. _release_notes: - -Release notes -============= - -For installation instructions read :ref:`this `. - -`Commits since last release `__ - -""" % latest_release.tag) - - banner(fptr) - - for idx, release in enumerate(releases): - is_last = idx == len(releases) - 1 - - if release.is_latest: - fptr.write(".. _latest_release:\n\n") - - title = ":vunit_commit:`%s <%s>` - %s" % (release.name, release.tag, release.date.strftime("%Y-%m-%d")) - if release.is_latest: - title += " (latest)" - fptr.write(title + "\n") - fptr.write("-" * len(title) + "\n\n") - - fptr.write(".. include:: %s\n" % relpath(release.file_name, source_path)) - - fptr.write("\n`Download from PyPI `__\n" - % release.name) - - if not is_last: - fptr.write("\n`Commits since previous release `__\n" - % (releases[idx + 1].tag, release.tag)) - banner(fptr) - - -class Release(object): - """ - A release object - """ - def __init__(self, file_name, is_latest): - self.file_name = file_name - self.name = splitext(basename(file_name))[0] - self.tag = "v" + self.name - self.is_latest = is_latest - - try: - self.date = _get_date(self.tag) - - except CalledProcessError: - if self.is_latest: - # Release tag for latest release not yet created, assume HEAD will become release - print("Release tag %s not created yet, use HEAD for date" % self.tag) - self.date = _get_date("HEAD") - else: - raise - - with open(file_name, "r") as fptr: - self.notes = fptr.read() - - -def _get_date(commit): - date_str = check_output(["git", "log", "-1", "--format=%ci", commit]).decode().strip() - date_str = " ".join(date_str.split(" ")[0:2]) - return datetime.datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S") +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Create monolithic release notes file from several input files +""" + +from __future__ import print_function + +from os.path import join, dirname, basename, splitext, relpath +from glob import glob +from subprocess import check_output, CalledProcessError +import datetime + + +def get_releases(source_path): + """ + Get all releases defined by release note files + """ + release_notes = join(source_path, "release_notes") + releases = [] + for idx, file_name in enumerate(sorted(glob(join(release_notes, "*.rst")), reverse=True)): + releases.append(Release(file_name, is_latest=idx == 0)) + return releases + + +def create_release_notes(): + """ + Create monolithic release notes file from several input files + """ + source_path = join(dirname(__file__), "..", "docs") + + releases = get_releases(source_path) + latest_release = releases[0] + + def banner(fptr): + fptr.write("\n" + ("-" * 80) + "\n\n") + + with open(join(source_path, "release_notes.rst"), "w") as fptr: + fptr.write(""" +.. _release_notes: + +Release notes +============= + +For installation instructions read :ref:`this `. + +`Commits since last release `__ + +""" % latest_release.tag) + + banner(fptr) + + for idx, release in enumerate(releases): + is_last = idx == len(releases) - 1 + + if release.is_latest: + fptr.write(".. _latest_release:\n\n") + + title = ":vunit_commit:`%s <%s>` - %s" % (release.name, release.tag, release.date.strftime("%Y-%m-%d")) + if release.is_latest: + title += " (latest)" + fptr.write(title + "\n") + fptr.write("-" * len(title) + "\n\n") + + fptr.write(".. include:: %s\n" % relpath(release.file_name, source_path)) + + fptr.write("\n`Download from PyPI `__\n" + % release.name) + + if not is_last: + fptr.write("\n`Commits since previous release `__\n" + % (releases[idx + 1].tag, release.tag)) + banner(fptr) + + +class Release(object): + """ + A release object + """ + def __init__(self, file_name, is_latest): + self.file_name = file_name + self.name = splitext(basename(file_name))[0] + self.tag = "v" + self.name + self.is_latest = is_latest + + try: + self.date = _get_date(self.tag) + + except CalledProcessError: + if self.is_latest: + # Release tag for latest release not yet created, assume HEAD will become release + print("Release tag %s not created yet, use HEAD for date" % self.tag) + self.date = _get_date("HEAD") + else: + raise + + with open(file_name, "r") as fptr: + self.notes = fptr.read() + + +def _get_date(commit): + date_str = check_output(["git", "log", "-1", "--format=%ci", commit]).decode().strip() + date_str = " ".join(date_str.split(" ")[0:2]) + return datetime.datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S") diff --git a/tools/incisive_vhdl_fixup.py b/tools/incisive_vhdl_fixup.py index fc4a650db..9acc0903a 100644 --- a/tools/incisive_vhdl_fixup.py +++ b/tools/incisive_vhdl_fixup.py @@ -1,90 +1,90 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Perform necessary modifications to VUnit VHDL code to support -Cadence Incisive -""" - -from __future__ import print_function - -import os -import re - - -def fix_file(file_name): - replace_context_with_use_clauses(file_name) - tee_to_double_writeline(file_name) - - -def tee_to_double_writeline(file_name): - """ - Convert TEE calls to double writeline calls - """ - - with open(file_name, "r") as fptr: - text = fptr.read() - - text = re.sub(r"^(\s *)TEE\((.*?),(.*?)\)\s *;", """\ --- \\g<0> -- Not supported by Cadence Incisive -\\1WriteLine(OUTPUT, \\3); -\\1WriteLine(\\2, \\3);""", text, flags=re.MULTILINE) - - with open(file_name, "w") as fptr: - fptr.write(text) - - -def replace_context_with_use_clauses(file_name): - """ - Replace VUnit contexts with use clauses - """ - - with open(file_name, "r") as fptr: - text = fptr.read() - - text = text.replace("context vunit_lib.vunit_context;", """\ --- context vunit_lib.vunit_context; -- Not supported by Cadence Incisive -use vunit_lib.lang.all; -use vunit_lib.string_ops.all; -use vunit_lib.dictionary.all; -use vunit_lib.path.all; -use vunit_lib.log_types_pkg.all; -use vunit_lib.log_special_types_pkg.all; -use vunit_lib.check_types_pkg.all; -use vunit_lib.check_special_types_pkg.all; -use vunit_lib.check_pkg.all; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_special_types_pkg.all; -use vunit_lib.run_base_pkg.all; -use vunit_lib.run_pkg.all;""") - - text = text.replace("context vunit_lib.com_context;", """\ --- context vunit_lib.com_context; -- Not supported by Cadence Incisive -use vunit_lib.com_pkg.all; -use vunit_lib.com_types_pkg.all; -use vunit_lib.com_codec_pkg.all; -use vunit_lib.com_string_pkg.all; -use vunit_lib.com_debug_codec_builder_pkg.all; -use vunit_lib.com_std_codec_builder_pkg.all; -""") - - with open(file_name, "w") as fptr: - fptr.write(text) - - -def main(): - """ - Remove incisive incompatabilities from source code - """ - root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) - for base, _, files in os.walk(root): - for file_name in files: - if file_name.endswith(".vhd"): - fix_file(os.path.join(base, file_name)) - - -if __name__ == "__main__": - main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Perform necessary modifications to VUnit VHDL code to support +Cadence Incisive +""" + +from __future__ import print_function + +import os +import re + + +def fix_file(file_name): + replace_context_with_use_clauses(file_name) + tee_to_double_writeline(file_name) + + +def tee_to_double_writeline(file_name): + """ + Convert TEE calls to double writeline calls + """ + + with open(file_name, "r") as fptr: + text = fptr.read() + + text = re.sub(r"^(\s *)TEE\((.*?),(.*?)\)\s *;", """\ +-- \\g<0> -- Not supported by Cadence Incisive +\\1WriteLine(OUTPUT, \\3); +\\1WriteLine(\\2, \\3);""", text, flags=re.MULTILINE) + + with open(file_name, "w") as fptr: + fptr.write(text) + + +def replace_context_with_use_clauses(file_name): + """ + Replace VUnit contexts with use clauses + """ + + with open(file_name, "r") as fptr: + text = fptr.read() + + text = text.replace("context vunit_lib.vunit_context;", """\ +-- context vunit_lib.vunit_context; -- Not supported by Cadence Incisive +use vunit_lib.lang.all; +use vunit_lib.string_ops.all; +use vunit_lib.dictionary.all; +use vunit_lib.path.all; +use vunit_lib.log_types_pkg.all; +use vunit_lib.log_special_types_pkg.all; +use vunit_lib.check_types_pkg.all; +use vunit_lib.check_special_types_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_special_types_pkg.all; +use vunit_lib.run_base_pkg.all; +use vunit_lib.run_pkg.all;""") + + text = text.replace("context vunit_lib.com_context;", """\ +-- context vunit_lib.com_context; -- Not supported by Cadence Incisive +use vunit_lib.com_pkg.all; +use vunit_lib.com_types_pkg.all; +use vunit_lib.com_codec_pkg.all; +use vunit_lib.com_string_pkg.all; +use vunit_lib.com_debug_codec_builder_pkg.all; +use vunit_lib.com_std_codec_builder_pkg.all; +""") + + with open(file_name, "w") as fptr: + fptr.write(text) + + +def main(): + """ + Remove incisive incompatabilities from source code + """ + root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + for base, _, files in os.walk(root): + for file_name in files: + if file_name.endswith(".vhd"): + fix_file(os.path.join(base, file_name)) + + +if __name__ == "__main__": + main() diff --git a/tools/is_new_release.py b/tools/is_new_release.py index 236fb7cd2..4fa63293e 100644 --- a/tools/is_new_release.py +++ b/tools/is_new_release.py @@ -1,78 +1,78 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Detects when a new release shall be made -""" - -from __future__ import print_function - -import json -from urllib.request import urlopen # pylint: disable=no-name-in-module, import-error -import sys -from os.path import dirname, join, exists -from subprocess import check_output - - -def get_local_version(): - """ - Return the local python package version - """ - setup_file = join(dirname(__file__), "..", "setup.py") - return check_output([sys.executable, setup_file, "--version"]).decode().strip() - - -def get_git_tags(): - return set(check_output(["git", "tag", "--list"]).decode().splitlines()) - - -def is_new_release(version): - """ - Return True when a new release shall be made - """ - - release_note = join(dirname(__file__), "..", "docs", "release_notes", version + ".rst") - if not exists(release_note): - print("Not releasing version %s since release note %s does not exist" % (version, release_note)) - return False - - tags = get_git_tags() - expected_tag = "v" + version - if expected_tag in tags: - print("Not releasing version %s since %s tag already exists" - % (version, expected_tag)) - return False - - if version.endswith("rc0"): - print("Not releasing version %s since it ends with rc0" % version) - return False - - with urlopen('https://pypi.python.org/pypi/vunit_hdl/json') as fptr: - info = json.load(fptr) - - if version in info["releases"].keys(): - print("Version %s has already been released" % version) - return False - - print("Releasing new version: %s" % version) - return True - - -def main(): - """ - Detects when a new release shall be made and writes True to output file - """ - version = get_local_version() - - with open(sys.argv[1], "w") as fptr: - fptr.write(version) - - with open(sys.argv[2], "w") as fptr: - fptr.write(str(is_new_release(version))) - - -if __name__ == "__main__": - main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Detects when a new release shall be made +""" + +from __future__ import print_function + +import json +from urllib.request import urlopen # pylint: disable=no-name-in-module, import-error +import sys +from os.path import dirname, join, exists +from subprocess import check_output + + +def get_local_version(): + """ + Return the local python package version + """ + setup_file = join(dirname(__file__), "..", "setup.py") + return check_output([sys.executable, setup_file, "--version"]).decode().strip() + + +def get_git_tags(): + return set(check_output(["git", "tag", "--list"]).decode().splitlines()) + + +def is_new_release(version): + """ + Return True when a new release shall be made + """ + + release_note = join(dirname(__file__), "..", "docs", "release_notes", version + ".rst") + if not exists(release_note): + print("Not releasing version %s since release note %s does not exist" % (version, release_note)) + return False + + tags = get_git_tags() + expected_tag = "v" + version + if expected_tag in tags: + print("Not releasing version %s since %s tag already exists" + % (version, expected_tag)) + return False + + if version.endswith("rc0"): + print("Not releasing version %s since it ends with rc0" % version) + return False + + with urlopen('https://pypi.python.org/pypi/vunit_hdl/json') as fptr: + info = json.load(fptr) + + if version in info["releases"].keys(): + print("Version %s has already been released" % version) + return False + + print("Releasing new version: %s" % version) + return True + + +def main(): + """ + Detects when a new release shall be made and writes True to output file + """ + version = get_local_version() + + with open(sys.argv[1], "w") as fptr: + fptr.write(version) + + with open(sys.argv[2], "w") as fptr: + fptr.write(str(is_new_release(version))) + + +if __name__ == "__main__": + main() diff --git a/vunit/__init__.py b/vunit/__init__.py index 9b59132c3..776a4f7ac 100644 --- a/vunit/__init__.py +++ b/vunit/__init__.py @@ -1,22 +1,22 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Public VUnit interface -""" - -from os.path import dirname, join, abspath -import vunit.version_check -from vunit.ui import VUnit -from vunit.vunit_cli import VUnitCLI -from vunit.about import version, doc -from vunit.json4vhdl import read_json, encode_json - -# Repository root -ROOT = abspath(join(dirname(__file__), "..")) - -__version__ = version() -__doc__ = doc() # pylint: disable=redefined-builtin +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Public VUnit interface +""" + +from os.path import dirname, join, abspath +import vunit.version_check +from vunit.ui import VUnit +from vunit.vunit_cli import VUnitCLI +from vunit.about import version, doc +from vunit.json4vhdl import read_json, encode_json + +# Repository root +ROOT = abspath(join(dirname(__file__), "..")) + +__version__ = version() +__doc__ = doc() # pylint: disable=redefined-builtin diff --git a/vunit/about.py b/vunit/about.py index 1cb3001bb..2be23ab73 100644 --- a/vunit/about.py +++ b/vunit/about.py @@ -1,74 +1,74 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Provides documentation and version information -""" - - -def license_text(): - """ - Returns licence text - """ - return """VUnit ------ - -VUnit except for OSVVM (see below) is released under the terms of -Mozilla Public License, v. 2.0. - -Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -OSVVM ------ - -OSVVM is redistributed as a submodule to VUnit for your convenience. OSVVM and derivative work -located under examples/vhdl/osvvm_integration/src are licensed under the terms of Artistic License 2.0. - -Copyright (c) 2006-2016, SynthWorks Design Inc http://www.synthworks.com -""" - - -def doc(): - """ - Returns short introduction to VUnit - """ - return r"""What is VUnit? -============== - -VUnit is an open source unit testing framework for VHDL/SystemVerilog -released under the terms of Mozilla Public License, v. 2.0. It -features the functionality needed to realize continuous and automated -testing of your HDL code. VUnit doesn't replace but rather complements -traditional testing methodologies by supporting a "test early and -often" approach through automation. - -**Read more on our** `Website `__ - -Contributing -============ -Contributing in the form of code, feedback, ideas or bug reports are -welcome. Read our `contribution guide -`__ to get started. - -License -======= -""" + license_text() - - -def version(): - """ - Returns VUnit version - """ - if PRE_RELEASE: - return '%i.%i.%irc0' % (VERSION[0], VERSION[1], VERSION[2] + 1) - - return '%i.%i.%i' % (VERSION[0], VERSION[1], VERSION[2]) - - -VERSION = (4, 0, 8) - -# DO NOT TOUCH: Only set to False by PyPI deployment script -PRE_RELEASE = True +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Provides documentation and version information +""" + + +def license_text(): + """ + Returns licence text + """ + return """VUnit +----- + +VUnit except for OSVVM (see below) is released under the terms of +Mozilla Public License, v. 2.0. + +Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +OSVVM +----- + +OSVVM is redistributed as a submodule to VUnit for your convenience. OSVVM and derivative work +located under examples/vhdl/osvvm_integration/src are licensed under the terms of Artistic License 2.0. + +Copyright (c) 2006-2016, SynthWorks Design Inc http://www.synthworks.com +""" + + +def doc(): + """ + Returns short introduction to VUnit + """ + return r"""What is VUnit? +============== + +VUnit is an open source unit testing framework for VHDL/SystemVerilog +released under the terms of Mozilla Public License, v. 2.0. It +features the functionality needed to realize continuous and automated +testing of your HDL code. VUnit doesn't replace but rather complements +traditional testing methodologies by supporting a "test early and +often" approach through automation. + +**Read more on our** `Website `__ + +Contributing +============ +Contributing in the form of code, feedback, ideas or bug reports are +welcome. Read our `contribution guide +`__ to get started. + +License +======= +""" + license_text() + + +def version(): + """ + Returns VUnit version + """ + if PRE_RELEASE: + return '%i.%i.%irc0' % (VERSION[0], VERSION[1], VERSION[2] + 1) + + return '%i.%i.%i' % (VERSION[0], VERSION[1], VERSION[2]) + + +VERSION = (4, 0, 8) + +# DO NOT TOUCH: Only set to False by PyPI deployment script +PRE_RELEASE = True diff --git a/vunit/activehdl_interface.py b/vunit/activehdl_interface.py index 3334e475d..c5e3a909a 100644 --- a/vunit/activehdl_interface.py +++ b/vunit/activehdl_interface.py @@ -1,417 +1,417 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Interface towards Aldec Active HDL -""" - - -from __future__ import print_function - -from os.path import join, dirname, abspath -import os -import re -import logging -from vunit.ostools import Process, write_file, file_exists, renew_path -from vunit.test_suites import get_result_file_name -from vunit.vsim_simulator_mixin import (get_is_test_suite_done_tcl, - fix_path) -from vunit.simulator_interface import (SimulatorInterface, - ListOfStringOption, - StringOption) -from vunit.exceptions import CompileError - -LOGGER = logging.getLogger(__name__) - - -class ActiveHDLInterface(SimulatorInterface): - """ - Active HDL interface - """ - - name = "activehdl" - supports_gui_flag = True - package_users_depend_on_bodies = True - compile_options = [ - ListOfStringOption("activehdl.vcom_flags"), - ListOfStringOption("activehdl.vlog_flags"), - ] - - sim_options = [ - ListOfStringOption("activehdl.vsim_flags"), - ListOfStringOption("activehdl.vsim_flags.gui"), - StringOption("activehdl.init_file.gui"), - ] - - @classmethod - def from_args(cls, args, output_path, **kwargs): - """ - Create new instance from command line arguments object - """ - return cls(prefix=cls.find_prefix(), - output_path=output_path, - gui=args.gui) - - @classmethod - def find_prefix_from_path(cls): - return cls.find_toolchain(["vsim", - "avhdl"]) - - @classmethod - def supports_vhdl_package_generics(cls): - """ - Returns True when this simulator supports VHDL package generics - """ - proc = Process([join(cls.find_prefix(), 'vcom'), '-version'], env=cls.get_env()) - consumer = VersionConsumer() - proc.consume_output(consumer) - if consumer.major is not None: - return consumer.minor >= 1 if consumer.major == 10 else consumer.major > 10 - - return False - - def __init__(self, prefix, output_path, gui=False): - SimulatorInterface.__init__(self, output_path, gui) - self._library_cfg = join(output_path, "library.cfg") - self._prefix = prefix - self._create_library_cfg() - self._libraries = [] - self._coverage_files = set() - - def setup_library_mapping(self, project): - """ - Setup library mapping - """ - mapped_libraries = self._get_mapped_libraries() - - for library in project.get_libraries(): - self._libraries.append(library) - self.create_library(library.name, library.directory, mapped_libraries) - - def compile_source_file_command(self, source_file): - """ - Returns the command to compile a single source_file - """ - if source_file.is_vhdl: - return self.compile_vhdl_file_command(source_file) - - if source_file.is_any_verilog: - return self.compile_verilog_file_command(source_file) - - LOGGER.error("Unknown file type: %s", source_file.file_type) - raise CompileError - - def compile_vhdl_file_command(self, source_file): - """ - Returns the command to compile a VHDL file - """ - return ([join(self._prefix, 'vcom'), '-quiet', '-j', dirname(self._library_cfg)] - + source_file.compile_options.get("activehdl.vcom_flags", []) - + ['-' + source_file.get_vhdl_standard(), '-work', source_file.library.name, source_file.name]) - - def compile_verilog_file_command(self, source_file): - """ - Returns the command to compile a Verilog file - """ - args = [join(self._prefix, 'vlog'), '-quiet', '-lc', self._library_cfg] - args += source_file.compile_options.get("activehdl.vlog_flags", []) - args += ['-work', source_file.library.name, source_file.name] - for library in self._libraries: - args += ["-l", library.name] - for include_dir in source_file.include_dirs: - args += ["+incdir+%s" % include_dir] - for key, value in source_file.defines.items(): - args += ["+define+%s=%s" % (key, value)] - return args - - def create_library(self, library_name, path, mapped_libraries=None): - """ - Create and map a library_name to path - """ - mapped_libraries = mapped_libraries if mapped_libraries is not None else {} - - if not file_exists(dirname(abspath(path))): - os.makedirs(dirname(abspath(path))) - - if not file_exists(path): - proc = Process([join(self._prefix, 'vlib'), library_name, path], - cwd=dirname(self._library_cfg), - env=self.get_env()) - proc.consume_output(callback=None) - - if library_name in mapped_libraries and mapped_libraries[library_name] == path: - return - - proc = Process([join(self._prefix, 'vmap'), library_name, path], - cwd=dirname(self._library_cfg), - env=self.get_env()) - proc.consume_output(callback=None) - - def _create_library_cfg(self): - """ - Create the library.cfg file if it does not exist - """ - if file_exists(self._library_cfg): - return - - with open(self._library_cfg, "w") as ofile: - ofile.write('$INCLUDE = "%s"\n' % join(self._prefix, "..", "vlib", "library.cfg")) - - _library_re = re.compile(r'([a-zA-Z_]+)\s=\s"(.*)"') - - def _get_mapped_libraries(self): - """ - Get mapped libraries from library.cfg file - """ - with open(self._library_cfg, "r") as fptr: - text = fptr.read() - - libraries = {} - for line in text.splitlines(): - match = self._library_re.match(line) - if match is None: - continue - key = match.group(1) - value = match.group(2) - libraries[key] = abspath(join(dirname(self._library_cfg), dirname(value))) - return libraries - - def _vsim_extra_args(self, config): - """ - Determine vsim_extra_args - """ - vsim_extra_args = [] - vsim_extra_args = config.sim_options.get("activehdl.vsim_flags", - vsim_extra_args) - - if self._gui: - vsim_extra_args = config.sim_options.get("activehdl.vsim_flags.gui", - vsim_extra_args) - - return " ".join(vsim_extra_args) - - def _create_load_function(self, config, output_path): - """ - Create the vunit_load TCL function that runs the vsim command and loads the design - """ - set_generic_str = "\n ".join(('set vunit_generic_%s {%s}' % (name, value) - for name, value in config.generics.items())) - set_generic_name_str = " ".join(('-g/%s/%s=${vunit_generic_%s}' % (config.entity_name, - name, - name) - for name in config.generics)) - pli_str = " ".join("-pli \"%s\"" % fix_path(name) for name in config.sim_options.get('pli', [])) - - vsim_flags = [pli_str, - set_generic_name_str, - "-lib", - config.library_name, - config.entity_name] - - if config.architecture_name is not None: - vsim_flags.append(config.architecture_name) - - if config.sim_options.get("enable_coverage", False): - coverage_file_path = join(output_path, "coverage.acdb") - self._coverage_files.add(coverage_file_path) - vsim_flags += ["-acdb_file {%s}" % fix_path(coverage_file_path)] - - vsim_flags += [self._vsim_extra_args(config)] - - if config.sim_options.get("disable_ieee_warnings", False): - vsim_flags.append("-ieee_nowarn") - - # Add the the testbench top-level unit last as coverage is - # only collected for the top-level unit specified last - - vhdl_assert_stop_level_mapping = dict(warning=1, error=2, failure=3) - - tcl = """ -proc vunit_load {{}} {{ - {set_generic_str} - set vsim_failed [catch {{ - vsim {vsim_flags} - }}] - if {{${{vsim_failed}}}} {{ - return true - }} - - global breakassertlevel - set breakassertlevel {breaklevel} - - global builtinbreakassertlevel - set builtinbreakassertlevel $breakassertlevel - - return false -}} -""".format(set_generic_str=set_generic_str, - vsim_flags=" ".join(vsim_flags), - breaklevel=vhdl_assert_stop_level_mapping[config.vhdl_assert_stop_level]) - - return tcl - - @staticmethod - def _create_run_function(): - """ - Create the vunit_run function to run the test bench - """ - return """ -proc vunit_run {} { - run -all - if {![is_test_suite_done]} { - catch { - # tb command can fail when error comes from pli - echo "" - echo "Stack trace result from 'bt' command" - bt - } - return true; - } - return false; -} -""" - - def merge_coverage(self, file_name, args=None): - """ - Merge coverage from all test cases, - """ - - merge_command = "onerror {quit -code 1}\n" - merge_command += "acdb merge" - - for coverage_file in self._coverage_files: - if file_exists(coverage_file): - merge_command += " -i {%s}" % fix_path(coverage_file) - else: - LOGGER.warning("Missing coverage file: %s", coverage_file) - - if args is not None: - merge_command += " " + " ".join("{%s}" % arg for arg in args) - - merge_command += " -o {%s}" % fix_path(file_name) + "\n" - - merge_script_name = join(self._output_path, "acdb_merge.tcl") - with open(merge_script_name, "w") as fptr: - fptr.write(merge_command + "\n") - - vcover_cmd = [join(self._prefix, 'vsimsa'), '-tcl', '%s' % fix_path(merge_script_name)] - - print("Merging coverage files into %s..." % file_name) - vcover_merge_process = Process(vcover_cmd, - env=self.get_env()) - vcover_merge_process.consume_output() - print("Done merging coverage files") - - def _create_common_script(self, config, output_path): - """ - Create tcl script with functions common to interactive and batch modes - """ - tcl = "" - tcl += get_is_test_suite_done_tcl(get_result_file_name(output_path)) - tcl += self._create_load_function(config, output_path) - tcl += self._create_run_function() - return tcl - - @staticmethod - def _create_batch_script(common_file_name, load_only=False): - """ - Create tcl script to run in batch mode - """ - batch_do = "" - batch_do += "source \"%s\"\n" % fix_path(common_file_name) - batch_do += "set failed [vunit_load]\n" - batch_do += "if {$failed} {quit -code 1}\n" - if not load_only: - batch_do += "set failed [vunit_run]\n" - batch_do += "if {$failed} {quit -code 1}\n" - batch_do += "quit -code 0\n" - return batch_do - - def _create_gui_script(self, common_file_name, config): - """ - Create the user facing script which loads common functions and prints a help message - """ - - tcl = "" - tcl += 'source "%s"\n' % fix_path(common_file_name) - tcl += 'workspace create workspace\n' - tcl += 'design create -a design .\n' - - for library in self._libraries: - tcl += "vmap %s %s\n" % (library.name, fix_path(library.directory)) - - tcl += 'vunit_load\n' - - init_file = config.sim_options.get(self.name + ".init_file.gui", None) - if init_file is not None: - tcl += 'source "%s"\n' % fix_path(abspath(init_file)) - - tcl += 'puts "VUnit help: Design already loaded. Use run -all to run the test."\n' - - return tcl - - def _run_batch_file(self, batch_file_name, gui, cwd): - """ - Run a test bench in batch by invoking a new vsim process from the command line - """ - - todo = "@do -tcl \"\"%s\"\"" % fix_path(batch_file_name) - if not gui: - todo = "@onerror {quit -code 1};" + todo - - try: - args = [join(self._prefix, "vsim"), "-gui" if gui else "-c", - "-l", join(dirname(batch_file_name), "transcript"), - '-do', todo] - - proc = Process(args, cwd=cwd, env=self.get_env()) - proc.consume_output() - except Process.NonZeroExitCode: - return False - return True - - def simulate(self, output_path, test_suite_name, config, elaborate_only): - """ - Run a test bench - """ - script_path = join(output_path, self.name) - common_file_name = join(script_path, "common.tcl") - batch_file_name = join(script_path, "batch.tcl") - gui_file_name = join(script_path, "gui.tcl") - - write_file(common_file_name, - self._create_common_script(config, output_path)) - write_file(gui_file_name, - self._create_gui_script(common_file_name, config)) - write_file(batch_file_name, - self._create_batch_script(common_file_name, elaborate_only)) - - if self._gui: - gui_path = join(script_path, "gui") - renew_path(gui_path) - return self._run_batch_file(gui_file_name, gui=True, - cwd=gui_path) - - return self._run_batch_file(batch_file_name, gui=False, - cwd=dirname(self._library_cfg)) - - -class VersionConsumer(object): - """ - Consume version information - """ - def __init__(self): - self.major = None - self.minor = None - - _version_re = re.compile(r'(?P\d+)\.(?P\d+)\.\d+\.\d+') - - def __call__(self, line): - match = self._version_re.search(line) - if match is not None: - self.major = int(match.group('major')) - self.minor = int(match.group('minor')) - return True +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Interface towards Aldec Active HDL +""" + + +from __future__ import print_function + +from os.path import join, dirname, abspath +import os +import re +import logging +from vunit.ostools import Process, write_file, file_exists, renew_path +from vunit.test_suites import get_result_file_name +from vunit.vsim_simulator_mixin import (get_is_test_suite_done_tcl, + fix_path) +from vunit.simulator_interface import (SimulatorInterface, + ListOfStringOption, + StringOption) +from vunit.exceptions import CompileError + +LOGGER = logging.getLogger(__name__) + + +class ActiveHDLInterface(SimulatorInterface): + """ + Active HDL interface + """ + + name = "activehdl" + supports_gui_flag = True + package_users_depend_on_bodies = True + compile_options = [ + ListOfStringOption("activehdl.vcom_flags"), + ListOfStringOption("activehdl.vlog_flags"), + ] + + sim_options = [ + ListOfStringOption("activehdl.vsim_flags"), + ListOfStringOption("activehdl.vsim_flags.gui"), + StringOption("activehdl.init_file.gui"), + ] + + @classmethod + def from_args(cls, args, output_path, **kwargs): + """ + Create new instance from command line arguments object + """ + return cls(prefix=cls.find_prefix(), + output_path=output_path, + gui=args.gui) + + @classmethod + def find_prefix_from_path(cls): + return cls.find_toolchain(["vsim", + "avhdl"]) + + @classmethod + def supports_vhdl_package_generics(cls): + """ + Returns True when this simulator supports VHDL package generics + """ + proc = Process([join(cls.find_prefix(), 'vcom'), '-version'], env=cls.get_env()) + consumer = VersionConsumer() + proc.consume_output(consumer) + if consumer.major is not None: + return consumer.minor >= 1 if consumer.major == 10 else consumer.major > 10 + + return False + + def __init__(self, prefix, output_path, gui=False): + SimulatorInterface.__init__(self, output_path, gui) + self._library_cfg = join(output_path, "library.cfg") + self._prefix = prefix + self._create_library_cfg() + self._libraries = [] + self._coverage_files = set() + + def setup_library_mapping(self, project): + """ + Setup library mapping + """ + mapped_libraries = self._get_mapped_libraries() + + for library in project.get_libraries(): + self._libraries.append(library) + self.create_library(library.name, library.directory, mapped_libraries) + + def compile_source_file_command(self, source_file): + """ + Returns the command to compile a single source_file + """ + if source_file.is_vhdl: + return self.compile_vhdl_file_command(source_file) + + if source_file.is_any_verilog: + return self.compile_verilog_file_command(source_file) + + LOGGER.error("Unknown file type: %s", source_file.file_type) + raise CompileError + + def compile_vhdl_file_command(self, source_file): + """ + Returns the command to compile a VHDL file + """ + return ([join(self._prefix, 'vcom'), '-quiet', '-j', dirname(self._library_cfg)] + + source_file.compile_options.get("activehdl.vcom_flags", []) + + ['-' + source_file.get_vhdl_standard(), '-work', source_file.library.name, source_file.name]) + + def compile_verilog_file_command(self, source_file): + """ + Returns the command to compile a Verilog file + """ + args = [join(self._prefix, 'vlog'), '-quiet', '-lc', self._library_cfg] + args += source_file.compile_options.get("activehdl.vlog_flags", []) + args += ['-work', source_file.library.name, source_file.name] + for library in self._libraries: + args += ["-l", library.name] + for include_dir in source_file.include_dirs: + args += ["+incdir+%s" % include_dir] + for key, value in source_file.defines.items(): + args += ["+define+%s=%s" % (key, value)] + return args + + def create_library(self, library_name, path, mapped_libraries=None): + """ + Create and map a library_name to path + """ + mapped_libraries = mapped_libraries if mapped_libraries is not None else {} + + if not file_exists(dirname(abspath(path))): + os.makedirs(dirname(abspath(path))) + + if not file_exists(path): + proc = Process([join(self._prefix, 'vlib'), library_name, path], + cwd=dirname(self._library_cfg), + env=self.get_env()) + proc.consume_output(callback=None) + + if library_name in mapped_libraries and mapped_libraries[library_name] == path: + return + + proc = Process([join(self._prefix, 'vmap'), library_name, path], + cwd=dirname(self._library_cfg), + env=self.get_env()) + proc.consume_output(callback=None) + + def _create_library_cfg(self): + """ + Create the library.cfg file if it does not exist + """ + if file_exists(self._library_cfg): + return + + with open(self._library_cfg, "w") as ofile: + ofile.write('$INCLUDE = "%s"\n' % join(self._prefix, "..", "vlib", "library.cfg")) + + _library_re = re.compile(r'([a-zA-Z_]+)\s=\s"(.*)"') + + def _get_mapped_libraries(self): + """ + Get mapped libraries from library.cfg file + """ + with open(self._library_cfg, "r") as fptr: + text = fptr.read() + + libraries = {} + for line in text.splitlines(): + match = self._library_re.match(line) + if match is None: + continue + key = match.group(1) + value = match.group(2) + libraries[key] = abspath(join(dirname(self._library_cfg), dirname(value))) + return libraries + + def _vsim_extra_args(self, config): + """ + Determine vsim_extra_args + """ + vsim_extra_args = [] + vsim_extra_args = config.sim_options.get("activehdl.vsim_flags", + vsim_extra_args) + + if self._gui: + vsim_extra_args = config.sim_options.get("activehdl.vsim_flags.gui", + vsim_extra_args) + + return " ".join(vsim_extra_args) + + def _create_load_function(self, config, output_path): + """ + Create the vunit_load TCL function that runs the vsim command and loads the design + """ + set_generic_str = "\n ".join(('set vunit_generic_%s {%s}' % (name, value) + for name, value in config.generics.items())) + set_generic_name_str = " ".join(('-g/%s/%s=${vunit_generic_%s}' % (config.entity_name, + name, + name) + for name in config.generics)) + pli_str = " ".join("-pli \"%s\"" % fix_path(name) for name in config.sim_options.get('pli', [])) + + vsim_flags = [pli_str, + set_generic_name_str, + "-lib", + config.library_name, + config.entity_name] + + if config.architecture_name is not None: + vsim_flags.append(config.architecture_name) + + if config.sim_options.get("enable_coverage", False): + coverage_file_path = join(output_path, "coverage.acdb") + self._coverage_files.add(coverage_file_path) + vsim_flags += ["-acdb_file {%s}" % fix_path(coverage_file_path)] + + vsim_flags += [self._vsim_extra_args(config)] + + if config.sim_options.get("disable_ieee_warnings", False): + vsim_flags.append("-ieee_nowarn") + + # Add the the testbench top-level unit last as coverage is + # only collected for the top-level unit specified last + + vhdl_assert_stop_level_mapping = dict(warning=1, error=2, failure=3) + + tcl = """ +proc vunit_load {{}} {{ + {set_generic_str} + set vsim_failed [catch {{ + vsim {vsim_flags} + }}] + if {{${{vsim_failed}}}} {{ + return true + }} + + global breakassertlevel + set breakassertlevel {breaklevel} + + global builtinbreakassertlevel + set builtinbreakassertlevel $breakassertlevel + + return false +}} +""".format(set_generic_str=set_generic_str, + vsim_flags=" ".join(vsim_flags), + breaklevel=vhdl_assert_stop_level_mapping[config.vhdl_assert_stop_level]) + + return tcl + + @staticmethod + def _create_run_function(): + """ + Create the vunit_run function to run the test bench + """ + return """ +proc vunit_run {} { + run -all + if {![is_test_suite_done]} { + catch { + # tb command can fail when error comes from pli + echo "" + echo "Stack trace result from 'bt' command" + bt + } + return true; + } + return false; +} +""" + + def merge_coverage(self, file_name, args=None): + """ + Merge coverage from all test cases, + """ + + merge_command = "onerror {quit -code 1}\n" + merge_command += "acdb merge" + + for coverage_file in self._coverage_files: + if file_exists(coverage_file): + merge_command += " -i {%s}" % fix_path(coverage_file) + else: + LOGGER.warning("Missing coverage file: %s", coverage_file) + + if args is not None: + merge_command += " " + " ".join("{%s}" % arg for arg in args) + + merge_command += " -o {%s}" % fix_path(file_name) + "\n" + + merge_script_name = join(self._output_path, "acdb_merge.tcl") + with open(merge_script_name, "w") as fptr: + fptr.write(merge_command + "\n") + + vcover_cmd = [join(self._prefix, 'vsimsa'), '-tcl', '%s' % fix_path(merge_script_name)] + + print("Merging coverage files into %s..." % file_name) + vcover_merge_process = Process(vcover_cmd, + env=self.get_env()) + vcover_merge_process.consume_output() + print("Done merging coverage files") + + def _create_common_script(self, config, output_path): + """ + Create tcl script with functions common to interactive and batch modes + """ + tcl = "" + tcl += get_is_test_suite_done_tcl(get_result_file_name(output_path)) + tcl += self._create_load_function(config, output_path) + tcl += self._create_run_function() + return tcl + + @staticmethod + def _create_batch_script(common_file_name, load_only=False): + """ + Create tcl script to run in batch mode + """ + batch_do = "" + batch_do += "source \"%s\"\n" % fix_path(common_file_name) + batch_do += "set failed [vunit_load]\n" + batch_do += "if {$failed} {quit -code 1}\n" + if not load_only: + batch_do += "set failed [vunit_run]\n" + batch_do += "if {$failed} {quit -code 1}\n" + batch_do += "quit -code 0\n" + return batch_do + + def _create_gui_script(self, common_file_name, config): + """ + Create the user facing script which loads common functions and prints a help message + """ + + tcl = "" + tcl += 'source "%s"\n' % fix_path(common_file_name) + tcl += 'workspace create workspace\n' + tcl += 'design create -a design .\n' + + for library in self._libraries: + tcl += "vmap %s %s\n" % (library.name, fix_path(library.directory)) + + tcl += 'vunit_load\n' + + init_file = config.sim_options.get(self.name + ".init_file.gui", None) + if init_file is not None: + tcl += 'source "%s"\n' % fix_path(abspath(init_file)) + + tcl += 'puts "VUnit help: Design already loaded. Use run -all to run the test."\n' + + return tcl + + def _run_batch_file(self, batch_file_name, gui, cwd): + """ + Run a test bench in batch by invoking a new vsim process from the command line + """ + + todo = "@do -tcl \"\"%s\"\"" % fix_path(batch_file_name) + if not gui: + todo = "@onerror {quit -code 1};" + todo + + try: + args = [join(self._prefix, "vsim"), "-gui" if gui else "-c", + "-l", join(dirname(batch_file_name), "transcript"), + '-do', todo] + + proc = Process(args, cwd=cwd, env=self.get_env()) + proc.consume_output() + except Process.NonZeroExitCode: + return False + return True + + def simulate(self, output_path, test_suite_name, config, elaborate_only): + """ + Run a test bench + """ + script_path = join(output_path, self.name) + common_file_name = join(script_path, "common.tcl") + batch_file_name = join(script_path, "batch.tcl") + gui_file_name = join(script_path, "gui.tcl") + + write_file(common_file_name, + self._create_common_script(config, output_path)) + write_file(gui_file_name, + self._create_gui_script(common_file_name, config)) + write_file(batch_file_name, + self._create_batch_script(common_file_name, elaborate_only)) + + if self._gui: + gui_path = join(script_path, "gui") + renew_path(gui_path) + return self._run_batch_file(gui_file_name, gui=True, + cwd=gui_path) + + return self._run_batch_file(batch_file_name, gui=False, + cwd=dirname(self._library_cfg)) + + +class VersionConsumer(object): + """ + Consume version information + """ + def __init__(self): + self.major = None + self.minor = None + + _version_re = re.compile(r'(?P\d+)\.(?P\d+)\.\d+\.\d+') + + def __call__(self, line): + match = self._version_re.search(line) + if match is not None: + self.major = int(match.group('major')) + self.minor = int(match.group('minor')) + return True diff --git a/vunit/builtins.py b/vunit/builtins.py index 56081bcfd..0f7c6e7de 100644 --- a/vunit/builtins.py +++ b/vunit/builtins.py @@ -1,235 +1,235 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Functions to add builtin VHDL code to a project for compilation -""" - - -from os.path import join, abspath, dirname, basename -from glob import glob - -VHDL_PATH = abspath(join(dirname(__file__), "vhdl")) -VERILOG_PATH = abspath(join(dirname(__file__), "verilog")) - - -class Builtins(object): - """ - Manage VUnit builtins and their dependencies - """ - def __init__(self, vunit_obj, vhdl_standard, simulator_class): - self._vunit_obj = vunit_obj - self._vunit_lib = vunit_obj.add_library("vunit_lib") - self._vhdl_standard = vhdl_standard - self._simulator_class = simulator_class - self._builtins_adder = BuiltinsAdder() - - def add(name, deps=tuple()): - self._builtins_adder.add_type(name, getattr(self, "_add_%s" % name), deps) - - add("array_util") - add("com") - add("verification_components", ["com", "osvvm"]) - add("osvvm") - add("random", ["osvvm"]) - add("json4vhdl") - - def add(self, name, args=None): - self._builtins_adder.add(name, args) - - def _add_files(self, pattern): - """ - Add files with naming convention to indicate which standard is supported - """ - supports_context = self._simulator_class.supports_vhdl_2008_contexts() and self._vhdl_standard == "2008" - - tags = { - "93": ("93",), - "2002": ("2002",), - "2008": ("2008",), - "200x": ("2002", "2008",), - } - - for file_name in glob(pattern): - - standards = set() - for tag, applicable_standards in tags.items(): - if tag in basename(file_name): - standards.update(applicable_standards) - - if standards and self._vhdl_standard not in standards: - continue - - if not supports_context and file_name.endswith("_context.vhd"): - continue - - self._vunit_lib.add_source_file(file_name) - - def _add_data_types(self): - """ - Add data types packages - """ - self._add_files(join(VHDL_PATH, "data_types", "src", "types", "*.vhd")) - self._add_files(join(VHDL_PATH, "data_types", "src", "*.vhd")) - - def _add_array_util(self): - """ - Add array utility - """ - if self._vhdl_standard != '2008': - raise RuntimeError("Array util only supports vhdl 2008") - - self._vunit_lib.add_source_files(join(VHDL_PATH, "array", "src", "*.vhd")) - - def _add_random(self): - """ - Add random pkg - """ - if self._vhdl_standard != '2008': - raise RuntimeError("Random only supports vhdl 2008") - - self._vunit_lib.add_source_files(join(VHDL_PATH, "random", "src", "*.vhd")) - - def _add_com(self): - """ - Add com library - """ - if self._vhdl_standard != '2008': - raise RuntimeError("Communication package only supports vhdl 2008") - - self._add_files(join(VHDL_PATH, "com", "src", "*.vhd")) - - def _add_verification_components(self): - """ - Add verification component library - """ - if self._vhdl_standard != '2008': - raise RuntimeError("Verification component library only supports vhdl 2008") - self._add_files(join(VHDL_PATH, "verification_components", "src", "*.vhd")) - - def _add_osvvm(self): - """ - Add osvvm library - """ - library_name = "osvvm" - - try: - library = self._vunit_obj.library(library_name) - except KeyError: - library = self._vunit_obj.add_library(library_name) - - simulator_coverage_api = self._simulator_class.get_osvvm_coverage_api() - supports_vhdl_package_generics = self._simulator_class.supports_vhdl_package_generics() - - if not osvvm_is_installed(): - raise RuntimeError(""" -Found no OSVVM VHDL files. Did you forget to run - -git submodule update --init --recursive - -in your VUnit Git repository? You have to do this first if installing using setup.py.""") - - for file_name in glob(join(VHDL_PATH, "osvvm", "*.vhd")): - if basename(file_name) == "AlertLogPkg_body_BVUL.vhd": - continue - - if (simulator_coverage_api != "rivierapro") and (basename(file_name) == "VendorCovApiPkg_Aldec.vhd"): - continue - - if (simulator_coverage_api == "rivierapro") and (basename(file_name) == "VendorCovApiPkg.vhd"): - continue - - if not supports_vhdl_package_generics and (basename(file_name) in ["ScoreboardGenericPkg.vhd", - "ScoreboardPkg_int.vhd", - "ScoreboardPkg_slv.vhd"]): - continue - - library.add_source_files(file_name, preprocessors=[]) - - def _add_json4vhdl(self): - """ - Add JSON-for-VHDL library - """ - library_name = "JSON" - - try: - library = self._vunit_obj.library(library_name) - except KeyError: - library = self._vunit_obj.add_library(library_name) - - library.add_source_files(join(VHDL_PATH, "JSON-for-VHDL", "vhdl", "*.vhdl")) - - def add_verilog_builtins(self): - """ - Add Verilog builtins - """ - self._vunit_lib.add_source_files(join(VERILOG_PATH, "vunit_pkg.sv")) - - def add_vhdl_builtins(self): - """ - Add vunit VHDL builtin libraries - """ - self._add_data_types() - self._add_files(join(VHDL_PATH, "*.vhd")) - for path in ("core", "logging", "string_ops", "check", "dictionary", "run", "path"): - self._add_files(join(VHDL_PATH, path, "src", "*.vhd")) - - -def osvvm_is_installed(): - """ - Checks if OSVVM is installed within the VUnit directory structure - """ - return len(glob(join(VHDL_PATH, "osvvm", "*.vhd"))) != 0 - - -def add_verilog_include_dir(include_dirs): - """ - Add VUnit Verilog include directory - """ - return [join(VERILOG_PATH, "include")] + include_dirs - - -class BuiltinsAdder(object): - """ - Class to manage adding of builtins with dependencies - """ - - def __init__(self): - self._already_added = {} - self._types = {} - - def add_type(self, name, function, dependencies=tuple()): - self._types[name] = (function, dependencies) - - def add(self, name, args=None): - """ - Add builtin with arguments - """ - args = {} if args is None else args - - if not self._add_check(name, args): - function, dependencies = self._types[name] - for dep_name in dependencies: - self.add(dep_name) - function(**args) - - def _add_check(self, name, args=None): - """ - Check if this package has already been added, - if it has already been added it must use the same parameters - - @returns False if not yet added - """ - if name not in self._already_added: - self._already_added[name] = args - return False - - old_args = self._already_added[name] - if args != old_args: - raise RuntimeError( - "Optional builtin %r added with arguments %r has already been added with arguments %r" - % (name, args, old_args)) - return True +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Functions to add builtin VHDL code to a project for compilation +""" + + +from os.path import join, abspath, dirname, basename +from glob import glob + +VHDL_PATH = abspath(join(dirname(__file__), "vhdl")) +VERILOG_PATH = abspath(join(dirname(__file__), "verilog")) + + +class Builtins(object): + """ + Manage VUnit builtins and their dependencies + """ + def __init__(self, vunit_obj, vhdl_standard, simulator_class): + self._vunit_obj = vunit_obj + self._vunit_lib = vunit_obj.add_library("vunit_lib") + self._vhdl_standard = vhdl_standard + self._simulator_class = simulator_class + self._builtins_adder = BuiltinsAdder() + + def add(name, deps=tuple()): + self._builtins_adder.add_type(name, getattr(self, "_add_%s" % name), deps) + + add("array_util") + add("com") + add("verification_components", ["com", "osvvm"]) + add("osvvm") + add("random", ["osvvm"]) + add("json4vhdl") + + def add(self, name, args=None): + self._builtins_adder.add(name, args) + + def _add_files(self, pattern): + """ + Add files with naming convention to indicate which standard is supported + """ + supports_context = self._simulator_class.supports_vhdl_2008_contexts() and self._vhdl_standard == "2008" + + tags = { + "93": ("93",), + "2002": ("2002",), + "2008": ("2008",), + "200x": ("2002", "2008",), + } + + for file_name in glob(pattern): + + standards = set() + for tag, applicable_standards in tags.items(): + if tag in basename(file_name): + standards.update(applicable_standards) + + if standards and self._vhdl_standard not in standards: + continue + + if not supports_context and file_name.endswith("_context.vhd"): + continue + + self._vunit_lib.add_source_file(file_name) + + def _add_data_types(self): + """ + Add data types packages + """ + self._add_files(join(VHDL_PATH, "data_types", "src", "types", "*.vhd")) + self._add_files(join(VHDL_PATH, "data_types", "src", "*.vhd")) + + def _add_array_util(self): + """ + Add array utility + """ + if self._vhdl_standard != '2008': + raise RuntimeError("Array util only supports vhdl 2008") + + self._vunit_lib.add_source_files(join(VHDL_PATH, "array", "src", "*.vhd")) + + def _add_random(self): + """ + Add random pkg + """ + if self._vhdl_standard != '2008': + raise RuntimeError("Random only supports vhdl 2008") + + self._vunit_lib.add_source_files(join(VHDL_PATH, "random", "src", "*.vhd")) + + def _add_com(self): + """ + Add com library + """ + if self._vhdl_standard != '2008': + raise RuntimeError("Communication package only supports vhdl 2008") + + self._add_files(join(VHDL_PATH, "com", "src", "*.vhd")) + + def _add_verification_components(self): + """ + Add verification component library + """ + if self._vhdl_standard != '2008': + raise RuntimeError("Verification component library only supports vhdl 2008") + self._add_files(join(VHDL_PATH, "verification_components", "src", "*.vhd")) + + def _add_osvvm(self): + """ + Add osvvm library + """ + library_name = "osvvm" + + try: + library = self._vunit_obj.library(library_name) + except KeyError: + library = self._vunit_obj.add_library(library_name) + + simulator_coverage_api = self._simulator_class.get_osvvm_coverage_api() + supports_vhdl_package_generics = self._simulator_class.supports_vhdl_package_generics() + + if not osvvm_is_installed(): + raise RuntimeError(""" +Found no OSVVM VHDL files. Did you forget to run + +git submodule update --init --recursive + +in your VUnit Git repository? You have to do this first if installing using setup.py.""") + + for file_name in glob(join(VHDL_PATH, "osvvm", "*.vhd")): + if basename(file_name) == "AlertLogPkg_body_BVUL.vhd": + continue + + if (simulator_coverage_api != "rivierapro") and (basename(file_name) == "VendorCovApiPkg_Aldec.vhd"): + continue + + if (simulator_coverage_api == "rivierapro") and (basename(file_name) == "VendorCovApiPkg.vhd"): + continue + + if not supports_vhdl_package_generics and (basename(file_name) in ["ScoreboardGenericPkg.vhd", + "ScoreboardPkg_int.vhd", + "ScoreboardPkg_slv.vhd"]): + continue + + library.add_source_files(file_name, preprocessors=[]) + + def _add_json4vhdl(self): + """ + Add JSON-for-VHDL library + """ + library_name = "JSON" + + try: + library = self._vunit_obj.library(library_name) + except KeyError: + library = self._vunit_obj.add_library(library_name) + + library.add_source_files(join(VHDL_PATH, "JSON-for-VHDL", "vhdl", "*.vhdl")) + + def add_verilog_builtins(self): + """ + Add Verilog builtins + """ + self._vunit_lib.add_source_files(join(VERILOG_PATH, "vunit_pkg.sv")) + + def add_vhdl_builtins(self): + """ + Add vunit VHDL builtin libraries + """ + self._add_data_types() + self._add_files(join(VHDL_PATH, "*.vhd")) + for path in ("core", "logging", "string_ops", "check", "dictionary", "run", "path"): + self._add_files(join(VHDL_PATH, path, "src", "*.vhd")) + + +def osvvm_is_installed(): + """ + Checks if OSVVM is installed within the VUnit directory structure + """ + return len(glob(join(VHDL_PATH, "osvvm", "*.vhd"))) != 0 + + +def add_verilog_include_dir(include_dirs): + """ + Add VUnit Verilog include directory + """ + return [join(VERILOG_PATH, "include")] + include_dirs + + +class BuiltinsAdder(object): + """ + Class to manage adding of builtins with dependencies + """ + + def __init__(self): + self._already_added = {} + self._types = {} + + def add_type(self, name, function, dependencies=tuple()): + self._types[name] = (function, dependencies) + + def add(self, name, args=None): + """ + Add builtin with arguments + """ + args = {} if args is None else args + + if not self._add_check(name, args): + function, dependencies = self._types[name] + for dep_name in dependencies: + self.add(dep_name) + function(**args) + + def _add_check(self, name, args=None): + """ + Check if this package has already been added, + if it has already been added it must use the same parameters + + @returns False if not yet added + """ + if name not in self._already_added: + self._already_added[name] = args + return False + + old_args = self._already_added[name] + if args != old_args: + raise RuntimeError( + "Optional builtin %r added with arguments %r has already been added with arguments %r" + % (name, args, old_args)) + return True diff --git a/vunit/cached.py b/vunit/cached.py index 7f9d396ff..11cf54315 100644 --- a/vunit/cached.py +++ b/vunit/cached.py @@ -1,91 +1,91 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Utility to perform costly operation on file contents which can be cached -""" - -import os -from vunit.hashing import hash_string -from vunit.ostools import read_file - - -def cached(key, function, file_name, encoding, database=None, newline=None): - """ - Call function with file content if an update is needed - """ - - if database is None: - # Without a database just return the function of the contents - content = read_file(file_name, encoding=encoding, newline=newline) - return function(content) - - function_key = ("%s(%s, newline=%s)" % (key, file_name, newline)).encode() - content, content_hash = _file_content_hash(file_name, encoding, database, newline=newline) - - if function_key not in database: - # We do not have a cached version of this computation - # recompute and update database - if content is None: - content = read_file(file_name, encoding=encoding, newline=newline) - result = function(content) - database[function_key] = content_hash, result - return result - - old_content_hash, old_result = database[function_key] - if old_content_hash == content_hash: - return old_result - - # Content hash differs, recompute and update database - if content is None: - content = read_file(file_name, encoding=encoding, newline=newline) - result = function(content) - database[function_key] = content_hash, result - return result - - -def file_content_hash(file_name, encoding, database=None): - """ - Returns the hash of the contents of the file - - Use the database to keep a persistent cache of the last content - hash. - """ - _, content_hash = _file_content_hash(file_name, encoding, database) - return content_hash - - -def _file_content_hash(file_name, encoding, database=None, newline=None): - """ - Returns the file content as well as the hash of the content - - Use the database to keep a persistent cache of the last content - hash. If the file modification date has not changed assume the - hash is the same and do not re-open the file. - """ - - if database is None: - content = read_file(file_name, encoding=encoding, newline=newline) - return content, hash_string(content) - - key = ("cached._file_content_hash(%s, newline=%s)" % (file_name, newline)).encode() - - if key not in database: - content = read_file(file_name, encoding=encoding, newline=newline) - content_hash = hash_string(content) - timestamp = os.path.getmtime(file_name) - database[key] = timestamp, content_hash - return content, content_hash - - timestamp = os.path.getmtime(file_name) - last_timestamp, last_content_hash = database[key] - if timestamp != last_timestamp: - content = read_file(file_name, encoding=encoding, newline=newline) - content_hash = hash_string(content) - database[key] = timestamp, content_hash - return content, content_hash - - return None, last_content_hash +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Utility to perform costly operation on file contents which can be cached +""" + +import os +from vunit.hashing import hash_string +from vunit.ostools import read_file + + +def cached(key, function, file_name, encoding, database=None, newline=None): + """ + Call function with file content if an update is needed + """ + + if database is None: + # Without a database just return the function of the contents + content = read_file(file_name, encoding=encoding, newline=newline) + return function(content) + + function_key = ("%s(%s, newline=%s)" % (key, file_name, newline)).encode() + content, content_hash = _file_content_hash(file_name, encoding, database, newline=newline) + + if function_key not in database: + # We do not have a cached version of this computation + # recompute and update database + if content is None: + content = read_file(file_name, encoding=encoding, newline=newline) + result = function(content) + database[function_key] = content_hash, result + return result + + old_content_hash, old_result = database[function_key] + if old_content_hash == content_hash: + return old_result + + # Content hash differs, recompute and update database + if content is None: + content = read_file(file_name, encoding=encoding, newline=newline) + result = function(content) + database[function_key] = content_hash, result + return result + + +def file_content_hash(file_name, encoding, database=None): + """ + Returns the hash of the contents of the file + + Use the database to keep a persistent cache of the last content + hash. + """ + _, content_hash = _file_content_hash(file_name, encoding, database) + return content_hash + + +def _file_content_hash(file_name, encoding, database=None, newline=None): + """ + Returns the file content as well as the hash of the content + + Use the database to keep a persistent cache of the last content + hash. If the file modification date has not changed assume the + hash is the same and do not re-open the file. + """ + + if database is None: + content = read_file(file_name, encoding=encoding, newline=newline) + return content, hash_string(content) + + key = ("cached._file_content_hash(%s, newline=%s)" % (file_name, newline)).encode() + + if key not in database: + content = read_file(file_name, encoding=encoding, newline=newline) + content_hash = hash_string(content) + timestamp = os.path.getmtime(file_name) + database[key] = timestamp, content_hash + return content, content_hash + + timestamp = os.path.getmtime(file_name) + last_timestamp, last_content_hash = database[key] + if timestamp != last_timestamp: + content = read_file(file_name, encoding=encoding, newline=newline) + content_hash = hash_string(content) + database[key] = timestamp, content_hash + return content, content_hash + + return None, last_content_hash diff --git a/vunit/cds_file.py b/vunit/cds_file.py index 2f7aa4478..3164256c3 100644 --- a/vunit/cds_file.py +++ b/vunit/cds_file.py @@ -1,56 +1,56 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Handles Cadence Incisive .cds files -""" - -import re -from vunit.ostools import read_file, write_file - - -class CDSFile(dict): - """ - Handles Cadence Incisive .cds files - - Only cares about 'define' but other lines are kept intact - """ - - _re_define = re.compile(r'\s*define\s+([a-zA-Z0-9_]+)\s+"?(.*?)"?(#|$)', - re.IGNORECASE) - - @classmethod - def parse(cls, file_name): - """ - Parse file_name and create CDSFile instance - """ - contents = read_file(file_name) - - other_lines = [] - defines = {} - for line in contents.splitlines(): - match = cls._re_define.match(line) - - if match is None: - other_lines.append(line) - else: - defines[match.group(1)] = match.group(2) - return cls(defines, other_lines) - - def __init__(self, defines=None, other_lines=None): - defines = {} if defines is None else defines - other_lines = [] if other_lines is None else other_lines - dict.__init__(self, defines) - self._other_lines = other_lines - - def write(self, file_name): - """ - Write cds file to file named 'file_name' - """ - contents = "\n".join(self._other_lines - + ['define %s "%s"' % item - for item in sorted(self.items())]) + "\n" - write_file(file_name, contents) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Handles Cadence Incisive .cds files +""" + +import re +from vunit.ostools import read_file, write_file + + +class CDSFile(dict): + """ + Handles Cadence Incisive .cds files + + Only cares about 'define' but other lines are kept intact + """ + + _re_define = re.compile(r'\s*define\s+([a-zA-Z0-9_]+)\s+"?(.*?)"?(#|$)', + re.IGNORECASE) + + @classmethod + def parse(cls, file_name): + """ + Parse file_name and create CDSFile instance + """ + contents = read_file(file_name) + + other_lines = [] + defines = {} + for line in contents.splitlines(): + match = cls._re_define.match(line) + + if match is None: + other_lines.append(line) + else: + defines[match.group(1)] = match.group(2) + return cls(defines, other_lines) + + def __init__(self, defines=None, other_lines=None): + defines = {} if defines is None else defines + other_lines = [] if other_lines is None else other_lines + dict.__init__(self, defines) + self._other_lines = other_lines + + def write(self, file_name): + """ + Write cds file to file named 'file_name' + """ + contents = "\n".join(self._other_lines + + ['define %s "%s"' % item + for item in sorted(self.items())]) + "\n" + write_file(file_name, contents) diff --git a/vunit/check_preprocessor.py b/vunit/check_preprocessor.py index f401257d8..eb992351a 100644 --- a/vunit/check_preprocessor.py +++ b/vunit/check_preprocessor.py @@ -1,215 +1,215 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Preprocessing of check functions -""" - -import re - - -class CheckPreprocessor(object): - """ - Preprocessing of check functions adding helpful message to check_relation calls - """ - def __init__(self): - self._find_operators = re.compile(r'\?/=|\?<=|\?>=|\?<|\?>|\?=|/=|<=|>=|<|>|=', re.MULTILINE) - self._find_quotes = re.compile(r'"|' + r"'", re.MULTILINE) - self._find_comments = re.compile(r'--|/\*|\*/', re.MULTILINE) - self._actual_formal = re.compile(r'=>(?P.*)', re.MULTILINE) - self._leading_paranthesis = re.compile(r'[\s(]*') - self._trailing_paranthesis = re.compile(r'[\s)]*') - - def run(self, code, file_name): # pylint: disable=unused-argument - """ - Preprocess code and return result also given the file_name of the original file - """ - check_relation_pattern = re.compile(r'[^a-zA-Z0-9_](?Pcheck_relation)\s*(?P\()', - re.MULTILINE) - - check_relation_calls = list(check_relation_pattern.finditer(code)) - check_relation_calls.reverse() - - for match in check_relation_calls: - relation, offset_to_point_before_closing_paranthesis = self._extract_relation(code, match) - if relation: - context_msg_parameter = ', context_msg => %s' % relation.make_context_msg() - code = (code[:match.end('parameters') + offset_to_point_before_closing_paranthesis] - + context_msg_parameter - + code[match.end('parameters') + offset_to_point_before_closing_paranthesis:]) - - return code - - def _extract_relation(self, code, check): - # pylint: disable=missing-docstring - def end_of_parameter(token): - return token.value == ',' if token.level == 1 else token.level == 0 - parameter_tokens = [] - index = 1 - relation = None - for token in self._classify_tokens(code[check.start('parameters') + 1:]): - add_token = True - if token.type == Token.NORMAL: - # The first found parameter containing a top-level relation is assumed - # to be the expr parameter. This is a very reasonable assumption since - # the return types of normal relational operators are boolean, std_ulogic, - # or bit. The expr parameter is the only input of these types. - if not relation: - if end_of_parameter(token): - relation = self._get_relation_from_parameter(parameter_tokens) - parameter_tokens = [] - add_token = False - - if token.level == 0: - break - elif token.is_comment: - add_token = False - - if add_token: - parameter_tokens.append(token) - - index += 1 - - if not relation: - raise SyntaxError('Failed to find relation in %s' % - code[check.start('call'): check.end('parameters') + index]) - - return relation, index - 1 - - @staticmethod - def _classify_tokens(code): - # pylint: disable=missing-docstring - # pylint: disable=too-many-branches - def even_quotes(code): - n_quotes = 0 - for index in range(0, len(code), 2): - if code[index] != "'": - break - n_quotes += 1 - - return (n_quotes % 2) == 0 - - code_section = Token.NORMAL - level = 1 - index = 0 - for char in code: - token = Token(char) - if code_section == Token.NORMAL: - if char == '"': - code_section = Token.STRING - elif char == "'": - # Used to avoid mixing up qualified expressions and - # character literals, e.g. std_logic'('1'). - if even_quotes(code[index:]): - code_section = Token.CHARACTER_LITERAL - elif code[index:index + 2] == '--': - code_section = Token.LINE_COMMENT - elif code[index:index + 2] == '/*': - code_section = Token.BLOCK_COMMENT - elif char == '(': - level += 1 - elif char == ')': - level -= 1 - - next_code_section = code_section - - elif code_section == Token.STRING: - if char == '"': - next_code_section = Token.NORMAL - elif code_section == Token.CHARACTER_LITERAL: - if char == "'": - next_code_section = Token.NORMAL - elif code_section == Token.LINE_COMMENT: - if char == '\n': - next_code_section = Token.NORMAL - elif code_section == Token.BLOCK_COMMENT: - if code[index - 1:index + 1] == '*/': - next_code_section = Token.NORMAL - - token.type = code_section - token.level = level - index += 1 - - yield token - - code_section = next_code_section - - def _get_relation_from_parameter(self, tokens): - # pylint: disable=missing-docstring - def find_top_level_match(matches, tokens, top_level=1): - if matches: - for match in matches: - if not tokens[match.start()].is_quote and tokens[match.start()].level == top_level: - return match - - return None - - relation = None - token_string = ''.join([token.value for token in tokens]).strip() - actual_formal = find_top_level_match(self._actual_formal.finditer(token_string), tokens) - if actual_formal: - expr = actual_formal.group('actual') - start = actual_formal.start('actual') - else: - expr = token_string - start = 0 - - # VHDL only allows one relational operator at the top level of an expression. - # This operator divides the relation between left and right. The token.level - # is normally one for the top level but may be higher if the expression is - # enclosed with parenthesis. - top_level = min([self._leading_paranthesis.match(expr).group().count('('), - self._trailing_paranthesis.match(expr[::-1]).group().count(')')]) + 1 - top_level_match = find_top_level_match(self._find_operators.finditer(expr), tokens[start:], top_level) - if top_level_match: - if top_level == 1: - left = expr[:top_level_match.start()].strip() - right = expr[top_level_match.end():].strip() - else: - left = expr[:top_level_match.start()].replace('(', '', top_level - 1).strip() - right = expr[:top_level_match.end():-1].replace(')', '', top_level - 1).strip()[::-1] - - relation = Relation(left, top_level_match.group(), right) - - return relation - - -class Token(object): - # pylint: disable=missing-docstring - NORMAL = 0 - STRING = 1 - CHARACTER_LITERAL = 2 - LINE_COMMENT = 3 - BLOCK_COMMENT = 4 - - def __init__(self, value): - self.value = value - self.type = None - self.level = None - - @property - def is_comment(self): - return self.type in [self.LINE_COMMENT, self.BLOCK_COMMENT] - - @property - def is_quote(self): - return self.type in [self.CHARACTER_LITERAL, self.STRING] - - -class Relation(object): - # pylint: disable=missing-docstring - def __init__(self, left, operand, right): - self._left = left - self._operand = operand - self._right = right - - def make_context_msg(self): - return ('"Expected %s %s %s. Left is " & to_string(%s) & ". Right is " & to_string(%s) & "."' - % (self._left.replace('"', '""'), - self._operand, - self._right.replace('"', '""'), - self._left, - self._right)) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Preprocessing of check functions +""" + +import re + + +class CheckPreprocessor(object): + """ + Preprocessing of check functions adding helpful message to check_relation calls + """ + def __init__(self): + self._find_operators = re.compile(r'\?/=|\?<=|\?>=|\?<|\?>|\?=|/=|<=|>=|<|>|=', re.MULTILINE) + self._find_quotes = re.compile(r'"|' + r"'", re.MULTILINE) + self._find_comments = re.compile(r'--|/\*|\*/', re.MULTILINE) + self._actual_formal = re.compile(r'=>(?P.*)', re.MULTILINE) + self._leading_paranthesis = re.compile(r'[\s(]*') + self._trailing_paranthesis = re.compile(r'[\s)]*') + + def run(self, code, file_name): # pylint: disable=unused-argument + """ + Preprocess code and return result also given the file_name of the original file + """ + check_relation_pattern = re.compile(r'[^a-zA-Z0-9_](?Pcheck_relation)\s*(?P\()', + re.MULTILINE) + + check_relation_calls = list(check_relation_pattern.finditer(code)) + check_relation_calls.reverse() + + for match in check_relation_calls: + relation, offset_to_point_before_closing_paranthesis = self._extract_relation(code, match) + if relation: + context_msg_parameter = ', context_msg => %s' % relation.make_context_msg() + code = (code[:match.end('parameters') + offset_to_point_before_closing_paranthesis] + + context_msg_parameter + + code[match.end('parameters') + offset_to_point_before_closing_paranthesis:]) + + return code + + def _extract_relation(self, code, check): + # pylint: disable=missing-docstring + def end_of_parameter(token): + return token.value == ',' if token.level == 1 else token.level == 0 + parameter_tokens = [] + index = 1 + relation = None + for token in self._classify_tokens(code[check.start('parameters') + 1:]): + add_token = True + if token.type == Token.NORMAL: + # The first found parameter containing a top-level relation is assumed + # to be the expr parameter. This is a very reasonable assumption since + # the return types of normal relational operators are boolean, std_ulogic, + # or bit. The expr parameter is the only input of these types. + if not relation: + if end_of_parameter(token): + relation = self._get_relation_from_parameter(parameter_tokens) + parameter_tokens = [] + add_token = False + + if token.level == 0: + break + elif token.is_comment: + add_token = False + + if add_token: + parameter_tokens.append(token) + + index += 1 + + if not relation: + raise SyntaxError('Failed to find relation in %s' % + code[check.start('call'): check.end('parameters') + index]) + + return relation, index - 1 + + @staticmethod + def _classify_tokens(code): + # pylint: disable=missing-docstring + # pylint: disable=too-many-branches + def even_quotes(code): + n_quotes = 0 + for index in range(0, len(code), 2): + if code[index] != "'": + break + n_quotes += 1 + + return (n_quotes % 2) == 0 + + code_section = Token.NORMAL + level = 1 + index = 0 + for char in code: + token = Token(char) + if code_section == Token.NORMAL: + if char == '"': + code_section = Token.STRING + elif char == "'": + # Used to avoid mixing up qualified expressions and + # character literals, e.g. std_logic'('1'). + if even_quotes(code[index:]): + code_section = Token.CHARACTER_LITERAL + elif code[index:index + 2] == '--': + code_section = Token.LINE_COMMENT + elif code[index:index + 2] == '/*': + code_section = Token.BLOCK_COMMENT + elif char == '(': + level += 1 + elif char == ')': + level -= 1 + + next_code_section = code_section + + elif code_section == Token.STRING: + if char == '"': + next_code_section = Token.NORMAL + elif code_section == Token.CHARACTER_LITERAL: + if char == "'": + next_code_section = Token.NORMAL + elif code_section == Token.LINE_COMMENT: + if char == '\n': + next_code_section = Token.NORMAL + elif code_section == Token.BLOCK_COMMENT: + if code[index - 1:index + 1] == '*/': + next_code_section = Token.NORMAL + + token.type = code_section + token.level = level + index += 1 + + yield token + + code_section = next_code_section + + def _get_relation_from_parameter(self, tokens): + # pylint: disable=missing-docstring + def find_top_level_match(matches, tokens, top_level=1): + if matches: + for match in matches: + if not tokens[match.start()].is_quote and tokens[match.start()].level == top_level: + return match + + return None + + relation = None + token_string = ''.join([token.value for token in tokens]).strip() + actual_formal = find_top_level_match(self._actual_formal.finditer(token_string), tokens) + if actual_formal: + expr = actual_formal.group('actual') + start = actual_formal.start('actual') + else: + expr = token_string + start = 0 + + # VHDL only allows one relational operator at the top level of an expression. + # This operator divides the relation between left and right. The token.level + # is normally one for the top level but may be higher if the expression is + # enclosed with parenthesis. + top_level = min([self._leading_paranthesis.match(expr).group().count('('), + self._trailing_paranthesis.match(expr[::-1]).group().count(')')]) + 1 + top_level_match = find_top_level_match(self._find_operators.finditer(expr), tokens[start:], top_level) + if top_level_match: + if top_level == 1: + left = expr[:top_level_match.start()].strip() + right = expr[top_level_match.end():].strip() + else: + left = expr[:top_level_match.start()].replace('(', '', top_level - 1).strip() + right = expr[:top_level_match.end():-1].replace(')', '', top_level - 1).strip()[::-1] + + relation = Relation(left, top_level_match.group(), right) + + return relation + + +class Token(object): + # pylint: disable=missing-docstring + NORMAL = 0 + STRING = 1 + CHARACTER_LITERAL = 2 + LINE_COMMENT = 3 + BLOCK_COMMENT = 4 + + def __init__(self, value): + self.value = value + self.type = None + self.level = None + + @property + def is_comment(self): + return self.type in [self.LINE_COMMENT, self.BLOCK_COMMENT] + + @property + def is_quote(self): + return self.type in [self.CHARACTER_LITERAL, self.STRING] + + +class Relation(object): + # pylint: disable=missing-docstring + def __init__(self, left, operand, right): + self._left = left + self._operand = operand + self._right = right + + def make_context_msg(self): + return ('"Expected %s %s %s. Left is " & to_string(%s) & ". Right is " & to_string(%s) & "."' + % (self._left.replace('"', '""'), + self._operand, + self._right.replace('"', '""'), + self._left, + self._right)) diff --git a/vunit/color_printer.py b/vunit/color_printer.py index 4ca0c8aa2..3d9ddc33c 100644 --- a/vunit/color_printer.py +++ b/vunit/color_printer.py @@ -1,202 +1,202 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Provides capability to print in color to the terminal in both Windows and Linux. -""" - -import sys -import ctypes -from ctypes import Structure, c_short, c_ushort, byref -from vunit.ostools import IS_WINDOWS_SYSTEM - - -class LinuxColorPrinter(object): - """ - Print in color on linux - """ - - BLUE = 'b' - GREEN = 'g' - RED = 'r' - INTENSITY = 'i' - WHITE = RED + GREEN + BLUE - - def __init__(self): - pass - - def write(self, text, output_file=None, fg=None, bg=None): - """ - Print the text in color to the output_file - uses stdout if output_file is None - """ - if output_file is None: - output_file = sys.stdout - - text = self._ansi_wrap(text, fg, bg) - output_file.write(text) - - @staticmethod - def _to_code(rgb): - """ - Translate strings containing 'rgb' characters to numerical color codes - """ - code = 0 - if 'r' in rgb: - code += 1 - - if 'g' in rgb: - code += 2 - - if 'b' in rgb: - code += 4 - return code - - def _ansi_wrap(self, text, fg, bg): - """ - Wrap the text into ANSI color escape codes - fg -- the foreground color - bg -- the background color - """ - codes = [] - - if fg is not None: - codes.append(30 + self._to_code(fg)) - - if bg is not None: - codes.append(40 + self._to_code(bg)) - - if fg is not None and 'i' in fg: - codes.append(1) # Bold - - if bg is not None and 'i' in bg: - codes.append(4) # Underscore - - return "\033[" + ";".join([str(code) for code in codes]) + "m" + text + "\033[0m" - - -class Coord(Structure): - """struct in wincon.h.""" - _fields_ = [ - ("X", c_short), - ("Y", c_short)] - - -class SmallRect(Structure): - """struct in wincon.h.""" - _fields_ = [ - ("Left", c_short), - ("Top", c_short), - ("Right", c_short), - ("Bottom", c_short)] - - -class ConsoleScreenBufferInfo(Structure): - """struct in wincon.h.""" - _fields_ = [ - ("dwSize", Coord), - ("dwCursorPosition", Coord), - ("wAttributes", c_ushort), - ("srWindow", SmallRect), - ("dwMaximumWindowSize", Coord)] - - -class Win32ColorPrinter(LinuxColorPrinter): - """ - Prints in color on windows - """ - def __init__(self): - LinuxColorPrinter.__init__(self) - self._stdout_handle = ctypes.windll.kernel32.GetStdHandle(-11) - self._stderr_handle = ctypes.windll.kernel32.GetStdHandle(-12) - self._default_attr = self._get_text_attr(self._stdout_handle) - - self._color_to_code = {self.RED: 4, - self.GREEN: 2, - self.BLUE: 1, - self.INTENSITY: 8} - - def write(self, text, output_file=None, fg=None, bg=None): - """ - Print the text in color to the output_file - uses stdout if output_file is None - """ - if output_file is None: - output_file = sys.stdout - - if output_file is sys.stdout: - handle = self._stdout_handle - elif output_file is sys.stderr: - handle = self._stderr_handle - else: - handle = None - - if handle is not None: - output_file.flush() - attr = self._default_attr - if fg is not None: - attr &= 0xf0 - attr |= self._decode_color(fg) - if bg is not None: - attr &= 0x0f - attr |= self._decode_color(bg) * 16 - self._set_text_attr(handle, attr) - - output_file.write(text) - - if handle is not None: - output_file.flush() - ctypes.windll.kernel32.SetConsoleTextAttribute(handle, self._default_attr) - - @staticmethod - def _get_text_attr(handle): - """ - Get current text attribute using win-api - """ - csbi = ConsoleScreenBufferInfo() - ctypes.windll.kernel32.GetConsoleScreenBufferInfo(handle, byref(csbi)) - return csbi.wAttributes - - @staticmethod - def _set_text_attr(handle, attr): - """ - Set current text attribute using win-api - """ - ctypes.windll.kernel32.SetConsoleTextAttribute(handle, attr) - - def _decode_color(self, color_str): - """ - Decode color string into numerical color code - """ - code = 0 - for char in color_str: - code |= self._color_to_code.get(char, 0) - return code - - -class NoColorPrinter(object): - """ - Dummy printer that does not print in color - """ - def __init__(self): - pass - - @staticmethod - def write(text, output_file=None, fg=None, bg=None): # pylint: disable=unused-argument - """ - Print the text in color to the output_file - uses stdout if output_file is None - """ - if output_file is None: - output_file = sys.stdout - output_file.write(text) - - -NO_COLOR_PRINTER = NoColorPrinter() -if IS_WINDOWS_SYSTEM: - COLOR_PRINTER = Win32ColorPrinter() -else: - COLOR_PRINTER = LinuxColorPrinter() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Provides capability to print in color to the terminal in both Windows and Linux. +""" + +import sys +import ctypes +from ctypes import Structure, c_short, c_ushort, byref +from vunit.ostools import IS_WINDOWS_SYSTEM + + +class LinuxColorPrinter(object): + """ + Print in color on linux + """ + + BLUE = 'b' + GREEN = 'g' + RED = 'r' + INTENSITY = 'i' + WHITE = RED + GREEN + BLUE + + def __init__(self): + pass + + def write(self, text, output_file=None, fg=None, bg=None): + """ + Print the text in color to the output_file + uses stdout if output_file is None + """ + if output_file is None: + output_file = sys.stdout + + text = self._ansi_wrap(text, fg, bg) + output_file.write(text) + + @staticmethod + def _to_code(rgb): + """ + Translate strings containing 'rgb' characters to numerical color codes + """ + code = 0 + if 'r' in rgb: + code += 1 + + if 'g' in rgb: + code += 2 + + if 'b' in rgb: + code += 4 + return code + + def _ansi_wrap(self, text, fg, bg): + """ + Wrap the text into ANSI color escape codes + fg -- the foreground color + bg -- the background color + """ + codes = [] + + if fg is not None: + codes.append(30 + self._to_code(fg)) + + if bg is not None: + codes.append(40 + self._to_code(bg)) + + if fg is not None and 'i' in fg: + codes.append(1) # Bold + + if bg is not None and 'i' in bg: + codes.append(4) # Underscore + + return "\033[" + ";".join([str(code) for code in codes]) + "m" + text + "\033[0m" + + +class Coord(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("X", c_short), + ("Y", c_short)] + + +class SmallRect(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("Left", c_short), + ("Top", c_short), + ("Right", c_short), + ("Bottom", c_short)] + + +class ConsoleScreenBufferInfo(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("dwSize", Coord), + ("dwCursorPosition", Coord), + ("wAttributes", c_ushort), + ("srWindow", SmallRect), + ("dwMaximumWindowSize", Coord)] + + +class Win32ColorPrinter(LinuxColorPrinter): + """ + Prints in color on windows + """ + def __init__(self): + LinuxColorPrinter.__init__(self) + self._stdout_handle = ctypes.windll.kernel32.GetStdHandle(-11) + self._stderr_handle = ctypes.windll.kernel32.GetStdHandle(-12) + self._default_attr = self._get_text_attr(self._stdout_handle) + + self._color_to_code = {self.RED: 4, + self.GREEN: 2, + self.BLUE: 1, + self.INTENSITY: 8} + + def write(self, text, output_file=None, fg=None, bg=None): + """ + Print the text in color to the output_file + uses stdout if output_file is None + """ + if output_file is None: + output_file = sys.stdout + + if output_file is sys.stdout: + handle = self._stdout_handle + elif output_file is sys.stderr: + handle = self._stderr_handle + else: + handle = None + + if handle is not None: + output_file.flush() + attr = self._default_attr + if fg is not None: + attr &= 0xf0 + attr |= self._decode_color(fg) + if bg is not None: + attr &= 0x0f + attr |= self._decode_color(bg) * 16 + self._set_text_attr(handle, attr) + + output_file.write(text) + + if handle is not None: + output_file.flush() + ctypes.windll.kernel32.SetConsoleTextAttribute(handle, self._default_attr) + + @staticmethod + def _get_text_attr(handle): + """ + Get current text attribute using win-api + """ + csbi = ConsoleScreenBufferInfo() + ctypes.windll.kernel32.GetConsoleScreenBufferInfo(handle, byref(csbi)) + return csbi.wAttributes + + @staticmethod + def _set_text_attr(handle, attr): + """ + Set current text attribute using win-api + """ + ctypes.windll.kernel32.SetConsoleTextAttribute(handle, attr) + + def _decode_color(self, color_str): + """ + Decode color string into numerical color code + """ + code = 0 + for char in color_str: + code |= self._color_to_code.get(char, 0) + return code + + +class NoColorPrinter(object): + """ + Dummy printer that does not print in color + """ + def __init__(self): + pass + + @staticmethod + def write(text, output_file=None, fg=None, bg=None): # pylint: disable=unused-argument + """ + Print the text in color to the output_file + uses stdout if output_file is None + """ + if output_file is None: + output_file = sys.stdout + output_file.write(text) + + +NO_COLOR_PRINTER = NoColorPrinter() +if IS_WINDOWS_SYSTEM: + COLOR_PRINTER = Win32ColorPrinter() +else: + COLOR_PRINTER = LinuxColorPrinter() diff --git a/vunit/com/__init__.py b/vunit/com/__init__.py index a66885c1d..17b27373b 100644 --- a/vunit/com/__init__.py +++ b/vunit/com/__init__.py @@ -1,5 +1,5 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com diff --git a/vunit/com/codec_datatype_template.py b/vunit/com/codec_datatype_template.py index f635e39c2..70fef52ec 100644 --- a/vunit/com/codec_datatype_template.py +++ b/vunit/com/codec_datatype_template.py @@ -1,47 +1,47 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Templates common to all datatype codecs. -""" - -from string import Template - - -class DatatypeCodecTemplate(object): - """Templates when generating codecs""" - - to_string_declarations = Template("""\ - function to_string ( - constant data : $type) - return string; - -""") - - codec_declarations = Template("""\ - function encode ( - constant data : $type) - return string; - alias encode_$type is encode[$type return string]; - function decode ( - constant code : string) - return $type; - alias decode_$type is decode[string return $type]; - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out $type); - alias decode_$type is decode[string, positive, $type]; - procedure push(queue : queue_t; value : $type); - impure function pop(queue : queue_t) return $type; - alias push_$type is push[queue_t, $type]; - alias pop_$type is pop[queue_t return $type]; - procedure push(msg : msg_t; value : $type); - impure function pop(msg : msg_t) return $type; - alias push_$type is push[msg_t, $type]; - alias pop_$type is pop[msg_t return $type]; - -""") +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Templates common to all datatype codecs. +""" + +from string import Template + + +class DatatypeCodecTemplate(object): + """Templates when generating codecs""" + + to_string_declarations = Template("""\ + function to_string ( + constant data : $type) + return string; + +""") + + codec_declarations = Template("""\ + function encode ( + constant data : $type) + return string; + alias encode_$type is encode[$type return string]; + function decode ( + constant code : string) + return $type; + alias decode_$type is decode[string return $type]; + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out $type); + alias decode_$type is decode[string, positive, $type]; + procedure push(queue : queue_t; value : $type); + impure function pop(queue : queue_t) return $type; + alias push_$type is push[queue_t, $type]; + alias pop_$type is pop[queue_t return $type]; + procedure push(msg : msg_t; value : $type); + impure function pop(msg : msg_t) return $type; + alias push_$type is push[msg_t, $type]; + alias pop_$type is pop[msg_t return $type]; + +""") diff --git a/vunit/com/codec_generator.py b/vunit/com/codec_generator.py index 9490d9a6c..600272826 100644 --- a/vunit/com/codec_generator.py +++ b/vunit/com/codec_generator.py @@ -1,77 +1,77 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Module for generating VHDL com codecs. -""" - -from string import Template -from vunit.ostools import read_file, write_file -from vunit.com.codec_vhdl_package import CodecVHDLPackage - - -def generate_codecs(input_package_design_unit, codec_package_name, # pylint: disable=too-many-arguments - used_packages, output_file): - """This function generates codecs for the types in the input package and compile the result into - codec_package_name. used_packages is a list specifying what to include into the result package - other than the input package. A used package on the format 'lib.pkg' will result in a library and - a use statement. A used package on the format 'pkg' is assumed to be located in work. output_file - is where the resulting codec package is written.""" - - # The design unit doesn't contain the package so it must be found first in the source file. This file - # may contain other packages - code = read_file(input_package_design_unit.source_file.name) - package = CodecVHDLPackage.find_named_package(code, input_package_design_unit.name) - if package is None: - raise KeyError(input_package_design_unit.name) - - # Get all function declarations and definitions derived from the package type definitions - declarations, definitions = package.generate_codecs_and_support_functions() - - # Create extra use clauses - use_clauses = '' - libraries = [] - for used_package in used_packages if used_packages is not None else []: - if '.' in used_package: - if used_package.split('.')[0] not in libraries: - libraries.append(used_package.split('.')[0]) - use_clauses += 'use %s.all;\n' % used_package - else: - use_clauses += 'use work.%s.all;\n' % used_package - if libraries: - use_clauses = 'library ' + ';\nlibrary '.join(libraries) + ';\n' + use_clauses - - # Assemble everything and write to output file - codec_package_template = Template("""\ -library vunit_lib; -use vunit_lib.string_ops.all; -context vunit_lib.com_context; -use vunit_lib.queue_pkg.all; -use vunit_lib.queue_2008_pkg.all; - -use std.textio.all; - -use work.$package_name.all; - -$use_clauses -package $codec_package_name is -$declarations -end package $codec_package_name; - -package body $codec_package_name is -$definitions -end package body $codec_package_name; - -""") - - codec_package = codec_package_template.substitute( - declarations=declarations, - definitions=definitions, - package_name=package.identifier, - codec_package_name=codec_package_name, - use_clauses=use_clauses) - - write_file(output_file, codec_package) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Module for generating VHDL com codecs. +""" + +from string import Template +from vunit.ostools import read_file, write_file +from vunit.com.codec_vhdl_package import CodecVHDLPackage + + +def generate_codecs(input_package_design_unit, codec_package_name, # pylint: disable=too-many-arguments + used_packages, output_file): + """This function generates codecs for the types in the input package and compile the result into + codec_package_name. used_packages is a list specifying what to include into the result package + other than the input package. A used package on the format 'lib.pkg' will result in a library and + a use statement. A used package on the format 'pkg' is assumed to be located in work. output_file + is where the resulting codec package is written.""" + + # The design unit doesn't contain the package so it must be found first in the source file. This file + # may contain other packages + code = read_file(input_package_design_unit.source_file.name) + package = CodecVHDLPackage.find_named_package(code, input_package_design_unit.name) + if package is None: + raise KeyError(input_package_design_unit.name) + + # Get all function declarations and definitions derived from the package type definitions + declarations, definitions = package.generate_codecs_and_support_functions() + + # Create extra use clauses + use_clauses = '' + libraries = [] + for used_package in used_packages if used_packages is not None else []: + if '.' in used_package: + if used_package.split('.')[0] not in libraries: + libraries.append(used_package.split('.')[0]) + use_clauses += 'use %s.all;\n' % used_package + else: + use_clauses += 'use work.%s.all;\n' % used_package + if libraries: + use_clauses = 'library ' + ';\nlibrary '.join(libraries) + ';\n' + use_clauses + + # Assemble everything and write to output file + codec_package_template = Template("""\ +library vunit_lib; +use vunit_lib.string_ops.all; +context vunit_lib.com_context; +use vunit_lib.queue_pkg.all; +use vunit_lib.queue_2008_pkg.all; + +use std.textio.all; + +use work.$package_name.all; + +$use_clauses +package $codec_package_name is +$declarations +end package $codec_package_name; + +package body $codec_package_name is +$definitions +end package body $codec_package_name; + +""") + + codec_package = codec_package_template.substitute( + declarations=declarations, + definitions=definitions, + package_name=package.identifier, + codec_package_name=codec_package_name, + use_clauses=use_clauses) + + write_file(output_file, codec_package) diff --git a/vunit/com/codec_vhdl_array_type.py b/vunit/com/codec_vhdl_array_type.py index 7553f2936..4cc22cdd3 100644 --- a/vunit/com/codec_vhdl_array_type.py +++ b/vunit/com/codec_vhdl_array_type.py @@ -1,483 +1,483 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Module containing the CodecVHDLArrayType class. -""" -from string import Template -from vunit.vhdl_parser import VHDLArrayType -from vunit.com.codec_datatype_template import DatatypeCodecTemplate - - -class CodecVHDLArrayType(VHDLArrayType): - """Class derived from VHDLArrayType to provide codec generator functionality constrained and - unconstrained 1D/2D arrays""" - def generate_codecs_and_support_functions(self): - """Generate codecs and communication support functions for the array type.""" - template = ArrayCodecTemplate() - - declarations = '' - definitions = '' - has_one_dimension = self.range2.left is None and self.range2.right is None and \ - self.range2.attribute is None and self.range2.range_type is None - is_constrained = self.range1.range_type is None and self.range2.range_type is None - - declarations += template.codec_declarations.substitute(type=self.identifier) - declarations += template.to_string_declarations.substitute(type=self.identifier) - if is_constrained: - if has_one_dimension: - definitions += template.constrained_1d_array_definition.substitute(type=self.identifier) - definitions += template.constrained_1d_array_to_string_definition.substitute(type=self.identifier) - else: - definitions += template.constrained_2d_array_definition.substitute(type=self.identifier) - definitions += template.constrained_2d_array_to_string_definition.substitute(type=self.identifier) - else: - if has_one_dimension: - init_value = '' - definitions += template.unconstrained_1d_array_definition.substitute(array_type=self.identifier, - init_value=init_value, - range_type=self.range1.range_type) - definitions += template.unconstrained_1d_array_to_string_definition.substitute( - array_type=self.identifier, - range_type=self.range1.range_type) - else: - definitions += template.unconstrained_2d_array_definition.substitute(array_type=self.identifier, - range_type1=self.range1.range_type, - range_type2=self.range2.range_type) - definitions += template.unconstrained_2d_array_to_string_definition.substitute( - array_type=self.identifier, - range_type1=self.range1.range_type, - range_type2=self.range2.range_type) - - return declarations, definitions - - -class ArrayCodecTemplate(DatatypeCodecTemplate): - """This class contains array codec templates.""" - - constrained_1d_array_to_string_definition = Template("""\ - function to_string ( - constant data : $type) - return string is - variable element : string(1 to 2 + data'length * 32); - variable l : line; - variable length : natural; - begin - open_group(l); - for i in data'range loop - append_group(l, encode(data(i))); - end loop; - close_group(l, element, length); - - return element(1 to length); - end function to_string; - -""") - - constrained_2d_array_to_string_definition = Template("""\ - function to_string ( - constant data : $type) - return string is - variable element : string(1 to 2 + data'length(1) * data'length(2) * 32); - variable l : line; - variable length : natural; - begin - open_group(l); - for i in data'range(1) loop - for j in data'range(2) loop - append_group(l, encode(data(i,j))); - end loop; - end loop; - close_group(l, element, length); - - return element(1 to length); - end function to_string; - -""") - - unconstrained_1d_array_to_string_definition = Template("""\ - function to_string ( - constant data : $array_type) - return string is - variable element : string(1 to 2 + data'length * 32); - variable l : line; - variable length : natural; - begin - open_group(l); - for i in data'range loop - append_group(l, encode(data(i))); - end loop; - close_group(l, element, length); - - return create_array_group(element(1 to length), encode(data'left), encode(data'right), data'ascending); - end function to_string; - -""") - - unconstrained_2d_array_to_string_definition = Template("""\ - function to_string ( - constant data : $array_type) - return string is - variable element : string(1 to 2 + data'length(1) * data'length(2) * 32); - variable l : line; - variable length : natural; - begin - open_group(l); - for i in data'range(1) loop - for j in data'range(2) loop - append_group(l, encode(data(i,j))); - end loop; - end loop; - close_group(l, element, length); - - return create_array_group(element(1 to length), encode(data'left(1)), encode(data'right(1)), data'ascending(1), - encode(data'left(2)), encode(data'right(2)), data'ascending(2)); - end function to_string; - -""") - - constrained_1d_array_definition = Template("""\ - function encode ( - constant data : $type) - return string is - constant length : positive := encode(data(data'left))'length; - variable index : positive := 1; - variable ret_val : string(1 to data'length * length); - begin - for i in data'range loop - ret_val(index to index + length - 1) := encode(data(i)); - index := index + length; - end loop; - - return ret_val; - end function encode; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out $type) is - begin - for i in result'range loop - decode(code, index, result(i)); - end loop; - end procedure decode; - - function decode ( - constant code : string) - return $type is - variable ret_val : $type; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end function decode; - - procedure push(queue : queue_t; value : $type) is - begin - push_variable_string(queue, encode(value)); - end; - - impure function pop(queue : queue_t) return $type is - begin - return decode(pop_variable_string(queue)); - end; - - procedure push(msg : msg_t; value : $type) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return $type is - begin - return pop(msg.data); - end; - -""") - - constrained_2d_array_definition = Template("""\ - function encode ( - constant data : $type) - return string is - constant length : positive := encode(data(data'left(1), data'left(2)))'length; - variable index : positive := 1; - variable ret_val : string(1 to data'length(1) * data'length(2) * length); - begin - for i in data'range(1) loop - for j in data'range(2) loop - ret_val(index to index + length - 1) := encode(data(i,j)); - index := index + length; - end loop; - end loop; - - return ret_val; - end function encode; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out $type) is - begin - for i in result'range(1) loop - for j in result'range(2) loop - decode(code, index, result(i,j)); - end loop; - end loop; - end procedure decode; - - function decode ( - constant code : string) - return $type is - variable ret_val : $type; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end function decode; - - procedure push(queue : queue_t; value : $type) is - begin - push_variable_string(queue, encode(value)); - end; - - impure function pop(queue : queue_t) return $type is - begin - return decode(pop_variable_string(queue)); - end; - - procedure push(msg : msg_t; value : $type) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return $type is - begin - return pop(msg.data); - end; - -""") - - unconstrained_1d_array_definition = Template("""\ - function encode ( - constant data : $array_type) - return string is - function element_length ( - constant data : $array_type) - return natural is - begin - if data'length = 0 then - return 0; - else - return encode(data(data'left))'length; - end if; - end; - constant length : natural := element_length(data); - constant range_length : positive := encode(data'left)'length; - variable index : positive := 2 + 2 * range_length; - variable ret_val : string(1 to 1 + 2 * range_length + data'length * length); - begin - ret_val(1 to 1 + 2 * range_length) := encode_array_header(encode(data'left), - encode(data'right), - encode(data'ascending)); - for i in data'range loop - ret_val(index to index + length - 1) := encode(data(i)); - index := index + length; - end loop; - - return ret_val; - end function encode; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out $array_type) is - constant range_length : positive := encode($range_type'left)'length; - begin - index := index + 1 + 2 * range_length; - for i in result'range loop - decode(code, index, result(i)); - end loop; - end procedure decode; - - function decode ( - constant code : string) - return $array_type is - constant range_length : positive := encode($range_type'left)'length; - function ret_val_range ( - constant code : string) - return $array_type is - constant range_left : $range_type := decode(code(code'left to code'left + range_length - 1)); - constant range_right : $range_type := decode(code(code'left + range_length to code'left + 2 * range_length - 1)); - constant is_ascending : boolean := decode(code(code'left + 2 * range_length to code'left + 2 *range_length)); - variable ret_val_ascending : $array_type(range_left to range_right); - variable ret_val_descending : $array_type(range_left downto range_right); - begin - if is_ascending then - return ret_val_ascending; - else - return ret_val_descending; - end if; - end function ret_val_range; - constant array_of_correct_range : $array_type := ret_val_range(code); - variable ret_val : $array_type(array_of_correct_range'range)$init_value; - variable index : positive := code'left + 1 + 2 * range_length; - begin - for i in ret_val'range loop - decode(code, index, ret_val(i)); - end loop; - - return ret_val; - end function decode; - - procedure push(queue : queue_t; value : $array_type) is - begin - push_variable_string(queue, encode(value)); - end; - - impure function pop(queue : queue_t) return $array_type is - begin - return decode(pop_variable_string(queue)); - end; - - procedure push(msg : msg_t; value : $array_type) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return $array_type is - begin - return pop(msg.data); - end; - -""") - - unconstrained_2d_array_definition = Template("""\ - function encode ( - constant data : $array_type) - return string is - function element_length ( - constant data : $array_type) - return natural is - begin - if data'length(1) * data'length(2) = 0 then - return 0; - else - return encode(data(data'left(1), data'left(2)))'length; - end if; - end; - constant length : natural := element_length(data); - constant range1_length : positive := encode(data'left(1))'length; - constant range2_length : positive := encode(data'left(2))'length; - variable index : positive := 3 + 2 * range1_length + 2 * range2_length; - variable ret_val : string(1 to 2 + 2 * range1_length + 2 * range2_length + - data'length(1) * data'length(2) * length); - begin - ret_val(1 to 2 + 2 * range1_length + 2 * range2_length) := - encode_array_header(encode(data'left(1)), encode(data'right(1)), encode(data'ascending(1)), - encode(data'left(2)), encode(data'right(2)), encode(data'ascending(2))); - for i in data'range(1) loop - for j in data'range(2) loop - ret_val(index to index + length - 1) := encode(data(i,j)); - index := index + length; - end loop; - end loop; - - return ret_val; - end function encode; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out $array_type) is - constant range1_length : positive := encode($range_type1'left)'length; - constant range2_length : positive := encode($range_type2'left)'length; - begin - index := index + 2 + 2 * range1_length + 2 * range2_length; - for i in result'range(1) loop - for j in result'range(2) loop - decode(code, index, result(i,j)); - end loop; - end loop; - end procedure decode; - - function decode ( - constant code : string) - return $array_type is - constant range1_length : positive := encode($range_type1'left)'length; - constant range2_length : positive := encode($range_type2'left)'length; - function ret_val_range ( - constant code : string) - return $array_type is - constant range_left1 : $range_type1 := decode(code(code'left to code'left + range1_length - 1)); - constant range_right1 : $range_type1 := decode(code(code'left + range1_length to - code'left + 2 * range1_length - 1)); - constant is_ascending1 : boolean := decode(code(code'left + 2 * range1_length to - code'left + 2 * range1_length)); - constant range_left2 : $range_type2 := decode(code(code'left + 2 * range1_length + 1 to - code'left + 2 * range1_length + range2_length)); - constant range_right2 : $range_type2 := decode(code(code'left + 2 * range1_length + range2_length + 1 to - code'left + 2 * range1_length + 2 * range2_length)); - constant is_ascending2 : boolean := decode(code(code'left + 2 * range1_length + 2 * range2_length + 1 to - code'left + 2 * range1_length + 2 * range2_length + 1)); - variable ret_val_ascending_ascending : $array_type(range_left1 to range_right1, - range_left2 to range_right2); - variable ret_val_ascending_decending : $array_type(range_left1 to range_right1, - range_left2 downto range_right2); - variable ret_val_decending_ascending : $array_type(range_left1 downto range_right1, - range_left2 to range_right2); - variable ret_val_decending_decending : $array_type(range_left1 downto range_right1, - range_left2 downto range_right2); - begin - if is_ascending1 then - if is_ascending2 then - return ret_val_ascending_ascending; - else - return ret_val_ascending_decending; - end if; - else - if is_ascending2 then - return ret_val_decending_ascending; - else - return ret_val_decending_decending; - end if; - end if; - end function ret_val_range; - - constant array_of_correct_range : $array_type := ret_val_range(code); - variable ret_val : $array_type(array_of_correct_range'range(1), array_of_correct_range'range(2)); - variable index : positive := code'left + 2 + 2 * range1_length + 2 * range2_length; - begin - for i in ret_val'range(1) loop - for j in ret_val'range(2) loop - decode(code, index, ret_val(i,j)); - end loop; - end loop; - - return ret_val; - end function decode; - - procedure push(queue : queue_t; value : $array_type) is - begin - push_variable_string(queue, encode(value)); - end; - - impure function pop(queue : queue_t) return $array_type is - begin - return decode(pop_variable_string(queue)); - end; - - procedure push(msg : msg_t; value : $array_type) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return $array_type is - begin - return pop(msg.data); - end; - -""") +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Module containing the CodecVHDLArrayType class. +""" +from string import Template +from vunit.vhdl_parser import VHDLArrayType +from vunit.com.codec_datatype_template import DatatypeCodecTemplate + + +class CodecVHDLArrayType(VHDLArrayType): + """Class derived from VHDLArrayType to provide codec generator functionality constrained and + unconstrained 1D/2D arrays""" + def generate_codecs_and_support_functions(self): + """Generate codecs and communication support functions for the array type.""" + template = ArrayCodecTemplate() + + declarations = '' + definitions = '' + has_one_dimension = self.range2.left is None and self.range2.right is None and \ + self.range2.attribute is None and self.range2.range_type is None + is_constrained = self.range1.range_type is None and self.range2.range_type is None + + declarations += template.codec_declarations.substitute(type=self.identifier) + declarations += template.to_string_declarations.substitute(type=self.identifier) + if is_constrained: + if has_one_dimension: + definitions += template.constrained_1d_array_definition.substitute(type=self.identifier) + definitions += template.constrained_1d_array_to_string_definition.substitute(type=self.identifier) + else: + definitions += template.constrained_2d_array_definition.substitute(type=self.identifier) + definitions += template.constrained_2d_array_to_string_definition.substitute(type=self.identifier) + else: + if has_one_dimension: + init_value = '' + definitions += template.unconstrained_1d_array_definition.substitute(array_type=self.identifier, + init_value=init_value, + range_type=self.range1.range_type) + definitions += template.unconstrained_1d_array_to_string_definition.substitute( + array_type=self.identifier, + range_type=self.range1.range_type) + else: + definitions += template.unconstrained_2d_array_definition.substitute(array_type=self.identifier, + range_type1=self.range1.range_type, + range_type2=self.range2.range_type) + definitions += template.unconstrained_2d_array_to_string_definition.substitute( + array_type=self.identifier, + range_type1=self.range1.range_type, + range_type2=self.range2.range_type) + + return declarations, definitions + + +class ArrayCodecTemplate(DatatypeCodecTemplate): + """This class contains array codec templates.""" + + constrained_1d_array_to_string_definition = Template("""\ + function to_string ( + constant data : $type) + return string is + variable element : string(1 to 2 + data'length * 32); + variable l : line; + variable length : natural; + begin + open_group(l); + for i in data'range loop + append_group(l, encode(data(i))); + end loop; + close_group(l, element, length); + + return element(1 to length); + end function to_string; + +""") + + constrained_2d_array_to_string_definition = Template("""\ + function to_string ( + constant data : $type) + return string is + variable element : string(1 to 2 + data'length(1) * data'length(2) * 32); + variable l : line; + variable length : natural; + begin + open_group(l); + for i in data'range(1) loop + for j in data'range(2) loop + append_group(l, encode(data(i,j))); + end loop; + end loop; + close_group(l, element, length); + + return element(1 to length); + end function to_string; + +""") + + unconstrained_1d_array_to_string_definition = Template("""\ + function to_string ( + constant data : $array_type) + return string is + variable element : string(1 to 2 + data'length * 32); + variable l : line; + variable length : natural; + begin + open_group(l); + for i in data'range loop + append_group(l, encode(data(i))); + end loop; + close_group(l, element, length); + + return create_array_group(element(1 to length), encode(data'left), encode(data'right), data'ascending); + end function to_string; + +""") + + unconstrained_2d_array_to_string_definition = Template("""\ + function to_string ( + constant data : $array_type) + return string is + variable element : string(1 to 2 + data'length(1) * data'length(2) * 32); + variable l : line; + variable length : natural; + begin + open_group(l); + for i in data'range(1) loop + for j in data'range(2) loop + append_group(l, encode(data(i,j))); + end loop; + end loop; + close_group(l, element, length); + + return create_array_group(element(1 to length), encode(data'left(1)), encode(data'right(1)), data'ascending(1), + encode(data'left(2)), encode(data'right(2)), data'ascending(2)); + end function to_string; + +""") + + constrained_1d_array_definition = Template("""\ + function encode ( + constant data : $type) + return string is + constant length : positive := encode(data(data'left))'length; + variable index : positive := 1; + variable ret_val : string(1 to data'length * length); + begin + for i in data'range loop + ret_val(index to index + length - 1) := encode(data(i)); + index := index + length; + end loop; + + return ret_val; + end function encode; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out $type) is + begin + for i in result'range loop + decode(code, index, result(i)); + end loop; + end procedure decode; + + function decode ( + constant code : string) + return $type is + variable ret_val : $type; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end function decode; + + procedure push(queue : queue_t; value : $type) is + begin + push_variable_string(queue, encode(value)); + end; + + impure function pop(queue : queue_t) return $type is + begin + return decode(pop_variable_string(queue)); + end; + + procedure push(msg : msg_t; value : $type) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return $type is + begin + return pop(msg.data); + end; + +""") + + constrained_2d_array_definition = Template("""\ + function encode ( + constant data : $type) + return string is + constant length : positive := encode(data(data'left(1), data'left(2)))'length; + variable index : positive := 1; + variable ret_val : string(1 to data'length(1) * data'length(2) * length); + begin + for i in data'range(1) loop + for j in data'range(2) loop + ret_val(index to index + length - 1) := encode(data(i,j)); + index := index + length; + end loop; + end loop; + + return ret_val; + end function encode; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out $type) is + begin + for i in result'range(1) loop + for j in result'range(2) loop + decode(code, index, result(i,j)); + end loop; + end loop; + end procedure decode; + + function decode ( + constant code : string) + return $type is + variable ret_val : $type; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end function decode; + + procedure push(queue : queue_t; value : $type) is + begin + push_variable_string(queue, encode(value)); + end; + + impure function pop(queue : queue_t) return $type is + begin + return decode(pop_variable_string(queue)); + end; + + procedure push(msg : msg_t; value : $type) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return $type is + begin + return pop(msg.data); + end; + +""") + + unconstrained_1d_array_definition = Template("""\ + function encode ( + constant data : $array_type) + return string is + function element_length ( + constant data : $array_type) + return natural is + begin + if data'length = 0 then + return 0; + else + return encode(data(data'left))'length; + end if; + end; + constant length : natural := element_length(data); + constant range_length : positive := encode(data'left)'length; + variable index : positive := 2 + 2 * range_length; + variable ret_val : string(1 to 1 + 2 * range_length + data'length * length); + begin + ret_val(1 to 1 + 2 * range_length) := encode_array_header(encode(data'left), + encode(data'right), + encode(data'ascending)); + for i in data'range loop + ret_val(index to index + length - 1) := encode(data(i)); + index := index + length; + end loop; + + return ret_val; + end function encode; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out $array_type) is + constant range_length : positive := encode($range_type'left)'length; + begin + index := index + 1 + 2 * range_length; + for i in result'range loop + decode(code, index, result(i)); + end loop; + end procedure decode; + + function decode ( + constant code : string) + return $array_type is + constant range_length : positive := encode($range_type'left)'length; + function ret_val_range ( + constant code : string) + return $array_type is + constant range_left : $range_type := decode(code(code'left to code'left + range_length - 1)); + constant range_right : $range_type := decode(code(code'left + range_length to code'left + 2 * range_length - 1)); + constant is_ascending : boolean := decode(code(code'left + 2 * range_length to code'left + 2 *range_length)); + variable ret_val_ascending : $array_type(range_left to range_right); + variable ret_val_descending : $array_type(range_left downto range_right); + begin + if is_ascending then + return ret_val_ascending; + else + return ret_val_descending; + end if; + end function ret_val_range; + constant array_of_correct_range : $array_type := ret_val_range(code); + variable ret_val : $array_type(array_of_correct_range'range)$init_value; + variable index : positive := code'left + 1 + 2 * range_length; + begin + for i in ret_val'range loop + decode(code, index, ret_val(i)); + end loop; + + return ret_val; + end function decode; + + procedure push(queue : queue_t; value : $array_type) is + begin + push_variable_string(queue, encode(value)); + end; + + impure function pop(queue : queue_t) return $array_type is + begin + return decode(pop_variable_string(queue)); + end; + + procedure push(msg : msg_t; value : $array_type) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return $array_type is + begin + return pop(msg.data); + end; + +""") + + unconstrained_2d_array_definition = Template("""\ + function encode ( + constant data : $array_type) + return string is + function element_length ( + constant data : $array_type) + return natural is + begin + if data'length(1) * data'length(2) = 0 then + return 0; + else + return encode(data(data'left(1), data'left(2)))'length; + end if; + end; + constant length : natural := element_length(data); + constant range1_length : positive := encode(data'left(1))'length; + constant range2_length : positive := encode(data'left(2))'length; + variable index : positive := 3 + 2 * range1_length + 2 * range2_length; + variable ret_val : string(1 to 2 + 2 * range1_length + 2 * range2_length + + data'length(1) * data'length(2) * length); + begin + ret_val(1 to 2 + 2 * range1_length + 2 * range2_length) := + encode_array_header(encode(data'left(1)), encode(data'right(1)), encode(data'ascending(1)), + encode(data'left(2)), encode(data'right(2)), encode(data'ascending(2))); + for i in data'range(1) loop + for j in data'range(2) loop + ret_val(index to index + length - 1) := encode(data(i,j)); + index := index + length; + end loop; + end loop; + + return ret_val; + end function encode; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out $array_type) is + constant range1_length : positive := encode($range_type1'left)'length; + constant range2_length : positive := encode($range_type2'left)'length; + begin + index := index + 2 + 2 * range1_length + 2 * range2_length; + for i in result'range(1) loop + for j in result'range(2) loop + decode(code, index, result(i,j)); + end loop; + end loop; + end procedure decode; + + function decode ( + constant code : string) + return $array_type is + constant range1_length : positive := encode($range_type1'left)'length; + constant range2_length : positive := encode($range_type2'left)'length; + function ret_val_range ( + constant code : string) + return $array_type is + constant range_left1 : $range_type1 := decode(code(code'left to code'left + range1_length - 1)); + constant range_right1 : $range_type1 := decode(code(code'left + range1_length to + code'left + 2 * range1_length - 1)); + constant is_ascending1 : boolean := decode(code(code'left + 2 * range1_length to + code'left + 2 * range1_length)); + constant range_left2 : $range_type2 := decode(code(code'left + 2 * range1_length + 1 to + code'left + 2 * range1_length + range2_length)); + constant range_right2 : $range_type2 := decode(code(code'left + 2 * range1_length + range2_length + 1 to + code'left + 2 * range1_length + 2 * range2_length)); + constant is_ascending2 : boolean := decode(code(code'left + 2 * range1_length + 2 * range2_length + 1 to + code'left + 2 * range1_length + 2 * range2_length + 1)); + variable ret_val_ascending_ascending : $array_type(range_left1 to range_right1, + range_left2 to range_right2); + variable ret_val_ascending_decending : $array_type(range_left1 to range_right1, + range_left2 downto range_right2); + variable ret_val_decending_ascending : $array_type(range_left1 downto range_right1, + range_left2 to range_right2); + variable ret_val_decending_decending : $array_type(range_left1 downto range_right1, + range_left2 downto range_right2); + begin + if is_ascending1 then + if is_ascending2 then + return ret_val_ascending_ascending; + else + return ret_val_ascending_decending; + end if; + else + if is_ascending2 then + return ret_val_decending_ascending; + else + return ret_val_decending_decending; + end if; + end if; + end function ret_val_range; + + constant array_of_correct_range : $array_type := ret_val_range(code); + variable ret_val : $array_type(array_of_correct_range'range(1), array_of_correct_range'range(2)); + variable index : positive := code'left + 2 + 2 * range1_length + 2 * range2_length; + begin + for i in ret_val'range(1) loop + for j in ret_val'range(2) loop + decode(code, index, ret_val(i,j)); + end loop; + end loop; + + return ret_val; + end function decode; + + procedure push(queue : queue_t; value : $array_type) is + begin + push_variable_string(queue, encode(value)); + end; + + impure function pop(queue : queue_t) return $array_type is + begin + return decode(pop_variable_string(queue)); + end; + + procedure push(msg : msg_t; value : $array_type) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return $array_type is + begin + return pop(msg.data); + end; + +""") diff --git a/vunit/com/codec_vhdl_enumeration_type.py b/vunit/com/codec_vhdl_enumeration_type.py index f47ada38f..8cb339f47 100644 --- a/vunit/com/codec_vhdl_enumeration_type.py +++ b/vunit/com/codec_vhdl_enumeration_type.py @@ -1,99 +1,99 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Module containing the CodecVHDLEnumerationType class. -""" -from string import Template -from vunit.vhdl_parser import VHDLEnumerationType -from vunit.com.codec_datatype_template import DatatypeCodecTemplate - - -class CodecVHDLEnumerationType(VHDLEnumerationType): - """Class derived from VHDLEnumerationType to provide codec generator functionality for the enumerated type.""" - def generate_codecs_and_support_functions(self, offset=0): - """Generate codecs and communication support functions for the enumerated type.""" - - template = EnumerationCodecTemplate() - - declarations = '' - definitions = '' - - if len(self.literals) > 256: - raise NotImplementedError('Support for enums with more than 256 values are yet to be implemented') - - declarations += template.codec_declarations.substitute(type=self.identifier) - definitions += template.enumeration_codec_definitions.substitute(type=self.identifier, offset=offset) - declarations += template.to_string_declarations.substitute(type=self.identifier) - definitions += template.enumeration_to_string_definitions.substitute(type=self.identifier) - - return declarations, definitions - - -class EnumerationCodecTemplate(DatatypeCodecTemplate): - """This class contains enumeration codec templates.""" - - enumeration_to_string_definitions = Template("""\ - function to_string ( - constant data : $type) - return string is - begin - return $type'image(data); - end function to_string; - -""") - - enumeration_codec_definitions = Template("""\ - function encode ( - constant data : $type) - return string is - constant offset : natural := $offset; - begin - return (1 => character'val($type'pos(data) + offset)); - end function encode; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out $type) is - constant offset : natural := $offset; - begin - result := $type'val(character'pos(code(index)) - offset); - index := index + 1; - end procedure decode; - - function decode ( - constant code : string) - return $type is - variable ret_val : $type; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end function decode; - - procedure push(queue : queue_t; value : $type) is - begin - push_fix_string(queue, encode(value)); - end; - - impure function pop(queue : queue_t) return $type is - begin - return decode(pop_fix_string(queue, 1)); - end; - - procedure push(msg : msg_t; value : $type) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return $type is - begin - return pop(msg.data); - end; - -""") +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Module containing the CodecVHDLEnumerationType class. +""" +from string import Template +from vunit.vhdl_parser import VHDLEnumerationType +from vunit.com.codec_datatype_template import DatatypeCodecTemplate + + +class CodecVHDLEnumerationType(VHDLEnumerationType): + """Class derived from VHDLEnumerationType to provide codec generator functionality for the enumerated type.""" + def generate_codecs_and_support_functions(self, offset=0): + """Generate codecs and communication support functions for the enumerated type.""" + + template = EnumerationCodecTemplate() + + declarations = '' + definitions = '' + + if len(self.literals) > 256: + raise NotImplementedError('Support for enums with more than 256 values are yet to be implemented') + + declarations += template.codec_declarations.substitute(type=self.identifier) + definitions += template.enumeration_codec_definitions.substitute(type=self.identifier, offset=offset) + declarations += template.to_string_declarations.substitute(type=self.identifier) + definitions += template.enumeration_to_string_definitions.substitute(type=self.identifier) + + return declarations, definitions + + +class EnumerationCodecTemplate(DatatypeCodecTemplate): + """This class contains enumeration codec templates.""" + + enumeration_to_string_definitions = Template("""\ + function to_string ( + constant data : $type) + return string is + begin + return $type'image(data); + end function to_string; + +""") + + enumeration_codec_definitions = Template("""\ + function encode ( + constant data : $type) + return string is + constant offset : natural := $offset; + begin + return (1 => character'val($type'pos(data) + offset)); + end function encode; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out $type) is + constant offset : natural := $offset; + begin + result := $type'val(character'pos(code(index)) - offset); + index := index + 1; + end procedure decode; + + function decode ( + constant code : string) + return $type is + variable ret_val : $type; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end function decode; + + procedure push(queue : queue_t; value : $type) is + begin + push_fix_string(queue, encode(value)); + end; + + impure function pop(queue : queue_t) return $type is + begin + return decode(pop_fix_string(queue, 1)); + end; + + procedure push(msg : msg_t; value : $type) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return $type is + begin + return pop(msg.data); + end; + +""") diff --git a/vunit/com/codec_vhdl_package.py b/vunit/com/codec_vhdl_package.py index 19a0991b8..94aca5a38 100644 --- a/vunit/com/codec_vhdl_package.py +++ b/vunit/com/codec_vhdl_package.py @@ -1,314 +1,314 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Module containing the CodecVHDLPackage class. -""" -from string import Template -from vunit.vhdl_parser import VHDLPackage -from vunit.vhdl_parser import remove_comments -from vunit.com.codec_vhdl_enumeration_type import CodecVHDLEnumerationType -from vunit.com.codec_vhdl_array_type import CodecVHDLArrayType -from vunit.com.codec_vhdl_record_type import CodecVHDLRecordType - - -class CodecVHDLPackage(VHDLPackage): - """Class derived from VHDLPackage to provide codec generator functionality for the data types definied - in the package.""" - - def __init__(self, identifier, - enumeration_types, record_types, array_types): - super(CodecVHDLPackage, self).__init__(identifier, - enumeration_types, - record_types, - array_types) - self._template = None - - @classmethod - def parse(cls, code): - """ - Return a new VHDLPackage instance for a single package found within the code - """ - code = remove_comments(code).lower() - # Extract identifier - identifier = cls._package_start_re.match(code).group('id') - enumeration_types = [e for e in CodecVHDLEnumerationType.find(code)] - record_types = [r for r in CodecVHDLRecordType.find(code)] - array_types = [a for a in CodecVHDLArrayType.find(code)] - - return cls(identifier, enumeration_types, record_types, array_types) - - @classmethod - def find_named_package(cls, code, name): - """Find and return the named package in the code (if it exists)""" - - for package in cls.find(code): - if package.identifier == name: - return package - - return None - - def generate_codecs_and_support_functions(self): - """Generate codecs and communication support functions for the data types defined in self.""" - - self._template = PackageCodecTemplate() - - declarations = '' - definitions = '' - - # Record - new_declarations, new_definitions = self._generate_record_codec_and_to_string_functions() - declarations += new_declarations - definitions += new_definitions - - new_declarations, new_definitions = self._generate_msg_type_encoders() - declarations += new_declarations - definitions += new_definitions - - new_declarations, new_definitions = self._generate_get_functions() - declarations += new_declarations - definitions += new_definitions - - # Enumerations - all_msg_types_enumeration_type, msg_type_enumeration_types = self._create_enumeration_of_all_msg_types() - if all_msg_types_enumeration_type is not None: - declarations += self._template.all_msg_types_enumeration_type_declaration.substitute( - identifier=all_msg_types_enumeration_type.identifier, - literals=', '.join(all_msg_types_enumeration_type.literals)) - - if all_msg_types_enumeration_type is not None: - declarations += \ - self._template.get_msg_type_declaration.substitute(type=all_msg_types_enumeration_type.identifier) - definitions += \ - self._template.get_msg_type_definition.substitute(type=all_msg_types_enumeration_type.identifier) - - new_declarations, new_definitions = \ - self._generate_enumeration_codec_and_to_string_functions(all_msg_types_enumeration_type, - msg_type_enumeration_types) - declarations += new_declarations - definitions += new_definitions - - # Arrays - new_declarations, new_definitions = self._generate_array_codec_and_to_string_functions() - declarations += new_declarations - definitions += new_definitions - - return declarations, definitions - - def _generate_record_codec_and_to_string_functions(self): - """Generate codecs and to_string functions for all record data types.""" - - declarations = '' - definitions = '' - for record in self.record_types: - new_declarations, new_definitions = record.generate_codecs_and_support_functions() - declarations += new_declarations - definitions += new_definitions - return declarations, definitions - - def _generate_array_codec_and_to_string_functions(self): - """Generate codecs and to_string functions for all array data types.""" - - declarations = '' - definitions = '' - for array in self.array_types: - new_declarations, new_definitions = array.generate_codecs_and_support_functions() - declarations += new_declarations - definitions += new_definitions - - return declarations, definitions - - def _create_enumeration_of_all_msg_types(self): - """Create an enumeration type containing all valid message types. These message types are collected from - records with a msg_type element which has an enumerated data type.""" - - msg_type_enumeration_types = [] - for record in self.record_types: - if record.elements[0].identifier_list[0] == 'msg_type': - msg_type_enumeration_types.append(record.elements[0].subtype_indication.code) - - msg_type_enumeration_literals = [] - for enum in self.enumeration_types: - if enum.identifier in msg_type_enumeration_types: - for literal in enum.literals: - if literal in msg_type_enumeration_literals: - raise RuntimeError('Different msg_type enumerations may not have the same literals') - - msg_type_enumeration_literals.append(literal) - - if msg_type_enumeration_literals: - all_msg_types_enumeration_type = CodecVHDLEnumerationType(self.identifier + '_msg_type_t', - msg_type_enumeration_literals) - else: - all_msg_types_enumeration_type = None - - return all_msg_types_enumeration_type, msg_type_enumeration_types - - def _generate_enumeration_codec_and_to_string_functions(self, - all_msg_types_enumeration_type, - msg_type_enumeration_types): - """Generate codecs and to_string functions for all enumeration data types.""" - - declarations = '' - definitions = '' - enumeration_offset = 0 - for enum in self.enumeration_types + ([all_msg_types_enumeration_type] if - all_msg_types_enumeration_type is not None else []): - - if enum.identifier in msg_type_enumeration_types: - offset = enumeration_offset - enumeration_offset += len(enum.literals) - else: - offset = 0 - - new_declarations, new_definitions = enum.generate_codecs_and_support_functions(offset) - declarations += new_declarations - definitions += new_definitions - - return declarations, definitions - - def _generate_msg_type_encoders(self): # pylint: disable=too-many-locals - """Generate message type encoders for records with the initial element = msg_type. An encoder is - generated for each value of the enumeration data type for msg_type. For example, if the record - has two message types, read and write, and two other fields, addr and data, then two encoders, - read(addr, data) and write(addr, data) will be generated. These are shorthands for - encode((, addr, data))""" - - declarations = '' - definitions = '' - - enumeration_types = {} - for enum in self.enumeration_types: - enumeration_types[enum.identifier] = enum.literals - - msg_type_record_types = self._get_records_with_an_initial_msg_type_element() - - for record in msg_type_record_types: - msg_type_values = enumeration_types.get(record.elements[0].subtype_indication.type_mark) - - if msg_type_values is None: - continue - - for value in msg_type_values: - parameter_list = [] - parameter_type_list = [] - encoding_list = [] - for element in record.elements: - for identifier in element.identifier_list: - if identifier != 'msg_type': - parameter_list.append(' constant %s : %s' % (identifier, - element.subtype_indication.code)) - parameter_type_list.append(element.subtype_indication.type_mark) - encoding_list.append('encode(%s)' % identifier) - else: - encoding_list.append("encode(%s'(%s))" % (element.subtype_indication.code, value)) - - if parameter_list == []: - parameter_part = '' - alias_signature = value + '[return string];' - else: - parameter_part = ' (\n' + ';\n'.join(parameter_list) + ')' - alias_signature = value + '[' + ', '.join(parameter_type_list) + ' return string];' - - encodings = ' & '.join(encoding_list) - - declarations += \ - self._template.msg_type_record_codec_declaration.substitute(name=value, - parameter_part=parameter_part, - alias_signature=alias_signature, - alias_name=value + '_msg') - definitions += \ - self._template.msg_type_record_codec_definition.substitute(name=value, - parameter_part=parameter_part, - num_of_encodings=len(encoding_list), - encodings=encodings) - - return declarations, definitions - - def _generate_get_functions(self): - """Generate a get function which will return the message type for records""" - - declarations = '' - definitions = '' - - msg_type_record_types = self._get_records_with_an_initial_msg_type_element() - msg_type_types = [] - for record in msg_type_record_types: - msg_type_type = record.elements[0].subtype_indication.code - if msg_type_type not in msg_type_types: - msg_type_types.append(msg_type_type) - declarations += self._template.get_specific_msg_type_declaration.substitute(type=msg_type_type) - definitions += self._template.get_specific_msg_type_definition.substitute(type=msg_type_type) - - return declarations, definitions - - def _get_records_with_an_initial_msg_type_element(self): - """Find all record types starting with a msg_type element""" - - msg_type_record_types = [] - for record in self.record_types: - if record.elements[0].identifier_list[0] == 'msg_type': - msg_type_record_types.append(record) - - return msg_type_record_types - - -class PackageCodecTemplate(object): - """This class contains package codec templates.""" - - msg_type_record_codec_declaration = Template("""\ - function $name$parameter_part - return string; - alias $alias_name is $alias_signature - -""") - - get_specific_msg_type_declaration = Template("""\ - function get_$type ( - constant code : string) - return $type; - -""") - - all_msg_types_enumeration_type_declaration = Template("""\ - type $identifier is ($literals); -""") - - get_msg_type_declaration = Template("""\ - function get_msg_type ( - constant code : string) - return $type; - -""") - - msg_type_record_codec_definition = Template("""\ - function $name$parameter_part - return string is - begin - return $encodings; - end function $name; - -""") - - get_specific_msg_type_definition = Template("""\ - function get_$type ( - constant code : string) - return $type is - begin - return decode(code); - end; - -""") - - get_msg_type_definition = Template("""\ - function get_msg_type ( - constant code : string) - return $type is - begin - return decode(code); - end; - -""") +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Module containing the CodecVHDLPackage class. +""" +from string import Template +from vunit.vhdl_parser import VHDLPackage +from vunit.vhdl_parser import remove_comments +from vunit.com.codec_vhdl_enumeration_type import CodecVHDLEnumerationType +from vunit.com.codec_vhdl_array_type import CodecVHDLArrayType +from vunit.com.codec_vhdl_record_type import CodecVHDLRecordType + + +class CodecVHDLPackage(VHDLPackage): + """Class derived from VHDLPackage to provide codec generator functionality for the data types definied + in the package.""" + + def __init__(self, identifier, + enumeration_types, record_types, array_types): + super(CodecVHDLPackage, self).__init__(identifier, + enumeration_types, + record_types, + array_types) + self._template = None + + @classmethod + def parse(cls, code): + """ + Return a new VHDLPackage instance for a single package found within the code + """ + code = remove_comments(code).lower() + # Extract identifier + identifier = cls._package_start_re.match(code).group('id') + enumeration_types = [e for e in CodecVHDLEnumerationType.find(code)] + record_types = [r for r in CodecVHDLRecordType.find(code)] + array_types = [a for a in CodecVHDLArrayType.find(code)] + + return cls(identifier, enumeration_types, record_types, array_types) + + @classmethod + def find_named_package(cls, code, name): + """Find and return the named package in the code (if it exists)""" + + for package in cls.find(code): + if package.identifier == name: + return package + + return None + + def generate_codecs_and_support_functions(self): + """Generate codecs and communication support functions for the data types defined in self.""" + + self._template = PackageCodecTemplate() + + declarations = '' + definitions = '' + + # Record + new_declarations, new_definitions = self._generate_record_codec_and_to_string_functions() + declarations += new_declarations + definitions += new_definitions + + new_declarations, new_definitions = self._generate_msg_type_encoders() + declarations += new_declarations + definitions += new_definitions + + new_declarations, new_definitions = self._generate_get_functions() + declarations += new_declarations + definitions += new_definitions + + # Enumerations + all_msg_types_enumeration_type, msg_type_enumeration_types = self._create_enumeration_of_all_msg_types() + if all_msg_types_enumeration_type is not None: + declarations += self._template.all_msg_types_enumeration_type_declaration.substitute( + identifier=all_msg_types_enumeration_type.identifier, + literals=', '.join(all_msg_types_enumeration_type.literals)) + + if all_msg_types_enumeration_type is not None: + declarations += \ + self._template.get_msg_type_declaration.substitute(type=all_msg_types_enumeration_type.identifier) + definitions += \ + self._template.get_msg_type_definition.substitute(type=all_msg_types_enumeration_type.identifier) + + new_declarations, new_definitions = \ + self._generate_enumeration_codec_and_to_string_functions(all_msg_types_enumeration_type, + msg_type_enumeration_types) + declarations += new_declarations + definitions += new_definitions + + # Arrays + new_declarations, new_definitions = self._generate_array_codec_and_to_string_functions() + declarations += new_declarations + definitions += new_definitions + + return declarations, definitions + + def _generate_record_codec_and_to_string_functions(self): + """Generate codecs and to_string functions for all record data types.""" + + declarations = '' + definitions = '' + for record in self.record_types: + new_declarations, new_definitions = record.generate_codecs_and_support_functions() + declarations += new_declarations + definitions += new_definitions + return declarations, definitions + + def _generate_array_codec_and_to_string_functions(self): + """Generate codecs and to_string functions for all array data types.""" + + declarations = '' + definitions = '' + for array in self.array_types: + new_declarations, new_definitions = array.generate_codecs_and_support_functions() + declarations += new_declarations + definitions += new_definitions + + return declarations, definitions + + def _create_enumeration_of_all_msg_types(self): + """Create an enumeration type containing all valid message types. These message types are collected from + records with a msg_type element which has an enumerated data type.""" + + msg_type_enumeration_types = [] + for record in self.record_types: + if record.elements[0].identifier_list[0] == 'msg_type': + msg_type_enumeration_types.append(record.elements[0].subtype_indication.code) + + msg_type_enumeration_literals = [] + for enum in self.enumeration_types: + if enum.identifier in msg_type_enumeration_types: + for literal in enum.literals: + if literal in msg_type_enumeration_literals: + raise RuntimeError('Different msg_type enumerations may not have the same literals') + + msg_type_enumeration_literals.append(literal) + + if msg_type_enumeration_literals: + all_msg_types_enumeration_type = CodecVHDLEnumerationType(self.identifier + '_msg_type_t', + msg_type_enumeration_literals) + else: + all_msg_types_enumeration_type = None + + return all_msg_types_enumeration_type, msg_type_enumeration_types + + def _generate_enumeration_codec_and_to_string_functions(self, + all_msg_types_enumeration_type, + msg_type_enumeration_types): + """Generate codecs and to_string functions for all enumeration data types.""" + + declarations = '' + definitions = '' + enumeration_offset = 0 + for enum in self.enumeration_types + ([all_msg_types_enumeration_type] if + all_msg_types_enumeration_type is not None else []): + + if enum.identifier in msg_type_enumeration_types: + offset = enumeration_offset + enumeration_offset += len(enum.literals) + else: + offset = 0 + + new_declarations, new_definitions = enum.generate_codecs_and_support_functions(offset) + declarations += new_declarations + definitions += new_definitions + + return declarations, definitions + + def _generate_msg_type_encoders(self): # pylint: disable=too-many-locals + """Generate message type encoders for records with the initial element = msg_type. An encoder is + generated for each value of the enumeration data type for msg_type. For example, if the record + has two message types, read and write, and two other fields, addr and data, then two encoders, + read(addr, data) and write(addr, data) will be generated. These are shorthands for + encode((, addr, data))""" + + declarations = '' + definitions = '' + + enumeration_types = {} + for enum in self.enumeration_types: + enumeration_types[enum.identifier] = enum.literals + + msg_type_record_types = self._get_records_with_an_initial_msg_type_element() + + for record in msg_type_record_types: + msg_type_values = enumeration_types.get(record.elements[0].subtype_indication.type_mark) + + if msg_type_values is None: + continue + + for value in msg_type_values: + parameter_list = [] + parameter_type_list = [] + encoding_list = [] + for element in record.elements: + for identifier in element.identifier_list: + if identifier != 'msg_type': + parameter_list.append(' constant %s : %s' % (identifier, + element.subtype_indication.code)) + parameter_type_list.append(element.subtype_indication.type_mark) + encoding_list.append('encode(%s)' % identifier) + else: + encoding_list.append("encode(%s'(%s))" % (element.subtype_indication.code, value)) + + if parameter_list == []: + parameter_part = '' + alias_signature = value + '[return string];' + else: + parameter_part = ' (\n' + ';\n'.join(parameter_list) + ')' + alias_signature = value + '[' + ', '.join(parameter_type_list) + ' return string];' + + encodings = ' & '.join(encoding_list) + + declarations += \ + self._template.msg_type_record_codec_declaration.substitute(name=value, + parameter_part=parameter_part, + alias_signature=alias_signature, + alias_name=value + '_msg') + definitions += \ + self._template.msg_type_record_codec_definition.substitute(name=value, + parameter_part=parameter_part, + num_of_encodings=len(encoding_list), + encodings=encodings) + + return declarations, definitions + + def _generate_get_functions(self): + """Generate a get function which will return the message type for records""" + + declarations = '' + definitions = '' + + msg_type_record_types = self._get_records_with_an_initial_msg_type_element() + msg_type_types = [] + for record in msg_type_record_types: + msg_type_type = record.elements[0].subtype_indication.code + if msg_type_type not in msg_type_types: + msg_type_types.append(msg_type_type) + declarations += self._template.get_specific_msg_type_declaration.substitute(type=msg_type_type) + definitions += self._template.get_specific_msg_type_definition.substitute(type=msg_type_type) + + return declarations, definitions + + def _get_records_with_an_initial_msg_type_element(self): + """Find all record types starting with a msg_type element""" + + msg_type_record_types = [] + for record in self.record_types: + if record.elements[0].identifier_list[0] == 'msg_type': + msg_type_record_types.append(record) + + return msg_type_record_types + + +class PackageCodecTemplate(object): + """This class contains package codec templates.""" + + msg_type_record_codec_declaration = Template("""\ + function $name$parameter_part + return string; + alias $alias_name is $alias_signature + +""") + + get_specific_msg_type_declaration = Template("""\ + function get_$type ( + constant code : string) + return $type; + +""") + + all_msg_types_enumeration_type_declaration = Template("""\ + type $identifier is ($literals); +""") + + get_msg_type_declaration = Template("""\ + function get_msg_type ( + constant code : string) + return $type; + +""") + + msg_type_record_codec_definition = Template("""\ + function $name$parameter_part + return string is + begin + return $encodings; + end function $name; + +""") + + get_specific_msg_type_definition = Template("""\ + function get_$type ( + constant code : string) + return $type is + begin + return decode(code); + end; + +""") + + get_msg_type_definition = Template("""\ + function get_msg_type ( + constant code : string) + return $type is + begin + return decode(code); + end; + +""") diff --git a/vunit/com/codec_vhdl_record_type.py b/vunit/com/codec_vhdl_record_type.py index 83d22eddc..750a60d6c 100644 --- a/vunit/com/codec_vhdl_record_type.py +++ b/vunit/com/codec_vhdl_record_type.py @@ -1,110 +1,110 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Module containing the CodecVHDLRecordType class. -""" -from string import Template -from vunit.vhdl_parser import VHDLRecordType -from vunit.com.codec_datatype_template import DatatypeCodecTemplate - - -class CodecVHDLRecordType(VHDLRecordType): - """Class derived from VHDLRecordType to provide codec generator functionality for the record type.""" - def generate_codecs_and_support_functions(self): - """Generate codecs and communication support functions for the record type.""" - - template = RecordCodecTemplate() - - declarations = '' - definitions = '' - - declarations += template.codec_declarations.substitute(type=self.identifier) - declarations += template.to_string_declarations.substitute(type=self.identifier) - element_encoding_list = [] - element_decoding_list = [] - num_of_elements = 0 - for element in self.elements: - for i in element.identifier_list: - element_encoding_list.append('encode(data.%s)' % i) - element_decoding_list.append('decode(code, index, result.%s);' % i) - - num_of_elements += 1 - element_encodings = ' & '.join(element_encoding_list) - - element_decodings = '\n '.join(element_decoding_list) - definitions += template.record_codec_definition.substitute(type=self.identifier, - element_encodings=element_encodings, - num_of_elements=str(num_of_elements), - element_decodings=element_decodings) - definitions += template.record_to_string_definition.substitute( - type=self.identifier, - element_encoding_list=', '.join(element_encoding_list), - num_of_elements=str(num_of_elements)) - - return declarations, definitions - - -class RecordCodecTemplate(DatatypeCodecTemplate): - """This class contains record templates.""" - - record_to_string_definition = Template("""\ - function to_string ( - constant data : $type) - return string is - begin - return create_group($num_of_elements, $element_encoding_list); - end function to_string; -""") - - record_codec_definition = Template("""\ - function encode ( - constant data : $type) - return string is - begin - return $element_encodings; - end function encode; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out $type) is - begin - $element_decodings - end procedure decode; - - function decode ( - constant code : string) - return $type is - variable ret_val : $type; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end function decode; - - procedure push(queue : queue_t; value : $type) is - begin - push_variable_string(queue, encode(value)); - end; - - impure function pop(queue : queue_t) return $type is - begin - return decode(pop_variable_string(queue)); - end; - - procedure push(msg : msg_t; value : $type) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return $type is - begin - return pop(msg.data); - end; - -""") +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Module containing the CodecVHDLRecordType class. +""" +from string import Template +from vunit.vhdl_parser import VHDLRecordType +from vunit.com.codec_datatype_template import DatatypeCodecTemplate + + +class CodecVHDLRecordType(VHDLRecordType): + """Class derived from VHDLRecordType to provide codec generator functionality for the record type.""" + def generate_codecs_and_support_functions(self): + """Generate codecs and communication support functions for the record type.""" + + template = RecordCodecTemplate() + + declarations = '' + definitions = '' + + declarations += template.codec_declarations.substitute(type=self.identifier) + declarations += template.to_string_declarations.substitute(type=self.identifier) + element_encoding_list = [] + element_decoding_list = [] + num_of_elements = 0 + for element in self.elements: + for i in element.identifier_list: + element_encoding_list.append('encode(data.%s)' % i) + element_decoding_list.append('decode(code, index, result.%s);' % i) + + num_of_elements += 1 + element_encodings = ' & '.join(element_encoding_list) + + element_decodings = '\n '.join(element_decoding_list) + definitions += template.record_codec_definition.substitute(type=self.identifier, + element_encodings=element_encodings, + num_of_elements=str(num_of_elements), + element_decodings=element_decodings) + definitions += template.record_to_string_definition.substitute( + type=self.identifier, + element_encoding_list=', '.join(element_encoding_list), + num_of_elements=str(num_of_elements)) + + return declarations, definitions + + +class RecordCodecTemplate(DatatypeCodecTemplate): + """This class contains record templates.""" + + record_to_string_definition = Template("""\ + function to_string ( + constant data : $type) + return string is + begin + return create_group($num_of_elements, $element_encoding_list); + end function to_string; +""") + + record_codec_definition = Template("""\ + function encode ( + constant data : $type) + return string is + begin + return $element_encodings; + end function encode; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out $type) is + begin + $element_decodings + end procedure decode; + + function decode ( + constant code : string) + return $type is + variable ret_val : $type; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end function decode; + + procedure push(queue : queue_t; value : $type) is + begin + push_variable_string(queue, encode(value)); + end; + + impure function pop(queue : queue_t) return $type is + begin + return decode(pop_variable_string(queue)); + end; + + procedure push(msg : msg_t; value : $type) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return $type is + begin + return pop(msg.data); + end; + +""") diff --git a/vunit/configuration.py b/vunit/configuration.py index 5c16665de..028a5ace7 100644 --- a/vunit/configuration.py +++ b/vunit/configuration.py @@ -1,270 +1,270 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Contains Configuration class which contains configuration of a test run -""" - -import logging -import inspect -from os.path import dirname -from copy import copy -from vunit.simulator_factory import SIMULATOR_FACTORY - - -LOGGER = logging.getLogger(__name__) - -# Name of default configuration -DEFAULT_NAME = None - - -class AttributeException(Exception): - pass - - -class Configuration(object): # pylint: disable=too-many-instance-attributes - """ - Represents a configuration of a test bench - """ - def __init__(self, # pylint: disable=too-many-arguments - name, - design_unit, - generics=None, - sim_options=None, - pre_config=None, - post_check=None, - attributes=None): - self.name = name - self._design_unit = design_unit - self.generics = {} if generics is None else generics - self.sim_options = {} if sim_options is None else sim_options - self.attributes = {} if attributes is None else attributes - - self.tb_path = dirname(design_unit.original_file_name) - - # Fill in tb_path generic with location of test bench - if "tb_path" in design_unit.generic_names: - self.generics["tb_path"] = '%s/' % self.tb_path.replace("\\", "/") - - self.pre_config = pre_config - self.post_check = post_check - - def copy(self): - return Configuration(name=self.name, - design_unit=self._design_unit, - generics=self.generics.copy(), - sim_options=self.sim_options.copy(), - pre_config=self.pre_config, - post_check=self.post_check, - attributes=self.attributes.copy()) - - @property - def is_default(self): - return self.name is DEFAULT_NAME - - @property - def generic_names(self): - return self._design_unit.generic_names - - @property - def entity_name(self): - return self._design_unit.name - - @property - def design_unit_name(self): - return self._design_unit.name - - @property - def library_name(self): - return self._design_unit.library_name - - @property - def architecture_name(self): # pylint: disable=missing-docstring - if self._design_unit.is_entity: - return next(iter(self._design_unit.architecture_names)) - - return None - - def set_attribute(self, name, value): - """ - Set attribute - """ - if name.startswith("."): - self.attributes[name] = value - else: - raise AttributeException - - def set_generic(self, name, value): - """ - Set generic - """ - if name not in self._design_unit.generic_names: - LOGGER.warning( - "Generic '%s' set to value '%s' not found in %s '%s.%s'. Possible values are [%s]", - name, value, - "entity" if self._design_unit.is_entity else "module", - self._design_unit.library_name, self._design_unit.name, - ", ".join('%s' % gname for gname in self._design_unit.generic_names)) - else: - self.generics[name] = value - - def set_sim_option(self, name, value): - """ - Set sim option - """ - SIMULATOR_FACTORY.check_sim_option(name, value) - self.sim_options[name] = copy(value) - - @property - def vhdl_assert_stop_level(self): - """ - Return the VHDL assert stop level to use with the simulator - """ - if "vhdl_assert_stop_level" in self.sim_options: - level = self.sim_options.get("vhdl_assert_stop_level") - else: - level = "error" - - return level - - def call_pre_config(self, output_path, simulator_output_path): - """ - Call pre_config if available. Setting optional output_path - """ - if self.pre_config is None: - return True - - args = inspect.getargspec(self.pre_config).args # pylint: disable=deprecated-method - - kwargs = {"output_path": output_path, - "simulator_output_path": simulator_output_path} - - for argname in list(kwargs.keys()): - if argname not in args: - del kwargs[argname] - - return self.pre_config(**kwargs) is True - - def call_post_check(self, output_path, read_output): - """ - Call post_check if available. Setting optional output_path - """ - if self.post_check is None: - return True - - args = inspect.getargspec(self.post_check).args # pylint: disable=deprecated-method - - kwargs = {"output_path": lambda: output_path, - "output": read_output} - - for argname, provider in list(kwargs.items()): - if argname not in args: - del kwargs[argname] - else: - kwargs[argname] = provider() - - return self.post_check(**kwargs) is True - - -class ConfigurationVisitor(object): - """ - An interface to visit simulation run configurations - """ - - def _check_enabled(self): - pass - - @staticmethod - def get_configuration_dicts(): - raise NotImplementedError - - def set_attribute(self, name, value): - """ - Set attribute - """ - self._check_enabled() - for configs in self.get_configuration_dicts(): - for config in configs.values(): - config.set_attribute(name, value) - - def set_generic(self, name, value): - """ - Set generic - """ - self._check_enabled() - for configs in self.get_configuration_dicts(): - for config in configs.values(): - config.set_generic(name, value) - - def set_sim_option(self, name, value, overwrite=True): - """ - Set sim option - - :param overwrite: To overwrite the option or append to the existing value - """ - self._check_enabled() - for configs in self.get_configuration_dicts(): - for config in configs.values(): - if not overwrite: - config.set_sim_option(name, config.sim_options.get(name, []) + value) - continue - config.set_sim_option(name, value) - - def set_pre_config(self, value): - """ - Set pre_config function - """ - self._check_enabled() - for configs in self.get_configuration_dicts(): - for config in configs.values(): - config.pre_config = value - - def set_post_check(self, value): - """ - Set post_check function - """ - self._check_enabled() - for configs in self.get_configuration_dicts(): - for config in configs.values(): - config.post_check = value - - def add_config(self, name, # pylint: disable=too-many-arguments - generics=None, pre_config=None, post_check=None, sim_options=None, attributes=None): - """ - Add a configuration copying unset fields from the default configuration: - """ - self._check_enabled() - - if name in (DEFAULT_NAME, '', u''): - raise ValueError("Illegal configuration name %r. Must be non-empty string" % name) - - for configs in self.get_configuration_dicts(): - if name in configs: - raise RuntimeError("Configuration name %s already defined" % name) - - # Copy default configuration - config = configs[DEFAULT_NAME].copy() - config.name = name - - if pre_config is not None: - config.pre_config = pre_config - - if post_check is not None: - config.post_check = post_check - - if generics is not None: - config.generics.update(generics) - - if sim_options is not None: - config.sim_options.update(sim_options) - - if attributes is not None: - for attribute in attributes: - if not attribute.startswith("."): - raise AttributeException - config.attributes.update(attributes) - - configs[config.name] = config +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Contains Configuration class which contains configuration of a test run +""" + +import logging +import inspect +from os.path import dirname +from copy import copy +from vunit.simulator_factory import SIMULATOR_FACTORY + + +LOGGER = logging.getLogger(__name__) + +# Name of default configuration +DEFAULT_NAME = None + + +class AttributeException(Exception): + pass + + +class Configuration(object): # pylint: disable=too-many-instance-attributes + """ + Represents a configuration of a test bench + """ + def __init__(self, # pylint: disable=too-many-arguments + name, + design_unit, + generics=None, + sim_options=None, + pre_config=None, + post_check=None, + attributes=None): + self.name = name + self._design_unit = design_unit + self.generics = {} if generics is None else generics + self.sim_options = {} if sim_options is None else sim_options + self.attributes = {} if attributes is None else attributes + + self.tb_path = dirname(design_unit.original_file_name) + + # Fill in tb_path generic with location of test bench + if "tb_path" in design_unit.generic_names: + self.generics["tb_path"] = '%s/' % self.tb_path.replace("\\", "/") + + self.pre_config = pre_config + self.post_check = post_check + + def copy(self): + return Configuration(name=self.name, + design_unit=self._design_unit, + generics=self.generics.copy(), + sim_options=self.sim_options.copy(), + pre_config=self.pre_config, + post_check=self.post_check, + attributes=self.attributes.copy()) + + @property + def is_default(self): + return self.name is DEFAULT_NAME + + @property + def generic_names(self): + return self._design_unit.generic_names + + @property + def entity_name(self): + return self._design_unit.name + + @property + def design_unit_name(self): + return self._design_unit.name + + @property + def library_name(self): + return self._design_unit.library_name + + @property + def architecture_name(self): # pylint: disable=missing-docstring + if self._design_unit.is_entity: + return next(iter(self._design_unit.architecture_names)) + + return None + + def set_attribute(self, name, value): + """ + Set attribute + """ + if name.startswith("."): + self.attributes[name] = value + else: + raise AttributeException + + def set_generic(self, name, value): + """ + Set generic + """ + if name not in self._design_unit.generic_names: + LOGGER.warning( + "Generic '%s' set to value '%s' not found in %s '%s.%s'. Possible values are [%s]", + name, value, + "entity" if self._design_unit.is_entity else "module", + self._design_unit.library_name, self._design_unit.name, + ", ".join('%s' % gname for gname in self._design_unit.generic_names)) + else: + self.generics[name] = value + + def set_sim_option(self, name, value): + """ + Set sim option + """ + SIMULATOR_FACTORY.check_sim_option(name, value) + self.sim_options[name] = copy(value) + + @property + def vhdl_assert_stop_level(self): + """ + Return the VHDL assert stop level to use with the simulator + """ + if "vhdl_assert_stop_level" in self.sim_options: + level = self.sim_options.get("vhdl_assert_stop_level") + else: + level = "error" + + return level + + def call_pre_config(self, output_path, simulator_output_path): + """ + Call pre_config if available. Setting optional output_path + """ + if self.pre_config is None: + return True + + args = inspect.getargspec(self.pre_config).args # pylint: disable=deprecated-method + + kwargs = {"output_path": output_path, + "simulator_output_path": simulator_output_path} + + for argname in list(kwargs.keys()): + if argname not in args: + del kwargs[argname] + + return self.pre_config(**kwargs) is True + + def call_post_check(self, output_path, read_output): + """ + Call post_check if available. Setting optional output_path + """ + if self.post_check is None: + return True + + args = inspect.getargspec(self.post_check).args # pylint: disable=deprecated-method + + kwargs = {"output_path": lambda: output_path, + "output": read_output} + + for argname, provider in list(kwargs.items()): + if argname not in args: + del kwargs[argname] + else: + kwargs[argname] = provider() + + return self.post_check(**kwargs) is True + + +class ConfigurationVisitor(object): + """ + An interface to visit simulation run configurations + """ + + def _check_enabled(self): + pass + + @staticmethod + def get_configuration_dicts(): + raise NotImplementedError + + def set_attribute(self, name, value): + """ + Set attribute + """ + self._check_enabled() + for configs in self.get_configuration_dicts(): + for config in configs.values(): + config.set_attribute(name, value) + + def set_generic(self, name, value): + """ + Set generic + """ + self._check_enabled() + for configs in self.get_configuration_dicts(): + for config in configs.values(): + config.set_generic(name, value) + + def set_sim_option(self, name, value, overwrite=True): + """ + Set sim option + + :param overwrite: To overwrite the option or append to the existing value + """ + self._check_enabled() + for configs in self.get_configuration_dicts(): + for config in configs.values(): + if not overwrite: + config.set_sim_option(name, config.sim_options.get(name, []) + value) + continue + config.set_sim_option(name, value) + + def set_pre_config(self, value): + """ + Set pre_config function + """ + self._check_enabled() + for configs in self.get_configuration_dicts(): + for config in configs.values(): + config.pre_config = value + + def set_post_check(self, value): + """ + Set post_check function + """ + self._check_enabled() + for configs in self.get_configuration_dicts(): + for config in configs.values(): + config.post_check = value + + def add_config(self, name, # pylint: disable=too-many-arguments + generics=None, pre_config=None, post_check=None, sim_options=None, attributes=None): + """ + Add a configuration copying unset fields from the default configuration: + """ + self._check_enabled() + + if name in (DEFAULT_NAME, '', u''): + raise ValueError("Illegal configuration name %r. Must be non-empty string" % name) + + for configs in self.get_configuration_dicts(): + if name in configs: + raise RuntimeError("Configuration name %s already defined" % name) + + # Copy default configuration + config = configs[DEFAULT_NAME].copy() + config.name = name + + if pre_config is not None: + config.pre_config = pre_config + + if post_check is not None: + config.post_check = post_check + + if generics is not None: + config.generics.update(generics) + + if sim_options is not None: + config.sim_options.update(sim_options) + + if attributes is not None: + for attribute in attributes: + if not attribute.startswith("."): + raise AttributeException + config.attributes.update(attributes) + + configs[config.name] = config diff --git a/vunit/csv_logs.py b/vunit/csv_logs.py index 2a3e50abf..64089a130 100644 --- a/vunit/csv_logs.py +++ b/vunit/csv_logs.py @@ -1,45 +1,45 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Provides csv log functionality -""" - -from csv import Sniffer, DictReader, DictWriter -from glob import glob -from os.path import abspath - - -class CsvLogs(object): - # pylint: disable=missing-docstring - - def __init__(self, pattern='', field_names=None): - default_field_names = ['#', 'Time', 'Level', 'File', 'Line', 'Source', 'Message'] - self._field_names = default_field_names if field_names is None else field_names - self._entries = [] - self.add(pattern) - - def __iter__(self): - return iter(self._entries) - - def add(self, pattern): - # pylint: disable=missing-docstring - for csv_file in [abspath(p) for p in glob(pattern)]: - with open(csv_file, "r") as fread: - sample = fread.readline() - fread.seek(0) - if sample: - dialect = Sniffer().sniff(sample) - self._entries += DictReader(fread, fieldnames=self._field_names, dialect=dialect) - - self._entries.sort(key=lambda dictionary: int(dictionary['#'])) - - def write(self, output_file): - # pylint: disable=missing-docstring - with open(output_file, "w") as fwrite: - csv_writer = DictWriter(fwrite, delimiter=',', fieldnames=self._field_names, lineterminator="\n") - csv_writer.writerow({name: name for name in self._field_names}) - csv_writer.writerows(self._entries) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Provides csv log functionality +""" + +from csv import Sniffer, DictReader, DictWriter +from glob import glob +from os.path import abspath + + +class CsvLogs(object): + # pylint: disable=missing-docstring + + def __init__(self, pattern='', field_names=None): + default_field_names = ['#', 'Time', 'Level', 'File', 'Line', 'Source', 'Message'] + self._field_names = default_field_names if field_names is None else field_names + self._entries = [] + self.add(pattern) + + def __iter__(self): + return iter(self._entries) + + def add(self, pattern): + # pylint: disable=missing-docstring + for csv_file in [abspath(p) for p in glob(pattern)]: + with open(csv_file, "r") as fread: + sample = fread.readline() + fread.seek(0) + if sample: + dialect = Sniffer().sniff(sample) + self._entries += DictReader(fread, fieldnames=self._field_names, dialect=dialect) + + self._entries.sort(key=lambda dictionary: int(dictionary['#'])) + + def write(self, output_file): + # pylint: disable=missing-docstring + with open(output_file, "w") as fwrite: + csv_writer = DictWriter(fwrite, delimiter=',', fieldnames=self._field_names, lineterminator="\n") + csv_writer.writerow({name: name for name in self._field_names}) + csv_writer.writerows(self._entries) diff --git a/vunit/database.py b/vunit/database.py index 15b550eb6..5c02aa660 100644 --- a/vunit/database.py +++ b/vunit/database.py @@ -1,143 +1,143 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -A simple file based database -""" - -from os.path import join, exists -import os -import pickle -import io -import struct -from vunit.ostools import renew_path - - -class DataBase(object): - """ - A simple file based database - both keys and values are bytes - - The database consists of a folder with files called nodes. - Each nodes contains four bytes denoting the key length as an - unsigned integer followed by the key followed by the data. - - The reason to not just have the keys as the file names is that - many operating systems does not support very long file names thus limiting the key length - """ - - def __init__(self, path, new=False): - """ - Create database in path - - path is a directory - - new create new database - """ - self._path = path - - if new: - renew_path(path) - elif not exists(path): - os.makedirs(path) - - # Map keys to nodes indexes - self._keys_to_nodes = self._discover_nodes() - if not self._keys_to_nodes: - self._next_node = 0 - else: - self._next_node = max(self._keys_to_nodes.values()) + 1 - - def _discover_nodes(self): - """ - Discover nodes already found in the database - """ - keys_to_nodes = {} - for file_base_name in os.listdir(self._path): - key = self._read_key(join(self._path, file_base_name)) - assert key not in keys_to_nodes # Two nodes contains the same key - keys_to_nodes[key] = int(file_base_name) - return keys_to_nodes - - @staticmethod - def _read_key_from_fptr(fptr): - """ - Read the key from a file pointer - first read four bytes for the key length then read the key - """ - key_size = struct.unpack("I", fptr.read(4))[0] - key = fptr.read(key_size) - return key - - def _read_key(self, file_name): - """ - Read key found in file_name - """ - with io.open(file_name, "rb") as fptr: - return self._read_key_from_fptr(fptr) - - def _read_data(self, file_name): - """ - Read key found in file_name - """ - with io.open(file_name, "rb") as fptr: - self._read_key_from_fptr(fptr) - data = fptr.read() - return data - - @staticmethod - def _write_node(file_name, key, value): - """ - Write node to file - """ - with io.open(file_name, "wb") as fptr: - fptr.write(struct.pack("I", len(key))) - fptr.write(key) - fptr.write(value) - - def _to_file_name(self, key): - """ - Convert key to file name - """ - return join(self._path, str(self._keys_to_nodes[key])) - - def _allocate_node_for_key(self, key): - """ - Allocate a node index for a new key - """ - assert key not in self._keys_to_nodes - self._keys_to_nodes[key] = self._next_node - self._next_node += 1 - - def __setitem__(self, key, value): - if key not in self._keys_to_nodes: - self._allocate_node_for_key(key) - self._write_node(self._to_file_name(key), key, value) - - def __getitem__(self, key): - if key not in self: - raise KeyError(key) - - return self._read_data(self._to_file_name(key)) - - def __contains__(self, key): - return key in self._keys_to_nodes - - -class PickledDataBase(object): - """ - Wraps a byte based database (un)pickling the values - Allowing storage of arbitrary Python objects - """ - def __init__(self, database): - self._database = database - - def __getitem__(self, key): - return pickle.loads(self._database[key]) - - def __setitem__(self, key, value): - self._database[key] = pickle.dumps(value, protocol=pickle.HIGHEST_PROTOCOL) - - def __contains__(self, key): - return key in self._database +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +A simple file based database +""" + +from os.path import join, exists +import os +import pickle +import io +import struct +from vunit.ostools import renew_path + + +class DataBase(object): + """ + A simple file based database + both keys and values are bytes + + The database consists of a folder with files called nodes. + Each nodes contains four bytes denoting the key length as an + unsigned integer followed by the key followed by the data. + + The reason to not just have the keys as the file names is that + many operating systems does not support very long file names thus limiting the key length + """ + + def __init__(self, path, new=False): + """ + Create database in path + - path is a directory + - new create new database + """ + self._path = path + + if new: + renew_path(path) + elif not exists(path): + os.makedirs(path) + + # Map keys to nodes indexes + self._keys_to_nodes = self._discover_nodes() + if not self._keys_to_nodes: + self._next_node = 0 + else: + self._next_node = max(self._keys_to_nodes.values()) + 1 + + def _discover_nodes(self): + """ + Discover nodes already found in the database + """ + keys_to_nodes = {} + for file_base_name in os.listdir(self._path): + key = self._read_key(join(self._path, file_base_name)) + assert key not in keys_to_nodes # Two nodes contains the same key + keys_to_nodes[key] = int(file_base_name) + return keys_to_nodes + + @staticmethod + def _read_key_from_fptr(fptr): + """ + Read the key from a file pointer + first read four bytes for the key length then read the key + """ + key_size = struct.unpack("I", fptr.read(4))[0] + key = fptr.read(key_size) + return key + + def _read_key(self, file_name): + """ + Read key found in file_name + """ + with io.open(file_name, "rb") as fptr: + return self._read_key_from_fptr(fptr) + + def _read_data(self, file_name): + """ + Read key found in file_name + """ + with io.open(file_name, "rb") as fptr: + self._read_key_from_fptr(fptr) + data = fptr.read() + return data + + @staticmethod + def _write_node(file_name, key, value): + """ + Write node to file + """ + with io.open(file_name, "wb") as fptr: + fptr.write(struct.pack("I", len(key))) + fptr.write(key) + fptr.write(value) + + def _to_file_name(self, key): + """ + Convert key to file name + """ + return join(self._path, str(self._keys_to_nodes[key])) + + def _allocate_node_for_key(self, key): + """ + Allocate a node index for a new key + """ + assert key not in self._keys_to_nodes + self._keys_to_nodes[key] = self._next_node + self._next_node += 1 + + def __setitem__(self, key, value): + if key not in self._keys_to_nodes: + self._allocate_node_for_key(key) + self._write_node(self._to_file_name(key), key, value) + + def __getitem__(self, key): + if key not in self: + raise KeyError(key) + + return self._read_data(self._to_file_name(key)) + + def __contains__(self, key): + return key in self._keys_to_nodes + + +class PickledDataBase(object): + """ + Wraps a byte based database (un)pickling the values + Allowing storage of arbitrary Python objects + """ + def __init__(self, database): + self._database = database + + def __getitem__(self, key): + return pickle.loads(self._database[key]) + + def __setitem__(self, key, value): + self._database[key] = pickle.dumps(value, protocol=pickle.HIGHEST_PROTOCOL) + + def __contains__(self, key): + return key in self._database diff --git a/vunit/dependency_graph.py b/vunit/dependency_graph.py index 7b7f1443a..9684e696b 100644 --- a/vunit/dependency_graph.py +++ b/vunit/dependency_graph.py @@ -1,126 +1,126 @@ -# Topological sorting of a directed ascylic graph -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Functionality to compute a dependency graph -""" - - -class DependencyGraph(object): - """ - A dependency graph - """ - def __init__(self): - self._forward = {} - self._backward = {} - self._nodes = [] - - def toposort(self): - """ - Perform a topological sort returning a list of nodes such that - every node is located after its dependency nodes - """ - sorted_nodes = [] - self._visit(sorted(self._nodes), - dict((key, sorted(values)) for key, values in self._forward.items()), - sorted_nodes.append) - sorted_nodes = list(reversed(sorted_nodes)) - return sorted_nodes - - def add_node(self, node): - self._nodes.append(node) - - def add_dependency(self, start, end): - """ - Add a dependency edge between the start and end node such that - end node depends on the start node - """ - new_dependency = (start not in self._forward - or end not in self._forward[start]) - - if start not in self._forward: - self._forward[start] = set() - - if end not in self._backward: - self._backward[end] = set() - - self._forward[start].add(end) - self._backward[end].add(start) - - return new_dependency - - @staticmethod - def _visit(nodes, graph, callback): - """ - Follow graph edges starting from the nodes iteratively - returning all the nodes visited - """ - def visit(node): - """ - Visit a single node and all following nodes in the graph - that have not already been visisted. - Detects circular dependencies - """ - if node in path: - start = path_ordered.index(node) - raise CircularDependencyException(path_ordered[start:] + [node, ]) - - path.add(node) - path_ordered.append(node) - if node in graph: - for other_node in graph[node]: - if other_node not in visited: - visit(other_node) - path.remove(node) - path_ordered.pop() - visited.add(node) - callback(node) - - visited = set() - for node in nodes: - if node not in visited: - path = set() - path_ordered = [] - visit(node) - - def get_dependent(self, nodes): - """ - Get all nodes which are directly or indirectly dependent on - the input nodes - """ - result = set() - self._visit(nodes, self._forward, result.add) - return result - - def get_dependencies(self, nodes): - """ - Get all nodes which are directly or indirectly dependencies of - the input nodes - """ - result = set() - self._visit(nodes, self._backward, result.add) - return result - - def get_direct_dependencies(self, node): - """ - Get the direct dependencies of node - """ - return self._backward.get(node, set()) - - -class CircularDependencyException(Exception): - """ - Raised when there are circular dependencies - """ - - def __init__(self, path): - Exception.__init__(self) - self.path = path - - def __repr__(self): - return "CircularDependencyException(%r)" % self.path +# Topological sorting of a directed ascylic graph +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Functionality to compute a dependency graph +""" + + +class DependencyGraph(object): + """ + A dependency graph + """ + def __init__(self): + self._forward = {} + self._backward = {} + self._nodes = [] + + def toposort(self): + """ + Perform a topological sort returning a list of nodes such that + every node is located after its dependency nodes + """ + sorted_nodes = [] + self._visit(sorted(self._nodes), + dict((key, sorted(values)) for key, values in self._forward.items()), + sorted_nodes.append) + sorted_nodes = list(reversed(sorted_nodes)) + return sorted_nodes + + def add_node(self, node): + self._nodes.append(node) + + def add_dependency(self, start, end): + """ + Add a dependency edge between the start and end node such that + end node depends on the start node + """ + new_dependency = (start not in self._forward + or end not in self._forward[start]) + + if start not in self._forward: + self._forward[start] = set() + + if end not in self._backward: + self._backward[end] = set() + + self._forward[start].add(end) + self._backward[end].add(start) + + return new_dependency + + @staticmethod + def _visit(nodes, graph, callback): + """ + Follow graph edges starting from the nodes iteratively + returning all the nodes visited + """ + def visit(node): + """ + Visit a single node and all following nodes in the graph + that have not already been visisted. + Detects circular dependencies + """ + if node in path: + start = path_ordered.index(node) + raise CircularDependencyException(path_ordered[start:] + [node, ]) + + path.add(node) + path_ordered.append(node) + if node in graph: + for other_node in graph[node]: + if other_node not in visited: + visit(other_node) + path.remove(node) + path_ordered.pop() + visited.add(node) + callback(node) + + visited = set() + for node in nodes: + if node not in visited: + path = set() + path_ordered = [] + visit(node) + + def get_dependent(self, nodes): + """ + Get all nodes which are directly or indirectly dependent on + the input nodes + """ + result = set() + self._visit(nodes, self._forward, result.add) + return result + + def get_dependencies(self, nodes): + """ + Get all nodes which are directly or indirectly dependencies of + the input nodes + """ + result = set() + self._visit(nodes, self._backward, result.add) + return result + + def get_direct_dependencies(self, node): + """ + Get the direct dependencies of node + """ + return self._backward.get(node, set()) + + +class CircularDependencyException(Exception): + """ + Raised when there are circular dependencies + """ + + def __init__(self, path): + Exception.__init__(self) + self.path = path + + def __repr__(self): + return "CircularDependencyException(%r)" % self.path diff --git a/vunit/design_unit.py b/vunit/design_unit.py index 462cf50e5..cc367919c 100644 --- a/vunit/design_unit.py +++ b/vunit/design_unit.py @@ -1,100 +1,100 @@ -# Classes to model a HDL design hierarchy -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Classes representing Entites, Architectures, Packades, Modules etc -""" - - -class DesignUnit(object): - """ - Represents a generic design unit - """ - def __init__(self, name, source_file, unit_type): - self.name = name - self.source_file = source_file - self.unit_type = unit_type - - @property - def file_name(self): - return self.source_file.name - - @property - def original_file_name(self): - return self.source_file.original_name - - @property - def library_name(self): - return self.source_file.library.name - - @property - def is_entity(self): - return False - - @property - def is_module(self): - return False - - -class VHDLDesignUnit(DesignUnit): - """ - Represents a VHDL design unit - """ - def __init__(self, # pylint: disable=too-many-arguments - name, source_file, unit_type, is_primary=True, primary_design_unit=None): - DesignUnit.__init__(self, name, source_file, unit_type) - self.is_primary = is_primary - self.primary_design_unit = primary_design_unit - - -class Entity(VHDLDesignUnit): - """ - Represents a VHDL Entity - """ - def __init__(self, name, source_file, generic_names=None): - VHDLDesignUnit.__init__(self, name, source_file, 'entity', True) - self.generic_names = [] if generic_names is None else generic_names - self._add_architecture_callback = None - self._architecture_names = {} - - def add_architecture(self, design_unit): - """ - Add architecture of this entity - """ - self._architecture_names[design_unit.name] = design_unit.source_file.name - - if self._add_architecture_callback is not None: - self._add_architecture_callback() - - def set_add_architecture_callback(self, callback): - """ - Set callback to be called when an architecture is added - """ - assert self._add_architecture_callback is None - self._add_architecture_callback = callback - - @property - def architecture_names(self): - return self._architecture_names - - @property - def is_entity(self): - return True - - -class Module(DesignUnit): - """ - Represents a Verilog Module - """ - def __init__(self, name, source_file, generic_names=None): - DesignUnit.__init__(self, name, source_file, 'module') - self.generic_names = [] if generic_names is None else generic_names - - @property - def is_module(self): - return True +# Classes to model a HDL design hierarchy +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Classes representing Entites, Architectures, Packades, Modules etc +""" + + +class DesignUnit(object): + """ + Represents a generic design unit + """ + def __init__(self, name, source_file, unit_type): + self.name = name + self.source_file = source_file + self.unit_type = unit_type + + @property + def file_name(self): + return self.source_file.name + + @property + def original_file_name(self): + return self.source_file.original_name + + @property + def library_name(self): + return self.source_file.library.name + + @property + def is_entity(self): + return False + + @property + def is_module(self): + return False + + +class VHDLDesignUnit(DesignUnit): + """ + Represents a VHDL design unit + """ + def __init__(self, # pylint: disable=too-many-arguments + name, source_file, unit_type, is_primary=True, primary_design_unit=None): + DesignUnit.__init__(self, name, source_file, unit_type) + self.is_primary = is_primary + self.primary_design_unit = primary_design_unit + + +class Entity(VHDLDesignUnit): + """ + Represents a VHDL Entity + """ + def __init__(self, name, source_file, generic_names=None): + VHDLDesignUnit.__init__(self, name, source_file, 'entity', True) + self.generic_names = [] if generic_names is None else generic_names + self._add_architecture_callback = None + self._architecture_names = {} + + def add_architecture(self, design_unit): + """ + Add architecture of this entity + """ + self._architecture_names[design_unit.name] = design_unit.source_file.name + + if self._add_architecture_callback is not None: + self._add_architecture_callback() + + def set_add_architecture_callback(self, callback): + """ + Set callback to be called when an architecture is added + """ + assert self._add_architecture_callback is None + self._add_architecture_callback = callback + + @property + def architecture_names(self): + return self._architecture_names + + @property + def is_entity(self): + return True + + +class Module(DesignUnit): + """ + Represents a Verilog Module + """ + def __init__(self, name, source_file, generic_names=None): + DesignUnit.__init__(self, name, source_file, 'module') + self.generic_names = [] if generic_names is None else generic_names + + @property + def is_module(self): + return True diff --git a/vunit/exceptions.py b/vunit/exceptions.py index d9a2df430..d07c9f3c2 100644 --- a/vunit/exceptions.py +++ b/vunit/exceptions.py @@ -1,15 +1,15 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Contains exceptions which are globally known -""" - - -class CompileError(Exception): - """ - An error occured when compiling a HDL file - """ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Contains exceptions which are globally known +""" + + +class CompileError(Exception): + """ + An error occured when compiling a HDL file + """ diff --git a/vunit/ghdl_interface.py b/vunit/ghdl_interface.py index 6e76ea9c6..2342bfed0 100644 --- a/vunit/ghdl_interface.py +++ b/vunit/ghdl_interface.py @@ -1,281 +1,281 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Interface for GHDL simulator -""" - -from __future__ import print_function -import logging -from os.path import exists, join, abspath -import os -import subprocess -import shlex -from sys import stdout # To avoid output catched in non-verbose mode -from vunit.ostools import Process -from vunit.simulator_interface import (SimulatorInterface, - ListOfStringOption, - StringOption, - BooleanOption) -from vunit.exceptions import CompileError -LOGGER = logging.getLogger(__name__) - - -class GHDLInterface(SimulatorInterface): - """ - Interface for GHDL simulator - """ - - name = "ghdl" - executable = os.environ.get("GHDL", "ghdl") - supports_gui_flag = True - supports_colors_in_gui = True - - compile_options = [ - ListOfStringOption("ghdl.flags"), - ] - - sim_options = [ - ListOfStringOption("ghdl.sim_flags"), - ListOfStringOption("ghdl.elab_flags"), - StringOption("ghdl.gtkwave_script.gui"), - BooleanOption("ghdl.elab_e") - ] - - @staticmethod - def add_arguments(parser): - """ - Add command line arguments - """ - group = parser.add_argument_group("ghdl", - description="GHDL specific flags") - group.add_argument("--gtkwave-fmt", choices=["vcd", "ghw"], - default=None, - help="Save .vcd or .ghw to open in gtkwave") - group.add_argument("--gtkwave-args", - default="", - help="Arguments to pass to gtkwave") - - @classmethod - def from_args(cls, args, output_path, **kwargs): - """ - Create instance from args namespace - """ - prefix = cls.find_prefix() - return cls(output_path=output_path, - prefix=prefix, - gui=args.gui, - gtkwave_fmt=args.gtkwave_fmt, - gtkwave_args=args.gtkwave_args, - backend=cls.determine_backend(prefix)) - - @classmethod - def find_prefix_from_path(cls): - """ - Find first valid ghdl toolchain prefix - """ - return cls.find_toolchain([cls.executable]) - - def __init__(self, # pylint: disable=too-many-arguments - output_path, prefix, gui=False, gtkwave_fmt=None, gtkwave_args="", backend="llvm"): - SimulatorInterface.__init__(self, output_path, gui) - self._prefix = prefix - self._project = None - - if gui and (not self.find_executable('gtkwave')): - raise RuntimeError( - "Cannot find the gtkwave executable in the PATH environment variable. GUI not possible") - - self._gui = gui - self._gtkwave_fmt = "ghw" if gui and gtkwave_fmt is None else gtkwave_fmt - self._gtkwave_args = gtkwave_args - self._backend = backend - self._vhdl_standard = None - - def has_valid_exit_code(self): - """ - Return if the simulation should fail with nonzero exit codes - """ - return self._vhdl_standard == "2008" - - @classmethod - def determine_backend(cls, prefix): - """ - Determine the GHDL backend - """ - mapping = { - "mcode code generator": "mcode", - "llvm code generator": "llvm", - "GCC back-end code generator": "gcc" - } - output = subprocess.check_output([join(prefix, cls.executable), "--version"]).decode() - for name, backend in mapping.items(): - if name in output: - LOGGER.debug("Detected GHDL %s", name) - return backend - - LOGGER.error("Could not detect known LLVM backend by parsing 'ghdl --version'") - print("Expected to find one of %r" % mapping.keys()) - print("== Output of 'ghdl --version'" + ("=" * 60)) - print(output) - print("=============================" + ("=" * 60)) - raise AssertionError("No known GHDL back-end could be detected from running 'ghdl --version'") - - @classmethod - def supports_vhpi(cls): - """ - Return if the simulator supports VHPI - """ - return cls.determine_backend(cls.find_prefix_from_path()) != "mcode" - - def _has_output_flag(self): - """ - Returns if backend supports output flag - """ - return self._backend in ("llvm", "gcc") - - def setup_library_mapping(self, project): - """ - Setup library mapping - """ - self._project = project - for library in project.get_libraries(): - if not exists(library.directory): - os.makedirs(library.directory) - - vhdl_standards = set(source_file.get_vhdl_standard() - for source_file in project.get_source_files_in_order() - if source_file.is_vhdl) - - if not vhdl_standards: - self._vhdl_standard = '2008' - elif len(vhdl_standards) != 1: - raise RuntimeError("GHDL cannot handle mixed VHDL standards, found %r" % list(vhdl_standards)) - else: - self._vhdl_standard = list(vhdl_standards)[0] - - def compile_source_file_command(self, source_file): - """ - Returns the command to compile a single source_file - """ - if source_file.is_vhdl: - return self.compile_vhdl_file_command(source_file) - - LOGGER.error("Unknown file type: %s", source_file.file_type) - raise CompileError - - @staticmethod - def _std_str(vhdl_standard): - """ - Convert standard to format of GHDL command line flag - """ - if vhdl_standard == "2002": - return "02" - - if vhdl_standard == "2008": - return "08" - - if vhdl_standard == "93": - return "93" - - raise ValueError("Invalid VHDL standard %s" % vhdl_standard) - - def compile_vhdl_file_command(self, source_file): - """ - Returns the command to compile a vhdl file - """ - cmd = [join(self._prefix, self.executable), '-a', '--workdir=%s' % source_file.library.directory, - '--work=%s' % source_file.library.name, - '--std=%s' % self._std_str(source_file.get_vhdl_standard())] - for library in self._project.get_libraries(): - cmd += ["-P%s" % library.directory] - cmd += source_file.compile_options.get("ghdl.flags", []) - cmd += [source_file.name] - return cmd - - def _get_command(self, config, output_path, ghdl_e): - """ - Return GHDL simulation command - """ - cmd = [join(self._prefix, self.executable)] - - if ghdl_e: - cmd += ['-e'] - else: - cmd += ['--elab-run'] - - cmd += ['--std=%s' % self._std_str(self._vhdl_standard)] - cmd += ['--work=%s' % config.library_name] - cmd += ['--workdir=%s' % self._project.get_library(config.library_name).directory] - cmd += ['-P%s' % lib.directory for lib in self._project.get_libraries()] - if self._has_output_flag(): - cmd += ['-o', join(output_path, "%s-%s" % (config.entity_name, - config.architecture_name))] - cmd += config.sim_options.get("ghdl.elab_flags", []) - cmd += [config.entity_name, config.architecture_name] - - if not ghdl_e: - cmd += config.sim_options.get("ghdl.sim_flags", []) - for name, value in config.generics.items(): - cmd += ['-g%s=%s' % (name, value)] - cmd += ['--assert-level=%s' % config.vhdl_assert_stop_level] - if config.sim_options.get("disable_ieee_warnings", False): - cmd += ["--ieee-asserts=disable"] - - return cmd - - def simulate(self, # pylint: disable=too-many-locals - output_path, - test_suite_name, - config, elaborate_only): - """ - Simulate with entity as top level using generics - """ - - script_path = join(output_path, self.name) - - if not exists(script_path): - os.makedirs(script_path) - - ghdl_e = elaborate_only and config.sim_options.get("ghdl.elab_e", False) - - cmd = self._get_command(config, script_path, ghdl_e) - - if elaborate_only and not ghdl_e: - cmd += ["--no-run"] - - if self._gtkwave_fmt is not None and not ghdl_e: - data_file_name = join(script_path, "wave.%s" % self._gtkwave_fmt) - - if exists(data_file_name): - os.remove(data_file_name) - - if self._gtkwave_fmt == "ghw": - cmd += ['--wave=%s' % data_file_name] - elif self._gtkwave_fmt == "vcd": - cmd += ['--vcd=%s' % data_file_name] - - else: - data_file_name = None - - status = True - try: - proc = Process(cmd) - proc.consume_output() - except Process.NonZeroExitCode: - status = False - - if self._gui and not elaborate_only: - cmd = ["gtkwave"] + shlex.split(self._gtkwave_args) + [data_file_name] - - init_file = config.sim_options.get(self.name + ".gtkwave_script.gui", None) - if init_file is not None: - cmd += ["--script", "\"{}\"".format(abspath(init_file))] - - stdout.write("%s\n" % " ".join(cmd)) - subprocess.call(cmd) - - return status +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Interface for GHDL simulator +""" + +from __future__ import print_function +import logging +from os.path import exists, join, abspath +import os +import subprocess +import shlex +from sys import stdout # To avoid output catched in non-verbose mode +from vunit.ostools import Process +from vunit.simulator_interface import (SimulatorInterface, + ListOfStringOption, + StringOption, + BooleanOption) +from vunit.exceptions import CompileError +LOGGER = logging.getLogger(__name__) + + +class GHDLInterface(SimulatorInterface): + """ + Interface for GHDL simulator + """ + + name = "ghdl" + executable = os.environ.get("GHDL", "ghdl") + supports_gui_flag = True + supports_colors_in_gui = True + + compile_options = [ + ListOfStringOption("ghdl.flags"), + ] + + sim_options = [ + ListOfStringOption("ghdl.sim_flags"), + ListOfStringOption("ghdl.elab_flags"), + StringOption("ghdl.gtkwave_script.gui"), + BooleanOption("ghdl.elab_e") + ] + + @staticmethod + def add_arguments(parser): + """ + Add command line arguments + """ + group = parser.add_argument_group("ghdl", + description="GHDL specific flags") + group.add_argument("--gtkwave-fmt", choices=["vcd", "ghw"], + default=None, + help="Save .vcd or .ghw to open in gtkwave") + group.add_argument("--gtkwave-args", + default="", + help="Arguments to pass to gtkwave") + + @classmethod + def from_args(cls, args, output_path, **kwargs): + """ + Create instance from args namespace + """ + prefix = cls.find_prefix() + return cls(output_path=output_path, + prefix=prefix, + gui=args.gui, + gtkwave_fmt=args.gtkwave_fmt, + gtkwave_args=args.gtkwave_args, + backend=cls.determine_backend(prefix)) + + @classmethod + def find_prefix_from_path(cls): + """ + Find first valid ghdl toolchain prefix + """ + return cls.find_toolchain([cls.executable]) + + def __init__(self, # pylint: disable=too-many-arguments + output_path, prefix, gui=False, gtkwave_fmt=None, gtkwave_args="", backend="llvm"): + SimulatorInterface.__init__(self, output_path, gui) + self._prefix = prefix + self._project = None + + if gui and (not self.find_executable('gtkwave')): + raise RuntimeError( + "Cannot find the gtkwave executable in the PATH environment variable. GUI not possible") + + self._gui = gui + self._gtkwave_fmt = "ghw" if gui and gtkwave_fmt is None else gtkwave_fmt + self._gtkwave_args = gtkwave_args + self._backend = backend + self._vhdl_standard = None + + def has_valid_exit_code(self): + """ + Return if the simulation should fail with nonzero exit codes + """ + return self._vhdl_standard == "2008" + + @classmethod + def determine_backend(cls, prefix): + """ + Determine the GHDL backend + """ + mapping = { + "mcode code generator": "mcode", + "llvm code generator": "llvm", + "GCC back-end code generator": "gcc" + } + output = subprocess.check_output([join(prefix, cls.executable), "--version"]).decode() + for name, backend in mapping.items(): + if name in output: + LOGGER.debug("Detected GHDL %s", name) + return backend + + LOGGER.error("Could not detect known LLVM backend by parsing 'ghdl --version'") + print("Expected to find one of %r" % mapping.keys()) + print("== Output of 'ghdl --version'" + ("=" * 60)) + print(output) + print("=============================" + ("=" * 60)) + raise AssertionError("No known GHDL back-end could be detected from running 'ghdl --version'") + + @classmethod + def supports_vhpi(cls): + """ + Return if the simulator supports VHPI + """ + return cls.determine_backend(cls.find_prefix_from_path()) != "mcode" + + def _has_output_flag(self): + """ + Returns if backend supports output flag + """ + return self._backend in ("llvm", "gcc") + + def setup_library_mapping(self, project): + """ + Setup library mapping + """ + self._project = project + for library in project.get_libraries(): + if not exists(library.directory): + os.makedirs(library.directory) + + vhdl_standards = set(source_file.get_vhdl_standard() + for source_file in project.get_source_files_in_order() + if source_file.is_vhdl) + + if not vhdl_standards: + self._vhdl_standard = '2008' + elif len(vhdl_standards) != 1: + raise RuntimeError("GHDL cannot handle mixed VHDL standards, found %r" % list(vhdl_standards)) + else: + self._vhdl_standard = list(vhdl_standards)[0] + + def compile_source_file_command(self, source_file): + """ + Returns the command to compile a single source_file + """ + if source_file.is_vhdl: + return self.compile_vhdl_file_command(source_file) + + LOGGER.error("Unknown file type: %s", source_file.file_type) + raise CompileError + + @staticmethod + def _std_str(vhdl_standard): + """ + Convert standard to format of GHDL command line flag + """ + if vhdl_standard == "2002": + return "02" + + if vhdl_standard == "2008": + return "08" + + if vhdl_standard == "93": + return "93" + + raise ValueError("Invalid VHDL standard %s" % vhdl_standard) + + def compile_vhdl_file_command(self, source_file): + """ + Returns the command to compile a vhdl file + """ + cmd = [join(self._prefix, self.executable), '-a', '--workdir=%s' % source_file.library.directory, + '--work=%s' % source_file.library.name, + '--std=%s' % self._std_str(source_file.get_vhdl_standard())] + for library in self._project.get_libraries(): + cmd += ["-P%s" % library.directory] + cmd += source_file.compile_options.get("ghdl.flags", []) + cmd += [source_file.name] + return cmd + + def _get_command(self, config, output_path, ghdl_e): + """ + Return GHDL simulation command + """ + cmd = [join(self._prefix, self.executable)] + + if ghdl_e: + cmd += ['-e'] + else: + cmd += ['--elab-run'] + + cmd += ['--std=%s' % self._std_str(self._vhdl_standard)] + cmd += ['--work=%s' % config.library_name] + cmd += ['--workdir=%s' % self._project.get_library(config.library_name).directory] + cmd += ['-P%s' % lib.directory for lib in self._project.get_libraries()] + if self._has_output_flag(): + cmd += ['-o', join(output_path, "%s-%s" % (config.entity_name, + config.architecture_name))] + cmd += config.sim_options.get("ghdl.elab_flags", []) + cmd += [config.entity_name, config.architecture_name] + + if not ghdl_e: + cmd += config.sim_options.get("ghdl.sim_flags", []) + for name, value in config.generics.items(): + cmd += ['-g%s=%s' % (name, value)] + cmd += ['--assert-level=%s' % config.vhdl_assert_stop_level] + if config.sim_options.get("disable_ieee_warnings", False): + cmd += ["--ieee-asserts=disable"] + + return cmd + + def simulate(self, # pylint: disable=too-many-locals + output_path, + test_suite_name, + config, elaborate_only): + """ + Simulate with entity as top level using generics + """ + + script_path = join(output_path, self.name) + + if not exists(script_path): + os.makedirs(script_path) + + ghdl_e = elaborate_only and config.sim_options.get("ghdl.elab_e", False) + + cmd = self._get_command(config, script_path, ghdl_e) + + if elaborate_only and not ghdl_e: + cmd += ["--no-run"] + + if self._gtkwave_fmt is not None and not ghdl_e: + data_file_name = join(script_path, "wave.%s" % self._gtkwave_fmt) + + if exists(data_file_name): + os.remove(data_file_name) + + if self._gtkwave_fmt == "ghw": + cmd += ['--wave=%s' % data_file_name] + elif self._gtkwave_fmt == "vcd": + cmd += ['--vcd=%s' % data_file_name] + + else: + data_file_name = None + + status = True + try: + proc = Process(cmd) + proc.consume_output() + except Process.NonZeroExitCode: + status = False + + if self._gui and not elaborate_only: + cmd = ["gtkwave"] + shlex.split(self._gtkwave_args) + [data_file_name] + + init_file = config.sim_options.get(self.name + ".gtkwave_script.gui", None) + if init_file is not None: + cmd += ["--script", "\"{}\"".format(abspath(init_file))] + + stdout.write("%s\n" % " ".join(cmd)) + subprocess.call(cmd) + + return status diff --git a/vunit/hashing.py b/vunit/hashing.py index a0404a7cd..76f99fbf2 100644 --- a/vunit/hashing.py +++ b/vunit/hashing.py @@ -1,18 +1,18 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Wrapper arround selected hash method -""" - -import hashlib - - -def hash_string(string): - """ - returns hash of bytes - """ - return hashlib.sha1(string.encode(encoding="utf-8")).hexdigest() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Wrapper arround selected hash method +""" + +import hashlib + + +def hash_string(string): + """ + returns hash of bytes + """ + return hashlib.sha1(string.encode(encoding="utf-8")).hexdigest() diff --git a/vunit/incisive_interface.py b/vunit/incisive_interface.py index e8ee76ca8..4861fa5bf 100644 --- a/vunit/incisive_interface.py +++ b/vunit/incisive_interface.py @@ -1,365 +1,365 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Interface for the Cadence Incisive simulator -""" - -from __future__ import print_function -import os -from os.path import join, dirname, abspath, relpath -import subprocess -import sys -import logging -from vunit.ostools import write_file, file_exists -from vunit.simulator_interface import (SimulatorInterface, - run_command, - ListOfStringOption) -from vunit.exceptions import CompileError -from vunit.cds_file import CDSFile -LOGGER = logging.getLogger(__name__) - - -class IncisiveInterface(SimulatorInterface): # pylint: disable=too-many-instance-attributes - """ - Interface for the Cadence Incisive simulator - """ - - name = "incisive" - supports_gui_flag = True - package_users_depend_on_bodies = False - - compile_options = [ - ListOfStringOption("incisive.irun_vhdl_flags"), - ListOfStringOption("incisive.irun_verilog_flags"), - ] - - sim_options = [ - ListOfStringOption("incisive.irun_sim_flags") - ] - - @staticmethod - def add_arguments(parser): - """ - Add command line arguments - """ - group = parser.add_argument_group("Incisive irun", - description="Incisive irun-specific flags") - group.add_argument("--cdslib", - default=None, - help="The cds.lib file to use. If not given, VUnit maintains its own cds.lib file.") - group.add_argument("--hdlvar", - default=None, - help="The hdl.var file to use. If not given, VUnit does not use a hdl.var file.") - - @classmethod - def from_args(cls, args, output_path, **kwargs): - """ - Create new instance from command line arguments object - """ - return cls(prefix=cls.find_prefix(), - output_path=output_path, - log_level=args.log_level, - gui=args.gui, - cdslib=args.cdslib, - hdlvar=args.hdlvar) - - @classmethod - def find_prefix_from_path(cls): - """ - Find incisive simulator from PATH environment variable - """ - return cls.find_toolchain(['irun']) - - @staticmethod - def supports_vhdl_2008_contexts(): - """ - Returns True when this simulator supports VHDL 2008 contexts - """ - return False - - def __init__(self, # pylint: disable=too-many-arguments - prefix, output_path, gui=False, log_level=None, cdslib=None, hdlvar=None): - SimulatorInterface.__init__(self, output_path, gui) - self._prefix = prefix - self._libraries = [] - self._log_level = log_level - if cdslib is None: - self._cdslib = abspath(join(output_path, 'cds.lib')) - else: - self._cdslib = abspath(cdslib) - self._hdlvar = hdlvar - self._cds_root_irun = self.find_cds_root_irun() - self._create_cdslib() - - def find_cds_root_irun(self): - """ - Finds irun cds root - """ - return subprocess.check_output([join(self._prefix, 'cds_root'), 'irun']).splitlines()[0] - - def find_cds_root_virtuoso(self): - """ - Finds virtuoso cds root - """ - try: - return subprocess.check_output([join(self._prefix, 'cds_root'), 'virtuoso']).splitlines()[0] - except subprocess.CalledProcessError: - return None - - def _create_cdslib(self): - """ - Create the cds.lib file in the output directory if it does not exist - """ - cds_root_virtuoso = self.find_cds_root_virtuoso() - - if cds_root_virtuoso is None: - contents = """\ -## cds.lib: Defines the locations of compiled libraries. -softinclude {0}/tools/inca/files/cds.lib -# needed for referencing the library 'basic' for cells 'cds_alias', 'cds_thru' etc. in analog models: -# NOTE: 'virtuoso' executable not found! -# define basic ".../tools/dfII/etc/cdslib/basic" -define work "{1}/libraries/work" -""".format(self._cds_root_irun, self._output_path) - else: - contents = """\ -## cds.lib: Defines the locations of compiled libraries. -softinclude {0}/tools/inca/files/cds.lib -# needed for referencing the library 'basic' for cells 'cds_alias', 'cds_thru' etc. in analog models: -define basic "{1}/tools/dfII/etc/cdslib/basic" -define work "{2}/libraries/work" -""".format(self._cds_root_irun, cds_root_virtuoso, self._output_path) - - write_file(self._cdslib, contents) - - def setup_library_mapping(self, project): - """ - Compile project using vhdl_standard - """ - mapped_libraries = self._get_mapped_libraries() - - for library in project.get_libraries(): - self._libraries.append(library) - self.create_library(library.name, library.directory, mapped_libraries) - - def compile_source_file_command(self, source_file): - """ - Returns the command to compile a single source file - """ - if source_file.is_vhdl: - return self.compile_vhdl_file_command(source_file) - - if source_file.is_any_verilog: - return self.compile_verilog_file_command(source_file) - - raise CompileError - - @staticmethod - def _vhdl_std_opt(vhdl_standard): - """ - Convert standard to format of irun command line flag - """ - if vhdl_standard == "2002": - return "-v200x -extv200x" - - if vhdl_standard == "2008": - return "-v200x -extv200x" - - if vhdl_standard == "93": - return "-v93" - - raise ValueError("Invalid VHDL standard %s" % vhdl_standard) - - def compile_vhdl_file_command(self, source_file): - """ - Returns command to compile a VHDL file - """ - cmd = join(self._prefix, 'irun') - args = [] - args += ['-compile'] - args += ['-nocopyright'] - args += ['-licqueue'] - args += ['-nowarn DLCPTH'] # "cds.lib Invalid path" - args += ['-nowarn DLCVAR'] # "cds.lib Invalid environment variable ''." - args += ['%s' % self._vhdl_std_opt(source_file.get_vhdl_standard())] - args += ['-work work'] - args += ['-cdslib "%s"' % self._cdslib] - args += self._hdlvar_args() - args += ['-log "%s"' % join(self._output_path, "irun_compile_vhdl_file_%s.log" % source_file.library.name)] - if not self._log_level == "debug": - args += ['-quiet'] - else: - args += ['-messages'] - args += ['-libverbose'] - args += source_file.compile_options.get('incisive.irun_vhdl_flags', []) - args += ['-nclibdirname "%s"' % dirname(source_file.library.directory)] - args += ['-makelib %s' % source_file.library.directory] - args += ['"%s"' % source_file.name] - args += ['-endlib'] - argsfile = join(self._output_path, "irun_compile_vhdl_file_%s.args" % source_file.library.name) - write_file(argsfile, "\n".join(args)) - return [cmd, '-f', argsfile] - - def compile_verilog_file_command(self, source_file): - """ - Returns commands to compile a Verilog file - """ - cmd = join(self._prefix, 'irun') - args = [] - args += ['-compile'] - args += ['-nocopyright'] - args += ['-licqueue'] - # "Ignored unexpected semicolon following SystemVerilog description keyword (endfunction)." - args += ['-nowarn UEXPSC'] - # "cds.lib Invalid path" - args += ['-nowarn DLCPTH'] - # "cds.lib Invalid environment variable ''." - args += ['-nowarn DLCVAR'] - args += ['-work work'] - args += source_file.compile_options.get('incisive.irun_verilog_flags', []) - args += ['-cdslib "%s"' % self._cdslib] - args += self._hdlvar_args() - args += ['-log "%s"' % join(self._output_path, "irun_compile_verilog_file_%s.log" % source_file.library.name)] - if not self._log_level == "debug": - args += ['-quiet'] - else: - args += ['-messages'] - args += ['-libverbose'] - for include_dir in source_file.include_dirs: - args += ['-incdir "%s"' % include_dir] - - # for "disciplines.vams" etc. - args += ['-incdir "%s/tools/spectre/etc/ahdl/"' % self._cds_root_irun] - - for key, value in source_file.defines.items(): - args += ['-define %s=%s' % (key, value.replace('"', '\\"'))] - args += ['-nclibdirname "%s"' % dirname(source_file.library.directory)] - args += ['-makelib %s' % source_file.library.name] - args += ['"%s"' % source_file.name] - args += ['-endlib'] - argsfile = join(self._output_path, "irun_compile_verilog_file_%s.args" % source_file.library.name) - write_file(argsfile, "\n".join(args)) - return [cmd, '-f', argsfile] - - def create_library(self, library_name, library_path, mapped_libraries=None): - """ - Create and map a library_name to library_path - """ - mapped_libraries = mapped_libraries if mapped_libraries is not None else {} - - if not file_exists(dirname(abspath(library_path))): - os.makedirs(dirname(abspath(library_path))) - - if library_name in mapped_libraries and mapped_libraries[library_name] == library_path: - return - - cds = CDSFile.parse(self._cdslib) - cds[library_name] = library_path - cds.write(self._cdslib) - - def _get_mapped_libraries(self): - """ - Get mapped libraries from cds.lib file - """ - cds = CDSFile.parse(self._cdslib) - return cds - - def simulate(self, # pylint: disable=too-many-locals - output_path, test_suite_name, config, elaborate_only=False): - """ - Elaborates and Simulates with entity as top level using generics - """ - - script_path = join(output_path, self.name) - launch_gui = self._gui is not False and not elaborate_only - - if elaborate_only: - steps = ['elaborate'] - else: - steps = ['elaborate', 'simulate'] - - for step in steps: - cmd = join(self._prefix, 'irun') - args = [] - if step == 'elaborate': - args += ['-elaborate'] - args += ['-nocopyright'] - args += ['-licqueue'] - # args += ['-dumpstack'] - # args += ['-gdbsh'] - # args += ['-rebuild'] - # args += ['-gdb'] - # args += ['-gdbelab'] - args += ['-errormax 10'] - args += ['-nowarn WRMNZD'] - args += ['-nowarn DLCPTH'] # "cds.lib Invalid path" - args += ['-nowarn DLCVAR'] # "cds.lib Invalid environment variable ''." - args += ['-ncerror EVBBOL'] # promote to error: "bad boolean literal in generic association" - args += ['-ncerror EVBSTR'] # promote to error: "bad string literal in generic association" - args += ['-ncerror EVBNAT'] # promote to error: "bad natural literal in generic association" - args += ['-work work'] - args += ['-nclibdirname "%s"' % (join(self._output_path, "libraries"))] # @TODO: ugly - args += config.sim_options.get('incisive.irun_sim_flags', []) - args += ['-cdslib "%s"' % self._cdslib] - args += self._hdlvar_args() - args += ['-log "%s"' % join(script_path, "irun_%s.log" % step)] - if not self._log_level == "debug": - args += ['-quiet'] - else: - args += ['-messages'] - # args += ['-libverbose'] - args += self._generic_args(config.entity_name, config.generics) - for library in self._libraries: - args += ['-reflib "%s"' % library.directory] - if launch_gui: - args += ['-access +rwc'] - # args += ['-linedebug'] - args += ['-gui'] - else: - args += ['-access +r'] - args += ['-input "@run"'] - - if config.architecture_name is None: - # we have a SystemVerilog toplevel: - args += ['-top %s' % join('%s.%s:sv' % (config.library_name, config.entity_name))] - else: - # we have a VHDL toplevel: - args += ['-top %s' % join('%s.%s:%s' % (config.library_name, config.entity_name, - config.architecture_name))] - argsfile = "%s/irun_%s.args" % (script_path, step) - write_file(argsfile, "\n".join(args)) - if not run_command([cmd, '-f', relpath(argsfile, script_path)], cwd=script_path, env=self.get_env()): - return False - return True - - def _hdlvar_args(self): - """ - Return hdlvar argument if available - """ - if self._hdlvar is None: - return [] - return ['-hdlvar "%s"' % self._hdlvar] - - @staticmethod - def _generic_args(entity_name, generics): - """ - Create irun arguments for generics/parameters - """ - args = [] - for name, value in generics.items(): - if _generic_needs_quoting(value): - args += ['''-gpg "%s.%s => \\"%s\\""''' % (entity_name, name, value)] - else: - args += ['''-gpg "%s.%s => %s"''' % (entity_name, name, value)] - return args - - -def _generic_needs_quoting(value): # pylint: disable=missing-docstring - if sys.version_info.major == 2: - return isinstance(value, (str, bool, unicode)) # pylint: disable=undefined-variable - - return isinstance(value, (str, bool)) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Interface for the Cadence Incisive simulator +""" + +from __future__ import print_function +import os +from os.path import join, dirname, abspath, relpath +import subprocess +import sys +import logging +from vunit.ostools import write_file, file_exists +from vunit.simulator_interface import (SimulatorInterface, + run_command, + ListOfStringOption) +from vunit.exceptions import CompileError +from vunit.cds_file import CDSFile +LOGGER = logging.getLogger(__name__) + + +class IncisiveInterface(SimulatorInterface): # pylint: disable=too-many-instance-attributes + """ + Interface for the Cadence Incisive simulator + """ + + name = "incisive" + supports_gui_flag = True + package_users_depend_on_bodies = False + + compile_options = [ + ListOfStringOption("incisive.irun_vhdl_flags"), + ListOfStringOption("incisive.irun_verilog_flags"), + ] + + sim_options = [ + ListOfStringOption("incisive.irun_sim_flags") + ] + + @staticmethod + def add_arguments(parser): + """ + Add command line arguments + """ + group = parser.add_argument_group("Incisive irun", + description="Incisive irun-specific flags") + group.add_argument("--cdslib", + default=None, + help="The cds.lib file to use. If not given, VUnit maintains its own cds.lib file.") + group.add_argument("--hdlvar", + default=None, + help="The hdl.var file to use. If not given, VUnit does not use a hdl.var file.") + + @classmethod + def from_args(cls, args, output_path, **kwargs): + """ + Create new instance from command line arguments object + """ + return cls(prefix=cls.find_prefix(), + output_path=output_path, + log_level=args.log_level, + gui=args.gui, + cdslib=args.cdslib, + hdlvar=args.hdlvar) + + @classmethod + def find_prefix_from_path(cls): + """ + Find incisive simulator from PATH environment variable + """ + return cls.find_toolchain(['irun']) + + @staticmethod + def supports_vhdl_2008_contexts(): + """ + Returns True when this simulator supports VHDL 2008 contexts + """ + return False + + def __init__(self, # pylint: disable=too-many-arguments + prefix, output_path, gui=False, log_level=None, cdslib=None, hdlvar=None): + SimulatorInterface.__init__(self, output_path, gui) + self._prefix = prefix + self._libraries = [] + self._log_level = log_level + if cdslib is None: + self._cdslib = abspath(join(output_path, 'cds.lib')) + else: + self._cdslib = abspath(cdslib) + self._hdlvar = hdlvar + self._cds_root_irun = self.find_cds_root_irun() + self._create_cdslib() + + def find_cds_root_irun(self): + """ + Finds irun cds root + """ + return subprocess.check_output([join(self._prefix, 'cds_root'), 'irun']).splitlines()[0] + + def find_cds_root_virtuoso(self): + """ + Finds virtuoso cds root + """ + try: + return subprocess.check_output([join(self._prefix, 'cds_root'), 'virtuoso']).splitlines()[0] + except subprocess.CalledProcessError: + return None + + def _create_cdslib(self): + """ + Create the cds.lib file in the output directory if it does not exist + """ + cds_root_virtuoso = self.find_cds_root_virtuoso() + + if cds_root_virtuoso is None: + contents = """\ +## cds.lib: Defines the locations of compiled libraries. +softinclude {0}/tools/inca/files/cds.lib +# needed for referencing the library 'basic' for cells 'cds_alias', 'cds_thru' etc. in analog models: +# NOTE: 'virtuoso' executable not found! +# define basic ".../tools/dfII/etc/cdslib/basic" +define work "{1}/libraries/work" +""".format(self._cds_root_irun, self._output_path) + else: + contents = """\ +## cds.lib: Defines the locations of compiled libraries. +softinclude {0}/tools/inca/files/cds.lib +# needed for referencing the library 'basic' for cells 'cds_alias', 'cds_thru' etc. in analog models: +define basic "{1}/tools/dfII/etc/cdslib/basic" +define work "{2}/libraries/work" +""".format(self._cds_root_irun, cds_root_virtuoso, self._output_path) + + write_file(self._cdslib, contents) + + def setup_library_mapping(self, project): + """ + Compile project using vhdl_standard + """ + mapped_libraries = self._get_mapped_libraries() + + for library in project.get_libraries(): + self._libraries.append(library) + self.create_library(library.name, library.directory, mapped_libraries) + + def compile_source_file_command(self, source_file): + """ + Returns the command to compile a single source file + """ + if source_file.is_vhdl: + return self.compile_vhdl_file_command(source_file) + + if source_file.is_any_verilog: + return self.compile_verilog_file_command(source_file) + + raise CompileError + + @staticmethod + def _vhdl_std_opt(vhdl_standard): + """ + Convert standard to format of irun command line flag + """ + if vhdl_standard == "2002": + return "-v200x -extv200x" + + if vhdl_standard == "2008": + return "-v200x -extv200x" + + if vhdl_standard == "93": + return "-v93" + + raise ValueError("Invalid VHDL standard %s" % vhdl_standard) + + def compile_vhdl_file_command(self, source_file): + """ + Returns command to compile a VHDL file + """ + cmd = join(self._prefix, 'irun') + args = [] + args += ['-compile'] + args += ['-nocopyright'] + args += ['-licqueue'] + args += ['-nowarn DLCPTH'] # "cds.lib Invalid path" + args += ['-nowarn DLCVAR'] # "cds.lib Invalid environment variable ''." + args += ['%s' % self._vhdl_std_opt(source_file.get_vhdl_standard())] + args += ['-work work'] + args += ['-cdslib "%s"' % self._cdslib] + args += self._hdlvar_args() + args += ['-log "%s"' % join(self._output_path, "irun_compile_vhdl_file_%s.log" % source_file.library.name)] + if not self._log_level == "debug": + args += ['-quiet'] + else: + args += ['-messages'] + args += ['-libverbose'] + args += source_file.compile_options.get('incisive.irun_vhdl_flags', []) + args += ['-nclibdirname "%s"' % dirname(source_file.library.directory)] + args += ['-makelib %s' % source_file.library.directory] + args += ['"%s"' % source_file.name] + args += ['-endlib'] + argsfile = join(self._output_path, "irun_compile_vhdl_file_%s.args" % source_file.library.name) + write_file(argsfile, "\n".join(args)) + return [cmd, '-f', argsfile] + + def compile_verilog_file_command(self, source_file): + """ + Returns commands to compile a Verilog file + """ + cmd = join(self._prefix, 'irun') + args = [] + args += ['-compile'] + args += ['-nocopyright'] + args += ['-licqueue'] + # "Ignored unexpected semicolon following SystemVerilog description keyword (endfunction)." + args += ['-nowarn UEXPSC'] + # "cds.lib Invalid path" + args += ['-nowarn DLCPTH'] + # "cds.lib Invalid environment variable ''." + args += ['-nowarn DLCVAR'] + args += ['-work work'] + args += source_file.compile_options.get('incisive.irun_verilog_flags', []) + args += ['-cdslib "%s"' % self._cdslib] + args += self._hdlvar_args() + args += ['-log "%s"' % join(self._output_path, "irun_compile_verilog_file_%s.log" % source_file.library.name)] + if not self._log_level == "debug": + args += ['-quiet'] + else: + args += ['-messages'] + args += ['-libverbose'] + for include_dir in source_file.include_dirs: + args += ['-incdir "%s"' % include_dir] + + # for "disciplines.vams" etc. + args += ['-incdir "%s/tools/spectre/etc/ahdl/"' % self._cds_root_irun] + + for key, value in source_file.defines.items(): + args += ['-define %s=%s' % (key, value.replace('"', '\\"'))] + args += ['-nclibdirname "%s"' % dirname(source_file.library.directory)] + args += ['-makelib %s' % source_file.library.name] + args += ['"%s"' % source_file.name] + args += ['-endlib'] + argsfile = join(self._output_path, "irun_compile_verilog_file_%s.args" % source_file.library.name) + write_file(argsfile, "\n".join(args)) + return [cmd, '-f', argsfile] + + def create_library(self, library_name, library_path, mapped_libraries=None): + """ + Create and map a library_name to library_path + """ + mapped_libraries = mapped_libraries if mapped_libraries is not None else {} + + if not file_exists(dirname(abspath(library_path))): + os.makedirs(dirname(abspath(library_path))) + + if library_name in mapped_libraries and mapped_libraries[library_name] == library_path: + return + + cds = CDSFile.parse(self._cdslib) + cds[library_name] = library_path + cds.write(self._cdslib) + + def _get_mapped_libraries(self): + """ + Get mapped libraries from cds.lib file + """ + cds = CDSFile.parse(self._cdslib) + return cds + + def simulate(self, # pylint: disable=too-many-locals + output_path, test_suite_name, config, elaborate_only=False): + """ + Elaborates and Simulates with entity as top level using generics + """ + + script_path = join(output_path, self.name) + launch_gui = self._gui is not False and not elaborate_only + + if elaborate_only: + steps = ['elaborate'] + else: + steps = ['elaborate', 'simulate'] + + for step in steps: + cmd = join(self._prefix, 'irun') + args = [] + if step == 'elaborate': + args += ['-elaborate'] + args += ['-nocopyright'] + args += ['-licqueue'] + # args += ['-dumpstack'] + # args += ['-gdbsh'] + # args += ['-rebuild'] + # args += ['-gdb'] + # args += ['-gdbelab'] + args += ['-errormax 10'] + args += ['-nowarn WRMNZD'] + args += ['-nowarn DLCPTH'] # "cds.lib Invalid path" + args += ['-nowarn DLCVAR'] # "cds.lib Invalid environment variable ''." + args += ['-ncerror EVBBOL'] # promote to error: "bad boolean literal in generic association" + args += ['-ncerror EVBSTR'] # promote to error: "bad string literal in generic association" + args += ['-ncerror EVBNAT'] # promote to error: "bad natural literal in generic association" + args += ['-work work'] + args += ['-nclibdirname "%s"' % (join(self._output_path, "libraries"))] # @TODO: ugly + args += config.sim_options.get('incisive.irun_sim_flags', []) + args += ['-cdslib "%s"' % self._cdslib] + args += self._hdlvar_args() + args += ['-log "%s"' % join(script_path, "irun_%s.log" % step)] + if not self._log_level == "debug": + args += ['-quiet'] + else: + args += ['-messages'] + # args += ['-libverbose'] + args += self._generic_args(config.entity_name, config.generics) + for library in self._libraries: + args += ['-reflib "%s"' % library.directory] + if launch_gui: + args += ['-access +rwc'] + # args += ['-linedebug'] + args += ['-gui'] + else: + args += ['-access +r'] + args += ['-input "@run"'] + + if config.architecture_name is None: + # we have a SystemVerilog toplevel: + args += ['-top %s' % join('%s.%s:sv' % (config.library_name, config.entity_name))] + else: + # we have a VHDL toplevel: + args += ['-top %s' % join('%s.%s:%s' % (config.library_name, config.entity_name, + config.architecture_name))] + argsfile = "%s/irun_%s.args" % (script_path, step) + write_file(argsfile, "\n".join(args)) + if not run_command([cmd, '-f', relpath(argsfile, script_path)], cwd=script_path, env=self.get_env()): + return False + return True + + def _hdlvar_args(self): + """ + Return hdlvar argument if available + """ + if self._hdlvar is None: + return [] + return ['-hdlvar "%s"' % self._hdlvar] + + @staticmethod + def _generic_args(entity_name, generics): + """ + Create irun arguments for generics/parameters + """ + args = [] + for name, value in generics.items(): + if _generic_needs_quoting(value): + args += ['''-gpg "%s.%s => \\"%s\\""''' % (entity_name, name, value)] + else: + args += ['''-gpg "%s.%s => %s"''' % (entity_name, name, value)] + return args + + +def _generic_needs_quoting(value): # pylint: disable=missing-docstring + if sys.version_info.major == 2: + return isinstance(value, (str, bool, unicode)) # pylint: disable=undefined-variable + + return isinstance(value, (str, bool)) diff --git a/vunit/json4vhdl.py b/vunit/json4vhdl.py index cf2b5ab0c..2063bef44 100644 --- a/vunit/json4vhdl.py +++ b/vunit/json4vhdl.py @@ -1,41 +1,41 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -json4vhdl helper functions -""" - -import json - - -def encode_json(obj): - """ - Convert object to stringified JSON - - :param obj: Object to stringify - - :example: - - .. code-block:: python - - stringified_generic = encode_json(generics) - """ - return json.dumps(obj, separators=(',', ':')) - - -def read_json(filename): - """ - Read a JSON file and return an object - - :param filename: The name of the file to read - - :example: - - .. code-block:: python - - generics = read_json(join(root, "src/test/data/data.json")) - """ - return json.loads(open(filename, 'r').read()) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +json4vhdl helper functions +""" + +import json + + +def encode_json(obj): + """ + Convert object to stringified JSON + + :param obj: Object to stringify + + :example: + + .. code-block:: python + + stringified_generic = encode_json(generics) + """ + return json.dumps(obj, separators=(',', ':')) + + +def read_json(filename): + """ + Read a JSON file and return an object + + :param filename: The name of the file to read + + :example: + + .. code-block:: python + + generics = read_json(join(root, "src/test/data/data.json")) + """ + return json.loads(open(filename, 'r').read()) diff --git a/vunit/location_preprocessor.py b/vunit/location_preprocessor.py index 7eaaaa1b0..27d4618f8 100644 --- a/vunit/location_preprocessor.py +++ b/vunit/location_preprocessor.py @@ -1,117 +1,117 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Preprocessing of VHDL files to add file_name and line_num arguments to function calls -to enable better messages -""" - - -import re - - -class LocationPreprocessor(object): - """ - Preprocessing of VHDL files to add file_name and line_num - arguments to calls of known function and procedures - """ - def __init__(self): - self._subprograms_with_arguments = [ - 'log', 'verbose_high2', 'verbose_high1', 'verbose', - 'verbose_low1', 'verbose_low2', 'debug_high2', 'debug_high1', - 'debug', 'debug_low1', 'debug_low2', 'info_high2', 'info_high1', - 'info', 'info_low1', 'info_low2', 'warning_high2', 'warning_high1', - 'warning', 'warning_low1', 'warning_low2', 'error_high2', - 'error_high1', 'error', 'error_low1', 'error_low2', 'failure_high2', - 'failure_high1', 'failure', 'failure_low1', 'failure_low2', 'check', - 'check_failed', 'check_true', 'check_false', 'check_implication', - 'check_stable', 'check_equal', 'check_not_unknown', 'check_zero_one_hot', - 'check_one_hot', 'check_next', 'check_sequence', 'check_relation', - 'lock_entry', 'lock_exit', 'unlock_entry', 'unlock_exit'] - self._subprograms_without_arguments = [] - - def add_subprogram(self, subprogram): - """ - Add a subprogram name to the list of known names to preprocess - """ - self._subprograms_without_arguments.append(subprogram) - self._subprograms_with_arguments.append(subprogram) - - def remove_subprogram(self, subprogram): - """ - Remove a subprogram name from the list of known names to preprocess - """ - if subprogram not in self._subprograms_without_arguments + self._subprograms_with_arguments: - raise RuntimeError('Unable to remove unknown subprogram %s' % subprogram) - - if subprogram in self._subprograms_without_arguments: - self._subprograms_without_arguments.remove(subprogram) - - if subprogram in self._subprograms_with_arguments: - self._subprograms_with_arguments.remove(subprogram) - - @staticmethod - def _find_closing_parenthesis(args): - """ - Find the balanced closing parentesis - - @TODO duplicate with vhdl_parser.py - """ - pattern = re.compile(r'\(|\)') - balance = 0 - for match in pattern.finditer(args): - balance += 1 if match.group() == '(' else -1 - if balance == 0: - return match.start() - return None - - _already_fixed_file_name_pattern = re.compile(r'file_name\s*=>', re.MULTILINE) - _already_fixed_line_num_pattern = re.compile(r'line_num\s*=>', re.MULTILINE) - _subprogram_declaration_start_backwards_pattern = re.compile(r'\s+(erudecorp|noitcnuf)') - _assignment_pattern = re.compile(r'\s*(:=|<=)', re.MULTILINE) - - def run(self, code, file_name): - """ - Return preprocessed code given file_name of original file - """ - potential_subprogram_call_with_arguments_pattern = re.compile( - r'[^a-zA-Z0-9_](?P' + '|'.join(self._subprograms_with_arguments) + r')\s*(?P\()', - re.MULTILINE) - - potential_subprogram_call_without_arguments_pattern = re.compile( - r'[^a-zA-Z0-9_](?P' + '|'.join(self._subprograms_without_arguments) + r')\s*;', - re.MULTILINE) - - matches = list(potential_subprogram_call_with_arguments_pattern.finditer(code)) - if self._subprograms_without_arguments: - matches += list(potential_subprogram_call_without_arguments_pattern.finditer(code)) - matches.sort(key=lambda match: match.start('subprogram'), reverse=True) - - for match in matches: - if self._subprogram_declaration_start_backwards_pattern.match(code[match.start():0:-1]): - continue - file_name_association = ', file_name => "' + file_name + '"' - line_num_association = ', line_num => ' + str(1 + code[:match.start('subprogram')].count('\n')) - if 'args' in match.groupdict(): - closing_paranthesis_start = self._find_closing_parenthesis(code[match.start('args'):]) - - if self._assignment_pattern.match(code[match.start('args') + closing_paranthesis_start + 1:]): - continue - - args = code[match.start('args'): match.start('args') + closing_paranthesis_start] - already_fixed_file_name = self._already_fixed_file_name_pattern.search(args) is not None - already_fixed_line_num = self._already_fixed_line_num_pattern.search(args) is not None - file_name_association = file_name_association if not already_fixed_file_name else '' - line_num_association = line_num_association if not already_fixed_line_num else '' - - code = (code[:match.start('args') + closing_paranthesis_start] - + line_num_association - + file_name_association + code[match.start('args') + closing_paranthesis_start:]) - else: - code = (code[:match.end('subprogram')] - + '(' + line_num_association[2:] + file_name_association + ')' - + code[match.end('subprogram'):]) - return code +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Preprocessing of VHDL files to add file_name and line_num arguments to function calls +to enable better messages +""" + + +import re + + +class LocationPreprocessor(object): + """ + Preprocessing of VHDL files to add file_name and line_num + arguments to calls of known function and procedures + """ + def __init__(self): + self._subprograms_with_arguments = [ + 'log', 'verbose_high2', 'verbose_high1', 'verbose', + 'verbose_low1', 'verbose_low2', 'debug_high2', 'debug_high1', + 'debug', 'debug_low1', 'debug_low2', 'info_high2', 'info_high1', + 'info', 'info_low1', 'info_low2', 'warning_high2', 'warning_high1', + 'warning', 'warning_low1', 'warning_low2', 'error_high2', + 'error_high1', 'error', 'error_low1', 'error_low2', 'failure_high2', + 'failure_high1', 'failure', 'failure_low1', 'failure_low2', 'check', + 'check_failed', 'check_true', 'check_false', 'check_implication', + 'check_stable', 'check_equal', 'check_not_unknown', 'check_zero_one_hot', + 'check_one_hot', 'check_next', 'check_sequence', 'check_relation', + 'lock_entry', 'lock_exit', 'unlock_entry', 'unlock_exit'] + self._subprograms_without_arguments = [] + + def add_subprogram(self, subprogram): + """ + Add a subprogram name to the list of known names to preprocess + """ + self._subprograms_without_arguments.append(subprogram) + self._subprograms_with_arguments.append(subprogram) + + def remove_subprogram(self, subprogram): + """ + Remove a subprogram name from the list of known names to preprocess + """ + if subprogram not in self._subprograms_without_arguments + self._subprograms_with_arguments: + raise RuntimeError('Unable to remove unknown subprogram %s' % subprogram) + + if subprogram in self._subprograms_without_arguments: + self._subprograms_without_arguments.remove(subprogram) + + if subprogram in self._subprograms_with_arguments: + self._subprograms_with_arguments.remove(subprogram) + + @staticmethod + def _find_closing_parenthesis(args): + """ + Find the balanced closing parentesis + + @TODO duplicate with vhdl_parser.py + """ + pattern = re.compile(r'\(|\)') + balance = 0 + for match in pattern.finditer(args): + balance += 1 if match.group() == '(' else -1 + if balance == 0: + return match.start() + return None + + _already_fixed_file_name_pattern = re.compile(r'file_name\s*=>', re.MULTILINE) + _already_fixed_line_num_pattern = re.compile(r'line_num\s*=>', re.MULTILINE) + _subprogram_declaration_start_backwards_pattern = re.compile(r'\s+(erudecorp|noitcnuf)') + _assignment_pattern = re.compile(r'\s*(:=|<=)', re.MULTILINE) + + def run(self, code, file_name): + """ + Return preprocessed code given file_name of original file + """ + potential_subprogram_call_with_arguments_pattern = re.compile( + r'[^a-zA-Z0-9_](?P' + '|'.join(self._subprograms_with_arguments) + r')\s*(?P\()', + re.MULTILINE) + + potential_subprogram_call_without_arguments_pattern = re.compile( + r'[^a-zA-Z0-9_](?P' + '|'.join(self._subprograms_without_arguments) + r')\s*;', + re.MULTILINE) + + matches = list(potential_subprogram_call_with_arguments_pattern.finditer(code)) + if self._subprograms_without_arguments: + matches += list(potential_subprogram_call_without_arguments_pattern.finditer(code)) + matches.sort(key=lambda match: match.start('subprogram'), reverse=True) + + for match in matches: + if self._subprogram_declaration_start_backwards_pattern.match(code[match.start():0:-1]): + continue + file_name_association = ', file_name => "' + file_name + '"' + line_num_association = ', line_num => ' + str(1 + code[:match.start('subprogram')].count('\n')) + if 'args' in match.groupdict(): + closing_paranthesis_start = self._find_closing_parenthesis(code[match.start('args'):]) + + if self._assignment_pattern.match(code[match.start('args') + closing_paranthesis_start + 1:]): + continue + + args = code[match.start('args'): match.start('args') + closing_paranthesis_start] + already_fixed_file_name = self._already_fixed_file_name_pattern.search(args) is not None + already_fixed_line_num = self._already_fixed_line_num_pattern.search(args) is not None + file_name_association = file_name_association if not already_fixed_file_name else '' + line_num_association = line_num_association if not already_fixed_line_num else '' + + code = (code[:match.start('args') + closing_paranthesis_start] + + line_num_association + + file_name_association + code[match.start('args') + closing_paranthesis_start:]) + else: + code = (code[:match.end('subprogram')] + + '(' + line_num_association[2:] + file_name_association + ')' + + code[match.end('subprogram'):]) + return code diff --git a/vunit/modelsim_interface.py b/vunit/modelsim_interface.py index 53ce14830..07312d756 100644 --- a/vunit/modelsim_interface.py +++ b/vunit/modelsim_interface.py @@ -1,409 +1,409 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Interface towards Mentor Graphics ModelSim -""" - - -from __future__ import print_function - -import logging -import sys -import io -import os -from os.path import join, dirname, abspath -try: - # Python 3 - from configparser import RawConfigParser -except ImportError: - # Python 2 - from ConfigParser import RawConfigParser # pylint: disable=import-error - -from vunit.ostools import Process, file_exists -from vunit.simulator_interface import (SimulatorInterface, - ListOfStringOption, - StringOption) -from vunit.exceptions import CompileError -from vunit.vsim_simulator_mixin import (VsimSimulatorMixin, - fix_path) - -LOGGER = logging.getLogger(__name__) - - -class ModelSimInterface(VsimSimulatorMixin, SimulatorInterface): # pylint: disable=too-many-instance-attributes - """ - Mentor Graphics ModelSim interface - - The interface supports both running each simulation in separate vsim processes or - re-using the same vsim process to avoid startup-overhead (persistent=True) - """ - name = "modelsim" - supports_gui_flag = True - package_users_depend_on_bodies = False - - compile_options = [ - ListOfStringOption("modelsim.vcom_flags"), - ListOfStringOption("modelsim.vlog_flags"), - ] - - sim_options = [ - ListOfStringOption("modelsim.vsim_flags"), - ListOfStringOption("modelsim.vsim_flags.gui"), - ListOfStringOption("modelsim.init_files.after_load"), - StringOption("modelsim.init_file.gui"), - ] - - @classmethod - def from_args(cls, args, output_path, **kwargs): - """ - Create new instance from command line arguments object - """ - persistent = not (args.unique_sim or args.gui) - - return cls(prefix=cls.find_prefix(), - output_path=output_path, - persistent=persistent, - gui=args.gui) - - @classmethod - def find_prefix_from_path(cls): - """ - Find first valid modelsim toolchain prefix - """ - def has_modelsim_ini(path): - return os.path.isfile(join(path, "..", "modelsim.ini")) - - return cls.find_toolchain(["vsim"], - constraints=[has_modelsim_ini]) - - @classmethod - def supports_vhdl_package_generics(cls): - """ - Returns True when this simulator supports VHDL package generics - """ - return True - - def __init__(self, prefix, output_path, persistent=False, gui=False): - SimulatorInterface.__init__(self, output_path, gui) - VsimSimulatorMixin.__init__(self, prefix, persistent, - sim_cfg_file_name=join(output_path, "modelsim.ini")) - self._libraries = [] - self._coverage_files = set() - assert not (persistent and gui) - self._create_modelsim_ini() - - def _create_modelsim_ini(self): - """ - Create the modelsim.ini file - """ - parent = dirname(self._sim_cfg_file_name) - if not file_exists(parent): - os.makedirs(parent) - - original_modelsim_ini = os.environ.get("VUNIT_MODELSIM_INI", - join(self._prefix, "..", "modelsim.ini")) - with open(original_modelsim_ini, 'rb') as fread: - with open(self._sim_cfg_file_name, 'wb') as fwrite: - fwrite.write(fread.read()) - - def add_simulator_specific(self, project): - """ - Add libraries from modelsim.ini file and add coverage flags - """ - mapped_libraries = self._get_mapped_libraries() - for library_name in mapped_libraries: - if not project.has_library(library_name): - project.add_builtin_library(library_name) - - def setup_library_mapping(self, project): - """ - Setup library mapping - """ - mapped_libraries = self._get_mapped_libraries() - - for library in project.get_libraries(): - self._libraries.append(library) - self.create_library(library.name, library.directory, mapped_libraries) - - def compile_source_file_command(self, source_file): - """ - Returns the command to compile a single source file - """ - if source_file.is_vhdl: - return self.compile_vhdl_file_command(source_file) - - if source_file.is_any_verilog: - return self.compile_verilog_file_command(source_file) - - LOGGER.error("Unknown file type: %s", source_file.file_type) - raise CompileError - - def compile_vhdl_file_command(self, source_file): - """ - Returns the command to compile a vhdl file - """ - return ([join(self._prefix, 'vcom'), '-quiet', '-modelsimini', self._sim_cfg_file_name] - + source_file.compile_options.get("modelsim.vcom_flags", []) - + ['-' + source_file.get_vhdl_standard(), '-work', source_file.library.name, source_file.name]) - - def compile_verilog_file_command(self, source_file): - """ - Returns the command to compile a verilog file - """ - args = [join(self._prefix, 'vlog'), '-quiet', '-modelsimini', self._sim_cfg_file_name] - if source_file.is_system_verilog: - args += ["-sv"] - args += source_file.compile_options.get("modelsim.vlog_flags", []) - args += ['-work', source_file.library.name, source_file.name] - - for library in self._libraries: - args += ["-L", library.name] - for include_dir in source_file.include_dirs: - args += ["+incdir+%s" % include_dir] - for key, value in source_file.defines.items(): - args += ["+define+%s=%s" % (key, value)] - return args - - def create_library(self, library_name, path, mapped_libraries=None): - """ - Create and map a library_name to path - """ - mapped_libraries = mapped_libraries if mapped_libraries is not None else {} - - if not file_exists(dirname(abspath(path))): - os.makedirs(dirname(abspath(path))) - - if not file_exists(path): - proc = Process([join(self._prefix, 'vlib'), '-unix', path], - env=self.get_env()) - proc.consume_output(callback=None) - - if library_name in mapped_libraries and mapped_libraries[library_name] == path: - return - - cfg = parse_modelsimini(self._sim_cfg_file_name) - cfg.set("Library", library_name, path) - write_modelsimini(cfg, self._sim_cfg_file_name) - - def _get_mapped_libraries(self): - """ - Get mapped libraries from modelsim.ini file - """ - cfg = parse_modelsimini(self._sim_cfg_file_name) - libraries = dict(cfg.items("Library")) - if "others" in libraries: - del libraries["others"] - return libraries - - def _create_load_function(self, test_suite_name, config, output_path): - """ - Create the vunit_load TCL function that runs the vsim command and loads the design - """ - - set_generic_str = " ".join(('-g/%s/%s=%s' % (config.entity_name, - name, - encode_generic_value(value)) - for name, value in config.generics.items())) - pli_str = " ".join("-pli {%s}" % fix_path(name) for name in config.sim_options.get('pli', [])) - - if config.architecture_name is None: - architecture_suffix = "" - else: - architecture_suffix = "(%s)" % config.architecture_name - - if config.sim_options.get("enable_coverage", False): - coverage_file = join(output_path, "coverage.ucdb") - self._coverage_files.add(coverage_file) - coverage_save_cmd = ( - "coverage save -onexit -testname {%s} -assert -directive -cvg -codeAll {%s}" - % (test_suite_name, fix_path(coverage_file))) - coverage_args = "-coverage" - else: - coverage_save_cmd = "" - coverage_args = "" - - vsim_flags = ["-wlf {%s}" % fix_path(join(output_path, "vsim.wlf")), - "-quiet", - "-t ps", - # for correct handling of verilog fatal/finish - "-onfinish stop", - pli_str, - set_generic_str, - config.library_name + "." + config.entity_name + architecture_suffix, - coverage_args, - self._vsim_extra_args(config)] - - # There is a known bug in modelsim that prevents the -modelsimini flag from accepting - # a space in the path even with escaping, see issue #36 - if " " not in self._sim_cfg_file_name: - vsim_flags.insert(0, "-modelsimini %s" % fix_path(self._sim_cfg_file_name)) - - for library in self._libraries: - vsim_flags += ["-L", library.name] - - vhdl_assert_stop_level_mapping = dict(warning=1, error=2, failure=3) - - tcl = """ -proc vunit_load {{{{vsim_extra_args ""}}}} {{ - set vsim_failed [catch {{ - eval vsim ${{vsim_extra_args}} {{{vsim_flags}}} - }}] - - if {{${{vsim_failed}}}} {{ - echo Command 'vsim ${{vsim_extra_args}} {vsim_flags}' failed - echo Bad flag from vsim_extra_args? - return true - }} - - if {{[_vunit_source_init_files_after_load]}} {{ - return true - }} - - global BreakOnAssertion - set BreakOnAssertion {break_on_assert} - - global NumericStdNoWarnings - set NumericStdNoWarnings {no_warnings} - - global StdArithNoWarnings - set StdArithNoWarnings {no_warnings} - - {coverage_save_cmd} - return false -}} -""".format(coverage_save_cmd=coverage_save_cmd, - vsim_flags=" ".join(vsim_flags), - break_on_assert=vhdl_assert_stop_level_mapping[config.vhdl_assert_stop_level], - no_warnings=1 if config.sim_options.get("disable_ieee_warnings", False) else 0) - - return tcl - - @staticmethod - def _create_run_function(): - """ - Create the vunit_run function to run the test bench - """ - return """ - -proc _vunit_run_failure {} { - catch { - # tb command can fail when error comes from pli - echo "Stack trace result from 'tb' command" - echo [tb] - echo - echo "Surrounding code from 'see' command" - echo [see] - } -} - -proc _vunit_run {} { - proc on_break {} { - resume - } - onbreak {on_break} - - run -all -} - -proc _vunit_sim_restart {} { - restart -f -} -""" - - def _vsim_extra_args(self, config): - """ - Determine vsim_extra_args - """ - vsim_extra_args = [] - vsim_extra_args = config.sim_options.get("modelsim.vsim_flags", - vsim_extra_args) - - if self._gui: - vsim_extra_args = config.sim_options.get("modelsim.vsim_flags.gui", - vsim_extra_args) - - return " ".join(vsim_extra_args) - - def merge_coverage(self, file_name, args=None): - """ - Merge coverage from all test cases - """ - if self._persistent_shell is not None: - # Teardown to ensure ucdb file was written. - self._persistent_shell.teardown() - - if args is None: - args = [] - - vcover_cmd = [join(self._prefix, 'vcover'), 'merge'] + args + [file_name] - - for coverage_file in self._coverage_files: - if file_exists(coverage_file): - vcover_cmd.append(coverage_file) - else: - LOGGER.warning("Missing coverage file: %s", coverage_file) - - print("Merging coverage files into %s..." % file_name) - vcover_merge_process = Process(vcover_cmd, - env=self.get_env()) - vcover_merge_process.consume_output() - print("Done merging coverage files") - - @staticmethod - def get_env(): - """ - Remove MODELSIM environment variable - """ - remove = ("MODELSIM",) - env = os.environ.copy() - for key in remove: - if key in env.keys(): - del env[key] - return env - - -def encode_generic_value(value): - """ - Ensure values with space in them are quoted - """ - s_value = str(value) - if " " in s_value: - return '{"%s"}' % s_value - if "," in s_value: - return '"%s"' % s_value - return s_value - - -def parse_modelsimini(file_name): - """ - Parse a modelsim.ini file - :returns: A RawConfigParser object - """ - cfg = RawConfigParser() - if sys.version_info.major == 2: - # For Python 2 read modelsim.ini as binary file since ConfigParser - # does not support unicode - with io.open(file_name, "rb") as fptr: - cfg.readfp(fptr) # pylint: disable=deprecated-method - else: - with io.open(file_name, "r", encoding="utf-8") as fptr: - cfg.read_file(fptr) - return cfg - - -def write_modelsimini(cfg, file_name): - """ - Writes a modelsim.ini file - """ - if sys.version_info.major == 2: - # For Python 2 write modelsim.ini as binary file since ConfigParser - # does not support unicode - with io.open(file_name, "wb") as optr: - cfg.write(optr) - else: - with io.open(file_name, "w", encoding="utf-8") as optr: - cfg.write(optr) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Interface towards Mentor Graphics ModelSim +""" + + +from __future__ import print_function + +import logging +import sys +import io +import os +from os.path import join, dirname, abspath +try: + # Python 3 + from configparser import RawConfigParser +except ImportError: + # Python 2 + from ConfigParser import RawConfigParser # pylint: disable=import-error + +from vunit.ostools import Process, file_exists +from vunit.simulator_interface import (SimulatorInterface, + ListOfStringOption, + StringOption) +from vunit.exceptions import CompileError +from vunit.vsim_simulator_mixin import (VsimSimulatorMixin, + fix_path) + +LOGGER = logging.getLogger(__name__) + + +class ModelSimInterface(VsimSimulatorMixin, SimulatorInterface): # pylint: disable=too-many-instance-attributes + """ + Mentor Graphics ModelSim interface + + The interface supports both running each simulation in separate vsim processes or + re-using the same vsim process to avoid startup-overhead (persistent=True) + """ + name = "modelsim" + supports_gui_flag = True + package_users_depend_on_bodies = False + + compile_options = [ + ListOfStringOption("modelsim.vcom_flags"), + ListOfStringOption("modelsim.vlog_flags"), + ] + + sim_options = [ + ListOfStringOption("modelsim.vsim_flags"), + ListOfStringOption("modelsim.vsim_flags.gui"), + ListOfStringOption("modelsim.init_files.after_load"), + StringOption("modelsim.init_file.gui"), + ] + + @classmethod + def from_args(cls, args, output_path, **kwargs): + """ + Create new instance from command line arguments object + """ + persistent = not (args.unique_sim or args.gui) + + return cls(prefix=cls.find_prefix(), + output_path=output_path, + persistent=persistent, + gui=args.gui) + + @classmethod + def find_prefix_from_path(cls): + """ + Find first valid modelsim toolchain prefix + """ + def has_modelsim_ini(path): + return os.path.isfile(join(path, "..", "modelsim.ini")) + + return cls.find_toolchain(["vsim"], + constraints=[has_modelsim_ini]) + + @classmethod + def supports_vhdl_package_generics(cls): + """ + Returns True when this simulator supports VHDL package generics + """ + return True + + def __init__(self, prefix, output_path, persistent=False, gui=False): + SimulatorInterface.__init__(self, output_path, gui) + VsimSimulatorMixin.__init__(self, prefix, persistent, + sim_cfg_file_name=join(output_path, "modelsim.ini")) + self._libraries = [] + self._coverage_files = set() + assert not (persistent and gui) + self._create_modelsim_ini() + + def _create_modelsim_ini(self): + """ + Create the modelsim.ini file + """ + parent = dirname(self._sim_cfg_file_name) + if not file_exists(parent): + os.makedirs(parent) + + original_modelsim_ini = os.environ.get("VUNIT_MODELSIM_INI", + join(self._prefix, "..", "modelsim.ini")) + with open(original_modelsim_ini, 'rb') as fread: + with open(self._sim_cfg_file_name, 'wb') as fwrite: + fwrite.write(fread.read()) + + def add_simulator_specific(self, project): + """ + Add libraries from modelsim.ini file and add coverage flags + """ + mapped_libraries = self._get_mapped_libraries() + for library_name in mapped_libraries: + if not project.has_library(library_name): + project.add_builtin_library(library_name) + + def setup_library_mapping(self, project): + """ + Setup library mapping + """ + mapped_libraries = self._get_mapped_libraries() + + for library in project.get_libraries(): + self._libraries.append(library) + self.create_library(library.name, library.directory, mapped_libraries) + + def compile_source_file_command(self, source_file): + """ + Returns the command to compile a single source file + """ + if source_file.is_vhdl: + return self.compile_vhdl_file_command(source_file) + + if source_file.is_any_verilog: + return self.compile_verilog_file_command(source_file) + + LOGGER.error("Unknown file type: %s", source_file.file_type) + raise CompileError + + def compile_vhdl_file_command(self, source_file): + """ + Returns the command to compile a vhdl file + """ + return ([join(self._prefix, 'vcom'), '-quiet', '-modelsimini', self._sim_cfg_file_name] + + source_file.compile_options.get("modelsim.vcom_flags", []) + + ['-' + source_file.get_vhdl_standard(), '-work', source_file.library.name, source_file.name]) + + def compile_verilog_file_command(self, source_file): + """ + Returns the command to compile a verilog file + """ + args = [join(self._prefix, 'vlog'), '-quiet', '-modelsimini', self._sim_cfg_file_name] + if source_file.is_system_verilog: + args += ["-sv"] + args += source_file.compile_options.get("modelsim.vlog_flags", []) + args += ['-work', source_file.library.name, source_file.name] + + for library in self._libraries: + args += ["-L", library.name] + for include_dir in source_file.include_dirs: + args += ["+incdir+%s" % include_dir] + for key, value in source_file.defines.items(): + args += ["+define+%s=%s" % (key, value)] + return args + + def create_library(self, library_name, path, mapped_libraries=None): + """ + Create and map a library_name to path + """ + mapped_libraries = mapped_libraries if mapped_libraries is not None else {} + + if not file_exists(dirname(abspath(path))): + os.makedirs(dirname(abspath(path))) + + if not file_exists(path): + proc = Process([join(self._prefix, 'vlib'), '-unix', path], + env=self.get_env()) + proc.consume_output(callback=None) + + if library_name in mapped_libraries and mapped_libraries[library_name] == path: + return + + cfg = parse_modelsimini(self._sim_cfg_file_name) + cfg.set("Library", library_name, path) + write_modelsimini(cfg, self._sim_cfg_file_name) + + def _get_mapped_libraries(self): + """ + Get mapped libraries from modelsim.ini file + """ + cfg = parse_modelsimini(self._sim_cfg_file_name) + libraries = dict(cfg.items("Library")) + if "others" in libraries: + del libraries["others"] + return libraries + + def _create_load_function(self, test_suite_name, config, output_path): + """ + Create the vunit_load TCL function that runs the vsim command and loads the design + """ + + set_generic_str = " ".join(('-g/%s/%s=%s' % (config.entity_name, + name, + encode_generic_value(value)) + for name, value in config.generics.items())) + pli_str = " ".join("-pli {%s}" % fix_path(name) for name in config.sim_options.get('pli', [])) + + if config.architecture_name is None: + architecture_suffix = "" + else: + architecture_suffix = "(%s)" % config.architecture_name + + if config.sim_options.get("enable_coverage", False): + coverage_file = join(output_path, "coverage.ucdb") + self._coverage_files.add(coverage_file) + coverage_save_cmd = ( + "coverage save -onexit -testname {%s} -assert -directive -cvg -codeAll {%s}" + % (test_suite_name, fix_path(coverage_file))) + coverage_args = "-coverage" + else: + coverage_save_cmd = "" + coverage_args = "" + + vsim_flags = ["-wlf {%s}" % fix_path(join(output_path, "vsim.wlf")), + "-quiet", + "-t ps", + # for correct handling of verilog fatal/finish + "-onfinish stop", + pli_str, + set_generic_str, + config.library_name + "." + config.entity_name + architecture_suffix, + coverage_args, + self._vsim_extra_args(config)] + + # There is a known bug in modelsim that prevents the -modelsimini flag from accepting + # a space in the path even with escaping, see issue #36 + if " " not in self._sim_cfg_file_name: + vsim_flags.insert(0, "-modelsimini %s" % fix_path(self._sim_cfg_file_name)) + + for library in self._libraries: + vsim_flags += ["-L", library.name] + + vhdl_assert_stop_level_mapping = dict(warning=1, error=2, failure=3) + + tcl = """ +proc vunit_load {{{{vsim_extra_args ""}}}} {{ + set vsim_failed [catch {{ + eval vsim ${{vsim_extra_args}} {{{vsim_flags}}} + }}] + + if {{${{vsim_failed}}}} {{ + echo Command 'vsim ${{vsim_extra_args}} {vsim_flags}' failed + echo Bad flag from vsim_extra_args? + return true + }} + + if {{[_vunit_source_init_files_after_load]}} {{ + return true + }} + + global BreakOnAssertion + set BreakOnAssertion {break_on_assert} + + global NumericStdNoWarnings + set NumericStdNoWarnings {no_warnings} + + global StdArithNoWarnings + set StdArithNoWarnings {no_warnings} + + {coverage_save_cmd} + return false +}} +""".format(coverage_save_cmd=coverage_save_cmd, + vsim_flags=" ".join(vsim_flags), + break_on_assert=vhdl_assert_stop_level_mapping[config.vhdl_assert_stop_level], + no_warnings=1 if config.sim_options.get("disable_ieee_warnings", False) else 0) + + return tcl + + @staticmethod + def _create_run_function(): + """ + Create the vunit_run function to run the test bench + """ + return """ + +proc _vunit_run_failure {} { + catch { + # tb command can fail when error comes from pli + echo "Stack trace result from 'tb' command" + echo [tb] + echo + echo "Surrounding code from 'see' command" + echo [see] + } +} + +proc _vunit_run {} { + proc on_break {} { + resume + } + onbreak {on_break} + + run -all +} + +proc _vunit_sim_restart {} { + restart -f +} +""" + + def _vsim_extra_args(self, config): + """ + Determine vsim_extra_args + """ + vsim_extra_args = [] + vsim_extra_args = config.sim_options.get("modelsim.vsim_flags", + vsim_extra_args) + + if self._gui: + vsim_extra_args = config.sim_options.get("modelsim.vsim_flags.gui", + vsim_extra_args) + + return " ".join(vsim_extra_args) + + def merge_coverage(self, file_name, args=None): + """ + Merge coverage from all test cases + """ + if self._persistent_shell is not None: + # Teardown to ensure ucdb file was written. + self._persistent_shell.teardown() + + if args is None: + args = [] + + vcover_cmd = [join(self._prefix, 'vcover'), 'merge'] + args + [file_name] + + for coverage_file in self._coverage_files: + if file_exists(coverage_file): + vcover_cmd.append(coverage_file) + else: + LOGGER.warning("Missing coverage file: %s", coverage_file) + + print("Merging coverage files into %s..." % file_name) + vcover_merge_process = Process(vcover_cmd, + env=self.get_env()) + vcover_merge_process.consume_output() + print("Done merging coverage files") + + @staticmethod + def get_env(): + """ + Remove MODELSIM environment variable + """ + remove = ("MODELSIM",) + env = os.environ.copy() + for key in remove: + if key in env.keys(): + del env[key] + return env + + +def encode_generic_value(value): + """ + Ensure values with space in them are quoted + """ + s_value = str(value) + if " " in s_value: + return '{"%s"}' % s_value + if "," in s_value: + return '"%s"' % s_value + return s_value + + +def parse_modelsimini(file_name): + """ + Parse a modelsim.ini file + :returns: A RawConfigParser object + """ + cfg = RawConfigParser() + if sys.version_info.major == 2: + # For Python 2 read modelsim.ini as binary file since ConfigParser + # does not support unicode + with io.open(file_name, "rb") as fptr: + cfg.readfp(fptr) # pylint: disable=deprecated-method + else: + with io.open(file_name, "r", encoding="utf-8") as fptr: + cfg.read_file(fptr) + return cfg + + +def write_modelsimini(cfg, file_name): + """ + Writes a modelsim.ini file + """ + if sys.version_info.major == 2: + # For Python 2 write modelsim.ini as binary file since ConfigParser + # does not support unicode + with io.open(file_name, "wb") as optr: + cfg.write(optr) + else: + with io.open(file_name, "w", encoding="utf-8") as optr: + cfg.write(optr) diff --git a/vunit/ostools.py b/vunit/ostools.py index 12bddec92..125d3acc2 100644 --- a/vunit/ostools.py +++ b/vunit/ostools.py @@ -1,360 +1,360 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Provides operating systems dependent functionality that can be easily -stubbed for testing -""" - - -from __future__ import print_function - -import time -import subprocess -import threading -import shutil -import sys -try: - # Python 3.x - from queue import Queue, Empty -except ImportError: - # Python 2.7 - from Queue import Queue, Empty # pylint: disable=import-error - -from os.path import exists, getmtime, dirname, relpath, splitdrive -import os -import io - -import logging -LOGGER = logging.getLogger(__name__) - -IS_WINDOWS_SYSTEM = os.name == 'nt' - - -class ProgramStatus(object): - """ - Maintain global program status to support graceful shutdown - """ - def __init__(self): - self._lock = threading.Lock() - self._shutting_down = False - - @property - def is_shutting_down(self): - with self._lock: # pylint: disable=not-context-manager - return self._shutting_down - - def check_for_shutdown(self): - if self.is_shutting_down: - raise KeyboardInterrupt - - def shutdown(self): - with self._lock: # pylint: disable=not-context-manager - LOGGER.debug("ProgramStatus.shutdown") - self._shutting_down = True - - def reset(self): - with self._lock: # pylint: disable=not-context-manager - self._shutting_down = False - - -PROGRAM_STATUS = ProgramStatus() - - -class InterruptableQueue(object): - """ - A Queue which can be interrupted - """ - - def __init__(self): - self._queue = Queue() - - def get(self): - """ - Get a value from the queue - """ - while True: - PROGRAM_STATUS.check_for_shutdown() - try: - return self._queue.get(timeout=0.1) - except Empty: - pass - - def put(self, value): - self._queue.put(value) - - def empty(self): - return self._queue.empty() - - -class Process(object): - """ - A simple process interface which supports asynchronously consuming the stdout and stderr - of the process while it is running. - """ - - class NonZeroExitCode(Exception): - pass - - def __init__(self, args, cwd=None, env=None): - self._args = args - - # Create process with new process group - # Sending a signal to a process group will send it to all children - # Hopefully this way no orphaned processes will be left behind - if IS_WINDOWS_SYSTEM: # Windows - self._process = subprocess.Popen( - args, - bufsize=0, - cwd=cwd, - env=env, - stdout=subprocess.PIPE, - stdin=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True, - # Create new process group on Windows - creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) - else: - self._process = subprocess.Popen( - args, - bufsize=0, - cwd=cwd, - env=env, - stdout=subprocess.PIPE, - stdin=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True, - # Create new process group on POSIX, setpgrp does not exist on Windows - preexec_fn=os.setpgrp) # pylint: disable=no-member - - LOGGER.debug("Started process with pid=%i: '%s'", self._process.pid, (" ".join(args))) - - self._queue = InterruptableQueue() - self._reader = AsynchronousFileReader(self._process.stdout, self._queue) - self._reader.start() - - def write(self, *args, **kwargs): - """ Write to stdin """ - if not self._process.stdin.closed: - self._process.stdin.write(*args, **kwargs) - - def writeline(self, line): - """ Write a line to stdin """ - if not self._process.stdin.closed: - self._process.stdin.write(line + "\n") - self._process.stdin.flush() - - def next_line(self): - """ - Return either the next line or the exit code - """ - - if not self._reader.eof(): - # Show what we received from standard output. - msg = self._queue.get() - - if msg is not None: - return msg - - retcode = self.wait() - return retcode - - def wait(self): - """ - Wait while without completely blocking to avoid - deadlock when shutting down - """ - while self._process.poll() is None: - PROGRAM_STATUS.check_for_shutdown() - time.sleep(0.05) - LOGGER.debug("Waiting for process with pid=%i to stop", self._process.pid) - return self._process.returncode - - def is_alive(self): - """ - Returns true if alive - """ - return self._process.poll() is None - - def consume_output(self, callback=print): - """ - Consume the output of the process. - The output is interpreted as UTF-8 text. - - @param callback Called for each line of output - @raises Process.NonZeroExitCode when the process does not exit with code zero - """ - - def default_callback(*args, **kwargs): - pass - - if not callback: - callback = default_callback - - while not self._reader.eof(): - line = self._queue.get() - if line is None: - break - - if callback(line) is not None: - return - - retcode = None - while retcode is None: - retcode = self.wait() - if retcode != 0: - raise Process.NonZeroExitCode - - def terminate(self): - """ - Terminate the process - """ - # Let's be tidy and join the threads we've started. - if self._process.poll() is None: - LOGGER.debug("Terminating process with pid=%i", self._process.pid) - self._process.terminate() - - if self._process.poll() is None: - time.sleep(0.05) - - if self._process.poll() is None: - LOGGER.debug("Killing process with pid=%i", self._process.pid) - self._process.kill() - - if self._process.poll() is None: - LOGGER.debug("Waiting for process with pid=%i", self._process.pid) - self.wait() - - LOGGER.debug("Process with pid=%i terminated with code=%i", - self._process.pid, - self._process.returncode) - - self._reader.join() - self._process.stdout.close() - self._process.stdin.close() - - def __del__(self): - try: - self.terminate() - except KeyboardInterrupt: - LOGGER.debug("Process.__del__: Ignoring KeyboardInterrupt") - - -class AsynchronousFileReader(threading.Thread): - """ - Helper class to implement asynchronous reading of a file - in a separate thread. Pushes read lines on a queue to - be consumed in another thread. - """ - - def __init__(self, fd, queue, encoding="utf-8"): - threading.Thread.__init__(self) - - # If Python 3 change encoding of TextIOWrapper to utf-8 ignoring decode errors - if isinstance(fd, io.TextIOWrapper): - fd = io.TextIOWrapper(fd.buffer, encoding=encoding, errors="ignore") - - self._fd = fd - self._queue = queue - self._encoding = encoding - - def run(self): - """The body of the tread: read lines and put them on the queue.""" - for line in iter(self._fd.readline, ''): - if PROGRAM_STATUS.is_shutting_down: - break - - # Convert string into utf-8 if necessary - if sys.version_info.major == 2: - string = line[:-1].decode(encoding=self._encoding, errors="ignore") - else: - string = line[:-1] - - self._queue.put(string) - self._queue.put(None) - - def eof(self): - """Check whether there is no more content to expect.""" - return not self.is_alive() and self._queue.empty() - - -def read_file(file_name, encoding="utf-8", newline=None): - """ To stub during testing """ - try: - with io.open(file_name, "r", encoding=encoding, newline=newline) as file_to_read: - data = file_to_read.read() - except UnicodeDecodeError: - LOGGER.warning("Could not decode file %s using encoding %s, ignoring encoding errors", - file_name, encoding) - with io.open(file_name, "r", encoding=encoding, errors="ignore", newline=newline) as file_to_read: - data = file_to_read.read() - - return data - - -def write_file(file_name, contents, encoding="utf-8"): - """ To stub during testing """ - - path = dirname(file_name) - if path == "": - path = "." - - if not file_exists(path): - os.makedirs(path) - - with io.open(file_name, "wb") as file_to_write: - file_to_write.write(contents.encode(encoding=encoding)) - - -def file_exists(file_name): - """ To stub during testing """ - return exists(file_name) - - -def get_modification_time(file_name): - """ To stub during testing """ - return getmtime(file_name) - - -def get_time(): - """ To stub during testing """ - return time.time() - - -def renew_path(path): - """ - Ensure path directory exists and is empty - - On Windows deleting a file will not actually delete it right away but only - mark it for deletion. Therefore there is a race-condition between rmtree and makedirs. - Virus scanners and file system indexers might temporarily block a file from being deleted right away - - http://stackoverflow.com/questions/27625683/can-anyone-explain-this-weird-behaviour-of-shutil-rmtree-and-shutil-copytree - """ - if IS_WINDOWS_SYSTEM: - retries = 10 - while retries > 0 and exists(path): - shutil.rmtree(path, ignore_errors=retries > 1) - time.sleep(0.01) - retries -= 1 - else: - if exists(path): - shutil.rmtree(path) - os.makedirs(path) - - -def simplify_path(path): - """ - Return relative path towards current working directory - unless it is a separate Windows drive - """ - cwd = os.getcwd() - drive_cwd = splitdrive(cwd)[0] - drive_path = splitdrive(path)[0] - if drive_path == drive_cwd: - return relpath(path, cwd) - - return path +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Provides operating systems dependent functionality that can be easily +stubbed for testing +""" + + +from __future__ import print_function + +import time +import subprocess +import threading +import shutil +import sys +try: + # Python 3.x + from queue import Queue, Empty +except ImportError: + # Python 2.7 + from Queue import Queue, Empty # pylint: disable=import-error + +from os.path import exists, getmtime, dirname, relpath, splitdrive +import os +import io + +import logging +LOGGER = logging.getLogger(__name__) + +IS_WINDOWS_SYSTEM = os.name == 'nt' + + +class ProgramStatus(object): + """ + Maintain global program status to support graceful shutdown + """ + def __init__(self): + self._lock = threading.Lock() + self._shutting_down = False + + @property + def is_shutting_down(self): + with self._lock: # pylint: disable=not-context-manager + return self._shutting_down + + def check_for_shutdown(self): + if self.is_shutting_down: + raise KeyboardInterrupt + + def shutdown(self): + with self._lock: # pylint: disable=not-context-manager + LOGGER.debug("ProgramStatus.shutdown") + self._shutting_down = True + + def reset(self): + with self._lock: # pylint: disable=not-context-manager + self._shutting_down = False + + +PROGRAM_STATUS = ProgramStatus() + + +class InterruptableQueue(object): + """ + A Queue which can be interrupted + """ + + def __init__(self): + self._queue = Queue() + + def get(self): + """ + Get a value from the queue + """ + while True: + PROGRAM_STATUS.check_for_shutdown() + try: + return self._queue.get(timeout=0.1) + except Empty: + pass + + def put(self, value): + self._queue.put(value) + + def empty(self): + return self._queue.empty() + + +class Process(object): + """ + A simple process interface which supports asynchronously consuming the stdout and stderr + of the process while it is running. + """ + + class NonZeroExitCode(Exception): + pass + + def __init__(self, args, cwd=None, env=None): + self._args = args + + # Create process with new process group + # Sending a signal to a process group will send it to all children + # Hopefully this way no orphaned processes will be left behind + if IS_WINDOWS_SYSTEM: # Windows + self._process = subprocess.Popen( + args, + bufsize=0, + cwd=cwd, + env=env, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + # Create new process group on Windows + creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) + else: + self._process = subprocess.Popen( + args, + bufsize=0, + cwd=cwd, + env=env, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + # Create new process group on POSIX, setpgrp does not exist on Windows + preexec_fn=os.setpgrp) # pylint: disable=no-member + + LOGGER.debug("Started process with pid=%i: '%s'", self._process.pid, (" ".join(args))) + + self._queue = InterruptableQueue() + self._reader = AsynchronousFileReader(self._process.stdout, self._queue) + self._reader.start() + + def write(self, *args, **kwargs): + """ Write to stdin """ + if not self._process.stdin.closed: + self._process.stdin.write(*args, **kwargs) + + def writeline(self, line): + """ Write a line to stdin """ + if not self._process.stdin.closed: + self._process.stdin.write(line + "\n") + self._process.stdin.flush() + + def next_line(self): + """ + Return either the next line or the exit code + """ + + if not self._reader.eof(): + # Show what we received from standard output. + msg = self._queue.get() + + if msg is not None: + return msg + + retcode = self.wait() + return retcode + + def wait(self): + """ + Wait while without completely blocking to avoid + deadlock when shutting down + """ + while self._process.poll() is None: + PROGRAM_STATUS.check_for_shutdown() + time.sleep(0.05) + LOGGER.debug("Waiting for process with pid=%i to stop", self._process.pid) + return self._process.returncode + + def is_alive(self): + """ + Returns true if alive + """ + return self._process.poll() is None + + def consume_output(self, callback=print): + """ + Consume the output of the process. + The output is interpreted as UTF-8 text. + + @param callback Called for each line of output + @raises Process.NonZeroExitCode when the process does not exit with code zero + """ + + def default_callback(*args, **kwargs): + pass + + if not callback: + callback = default_callback + + while not self._reader.eof(): + line = self._queue.get() + if line is None: + break + + if callback(line) is not None: + return + + retcode = None + while retcode is None: + retcode = self.wait() + if retcode != 0: + raise Process.NonZeroExitCode + + def terminate(self): + """ + Terminate the process + """ + # Let's be tidy and join the threads we've started. + if self._process.poll() is None: + LOGGER.debug("Terminating process with pid=%i", self._process.pid) + self._process.terminate() + + if self._process.poll() is None: + time.sleep(0.05) + + if self._process.poll() is None: + LOGGER.debug("Killing process with pid=%i", self._process.pid) + self._process.kill() + + if self._process.poll() is None: + LOGGER.debug("Waiting for process with pid=%i", self._process.pid) + self.wait() + + LOGGER.debug("Process with pid=%i terminated with code=%i", + self._process.pid, + self._process.returncode) + + self._reader.join() + self._process.stdout.close() + self._process.stdin.close() + + def __del__(self): + try: + self.terminate() + except KeyboardInterrupt: + LOGGER.debug("Process.__del__: Ignoring KeyboardInterrupt") + + +class AsynchronousFileReader(threading.Thread): + """ + Helper class to implement asynchronous reading of a file + in a separate thread. Pushes read lines on a queue to + be consumed in another thread. + """ + + def __init__(self, fd, queue, encoding="utf-8"): + threading.Thread.__init__(self) + + # If Python 3 change encoding of TextIOWrapper to utf-8 ignoring decode errors + if isinstance(fd, io.TextIOWrapper): + fd = io.TextIOWrapper(fd.buffer, encoding=encoding, errors="ignore") + + self._fd = fd + self._queue = queue + self._encoding = encoding + + def run(self): + """The body of the tread: read lines and put them on the queue.""" + for line in iter(self._fd.readline, ''): + if PROGRAM_STATUS.is_shutting_down: + break + + # Convert string into utf-8 if necessary + if sys.version_info.major == 2: + string = line[:-1].decode(encoding=self._encoding, errors="ignore") + else: + string = line[:-1] + + self._queue.put(string) + self._queue.put(None) + + def eof(self): + """Check whether there is no more content to expect.""" + return not self.is_alive() and self._queue.empty() + + +def read_file(file_name, encoding="utf-8", newline=None): + """ To stub during testing """ + try: + with io.open(file_name, "r", encoding=encoding, newline=newline) as file_to_read: + data = file_to_read.read() + except UnicodeDecodeError: + LOGGER.warning("Could not decode file %s using encoding %s, ignoring encoding errors", + file_name, encoding) + with io.open(file_name, "r", encoding=encoding, errors="ignore", newline=newline) as file_to_read: + data = file_to_read.read() + + return data + + +def write_file(file_name, contents, encoding="utf-8"): + """ To stub during testing """ + + path = dirname(file_name) + if path == "": + path = "." + + if not file_exists(path): + os.makedirs(path) + + with io.open(file_name, "wb") as file_to_write: + file_to_write.write(contents.encode(encoding=encoding)) + + +def file_exists(file_name): + """ To stub during testing """ + return exists(file_name) + + +def get_modification_time(file_name): + """ To stub during testing """ + return getmtime(file_name) + + +def get_time(): + """ To stub during testing """ + return time.time() + + +def renew_path(path): + """ + Ensure path directory exists and is empty + + On Windows deleting a file will not actually delete it right away but only + mark it for deletion. Therefore there is a race-condition between rmtree and makedirs. + Virus scanners and file system indexers might temporarily block a file from being deleted right away + + http://stackoverflow.com/questions/27625683/can-anyone-explain-this-weird-behaviour-of-shutil-rmtree-and-shutil-copytree + """ + if IS_WINDOWS_SYSTEM: + retries = 10 + while retries > 0 and exists(path): + shutil.rmtree(path, ignore_errors=retries > 1) + time.sleep(0.01) + retries -= 1 + else: + if exists(path): + shutil.rmtree(path) + os.makedirs(path) + + +def simplify_path(path): + """ + Return relative path towards current working directory + unless it is a separate Windows drive + """ + cwd = os.getcwd() + drive_cwd = splitdrive(cwd)[0] + drive_path = splitdrive(path)[0] + if drive_path == drive_cwd: + return relpath(path, cwd) + + return path diff --git a/vunit/parsing/__init__.py b/vunit/parsing/__init__.py index a66885c1d..17b27373b 100644 --- a/vunit/parsing/__init__.py +++ b/vunit/parsing/__init__.py @@ -1,5 +1,5 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com diff --git a/vunit/parsing/encodings.py b/vunit/parsing/encodings.py index 99e6a572d..172567016 100644 --- a/vunit/parsing/encodings.py +++ b/vunit/parsing/encodings.py @@ -1,13 +1,13 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Define common encodings -""" - - -# Both VHDL and Verilog standardize on ISO-8859-1 which is latin-1 -HDL_FILE_ENCODING = "latin-1" +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Define common encodings +""" + + +# Both VHDL and Verilog standardize on ISO-8859-1 which is latin-1 +HDL_FILE_ENCODING = "latin-1" diff --git a/vunit/parsing/tokenizer.py b/vunit/parsing/tokenizer.py index f1953ca18..af0d5d49e 100644 --- a/vunit/parsing/tokenizer.py +++ b/vunit/parsing/tokenizer.py @@ -1,273 +1,273 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -A general tokenizer -""" - -import collections -import re -from vunit.ostools import read_file, file_exists, simplify_path - -TokenType = collections.namedtuple("Token", ["kind", "value", "location"]) - - -def Token(kind, value="", location=None): # pylint: disable=invalid-name - return TokenType(kind, value, location) - - -def new_token_kind(name): - """ - Create a new token kind with nice __repr__ - """ - - def new_token(kind, value='', location=None): - """ - Create new token of kind - """ - return Token(kind, value, location) - - cls = type(name, - (object,), { - "__repr__": lambda self: name, - "__call__": new_token - }) - return cls() - - -class Tokenizer(object): - """ - Maintain a prioritized list of token regex - """ - - def __init__(self): - self._regexs = [] - self._assoc = {} - self._regex = None - - def add(self, kind, regex, func=None): - """ - Add token type - """ - key = chr(ord('a') + len(self._regexs)) - self._regexs.append((key, regex)) - self._assoc[key] = (kind, func) - return kind - - def finalize(self): - self._regex = re.compile("|".join("(?P<%s>%s)" % spec for spec in self._regexs), re.VERBOSE | re.MULTILINE) - - def tokenize(self, code, file_name=None, previous_location=None, create_locations=False): - """ - Tokenize the code - """ - tokens = [] - start = 0 - while True: - match = self._regex.search(code, pos=start) - if match is None: - break - lexpos = (start, match.end() - 1) - start = match.end() - kind, func = self._assoc[match.lastgroup] - value = match.group(match.lastgroup) - - if create_locations: - location = ((file_name, lexpos), previous_location) - else: - location = None - - token = Token(kind, value, location) - if func is not None: - token = func(token) - - if token is not None: - tokens.append(token) - return tokens - - -class TokenStream(object): - """ - Helper class for traversing a stream of tokens - """ - - def __init__(self, tokens): - self._tokens = tokens - self._idx = 0 - - def __len__(self): - return len(self._tokens) - - def __getitem__(self, index): - return self._tokens[index] - - @property - def eof(self): - return not self._idx < len(self._tokens) - - @property - def idx(self): - return self._idx - - @property - def current(self): - return self._tokens[self._idx] - - def peek(self, offset=0): - return self._tokens[self._idx + offset] - - def skip_while(self, *kinds): - """ - Skip forward while token kind is present - """ - while not self.eof: - if not any(self._tokens[self._idx].kind == kind for kind in kinds): - break - self._idx += 1 - return self._idx - - def skip_until(self, *kinds): - """ - Skip forward until token kind is present - """ - while not self.eof: - if any(self._tokens[self._idx].kind == kind for kind in kinds): - break - self._idx += 1 - return self._idx - - def pop(self): - """ - Return current token and advance stream - """ - if self.eof: - raise EOFException() - - self._idx += 1 - return self._tokens[self._idx - 1] - - def expect(self, *kinds): - """ - Expect to pop token with any of kinds - """ - token = self.pop() - if token.kind not in kinds: - if len(kinds) == 1: - expected = str(kinds[0]) - else: - expected = "any of [%s]" % ", ".join(str(kind) for kind in kinds) - raise LocationException.error("Expected %s got %s" % (expected, token.kind), - token.location) - return token - - def slice(self, start, end): - return self._tokens[start:end] - - -def describe_location(location, first=True): - """ - Describe the location as a string - """ - if location is None: - return "Unknown location" - - ((file_name, (start, end)), previous) = location - - retval = "" - if previous is not None: - retval += describe_location(previous, first=False) + "\n" - - if file_name is None: - retval += "Unknown Python string" - return retval - - if not file_exists(file_name): - retval += "Unknown location in %s" % file_name - return retval - - contents = read_file(file_name) - - if first: - prefix = "at" - else: - prefix = "from" - - count = 0 - for lineno, line in enumerate(contents.splitlines()): - lstart = count - lend = lstart + len(line) - if lstart <= start <= lend: - retval += "%s %s line %i:\n" % (prefix, simplify_path(file_name), lineno + 1) - retval += line + "\n" - retval += (" " * (start - lstart)) + ("~" * (min(lend - 1, end) - start + 1)) - return retval - - count = lend + 1 - return retval - - -class EOFException(Exception): - """ - End of file - """ - - -class LocationException(Exception): - """ - A an exception to be raised when there is a problem in a location - """ - @classmethod - def error(cls, message, location): - return cls(message, location, "error") - - @classmethod - def warning(cls, message, location): - return cls(message, location, "warning") - - @classmethod - def debug(cls, message, location): - return cls(message, location, "debug") - - def __init__(self, message, location, severity): - Exception.__init__(self) - assert severity in ("debug", "warning", "error") - self._severtity = severity - self._message = message - self._location = location - - def log(self, logger): - """ - Log the exception - """ - if self._severtity == "error": - method = logger.error - elif self._severtity == "warning": - method = logger.warning - else: - method = logger.debug - - method(self._message + "\n%s", - describe_location(self._location)) - - -def add_previous(location, previous): - """ - Add previous location - """ - if location is None: - return previous - - current, old_previous = location - return (current, add_previous(old_previous, previous)) - - -def strip_previous(location): - """ - Strip previous location - """ - if location is None: - return None - return location[0] +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +A general tokenizer +""" + +import collections +import re +from vunit.ostools import read_file, file_exists, simplify_path + +TokenType = collections.namedtuple("Token", ["kind", "value", "location"]) + + +def Token(kind, value="", location=None): # pylint: disable=invalid-name + return TokenType(kind, value, location) + + +def new_token_kind(name): + """ + Create a new token kind with nice __repr__ + """ + + def new_token(kind, value='', location=None): + """ + Create new token of kind + """ + return Token(kind, value, location) + + cls = type(name, + (object,), { + "__repr__": lambda self: name, + "__call__": new_token + }) + return cls() + + +class Tokenizer(object): + """ + Maintain a prioritized list of token regex + """ + + def __init__(self): + self._regexs = [] + self._assoc = {} + self._regex = None + + def add(self, kind, regex, func=None): + """ + Add token type + """ + key = chr(ord('a') + len(self._regexs)) + self._regexs.append((key, regex)) + self._assoc[key] = (kind, func) + return kind + + def finalize(self): + self._regex = re.compile("|".join("(?P<%s>%s)" % spec for spec in self._regexs), re.VERBOSE | re.MULTILINE) + + def tokenize(self, code, file_name=None, previous_location=None, create_locations=False): + """ + Tokenize the code + """ + tokens = [] + start = 0 + while True: + match = self._regex.search(code, pos=start) + if match is None: + break + lexpos = (start, match.end() - 1) + start = match.end() + kind, func = self._assoc[match.lastgroup] + value = match.group(match.lastgroup) + + if create_locations: + location = ((file_name, lexpos), previous_location) + else: + location = None + + token = Token(kind, value, location) + if func is not None: + token = func(token) + + if token is not None: + tokens.append(token) + return tokens + + +class TokenStream(object): + """ + Helper class for traversing a stream of tokens + """ + + def __init__(self, tokens): + self._tokens = tokens + self._idx = 0 + + def __len__(self): + return len(self._tokens) + + def __getitem__(self, index): + return self._tokens[index] + + @property + def eof(self): + return not self._idx < len(self._tokens) + + @property + def idx(self): + return self._idx + + @property + def current(self): + return self._tokens[self._idx] + + def peek(self, offset=0): + return self._tokens[self._idx + offset] + + def skip_while(self, *kinds): + """ + Skip forward while token kind is present + """ + while not self.eof: + if not any(self._tokens[self._idx].kind == kind for kind in kinds): + break + self._idx += 1 + return self._idx + + def skip_until(self, *kinds): + """ + Skip forward until token kind is present + """ + while not self.eof: + if any(self._tokens[self._idx].kind == kind for kind in kinds): + break + self._idx += 1 + return self._idx + + def pop(self): + """ + Return current token and advance stream + """ + if self.eof: + raise EOFException() + + self._idx += 1 + return self._tokens[self._idx - 1] + + def expect(self, *kinds): + """ + Expect to pop token with any of kinds + """ + token = self.pop() + if token.kind not in kinds: + if len(kinds) == 1: + expected = str(kinds[0]) + else: + expected = "any of [%s]" % ", ".join(str(kind) for kind in kinds) + raise LocationException.error("Expected %s got %s" % (expected, token.kind), + token.location) + return token + + def slice(self, start, end): + return self._tokens[start:end] + + +def describe_location(location, first=True): + """ + Describe the location as a string + """ + if location is None: + return "Unknown location" + + ((file_name, (start, end)), previous) = location + + retval = "" + if previous is not None: + retval += describe_location(previous, first=False) + "\n" + + if file_name is None: + retval += "Unknown Python string" + return retval + + if not file_exists(file_name): + retval += "Unknown location in %s" % file_name + return retval + + contents = read_file(file_name) + + if first: + prefix = "at" + else: + prefix = "from" + + count = 0 + for lineno, line in enumerate(contents.splitlines()): + lstart = count + lend = lstart + len(line) + if lstart <= start <= lend: + retval += "%s %s line %i:\n" % (prefix, simplify_path(file_name), lineno + 1) + retval += line + "\n" + retval += (" " * (start - lstart)) + ("~" * (min(lend - 1, end) - start + 1)) + return retval + + count = lend + 1 + return retval + + +class EOFException(Exception): + """ + End of file + """ + + +class LocationException(Exception): + """ + A an exception to be raised when there is a problem in a location + """ + @classmethod + def error(cls, message, location): + return cls(message, location, "error") + + @classmethod + def warning(cls, message, location): + return cls(message, location, "warning") + + @classmethod + def debug(cls, message, location): + return cls(message, location, "debug") + + def __init__(self, message, location, severity): + Exception.__init__(self) + assert severity in ("debug", "warning", "error") + self._severtity = severity + self._message = message + self._location = location + + def log(self, logger): + """ + Log the exception + """ + if self._severtity == "error": + method = logger.error + elif self._severtity == "warning": + method = logger.warning + else: + method = logger.debug + + method(self._message + "\n%s", + describe_location(self._location)) + + +def add_previous(location, previous): + """ + Add previous location + """ + if location is None: + return previous + + current, old_previous = location + return (current, add_previous(old_previous, previous)) + + +def strip_previous(location): + """ + Strip previous location + """ + if location is None: + return None + return location[0] diff --git a/vunit/parsing/verilog/__init__.py b/vunit/parsing/verilog/__init__.py index a66885c1d..17b27373b 100644 --- a/vunit/parsing/verilog/__init__.py +++ b/vunit/parsing/verilog/__init__.py @@ -1,5 +1,5 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com diff --git a/vunit/parsing/verilog/parser.py b/vunit/parsing/verilog/parser.py index 94b336a37..2198c66f2 100644 --- a/vunit/parsing/verilog/parser.py +++ b/vunit/parsing/verilog/parser.py @@ -1,339 +1,339 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -# pylint: disable=unused-wildcard-import -# pylint: disable=wildcard-import - -""" -Verilog parsing functionality -""" - -import logging -from os.path import dirname, exists, abspath -from vunit.ostools import read_file -from vunit.parsing.encodings import HDL_FILE_ENCODING -from vunit.parsing.tokenizer import TokenStream, EOFException, LocationException -from vunit.parsing.verilog.tokenizer import VerilogTokenizer -from vunit.parsing.verilog.preprocess import VerilogPreprocessor, find_included_file, Macro -from vunit.parsing.verilog.tokens import * -from vunit.cached import file_content_hash - -LOGGER = logging.getLogger(__name__) - - -class VerilogParser(object): - """ - Parse a single Verilog file - """ - - def __init__(self, database=None): - self._tokenizer = VerilogTokenizer() - self._preprocessor = VerilogPreprocessor(self._tokenizer) - self._database = database - self._content_cache = {} - - def parse(self, file_name, include_paths=None, defines=None): - """ - Parse verilog code - """ - - defines = {} if defines is None else defines - include_paths = [] if include_paths is None else include_paths - include_paths = [dirname(file_name)] + include_paths - - cached = self._lookup_parse_cache(file_name, include_paths, defines) - if cached is not None: - return cached - - initial_defines = dict((key, Macro(key, self._tokenizer.tokenize(value))) - for key, value in defines.items()) - code = read_file(file_name, encoding=HDL_FILE_ENCODING) - tokens = self._tokenizer.tokenize(code, file_name=file_name) - included_files = [] - pp_tokens = self._preprocessor.preprocess(tokens, - include_paths=include_paths, - defines=initial_defines, - included_files=included_files) - - included_files_for_design_file = [name for _, name in included_files if name is not None] - result = VerilogDesignFile.parse(pp_tokens, included_files_for_design_file) - - if self._database is None: - return result - - self._store_result(file_name, result, included_files, defines) - return result - - @staticmethod - def _key(file_name): - """ - Returns the database key for parse results of file_name - """ - return ("CachedVerilogParser.parse(%s)" % abspath(file_name)).encode() - - def _store_result(self, file_name, result, included_files, defines): - """ - Store parse result into back into cache - """ - new_included_files = [(short_name, full_name, self._content_hash(full_name)) - for short_name, full_name in included_files] - key = self._key(file_name) - self._database[key] = self._content_hash(file_name), new_included_files, defines, result - return result - - def _content_hash(self, file_name): - """ - Hash the contents of the file - """ - if file_name is None or not exists(file_name): - return None - if file_name not in self._content_cache: - self._content_cache[file_name] = file_content_hash(file_name, - encoding=HDL_FILE_ENCODING, - database=self._database) - return self._content_cache[file_name] - - def _lookup_parse_cache(self, file_name, include_paths, defines): - """ - Use verilog code from cache - """ - # pylint: disable=too-many-return-statements - - if self._database is None: - return None - - key = self._key(file_name) - if key not in self._database: - return None - - old_content_hash, old_included_files, old_defines, old_result = self._database[key] - if old_defines != defines: - return None - - if old_content_hash != self._content_hash(file_name): - return None - - for include_str, included_file_name, last_content_hash in old_included_files: - if last_content_hash != self._content_hash(included_file_name): - return None - - if find_included_file(include_paths, include_str) != included_file_name: - return None - - LOGGER.debug("Re-using cached Verilog parse results for %s", file_name) - - return old_result - - -class VerilogDesignFile(object): - """ - Contains Verilog objecs found within a file - """ - def __init__(self, # pylint: disable=too-many-arguments - modules=None, - packages=None, - imports=None, - package_references=None, - instances=None, - included_files=None): - self.modules = [] if modules is None else modules - self.packages = [] if packages is None else packages - self.imports = [] if imports is None else imports - self.package_references = [] if package_references is None else package_references - self.instances = [] if instances is None else instances - self.included_files = [] if included_files is None else included_files - - @classmethod - def parse(cls, tokens, included_files): - """ - Parse verilog file - """ - tokens = [token - for token in tokens - if token.kind not in (WHITESPACE, - COMMENT, - NEWLINE, - MULTI_COMMENT)] - return cls(modules=VerilogModule.find(tokens), - packages=VerilogPackage.find(tokens), - imports=cls.find_imports(tokens), - package_references=cls.find_package_references(tokens), - instances=cls.find_instances(tokens), - included_files=included_files) - - @staticmethod - def find_imports(tokens): - """ - Find imports - """ - results = [] - stream = TokenStream(tokens) - while not stream.eof: - token = stream.pop() - - if token.kind != IMPORT: - continue - import_token = token - try: - token = stream.pop() - if token.kind == IDENTIFIER: - results.append(token.value) - else: - LocationException.warning("import bad argument", - token.location).log(LOGGER) - except EOFException: - LocationException.warning("EOF reached when parsing import", - location=import_token.location).log(LOGGER) - return results - - @staticmethod - def find_package_references(tokens): - """ - Find package_references pkg::func - """ - results = [] - stream = TokenStream(tokens) - while not stream.eof: - token = stream.pop() - if token.kind == IMPORT: - stream.skip_until(SEMI_COLON) - if not stream.eof: - stream.pop() - - elif token.kind == IDENTIFIER and not stream.eof: - kind = stream.pop().kind - if kind == DOUBLE_COLON: - results.append(token.value) - stream.skip_while(IDENTIFIER, DOUBLE_COLON) - return results - - @staticmethod - def find_instances(tokens): - """ - Find module instances - """ - results = [] - stream = TokenStream(tokens) - while not stream.eof: - token = stream.pop() - - if token.kind in (BEGIN, END): - _parse_block_label(stream) - continue - - if not token.kind == IDENTIFIER: - continue - modulename = token.value - - try: - token = stream.pop() - except EOFException: - continue - - if token.kind == HASH: - results.append(modulename) - continue - elif token.kind == IDENTIFIER: - results.append(modulename) - continue - - return results - - -def _parse_block_label(stream): - """ - Parse a optional block label after begin|end keyword - """ - try: - token = stream.peek() - - if token.kind != COLON: - # Is not block label - return - - stream.pop() - stream.expect(IDENTIFIER) - - except EOFException: - return - - -class VerilogModule(object): - """ - A verilog module - """ - - def __init__(self, name, parameters): - self.name = name - self.parameters = parameters - - @classmethod - def parse_parameter(cls, idx, tokens): - """ - Parse parameter at point - """ - if not tokens[idx].kind == PARAMETER: - return None - - if tokens[idx + 2].kind == IDENTIFIER: - return tokens[idx + 2].value - - return tokens[idx + 1].value - - @classmethod - def find(cls, tokens): - """ - Find all modules within code, nested modules are ignored - """ - idx = 0 - name = None - balance = 0 - results = [] - parameters = [] - while idx < len(tokens): - - if tokens[idx].kind == MODULE: - if balance == 0: - name = tokens[idx + 1].value - parameters = [] - balance += 1 - - elif tokens[idx].kind == ENDMODULE: - balance -= 1 - if balance == 0: - results.append(cls(name, parameters)) - - elif balance == 1: - parameter = cls.parse_parameter(idx, tokens) - if parameter is not None: - parameters.append(parameter) - - idx += 1 - return results - - -class VerilogPackage(object): - """ - A verilog package - """ - - def __init__(self, name): - self.name = name - - @classmethod - def find(cls, tokens): - """ - Find all modules within code, nested modules are ignored - """ - idx = 0 - results = [] - while idx < len(tokens): - if tokens[idx].kind == PACKAGE: - idx += 1 - name = tokens[idx].value - results.append(cls(name)) - idx += 1 - return results +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +# pylint: disable=unused-wildcard-import +# pylint: disable=wildcard-import + +""" +Verilog parsing functionality +""" + +import logging +from os.path import dirname, exists, abspath +from vunit.ostools import read_file +from vunit.parsing.encodings import HDL_FILE_ENCODING +from vunit.parsing.tokenizer import TokenStream, EOFException, LocationException +from vunit.parsing.verilog.tokenizer import VerilogTokenizer +from vunit.parsing.verilog.preprocess import VerilogPreprocessor, find_included_file, Macro +from vunit.parsing.verilog.tokens import * +from vunit.cached import file_content_hash + +LOGGER = logging.getLogger(__name__) + + +class VerilogParser(object): + """ + Parse a single Verilog file + """ + + def __init__(self, database=None): + self._tokenizer = VerilogTokenizer() + self._preprocessor = VerilogPreprocessor(self._tokenizer) + self._database = database + self._content_cache = {} + + def parse(self, file_name, include_paths=None, defines=None): + """ + Parse verilog code + """ + + defines = {} if defines is None else defines + include_paths = [] if include_paths is None else include_paths + include_paths = [dirname(file_name)] + include_paths + + cached = self._lookup_parse_cache(file_name, include_paths, defines) + if cached is not None: + return cached + + initial_defines = dict((key, Macro(key, self._tokenizer.tokenize(value))) + for key, value in defines.items()) + code = read_file(file_name, encoding=HDL_FILE_ENCODING) + tokens = self._tokenizer.tokenize(code, file_name=file_name) + included_files = [] + pp_tokens = self._preprocessor.preprocess(tokens, + include_paths=include_paths, + defines=initial_defines, + included_files=included_files) + + included_files_for_design_file = [name for _, name in included_files if name is not None] + result = VerilogDesignFile.parse(pp_tokens, included_files_for_design_file) + + if self._database is None: + return result + + self._store_result(file_name, result, included_files, defines) + return result + + @staticmethod + def _key(file_name): + """ + Returns the database key for parse results of file_name + """ + return ("CachedVerilogParser.parse(%s)" % abspath(file_name)).encode() + + def _store_result(self, file_name, result, included_files, defines): + """ + Store parse result into back into cache + """ + new_included_files = [(short_name, full_name, self._content_hash(full_name)) + for short_name, full_name in included_files] + key = self._key(file_name) + self._database[key] = self._content_hash(file_name), new_included_files, defines, result + return result + + def _content_hash(self, file_name): + """ + Hash the contents of the file + """ + if file_name is None or not exists(file_name): + return None + if file_name not in self._content_cache: + self._content_cache[file_name] = file_content_hash(file_name, + encoding=HDL_FILE_ENCODING, + database=self._database) + return self._content_cache[file_name] + + def _lookup_parse_cache(self, file_name, include_paths, defines): + """ + Use verilog code from cache + """ + # pylint: disable=too-many-return-statements + + if self._database is None: + return None + + key = self._key(file_name) + if key not in self._database: + return None + + old_content_hash, old_included_files, old_defines, old_result = self._database[key] + if old_defines != defines: + return None + + if old_content_hash != self._content_hash(file_name): + return None + + for include_str, included_file_name, last_content_hash in old_included_files: + if last_content_hash != self._content_hash(included_file_name): + return None + + if find_included_file(include_paths, include_str) != included_file_name: + return None + + LOGGER.debug("Re-using cached Verilog parse results for %s", file_name) + + return old_result + + +class VerilogDesignFile(object): + """ + Contains Verilog objecs found within a file + """ + def __init__(self, # pylint: disable=too-many-arguments + modules=None, + packages=None, + imports=None, + package_references=None, + instances=None, + included_files=None): + self.modules = [] if modules is None else modules + self.packages = [] if packages is None else packages + self.imports = [] if imports is None else imports + self.package_references = [] if package_references is None else package_references + self.instances = [] if instances is None else instances + self.included_files = [] if included_files is None else included_files + + @classmethod + def parse(cls, tokens, included_files): + """ + Parse verilog file + """ + tokens = [token + for token in tokens + if token.kind not in (WHITESPACE, + COMMENT, + NEWLINE, + MULTI_COMMENT)] + return cls(modules=VerilogModule.find(tokens), + packages=VerilogPackage.find(tokens), + imports=cls.find_imports(tokens), + package_references=cls.find_package_references(tokens), + instances=cls.find_instances(tokens), + included_files=included_files) + + @staticmethod + def find_imports(tokens): + """ + Find imports + """ + results = [] + stream = TokenStream(tokens) + while not stream.eof: + token = stream.pop() + + if token.kind != IMPORT: + continue + import_token = token + try: + token = stream.pop() + if token.kind == IDENTIFIER: + results.append(token.value) + else: + LocationException.warning("import bad argument", + token.location).log(LOGGER) + except EOFException: + LocationException.warning("EOF reached when parsing import", + location=import_token.location).log(LOGGER) + return results + + @staticmethod + def find_package_references(tokens): + """ + Find package_references pkg::func + """ + results = [] + stream = TokenStream(tokens) + while not stream.eof: + token = stream.pop() + if token.kind == IMPORT: + stream.skip_until(SEMI_COLON) + if not stream.eof: + stream.pop() + + elif token.kind == IDENTIFIER and not stream.eof: + kind = stream.pop().kind + if kind == DOUBLE_COLON: + results.append(token.value) + stream.skip_while(IDENTIFIER, DOUBLE_COLON) + return results + + @staticmethod + def find_instances(tokens): + """ + Find module instances + """ + results = [] + stream = TokenStream(tokens) + while not stream.eof: + token = stream.pop() + + if token.kind in (BEGIN, END): + _parse_block_label(stream) + continue + + if not token.kind == IDENTIFIER: + continue + modulename = token.value + + try: + token = stream.pop() + except EOFException: + continue + + if token.kind == HASH: + results.append(modulename) + continue + elif token.kind == IDENTIFIER: + results.append(modulename) + continue + + return results + + +def _parse_block_label(stream): + """ + Parse a optional block label after begin|end keyword + """ + try: + token = stream.peek() + + if token.kind != COLON: + # Is not block label + return + + stream.pop() + stream.expect(IDENTIFIER) + + except EOFException: + return + + +class VerilogModule(object): + """ + A verilog module + """ + + def __init__(self, name, parameters): + self.name = name + self.parameters = parameters + + @classmethod + def parse_parameter(cls, idx, tokens): + """ + Parse parameter at point + """ + if not tokens[idx].kind == PARAMETER: + return None + + if tokens[idx + 2].kind == IDENTIFIER: + return tokens[idx + 2].value + + return tokens[idx + 1].value + + @classmethod + def find(cls, tokens): + """ + Find all modules within code, nested modules are ignored + """ + idx = 0 + name = None + balance = 0 + results = [] + parameters = [] + while idx < len(tokens): + + if tokens[idx].kind == MODULE: + if balance == 0: + name = tokens[idx + 1].value + parameters = [] + balance += 1 + + elif tokens[idx].kind == ENDMODULE: + balance -= 1 + if balance == 0: + results.append(cls(name, parameters)) + + elif balance == 1: + parameter = cls.parse_parameter(idx, tokens) + if parameter is not None: + parameters.append(parameter) + + idx += 1 + return results + + +class VerilogPackage(object): + """ + A verilog package + """ + + def __init__(self, name): + self.name = name + + @classmethod + def find(cls, tokens): + """ + Find all modules within code, nested modules are ignored + """ + idx = 0 + results = [] + while idx < len(tokens): + if tokens[idx].kind == PACKAGE: + idx += 1 + name = tokens[idx].value + results.append(cls(name)) + idx += 1 + return results diff --git a/vunit/parsing/verilog/preprocess.py b/vunit/parsing/verilog/preprocess.py index efa4de816..9c4c74aeb 100644 --- a/vunit/parsing/verilog/preprocess.py +++ b/vunit/parsing/verilog/preprocess.py @@ -1,523 +1,523 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -# pylint: disable=unused-wildcard-import -# pylint: disable=wildcard-import - -""" -Verilog parsing functionality -""" -from os.path import join, exists, abspath -import logging -from vunit.parsing.tokenizer import (TokenStream, - Token, - add_previous, - strip_previous, - EOFException, - LocationException) -from vunit.parsing.verilog.tokens import * -from vunit.ostools import read_file -LOGGER = logging.getLogger(__name__) - - -class VerilogPreprocessor(object): - """ - A Verilog preprocessor - """ - - def __init__(self, tokenizer): - self._tokenizer = tokenizer - self._macro_trace = set() - self._include_trace = set() - - def preprocess(self, tokens, defines=None, include_paths=None, included_files=None): - """ - Entry point of preprocessing - """ - self._include_trace = set() - self._macro_trace = set() - return self._preprocess(tokens, defines, include_paths, included_files) - - def _preprocess(self, tokens, defines=None, include_paths=None, included_files=None): - """ - Pre-process tokens while filling in defines - """ - stream = TokenStream(tokens) - include_paths = [] if include_paths is None else include_paths - included_files = [] if included_files is None else included_files - defines = {} if defines is None else defines - result = [] - - while not stream.eof: - token = stream.pop() - if not token.kind == PREPROCESSOR: - result.append(token) - continue - - try: - result += self.preprocessor(token, stream, defines, include_paths, included_files) - except LocationException as exc: - exc.log(LOGGER) - - return result - - def preprocessor(self, # pylint: disable=too-many-arguments,too-many-branches - token, stream, defines, include_paths, included_files): - """ - Handle preprocessor token - """ - if token.value == "define": - macro = define(token, stream) - if macro is not None: - defines[macro.name] = macro - - elif token.value == "undef": - undef(token, stream, defines) - - elif token.value in ("undefineall", "resetall"): - defines.clear() - - elif token.value == "include": - return self.include(token, stream, include_paths, included_files, defines) - - elif token.value in ("ifdef", "ifndef"): - try: - tokens = self.if_statement(token, stream, defines) - return self._preprocess(tokens, - defines=defines, - include_paths=include_paths, - included_files=included_files) - except EOFException: - raise LocationException.warning( - "EOF reached when parsing `%s" % token.value, - token.location) - - elif token.value in ("celldefine", "endcelldefine", "nounconnected_drive"): - # Ignored - pass - - elif token.value in ("timescale", "default_nettype", "unconnected_drive"): - # Ignore directive and arguments - stream.skip_until(NEWLINE) - - elif token.value == "pragma": - stream.skip_while(WHITESPACE) - pp_token = stream.pop() - - if pp_token.value == "protect": - stream.skip_while(WHITESPACE) - token = stream.pop() - - if token.value == "begin_protected": - self._skip_protected_region(stream) - - elif token.value in defines: - return self.expand_macro(token, stream, defines, include_paths, included_files) - else: - raise LocationException.debug( - "Verilog undefined name", - token.location) - - return [] - - @staticmethod - def _skip_protected_region(stream): - """ - Skip a protected region -`pragma protect begin_protected -Skipped -`pragma protect end_protected - """ - while not stream.eof: - stream.skip_while(WHITESPACE) - token = stream.pop() - - if token.kind == PREPROCESSOR and token.value == "pragma": - stream.skip_while(WHITESPACE) - token = stream.pop() - - if token.value == "protect": - stream.skip_while(WHITESPACE) - token = stream.pop() - - if token.value == "end_protected": - return - - def expand_macro(self, # pylint: disable=too-many-arguments - macro_token, stream, defines, include_paths, included_files): - """ - Expand a macro - """ - macro = defines[macro_token.value] - macro_point = (strip_previous(macro_token.location), hash(frozenset(defines.keys()))) - if macro_point in self._macro_trace: - raise LocationException.error( - "Circular macro expansion of %s detected" % macro_token.value, - macro_token.location) - self._macro_trace.add(macro_point) - tokens = self._preprocess(macro.expand_from_stream(macro_token, - stream, - previous=macro_token.location), - defines=defines, - include_paths=include_paths, - included_files=included_files) - self._macro_trace.remove(macro_point) - return tokens - - @staticmethod - def if_statement(if_token, stream, defines): - """ - Handle if statement - """ - def check_arg(if_token, arg): - """ - Check the define argument of an if statement - """ - if arg.kind != IDENTIFIER: - raise LocationException.warning( - "Bad argument to `%s" % if_token.value, - arg.location) - stream.skip_while(NEWLINE) - - def determine_if_taken(if_token, arg): - """ - Determine if the branch was taken - """ - if if_token.value in ("ifdef", "elsif"): - return arg.value in defines - - if if_token.value == "ifndef": - return arg.value not in defines - - raise ValueError("Invalid if token %r" % if_token.value) - - result = [] - stream.skip_while(WHITESPACE) - arg = stream.pop() - check_arg(if_token, arg) - - taken = determine_if_taken(if_token, arg) - any_taken = taken - count = 1 - while True: - token = stream.pop() - if token.kind == PREPROCESSOR: - if token.value in ("ifdef", "ifndef"): - count += 1 - elif token.value == "endif": - count -= 1 - if count == 0: - break - - if count == 1 and (token.kind, token.value) == (PREPROCESSOR, "else"): - stream.skip_while(NEWLINE) - if not any_taken: - taken = True - any_taken = True - else: - taken = False - elif count == 1 and (token.kind, token.value) == (PREPROCESSOR, "elsif"): - stream.skip_while(WHITESPACE) - arg = stream.pop() - check_arg(token, arg) - stream.skip_while(NEWLINE) - if not any_taken: - taken = determine_if_taken(token, arg) - any_taken = taken - else: - taken = False - elif taken: - result.append(token) - stream.skip_while(NEWLINE) - return result - - def include(self, # pylint: disable=too-many-arguments - token, stream, include_paths, included_files, defines): - """ - Handle `include directive - """ - stream.skip_while(WHITESPACE) - try: - tok = stream.pop() - except EOFException: - raise LocationException.warning( - "EOF reached when parsing `include argument", - token.location) - - if tok.kind == PREPROCESSOR: - if tok.value in defines: - macro = defines[tok.value] - else: - raise LocationException.warning( - "Verilog `include argument not defined", - tok.location) - - expanded_tokens = self.expand_macro(tok, - stream, - defines, - include_paths, - included_files) - - # pylint crashes when trying to fix the warning below - if len(expanded_tokens) == 0: # pylint: disable=len-as-condition - raise LocationException.warning("Verilog `include has bad argument, empty define `%s" % macro.name, - tok.location) - - if expanded_tokens[0].kind != STRING: - raise LocationException.warning("Verilog `include has bad argument", - expanded_tokens[0].location) - - file_name_tok = expanded_tokens[0] - - elif tok.kind == STRING: - file_name_tok = tok - else: - raise LocationException.warning("Verilog `include bad argument", - tok.location) - - included_file = find_included_file(include_paths, file_name_tok.value) - included_files.append((file_name_tok.value, included_file)) - if included_file is None: - # Is debug message since there are so many builtin includes in tools - raise LocationException.debug( - "Could not find `include file %s" % file_name_tok.value, - file_name_tok.location) - - include_point = (strip_previous(token.location), hash(frozenset(defines.keys()))) - if include_point in self._include_trace: - raise LocationException.error( - "Circular `include of %s detected" % file_name_tok.value, - file_name_tok.location) - self._include_trace.add(include_point) - - included_tokens = self._tokenizer.tokenize(read_file(included_file), - file_name=included_file, - previous_location=token.location) - included_tokens = self._preprocess(included_tokens, - defines, - include_paths, - included_files) - self._include_trace.remove(include_point) - return included_tokens - - -def find_included_file(include_paths, file_name): - """ - Find the file to include given include_paths - """ - for include_path in include_paths: - full_name = abspath(join(include_path, file_name)) - if exists(full_name): - return full_name - return None - - -def undef(undef_token, stream, defines): - """ - Handles undef directive - """ - stream.skip_while(WHITESPACE, NEWLINE) - try: - name_token = stream.pop() - except EOFException: - raise LocationException.warning("EOF reached when parsing `undef", - undef_token.location) - - if name_token.kind != IDENTIFIER: - raise LocationException.warning("Bad argument to `undef", - name_token.location) - - if name_token.value not in defines: - raise LocationException.warning("`undef argument was not previously defined", - name_token.location) - - del defines[name_token.value] - - -def define(define_token, stream): - """ - Handle a `define directive - """ - stream.skip_while(WHITESPACE, NEWLINE) - try: - name_token = stream.pop() - except EOFException: - raise LocationException.warning("Verilog `define without argument", - define_token.location) - - if name_token.kind != IDENTIFIER: - raise LocationException.warning("Verilog `define invalid name", - name_token.location) - - name = name_token.value - - try: - token = stream.pop() - except EOFException: - # Empty define - return Macro(name) - - if token.kind in (NEWLINE,): - # Empty define - return Macro(name) - - if token.kind in (WHITESPACE,): - # Define without arguments - args = tuple() - defaults = {} - elif token.kind == LPAR: - lpar_token = token - args = tuple() - defaults = {} - - try: - while token.kind != RPAR: - if token.kind == IDENTIFIER: - argname = token.value - args = args + (argname,) - token = stream.pop() - if token.kind == EQUAL: - token = stream.pop() - defaults[argname] = [token] - token = stream.pop() - else: - token = stream.pop() - except EOFException: - raise LocationException.warning( - "EOF reached when parsing `define argument list", - lpar_token.location) - - stream.skip_while(WHITESPACE) - start = stream.idx - end = stream.skip_until(NEWLINE) - if not stream.eof: - stream.pop() - return Macro(name, - tokens=stream.slice(start, end), - args=args, - defaults=defaults) - - -class Macro(object): - """ - A `define macro with zero or more arguments - """ - - def __init__(self, name, tokens=None, args=tuple(), defaults=None): - self.name = name - self.tokens = [] if tokens is None else tokens - self.args = args - self.defaults = {} if defaults is None else defaults - - @property - def num_args(self): - return len(self.args) - - def __repr__(self): - return "Macro(%r, %r %r, %r)" % (self.name, self.tokens, self.args, self.defaults) - - def expand(self, values, previous): - """ - Expand macro with actual values, returns a list of expanded tokens - """ - tokens = [] - for token in self.tokens: - if token.kind == IDENTIFIER and token.value in self.args: - idx = self.args.index(token.value) - value = values[idx] - tokens += value - else: - tokens.append(token) - return [Token(tok.kind, tok.value, add_previous(tok.location, previous)) - for tok in tokens] - - def __eq__(self, other): - return ((self.name == other.name) - and (self.tokens == other.tokens) - and (self.args == other.args) - and (self.defaults == other.defaults)) - - def expand_from_stream(self, token, stream, previous=None): - """ - Expand macro consuming arguments from the stream - returns the expanded tokens - """ - if self.num_args == 0: - values = [] - else: - try: - values = self._parse_macro_actuals(token, stream) - except EOFException: - raise LocationException.warning("EOF reached when parsing `define actuals", - location=token.location) - - # Bind defaults - if len(values) < len(self.args): - for i in range(len(values), len(self.args)): - name = self.args[i] - if name in self.defaults: - values.append(self.defaults[name]) - else: - raise LocationException.warning( - "Missing value for argument %s" % name, - token.location) - - elif len(values) > len(self.args): - raise LocationException.warning("Too many arguments got %i expected %i" % - (len(values), len(self.args)), - token.location) - - return self.expand(values, previous) - - @staticmethod - def _parse_macro_actuals(define_token, stream): - """ - Parse the actual values of macro call such as - 1 2 in `macro(1, 2) - """ - - stream.skip_while(WHITESPACE) - - token = stream.pop() - if token.kind != LPAR: - raise LocationException.warning("Bad `define argument list", - define_token.location) - token = stream.pop() - value = [] - values = [] - - bracket_count = 0 - brace_count = 0 - par_count = 0 - - while not (token.kind == RPAR and par_count == 0): - if token.kind is LBRACKET: - bracket_count += 1 - elif token.kind is RBRACKET: - bracket_count += -1 - elif token.kind is LBRACE: - brace_count += 1 - elif token.kind is RBRACE: - brace_count += -1 - elif token.kind is LPAR: - par_count += 1 - elif token.kind is RPAR: - par_count += -1 - - value_ok = (token.kind == COMMA - and bracket_count == 0 - and brace_count == 0 - and par_count == 0) - - if value_ok: - values.append(value) - value = [] - else: - value.append(token) - token = stream.pop() - - values.append(value) - return values +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +# pylint: disable=unused-wildcard-import +# pylint: disable=wildcard-import + +""" +Verilog parsing functionality +""" +from os.path import join, exists, abspath +import logging +from vunit.parsing.tokenizer import (TokenStream, + Token, + add_previous, + strip_previous, + EOFException, + LocationException) +from vunit.parsing.verilog.tokens import * +from vunit.ostools import read_file +LOGGER = logging.getLogger(__name__) + + +class VerilogPreprocessor(object): + """ + A Verilog preprocessor + """ + + def __init__(self, tokenizer): + self._tokenizer = tokenizer + self._macro_trace = set() + self._include_trace = set() + + def preprocess(self, tokens, defines=None, include_paths=None, included_files=None): + """ + Entry point of preprocessing + """ + self._include_trace = set() + self._macro_trace = set() + return self._preprocess(tokens, defines, include_paths, included_files) + + def _preprocess(self, tokens, defines=None, include_paths=None, included_files=None): + """ + Pre-process tokens while filling in defines + """ + stream = TokenStream(tokens) + include_paths = [] if include_paths is None else include_paths + included_files = [] if included_files is None else included_files + defines = {} if defines is None else defines + result = [] + + while not stream.eof: + token = stream.pop() + if not token.kind == PREPROCESSOR: + result.append(token) + continue + + try: + result += self.preprocessor(token, stream, defines, include_paths, included_files) + except LocationException as exc: + exc.log(LOGGER) + + return result + + def preprocessor(self, # pylint: disable=too-many-arguments,too-many-branches + token, stream, defines, include_paths, included_files): + """ + Handle preprocessor token + """ + if token.value == "define": + macro = define(token, stream) + if macro is not None: + defines[macro.name] = macro + + elif token.value == "undef": + undef(token, stream, defines) + + elif token.value in ("undefineall", "resetall"): + defines.clear() + + elif token.value == "include": + return self.include(token, stream, include_paths, included_files, defines) + + elif token.value in ("ifdef", "ifndef"): + try: + tokens = self.if_statement(token, stream, defines) + return self._preprocess(tokens, + defines=defines, + include_paths=include_paths, + included_files=included_files) + except EOFException: + raise LocationException.warning( + "EOF reached when parsing `%s" % token.value, + token.location) + + elif token.value in ("celldefine", "endcelldefine", "nounconnected_drive"): + # Ignored + pass + + elif token.value in ("timescale", "default_nettype", "unconnected_drive"): + # Ignore directive and arguments + stream.skip_until(NEWLINE) + + elif token.value == "pragma": + stream.skip_while(WHITESPACE) + pp_token = stream.pop() + + if pp_token.value == "protect": + stream.skip_while(WHITESPACE) + token = stream.pop() + + if token.value == "begin_protected": + self._skip_protected_region(stream) + + elif token.value in defines: + return self.expand_macro(token, stream, defines, include_paths, included_files) + else: + raise LocationException.debug( + "Verilog undefined name", + token.location) + + return [] + + @staticmethod + def _skip_protected_region(stream): + """ + Skip a protected region +`pragma protect begin_protected +Skipped +`pragma protect end_protected + """ + while not stream.eof: + stream.skip_while(WHITESPACE) + token = stream.pop() + + if token.kind == PREPROCESSOR and token.value == "pragma": + stream.skip_while(WHITESPACE) + token = stream.pop() + + if token.value == "protect": + stream.skip_while(WHITESPACE) + token = stream.pop() + + if token.value == "end_protected": + return + + def expand_macro(self, # pylint: disable=too-many-arguments + macro_token, stream, defines, include_paths, included_files): + """ + Expand a macro + """ + macro = defines[macro_token.value] + macro_point = (strip_previous(macro_token.location), hash(frozenset(defines.keys()))) + if macro_point in self._macro_trace: + raise LocationException.error( + "Circular macro expansion of %s detected" % macro_token.value, + macro_token.location) + self._macro_trace.add(macro_point) + tokens = self._preprocess(macro.expand_from_stream(macro_token, + stream, + previous=macro_token.location), + defines=defines, + include_paths=include_paths, + included_files=included_files) + self._macro_trace.remove(macro_point) + return tokens + + @staticmethod + def if_statement(if_token, stream, defines): + """ + Handle if statement + """ + def check_arg(if_token, arg): + """ + Check the define argument of an if statement + """ + if arg.kind != IDENTIFIER: + raise LocationException.warning( + "Bad argument to `%s" % if_token.value, + arg.location) + stream.skip_while(NEWLINE) + + def determine_if_taken(if_token, arg): + """ + Determine if the branch was taken + """ + if if_token.value in ("ifdef", "elsif"): + return arg.value in defines + + if if_token.value == "ifndef": + return arg.value not in defines + + raise ValueError("Invalid if token %r" % if_token.value) + + result = [] + stream.skip_while(WHITESPACE) + arg = stream.pop() + check_arg(if_token, arg) + + taken = determine_if_taken(if_token, arg) + any_taken = taken + count = 1 + while True: + token = stream.pop() + if token.kind == PREPROCESSOR: + if token.value in ("ifdef", "ifndef"): + count += 1 + elif token.value == "endif": + count -= 1 + if count == 0: + break + + if count == 1 and (token.kind, token.value) == (PREPROCESSOR, "else"): + stream.skip_while(NEWLINE) + if not any_taken: + taken = True + any_taken = True + else: + taken = False + elif count == 1 and (token.kind, token.value) == (PREPROCESSOR, "elsif"): + stream.skip_while(WHITESPACE) + arg = stream.pop() + check_arg(token, arg) + stream.skip_while(NEWLINE) + if not any_taken: + taken = determine_if_taken(token, arg) + any_taken = taken + else: + taken = False + elif taken: + result.append(token) + stream.skip_while(NEWLINE) + return result + + def include(self, # pylint: disable=too-many-arguments + token, stream, include_paths, included_files, defines): + """ + Handle `include directive + """ + stream.skip_while(WHITESPACE) + try: + tok = stream.pop() + except EOFException: + raise LocationException.warning( + "EOF reached when parsing `include argument", + token.location) + + if tok.kind == PREPROCESSOR: + if tok.value in defines: + macro = defines[tok.value] + else: + raise LocationException.warning( + "Verilog `include argument not defined", + tok.location) + + expanded_tokens = self.expand_macro(tok, + stream, + defines, + include_paths, + included_files) + + # pylint crashes when trying to fix the warning below + if len(expanded_tokens) == 0: # pylint: disable=len-as-condition + raise LocationException.warning("Verilog `include has bad argument, empty define `%s" % macro.name, + tok.location) + + if expanded_tokens[0].kind != STRING: + raise LocationException.warning("Verilog `include has bad argument", + expanded_tokens[0].location) + + file_name_tok = expanded_tokens[0] + + elif tok.kind == STRING: + file_name_tok = tok + else: + raise LocationException.warning("Verilog `include bad argument", + tok.location) + + included_file = find_included_file(include_paths, file_name_tok.value) + included_files.append((file_name_tok.value, included_file)) + if included_file is None: + # Is debug message since there are so many builtin includes in tools + raise LocationException.debug( + "Could not find `include file %s" % file_name_tok.value, + file_name_tok.location) + + include_point = (strip_previous(token.location), hash(frozenset(defines.keys()))) + if include_point in self._include_trace: + raise LocationException.error( + "Circular `include of %s detected" % file_name_tok.value, + file_name_tok.location) + self._include_trace.add(include_point) + + included_tokens = self._tokenizer.tokenize(read_file(included_file), + file_name=included_file, + previous_location=token.location) + included_tokens = self._preprocess(included_tokens, + defines, + include_paths, + included_files) + self._include_trace.remove(include_point) + return included_tokens + + +def find_included_file(include_paths, file_name): + """ + Find the file to include given include_paths + """ + for include_path in include_paths: + full_name = abspath(join(include_path, file_name)) + if exists(full_name): + return full_name + return None + + +def undef(undef_token, stream, defines): + """ + Handles undef directive + """ + stream.skip_while(WHITESPACE, NEWLINE) + try: + name_token = stream.pop() + except EOFException: + raise LocationException.warning("EOF reached when parsing `undef", + undef_token.location) + + if name_token.kind != IDENTIFIER: + raise LocationException.warning("Bad argument to `undef", + name_token.location) + + if name_token.value not in defines: + raise LocationException.warning("`undef argument was not previously defined", + name_token.location) + + del defines[name_token.value] + + +def define(define_token, stream): + """ + Handle a `define directive + """ + stream.skip_while(WHITESPACE, NEWLINE) + try: + name_token = stream.pop() + except EOFException: + raise LocationException.warning("Verilog `define without argument", + define_token.location) + + if name_token.kind != IDENTIFIER: + raise LocationException.warning("Verilog `define invalid name", + name_token.location) + + name = name_token.value + + try: + token = stream.pop() + except EOFException: + # Empty define + return Macro(name) + + if token.kind in (NEWLINE,): + # Empty define + return Macro(name) + + if token.kind in (WHITESPACE,): + # Define without arguments + args = tuple() + defaults = {} + elif token.kind == LPAR: + lpar_token = token + args = tuple() + defaults = {} + + try: + while token.kind != RPAR: + if token.kind == IDENTIFIER: + argname = token.value + args = args + (argname,) + token = stream.pop() + if token.kind == EQUAL: + token = stream.pop() + defaults[argname] = [token] + token = stream.pop() + else: + token = stream.pop() + except EOFException: + raise LocationException.warning( + "EOF reached when parsing `define argument list", + lpar_token.location) + + stream.skip_while(WHITESPACE) + start = stream.idx + end = stream.skip_until(NEWLINE) + if not stream.eof: + stream.pop() + return Macro(name, + tokens=stream.slice(start, end), + args=args, + defaults=defaults) + + +class Macro(object): + """ + A `define macro with zero or more arguments + """ + + def __init__(self, name, tokens=None, args=tuple(), defaults=None): + self.name = name + self.tokens = [] if tokens is None else tokens + self.args = args + self.defaults = {} if defaults is None else defaults + + @property + def num_args(self): + return len(self.args) + + def __repr__(self): + return "Macro(%r, %r %r, %r)" % (self.name, self.tokens, self.args, self.defaults) + + def expand(self, values, previous): + """ + Expand macro with actual values, returns a list of expanded tokens + """ + tokens = [] + for token in self.tokens: + if token.kind == IDENTIFIER and token.value in self.args: + idx = self.args.index(token.value) + value = values[idx] + tokens += value + else: + tokens.append(token) + return [Token(tok.kind, tok.value, add_previous(tok.location, previous)) + for tok in tokens] + + def __eq__(self, other): + return ((self.name == other.name) + and (self.tokens == other.tokens) + and (self.args == other.args) + and (self.defaults == other.defaults)) + + def expand_from_stream(self, token, stream, previous=None): + """ + Expand macro consuming arguments from the stream + returns the expanded tokens + """ + if self.num_args == 0: + values = [] + else: + try: + values = self._parse_macro_actuals(token, stream) + except EOFException: + raise LocationException.warning("EOF reached when parsing `define actuals", + location=token.location) + + # Bind defaults + if len(values) < len(self.args): + for i in range(len(values), len(self.args)): + name = self.args[i] + if name in self.defaults: + values.append(self.defaults[name]) + else: + raise LocationException.warning( + "Missing value for argument %s" % name, + token.location) + + elif len(values) > len(self.args): + raise LocationException.warning("Too many arguments got %i expected %i" % + (len(values), len(self.args)), + token.location) + + return self.expand(values, previous) + + @staticmethod + def _parse_macro_actuals(define_token, stream): + """ + Parse the actual values of macro call such as + 1 2 in `macro(1, 2) + """ + + stream.skip_while(WHITESPACE) + + token = stream.pop() + if token.kind != LPAR: + raise LocationException.warning("Bad `define argument list", + define_token.location) + token = stream.pop() + value = [] + values = [] + + bracket_count = 0 + brace_count = 0 + par_count = 0 + + while not (token.kind == RPAR and par_count == 0): + if token.kind is LBRACKET: + bracket_count += 1 + elif token.kind is RBRACKET: + bracket_count += -1 + elif token.kind is LBRACE: + brace_count += 1 + elif token.kind is RBRACE: + brace_count += -1 + elif token.kind is LPAR: + par_count += 1 + elif token.kind is RPAR: + par_count += -1 + + value_ok = (token.kind == COMMA + and bracket_count == 0 + and brace_count == 0 + and par_count == 0) + + if value_ok: + values.append(value) + value = [] + else: + value.append(token) + token = stream.pop() + + values.append(value) + return values diff --git a/vunit/parsing/verilog/tokenizer.py b/vunit/parsing/verilog/tokenizer.py index e30cbb212..22fbc7a54 100644 --- a/vunit/parsing/verilog/tokenizer.py +++ b/vunit/parsing/verilog/tokenizer.py @@ -1,144 +1,144 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -# pylint: disable=unused-wildcard-import -# pylint: disable=wildcard-import - - -""" -Verilog preprocessing -""" - -from __future__ import print_function -from vunit.parsing.tokenizer import Tokenizer, Token -from vunit.parsing.verilog.tokens import * - - -class VerilogTokenizer(object): - """ - A Verilog tokenizer - """ - - def __init__(self, create_locations=True): - self._tokenizer = Tokenizer() - self._create_locations = create_locations - - def slice_value(token, start=None, end=None): - return Token(token.kind, token.value[start:end], token.location) - - def str_value(token): - return Token(token.kind, - token.value[1:-1].replace("\\\n", "").replace("\\\"", "\""), - token.location) - - def remove_value(token): - return Token(token.kind, '', token.location) - - def ignore_value(token): # pylint: disable=unused-argument - pass - - def add(kind, regex, func=None): - self._tokenizer.add(kind, regex, func) - - def replace_keywords(token): # pylint: disable=missing-docstring - if token.value in KEYWORDS: - return Token(KEYWORDS[token.value], '', token.location) - - return token - - add(PREPROCESSOR, - r"`[a-zA-Z][a-zA-Z0-9_]*", - lambda token: slice_value(token, start=1)) - - add(STRING, - r'(?\n".join(source_file.name for source_file in exception.path)) - - def _get_compile_timestamps(self, files): - """ - Return a dictionary of mapping file to the timestamp when it - was compiled or None if it was not compiled - """ - # Cache timestamps to avoid duplicate file operations - timestamps = {} - for source_file in files: - hash_file_name = self._hash_file_name_of(source_file) - if not ostools.file_exists(hash_file_name): - timestamps[source_file] = None - else: - timestamps[source_file] = ostools.get_modification_time(hash_file_name) - return timestamps - - def get_files_in_compile_order(self, incremental=True, dependency_graph=None): - """ - Get a list of all files in compile order - incremental -- Only return files that need recompile if True - """ - if dependency_graph is None: - dependency_graph = self.create_dependency_graph() - - all_files = self.get_source_files_in_order() - timestamps = self._get_compile_timestamps(all_files) - files = [] - for source_file in all_files: - if (not incremental) or self._needs_recompile(dependency_graph, source_file, timestamps): - files.append(source_file) - - # Get files that are affected by recompiling the modified files - try: - affected_files = dependency_graph.get_dependent(files) - compile_order = dependency_graph.toposort() - except CircularDependencyException as exc: - self._handle_circular_dependency(exc) - raise CompileError - - def comparison_key(source_file): - return compile_order.index(source_file) - - retval = sorted(affected_files, key=comparison_key) - return retval - - def get_dependencies_in_compile_order(self, target_files=None, implementation_dependencies=False): - """ - Get a list of dependencies of target files including the - target files. - :param target_files: A list of SourceFiles - """ - - if target_files is None: - target_files = self.get_source_files_in_order() - - dependency_graph = self.create_dependency_graph(implementation_dependencies) - - try: - affected_files = dependency_graph.get_dependencies(set(target_files)) - compile_order = dependency_graph.toposort() - except CircularDependencyException as exc: - self._handle_circular_dependency(exc) - raise CompileError - - def comparison_key(source_file): - return compile_order.index(source_file) - - sorted_files = sorted(affected_files, key=comparison_key) - return sorted_files - - def get_source_files_in_order(self): - """ - Get a list of source files in the order they were added to the project - """ - return [source_file for source_file in self._source_files_in_order] - - def get_libraries(self): - return self._libraries.values() - - def get_library(self, library_name): - return self._libraries[library_name] - - def has_library(self, library_name): - return library_name in self._libraries - - def _needs_recompile(self, dependency_graph, source_file, timestamps): - """ - Returns True if the source_file needs to be recompiled - given the dependency_graph, the file contents and the last modification time - """ - timestamp = timestamps[source_file] - - content_hash_file_name = self._hash_file_name_of(source_file) - if timestamp is None: - LOGGER.debug("%s has no vunit_hash file at %s and must be recompiled", - source_file.name, content_hash_file_name) - return True - - old_content_hash = ostools.read_file(content_hash_file_name) - if old_content_hash != source_file.content_hash: - LOGGER.debug("%s has different hash than last time and must be recompiled", - source_file.name) - return True - - for other_file in dependency_graph.get_direct_dependencies(source_file): - other_timestamp = timestamps[other_file] - - if other_timestamp is None: - # Other file has not been compiled and will trigger recompile of this file - continue - - if other_timestamp > timestamp: - LOGGER.debug("%s has dependency compiled earlier and must be recompiled", - source_file.name) - return True - - LOGGER.debug("%s has same hash file and must not be recompiled", - source_file.name) - - return False - - def _hash_file_name_of(self, source_file): - """ - Returns the name of the hash file associated with the source_file - """ - library = self.get_library(source_file.library.name) - prefix = hash_string(dirname(source_file.name)) - return join(library.directory, prefix, basename(source_file.name) + ".vunit_hash") - - def update(self, source_file): - """ - Mark that source_file has been recompiled, triggers a re-write of the hash file - to update the timestamp - """ - new_content_hash = source_file.content_hash - ostools.write_file(self._hash_file_name_of(source_file), new_content_hash) - LOGGER.debug('Wrote %s content_hash=%s', source_file.name, new_content_hash) - - -class Library(object): # pylint: disable=too-many-instance-attributes - """ - Represents a VHDL library - """ - def __init__(self, name, directory, vhdl_standard, is_external=False): - self.name = name - self.directory = directory - - # Default VHDL standard for files added unless explicitly set per file - self.vhdl_standard = vhdl_standard - - self._source_files = {} - - # Entity objects - self._entities = {} - self._package_bodies = {} - - self.primary_design_units = {} - - # Entity name to architecture design unit mapping - self._architectures = {} - - # Verilog specific - # Module objects - self.modules = {} - self.verilog_packages = {} - - self._is_external = is_external - - def add_source_file(self, source_file): - """ - Add source file to library unless it exists - - returns The source file that has added or the old source file - """ - if source_file.name in self._source_files: - old_source_file = self._source_files[source_file.name] - if old_source_file.content_hash != source_file.content_hash: - raise RuntimeError("%s already added to library %s" % ( - source_file.name, self.name)) - - LOGGER.info("Ignoring duplicate file %s added to library %s due to identical contents", - source_file.name, self.name) - - return old_source_file - - self._source_files[source_file.name] = source_file - source_file.add_to_library(self) - - return source_file - - def get_source_file(self, file_name): - """ - Get source file with file name or raise KeyError - """ - return self._source_files[file_name] - - @property - def is_external(self): - """ - External black box library, typically compiled outside of VUnit - """ - return self._is_external - - @staticmethod - def _warning_on_duplication(design_unit, old_file_name): - """ - Utility function to give warning for design unit duplication - """ - LOGGER.warning("%s: %s '%s' previously defined in %s", - design_unit.source_file.name, - design_unit.unit_type, - design_unit.name, - old_file_name) - - def _check_duplication(self, dictionary, design_unit): - """ - Utility function to check if design_unit already in dictionary - and give warning - """ - if design_unit.name in dictionary: - self._warning_on_duplication(design_unit, dictionary[design_unit.name].source_file.name) - - def add_vhdl_design_units(self, design_units): - """ - Add VHDL design units to the library - """ - for design_unit in design_units: - if design_unit.is_primary: - self._check_duplication(self.primary_design_units, - design_unit) - self.primary_design_units[design_unit.name] = design_unit - - if design_unit.unit_type == 'entity': - if design_unit.name not in self._architectures: - self._architectures[design_unit.name] = {} - self._entities[design_unit.name] = design_unit - - for architecture in self._architectures[design_unit.name].values(): - design_unit.add_architecture(architecture) - - else: - if design_unit.unit_type == 'architecture': - if design_unit.primary_design_unit not in self._architectures: - self._architectures[design_unit.primary_design_unit] = {} - - if design_unit.name in self._architectures[design_unit.primary_design_unit]: - self._warning_on_duplication( - design_unit, - self._architectures[design_unit.primary_design_unit][design_unit.name].source_file.name) - - self._architectures[design_unit.primary_design_unit][design_unit.name] = design_unit - - if design_unit.primary_design_unit in self._entities: - self._entities[design_unit.primary_design_unit].add_architecture(design_unit) - - if design_unit.unit_type == 'package body': - if design_unit.primary_design_unit in self._package_bodies: - self._warning_on_duplication( - design_unit, - self._package_bodies[design_unit.primary_design_unit].source_file.name) - self._package_bodies[design_unit.primary_design_unit] = design_unit - - def add_verilog_design_units(self, design_units): - """ - Add Verilog design units to the library - """ - for design_unit in design_units: - if design_unit.unit_type == 'module': - if design_unit.name in self.modules: - self._warning_on_duplication(design_unit, self.modules[design_unit.name].source_file.name) - self.modules[design_unit.name] = design_unit - elif design_unit.unit_type == 'package': - if design_unit.name in self.verilog_packages: - self._warning_on_duplication(design_unit, self.verilog_packages[design_unit.name].source_file.name) - self.verilog_packages[design_unit.name] = design_unit - - def get_entities(self): - """ - Return a list of all entites in the design with their generic names and architecture names - """ - entities = [] - for entity in self._entities.values(): - entities.append(entity) - return entities - - def get_modules(self): - """ - Return a list of all modules in the design - """ - return list(self.modules.values()) - - def get_package_body(self, name): - return self._package_bodies[name] - - def has_entity(self, name): - """ - Return true if entity with 'name' is in library - """ - return name in self._entities - - def __eq__(self, other): - if isinstance(other, type(self)): - return self.name == other.name - - return False - - def __lt__(self, other): - return self.name < other.name - - def __hash__(self): - return hash(self.name) - - -class SourceFile(object): - """ - Represents a generic source file - """ - - def __init__(self, name, library, file_type): - self.name = name - self.library = library - self.file_type = file_type - self.design_units = [] - self._content_hash = None - self._compile_options = {} - - # The file name before preprocessing - self.original_name = name - - @property - def is_vhdl(self): - return self.file_type == "vhdl" - - @property - def is_system_verilog(self): - return self.file_type == "systemverilog" - - @property - def is_any_verilog(self): - return self.file_type in VERILOG_FILE_TYPES - - def __eq__(self, other): - if isinstance(other, type(self)): - return self.to_tuple() == other.to_tuple() - - return False - - def to_tuple(self): - return (self.name, self.library, self.file_type) - - def __lt__(self, other): - return self.to_tuple() < other.to_tuple() - - def __hash__(self): - return hash(self.to_tuple()) - - def __repr__(self): - return "SourceFile(%s, %s)" % (self.name, self.library.name) - - def set_compile_option(self, name, value): - """ - Set compile option - """ - SIMULATOR_FACTORY.check_compile_option(name, value) - self._compile_options[name] = copy(value) - - def add_compile_option(self, name, value): - """ - Add compile option - """ - SIMULATOR_FACTORY.check_compile_option(name, value) - - if name not in self._compile_options: - self._compile_options[name] = copy(value) - else: - self._compile_options[name] += value - - @property - def compile_options(self): - return self._compile_options - - def get_compile_option(self, name): - """ - Return a copy of the compile option list - """ - SIMULATOR_FACTORY.check_compile_option_name(name) - - if name not in self._compile_options: - self._compile_options[name] = [] - - return copy(self._compile_options[name]) - - def _compile_options_hash(self): - """ - Compute hash of compile options - - Needs to be updated if there are nested dictionaries - """ - return hash_string(repr(sorted(self._compile_options.items()))) - - @property - def content_hash(self): - """ - Compute hash of contents and compile options - """ - return hash_string(self._content_hash + self._compile_options_hash()) - - -class VerilogSourceFile(SourceFile): - """ - Represents a Verilog source file - """ - def __init__(self, # pylint: disable=too-many-arguments - file_type, name, library, verilog_parser, database, include_dirs=None, defines=None, no_parse=False): - SourceFile.__init__(self, name, library, file_type) - self.package_dependencies = [] - self.module_dependencies = [] - self.include_dirs = include_dirs if include_dirs is not None else [] - self.defines = defines.copy() if defines is not None else {} - self._content_hash = file_content_hash(self.name, encoding=HDL_FILE_ENCODING, - database=database) - - for path in self.include_dirs: - self._content_hash = hash_string(self._content_hash + hash_string(path)) - - for key, value in sorted(self.defines.items()): - self._content_hash = hash_string(self._content_hash + hash_string(key)) - self._content_hash = hash_string(self._content_hash + hash_string(value)) - - if not no_parse: - self.parse(verilog_parser, database, include_dirs) - - def parse(self, parser, database, include_dirs): - """ - Parse Verilog code and adding dependencies and design units - """ - try: - design_file = parser.parse(self.name, include_dirs, self.defines) - for included_file_name in design_file.included_files: - self._content_hash = hash_string(self._content_hash - + file_content_hash(included_file_name, - encoding=HDL_FILE_ENCODING, - database=database)) - - for module in design_file.modules: - self.design_units.append(Module(module.name, self, module.parameters)) - - for package in design_file.packages: - self.design_units.append(DesignUnit(package.name, self, "package")) - - for package_name in design_file.imports: - self.package_dependencies.append(package_name) - - for package_name in design_file.package_references: - self.package_dependencies.append(package_name) - - for instance_name in design_file.instances: - self.module_dependencies.append(instance_name) - - except KeyboardInterrupt: - raise KeyboardInterrupt - except: # pylint: disable=bare-except - traceback.print_exc() - LOGGER.error("Failed to parse %s", self.name) - - def add_to_library(self, library): - """ - Add design units to the library - """ - assert self.library == library - library.add_verilog_design_units(self.design_units) - - -class VHDLSourceFile(SourceFile): - """ - Represents a VHDL source file - """ - def __init__(self, # pylint: disable=too-many-arguments - name, library, vhdl_parser, database, vhdl_standard, no_parse=False): - SourceFile.__init__(self, name, library, 'vhdl') - self.dependencies = [] - self.depending_components = [] - self._vhdl_standard = vhdl_standard - check_vhdl_standard(vhdl_standard) - - if not no_parse: - - try: - design_file = vhdl_parser.parse(self.name) - except KeyboardInterrupt: - raise KeyboardInterrupt - except: # pylint: disable=bare-except - traceback.print_exc() - LOGGER.error("Failed to parse %s", self.name) - else: - self._add_design_file(design_file) - - self._content_hash = file_content_hash(self.name, - encoding=HDL_FILE_ENCODING, - database=database) - - def get_vhdl_standard(self): - """ - Return the VHDL standard used to create this file - """ - return self._vhdl_standard - - def _add_design_file(self, design_file): - """ - Parse VHDL code and adding dependencies and design units - """ - self.design_units = self._find_design_units(design_file) - self.dependencies = self._find_dependencies(design_file) - self.depending_components = design_file.component_instantiations - - for design_unit in self.design_units: - if design_unit.is_primary: - LOGGER.debug('Adding primary design unit (%s) %s', design_unit.unit_type, design_unit.name) - elif design_unit.unit_type == 'package body': - LOGGER.debug('Adding secondary design unit (package body) for package %s', - design_unit.primary_design_unit) - else: - LOGGER.debug('Adding secondary design unit (%s) %s', design_unit.unit_type, design_unit.name) - - if self.depending_components: - LOGGER.debug("The file '%s' has the following components:", self.name) - for component in self.depending_components: - LOGGER.debug(component) - else: - LOGGER.debug("The file '%s' has no components", self.name) - - def _find_dependencies(self, design_file): - """ - Return a list of dependencies of this source_file based on the - use clause and entity instantiations - """ - # Find dependencies introduced by the use clause - result = [] - for ref in design_file.references: - ref = ref.copy() - - if ref.library == "work": - # Work means same library as current file - ref.library = self.library.name - - result.append(ref) - - for configuration in design_file.configurations: - result.append(VHDLReference('entity', self.library.name, configuration.entity, 'all')) - - return result - - def _find_design_units(self, design_file): - """ - Return all design units found in the design_file - """ - result = [] - for entity in design_file.entities: - generic_names = [generic.identifier for generic in entity.generics] - result.append(Entity(entity.identifier, self, generic_names)) - - for context in design_file.contexts: - result.append(VHDLDesignUnit(context.identifier, self, 'context')) - - for package in design_file.packages: - result.append(VHDLDesignUnit(package.identifier, self, 'package')) - - for architecture in design_file.architectures: - result.append(VHDLDesignUnit(architecture.identifier, self, 'architecture', False, architecture.entity)) - - for configuration in design_file.configurations: - result.append(VHDLDesignUnit(configuration.identifier, self, 'configuration')) - - for body in design_file.package_bodies: - result.append(VHDLDesignUnit(body.identifier, - self, 'package body', False, body.identifier)) - - return result - - @property - def content_hash(self): - """ - Compute hash of contents and compile options - """ - return hash_string(self._content_hash + self._compile_options_hash() + hash_string(self._vhdl_standard)) - - def add_to_library(self, library): - """ - Add design units to the library - """ - assert self.library == library - library.add_vhdl_design_units(self.design_units) - - -# lower case representation of supported extensions -VHDL_EXTENSIONS = (".vhd", ".vhdl", ".vho") -VERILOG_EXTENSIONS = (".v", ".vp", ".vams", ".vo") -SYSTEM_VERILOG_EXTENSIONS = (".sv",) -VERILOG_FILE_TYPES = ("verilog", "systemverilog") -FILE_TYPES = ("vhdl", ) + VERILOG_FILE_TYPES - - -def file_type_of(file_name): - """ - Return the file type of file_name based on the file ending - """ - _, ext = splitext(file_name) - if ext.lower() in VHDL_EXTENSIONS: - return "vhdl" - - if ext.lower() in VERILOG_EXTENSIONS: - return "verilog" - - if ext.lower() in SYSTEM_VERILOG_EXTENSIONS: - return "systemverilog" - - raise RuntimeError("Unknown file ending '%s' of %s" % (ext, file_name)) - - -def check_vhdl_standard(vhdl_standard, from_str=None): - """ - Check the VHDL standard selected is recognized - """ - if from_str is None: - from_str = "" - else: - from_str += " " - - valid_standards = ('93', '2002', '2008') - if vhdl_standard not in valid_standards: - raise ValueError("Unknown VHDL standard '%s' %snot one of %r" % (vhdl_standard, from_str, valid_standards)) +# Classes to model a HDL design hierarchy +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Functionality to represent and operate on a HDL code project +""" + + +from os.path import join, basename, dirname, splitext, isdir, exists +from copy import copy +import traceback +import logging +from collections import OrderedDict +from vunit.hashing import hash_string +from vunit.dependency_graph import (DependencyGraph, + CircularDependencyException) +from vunit.vhdl_parser import VHDLParser, VHDLReference +from vunit.cached import file_content_hash +from vunit.parsing.verilog.parser import VerilogParser +from vunit.parsing.encodings import HDL_FILE_ENCODING +from vunit.exceptions import CompileError +from vunit.simulator_factory import SIMULATOR_FACTORY +from vunit.design_unit import DesignUnit, VHDLDesignUnit, Entity, Module +from vunit import ostools +LOGGER = logging.getLogger(__name__) + + +class Project(object): # pylint: disable=too-many-instance-attributes + """ + The representation of a HDL code project. + Compute lists of source files to recompile based on file contents, + timestamps and depenencies derived from the design hierarchy. + """ + def __init__(self, + depend_on_package_body=False, + database=None): + """ + depend_on_package_body - Package users depend also on package body + """ + self._database = database + self._vhdl_parser = VHDLParser(database=self._database) + self._verilog_parser = VerilogParser(database=self._database) + self._libraries = OrderedDict() + # Mapping between library lower case name and real library name + self._lower_library_names_dict = {} + self._source_files_in_order = [] + self._manual_dependencies = [] + self._depend_on_package_body = depend_on_package_body + self._builtin_libraries = set(["ieee", "std"]) + + def _validate_new_library_name(self, library_name): + """ + Check that the library_name is valid or raise RuntimeError + """ + if library_name == "work": + LOGGER.error("Cannot add library named work. work is a reference to the current library. " + "http://www.sigasi.com/content/work-not-vhdl-library") + raise RuntimeError("Illegal library name 'work'") + + if library_name in self._libraries: + raise ValueError("Library %s already exists" % library_name) + + lower_name = library_name.lower() + if lower_name in self._lower_library_names_dict: + raise RuntimeError( + "Library name %r not case-insensitive unique. Library name %r previously defined" + % (library_name, self._lower_library_names_dict[lower_name])) + + def add_builtin_library(self, logical_name): + """ + Add a builtin library name that does not give missing dependency warnings + """ + self._builtin_libraries.add(logical_name) + + def add_library(self, logical_name, directory, vhdl_standard='2008', is_external=False): + """ + Add library to project with logical_name located or to be located in directory + is_external -- Library is assumed to a black-box + """ + self._validate_new_library_name(logical_name) + + if is_external: + if not exists(directory): + raise ValueError("External library %r does not exist" % directory) + + if not isdir(directory): + raise ValueError("External library must be a directory. Got %r" % directory) + + library = Library(logical_name, directory, vhdl_standard, is_external=is_external) + LOGGER.debug('Adding library %s with path %s', logical_name, directory) + + self._libraries[logical_name] = library + self._lower_library_names_dict[logical_name.lower()] = library.name + + def add_source_file(self, # pylint: disable=too-many-arguments + file_name, library_name, file_type='vhdl', include_dirs=None, defines=None, + vhdl_standard=None, + no_parse=False): + """ + Add a file_name as a source file in library_name with file_type + + :param no_parse: Do not parse file contents + """ + if not ostools.file_exists(file_name): + raise ValueError("File %r does not exist" % file_name) + + LOGGER.debug('Adding source file %s to library %s', file_name, library_name) + library = self._libraries[library_name] + + if file_type == "vhdl": + assert include_dirs is None + source_file = VHDLSourceFile( + file_name, + library, + vhdl_parser=self._vhdl_parser, + database=self._database, + vhdl_standard=library.vhdl_standard if vhdl_standard is None else vhdl_standard, + no_parse=no_parse) + elif file_type in VERILOG_FILE_TYPES: + source_file = VerilogSourceFile(file_type, + file_name, + library, + verilog_parser=self._verilog_parser, + database=self._database, + include_dirs=include_dirs, + defines=defines, + no_parse=no_parse) + else: + raise ValueError(file_type) + + old_source_file = library.add_source_file(source_file) + if id(source_file) == id(old_source_file): + self._source_files_in_order.append(source_file) + + return old_source_file + + def add_manual_dependency(self, source_file, depends_on): + """ + Add manual dependency where 'source_file' depends_on 'depends_on' + """ + self._manual_dependencies.append((source_file, depends_on)) + + @staticmethod + def _find_primary_secondary_design_unit_dependencies(source_file): + """ + Iterate over dependencies between the primary design units of the source_file + and their secondary design units + """ + library = source_file.library + + for unit in source_file.design_units: + if unit.is_primary: + continue + + try: + primary_unit = library.primary_design_units[unit.primary_design_unit] + except KeyError: + LOGGER.warning("%s: failed to find a primary design unit '%s' in library '%s'", + source_file.name, unit.primary_design_unit, library.name) + else: + yield primary_unit.source_file + + def _find_vhdl_library_reference(self, library_name): + """ + Find a VHDL library reference that is case insensitive or raise KeyError + """ + real_library_name = self._lower_library_names_dict[library_name] + return self._libraries[real_library_name] + + def _find_other_vhdl_design_unit_dependencies(self, # pylint: disable=too-many-branches + source_file, + depend_on_package_body, + implementation_dependencies): + """ + Iterate over the dependencies on other design unit of the source_file + """ + for ref in source_file.dependencies: + try: + library = self._find_vhdl_library_reference(ref.library) + except KeyError: + if ref.library not in self._builtin_libraries: + LOGGER.warning("%s: failed to find library '%s'", source_file.name, ref.library) + continue + + if ref.is_entity_reference() and ref.design_unit in library.modules: + # Is a verilog module instantiation + yield library.modules[ref.design_unit].source_file + continue + + try: + primary_unit = library.primary_design_units[ref.design_unit] + except KeyError: + if not library.is_external: + LOGGER.warning("%s: failed to find a primary design unit '%s' in library '%s'", + source_file.name, ref.design_unit, library.name) + continue + else: + yield primary_unit.source_file + + if ref.is_entity_reference(): + if ref.reference_all_names_within(): + # Reference all architectures, + # We make configuration declarations implicitly reference all architectures + names = primary_unit.architecture_names.keys() + elif ref.name_within is None and implementation_dependencies: + # For implementation dependencies we add a dependency to all architectures + names = primary_unit.architecture_names.keys() + else: + names = [ref.name_within] + + for name in names: + if name is None: + # Was not a reference to a specific architecture + continue + + if name in primary_unit.architecture_names: + file_name = primary_unit.architecture_names[name] + yield library.get_source_file(file_name) + else: + LOGGER.warning("%s: failed to find architecture '%s' of entity '%s.%s'", + source_file.name, name, library.name, primary_unit.name) + + elif ref.is_package_reference() and depend_on_package_body: + try: + yield library.get_package_body(primary_unit.name).source_file + except KeyError: + # There was no package body, which is legal in VHDL + pass + + def _find_verilog_package_dependencies(self, source_file): + """ + Find dependencies from import of verilog packages + """ + for package_name in source_file.package_dependencies: + for library in self._libraries.values(): + try: + design_unit = library.verilog_packages[package_name] + yield design_unit.source_file + except KeyError: + pass + + def _find_verilog_module_dependencies(self, source_file): + """ + Find dependencies from instantiation of verilog modules + """ + for module_name in source_file.module_dependencies: + if module_name in source_file.library.modules: + design_unit = source_file.library.modules[module_name] + yield design_unit.source_file + else: + for library in self._libraries.values(): + try: + design_unit = library.modules[module_name] + yield design_unit.source_file + except KeyError: + pass + + @staticmethod + def _find_component_design_unit_dependencies(source_file): + """ + Iterate over the dependencies on other design units of the source_file + that are the result of component instantiations + """ + for unit_name in source_file.depending_components: + found_component_match = False + + try: + primary_unit = source_file.library.primary_design_units[unit_name] + yield primary_unit.source_file + + for file_name in primary_unit.architecture_names.values(): + yield source_file.library.get_source_file(file_name) + except KeyError: + pass + else: + found_component_match = True + + try: + module = source_file.library.modules[unit_name] + except KeyError: + pass + else: + found_component_match = True + yield module.source_file + + if not found_component_match: + LOGGER.debug("failed to find a matching entity/module for component '%s' ", unit_name) + + def create_dependency_graph(self, implementation_dependencies=False): + """ + Create a DependencyGraph object of the HDL code project + """ + def add_dependency(start, end): + """ + Utility to add dependency + """ + if start.name == end.name: + return + + is_new = dependency_graph.add_dependency(start, end) + + if is_new: + LOGGER.debug('Adding dependency: %s depends on %s', end.name, start.name) + + def add_dependencies(dependency_function, files): + """ + Utility to add all dependencies returned by a dependency_function + returning an iterator of dependencies + """ + for source_file in files: + for dependency in dependency_function(source_file): + add_dependency(dependency, source_file) + + dependency_graph = DependencyGraph() + for source_file in self.get_source_files_in_order(): + dependency_graph.add_node(source_file) + + vhdl_files = [source_file + for source_file in self.get_source_files_in_order() + if source_file.file_type == 'vhdl'] + + depend_on_package_bodies = self._depend_on_package_body or implementation_dependencies + add_dependencies( + lambda source_file: self._find_other_vhdl_design_unit_dependencies(source_file, + depend_on_package_bodies, + implementation_dependencies), + vhdl_files) + add_dependencies(self._find_primary_secondary_design_unit_dependencies, vhdl_files) + + verilog_files = [source_file + for source_file in self.get_source_files_in_order() + if source_file.file_type in VERILOG_FILE_TYPES] + + add_dependencies(self._find_verilog_package_dependencies, verilog_files) + add_dependencies(self._find_verilog_module_dependencies, verilog_files) + + if implementation_dependencies: + add_dependencies(self._find_component_design_unit_dependencies, vhdl_files) + + for source_file, depends_on in self._manual_dependencies: + add_dependency(depends_on, source_file) + + return dependency_graph + + @staticmethod + def _handle_circular_dependency(exception): + """ + Pretty print circular dependency to error log + """ + LOGGER.error("Found circular dependency:\n%s", + " ->\n".join(source_file.name for source_file in exception.path)) + + def _get_compile_timestamps(self, files): + """ + Return a dictionary of mapping file to the timestamp when it + was compiled or None if it was not compiled + """ + # Cache timestamps to avoid duplicate file operations + timestamps = {} + for source_file in files: + hash_file_name = self._hash_file_name_of(source_file) + if not ostools.file_exists(hash_file_name): + timestamps[source_file] = None + else: + timestamps[source_file] = ostools.get_modification_time(hash_file_name) + return timestamps + + def get_files_in_compile_order(self, incremental=True, dependency_graph=None): + """ + Get a list of all files in compile order + incremental -- Only return files that need recompile if True + """ + if dependency_graph is None: + dependency_graph = self.create_dependency_graph() + + all_files = self.get_source_files_in_order() + timestamps = self._get_compile_timestamps(all_files) + files = [] + for source_file in all_files: + if (not incremental) or self._needs_recompile(dependency_graph, source_file, timestamps): + files.append(source_file) + + # Get files that are affected by recompiling the modified files + try: + affected_files = dependency_graph.get_dependent(files) + compile_order = dependency_graph.toposort() + except CircularDependencyException as exc: + self._handle_circular_dependency(exc) + raise CompileError + + def comparison_key(source_file): + return compile_order.index(source_file) + + retval = sorted(affected_files, key=comparison_key) + return retval + + def get_dependencies_in_compile_order(self, target_files=None, implementation_dependencies=False): + """ + Get a list of dependencies of target files including the + target files. + :param target_files: A list of SourceFiles + """ + + if target_files is None: + target_files = self.get_source_files_in_order() + + dependency_graph = self.create_dependency_graph(implementation_dependencies) + + try: + affected_files = dependency_graph.get_dependencies(set(target_files)) + compile_order = dependency_graph.toposort() + except CircularDependencyException as exc: + self._handle_circular_dependency(exc) + raise CompileError + + def comparison_key(source_file): + return compile_order.index(source_file) + + sorted_files = sorted(affected_files, key=comparison_key) + return sorted_files + + def get_source_files_in_order(self): + """ + Get a list of source files in the order they were added to the project + """ + return [source_file for source_file in self._source_files_in_order] + + def get_libraries(self): + return self._libraries.values() + + def get_library(self, library_name): + return self._libraries[library_name] + + def has_library(self, library_name): + return library_name in self._libraries + + def _needs_recompile(self, dependency_graph, source_file, timestamps): + """ + Returns True if the source_file needs to be recompiled + given the dependency_graph, the file contents and the last modification time + """ + timestamp = timestamps[source_file] + + content_hash_file_name = self._hash_file_name_of(source_file) + if timestamp is None: + LOGGER.debug("%s has no vunit_hash file at %s and must be recompiled", + source_file.name, content_hash_file_name) + return True + + old_content_hash = ostools.read_file(content_hash_file_name) + if old_content_hash != source_file.content_hash: + LOGGER.debug("%s has different hash than last time and must be recompiled", + source_file.name) + return True + + for other_file in dependency_graph.get_direct_dependencies(source_file): + other_timestamp = timestamps[other_file] + + if other_timestamp is None: + # Other file has not been compiled and will trigger recompile of this file + continue + + if other_timestamp > timestamp: + LOGGER.debug("%s has dependency compiled earlier and must be recompiled", + source_file.name) + return True + + LOGGER.debug("%s has same hash file and must not be recompiled", + source_file.name) + + return False + + def _hash_file_name_of(self, source_file): + """ + Returns the name of the hash file associated with the source_file + """ + library = self.get_library(source_file.library.name) + prefix = hash_string(dirname(source_file.name)) + return join(library.directory, prefix, basename(source_file.name) + ".vunit_hash") + + def update(self, source_file): + """ + Mark that source_file has been recompiled, triggers a re-write of the hash file + to update the timestamp + """ + new_content_hash = source_file.content_hash + ostools.write_file(self._hash_file_name_of(source_file), new_content_hash) + LOGGER.debug('Wrote %s content_hash=%s', source_file.name, new_content_hash) + + +class Library(object): # pylint: disable=too-many-instance-attributes + """ + Represents a VHDL library + """ + def __init__(self, name, directory, vhdl_standard, is_external=False): + self.name = name + self.directory = directory + + # Default VHDL standard for files added unless explicitly set per file + self.vhdl_standard = vhdl_standard + + self._source_files = {} + + # Entity objects + self._entities = {} + self._package_bodies = {} + + self.primary_design_units = {} + + # Entity name to architecture design unit mapping + self._architectures = {} + + # Verilog specific + # Module objects + self.modules = {} + self.verilog_packages = {} + + self._is_external = is_external + + def add_source_file(self, source_file): + """ + Add source file to library unless it exists + + returns The source file that has added or the old source file + """ + if source_file.name in self._source_files: + old_source_file = self._source_files[source_file.name] + if old_source_file.content_hash != source_file.content_hash: + raise RuntimeError("%s already added to library %s" % ( + source_file.name, self.name)) + + LOGGER.info("Ignoring duplicate file %s added to library %s due to identical contents", + source_file.name, self.name) + + return old_source_file + + self._source_files[source_file.name] = source_file + source_file.add_to_library(self) + + return source_file + + def get_source_file(self, file_name): + """ + Get source file with file name or raise KeyError + """ + return self._source_files[file_name] + + @property + def is_external(self): + """ + External black box library, typically compiled outside of VUnit + """ + return self._is_external + + @staticmethod + def _warning_on_duplication(design_unit, old_file_name): + """ + Utility function to give warning for design unit duplication + """ + LOGGER.warning("%s: %s '%s' previously defined in %s", + design_unit.source_file.name, + design_unit.unit_type, + design_unit.name, + old_file_name) + + def _check_duplication(self, dictionary, design_unit): + """ + Utility function to check if design_unit already in dictionary + and give warning + """ + if design_unit.name in dictionary: + self._warning_on_duplication(design_unit, dictionary[design_unit.name].source_file.name) + + def add_vhdl_design_units(self, design_units): + """ + Add VHDL design units to the library + """ + for design_unit in design_units: + if design_unit.is_primary: + self._check_duplication(self.primary_design_units, + design_unit) + self.primary_design_units[design_unit.name] = design_unit + + if design_unit.unit_type == 'entity': + if design_unit.name not in self._architectures: + self._architectures[design_unit.name] = {} + self._entities[design_unit.name] = design_unit + + for architecture in self._architectures[design_unit.name].values(): + design_unit.add_architecture(architecture) + + else: + if design_unit.unit_type == 'architecture': + if design_unit.primary_design_unit not in self._architectures: + self._architectures[design_unit.primary_design_unit] = {} + + if design_unit.name in self._architectures[design_unit.primary_design_unit]: + self._warning_on_duplication( + design_unit, + self._architectures[design_unit.primary_design_unit][design_unit.name].source_file.name) + + self._architectures[design_unit.primary_design_unit][design_unit.name] = design_unit + + if design_unit.primary_design_unit in self._entities: + self._entities[design_unit.primary_design_unit].add_architecture(design_unit) + + if design_unit.unit_type == 'package body': + if design_unit.primary_design_unit in self._package_bodies: + self._warning_on_duplication( + design_unit, + self._package_bodies[design_unit.primary_design_unit].source_file.name) + self._package_bodies[design_unit.primary_design_unit] = design_unit + + def add_verilog_design_units(self, design_units): + """ + Add Verilog design units to the library + """ + for design_unit in design_units: + if design_unit.unit_type == 'module': + if design_unit.name in self.modules: + self._warning_on_duplication(design_unit, self.modules[design_unit.name].source_file.name) + self.modules[design_unit.name] = design_unit + elif design_unit.unit_type == 'package': + if design_unit.name in self.verilog_packages: + self._warning_on_duplication(design_unit, self.verilog_packages[design_unit.name].source_file.name) + self.verilog_packages[design_unit.name] = design_unit + + def get_entities(self): + """ + Return a list of all entites in the design with their generic names and architecture names + """ + entities = [] + for entity in self._entities.values(): + entities.append(entity) + return entities + + def get_modules(self): + """ + Return a list of all modules in the design + """ + return list(self.modules.values()) + + def get_package_body(self, name): + return self._package_bodies[name] + + def has_entity(self, name): + """ + Return true if entity with 'name' is in library + """ + return name in self._entities + + def __eq__(self, other): + if isinstance(other, type(self)): + return self.name == other.name + + return False + + def __lt__(self, other): + return self.name < other.name + + def __hash__(self): + return hash(self.name) + + +class SourceFile(object): + """ + Represents a generic source file + """ + + def __init__(self, name, library, file_type): + self.name = name + self.library = library + self.file_type = file_type + self.design_units = [] + self._content_hash = None + self._compile_options = {} + + # The file name before preprocessing + self.original_name = name + + @property + def is_vhdl(self): + return self.file_type == "vhdl" + + @property + def is_system_verilog(self): + return self.file_type == "systemverilog" + + @property + def is_any_verilog(self): + return self.file_type in VERILOG_FILE_TYPES + + def __eq__(self, other): + if isinstance(other, type(self)): + return self.to_tuple() == other.to_tuple() + + return False + + def to_tuple(self): + return (self.name, self.library, self.file_type) + + def __lt__(self, other): + return self.to_tuple() < other.to_tuple() + + def __hash__(self): + return hash(self.to_tuple()) + + def __repr__(self): + return "SourceFile(%s, %s)" % (self.name, self.library.name) + + def set_compile_option(self, name, value): + """ + Set compile option + """ + SIMULATOR_FACTORY.check_compile_option(name, value) + self._compile_options[name] = copy(value) + + def add_compile_option(self, name, value): + """ + Add compile option + """ + SIMULATOR_FACTORY.check_compile_option(name, value) + + if name not in self._compile_options: + self._compile_options[name] = copy(value) + else: + self._compile_options[name] += value + + @property + def compile_options(self): + return self._compile_options + + def get_compile_option(self, name): + """ + Return a copy of the compile option list + """ + SIMULATOR_FACTORY.check_compile_option_name(name) + + if name not in self._compile_options: + self._compile_options[name] = [] + + return copy(self._compile_options[name]) + + def _compile_options_hash(self): + """ + Compute hash of compile options + + Needs to be updated if there are nested dictionaries + """ + return hash_string(repr(sorted(self._compile_options.items()))) + + @property + def content_hash(self): + """ + Compute hash of contents and compile options + """ + return hash_string(self._content_hash + self._compile_options_hash()) + + +class VerilogSourceFile(SourceFile): + """ + Represents a Verilog source file + """ + def __init__(self, # pylint: disable=too-many-arguments + file_type, name, library, verilog_parser, database, include_dirs=None, defines=None, no_parse=False): + SourceFile.__init__(self, name, library, file_type) + self.package_dependencies = [] + self.module_dependencies = [] + self.include_dirs = include_dirs if include_dirs is not None else [] + self.defines = defines.copy() if defines is not None else {} + self._content_hash = file_content_hash(self.name, encoding=HDL_FILE_ENCODING, + database=database) + + for path in self.include_dirs: + self._content_hash = hash_string(self._content_hash + hash_string(path)) + + for key, value in sorted(self.defines.items()): + self._content_hash = hash_string(self._content_hash + hash_string(key)) + self._content_hash = hash_string(self._content_hash + hash_string(value)) + + if not no_parse: + self.parse(verilog_parser, database, include_dirs) + + def parse(self, parser, database, include_dirs): + """ + Parse Verilog code and adding dependencies and design units + """ + try: + design_file = parser.parse(self.name, include_dirs, self.defines) + for included_file_name in design_file.included_files: + self._content_hash = hash_string(self._content_hash + + file_content_hash(included_file_name, + encoding=HDL_FILE_ENCODING, + database=database)) + + for module in design_file.modules: + self.design_units.append(Module(module.name, self, module.parameters)) + + for package in design_file.packages: + self.design_units.append(DesignUnit(package.name, self, "package")) + + for package_name in design_file.imports: + self.package_dependencies.append(package_name) + + for package_name in design_file.package_references: + self.package_dependencies.append(package_name) + + for instance_name in design_file.instances: + self.module_dependencies.append(instance_name) + + except KeyboardInterrupt: + raise KeyboardInterrupt + except: # pylint: disable=bare-except + traceback.print_exc() + LOGGER.error("Failed to parse %s", self.name) + + def add_to_library(self, library): + """ + Add design units to the library + """ + assert self.library == library + library.add_verilog_design_units(self.design_units) + + +class VHDLSourceFile(SourceFile): + """ + Represents a VHDL source file + """ + def __init__(self, # pylint: disable=too-many-arguments + name, library, vhdl_parser, database, vhdl_standard, no_parse=False): + SourceFile.__init__(self, name, library, 'vhdl') + self.dependencies = [] + self.depending_components = [] + self._vhdl_standard = vhdl_standard + check_vhdl_standard(vhdl_standard) + + if not no_parse: + + try: + design_file = vhdl_parser.parse(self.name) + except KeyboardInterrupt: + raise KeyboardInterrupt + except: # pylint: disable=bare-except + traceback.print_exc() + LOGGER.error("Failed to parse %s", self.name) + else: + self._add_design_file(design_file) + + self._content_hash = file_content_hash(self.name, + encoding=HDL_FILE_ENCODING, + database=database) + + def get_vhdl_standard(self): + """ + Return the VHDL standard used to create this file + """ + return self._vhdl_standard + + def _add_design_file(self, design_file): + """ + Parse VHDL code and adding dependencies and design units + """ + self.design_units = self._find_design_units(design_file) + self.dependencies = self._find_dependencies(design_file) + self.depending_components = design_file.component_instantiations + + for design_unit in self.design_units: + if design_unit.is_primary: + LOGGER.debug('Adding primary design unit (%s) %s', design_unit.unit_type, design_unit.name) + elif design_unit.unit_type == 'package body': + LOGGER.debug('Adding secondary design unit (package body) for package %s', + design_unit.primary_design_unit) + else: + LOGGER.debug('Adding secondary design unit (%s) %s', design_unit.unit_type, design_unit.name) + + if self.depending_components: + LOGGER.debug("The file '%s' has the following components:", self.name) + for component in self.depending_components: + LOGGER.debug(component) + else: + LOGGER.debug("The file '%s' has no components", self.name) + + def _find_dependencies(self, design_file): + """ + Return a list of dependencies of this source_file based on the + use clause and entity instantiations + """ + # Find dependencies introduced by the use clause + result = [] + for ref in design_file.references: + ref = ref.copy() + + if ref.library == "work": + # Work means same library as current file + ref.library = self.library.name + + result.append(ref) + + for configuration in design_file.configurations: + result.append(VHDLReference('entity', self.library.name, configuration.entity, 'all')) + + return result + + def _find_design_units(self, design_file): + """ + Return all design units found in the design_file + """ + result = [] + for entity in design_file.entities: + generic_names = [generic.identifier for generic in entity.generics] + result.append(Entity(entity.identifier, self, generic_names)) + + for context in design_file.contexts: + result.append(VHDLDesignUnit(context.identifier, self, 'context')) + + for package in design_file.packages: + result.append(VHDLDesignUnit(package.identifier, self, 'package')) + + for architecture in design_file.architectures: + result.append(VHDLDesignUnit(architecture.identifier, self, 'architecture', False, architecture.entity)) + + for configuration in design_file.configurations: + result.append(VHDLDesignUnit(configuration.identifier, self, 'configuration')) + + for body in design_file.package_bodies: + result.append(VHDLDesignUnit(body.identifier, + self, 'package body', False, body.identifier)) + + return result + + @property + def content_hash(self): + """ + Compute hash of contents and compile options + """ + return hash_string(self._content_hash + self._compile_options_hash() + hash_string(self._vhdl_standard)) + + def add_to_library(self, library): + """ + Add design units to the library + """ + assert self.library == library + library.add_vhdl_design_units(self.design_units) + + +# lower case representation of supported extensions +VHDL_EXTENSIONS = (".vhd", ".vhdl", ".vho") +VERILOG_EXTENSIONS = (".v", ".vp", ".vams", ".vo") +SYSTEM_VERILOG_EXTENSIONS = (".sv",) +VERILOG_FILE_TYPES = ("verilog", "systemverilog") +FILE_TYPES = ("vhdl", ) + VERILOG_FILE_TYPES + + +def file_type_of(file_name): + """ + Return the file type of file_name based on the file ending + """ + _, ext = splitext(file_name) + if ext.lower() in VHDL_EXTENSIONS: + return "vhdl" + + if ext.lower() in VERILOG_EXTENSIONS: + return "verilog" + + if ext.lower() in SYSTEM_VERILOG_EXTENSIONS: + return "systemverilog" + + raise RuntimeError("Unknown file ending '%s' of %s" % (ext, file_name)) + + +def check_vhdl_standard(vhdl_standard, from_str=None): + """ + Check the VHDL standard selected is recognized + """ + if from_str is None: + from_str = "" + else: + from_str += " " + + valid_standards = ('93', '2002', '2008') + if vhdl_standard not in valid_standards: + raise ValueError("Unknown VHDL standard '%s' %snot one of %r" % (vhdl_standard, from_str, valid_standards)) diff --git a/vunit/rivierapro_interface.py b/vunit/rivierapro_interface.py index 833e49315..0f857ccff 100644 --- a/vunit/rivierapro_interface.py +++ b/vunit/rivierapro_interface.py @@ -1,389 +1,389 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Interface towards Aldec Riviera Pro -""" - - -from __future__ import print_function - -from os.path import join, dirname, abspath -import os -import re -import logging -from vunit.ostools import Process, file_exists -from vunit.simulator_interface import (SimulatorInterface, - ListOfStringOption, - StringOption) -from vunit.exceptions import CompileError -from vunit.vsim_simulator_mixin import (VsimSimulatorMixin, - fix_path) - -LOGGER = logging.getLogger(__name__) - - -class RivieraProInterface(VsimSimulatorMixin, SimulatorInterface): - """ - Riviera Pro interface - """ - - name = "rivierapro" - supports_gui_flag = True - package_users_depend_on_bodies = True - - compile_options = [ - ListOfStringOption("rivierapro.vcom_flags"), - ListOfStringOption("rivierapro.vlog_flags"), - ] - - sim_options = [ - ListOfStringOption("rivierapro.vsim_flags"), - ListOfStringOption("rivierapro.vsim_flags.gui"), - ListOfStringOption("rivierapro.init_files.after_load"), - StringOption("rivierapro.init_file.gui"), - ] - - @classmethod - def from_args(cls, args, output_path, **kwargs): - """ - Create new instance from command line arguments object - """ - persistent = not (args.unique_sim or args.gui) - - return cls(prefix=cls.find_prefix(), - output_path=output_path, - persistent=persistent, - gui=args.gui) - - @classmethod - def find_prefix_from_path(cls): - """ - Find RivieraPro toolchain. - - Must have vsim and vsimsa binaries but no avhdl.exe - """ - def no_avhdl(path): - return not file_exists(join(path, "avhdl.exe")) - return cls.find_toolchain(["vsim", - "vsimsa"], - constraints=[no_avhdl]) - - @classmethod - def get_osvvm_coverage_api(cls): - """ - Returns simulator name when OSVVM coverage API is supported, None otherwise. - """ - proc = Process([join(cls.find_prefix(), 'vcom'), '-version'], - env=cls.get_env()) - consumer = VersionConsumer() - proc.consume_output(consumer) - if consumer.year is not None: - if (consumer.year == 2016 and consumer.month >= 10) or (consumer.year > 2016): - return cls.name - - return None - - @classmethod - def supports_vhdl_package_generics(cls): - """ - Returns True when this simulator supports VHDL package generics - """ - return True - - def __init__(self, prefix, output_path, persistent=False, gui=False): - SimulatorInterface.__init__(self, output_path, gui) - VsimSimulatorMixin.__init__(self, prefix, persistent, - sim_cfg_file_name=join(output_path, "library.cfg")) - self._create_library_cfg() - self._libraries = [] - self._coverage_files = set() - - def add_simulator_specific(self, project): - """ - Add builtin (global) libraries - """ - built_in_libraries = self._get_mapped_libraries(self._builtin_library_cfg) - - for library_name in built_in_libraries: - # A user might shadow a built in library with their own version - if not project.has_library(library_name): - project.add_builtin_library(library_name) - - def setup_library_mapping(self, project): - """ - Setup library mapping - """ - mapped_libraries = self._get_mapped_libraries(self._sim_cfg_file_name) - for library in project.get_libraries(): - self._libraries.append(library) - self.create_library(library.name, library.directory, mapped_libraries) - - def compile_source_file_command(self, source_file): - """ - Returns the command to compile a single source_file - """ - if source_file.is_vhdl: - return self.compile_vhdl_file_command(source_file) - - if source_file.is_any_verilog: - return self.compile_verilog_file_command(source_file) - - LOGGER.error("Unknown file type: %s", source_file.file_type) - raise CompileError - - def compile_vhdl_file_command(self, source_file): - """ - Returns the command to compile a VHDL file - """ - return ([join(self._prefix, 'vcom'), '-quiet', '-j', dirname(self._sim_cfg_file_name)] - + source_file.compile_options.get("rivierapro.vcom_flags", []) - + ['-' + source_file.get_vhdl_standard(), '-work', source_file.library.name, source_file.name]) - - def compile_verilog_file_command(self, source_file): - """ - Returns the command to compile a Verilog file - """ - args = [join(self._prefix, 'vlog'), '-quiet', '-lc', self._sim_cfg_file_name] - if source_file.is_system_verilog: - args += ['-sv2k12'] - args += source_file.compile_options.get("rivierapro.vlog_flags", []) - args += ['-work', source_file.library.name, source_file.name] - for library in self._libraries: - args += ["-l", library.name] - for include_dir in source_file.include_dirs: - args += ["+incdir+%s" % include_dir] - for key, value in source_file.defines.items(): - args += ["+define+%s=%s" % (key, value)] - return args - - def create_library(self, library_name, path, mapped_libraries=None): - """ - Create and map a library_name to path - """ - mapped_libraries = mapped_libraries if mapped_libraries is not None else {} - - if not file_exists(dirname(abspath(path))): - os.makedirs(dirname(abspath(path))) - - if not file_exists(path): - proc = Process([join(self._prefix, 'vlib'), library_name, path], - cwd=dirname(self._sim_cfg_file_name), - env=self.get_env()) - proc.consume_output(callback=None) - - if library_name in mapped_libraries and mapped_libraries[library_name] == path: - return - - proc = Process([join(self._prefix, 'vmap'), library_name, path], - cwd=dirname(self._sim_cfg_file_name), - env=self.get_env()) - proc.consume_output(callback=None) - - def _create_library_cfg(self): - """ - Create the library.cfg file if it does not exist - """ - if file_exists(self._sim_cfg_file_name): - return - - with open(self._sim_cfg_file_name, "w") as ofile: - ofile.write('$INCLUDE = "%s"\n' % self._builtin_library_cfg) - - @property - def _builtin_library_cfg(self): - return join(self._prefix, "..", "vlib", "library.cfg") - - _library_re = re.compile(r'([a-zA-Z_0-9]+)\s=\s(.*)') - - def _get_mapped_libraries(self, library_cfg_file): - """ - Get mapped libraries by running vlist on the working directory - """ - lines = [] - proc = Process([join(self._prefix, 'vlist')], cwd=dirname(library_cfg_file)) - proc.consume_output(callback=lines.append) - - libraries = {} - for line in lines: - match = self._library_re.match(line) - if match is None: - continue - key = match.group(1) - value = match.group(2) - libraries[key] = abspath(join(dirname(library_cfg_file), dirname(value))) - return libraries - - def _create_load_function(self, - test_suite_name, # pylint: disable=unused-argument - config, output_path): - """ - Create the vunit_load TCL function that runs the vsim command and loads the design - """ - set_generic_str = " ".join(('-g/%s/%s=%s' % (config.entity_name, - name, - format_generic(value)) - for name, value in config.generics.items())) - pli_str = " ".join("-pli \"%s\"" % fix_path(name) for name in config.sim_options.get('pli', [])) - - vsim_flags = ["-dataset {%s}" % fix_path(join(output_path, "dataset.asdb")), - pli_str, - set_generic_str] - - if config.sim_options.get("enable_coverage", False): - coverage_file_path = join(output_path, "coverage.acdb") - self._coverage_files.add(coverage_file_path) - vsim_flags += ["-acdb_file {%s}" % coverage_file_path] - - vsim_flags += [self._vsim_extra_args(config)] - - if config.sim_options.get("disable_ieee_warnings", False): - vsim_flags.append("-ieee_nowarn") - - # Add the the testbench top-level unit last as coverage is - # only collected for the top-level unit specified last - vsim_flags += ["-lib", - config.library_name, - config.entity_name] - - if config.architecture_name is not None: - vsim_flags.append(config.architecture_name) - - tcl = """ -proc vunit_load {{}} {{ - # Make the variable 'aldec' visible; otherwise, the Matlab interface - # is broken because vsim does not find the library aldec_matlab_cosim. - global aldec - # Make the variable 'LICENSE_QUEUE' visible (if set); otherwise vsim - # will not wait for simulation licenses. - global LICENSE_QUEUE - - set vsim_failed [catch {{ - eval vsim {{{vsim_flags}}} - }}] - - if {{${{vsim_failed}}}} {{ - return true - }} - - if {{[_vunit_source_init_files_after_load]}} {{ - return true - }} - - vhdlassert.break {break_level} - vhdlassert.break -builtin {break_level} - - return false -}} -""".format(vsim_flags=" ".join(vsim_flags), - break_level=config.vhdl_assert_stop_level) - - return tcl - - def _vsim_extra_args(self, config): - """ - Determine vsim_extra_args - """ - vsim_extra_args = [] - vsim_extra_args = config.sim_options.get("rivierapro.vsim_flags", - vsim_extra_args) - - if self._gui: - vsim_extra_args = config.sim_options.get("rivierapro.vsim_flags.gui", - vsim_extra_args) - - return " ".join(vsim_extra_args) - - @staticmethod - def _create_run_function(): - """ - Create the vunit_run function to run the test bench - """ - return """ -proc _vunit_run_failure {} { - catch { - # tb command can fail when error comes from pli - echo "Stack trace result from 'bt' command" - bt - } -} - -proc _vunit_run {} { - proc on_break {} { - resume - } - onbreak {on_break} - - run -all -} - -proc _vunit_sim_restart {} { - restart -} -""" - - def merge_coverage(self, file_name, args=None): - """ - Merge coverage from all test cases, - """ - - if self._persistent_shell is not None: - # Teardown to ensure acdb file was written. - self._persistent_shell.teardown() - - merge_command = "acdb merge" - - for coverage_file in self._coverage_files: - if file_exists(coverage_file): - merge_command += " -i {%s}" % coverage_file.replace('\\', '/') - else: - LOGGER.warning("Missing coverage file: %s", coverage_file) - - if args is not None: - merge_command += " " + " ".join("{%s}" % arg for arg in args) - - merge_command += " -o {%s}" % file_name.replace('\\', '/') - - merge_script_name = join(self._output_path, "acdb_merge.tcl") - with open(merge_script_name, "w") as fptr: - fptr.write(merge_command + "\n") - - vcover_cmd = [join(self._prefix, 'vsim'), '-c', '-do', - 'source %s; quit;' % merge_script_name.replace('\\', '/')] - - print("Merging coverage files into %s..." % file_name) - vcover_merge_process = Process(vcover_cmd, - env=self.get_env()) - vcover_merge_process.consume_output() - print("Done merging coverage files") - - -def format_generic(value): - """ - Generic values with space in them need to be quoted - """ - value_str = str(value) - if " " in value_str: - return '"%s"' % value_str - return value_str - - -class VersionConsumer(object): - """ - Consume version information - """ - def __init__(self): - self.year = None - self.month = None - - _version_re = re.compile(r'(?P\d+)\.(?P\d+)\.\d+') - - def __call__(self, line): - match = self._version_re.search(line) - if match is not None: - self.year = int(match.group('year')) - self.month = int(match.group('month')) - return True +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Interface towards Aldec Riviera Pro +""" + + +from __future__ import print_function + +from os.path import join, dirname, abspath +import os +import re +import logging +from vunit.ostools import Process, file_exists +from vunit.simulator_interface import (SimulatorInterface, + ListOfStringOption, + StringOption) +from vunit.exceptions import CompileError +from vunit.vsim_simulator_mixin import (VsimSimulatorMixin, + fix_path) + +LOGGER = logging.getLogger(__name__) + + +class RivieraProInterface(VsimSimulatorMixin, SimulatorInterface): + """ + Riviera Pro interface + """ + + name = "rivierapro" + supports_gui_flag = True + package_users_depend_on_bodies = True + + compile_options = [ + ListOfStringOption("rivierapro.vcom_flags"), + ListOfStringOption("rivierapro.vlog_flags"), + ] + + sim_options = [ + ListOfStringOption("rivierapro.vsim_flags"), + ListOfStringOption("rivierapro.vsim_flags.gui"), + ListOfStringOption("rivierapro.init_files.after_load"), + StringOption("rivierapro.init_file.gui"), + ] + + @classmethod + def from_args(cls, args, output_path, **kwargs): + """ + Create new instance from command line arguments object + """ + persistent = not (args.unique_sim or args.gui) + + return cls(prefix=cls.find_prefix(), + output_path=output_path, + persistent=persistent, + gui=args.gui) + + @classmethod + def find_prefix_from_path(cls): + """ + Find RivieraPro toolchain. + + Must have vsim and vsimsa binaries but no avhdl.exe + """ + def no_avhdl(path): + return not file_exists(join(path, "avhdl.exe")) + return cls.find_toolchain(["vsim", + "vsimsa"], + constraints=[no_avhdl]) + + @classmethod + def get_osvvm_coverage_api(cls): + """ + Returns simulator name when OSVVM coverage API is supported, None otherwise. + """ + proc = Process([join(cls.find_prefix(), 'vcom'), '-version'], + env=cls.get_env()) + consumer = VersionConsumer() + proc.consume_output(consumer) + if consumer.year is not None: + if (consumer.year == 2016 and consumer.month >= 10) or (consumer.year > 2016): + return cls.name + + return None + + @classmethod + def supports_vhdl_package_generics(cls): + """ + Returns True when this simulator supports VHDL package generics + """ + return True + + def __init__(self, prefix, output_path, persistent=False, gui=False): + SimulatorInterface.__init__(self, output_path, gui) + VsimSimulatorMixin.__init__(self, prefix, persistent, + sim_cfg_file_name=join(output_path, "library.cfg")) + self._create_library_cfg() + self._libraries = [] + self._coverage_files = set() + + def add_simulator_specific(self, project): + """ + Add builtin (global) libraries + """ + built_in_libraries = self._get_mapped_libraries(self._builtin_library_cfg) + + for library_name in built_in_libraries: + # A user might shadow a built in library with their own version + if not project.has_library(library_name): + project.add_builtin_library(library_name) + + def setup_library_mapping(self, project): + """ + Setup library mapping + """ + mapped_libraries = self._get_mapped_libraries(self._sim_cfg_file_name) + for library in project.get_libraries(): + self._libraries.append(library) + self.create_library(library.name, library.directory, mapped_libraries) + + def compile_source_file_command(self, source_file): + """ + Returns the command to compile a single source_file + """ + if source_file.is_vhdl: + return self.compile_vhdl_file_command(source_file) + + if source_file.is_any_verilog: + return self.compile_verilog_file_command(source_file) + + LOGGER.error("Unknown file type: %s", source_file.file_type) + raise CompileError + + def compile_vhdl_file_command(self, source_file): + """ + Returns the command to compile a VHDL file + """ + return ([join(self._prefix, 'vcom'), '-quiet', '-j', dirname(self._sim_cfg_file_name)] + + source_file.compile_options.get("rivierapro.vcom_flags", []) + + ['-' + source_file.get_vhdl_standard(), '-work', source_file.library.name, source_file.name]) + + def compile_verilog_file_command(self, source_file): + """ + Returns the command to compile a Verilog file + """ + args = [join(self._prefix, 'vlog'), '-quiet', '-lc', self._sim_cfg_file_name] + if source_file.is_system_verilog: + args += ['-sv2k12'] + args += source_file.compile_options.get("rivierapro.vlog_flags", []) + args += ['-work', source_file.library.name, source_file.name] + for library in self._libraries: + args += ["-l", library.name] + for include_dir in source_file.include_dirs: + args += ["+incdir+%s" % include_dir] + for key, value in source_file.defines.items(): + args += ["+define+%s=%s" % (key, value)] + return args + + def create_library(self, library_name, path, mapped_libraries=None): + """ + Create and map a library_name to path + """ + mapped_libraries = mapped_libraries if mapped_libraries is not None else {} + + if not file_exists(dirname(abspath(path))): + os.makedirs(dirname(abspath(path))) + + if not file_exists(path): + proc = Process([join(self._prefix, 'vlib'), library_name, path], + cwd=dirname(self._sim_cfg_file_name), + env=self.get_env()) + proc.consume_output(callback=None) + + if library_name in mapped_libraries and mapped_libraries[library_name] == path: + return + + proc = Process([join(self._prefix, 'vmap'), library_name, path], + cwd=dirname(self._sim_cfg_file_name), + env=self.get_env()) + proc.consume_output(callback=None) + + def _create_library_cfg(self): + """ + Create the library.cfg file if it does not exist + """ + if file_exists(self._sim_cfg_file_name): + return + + with open(self._sim_cfg_file_name, "w") as ofile: + ofile.write('$INCLUDE = "%s"\n' % self._builtin_library_cfg) + + @property + def _builtin_library_cfg(self): + return join(self._prefix, "..", "vlib", "library.cfg") + + _library_re = re.compile(r'([a-zA-Z_0-9]+)\s=\s(.*)') + + def _get_mapped_libraries(self, library_cfg_file): + """ + Get mapped libraries by running vlist on the working directory + """ + lines = [] + proc = Process([join(self._prefix, 'vlist')], cwd=dirname(library_cfg_file)) + proc.consume_output(callback=lines.append) + + libraries = {} + for line in lines: + match = self._library_re.match(line) + if match is None: + continue + key = match.group(1) + value = match.group(2) + libraries[key] = abspath(join(dirname(library_cfg_file), dirname(value))) + return libraries + + def _create_load_function(self, + test_suite_name, # pylint: disable=unused-argument + config, output_path): + """ + Create the vunit_load TCL function that runs the vsim command and loads the design + """ + set_generic_str = " ".join(('-g/%s/%s=%s' % (config.entity_name, + name, + format_generic(value)) + for name, value in config.generics.items())) + pli_str = " ".join("-pli \"%s\"" % fix_path(name) for name in config.sim_options.get('pli', [])) + + vsim_flags = ["-dataset {%s}" % fix_path(join(output_path, "dataset.asdb")), + pli_str, + set_generic_str] + + if config.sim_options.get("enable_coverage", False): + coverage_file_path = join(output_path, "coverage.acdb") + self._coverage_files.add(coverage_file_path) + vsim_flags += ["-acdb_file {%s}" % coverage_file_path] + + vsim_flags += [self._vsim_extra_args(config)] + + if config.sim_options.get("disable_ieee_warnings", False): + vsim_flags.append("-ieee_nowarn") + + # Add the the testbench top-level unit last as coverage is + # only collected for the top-level unit specified last + vsim_flags += ["-lib", + config.library_name, + config.entity_name] + + if config.architecture_name is not None: + vsim_flags.append(config.architecture_name) + + tcl = """ +proc vunit_load {{}} {{ + # Make the variable 'aldec' visible; otherwise, the Matlab interface + # is broken because vsim does not find the library aldec_matlab_cosim. + global aldec + # Make the variable 'LICENSE_QUEUE' visible (if set); otherwise vsim + # will not wait for simulation licenses. + global LICENSE_QUEUE + + set vsim_failed [catch {{ + eval vsim {{{vsim_flags}}} + }}] + + if {{${{vsim_failed}}}} {{ + return true + }} + + if {{[_vunit_source_init_files_after_load]}} {{ + return true + }} + + vhdlassert.break {break_level} + vhdlassert.break -builtin {break_level} + + return false +}} +""".format(vsim_flags=" ".join(vsim_flags), + break_level=config.vhdl_assert_stop_level) + + return tcl + + def _vsim_extra_args(self, config): + """ + Determine vsim_extra_args + """ + vsim_extra_args = [] + vsim_extra_args = config.sim_options.get("rivierapro.vsim_flags", + vsim_extra_args) + + if self._gui: + vsim_extra_args = config.sim_options.get("rivierapro.vsim_flags.gui", + vsim_extra_args) + + return " ".join(vsim_extra_args) + + @staticmethod + def _create_run_function(): + """ + Create the vunit_run function to run the test bench + """ + return """ +proc _vunit_run_failure {} { + catch { + # tb command can fail when error comes from pli + echo "Stack trace result from 'bt' command" + bt + } +} + +proc _vunit_run {} { + proc on_break {} { + resume + } + onbreak {on_break} + + run -all +} + +proc _vunit_sim_restart {} { + restart +} +""" + + def merge_coverage(self, file_name, args=None): + """ + Merge coverage from all test cases, + """ + + if self._persistent_shell is not None: + # Teardown to ensure acdb file was written. + self._persistent_shell.teardown() + + merge_command = "acdb merge" + + for coverage_file in self._coverage_files: + if file_exists(coverage_file): + merge_command += " -i {%s}" % coverage_file.replace('\\', '/') + else: + LOGGER.warning("Missing coverage file: %s", coverage_file) + + if args is not None: + merge_command += " " + " ".join("{%s}" % arg for arg in args) + + merge_command += " -o {%s}" % file_name.replace('\\', '/') + + merge_script_name = join(self._output_path, "acdb_merge.tcl") + with open(merge_script_name, "w") as fptr: + fptr.write(merge_command + "\n") + + vcover_cmd = [join(self._prefix, 'vsim'), '-c', '-do', + 'source %s; quit;' % merge_script_name.replace('\\', '/')] + + print("Merging coverage files into %s..." % file_name) + vcover_merge_process = Process(vcover_cmd, + env=self.get_env()) + vcover_merge_process.consume_output() + print("Done merging coverage files") + + +def format_generic(value): + """ + Generic values with space in them need to be quoted + """ + value_str = str(value) + if " " in value_str: + return '"%s"' % value_str + return value_str + + +class VersionConsumer(object): + """ + Consume version information + """ + def __init__(self): + self.year = None + self.month = None + + _version_re = re.compile(r'(?P\d+)\.(?P\d+)\.\d+') + + def __call__(self, line): + match = self._version_re.search(line) + if match is not None: + self.year = int(match.group('year')) + self.month = int(match.group('month')) + return True diff --git a/vunit/simulator_factory.py b/vunit/simulator_factory.py index b0d71e7bb..41d0a1cf0 100644 --- a/vunit/simulator_factory.py +++ b/vunit/simulator_factory.py @@ -1,154 +1,154 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Create simulator instances -""" - -import os -from vunit.modelsim_interface import ModelSimInterface -from vunit.activehdl_interface import ActiveHDLInterface -from vunit.rivierapro_interface import RivieraProInterface -from vunit.ghdl_interface import GHDLInterface -from vunit.incisive_interface import IncisiveInterface -from vunit.simulator_interface import (BooleanOption, - ListOfStringOption, - VHDLAssertLevelOption) - - -class SimulatorFactory(object): - """ - Create simulator instances - """ - - @staticmethod - def supported_simulators(): - """ - Return a list of supported simulator classes - """ - return [ModelSimInterface, - RivieraProInterface, - ActiveHDLInterface, - GHDLInterface, - IncisiveInterface] - - def _extract_compile_options(self): - """ - Return all supported compile options - """ - result = dict() - for sim_class in self.supported_simulators(): - for opt in sim_class.compile_options: - assert hasattr(opt, "name") - assert hasattr(opt, "validate") - assert opt.name.startswith(sim_class.name + ".") - assert opt.name not in result - result[opt.name] = opt - return result - - def _extract_sim_options(self): - """ - Return all supported sim options - """ - result = dict((opt.name, opt) for opt in - [VHDLAssertLevelOption(), - BooleanOption("disable_ieee_warnings"), - BooleanOption("enable_coverage"), - ListOfStringOption("pli")]) - - for sim_class in self.supported_simulators(): - for opt in sim_class.sim_options: - assert hasattr(opt, "name") - assert hasattr(opt, "validate") - assert opt.name.startswith(sim_class.name + ".") - assert opt.name not in result - result[opt.name] = opt - - return result - - def check_sim_option(self, name, value): - """ - Check that sim_option has legal name and value - """ - known_options = sorted(list(self._sim_options.keys())) - - if name not in self._sim_options: - raise ValueError("Unknown sim_option %r, expected one of %r" % - (name, known_options)) - - self._sim_options[name].validate(value) - - def check_compile_option_name(self, name): - """ - Check that the compile option is valid - """ - known_options = sorted(list(self._compile_options.keys())) - if name not in known_options: - raise ValueError("Unknown compile_option %r, expected one of %r" % - (name, known_options)) - - def check_compile_option(self, name, value): - """ - Check that the compile option is valid - """ - self.check_compile_option_name(name) - self._compile_options[name].validate(value) - - def select_simulator(self): - """ - Select simulator class, either from VUNIT_SIMULATOR environment variable - or the first available - """ - available_simulators = self._detect_available_simulators() - name_mapping = {simulator_class.name: simulator_class for simulator_class in self.supported_simulators()} - if not available_simulators: - return None - - environ_name = "VUNIT_SIMULATOR" - if environ_name in os.environ: - simulator_name = os.environ[environ_name] - if simulator_name not in name_mapping: - raise RuntimeError( - ("Simulator from " + environ_name + " environment variable %r is not supported. " - "Supported simulators are %r") - % (simulator_name, name_mapping.keys())) - simulator_class = name_mapping[simulator_name] - else: - simulator_class = available_simulators[0] - - return simulator_class - - def add_arguments(self, parser): - """ - Add command line arguments to parser - """ - - parser.add_argument('-g', '--gui', - action="store_true", - default=False, - help=("Open test case(s) in simulator gui with top level pre loaded")) - - for sim in self.supported_simulators(): - sim.add_arguments(parser) - - def __init__(self): - self._compile_options = self._extract_compile_options() - self._sim_options = self._extract_sim_options() - - def _detect_available_simulators(self): - """ - Detect available simulators and return a list - """ - return [simulator_class - for simulator_class in self.supported_simulators() - if simulator_class.is_available()] - - @property - def has_simulator(self): - return bool(self._detect_available_simulators()) - - -SIMULATOR_FACTORY = SimulatorFactory() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Create simulator instances +""" + +import os +from vunit.modelsim_interface import ModelSimInterface +from vunit.activehdl_interface import ActiveHDLInterface +from vunit.rivierapro_interface import RivieraProInterface +from vunit.ghdl_interface import GHDLInterface +from vunit.incisive_interface import IncisiveInterface +from vunit.simulator_interface import (BooleanOption, + ListOfStringOption, + VHDLAssertLevelOption) + + +class SimulatorFactory(object): + """ + Create simulator instances + """ + + @staticmethod + def supported_simulators(): + """ + Return a list of supported simulator classes + """ + return [ModelSimInterface, + RivieraProInterface, + ActiveHDLInterface, + GHDLInterface, + IncisiveInterface] + + def _extract_compile_options(self): + """ + Return all supported compile options + """ + result = dict() + for sim_class in self.supported_simulators(): + for opt in sim_class.compile_options: + assert hasattr(opt, "name") + assert hasattr(opt, "validate") + assert opt.name.startswith(sim_class.name + ".") + assert opt.name not in result + result[opt.name] = opt + return result + + def _extract_sim_options(self): + """ + Return all supported sim options + """ + result = dict((opt.name, opt) for opt in + [VHDLAssertLevelOption(), + BooleanOption("disable_ieee_warnings"), + BooleanOption("enable_coverage"), + ListOfStringOption("pli")]) + + for sim_class in self.supported_simulators(): + for opt in sim_class.sim_options: + assert hasattr(opt, "name") + assert hasattr(opt, "validate") + assert opt.name.startswith(sim_class.name + ".") + assert opt.name not in result + result[opt.name] = opt + + return result + + def check_sim_option(self, name, value): + """ + Check that sim_option has legal name and value + """ + known_options = sorted(list(self._sim_options.keys())) + + if name not in self._sim_options: + raise ValueError("Unknown sim_option %r, expected one of %r" % + (name, known_options)) + + self._sim_options[name].validate(value) + + def check_compile_option_name(self, name): + """ + Check that the compile option is valid + """ + known_options = sorted(list(self._compile_options.keys())) + if name not in known_options: + raise ValueError("Unknown compile_option %r, expected one of %r" % + (name, known_options)) + + def check_compile_option(self, name, value): + """ + Check that the compile option is valid + """ + self.check_compile_option_name(name) + self._compile_options[name].validate(value) + + def select_simulator(self): + """ + Select simulator class, either from VUNIT_SIMULATOR environment variable + or the first available + """ + available_simulators = self._detect_available_simulators() + name_mapping = {simulator_class.name: simulator_class for simulator_class in self.supported_simulators()} + if not available_simulators: + return None + + environ_name = "VUNIT_SIMULATOR" + if environ_name in os.environ: + simulator_name = os.environ[environ_name] + if simulator_name not in name_mapping: + raise RuntimeError( + ("Simulator from " + environ_name + " environment variable %r is not supported. " + "Supported simulators are %r") + % (simulator_name, name_mapping.keys())) + simulator_class = name_mapping[simulator_name] + else: + simulator_class = available_simulators[0] + + return simulator_class + + def add_arguments(self, parser): + """ + Add command line arguments to parser + """ + + parser.add_argument('-g', '--gui', + action="store_true", + default=False, + help=("Open test case(s) in simulator gui with top level pre loaded")) + + for sim in self.supported_simulators(): + sim.add_arguments(parser) + + def __init__(self): + self._compile_options = self._extract_compile_options() + self._sim_options = self._extract_sim_options() + + def _detect_available_simulators(self): + """ + Detect available simulators and return a list + """ + return [simulator_class + for simulator_class in self.supported_simulators() + if simulator_class.is_available()] + + @property + def has_simulator(self): + return bool(self._detect_available_simulators()) + + +SIMULATOR_FACTORY = SimulatorFactory() diff --git a/vunit/simulator_interface.py b/vunit/simulator_interface.py index b456490cd..c1c38eeec 100644 --- a/vunit/simulator_interface.py +++ b/vunit/simulator_interface.py @@ -1,390 +1,390 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Generic simulator interface -""" - -from __future__ import print_function -import sys -import os -import subprocess -from vunit.ostools import Process, simplify_path -from vunit.exceptions import CompileError -from vunit.color_printer import NO_COLOR_PRINTER - - -class SimulatorInterface(object): # pylint: disable=too-many-public-methods - """ - Generic simulator interface - """ - - name = None - supports_gui_flag = False - package_users_depend_on_bodies = False - compile_options = [] - sim_options = [] - - # True if simulator supports ANSI colors in GUI mode - supports_colors_in_gui = False - - def __init__(self, output_path, gui): - self._output_path = output_path - self._gui = gui - - @property - def output_path(self): - return self._output_path - - @property - def use_color(self): - return (not self._gui) or self.supports_colors_in_gui - - @staticmethod - def add_arguments(parser): - """ - Add command line arguments - """ - - @staticmethod - def supports_vhdl_2008_contexts(): - """ - Returns True when this simulator supports VHDL 2008 contexts - """ - return True - - @staticmethod - def find_executable(executable): - """ - Return a list of all executables found in PATH - """ - path = os.environ.get('PATH', None) - if path is None: - return [] - - paths = path.split(os.pathsep) - _, ext = os.path.splitext(executable) - - if (sys.platform == 'win32' or os.name == 'os2') and (ext != '.exe'): - executable = executable + '.exe' - - result = [] - if isfile(executable): - result.append(executable) - - for prefix in paths: - file_name = os.path.join(prefix, executable) - if isfile(file_name): - # the file exists, we have a shot at spawn working - result.append(file_name) - return result - - @classmethod - def find_prefix(cls): - """ - Find prefix by looking at VUNIT__PATH environment variable - """ - prefix = os.environ.get("VUNIT_" + cls.name.upper() + "_PATH", None) - if prefix is not None: - return prefix - return cls.find_prefix_from_path() - - @classmethod - def find_prefix_from_path(cls): - """ - Find simulator toolchain prefix from PATH environment variable - """ - - @classmethod - def is_available(cls): - """ - Returns True if simulator is available - """ - return cls.find_prefix() is not None - - @classmethod - def find_toolchain(cls, executables, constraints=None): - """ - Find the first path prefix containing all executables - """ - constraints = [] if constraints is None else constraints - - if not executables: - return None - - all_paths = [[os.path.abspath(os.path.dirname(executables)) - for executables in cls.find_executable(name)] - for name in executables] - - for path0 in all_paths[0]: - if all([path0 in paths for paths in all_paths] - + [constraint(path0) for constraint in constraints]): - return path0 - return None - - @classmethod - def get_osvvm_coverage_api(cls): - """ - Returns simulator name when OSVVM coverage API is supported, None otherwise. - """ - - @classmethod - def supports_vhdl_package_generics(cls): - """ - Returns True when this simulator supports VHDL package generics - """ - return False - - @staticmethod - def has_valid_exit_code(): - """ - Return if the simulation should fail with nonzero exit codes - """ - return False - - @staticmethod - def supports_vhpi(): - """ - Return if the simulator supports VHPI - """ - return False - - def merge_coverage(self, file_name, args): # pylint: disable=unused-argument, no-self-use - """ - Hook for simulator interface to creating coverage reports - """ - raise RuntimeError("This simulator does not support merging coverage") - - def add_simulator_specific(self, project): - """ - Hook for the simulator interface to add simulator specific things to the project - """ - - def compile_project(self, project, printer=NO_COLOR_PRINTER, continue_on_error=False): - """ - Compile the project - """ - self.add_simulator_specific(project) - self.setup_library_mapping(project) - self.compile_source_files(project, printer, continue_on_error) - - def simulate(self, output_path, test_suite_name, config, elaborate_only): - """ - Simulate - """ - - def setup_library_mapping(self, project): - """ - Implemented by specific simulators - """ - - def __compile_source_file(self, source_file, printer): - """ - Compiles a single source file and prints status information - """ - try: - command = self.compile_source_file_command(source_file) - except CompileError: - command = None - printer.write("failed", fg="ri") - printer.write("\n") - printer.write("File type not supported by %s simulator\n" % (self.name)) - - return False - - try: - output = check_output(command, - env=self.get_env()) - printer.write("passed", fg="gi") - printer.write("\n") - printer.write(output) - - except subprocess.CalledProcessError as err: - printer.write("failed", fg="ri") - printer.write("\n") - printer.write("=== Command used: ===\n%s\n" - % (subprocess.list2cmdline(command))) - printer.write("\n") - printer.write("=== Command output: ===\n%s\n" % err.output) - - return False - - return True - - def compile_source_files(self, project, printer=NO_COLOR_PRINTER, continue_on_error=False): - """ - Use compile_source_file_command to compile all source_files - """ - dependency_graph = project.create_dependency_graph() - failures = [] - source_files = project.get_files_in_compile_order(dependency_graph=dependency_graph) - source_files_to_skip = set() - - max_library_name = 0 - max_source_file_name = 0 - if source_files: - max_library_name = max(len(source_file.library.name) for source_file in source_files) - max_source_file_name = max(len(simplify_path(source_file.name)) for source_file in source_files) - - for source_file in source_files: - printer.write( - 'Compiling into %s %s ' % ( - (source_file.library.name + ":").ljust(max_library_name + 1), - simplify_path(source_file.name).ljust(max_source_file_name))) - sys.stdout.flush() - - if source_file in source_files_to_skip: - printer.write("skipped", fg="rgi") - printer.write("\n") - continue - - if self.__compile_source_file(source_file, printer): - project.update(source_file) - else: - source_files_to_skip.update(dependency_graph.get_dependent([source_file])) - failures.append(source_file) - - if not continue_on_error: - break - - if failures: - printer.write("Compile failed\n", fg='ri') - raise CompileError - - if source_files: - printer.write("Compile passed\n", fg='gi') - else: - printer.write("Re-compile not needed\n") - - def compile_source_file_command(self, source_file): # pylint: disable=unused-argument - raise NotImplementedError - - @staticmethod - def get_env(): - """ - Allows inheriting classes to overload this to modify environment variables. Return None for default environment - """ - - -def isfile(file_name): - """ - Case insensitive os.path.isfile - """ - if not os.path.isfile(file_name): - return False - - return os.path.basename(file_name) in os.listdir(os.path.dirname(file_name)) - - -def run_command(command, cwd=None, env=None): - """ - Run a command - """ - try: - proc = Process(command, cwd=cwd, env=env) - proc.consume_output() - return True - except Process.NonZeroExitCode: - pass - return False - - -def check_output(command, env=None): - """ - Wrapper arround subprocess.check_output - """ - try: - output = subprocess.check_output(command, - env=env, - stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as err: - err.output = err.output.decode("utf-8") - raise err - return output.decode("utf-8") - - -class Option(object): - """ - A compile or sim option - """ - - def __init__(self, name): - self._name = name - - @property - def name(self): - return self._name - - def validate(self, value): - pass - - -class BooleanOption(Option): - """ - Must be a boolean - """ - - def validate(self, value): - if value not in (True, False): - raise ValueError("Option %r must be a boolean. Got %r" - % (self.name, value)) - - -class StringOption(Option): - """ - Must be a string - """ - - def validate(self, value): - if not is_string_not_iterable(value): - raise ValueError("Option %r must be a string. Got %r" - % (self.name, value)) - - -class ListOfStringOption(Option): - """ - Must be a list of strings - """ - def validate(self, value): - def fail(): - raise ValueError("Option %r must be a list of strings. Got %r" - % (self.name, value)) - - if is_string_not_iterable(value): - fail() - - try: - for elem in value: - if not is_string_not_iterable(elem): - fail() - except TypeError: - fail() - - -class VHDLAssertLevelOption(Option): - """ - VHDL assert level - """ - - _legal_values = ("warning", "error", "failure") - - def __init__(self): - Option.__init__(self, "vhdl_assert_stop_level") - - def validate(self, value): - if value not in self._legal_values: - raise ValueError("Option %r must be one of %s. Got %r" - % (self.name, self._legal_values, value)) - - -def is_string_not_iterable(value): - """ - Returns True if value is a string and not another iterable - """ - if sys.version_info.major == 3: - return isinstance(value, str) - - return isinstance(value, (str, unicode)) # pylint: disable=undefined-variable +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Generic simulator interface +""" + +from __future__ import print_function +import sys +import os +import subprocess +from vunit.ostools import Process, simplify_path +from vunit.exceptions import CompileError +from vunit.color_printer import NO_COLOR_PRINTER + + +class SimulatorInterface(object): # pylint: disable=too-many-public-methods + """ + Generic simulator interface + """ + + name = None + supports_gui_flag = False + package_users_depend_on_bodies = False + compile_options = [] + sim_options = [] + + # True if simulator supports ANSI colors in GUI mode + supports_colors_in_gui = False + + def __init__(self, output_path, gui): + self._output_path = output_path + self._gui = gui + + @property + def output_path(self): + return self._output_path + + @property + def use_color(self): + return (not self._gui) or self.supports_colors_in_gui + + @staticmethod + def add_arguments(parser): + """ + Add command line arguments + """ + + @staticmethod + def supports_vhdl_2008_contexts(): + """ + Returns True when this simulator supports VHDL 2008 contexts + """ + return True + + @staticmethod + def find_executable(executable): + """ + Return a list of all executables found in PATH + """ + path = os.environ.get('PATH', None) + if path is None: + return [] + + paths = path.split(os.pathsep) + _, ext = os.path.splitext(executable) + + if (sys.platform == 'win32' or os.name == 'os2') and (ext != '.exe'): + executable = executable + '.exe' + + result = [] + if isfile(executable): + result.append(executable) + + for prefix in paths: + file_name = os.path.join(prefix, executable) + if isfile(file_name): + # the file exists, we have a shot at spawn working + result.append(file_name) + return result + + @classmethod + def find_prefix(cls): + """ + Find prefix by looking at VUNIT__PATH environment variable + """ + prefix = os.environ.get("VUNIT_" + cls.name.upper() + "_PATH", None) + if prefix is not None: + return prefix + return cls.find_prefix_from_path() + + @classmethod + def find_prefix_from_path(cls): + """ + Find simulator toolchain prefix from PATH environment variable + """ + + @classmethod + def is_available(cls): + """ + Returns True if simulator is available + """ + return cls.find_prefix() is not None + + @classmethod + def find_toolchain(cls, executables, constraints=None): + """ + Find the first path prefix containing all executables + """ + constraints = [] if constraints is None else constraints + + if not executables: + return None + + all_paths = [[os.path.abspath(os.path.dirname(executables)) + for executables in cls.find_executable(name)] + for name in executables] + + for path0 in all_paths[0]: + if all([path0 in paths for paths in all_paths] + + [constraint(path0) for constraint in constraints]): + return path0 + return None + + @classmethod + def get_osvvm_coverage_api(cls): + """ + Returns simulator name when OSVVM coverage API is supported, None otherwise. + """ + + @classmethod + def supports_vhdl_package_generics(cls): + """ + Returns True when this simulator supports VHDL package generics + """ + return False + + @staticmethod + def has_valid_exit_code(): + """ + Return if the simulation should fail with nonzero exit codes + """ + return False + + @staticmethod + def supports_vhpi(): + """ + Return if the simulator supports VHPI + """ + return False + + def merge_coverage(self, file_name, args): # pylint: disable=unused-argument, no-self-use + """ + Hook for simulator interface to creating coverage reports + """ + raise RuntimeError("This simulator does not support merging coverage") + + def add_simulator_specific(self, project): + """ + Hook for the simulator interface to add simulator specific things to the project + """ + + def compile_project(self, project, printer=NO_COLOR_PRINTER, continue_on_error=False): + """ + Compile the project + """ + self.add_simulator_specific(project) + self.setup_library_mapping(project) + self.compile_source_files(project, printer, continue_on_error) + + def simulate(self, output_path, test_suite_name, config, elaborate_only): + """ + Simulate + """ + + def setup_library_mapping(self, project): + """ + Implemented by specific simulators + """ + + def __compile_source_file(self, source_file, printer): + """ + Compiles a single source file and prints status information + """ + try: + command = self.compile_source_file_command(source_file) + except CompileError: + command = None + printer.write("failed", fg="ri") + printer.write("\n") + printer.write("File type not supported by %s simulator\n" % (self.name)) + + return False + + try: + output = check_output(command, + env=self.get_env()) + printer.write("passed", fg="gi") + printer.write("\n") + printer.write(output) + + except subprocess.CalledProcessError as err: + printer.write("failed", fg="ri") + printer.write("\n") + printer.write("=== Command used: ===\n%s\n" + % (subprocess.list2cmdline(command))) + printer.write("\n") + printer.write("=== Command output: ===\n%s\n" % err.output) + + return False + + return True + + def compile_source_files(self, project, printer=NO_COLOR_PRINTER, continue_on_error=False): + """ + Use compile_source_file_command to compile all source_files + """ + dependency_graph = project.create_dependency_graph() + failures = [] + source_files = project.get_files_in_compile_order(dependency_graph=dependency_graph) + source_files_to_skip = set() + + max_library_name = 0 + max_source_file_name = 0 + if source_files: + max_library_name = max(len(source_file.library.name) for source_file in source_files) + max_source_file_name = max(len(simplify_path(source_file.name)) for source_file in source_files) + + for source_file in source_files: + printer.write( + 'Compiling into %s %s ' % ( + (source_file.library.name + ":").ljust(max_library_name + 1), + simplify_path(source_file.name).ljust(max_source_file_name))) + sys.stdout.flush() + + if source_file in source_files_to_skip: + printer.write("skipped", fg="rgi") + printer.write("\n") + continue + + if self.__compile_source_file(source_file, printer): + project.update(source_file) + else: + source_files_to_skip.update(dependency_graph.get_dependent([source_file])) + failures.append(source_file) + + if not continue_on_error: + break + + if failures: + printer.write("Compile failed\n", fg='ri') + raise CompileError + + if source_files: + printer.write("Compile passed\n", fg='gi') + else: + printer.write("Re-compile not needed\n") + + def compile_source_file_command(self, source_file): # pylint: disable=unused-argument + raise NotImplementedError + + @staticmethod + def get_env(): + """ + Allows inheriting classes to overload this to modify environment variables. Return None for default environment + """ + + +def isfile(file_name): + """ + Case insensitive os.path.isfile + """ + if not os.path.isfile(file_name): + return False + + return os.path.basename(file_name) in os.listdir(os.path.dirname(file_name)) + + +def run_command(command, cwd=None, env=None): + """ + Run a command + """ + try: + proc = Process(command, cwd=cwd, env=env) + proc.consume_output() + return True + except Process.NonZeroExitCode: + pass + return False + + +def check_output(command, env=None): + """ + Wrapper arround subprocess.check_output + """ + try: + output = subprocess.check_output(command, + env=env, + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as err: + err.output = err.output.decode("utf-8") + raise err + return output.decode("utf-8") + + +class Option(object): + """ + A compile or sim option + """ + + def __init__(self, name): + self._name = name + + @property + def name(self): + return self._name + + def validate(self, value): + pass + + +class BooleanOption(Option): + """ + Must be a boolean + """ + + def validate(self, value): + if value not in (True, False): + raise ValueError("Option %r must be a boolean. Got %r" + % (self.name, value)) + + +class StringOption(Option): + """ + Must be a string + """ + + def validate(self, value): + if not is_string_not_iterable(value): + raise ValueError("Option %r must be a string. Got %r" + % (self.name, value)) + + +class ListOfStringOption(Option): + """ + Must be a list of strings + """ + def validate(self, value): + def fail(): + raise ValueError("Option %r must be a list of strings. Got %r" + % (self.name, value)) + + if is_string_not_iterable(value): + fail() + + try: + for elem in value: + if not is_string_not_iterable(elem): + fail() + except TypeError: + fail() + + +class VHDLAssertLevelOption(Option): + """ + VHDL assert level + """ + + _legal_values = ("warning", "error", "failure") + + def __init__(self): + Option.__init__(self, "vhdl_assert_stop_level") + + def validate(self, value): + if value not in self._legal_values: + raise ValueError("Option %r must be one of %s. Got %r" + % (self.name, self._legal_values, value)) + + +def is_string_not_iterable(value): + """ + Returns True if value is a string and not another iterable + """ + if sys.version_info.major == 3: + return isinstance(value, str) + + return isinstance(value, (str, unicode)) # pylint: disable=undefined-variable diff --git a/vunit/tcl_read_eval_loop.tcl b/vunit/tcl_read_eval_loop.tcl index cf24ec62f..5c463ed72 100644 --- a/vunit/tcl_read_eval_loop.tcl +++ b/vunit/tcl_read_eval_loop.tcl @@ -1,12 +1,12 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2016, Lars Asplund lars.anders.asplund@gmail.com - -while {1} { - set line [gets stdin] - if {[catch {eval $line} error_msg]} { - puts "$line - $error_msg" - } -} +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2016, Lars Asplund lars.anders.asplund@gmail.com + +while {1} { + set line [gets stdin] + if {[catch {eval $line} error_msg]} { + puts "$line - $error_msg" + } +} diff --git a/vunit/test/__init__.py b/vunit/test/__init__.py index a66885c1d..17b27373b 100644 --- a/vunit/test/__init__.py +++ b/vunit/test/__init__.py @@ -1,5 +1,5 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com diff --git a/vunit/test/acceptance/__init__.py b/vunit/test/acceptance/__init__.py index a66885c1d..17b27373b 100644 --- a/vunit/test/acceptance/__init__.py +++ b/vunit/test/acceptance/__init__.py @@ -1,5 +1,5 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com diff --git a/vunit/test/acceptance/artificial/verilog/other_file_tests.sv b/vunit/test/acceptance/artificial/verilog/other_file_tests.sv index b318eff42..e3057393c 100644 --- a/vunit/test/acceptance/artificial/verilog/other_file_tests.sv +++ b/vunit/test/acceptance/artificial/verilog/other_file_tests.sv @@ -1,23 +1,23 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - - -`include "vunit_defines.svh" - -module other_file_tests; - `NESTED_TEST_SUITE begin - - `TEST_CASE("pass") begin - $info("pass"); - end - - `TEST_CASE("fail") begin - $error("fail"); - end - end; - - `WATCHDOG(1ns); -endmodule +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + + +`include "vunit_defines.svh" + +module other_file_tests; + `NESTED_TEST_SUITE begin + + `TEST_CASE("pass") begin + $info("pass"); + end + + `TEST_CASE("fail") begin + $error("fail"); + end + end; + + `WATCHDOG(1ns); +endmodule diff --git a/vunit/test/acceptance/artificial/verilog/run.py b/vunit/test/acceptance/artificial/verilog/run.py index 14429637a..d464af446 100644 --- a/vunit/test/acceptance/artificial/verilog/run.py +++ b/vunit/test/acceptance/artificial/verilog/run.py @@ -1,55 +1,55 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit.verilog import VUnit - -root = dirname(__file__) - -ui = VUnit.from_argv() -lib = ui.add_library("lib") -lib.add_source_files(join(root, "*.sv"), - defines={"DEFINE_FROM_RUN_PY": ""}) - - -def configure_tb_with_parameter_config(ui): - """ - Configure tb_with_parameter_config test bench - """ - bench = lib.module("tb_with_parameter_config") - tests = [bench.test("Test %i" % i) for i in range(5)] - - bench.set_parameter("set_parameter", "set-for-module") - - tests[1].add_config('cfg', parameters=dict(config_parameter="set-from-config")) - - tests[2].set_parameter("set_parameter", "set-for-test") - - tests[3].add_config('cfg', parameters=dict(set_parameter="set-for-test", - config_parameter="set-from-config")) - - def post_check(output_path): - with open(join(output_path, "post_check.txt"), "r") as fptr: - return fptr.read() == "Test 4 was here" - - tests[4].add_config('cfg', - parameters=dict(set_parameter="set-from-config", - config_parameter="set-from-config"), - post_check=post_check) - - -def configure_tb_same_sim_all_pass(self): - def post_check(output_path): - with open(join(output_path, "post_check.txt"), "r") as fptr: - return fptr.read() == "Test 3 was here" - module = ui.library("lib").module("tb_same_sim_all_pass") - module.add_config('cfg', post_check=post_check) - - -configure_tb_with_parameter_config(ui) -configure_tb_same_sim_all_pass(ui) -lib.module("tb_other_file_tests").scan_tests_from_file(join(root, "other_file_tests.sv")) -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit.verilog import VUnit + +root = dirname(__file__) + +ui = VUnit.from_argv() +lib = ui.add_library("lib") +lib.add_source_files(join(root, "*.sv"), + defines={"DEFINE_FROM_RUN_PY": ""}) + + +def configure_tb_with_parameter_config(ui): + """ + Configure tb_with_parameter_config test bench + """ + bench = lib.module("tb_with_parameter_config") + tests = [bench.test("Test %i" % i) for i in range(5)] + + bench.set_parameter("set_parameter", "set-for-module") + + tests[1].add_config('cfg', parameters=dict(config_parameter="set-from-config")) + + tests[2].set_parameter("set_parameter", "set-for-test") + + tests[3].add_config('cfg', parameters=dict(set_parameter="set-for-test", + config_parameter="set-from-config")) + + def post_check(output_path): + with open(join(output_path, "post_check.txt"), "r") as fptr: + return fptr.read() == "Test 4 was here" + + tests[4].add_config('cfg', + parameters=dict(set_parameter="set-from-config", + config_parameter="set-from-config"), + post_check=post_check) + + +def configure_tb_same_sim_all_pass(self): + def post_check(output_path): + with open(join(output_path, "post_check.txt"), "r") as fptr: + return fptr.read() == "Test 3 was here" + module = ui.library("lib").module("tb_same_sim_all_pass") + module.add_config('cfg', post_check=post_check) + + +configure_tb_with_parameter_config(ui) +configure_tb_same_sim_all_pass(ui) +lib.module("tb_other_file_tests").scan_tests_from_file(join(root, "other_file_tests.sv")) +ui.main() diff --git a/vunit/test/acceptance/artificial/verilog/tb_fail_on_fatal_and_early_finish.sv b/vunit/test/acceptance/artificial/verilog/tb_fail_on_fatal_and_early_finish.sv index 3fba26a95..5ea2474bf 100644 --- a/vunit/test/acceptance/artificial/verilog/tb_fail_on_fatal_and_early_finish.sv +++ b/vunit/test/acceptance/artificial/verilog/tb_fail_on_fatal_and_early_finish.sv @@ -1,28 +1,28 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - - -`include "vunit_defines.svh" - -module tb_fail_on_fatal_and_early_finish; - `TEST_SUITE begin - `TEST_CASE("fatal0") begin - $fatal(0, "fatal0"); - end - - `TEST_CASE("fatal1") begin - $fatal(1, "fatal1"); - end - - `TEST_CASE("finish0") begin - $finish(0); - end - - `TEST_CASE("finish1") begin - $finish(1); - end - end; -endmodule +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + + +`include "vunit_defines.svh" + +module tb_fail_on_fatal_and_early_finish; + `TEST_SUITE begin + `TEST_CASE("fatal0") begin + $fatal(0, "fatal0"); + end + + `TEST_CASE("fatal1") begin + $fatal(1, "fatal1"); + end + + `TEST_CASE("finish0") begin + $finish(0); + end + + `TEST_CASE("finish1") begin + $finish(1); + end + end; +endmodule diff --git a/vunit/test/acceptance/artificial/verilog/tb_fail_on_warning.sv b/vunit/test/acceptance/artificial/verilog/tb_fail_on_warning.sv index e74e9a732..766046edb 100644 --- a/vunit/test/acceptance/artificial/verilog/tb_fail_on_warning.sv +++ b/vunit/test/acceptance/artificial/verilog/tb_fail_on_warning.sv @@ -1,17 +1,17 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -// vunit: fail_on_warning - -`include "vunit_defines.svh" - -module tb_fail_on_warning; - `TEST_SUITE begin - `TEST_CASE("fail") begin - $warning("A warning"); - end - end; -endmodule +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +// vunit: fail_on_warning + +`include "vunit_defines.svh" + +module tb_fail_on_warning; + `TEST_SUITE begin + `TEST_CASE("fail") begin + $warning("A warning"); + end + end; +endmodule diff --git a/vunit/test/acceptance/artificial/verilog/tb_magic_paths.sv b/vunit/test/acceptance/artificial/verilog/tb_magic_paths.sv index 0a185ba8f..3cba90f3d 100644 --- a/vunit/test/acceptance/artificial/verilog/tb_magic_paths.sv +++ b/vunit/test/acceptance/artificial/verilog/tb_magic_paths.sv @@ -1,29 +1,29 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - - -`include "vunit_defines.svh" - -module tb_magic_paths; - parameter string tb_path = ""; - parameter string output_path = ""; - - function void check_equal(string got, string expected); - assert (got == expected) else $error("Mismatch got %s expected %s", got, expected); - endfunction; - - function void check_has_suffix(string value, string suffix); - check_equal(value.substr(value.len()-suffix.len(), value.len()-1), suffix); - endfunction; - - `TEST_SUITE begin - `TEST_CASE("Test magic paths are correct") begin - check_has_suffix(tb_path, "acceptance/artificial/verilog/"); - end - end; - - `WATCHDOG(1ns); -endmodule +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + + +`include "vunit_defines.svh" + +module tb_magic_paths; + parameter string tb_path = ""; + parameter string output_path = ""; + + function void check_equal(string got, string expected); + assert (got == expected) else $error("Mismatch got %s expected %s", got, expected); + endfunction; + + function void check_has_suffix(string value, string suffix); + check_equal(value.substr(value.len()-suffix.len(), value.len()-1), suffix); + endfunction; + + `TEST_SUITE begin + `TEST_CASE("Test magic paths are correct") begin + check_has_suffix(tb_path, "acceptance/artificial/verilog/"); + end + end; + + `WATCHDOG(1ns); +endmodule diff --git a/vunit/test/acceptance/artificial/verilog/tb_other_file_tests.sv b/vunit/test/acceptance/artificial/verilog/tb_other_file_tests.sv index 8fafb3973..2cdb06db2 100644 --- a/vunit/test/acceptance/artificial/verilog/tb_other_file_tests.sv +++ b/vunit/test/acceptance/artificial/verilog/tb_other_file_tests.sv @@ -1,10 +1,10 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -module tb_other_file_tests; - parameter string runner_cfg = ""; - other_file_tests #(.nested_runner_cfg(runner_cfg)) other_file_tests_inst(); -endmodule +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +module tb_other_file_tests; + parameter string runner_cfg = ""; + other_file_tests #(.nested_runner_cfg(runner_cfg)) other_file_tests_inst(); +endmodule diff --git a/vunit/test/acceptance/artificial/verilog/tb_same_sim_all_pass.sv b/vunit/test/acceptance/artificial/verilog/tb_same_sim_all_pass.sv index f3d97999d..d2b244777 100644 --- a/vunit/test/acceptance/artificial/verilog/tb_same_sim_all_pass.sv +++ b/vunit/test/acceptance/artificial/verilog/tb_same_sim_all_pass.sv @@ -1,43 +1,43 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -// vunit: run_all_in_same_sim - -`include "vunit_defines.svh" - -module tb_same_sim_all_pass; - - parameter string output_path = ""; - - integer counter = 1; - - `TEST_SUITE begin - - `TEST_CASE("Test 1") begin - $info("Test 1"); - `CHECK_EQUAL(counter, 1); - counter = counter + 1; - end - - `TEST_CASE("Test 2") begin - $info("Test 2"); - `CHECK_EQUAL(counter, 2); - counter = counter + 1; - end - - `TEST_CASE("Test 3") begin - int fd; - $info("Test 3"); - `CHECK_EQUAL(counter, 3); - counter = counter + 1; - fd = $fopen({output_path, "post_check.txt"}); - $fwrite(fd, "Test 3 was here"); - $fclose(fd); - end - end; - - `WATCHDOG(1ns); -endmodule +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +// vunit: run_all_in_same_sim + +`include "vunit_defines.svh" + +module tb_same_sim_all_pass; + + parameter string output_path = ""; + + integer counter = 1; + + `TEST_SUITE begin + + `TEST_CASE("Test 1") begin + $info("Test 1"); + `CHECK_EQUAL(counter, 1); + counter = counter + 1; + end + + `TEST_CASE("Test 2") begin + $info("Test 2"); + `CHECK_EQUAL(counter, 2); + counter = counter + 1; + end + + `TEST_CASE("Test 3") begin + int fd; + $info("Test 3"); + `CHECK_EQUAL(counter, 3); + counter = counter + 1; + fd = $fopen({output_path, "post_check.txt"}); + $fwrite(fd, "Test 3 was here"); + $fclose(fd); + end + end; + + `WATCHDOG(1ns); +endmodule diff --git a/vunit/test/acceptance/artificial/verilog/tb_same_sim_some_fail.sv b/vunit/test/acceptance/artificial/verilog/tb_same_sim_some_fail.sv index 0e4e0f40d..0255b9f82 100644 --- a/vunit/test/acceptance/artificial/verilog/tb_same_sim_some_fail.sv +++ b/vunit/test/acceptance/artificial/verilog/tb_same_sim_some_fail.sv @@ -1,30 +1,30 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -// vunit: run_all_in_same_sim - -`include "vunit_defines.svh" - -module tb_same_sim_some_fail; - - `TEST_SUITE begin - - `TEST_CASE("Test 1") begin - $info("Test 1"); - end - - `TEST_CASE("Test 2") begin - $info("Test 2"); - $error(""); - end - - `TEST_CASE("Test 3") begin - $info("Test 3"); - end - end; - - `WATCHDOG(1ns); -endmodule +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +// vunit: run_all_in_same_sim + +`include "vunit_defines.svh" + +module tb_same_sim_some_fail; + + `TEST_SUITE begin + + `TEST_CASE("Test 1") begin + $info("Test 1"); + end + + `TEST_CASE("Test 2") begin + $info("Test 2"); + $error(""); + end + + `TEST_CASE("Test 3") begin + $info("Test 3"); + end + end; + + `WATCHDOG(1ns); +endmodule diff --git a/vunit/test/acceptance/artificial/verilog/tb_with_define.sv b/vunit/test/acceptance/artificial/verilog/tb_with_define.sv index b831bbcbe..74cf2e165 100644 --- a/vunit/test/acceptance/artificial/verilog/tb_with_define.sv +++ b/vunit/test/acceptance/artificial/verilog/tb_with_define.sv @@ -1,21 +1,21 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - - -`include "vunit_defines.svh" - -module tb_with_define; - `TEST_SUITE begin - - `TEST_CASE("test 1") begin -`ifndef DEFINE_FROM_RUN_PY - `CHECK_EQUAL(0, 1); -`endif - end - end; - - `WATCHDOG(1ns); -endmodule +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + + +`include "vunit_defines.svh" + +module tb_with_define; + `TEST_SUITE begin + + `TEST_CASE("test 1") begin +`ifndef DEFINE_FROM_RUN_PY + `CHECK_EQUAL(0, 1); +`endif + end + end; + + `WATCHDOG(1ns); +endmodule diff --git a/vunit/test/acceptance/artificial/verilog/tb_with_parameter_config.sv b/vunit/test/acceptance/artificial/verilog/tb_with_parameter_config.sv index d31a6aba8..1e349c7ba 100644 --- a/vunit/test/acceptance/artificial/verilog/tb_with_parameter_config.sv +++ b/vunit/test/acceptance/artificial/verilog/tb_with_parameter_config.sv @@ -1,42 +1,42 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - - -`include "vunit_defines.svh" - -module tb_with_parameter_config; - - parameter string output_path = ""; - parameter string set_parameter = "default"; - parameter string config_parameter = "default"; - - `TEST_SUITE begin - `TEST_CASE("Test 0") begin - `CHECK_EQUAL(set_parameter, "set-for-module"); - `CHECK_EQUAL(config_parameter, "default"); - end - `TEST_CASE("Test 1") begin - `CHECK_EQUAL(set_parameter, "set-for-module"); - `CHECK_EQUAL(config_parameter, "set-from-config"); - end - `TEST_CASE("Test 2") begin - `CHECK_EQUAL(set_parameter, "set-for-test"); - `CHECK_EQUAL(config_parameter, "default"); - end - `TEST_CASE("Test 3") begin - `CHECK_EQUAL(set_parameter, "set-for-test"); - `CHECK_EQUAL(config_parameter, "set-from-config"); - end - `TEST_CASE("Test 4") begin - int fd; - `CHECK_EQUAL(set_parameter, "set-from-config"); - `CHECK_EQUAL(config_parameter, "set-from-config"); - fd = $fopen({output_path, "post_check.txt"}); - $fwrite(fd, "Test 4 was here"); - $fclose(fd); - end - end; -endmodule +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + + +`include "vunit_defines.svh" + +module tb_with_parameter_config; + + parameter string output_path = ""; + parameter string set_parameter = "default"; + parameter string config_parameter = "default"; + + `TEST_SUITE begin + `TEST_CASE("Test 0") begin + `CHECK_EQUAL(set_parameter, "set-for-module"); + `CHECK_EQUAL(config_parameter, "default"); + end + `TEST_CASE("Test 1") begin + `CHECK_EQUAL(set_parameter, "set-for-module"); + `CHECK_EQUAL(config_parameter, "set-from-config"); + end + `TEST_CASE("Test 2") begin + `CHECK_EQUAL(set_parameter, "set-for-test"); + `CHECK_EQUAL(config_parameter, "default"); + end + `TEST_CASE("Test 3") begin + `CHECK_EQUAL(set_parameter, "set-for-test"); + `CHECK_EQUAL(config_parameter, "set-from-config"); + end + `TEST_CASE("Test 4") begin + int fd; + `CHECK_EQUAL(set_parameter, "set-from-config"); + `CHECK_EQUAL(config_parameter, "set-from-config"); + fd = $fopen({output_path, "post_check.txt"}); + $fwrite(fd, "Test 4 was here"); + $fclose(fd); + end + end; +endmodule diff --git a/vunit/test/acceptance/artificial/verilog/tb_with_runner.sv b/vunit/test/acceptance/artificial/verilog/tb_with_runner.sv index 9bf92b451..d6c4c33e7 100644 --- a/vunit/test/acceptance/artificial/verilog/tb_with_runner.sv +++ b/vunit/test/acceptance/artificial/verilog/tb_with_runner.sv @@ -1,23 +1,23 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - - -`include "vunit_defines.svh" - -module tb_with_runner; - `TEST_SUITE begin - - `TEST_CASE("pass") begin - $info("pass"); - end - - `TEST_CASE("fail") begin - $error("fail"); - end - end; - - `WATCHDOG(1ns); -endmodule +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + + +`include "vunit_defines.svh" + +module tb_with_runner; + `TEST_SUITE begin + + `TEST_CASE("pass") begin + $info("pass"); + end + + `TEST_CASE("fail") begin + $error("fail"); + end + end; + + `WATCHDOG(1ns); +endmodule diff --git a/vunit/test/acceptance/artificial/vhdl/bool_driver.vhd b/vunit/test/acceptance/artificial/vhdl/bool_driver.vhd index 110ef511a..ff0f4d594 100644 --- a/vunit/test/acceptance/artificial/vhdl/bool_driver.vhd +++ b/vunit/test/acceptance/artificial/vhdl/bool_driver.vhd @@ -1,19 +1,19 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- From Issue 71. Generic overridden on all hiearchy levels. - -entity bool_driver is - generic ( - g_val : boolean := true); - port ( - outp : out boolean); -end entity; - -architecture rtl of bool_driver is -begin - outp <= g_val; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- From Issue 71. Generic overridden on all hiearchy levels. + +entity bool_driver is + generic ( + g_val : boolean := true); + port ( + outp : out boolean); +end entity; + +architecture rtl of bool_driver is +begin + outp <= g_val; +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/other_file_tests.vhd b/vunit/test/acceptance/artificial/vhdl/other_file_tests.vhd index a9c5196c3..2c024c9b2 100644 --- a/vunit/test/acceptance/artificial/vhdl/other_file_tests.vhd +++ b/vunit/test/acceptance/artificial/vhdl/other_file_tests.vhd @@ -1,31 +1,31 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity other_file_tests is - generic ( - nested_runner_cfg : runner_cfg_t); -end entity; - -architecture vunit_test_bench of other_file_tests is -begin - test_runner : process - begin - test_runner_setup(runner, nested_runner_cfg); - while test_suite loop - if run("pass") then - report "Test pass"; - elsif run("fail") then - report "Test fail"; - assert false; - end if; - end loop; - test_runner_cleanup(runner); - wait; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity other_file_tests is + generic ( + nested_runner_cfg : runner_cfg_t); +end entity; + +architecture vunit_test_bench of other_file_tests is +begin + test_runner : process + begin + test_runner_setup(runner, nested_runner_cfg); + while test_suite loop + if run("pass") then + report "Test pass"; + elsif run("fail") then + report "Test fail"; + assert false; + end if; + end loop; + test_runner_cleanup(runner); + wait; + end process; +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/run.py b/vunit/test/acceptance/artificial/vhdl/run.py index 020c8190e..c871e76d2 100644 --- a/vunit/test/acceptance/artificial/vhdl/run.py +++ b/vunit/test/acceptance/artificial/vhdl/run.py @@ -1,84 +1,84 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -root = dirname(__file__) - -ui = VUnit.from_argv() -lib = ui.add_library("lib") -lib.add_source_files(join(root, "*.vhd")) - - -def configure_tb_with_generic_config(ui): - """ - Configure tb_with_generic_config test bench - """ - bench = lib.entity("tb_with_generic_config") - tests = [bench.test("Test %i" % i) for i in range(5)] - - bench.set_generic("set_generic", "set-for-entity") - - tests[1].add_config("cfg", generics=dict(config_generic="set-from-config")) - - tests[2].set_generic("set_generic", "set-for-test") - - tests[3].add_config("cfg", generics=dict(set_generic="set-for-test", - config_generic="set-from-config")) - - def post_check(output_path): - with open(join(output_path, "post_check.txt"), "r") as fptr: - return "Test 4 was here" in fptr.read() - - tests[4].add_config("cfg", - generics=dict(set_generic="set-from-config", - config_generic="set-from-config"), - post_check=post_check) - - -def configure_tb_same_sim_all_pass(ui): - def post_check(output_path): - with open(join(output_path, "post_check.txt"), "r") as fptr: - return "Test 3 was here" in fptr.read() - ent = ui.library("lib").entity("tb_same_sim_all_pass") - ent.add_config("cfg", generics=dict(), post_check=post_check) - - -def configure_tb_set_generic(ui): - tb = ui.library("lib").entity("tb_set_generic") - is_ghdl = ui._simulator_class.name == "ghdl" - tb.set_generic("is_ghdl", is_ghdl) - tb.set_generic("true_boolean", True) - tb.set_generic("false_boolean", False) - tb.set_generic("negative_integer", -10000) - tb.set_generic("positive_integer", 99999) - if not is_ghdl: - tb.set_generic("negative_real", -9999.9) - tb.set_generic("positive_real", 2222.2) - tb.set_generic("time_val", "4ns") - tb.set_generic("str_val", "4ns") - tb.set_generic("str_space_val", "1 2 3") - tb.set_generic("str_quote_val", 'a"b') - - -def configure_tb_assert_stop_level(ui): - tb = ui.library("lib").entity("tb_assert_stop_level") - - for vhdl_assert_stop_level in ["warning", "error", "failure"]: - for report_level in ["warning", "error", "failure"]: - test = tb.test("Report %s when VHDL assert stop level = %s" % (report_level, vhdl_assert_stop_level)) - test.set_sim_option("vhdl_assert_stop_level", vhdl_assert_stop_level) - - -configure_tb_with_generic_config(ui) -configure_tb_same_sim_all_pass(ui) -configure_tb_set_generic(ui) -configure_tb_assert_stop_level(ui) -lib.entity("tb_no_generic_override").set_generic("g_val", False) -lib.entity("tb_ieee_warning").test("pass").set_sim_option("disable_ieee_warnings", True) -lib.entity("tb_other_file_tests").scan_tests_from_file(join(root, "other_file_tests.vhd")) -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +root = dirname(__file__) + +ui = VUnit.from_argv() +lib = ui.add_library("lib") +lib.add_source_files(join(root, "*.vhd")) + + +def configure_tb_with_generic_config(ui): + """ + Configure tb_with_generic_config test bench + """ + bench = lib.entity("tb_with_generic_config") + tests = [bench.test("Test %i" % i) for i in range(5)] + + bench.set_generic("set_generic", "set-for-entity") + + tests[1].add_config("cfg", generics=dict(config_generic="set-from-config")) + + tests[2].set_generic("set_generic", "set-for-test") + + tests[3].add_config("cfg", generics=dict(set_generic="set-for-test", + config_generic="set-from-config")) + + def post_check(output_path): + with open(join(output_path, "post_check.txt"), "r") as fptr: + return "Test 4 was here" in fptr.read() + + tests[4].add_config("cfg", + generics=dict(set_generic="set-from-config", + config_generic="set-from-config"), + post_check=post_check) + + +def configure_tb_same_sim_all_pass(ui): + def post_check(output_path): + with open(join(output_path, "post_check.txt"), "r") as fptr: + return "Test 3 was here" in fptr.read() + ent = ui.library("lib").entity("tb_same_sim_all_pass") + ent.add_config("cfg", generics=dict(), post_check=post_check) + + +def configure_tb_set_generic(ui): + tb = ui.library("lib").entity("tb_set_generic") + is_ghdl = ui._simulator_class.name == "ghdl" + tb.set_generic("is_ghdl", is_ghdl) + tb.set_generic("true_boolean", True) + tb.set_generic("false_boolean", False) + tb.set_generic("negative_integer", -10000) + tb.set_generic("positive_integer", 99999) + if not is_ghdl: + tb.set_generic("negative_real", -9999.9) + tb.set_generic("positive_real", 2222.2) + tb.set_generic("time_val", "4ns") + tb.set_generic("str_val", "4ns") + tb.set_generic("str_space_val", "1 2 3") + tb.set_generic("str_quote_val", 'a"b') + + +def configure_tb_assert_stop_level(ui): + tb = ui.library("lib").entity("tb_assert_stop_level") + + for vhdl_assert_stop_level in ["warning", "error", "failure"]: + for report_level in ["warning", "error", "failure"]: + test = tb.test("Report %s when VHDL assert stop level = %s" % (report_level, vhdl_assert_stop_level)) + test.set_sim_option("vhdl_assert_stop_level", vhdl_assert_stop_level) + + +configure_tb_with_generic_config(ui) +configure_tb_same_sim_all_pass(ui) +configure_tb_set_generic(ui) +configure_tb_assert_stop_level(ui) +lib.entity("tb_no_generic_override").set_generic("g_val", False) +lib.entity("tb_ieee_warning").test("pass").set_sim_option("disable_ieee_warnings", True) +lib.entity("tb_other_file_tests").scan_tests_from_file(join(root, "other_file_tests.vhd")) +ui.main() diff --git a/vunit/test/acceptance/artificial/vhdl/tb_assert_stop_level.vhd b/vunit/test/acceptance/artificial/vhdl/tb_assert_stop_level.vhd index 76008f5c4..0b32ada56 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_assert_stop_level.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_assert_stop_level.vhd @@ -1,45 +1,45 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- This attribute should be ignored when VHDL assert stop level is used --- vunit: fail_on_warning - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_assert_stop_level is - generic (runner_cfg : string); -end entity; - -architecture vunit_test_bench of tb_assert_stop_level is -begin - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Report warning when VHDL assert stop level = warning") or - run("Report warning when VHDL assert stop level = error") or - run("Report warning when VHDL assert stop level = failure") then - - report "Warning" severity warning; - elsif run("Report error when VHDL assert stop level = warning") or - run("Report error when VHDL assert stop level = error") or - run("Report error when VHDL assert stop level = failure") then - - report "Error" severity error; - elsif run("Report failure when VHDL assert stop level = warning") or - run("Report failure when VHDL assert stop level = error") or - run("Report failure when VHDL assert stop level = failure") then - - report "Failure" severity failure; - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- This attribute should be ignored when VHDL assert stop level is used +-- vunit: fail_on_warning + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_assert_stop_level is + generic (runner_cfg : string); +end entity; + +architecture vunit_test_bench of tb_assert_stop_level is +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Report warning when VHDL assert stop level = warning") or + run("Report warning when VHDL assert stop level = error") or + run("Report warning when VHDL assert stop level = failure") then + + report "Warning" severity warning; + elsif run("Report error when VHDL assert stop level = warning") or + run("Report error when VHDL assert stop level = error") or + run("Report error when VHDL assert stop level = failure") then + + report "Error" severity error; + elsif run("Report failure when VHDL assert stop level = warning") or + run("Report failure when VHDL assert stop level = error") or + run("Report failure when VHDL assert stop level = failure") then + + report "Failure" severity failure; + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/tb_elab_fail.vhd b/vunit/test/acceptance/artificial/vhdl/tb_elab_fail.vhd index b10cabf48..ccebe6c78 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_elab_fail.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_elab_fail.vhd @@ -1,23 +1,23 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_elab_fail is - generic (runner_cfg : string; - generic_without_default : string); -end entity; - -architecture vunit_test_bench of tb_elab_fail is -begin - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - test_runner_cleanup(runner); - wait; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_elab_fail is + generic (runner_cfg : string; + generic_without_default : string); +end entity; + +architecture vunit_test_bench of tb_elab_fail is +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + wait; + end process; +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/tb_fail.vhd b/vunit/test/acceptance/artificial/vhdl/tb_fail.vhd index 5ebd2d1ad..468b80c54 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_fail.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_fail.vhd @@ -1,23 +1,23 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_fail is - generic (runner_cfg : string); -end entity; - -architecture vunit_test_bench of tb_fail is -begin - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - assert false; - test_runner_cleanup(runner); - wait; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_fail is + generic (runner_cfg : string); +end entity; + +architecture vunit_test_bench of tb_fail is +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + assert false; + test_runner_cleanup(runner); + wait; + end process; +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/tb_fail_on_warning.vhd b/vunit/test/acceptance/artificial/vhdl/tb_fail_on_warning.vhd index b25310fd1..32b3463ce 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_fail_on_warning.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_fail_on_warning.vhd @@ -1,25 +1,25 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: fail_on_warning - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_fail_on_warning is - generic (runner_cfg : string); -end entity; - -architecture vunit_test_bench of tb_fail_on_warning is -begin - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - report "A warning" severity warning; - test_runner_cleanup(runner); - wait; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: fail_on_warning + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_fail_on_warning is + generic (runner_cfg : string); +end entity; + +architecture vunit_test_bench of tb_fail_on_warning is +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + report "A warning" severity warning; + test_runner_cleanup(runner); + wait; + end process; +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/tb_ieee_warning.vhd b/vunit/test/acceptance/artificial/vhdl/tb_ieee_warning.vhd index a97197e23..0a337d2e5 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_ieee_warning.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_ieee_warning.vhd @@ -1,39 +1,39 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: fail_on_warning - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_ieee_warning is - generic (runner_cfg : string); -end entity; - -architecture vunit_test_bench of tb_ieee_warning is -begin - test_runner : process - variable undefined : unsigned(15 downto 0); - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("pass") then - report integer'image(to_integer(undefined)); - - elsif run("fail") then - report integer'image(to_integer(undefined)); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: fail_on_warning + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_ieee_warning is + generic (runner_cfg : string); +end entity; + +architecture vunit_test_bench of tb_ieee_warning is +begin + test_runner : process + variable undefined : unsigned(15 downto 0); + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("pass") then + report integer'image(to_integer(undefined)); + + elsif run("fail") then + report integer'image(to_integer(undefined)); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/tb_infinite_events.vhd b/vunit/test/acceptance/artificial/vhdl/tb_infinite_events.vhd index acf6d96a1..23184fb0c 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_infinite_events.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_infinite_events.vhd @@ -1,30 +1,30 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_infinite_events is - generic (runner_cfg : string); -end entity; - -architecture vunit_test_bench of tb_infinite_events is - signal toggle : boolean := false; -begin - -- Trigger infinite simulation events - -- to prove that simulation still ends at test_runner_cleanup - -- When not events the simulation will finish anyway - toggle <= not toggle after 1 ns; - - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - wait for 10 ns; - test_runner_cleanup(runner); - wait; - end process; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_infinite_events is + generic (runner_cfg : string); +end entity; + +architecture vunit_test_bench of tb_infinite_events is + signal toggle : boolean := false; +begin + -- Trigger infinite simulation events + -- to prove that simulation still ends at test_runner_cleanup + -- When not events the simulation will finish anyway + toggle <= not toggle after 1 ns; + + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + wait for 10 ns; + test_runner_cleanup(runner); + wait; + end process; + +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/tb_magic_paths.vhd b/vunit/test/acceptance/artificial/vhdl/tb_magic_paths.vhd index e9db22bcc..91812b71f 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_magic_paths.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_magic_paths.vhd @@ -1,31 +1,31 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_magic_paths is - generic ( - runner_cfg : string; - tb_path : string; - output_path : string); -end entity; - -architecture vunit_test_bench of tb_magic_paths is -begin - test_runner : process - procedure check_has_suffix(value : string; suffix : string) is - begin - check_equal(value(value'length+1-suffix'length to value'length), suffix); - end procedure; - begin - test_runner_setup(runner, runner_cfg); - check_has_suffix(tb_path, "/vunit/test/acceptance/artificial/vhdl/"); - check_has_suffix(vunit_lib.run_pkg.tb_path(runner_cfg), "/vunit/test/acceptance/artificial/vhdl/"); - test_runner_cleanup(runner); - wait; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_magic_paths is + generic ( + runner_cfg : string; + tb_path : string; + output_path : string); +end entity; + +architecture vunit_test_bench of tb_magic_paths is +begin + test_runner : process + procedure check_has_suffix(value : string; suffix : string) is + begin + check_equal(value(value'length+1-suffix'length to value'length), suffix); + end procedure; + begin + test_runner_setup(runner, runner_cfg); + check_has_suffix(tb_path, "/vunit/test/acceptance/artificial/vhdl/"); + check_has_suffix(vunit_lib.run_pkg.tb_path(runner_cfg), "/vunit/test/acceptance/artificial/vhdl/"); + test_runner_cleanup(runner); + wait; + end process; +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/tb_no_fail_after_cleanup.vhd b/vunit/test/acceptance/artificial/vhdl/tb_no_fail_after_cleanup.vhd index fc286a457..5b7c3f929 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_no_fail_after_cleanup.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_no_fail_after_cleanup.vhd @@ -1,23 +1,23 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_no_fail_after_cleanup is - generic (runner_cfg : string); -end entity; - -architecture vunit_test_bench of tb_no_fail_after_cleanup is -begin - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - test_runner_cleanup(runner); - assert false; - wait; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_no_fail_after_cleanup is + generic (runner_cfg : string); +end entity; + +architecture vunit_test_bench of tb_no_fail_after_cleanup is +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + assert false; + wait; + end process; +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/tb_no_fail_on_warning.vhd b/vunit/test/acceptance/artificial/vhdl/tb_no_fail_on_warning.vhd index 8124eaf9a..6e1967f6b 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_no_fail_on_warning.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_no_fail_on_warning.vhd @@ -1,23 +1,23 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_no_fail_on_warning is - generic (runner_cfg : string); -end entity; - -architecture vunit_test_bench of tb_no_fail_on_warning is -begin - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - report "A warning" severity warning; - test_runner_cleanup(runner); - wait; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_no_fail_on_warning is + generic (runner_cfg : string); +end entity; + +architecture vunit_test_bench of tb_no_fail_on_warning is +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + report "A warning" severity warning; + test_runner_cleanup(runner); + wait; + end process; +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/tb_no_generic_override.vhd b/vunit/test/acceptance/artificial/vhdl/tb_no_generic_override.vhd index 0a6066467..1d41af3c4 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_no_generic_override.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_no_generic_override.vhd @@ -1,31 +1,31 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- From Issue 71. Generic overridden on all hiearchy levels. - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_no_generic_override is - generic ( - runner_cfg : string; - g_val : boolean); -end entity; - -architecture tb of tb_no_generic_override is - signal s_outp : boolean; -begin - main : process - begin - test_runner_setup(runner, runner_cfg); - assert not g_val; - assert s_outp; - test_runner_cleanup(runner); - end process; - - dut : entity work.bool_driver - port map ( outp => s_outp ); -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- From Issue 71. Generic overridden on all hiearchy levels. + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_no_generic_override is + generic ( + runner_cfg : string; + g_val : boolean); +end entity; + +architecture tb of tb_no_generic_override is + signal s_outp : boolean; +begin + main : process + begin + test_runner_setup(runner, runner_cfg); + assert not g_val; + assert s_outp; + test_runner_cleanup(runner); + end process; + + dut : entity work.bool_driver + port map ( outp => s_outp ); +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/tb_other_file_tests.vhd b/vunit/test/acceptance/artificial/vhdl/tb_other_file_tests.vhd index e9590bf87..087e67f5d 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_other_file_tests.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_other_file_tests.vhd @@ -1,20 +1,20 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_other_file_tests is - generic ( - runner_cfg : string); -end entity; - -architecture vunit_test_bench of tb_other_file_tests is -begin - tests : entity work.other_file_tests - generic map ( - nested_runner_cfg => runner_cfg); -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_other_file_tests is + generic ( + runner_cfg : string); +end entity; + +architecture vunit_test_bench of tb_other_file_tests is +begin + tests : entity work.other_file_tests + generic map ( + nested_runner_cfg => runner_cfg); +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/tb_pass.vhd b/vunit/test/acceptance/artificial/vhdl/tb_pass.vhd index 8e5af9270..b8c012c25 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_pass.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_pass.vhd @@ -1,22 +1,22 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_pass is - generic (runner_cfg : string); -end entity; - -architecture vunit_test_bench of tb_pass is -begin - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - test_runner_cleanup(runner); - wait; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_pass is + generic (runner_cfg : string); +end entity; + +architecture vunit_test_bench of tb_pass is +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + wait; + end process; +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/tb_same_sim_all_pass.vhd b/vunit/test/acceptance/artificial/vhdl/tb_same_sim_all_pass.vhd index 8e9dea59b..4528b8fab 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_same_sim_all_pass.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_same_sim_all_pass.vhd @@ -1,53 +1,53 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -use std.textio.all; - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_same_sim_all_pass is - generic ( - output_path : string; - runner_cfg : string); -end entity; - -architecture vunit_test_bench of tb_same_sim_all_pass is -begin - test_runner : process - file fptr : text; - variable counter : integer := 1; - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test 1") then - wait for 10 ns; - report "Test 1"; - assert counter = 1; - counter := counter + 1; - elsif run("Test 2") then - wait for 10 ns; - report "Test 2"; - assert counter = 2; - counter := counter + 1; - elsif run("Test 3") then - wait for 10 ns; - report "Test 3"; - assert counter = 3; - counter := counter + 1; - file_open(fptr, output_path & "post_check.txt", write_mode); - write(fptr, string'("Test 3 was here")); - file_close(fptr); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +use std.textio.all; + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_same_sim_all_pass is + generic ( + output_path : string; + runner_cfg : string); +end entity; + +architecture vunit_test_bench of tb_same_sim_all_pass is +begin + test_runner : process + file fptr : text; + variable counter : integer := 1; + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test 1") then + wait for 10 ns; + report "Test 1"; + assert counter = 1; + counter := counter + 1; + elsif run("Test 2") then + wait for 10 ns; + report "Test 2"; + assert counter = 2; + counter := counter + 1; + elsif run("Test 3") then + wait for 10 ns; + report "Test 3"; + assert counter = 3; + counter := counter + 1; + file_open(fptr, output_path & "post_check.txt", write_mode); + write(fptr, string'("Test 3 was here")); + file_close(fptr); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/tb_same_sim_some_fail.vhd b/vunit/test/acceptance/artificial/vhdl/tb_same_sim_some_fail.vhd index 481640b52..7f81c6670 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_same_sim_some_fail.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_same_sim_some_fail.vhd @@ -1,40 +1,40 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_same_sim_some_fail is - generic ( - runner_cfg : string); -end entity; - -architecture vunit_test_bench of tb_same_sim_some_fail is -begin - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test 1") then - wait for 10 ns; - report "Test 1"; - elsif run("Test 2") then - wait for 10 ns; - report "Test 2"; - assert false; - elsif run("Test 3") then - wait for 10 ns; - report "Test 3"; - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_same_sim_some_fail is + generic ( + runner_cfg : string); +end entity; + +architecture vunit_test_bench of tb_same_sim_some_fail is +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test 1") then + wait for 10 ns; + report "Test 1"; + elsif run("Test 2") then + wait for 10 ns; + report "Test 2"; + assert false; + elsif run("Test 3") then + wait for 10 ns; + report "Test 3"; + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/tb_set_generic.vhd b/vunit/test/acceptance/artificial/vhdl/tb_set_generic.vhd index e89be1696..f8bc30cad 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_set_generic.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_set_generic.vhd @@ -1,45 +1,45 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_set_generic is - generic ( - runner_cfg : string; - is_ghdl : boolean; - true_boolean : boolean; - false_boolean : boolean; - negative_integer : integer; - positive_integer : integer; - negative_real : real := 0.0; - positive_real : real := 0.0; - time_val : time := 0 ns; - str_val : string; - str_space_val : string; - str_quote_val : string); -end entity; - -architecture tb of tb_set_generic is -begin - main : process - begin - test_runner_setup(runner, runner_cfg); - assert true_boolean = true; - assert false_boolean = false; - assert negative_integer = -10000; - assert positive_integer = 99999; - if not is_ghdl then - assert negative_real = -9999.9; - assert positive_real = 2222.2; - assert time_val = 4 ns; - end if; - assert str_val = "4ns"; - assert str_space_val = "1 2 3"; - assert str_quote_val = "a""b"; - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_set_generic is + generic ( + runner_cfg : string; + is_ghdl : boolean; + true_boolean : boolean; + false_boolean : boolean; + negative_integer : integer; + positive_integer : integer; + negative_real : real := 0.0; + positive_real : real := 0.0; + time_val : time := 0 ns; + str_val : string; + str_space_val : string; + str_quote_val : string); +end entity; + +architecture tb of tb_set_generic is +begin + main : process + begin + test_runner_setup(runner, runner_cfg); + assert true_boolean = true; + assert false_boolean = false; + assert negative_integer = -10000; + assert positive_integer = 99999; + if not is_ghdl then + assert negative_real = -9999.9; + assert positive_real = 2222.2; + assert time_val = 4 ns; + end if; + assert str_val = "4ns"; + assert str_space_val = "1 2 3"; + assert str_quote_val = "a""b"; + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/tb_with_checks.vhd b/vunit/test/acceptance/artificial/vhdl/tb_with_checks.vhd index a474b0aa3..a9d843127 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_with_checks.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_with_checks.vhd @@ -1,39 +1,39 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_with_checks is - generic ( - runner_cfg : string); -end entity; - -architecture vunit_test_bench of tb_with_checks is -begin - test_runner : process - variable pass : boolean; - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test passing check") then - wait for 10 ns; - check(true, "Should pass"); - elsif run("Test failing check") then - wait for 10 ns; - check(false, "Should pass"); - elsif run("Test non-stopping failing check") then - wait for 10 ns; - set_stop_level(failure); - check(false, "Should fail"); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_with_checks is + generic ( + runner_cfg : string); +end entity; + +architecture vunit_test_bench of tb_with_checks is +begin + test_runner : process + variable pass : boolean; + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test passing check") then + wait for 10 ns; + check(true, "Should pass"); + elsif run("Test failing check") then + wait for 10 ns; + check(false, "Should pass"); + elsif run("Test non-stopping failing check") then + wait for 10 ns; + set_stop_level(failure); + check(false, "Should fail"); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/tb_with_generic_config.vhd b/vunit/test/acceptance/artificial/vhdl/tb_with_generic_config.vhd index b42ccd733..caa063bbe 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_with_generic_config.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_with_generic_config.vhd @@ -1,57 +1,57 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use std.textio.all; - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_with_generic_config is - generic ( - runner_cfg : string; - output_path : string; - set_generic : string := "default"; - config_generic : string := "default"); -end entity; - -architecture tb of tb_with_generic_config is -begin - test_runner : process - file fptr : text; - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test 0") then - assert set_generic = "set-for-entity"; - assert config_generic = "default"; - - elsif run("Test 1") then - assert set_generic = "set-for-entity"; - assert config_generic = "set-from-config"; - - elsif run("Test 2") then - assert set_generic = "set-for-test"; - assert config_generic = "default"; - - elsif run("Test 3") then - assert set_generic = "set-for-test"; - assert config_generic = "set-from-config"; - - elsif run("Test 4") then - assert set_generic = "set-from-config"; - assert config_generic = "set-from-config"; - file_open(fptr, output_path & "post_check.txt", write_mode); - write(fptr, string'("Test 4 was here")); - file_close(fptr); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - test_runner_watchdog(runner, 50 ns); -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use std.textio.all; + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_with_generic_config is + generic ( + runner_cfg : string; + output_path : string; + set_generic : string := "default"; + config_generic : string := "default"); +end entity; + +architecture tb of tb_with_generic_config is +begin + test_runner : process + file fptr : text; + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test 0") then + assert set_generic = "set-for-entity"; + assert config_generic = "default"; + + elsif run("Test 1") then + assert set_generic = "set-for-entity"; + assert config_generic = "set-from-config"; + + elsif run("Test 2") then + assert set_generic = "set-for-test"; + assert config_generic = "default"; + + elsif run("Test 3") then + assert set_generic = "set-for-test"; + assert config_generic = "set-from-config"; + + elsif run("Test 4") then + assert set_generic = "set-from-config"; + assert config_generic = "set-from-config"; + file_open(fptr, output_path & "post_check.txt", write_mode); + write(fptr, string'("Test 4 was here")); + file_close(fptr); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + test_runner_watchdog(runner, 50 ns); +end architecture; diff --git a/vunit/test/acceptance/artificial/vhdl/tb_with_vhdl_runner.vhd b/vunit/test/acceptance/artificial/vhdl/tb_with_vhdl_runner.vhd index 82e264c4d..1706e999b 100644 --- a/vunit/test/acceptance/artificial/vhdl/tb_with_vhdl_runner.vhd +++ b/vunit/test/acceptance/artificial/vhdl/tb_with_vhdl_runner.vhd @@ -1,41 +1,41 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -entity tb_with_vhdl_runner is - generic ( - runner_cfg : string); -end entity; - -architecture vunit_test_bench of tb_with_vhdl_runner is -begin - test_runner : process - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("pass") then - wait for 10 ns; - report "Test pass"; - elsif run("fail") then - wait for 10 ns; - report "Test fail"; - assert false; - elsif run("Test with spaces") then - wait for 10 ns; - report "Test with spaces"; - elsif run("Test that timeouts") then - wait for 100 ns; - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - test_runner_watchdog(runner, 50 ns); -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +entity tb_with_vhdl_runner is + generic ( + runner_cfg : string); +end entity; + +architecture vunit_test_bench of tb_with_vhdl_runner is +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("pass") then + wait for 10 ns; + report "Test pass"; + elsif run("fail") then + wait for 10 ns; + report "Test fail"; + assert false; + elsif run("Test with spaces") then + wait for 10 ns; + report "Test with spaces"; + elsif run("Test that timeouts") then + wait for 100 ns; + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + test_runner_watchdog(runner, 50 ns); +end architecture; diff --git a/vunit/test/acceptance/dependencies/pkg.vhd b/vunit/test/acceptance/dependencies/pkg.vhd index fc9a01f11..0745a8a04 100644 --- a/vunit/test/acceptance/dependencies/pkg.vhd +++ b/vunit/test/acceptance/dependencies/pkg.vhd @@ -1,9 +1,9 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -package pkg is - procedure proc(value : integer); -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +package pkg is + procedure proc(value : integer); +end package; diff --git a/vunit/test/acceptance/dependencies/pkg_body1.vhd b/vunit/test/acceptance/dependencies/pkg_body1.vhd index 0734b2eb3..cf7badb4c 100644 --- a/vunit/test/acceptance/dependencies/pkg_body1.vhd +++ b/vunit/test/acceptance/dependencies/pkg_body1.vhd @@ -1,12 +1,12 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -package body pkg is - procedure proc(value : integer) is - begin - assert value = 1; - end procedure; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +package body pkg is + procedure proc(value : integer) is + begin + assert value = 1; + end procedure; +end package body; diff --git a/vunit/test/acceptance/dependencies/pkg_body2.vhd b/vunit/test/acceptance/dependencies/pkg_body2.vhd index 37421144a..d28c115f7 100644 --- a/vunit/test/acceptance/dependencies/pkg_body2.vhd +++ b/vunit/test/acceptance/dependencies/pkg_body2.vhd @@ -1,12 +1,12 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -package body pkg is - procedure proc(value : integer) is - begin - assert value = 2; - end procedure; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +package body pkg is + procedure proc(value : integer) is + begin + assert value = 2; + end procedure; +end package body; diff --git a/vunit/test/acceptance/dependencies/tb_pkg.vhd b/vunit/test/acceptance/dependencies/tb_pkg.vhd index 4399af4cc..05bc1f5aa 100644 --- a/vunit/test/acceptance/dependencies/tb_pkg.vhd +++ b/vunit/test/acceptance/dependencies/tb_pkg.vhd @@ -1,27 +1,27 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -use work.pkg.all; - -entity tb_pkg is - generic ( - runner_cfg : string; - value : integer); -end entity; - -architecture a of tb_pkg is -begin - main : process - begin - test_runner_setup(runner, runner_cfg); - report integer'image(value); - proc(value); - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +use work.pkg.all; + +entity tb_pkg is + generic ( + runner_cfg : string; + value : integer); +end entity; + +architecture a of tb_pkg is +begin + main : process + begin + test_runner_setup(runner, runner_cfg); + report integer'image(value); + proc(value); + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/test/acceptance/test_artificial.py b/vunit/test/acceptance/test_artificial.py index 4719f0792..dafd7e7fc 100644 --- a/vunit/test/acceptance/test_artificial.py +++ b/vunit/test/acceptance/test_artificial.py @@ -1,229 +1,229 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Acceptance test of VUnit end to end functionality -""" - - -import unittest -from os.path import join, dirname -from os import environ -from subprocess import call -import sys -from vunit.test.common import has_simulator, check_report, simulator_is - - -@unittest.skipUnless(has_simulator(), "Requires simulator") -class TestVunitArtificial(unittest.TestCase): - """ - Acceptance test of VUnit end to end functionality using - artificial test benches. - """ - def setUp(self): - # Spaces in path intentional to verify that it is supported - self.output_path = join(dirname(__file__), "artificial out") - self.report_file = join(self.output_path, "xunit.xml") - self.artificial_run_vhdl = join(dirname(__file__), "artificial", "vhdl", "run.py") - self.artificial_run_verilog = join(dirname(__file__), "artificial", "verilog", "run.py") - - @unittest.skipUnless(simulator_is("modelsim", "rivierapro"), - "Only simulators with persistance functionality") - def test_artificial_modelsim_unique_sim(self): - self._test_artificial(args=["--unique-sim"]) - - def test_artificial(self): - self._test_artificial() - - def test_artificial_elaborate_only(self): - self.check(self.artificial_run_vhdl, - exit_code=1, - args=["--elaborate"]) - - elab_expected_report = [] - for status, name in EXPECTED_REPORT: - if name in ("lib.tb_elab_fail.all",): - status = "failed" - else: - status = "passed" - elab_expected_report.append((status, name)) - - check_report(self.report_file, elab_expected_report) - - self.check(self.artificial_run_vhdl, - exit_code=0, - clean=False, - args=["--elaborate", "lib.tb_pass.all"]) - check_report(self.report_file, [ - ("passed", "lib.tb_pass.all")]) - - self.check(self.artificial_run_vhdl, - exit_code=1, - clean=False, - args=["--elaborate", "lib.tb_elab_fail.all"]) - check_report(self.report_file, [ - ("failed", "lib.tb_elab_fail.all")]) - - def _test_artificial(self, args=None): - """ - Utility function to run and check the result of all test benches - using either persistent or non-persistent simulator interface mode - """ - self.check(self.artificial_run_vhdl, - exit_code=1, - args=args) - check_report(self.report_file, EXPECTED_REPORT) - - def test_run_selected_tests_in_same_sim_test_bench_vhdl(self): - self._test_run_selected_tests_in_same_sim_test_bench(self.artificial_run_vhdl) - - @unittest.skipUnless(simulator_is("modelsim"), "Only modelsim supports verilog") - def test_run_selected_tests_in_same_sim_test_bench_verilog(self): - self._test_run_selected_tests_in_same_sim_test_bench(self.artificial_run_verilog) - - def _test_run_selected_tests_in_same_sim_test_bench(self, run_file): - """ - Run selected "same_sim" test in isolation - """ - self.check(run_file, - exit_code=0, - clean=True, - args=["*same_sim_some_fail*Test 1*"]) - check_report(self.report_file, [ - ("passed", "lib.tb_same_sim_some_fail.Test 1")]) - - self.check(run_file, - exit_code=1, - clean=False, - args=["*same_sim_some_fail*Test 2*"]) - check_report(self.report_file, [ - ("failed", "lib.tb_same_sim_some_fail.Test 2")]) - - self.check(run_file, - exit_code=0, - clean=False, - args=["*same_sim_some_fail*Test 3*"]) - check_report(self.report_file, [ - ("passed", "lib.tb_same_sim_some_fail.Test 3")]) - - self.check(run_file, - exit_code=1, - clean=False, - args=["*same_sim_some_fail*Test 2*", "*same_sim_some_fail*Test 3*"]) - check_report(self.report_file, [ - ("failed", "lib.tb_same_sim_some_fail.Test 2"), - ("skipped", "lib.tb_same_sim_some_fail.Test 3")]) - - @unittest.skipUnless(simulator_is("modelsim"), "Only modelsim supports verilog") - def test_artificial_verilog(self): - self.check(self.artificial_run_verilog, - exit_code=1) - check_report(self.report_file, [ - ("passed", "lib.tb_other_file_tests.pass"), - ("failed", "lib.tb_other_file_tests.fail"), - - ("passed", "lib.tb_magic_paths.Test magic paths are correct"), - ("passed", "lib.tb_with_define.test 1"), - - ("failed", "lib.tb_fail_on_warning.fail"), - - ("failed", "lib.tb_fail_on_fatal_and_early_finish.fatal0"), - ("failed", "lib.tb_fail_on_fatal_and_early_finish.fatal1"), - ("failed", "lib.tb_fail_on_fatal_and_early_finish.finish0"), - ("failed", "lib.tb_fail_on_fatal_and_early_finish.finish1"), - - ("passed", "lib.tb_with_parameter_config.Test 0"), - ("passed", "lib.tb_with_parameter_config.cfg.Test 1"), - ("passed", "lib.tb_with_parameter_config.Test 2"), - ("passed", "lib.tb_with_parameter_config.cfg.Test 3"), - ("passed", "lib.tb_with_parameter_config.cfg.Test 4"), - - ("passed", "lib.tb_same_sim_all_pass.cfg.Test 1"), - ("passed", "lib.tb_same_sim_all_pass.cfg.Test 2"), - ("passed", "lib.tb_same_sim_all_pass.cfg.Test 3"), - - ("passed", "lib.tb_same_sim_some_fail.Test 1"), - ("failed", "lib.tb_same_sim_some_fail.Test 2"), - ("skipped", "lib.tb_same_sim_some_fail.Test 3"), - - ("passed", "lib.tb_with_runner.pass"), - ("failed", "lib.tb_with_runner.fail")]) - - # pylint: disable=too-many-arguments - def check(self, run_file, args=None, clean=True, exit_code=0): - """ - Run external run file and verify exit code - """ - args = args if args is not None else [] - new_env = environ.copy() - new_env["VUNIT_VHDL_STANDARD"] = '2008' - if clean: - args += ["--clean"] - retcode = call([sys.executable, run_file, - "--output-path=%s" % self.output_path, - "--xunit-xml=%s" % self.report_file] + args, - env=new_env) - self.assertEqual(retcode, exit_code) - - def test_exit_0_flag(self): - self.check(self.artificial_run_vhdl, - exit_code=1, - args=["lib.tb_fail.all"]) - self.check(self.artificial_run_vhdl, - exit_code=0, - args=["--exit-0", "lib.tb_fail.all"]) - - -EXPECTED_REPORT = ( - ("passed", "lib.tb_other_file_tests.pass"), - ("failed", "lib.tb_other_file_tests.fail"), - ("passed", "lib.tb_pass.all"), - ("failed", "lib.tb_fail.all"), - ("passed", "lib.tb_infinite_events.all"), - ("failed", "lib.tb_fail_on_warning.all"), - ("passed", "lib.tb_no_fail_on_warning.all"), - ("passed", "lib.tb_with_vhdl_runner.pass"), - ("passed", "lib.tb_with_vhdl_runner.Test with spaces"), - ("failed", "lib.tb_with_vhdl_runner.fail"), - ("failed", "lib.tb_with_vhdl_runner.Test that timeouts"), - ("passed", "lib.tb_magic_paths.all"), - ("passed", "lib.tb_no_fail_after_cleanup.all"), - ("failed", "lib.tb_elab_fail.all"), - - ("passed", "lib.tb_same_sim_all_pass.cfg.Test 1"), - ("passed", "lib.tb_same_sim_all_pass.cfg.Test 2"), - ("passed", "lib.tb_same_sim_all_pass.cfg.Test 3"), - - ("passed", "lib.tb_same_sim_some_fail.Test 1"), - ("failed", "lib.tb_same_sim_some_fail.Test 2"), - ("skipped", "lib.tb_same_sim_some_fail.Test 3"), - - ("passed", "lib.tb_with_checks.Test passing check"), - ("failed", "lib.tb_with_checks.Test failing check"), - ("failed", "lib.tb_with_checks.Test non-stopping failing check"), - - ("passed", "lib.tb_set_generic.all"), - - ("passed", "lib.tb_with_generic_config.Test 0"), - ("passed", "lib.tb_with_generic_config.cfg.Test 1"), - ("passed", "lib.tb_with_generic_config.Test 2"), - ("passed", "lib.tb_with_generic_config.cfg.Test 3"), - ("passed", "lib.tb_with_generic_config.cfg.Test 4"), - - ("passed", "lib.tb_no_generic_override.all"), - - ("passed", "lib.tb_ieee_warning.pass"), - ("failed", "lib.tb_ieee_warning.fail"), - - ("failed", "lib.tb_assert_stop_level.Report warning when VHDL assert stop level = warning"), - ("failed", "lib.tb_assert_stop_level.Report error when VHDL assert stop level = warning"), - ("failed", "lib.tb_assert_stop_level.Report failure when VHDL assert stop level = warning"), - ("passed", "lib.tb_assert_stop_level.Report warning when VHDL assert stop level = error"), - ("failed", "lib.tb_assert_stop_level.Report error when VHDL assert stop level = error"), - ("failed", "lib.tb_assert_stop_level.Report failure when VHDL assert stop level = error"), - ("passed", "lib.tb_assert_stop_level.Report warning when VHDL assert stop level = failure"), - ("passed", "lib.tb_assert_stop_level.Report error when VHDL assert stop level = failure"), - ("failed", "lib.tb_assert_stop_level.Report failure when VHDL assert stop level = failure")) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Acceptance test of VUnit end to end functionality +""" + + +import unittest +from os.path import join, dirname +from os import environ +from subprocess import call +import sys +from vunit.test.common import has_simulator, check_report, simulator_is + + +@unittest.skipUnless(has_simulator(), "Requires simulator") +class TestVunitArtificial(unittest.TestCase): + """ + Acceptance test of VUnit end to end functionality using + artificial test benches. + """ + def setUp(self): + # Spaces in path intentional to verify that it is supported + self.output_path = join(dirname(__file__), "artificial out") + self.report_file = join(self.output_path, "xunit.xml") + self.artificial_run_vhdl = join(dirname(__file__), "artificial", "vhdl", "run.py") + self.artificial_run_verilog = join(dirname(__file__), "artificial", "verilog", "run.py") + + @unittest.skipUnless(simulator_is("modelsim", "rivierapro"), + "Only simulators with persistance functionality") + def test_artificial_modelsim_unique_sim(self): + self._test_artificial(args=["--unique-sim"]) + + def test_artificial(self): + self._test_artificial() + + def test_artificial_elaborate_only(self): + self.check(self.artificial_run_vhdl, + exit_code=1, + args=["--elaborate"]) + + elab_expected_report = [] + for status, name in EXPECTED_REPORT: + if name in ("lib.tb_elab_fail.all",): + status = "failed" + else: + status = "passed" + elab_expected_report.append((status, name)) + + check_report(self.report_file, elab_expected_report) + + self.check(self.artificial_run_vhdl, + exit_code=0, + clean=False, + args=["--elaborate", "lib.tb_pass.all"]) + check_report(self.report_file, [ + ("passed", "lib.tb_pass.all")]) + + self.check(self.artificial_run_vhdl, + exit_code=1, + clean=False, + args=["--elaborate", "lib.tb_elab_fail.all"]) + check_report(self.report_file, [ + ("failed", "lib.tb_elab_fail.all")]) + + def _test_artificial(self, args=None): + """ + Utility function to run and check the result of all test benches + using either persistent or non-persistent simulator interface mode + """ + self.check(self.artificial_run_vhdl, + exit_code=1, + args=args) + check_report(self.report_file, EXPECTED_REPORT) + + def test_run_selected_tests_in_same_sim_test_bench_vhdl(self): + self._test_run_selected_tests_in_same_sim_test_bench(self.artificial_run_vhdl) + + @unittest.skipUnless(simulator_is("modelsim"), "Only modelsim supports verilog") + def test_run_selected_tests_in_same_sim_test_bench_verilog(self): + self._test_run_selected_tests_in_same_sim_test_bench(self.artificial_run_verilog) + + def _test_run_selected_tests_in_same_sim_test_bench(self, run_file): + """ + Run selected "same_sim" test in isolation + """ + self.check(run_file, + exit_code=0, + clean=True, + args=["*same_sim_some_fail*Test 1*"]) + check_report(self.report_file, [ + ("passed", "lib.tb_same_sim_some_fail.Test 1")]) + + self.check(run_file, + exit_code=1, + clean=False, + args=["*same_sim_some_fail*Test 2*"]) + check_report(self.report_file, [ + ("failed", "lib.tb_same_sim_some_fail.Test 2")]) + + self.check(run_file, + exit_code=0, + clean=False, + args=["*same_sim_some_fail*Test 3*"]) + check_report(self.report_file, [ + ("passed", "lib.tb_same_sim_some_fail.Test 3")]) + + self.check(run_file, + exit_code=1, + clean=False, + args=["*same_sim_some_fail*Test 2*", "*same_sim_some_fail*Test 3*"]) + check_report(self.report_file, [ + ("failed", "lib.tb_same_sim_some_fail.Test 2"), + ("skipped", "lib.tb_same_sim_some_fail.Test 3")]) + + @unittest.skipUnless(simulator_is("modelsim"), "Only modelsim supports verilog") + def test_artificial_verilog(self): + self.check(self.artificial_run_verilog, + exit_code=1) + check_report(self.report_file, [ + ("passed", "lib.tb_other_file_tests.pass"), + ("failed", "lib.tb_other_file_tests.fail"), + + ("passed", "lib.tb_magic_paths.Test magic paths are correct"), + ("passed", "lib.tb_with_define.test 1"), + + ("failed", "lib.tb_fail_on_warning.fail"), + + ("failed", "lib.tb_fail_on_fatal_and_early_finish.fatal0"), + ("failed", "lib.tb_fail_on_fatal_and_early_finish.fatal1"), + ("failed", "lib.tb_fail_on_fatal_and_early_finish.finish0"), + ("failed", "lib.tb_fail_on_fatal_and_early_finish.finish1"), + + ("passed", "lib.tb_with_parameter_config.Test 0"), + ("passed", "lib.tb_with_parameter_config.cfg.Test 1"), + ("passed", "lib.tb_with_parameter_config.Test 2"), + ("passed", "lib.tb_with_parameter_config.cfg.Test 3"), + ("passed", "lib.tb_with_parameter_config.cfg.Test 4"), + + ("passed", "lib.tb_same_sim_all_pass.cfg.Test 1"), + ("passed", "lib.tb_same_sim_all_pass.cfg.Test 2"), + ("passed", "lib.tb_same_sim_all_pass.cfg.Test 3"), + + ("passed", "lib.tb_same_sim_some_fail.Test 1"), + ("failed", "lib.tb_same_sim_some_fail.Test 2"), + ("skipped", "lib.tb_same_sim_some_fail.Test 3"), + + ("passed", "lib.tb_with_runner.pass"), + ("failed", "lib.tb_with_runner.fail")]) + + # pylint: disable=too-many-arguments + def check(self, run_file, args=None, clean=True, exit_code=0): + """ + Run external run file and verify exit code + """ + args = args if args is not None else [] + new_env = environ.copy() + new_env["VUNIT_VHDL_STANDARD"] = '2008' + if clean: + args += ["--clean"] + retcode = call([sys.executable, run_file, + "--output-path=%s" % self.output_path, + "--xunit-xml=%s" % self.report_file] + args, + env=new_env) + self.assertEqual(retcode, exit_code) + + def test_exit_0_flag(self): + self.check(self.artificial_run_vhdl, + exit_code=1, + args=["lib.tb_fail.all"]) + self.check(self.artificial_run_vhdl, + exit_code=0, + args=["--exit-0", "lib.tb_fail.all"]) + + +EXPECTED_REPORT = ( + ("passed", "lib.tb_other_file_tests.pass"), + ("failed", "lib.tb_other_file_tests.fail"), + ("passed", "lib.tb_pass.all"), + ("failed", "lib.tb_fail.all"), + ("passed", "lib.tb_infinite_events.all"), + ("failed", "lib.tb_fail_on_warning.all"), + ("passed", "lib.tb_no_fail_on_warning.all"), + ("passed", "lib.tb_with_vhdl_runner.pass"), + ("passed", "lib.tb_with_vhdl_runner.Test with spaces"), + ("failed", "lib.tb_with_vhdl_runner.fail"), + ("failed", "lib.tb_with_vhdl_runner.Test that timeouts"), + ("passed", "lib.tb_magic_paths.all"), + ("passed", "lib.tb_no_fail_after_cleanup.all"), + ("failed", "lib.tb_elab_fail.all"), + + ("passed", "lib.tb_same_sim_all_pass.cfg.Test 1"), + ("passed", "lib.tb_same_sim_all_pass.cfg.Test 2"), + ("passed", "lib.tb_same_sim_all_pass.cfg.Test 3"), + + ("passed", "lib.tb_same_sim_some_fail.Test 1"), + ("failed", "lib.tb_same_sim_some_fail.Test 2"), + ("skipped", "lib.tb_same_sim_some_fail.Test 3"), + + ("passed", "lib.tb_with_checks.Test passing check"), + ("failed", "lib.tb_with_checks.Test failing check"), + ("failed", "lib.tb_with_checks.Test non-stopping failing check"), + + ("passed", "lib.tb_set_generic.all"), + + ("passed", "lib.tb_with_generic_config.Test 0"), + ("passed", "lib.tb_with_generic_config.cfg.Test 1"), + ("passed", "lib.tb_with_generic_config.Test 2"), + ("passed", "lib.tb_with_generic_config.cfg.Test 3"), + ("passed", "lib.tb_with_generic_config.cfg.Test 4"), + + ("passed", "lib.tb_no_generic_override.all"), + + ("passed", "lib.tb_ieee_warning.pass"), + ("failed", "lib.tb_ieee_warning.fail"), + + ("failed", "lib.tb_assert_stop_level.Report warning when VHDL assert stop level = warning"), + ("failed", "lib.tb_assert_stop_level.Report error when VHDL assert stop level = warning"), + ("failed", "lib.tb_assert_stop_level.Report failure when VHDL assert stop level = warning"), + ("passed", "lib.tb_assert_stop_level.Report warning when VHDL assert stop level = error"), + ("failed", "lib.tb_assert_stop_level.Report error when VHDL assert stop level = error"), + ("failed", "lib.tb_assert_stop_level.Report failure when VHDL assert stop level = error"), + ("passed", "lib.tb_assert_stop_level.Report warning when VHDL assert stop level = failure"), + ("passed", "lib.tb_assert_stop_level.Report error when VHDL assert stop level = failure"), + ("failed", "lib.tb_assert_stop_level.Report failure when VHDL assert stop level = failure")) diff --git a/vunit/test/acceptance/test_dependencies.py b/vunit/test/acceptance/test_dependencies.py index c793499ec..caa4acfde 100644 --- a/vunit/test/acceptance/test_dependencies.py +++ b/vunit/test/acceptance/test_dependencies.py @@ -1,60 +1,60 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Dependency requirements differ between simulators. This file contains -tests to expose those differences. -""" - - -import unittest -from os.path import join, dirname -from vunit import VUnit - - -class TestDependencies(unittest.TestCase): - """ - Test to expose simulator dependency requirements - """ - def setUp(self): - self.data_path = join(dirname(__file__), "dependencies") - self.output_path = join(dirname(__file__), "dependencies_vunit_out") - - def test_package_body_dependencies(self): - """ - Some simulators require package users to be re-compiled when - a package body has changed even though the package header did not change. - The purpose of this test is to ensure this is handled. - """ - - def run(value): - """ - Utility function to first run with pkg_body1 then pkg_body2 - """ - - tb_pkg_file_name = join(self.data_path, "tb_pkg.vhd") - pkg_file_name = join(self.data_path, "pkg.vhd") - pkg_body_file_name = join(self.data_path, "pkg_body%i.vhd" % value) - - argv = ["--output-path=%s" % self.output_path, - "-v"] - if value == 1: - argv.append("--clean") - - ui = VUnit.from_argv(argv=argv) - lib = ui.add_library("lib") - lib.add_source_files(tb_pkg_file_name) - lib.add_source_files(pkg_file_name) - lib.add_source_files(pkg_body_file_name) - lib.entity("tb_pkg").set_generic("value", value) - - try: - ui.main() - except SystemExit as ex: - self.assertEqual(ex.code, 0) - - run(1) - run(2) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Dependency requirements differ between simulators. This file contains +tests to expose those differences. +""" + + +import unittest +from os.path import join, dirname +from vunit import VUnit + + +class TestDependencies(unittest.TestCase): + """ + Test to expose simulator dependency requirements + """ + def setUp(self): + self.data_path = join(dirname(__file__), "dependencies") + self.output_path = join(dirname(__file__), "dependencies_vunit_out") + + def test_package_body_dependencies(self): + """ + Some simulators require package users to be re-compiled when + a package body has changed even though the package header did not change. + The purpose of this test is to ensure this is handled. + """ + + def run(value): + """ + Utility function to first run with pkg_body1 then pkg_body2 + """ + + tb_pkg_file_name = join(self.data_path, "tb_pkg.vhd") + pkg_file_name = join(self.data_path, "pkg.vhd") + pkg_body_file_name = join(self.data_path, "pkg_body%i.vhd" % value) + + argv = ["--output-path=%s" % self.output_path, + "-v"] + if value == 1: + argv.append("--clean") + + ui = VUnit.from_argv(argv=argv) + lib = ui.add_library("lib") + lib.add_source_files(tb_pkg_file_name) + lib.add_source_files(pkg_file_name) + lib.add_source_files(pkg_body_file_name) + lib.entity("tb_pkg").set_generic("value", value) + + try: + ui.main() + except SystemExit as ex: + self.assertEqual(ex.code, 0) + + run(1) + run(2) diff --git a/vunit/test/acceptance/test_external_run_scripts.py b/vunit/test/acceptance/test_external_run_scripts.py index c3016d77f..7c3558846 100644 --- a/vunit/test/acceptance/test_external_run_scripts.py +++ b/vunit/test/acceptance/test_external_run_scripts.py @@ -1,239 +1,239 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Verify that all external run scripts work correctly -""" - - -import unittest -from os import environ -from os.path import join, dirname -from subprocess import call -import sys -from vunit import ROOT -from vunit.builtins import VHDL_PATH -from vunit.test.common import has_simulator, check_report, simulator_is - - -def simulator_supports_verilog(): - """ - Returns True if simulator supports Verilog - """ - return simulator_is("modelsim", "incisive") - - -# pylint: disable=too-many-public-methods -@unittest.skipUnless(has_simulator(), "Requires simulator") -class TestExternalRunScripts(unittest.TestCase): - """ - Verify that example projects run correctly - """ - - def test_vhdl_uart_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "uart", "run.py")) - - @unittest.skipUnless(simulator_supports_verilog(), "Verilog") - def test_verilog_uart_example_project(self): - self.check(join(ROOT, "examples", "verilog", "uart", "run.py")) - - @unittest.skipUnless(simulator_supports_verilog(), "Verilog") - def test_verilog_ams_example(self): - self.check(join(ROOT, "examples", "verilog", "verilog_ams", "run.py")) - check_report(self.report_file, - [("passed", "lib.tb_dut.Test that pass"), - ("failed", "lib.tb_dut.Test that fail")]) - - def test_vhdl_logging_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "logging", "run.py")) - - def test_vhdl_run_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "run", "run.py"), exit_code=1) - check_report(self.report_file, - [("passed", "lib.tb_with_watchdog.Test to_string for boolean"), - ("passed", "lib.tb_with_watchdog.Test that needs longer timeout"), - ("passed", "lib.tb_standalone.Test to_string for boolean"), - ("passed", "lib.tb_with_test_cases.Test to_string for integer"), - ("passed", "lib.tb_with_test_cases.Test to_string for boolean"), - ("passed", "lib.tb_with_lower_level_control.Test something"), - ("passed", "lib.tb_with_lower_level_control.Test something else"), - ("passed", "lib.tb_running_test_case.Test scenario A"), - ("passed", "lib.tb_running_test_case.Test scenario B"), - ("passed", "lib.tb_running_test_case.Test something else"), - ("passed", "lib.tb_minimal.all"), - ("passed", "lib.tb_magic_paths.all"), - ("failed", "lib.tb_with_watchdog.Test that stalls"), - ("failed", "lib.tb_counting_errors.Test that fails multiple times but doesn't stop"), - ("failed", "lib.tb_standalone.Test that fails on VUnit check procedure"), - ("failed", "lib.tb_many_ways_to_fail.Test that fails on an assert"), - ("failed", "lib.tb_many_ways_to_fail.Test that crashes on boundary problems"), - ("failed", "lib.tb_many_ways_to_fail.Test that fails on VUnit check procedure")]) - - def test_vhdl_third_party_integration_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "third_party_integration", "run.py"), exit_code=1) - check_report(self.report_file, - [("passed", "lib.tb_external_framework_integration.Test that pass"), - ("failed", - "lib.tb_external_framework_integration.Test that stops the simulation on first error"), - ("failed", - "lib.tb_external_framework_integration.Test that doesn't stop the simulation on error")]) - - def test_vhdl_check_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "check", "run.py")) - - def test_vhdl_generate_tests_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "generate_tests", "run.py")) - check_report(self.report_file, - [("passed", "lib.tb_generated.data_width=1,sign=False.Test 1"), - ("passed", "lib.tb_generated.data_width=1,sign=True.Test 1"), - ("passed", "lib.tb_generated.data_width=2,sign=False.Test 1"), - ("passed", "lib.tb_generated.data_width=2,sign=True.Test 1"), - ("passed", "lib.tb_generated.data_width=3,sign=False.Test 1"), - ("passed", "lib.tb_generated.data_width=3,sign=True.Test 1"), - ("passed", "lib.tb_generated.data_width=4,sign=False.Test 1"), - ("passed", "lib.tb_generated.data_width=4,sign=True.Test 1"), - ("passed", "lib.tb_generated.data_width=16,sign=True.Test 2")]) - - def test_vhdl_composite_generics_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "composite_generics", "run.py")) - check_report(self.report_file, - [("passed", "tb_lib.tb_composite_generics.VGA.Test 1"), - ("passed", "tb_lib.tb_composite_generics.tiny.Test 1")]) - - def test_vhdl_json4vhdl_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "json4vhdl", "run.py")) - - def test_vhdl_array_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "array", "run.py")) - - def test_vhdl_array_axis_vcs_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "array_axis_vcs", "run.py")) - - def test_vhdl_axi_dma_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "axi_dma", "run.py")) - - def test_vhdl_user_guide_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "user_guide", "run.py"), exit_code=1) - check_report(self.report_file, - [("passed", "lib.tb_example.all"), - ("passed", "lib.tb_example_many.test_pass"), - ("failed", "lib.tb_example_many.test_fail")]) - - @unittest.skipUnless(simulator_supports_verilog(), "Verilog") - def test_verilog_user_guide_example_project(self): - self.check(join(ROOT, "examples", "verilog", "user_guide", "run.py"), exit_code=1) - check_report(self.report_file, - [("passed", "lib.tb_example_basic.all"), - ("passed", "lib.tb_example.Test that a successful test case passes"), - ("failed", "lib.tb_example.Test that a failing test case actually fails"), - ("failed", "lib.tb_example.Test that a test case that takes too long time fails with a timeout")]) - - def test_vhdl_com_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "com", "run.py")) - - def test_array_vhdl_2008(self): - self.check(join(VHDL_PATH, "array", "run.py")) - - def test_data_types_vhdl_2008(self): - self.check(join(VHDL_PATH, "data_types", "run.py")) - - def test_data_types_vhdl_2002(self): - self.check(join(VHDL_PATH, "data_types", "run.py"), - vhdl_standard="2002") - - def test_data_types_vhdl_93(self): - self.check(join(VHDL_PATH, "data_types", "run.py"), - vhdl_standard="93") - - def test_random_vhdl_2008(self): - self.check(join(VHDL_PATH, "random", "run.py")) - - def test_check_vhdl_2008(self): - self.check(join(VHDL_PATH, "check", "run.py")) - - def test_check_vhdl_2002(self): - self.check(join(VHDL_PATH, "check", "run.py"), - vhdl_standard='2002') - - def test_check_vhdl_93(self): - self.check(join(VHDL_PATH, "check", "run.py"), - vhdl_standard='93') - - def test_logging_vhdl_2008(self): - self.check(join(VHDL_PATH, "logging", "run.py")) - - def test_logging_vhdl_2002(self): - self.check(join(VHDL_PATH, "logging", "run.py"), - vhdl_standard='2002') - - def test_logging_vhdl_93(self): - self.check(join(VHDL_PATH, "logging", "run.py"), - vhdl_standard='93') - - def test_run_vhdl_2008(self): - self.check(join(VHDL_PATH, "run", "run.py")) - - def test_run_vhdl_2002(self): - self.check(join(VHDL_PATH, "run", "run.py"), - vhdl_standard='2002') - - def test_run_vhdl_93(self): - self.check(join(VHDL_PATH, "run", "run.py"), - vhdl_standard='93') - - def test_string_ops_vhdl_2008(self): - self.check(join(VHDL_PATH, "string_ops", "run.py")) - - def test_string_ops_vhdl_2002(self): - self.check(join(VHDL_PATH, "string_ops", "run.py"), - vhdl_standard='2002') - - def test_string_ops_vhdl_93(self): - self.check(join(VHDL_PATH, "string_ops", "run.py"), - vhdl_standard='93') - - def test_dictionary_vhdl_2008(self): - self.check(join(VHDL_PATH, "dictionary", "run.py")) - - def test_dictionary_vhdl_2002(self): - self.check(join(VHDL_PATH, "dictionary", "run.py"), - vhdl_standard='2002') - - def test_dictionary_vhdl_93(self): - self.check(join(VHDL_PATH, "dictionary", "run.py"), - vhdl_standard='93') - - def test_path_vhdl_2008(self): - self.check(join(VHDL_PATH, "path", "run.py")) - - def test_path_vhdl_2002(self): - self.check(join(VHDL_PATH, "path", "run.py"), - vhdl_standard='2002') - - def test_path_vhdl_93(self): - self.check(join(VHDL_PATH, "path", "run.py"), - vhdl_standard='93') - - def test_com_vhdl_2008(self): - self.check(join(VHDL_PATH, "com", "run.py")) - - def setUp(self): - self.output_path = join(dirname(__file__), "external_run_out") - self.report_file = join(self.output_path, "xunit.xml") - - def check(self, run_file, args=None, vhdl_standard='2008', exit_code=0): - """ - Run external run file and verify exit code - """ - args = args if args is not None else [] - new_env = environ.copy() - new_env["VUNIT_VHDL_STANDARD"] = vhdl_standard - retcode = call([sys.executable, run_file, - "--clean", - "--output-path=%s" % self.output_path, - "--xunit-xml=%s" % self.report_file] + args, - env=new_env) - self.assertEqual(retcode, exit_code) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Verify that all external run scripts work correctly +""" + + +import unittest +from os import environ +from os.path import join, dirname +from subprocess import call +import sys +from vunit import ROOT +from vunit.builtins import VHDL_PATH +from vunit.test.common import has_simulator, check_report, simulator_is + + +def simulator_supports_verilog(): + """ + Returns True if simulator supports Verilog + """ + return simulator_is("modelsim", "incisive") + + +# pylint: disable=too-many-public-methods +@unittest.skipUnless(has_simulator(), "Requires simulator") +class TestExternalRunScripts(unittest.TestCase): + """ + Verify that example projects run correctly + """ + + def test_vhdl_uart_example_project(self): + self.check(join(ROOT, "examples", "vhdl", "uart", "run.py")) + + @unittest.skipUnless(simulator_supports_verilog(), "Verilog") + def test_verilog_uart_example_project(self): + self.check(join(ROOT, "examples", "verilog", "uart", "run.py")) + + @unittest.skipUnless(simulator_supports_verilog(), "Verilog") + def test_verilog_ams_example(self): + self.check(join(ROOT, "examples", "verilog", "verilog_ams", "run.py")) + check_report(self.report_file, + [("passed", "lib.tb_dut.Test that pass"), + ("failed", "lib.tb_dut.Test that fail")]) + + def test_vhdl_logging_example_project(self): + self.check(join(ROOT, "examples", "vhdl", "logging", "run.py")) + + def test_vhdl_run_example_project(self): + self.check(join(ROOT, "examples", "vhdl", "run", "run.py"), exit_code=1) + check_report(self.report_file, + [("passed", "lib.tb_with_watchdog.Test to_string for boolean"), + ("passed", "lib.tb_with_watchdog.Test that needs longer timeout"), + ("passed", "lib.tb_standalone.Test to_string for boolean"), + ("passed", "lib.tb_with_test_cases.Test to_string for integer"), + ("passed", "lib.tb_with_test_cases.Test to_string for boolean"), + ("passed", "lib.tb_with_lower_level_control.Test something"), + ("passed", "lib.tb_with_lower_level_control.Test something else"), + ("passed", "lib.tb_running_test_case.Test scenario A"), + ("passed", "lib.tb_running_test_case.Test scenario B"), + ("passed", "lib.tb_running_test_case.Test something else"), + ("passed", "lib.tb_minimal.all"), + ("passed", "lib.tb_magic_paths.all"), + ("failed", "lib.tb_with_watchdog.Test that stalls"), + ("failed", "lib.tb_counting_errors.Test that fails multiple times but doesn't stop"), + ("failed", "lib.tb_standalone.Test that fails on VUnit check procedure"), + ("failed", "lib.tb_many_ways_to_fail.Test that fails on an assert"), + ("failed", "lib.tb_many_ways_to_fail.Test that crashes on boundary problems"), + ("failed", "lib.tb_many_ways_to_fail.Test that fails on VUnit check procedure")]) + + def test_vhdl_third_party_integration_example_project(self): + self.check(join(ROOT, "examples", "vhdl", "third_party_integration", "run.py"), exit_code=1) + check_report(self.report_file, + [("passed", "lib.tb_external_framework_integration.Test that pass"), + ("failed", + "lib.tb_external_framework_integration.Test that stops the simulation on first error"), + ("failed", + "lib.tb_external_framework_integration.Test that doesn't stop the simulation on error")]) + + def test_vhdl_check_example_project(self): + self.check(join(ROOT, "examples", "vhdl", "check", "run.py")) + + def test_vhdl_generate_tests_example_project(self): + self.check(join(ROOT, "examples", "vhdl", "generate_tests", "run.py")) + check_report(self.report_file, + [("passed", "lib.tb_generated.data_width=1,sign=False.Test 1"), + ("passed", "lib.tb_generated.data_width=1,sign=True.Test 1"), + ("passed", "lib.tb_generated.data_width=2,sign=False.Test 1"), + ("passed", "lib.tb_generated.data_width=2,sign=True.Test 1"), + ("passed", "lib.tb_generated.data_width=3,sign=False.Test 1"), + ("passed", "lib.tb_generated.data_width=3,sign=True.Test 1"), + ("passed", "lib.tb_generated.data_width=4,sign=False.Test 1"), + ("passed", "lib.tb_generated.data_width=4,sign=True.Test 1"), + ("passed", "lib.tb_generated.data_width=16,sign=True.Test 2")]) + + def test_vhdl_composite_generics_example_project(self): + self.check(join(ROOT, "examples", "vhdl", "composite_generics", "run.py")) + check_report(self.report_file, + [("passed", "tb_lib.tb_composite_generics.VGA.Test 1"), + ("passed", "tb_lib.tb_composite_generics.tiny.Test 1")]) + + def test_vhdl_json4vhdl_example_project(self): + self.check(join(ROOT, "examples", "vhdl", "json4vhdl", "run.py")) + + def test_vhdl_array_example_project(self): + self.check(join(ROOT, "examples", "vhdl", "array", "run.py")) + + def test_vhdl_array_axis_vcs_example_project(self): + self.check(join(ROOT, "examples", "vhdl", "array_axis_vcs", "run.py")) + + def test_vhdl_axi_dma_example_project(self): + self.check(join(ROOT, "examples", "vhdl", "axi_dma", "run.py")) + + def test_vhdl_user_guide_example_project(self): + self.check(join(ROOT, "examples", "vhdl", "user_guide", "run.py"), exit_code=1) + check_report(self.report_file, + [("passed", "lib.tb_example.all"), + ("passed", "lib.tb_example_many.test_pass"), + ("failed", "lib.tb_example_many.test_fail")]) + + @unittest.skipUnless(simulator_supports_verilog(), "Verilog") + def test_verilog_user_guide_example_project(self): + self.check(join(ROOT, "examples", "verilog", "user_guide", "run.py"), exit_code=1) + check_report(self.report_file, + [("passed", "lib.tb_example_basic.all"), + ("passed", "lib.tb_example.Test that a successful test case passes"), + ("failed", "lib.tb_example.Test that a failing test case actually fails"), + ("failed", "lib.tb_example.Test that a test case that takes too long time fails with a timeout")]) + + def test_vhdl_com_example_project(self): + self.check(join(ROOT, "examples", "vhdl", "com", "run.py")) + + def test_array_vhdl_2008(self): + self.check(join(VHDL_PATH, "array", "run.py")) + + def test_data_types_vhdl_2008(self): + self.check(join(VHDL_PATH, "data_types", "run.py")) + + def test_data_types_vhdl_2002(self): + self.check(join(VHDL_PATH, "data_types", "run.py"), + vhdl_standard="2002") + + def test_data_types_vhdl_93(self): + self.check(join(VHDL_PATH, "data_types", "run.py"), + vhdl_standard="93") + + def test_random_vhdl_2008(self): + self.check(join(VHDL_PATH, "random", "run.py")) + + def test_check_vhdl_2008(self): + self.check(join(VHDL_PATH, "check", "run.py")) + + def test_check_vhdl_2002(self): + self.check(join(VHDL_PATH, "check", "run.py"), + vhdl_standard='2002') + + def test_check_vhdl_93(self): + self.check(join(VHDL_PATH, "check", "run.py"), + vhdl_standard='93') + + def test_logging_vhdl_2008(self): + self.check(join(VHDL_PATH, "logging", "run.py")) + + def test_logging_vhdl_2002(self): + self.check(join(VHDL_PATH, "logging", "run.py"), + vhdl_standard='2002') + + def test_logging_vhdl_93(self): + self.check(join(VHDL_PATH, "logging", "run.py"), + vhdl_standard='93') + + def test_run_vhdl_2008(self): + self.check(join(VHDL_PATH, "run", "run.py")) + + def test_run_vhdl_2002(self): + self.check(join(VHDL_PATH, "run", "run.py"), + vhdl_standard='2002') + + def test_run_vhdl_93(self): + self.check(join(VHDL_PATH, "run", "run.py"), + vhdl_standard='93') + + def test_string_ops_vhdl_2008(self): + self.check(join(VHDL_PATH, "string_ops", "run.py")) + + def test_string_ops_vhdl_2002(self): + self.check(join(VHDL_PATH, "string_ops", "run.py"), + vhdl_standard='2002') + + def test_string_ops_vhdl_93(self): + self.check(join(VHDL_PATH, "string_ops", "run.py"), + vhdl_standard='93') + + def test_dictionary_vhdl_2008(self): + self.check(join(VHDL_PATH, "dictionary", "run.py")) + + def test_dictionary_vhdl_2002(self): + self.check(join(VHDL_PATH, "dictionary", "run.py"), + vhdl_standard='2002') + + def test_dictionary_vhdl_93(self): + self.check(join(VHDL_PATH, "dictionary", "run.py"), + vhdl_standard='93') + + def test_path_vhdl_2008(self): + self.check(join(VHDL_PATH, "path", "run.py")) + + def test_path_vhdl_2002(self): + self.check(join(VHDL_PATH, "path", "run.py"), + vhdl_standard='2002') + + def test_path_vhdl_93(self): + self.check(join(VHDL_PATH, "path", "run.py"), + vhdl_standard='93') + + def test_com_vhdl_2008(self): + self.check(join(VHDL_PATH, "com", "run.py")) + + def setUp(self): + self.output_path = join(dirname(__file__), "external_run_out") + self.report_file = join(self.output_path, "xunit.xml") + + def check(self, run_file, args=None, vhdl_standard='2008', exit_code=0): + """ + Run external run file and verify exit code + """ + args = args if args is not None else [] + new_env = environ.copy() + new_env["VUNIT_VHDL_STANDARD"] = vhdl_standard + retcode = call([sys.executable, run_file, + "--clean", + "--output-path=%s" % self.output_path, + "--xunit-xml=%s" % self.report_file] + args, + env=new_env) + self.assertEqual(retcode, exit_code) diff --git a/vunit/test/common.py b/vunit/test/common.py index c96584b84..87496e47c 100644 --- a/vunit/test/common.py +++ b/vunit/test/common.py @@ -1,231 +1,231 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Common functions re-used between test cases -""" - - -from xml.etree import ElementTree -import contextlib -import functools -import os -import shutil -import random -from vunit.simulator_factory import SIMULATOR_FACTORY - - -def has_simulator(): - return SIMULATOR_FACTORY.has_simulator - - -def simulator_is(*names): - """ - Check that current simulator is any of names - """ - supported_names = [sim.name for sim in SIMULATOR_FACTORY.supported_simulators()] - for name in names: - assert name in supported_names - return SIMULATOR_FACTORY.select_simulator().name in names - - -def simulator_check(func): - """ - Check some method of the selected simulator - """ - simif = SIMULATOR_FACTORY.select_simulator() - if simif is None: - return False - return func(simif) - - -def check_report(report_file, tests=None): - """ - Check an XML report_file for the exact occurrence of specific test results - """ - tree = ElementTree.parse(report_file) - root = tree.getroot() - report = {} - for test in root.iter("testcase"): - status = "passed" - - if test.find("skipped") is not None: - status = "skipped" - - if test.find("failure") is not None: - status = "failed" - report[test.attrib["classname"] + "." + test.attrib["name"]] = status - - if tests is None: - tests = [] - for name in report: - expected_status = "failed" if "Expected to fail:" in name else "passed" - tests.append((expected_status, name)) - - for status, name in tests: - if report[name] != status: - raise AssertionError("Wrong status of %s got %s expected %s" % - (name, report[name], status)) - - num_tests = int(root.attrib["tests"]) - assert num_tests == len(tests) - - -def assert_exit(function, code=0): - """ - Assert that 'function' performs SystemExit with code - """ - try: - function() - except SystemExit as ex: - assert ex.code == code - else: - assert False - - -@contextlib.contextmanager -def set_env(**environ): - """ - Temporarily set the process environment variables. - - >>> with set_env(PLUGINS_DIR=u'test/plugins'): - ... "PLUGINS_DIR" in os.environ - True - - >>> "PLUGINS_DIR" in os.environ - False - - :type environ: dict[str, unicode] - :param environ: Environment variables to set - """ - old_environ = dict(os.environ) - os.environ.clear() - os.environ.update(environ) - try: - yield - finally: - os.environ.clear() - os.environ.update(old_environ) - - -@contextlib.contextmanager -def create_tempdir(path=None): - """ - Create a temporary directory that is removed after the unit test - """ - - if path is None: - path = os.path.join(os.path.dirname(__file__), - "tempdir_%i" % random.randint(0, 2**64 - 1)) - - if os.path.exists(path): - shutil.rmtree(path) - - os.makedirs(path) - - try: - yield path - finally: - shutil.rmtree(path) - - -def with_tempdir(func): - """ - Decorator to provide test function with a temporary path that is - removed after calling the function. - - The path is named the same as the function and its parent module - """ - @functools.wraps(func) - def new_function(*args, **kwargs): - """ - Wrapper funciton that maintains temporary directory around nested - function - """ - path_name = os.path.join(os.path.dirname(__file__), - func.__module__ + "." + func.__name__) - - with create_tempdir(path_name) as path: - return func(*args, tempdir=path, **kwargs) - - return new_function - - -def get_vhdl_test_bench(test_bench_name, - tests=None, - same_sim=False, - test_attributes=None): - """ - Create a valid VUnit test bench - - returns a string - """ - - if test_attributes is None: - test_attributes = {} - - tests_contents = "" - if tests is None: - pass - else: - tests_contents = "" - last_idx = len(tests) - for idx, test_name in enumerate(tests): - if idx == 0: - tests_contents += " if " - else: - tests_contents += " elsif " - - tests_contents += 'run("%s") then\n' % test_name - - if test_name in test_attributes: - for attr_name in test_attributes[test_name]: - tests_contents += "-- vunit: %s\n" % attr_name - - if idx == last_idx: - tests_contents += ' endif;\n' - - if same_sim: - contents = "-- vunit: run_all_in_same_sim\n" - else: - contents = "\n" - - contents += """\ -library vunit_lib; -context vunit_lib.vunit_context; - -entity {test_bench_name} is - generic (runner_cfg : string); -end entity; - -architecture a of {test_bench_name} is -begin - main : process - begin - test_runner_setup(runner, runner_cfg); - {tests_contents} - test_runner_cleanup(runner); - end process; -end architecture; -""".format(test_bench_name=test_bench_name, - tests_contents=tests_contents) - - return contents - - -def create_vhdl_test_bench_file(test_bench_name, - file_name, - tests=None, - same_sim=False, - test_attributes=None): - """ - Create a valid VUnit test bench and writes it to file_name - """ - with open(file_name, "wb") as fptr: - fptr.write(get_vhdl_test_bench(test_bench_name=test_bench_name, - tests=tests, - same_sim=same_sim, - test_attributes=test_attributes).encode("utf-8")) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Common functions re-used between test cases +""" + + +from xml.etree import ElementTree +import contextlib +import functools +import os +import shutil +import random +from vunit.simulator_factory import SIMULATOR_FACTORY + + +def has_simulator(): + return SIMULATOR_FACTORY.has_simulator + + +def simulator_is(*names): + """ + Check that current simulator is any of names + """ + supported_names = [sim.name for sim in SIMULATOR_FACTORY.supported_simulators()] + for name in names: + assert name in supported_names + return SIMULATOR_FACTORY.select_simulator().name in names + + +def simulator_check(func): + """ + Check some method of the selected simulator + """ + simif = SIMULATOR_FACTORY.select_simulator() + if simif is None: + return False + return func(simif) + + +def check_report(report_file, tests=None): + """ + Check an XML report_file for the exact occurrence of specific test results + """ + tree = ElementTree.parse(report_file) + root = tree.getroot() + report = {} + for test in root.iter("testcase"): + status = "passed" + + if test.find("skipped") is not None: + status = "skipped" + + if test.find("failure") is not None: + status = "failed" + report[test.attrib["classname"] + "." + test.attrib["name"]] = status + + if tests is None: + tests = [] + for name in report: + expected_status = "failed" if "Expected to fail:" in name else "passed" + tests.append((expected_status, name)) + + for status, name in tests: + if report[name] != status: + raise AssertionError("Wrong status of %s got %s expected %s" % + (name, report[name], status)) + + num_tests = int(root.attrib["tests"]) + assert num_tests == len(tests) + + +def assert_exit(function, code=0): + """ + Assert that 'function' performs SystemExit with code + """ + try: + function() + except SystemExit as ex: + assert ex.code == code + else: + assert False + + +@contextlib.contextmanager +def set_env(**environ): + """ + Temporarily set the process environment variables. + + >>> with set_env(PLUGINS_DIR=u'test/plugins'): + ... "PLUGINS_DIR" in os.environ + True + + >>> "PLUGINS_DIR" in os.environ + False + + :type environ: dict[str, unicode] + :param environ: Environment variables to set + """ + old_environ = dict(os.environ) + os.environ.clear() + os.environ.update(environ) + try: + yield + finally: + os.environ.clear() + os.environ.update(old_environ) + + +@contextlib.contextmanager +def create_tempdir(path=None): + """ + Create a temporary directory that is removed after the unit test + """ + + if path is None: + path = os.path.join(os.path.dirname(__file__), + "tempdir_%i" % random.randint(0, 2**64 - 1)) + + if os.path.exists(path): + shutil.rmtree(path) + + os.makedirs(path) + + try: + yield path + finally: + shutil.rmtree(path) + + +def with_tempdir(func): + """ + Decorator to provide test function with a temporary path that is + removed after calling the function. + + The path is named the same as the function and its parent module + """ + @functools.wraps(func) + def new_function(*args, **kwargs): + """ + Wrapper funciton that maintains temporary directory around nested + function + """ + path_name = os.path.join(os.path.dirname(__file__), + func.__module__ + "." + func.__name__) + + with create_tempdir(path_name) as path: + return func(*args, tempdir=path, **kwargs) + + return new_function + + +def get_vhdl_test_bench(test_bench_name, + tests=None, + same_sim=False, + test_attributes=None): + """ + Create a valid VUnit test bench + + returns a string + """ + + if test_attributes is None: + test_attributes = {} + + tests_contents = "" + if tests is None: + pass + else: + tests_contents = "" + last_idx = len(tests) + for idx, test_name in enumerate(tests): + if idx == 0: + tests_contents += " if " + else: + tests_contents += " elsif " + + tests_contents += 'run("%s") then\n' % test_name + + if test_name in test_attributes: + for attr_name in test_attributes[test_name]: + tests_contents += "-- vunit: %s\n" % attr_name + + if idx == last_idx: + tests_contents += ' endif;\n' + + if same_sim: + contents = "-- vunit: run_all_in_same_sim\n" + else: + contents = "\n" + + contents += """\ +library vunit_lib; +context vunit_lib.vunit_context; + +entity {test_bench_name} is + generic (runner_cfg : string); +end entity; + +architecture a of {test_bench_name} is +begin + main : process + begin + test_runner_setup(runner, runner_cfg); + {tests_contents} + test_runner_cleanup(runner); + end process; +end architecture; +""".format(test_bench_name=test_bench_name, + tests_contents=tests_contents) + + return contents + + +def create_vhdl_test_bench_file(test_bench_name, + file_name, + tests=None, + same_sim=False, + test_attributes=None): + """ + Create a valid VUnit test bench and writes it to file_name + """ + with open(file_name, "wb") as fptr: + fptr.write(get_vhdl_test_bench(test_bench_name=test_bench_name, + tests=tests, + same_sim=same_sim, + test_attributes=test_attributes).encode("utf-8")) diff --git a/vunit/test/lint/__init__.py b/vunit/test/lint/__init__.py index a66885c1d..17b27373b 100644 --- a/vunit/test/lint/__init__.py +++ b/vunit/test/lint/__init__.py @@ -1,5 +1,5 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com diff --git a/vunit/test/lint/test_license.py b/vunit/test/lint/test_license.py index 4bcd83fc0..ea291cdab 100644 --- a/vunit/test/lint/test_license.py +++ b/vunit/test/lint/test_license.py @@ -1,149 +1,149 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -License header sanity check -""" -from __future__ import print_function - -import unittest -from warnings import simplefilter, catch_warnings -from os.path import join, splitext, abspath, commonprefix -from os import walk -import re -from vunit import ROOT -from vunit.builtins import VHDL_PATH -from vunit import ostools -from vunit.about import license_text - -RE_LICENSE_NOTICE = re.compile( - r"(?P#|--|//) This Source Code Form is subject to the terms of the Mozilla Public" + "\n" - r"(?P=comment_start) License, v\. 2\.0\. If a copy of the MPL was not distributed with this file," + "\n" - r"(?P=comment_start) You can obtain one at http://mozilla\.org/MPL/2\.0/\." + "\n" - r"(?P=comment_start)" + "\n" - r"(?P=comment_start) Copyright \(c\) (?P20\d\d)(-(?P20\d\d))?, " - + r"Lars Asplund lars\.anders\.asplund@gmail\.com") - -RE_LOG_DATE = re.compile(r'Date:\s*(?P20\d\d)-\d\d-\d\d') - -FIRST_YEAR = 2014 -LAST_YEAR = 2019 - - -class TestLicense(unittest.TestCase): - """ - Test that each file in the repository contains a valid license - header with a correct year range. - The correct year range is computed based on the commit history. - """ - - def test_that_a_valid_license_exists_in_source_files_and_that_global_licensing_information_is_correct(self): - for file_name in find_licensed_files(): - code = ostools.read_file(file_name) - self._check_license(code, file_name) - if splitext(file_name)[1] in ('.vhd', '.vhdl', '.v', '.sv'): - self._check_no_trailing_whitespace(code, file_name) - - def test_that_license_file_matches_vunit_license_text(self): - with catch_warnings(): - simplefilter("ignore", category=DeprecationWarning) - with open(join(ROOT, 'LICENSE.txt'), "rU") as lic: - self.assertEqual(lic.read(), license_text()) - - def _check_license(self, code, file_name): - """ - Check that the license header of file_name is valid - """ - - match = RE_LICENSE_NOTICE.search(code) - self.assertIsNotNone(match, "Failed to find license notice in %s" % file_name) - self.assertEqual(int(match.group('first_year')), FIRST_YEAR, - 'Expected copyright year range to start with %d in %s' % (FIRST_YEAR, file_name)) - self.assertEqual(int(match.group('last_year')), LAST_YEAR, - 'Expected copyright year range to end with %d in %s' % (LAST_YEAR, file_name)) - - @staticmethod - def _check_no_trailing_whitespace(code, file_name): - """ - Check that there is no trailing whitespace within the code - """ - for idx, line in enumerate(code.splitlines()): - sline = line.rstrip() - if sline == line: - continue - line_prefix = "%i: " % (idx + 1) - print("Trailing whitespace violation in %s" % file_name) - print(line_prefix + line) - for _ in range(len(line_prefix) + len(sline)): - print(" ", end="") - for _ in range(len(line) - len(sline)): - print("~", end="") - print() - raise AssertionError("Line %i of %s contains trailing whitespace" % (idx + 1, file_name)) - - -def fix_license(file_name): - """ - Fix license notice in file - """ - - with open(file_name, "r") as fptr: - text = fptr.read() - - replacement = "Copyright (c) %i-%i, Lars Asplund lars.anders.asplund@gmail.com" % (FIRST_YEAR, LAST_YEAR) - - text = re.sub(r"Copyright \(c\) (?P20\d\d)(-(?P20\d\d))?, " - r"Lars Asplund lars\.anders\.asplund@gmail\.com", - replacement, - text) - - with open(file_name, "w") as fptr: - fptr.write(text) - - -def find_licensed_files(): - """ - Return all licensed files - """ - licensed_files = [] - osvvm_directory = abspath(join(VHDL_PATH, 'osvvm')) - json4vhdl_directory = abspath(join(VHDL_PATH, 'JSON-for-VHDL')) - for root, _, files in walk(ROOT): - for file_name in files: - if 'preprocessed' in root: - continue - if 'codecs' in root: - continue - if root == join(ROOT, "docs"): - continue - if join(ROOT, ".tox") in root: - continue - if is_prefix_of(osvvm_directory, abspath(join(root, file_name))): - continue - if is_prefix_of(json4vhdl_directory, abspath(join(root, file_name))): - continue - if splitext(file_name)[1] in ('.vhd', '.vhdl', '.py', '.v', '.sv'): - licensed_files.append(join(root, file_name)) - return licensed_files - - -def is_prefix_of(prefix, of_path): - """ - Return True if 'prefix' is a prefix of 'of_path' - """ - return commonprefix([prefix, of_path]) == prefix - - -def main(): - """ - Fix license notice in all licensed files - """ - for file_name in find_licensed_files(): - fix_license(file_name) - - -if __name__ == "__main__": - main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +License header sanity check +""" +from __future__ import print_function + +import unittest +from warnings import simplefilter, catch_warnings +from os.path import join, splitext, abspath, commonprefix +from os import walk +import re +from vunit import ROOT +from vunit.builtins import VHDL_PATH +from vunit import ostools +from vunit.about import license_text + +RE_LICENSE_NOTICE = re.compile( + r"(?P#|--|//) This Source Code Form is subject to the terms of the Mozilla Public" + "\n" + r"(?P=comment_start) License, v\. 2\.0\. If a copy of the MPL was not distributed with this file," + "\n" + r"(?P=comment_start) You can obtain one at http://mozilla\.org/MPL/2\.0/\." + "\n" + r"(?P=comment_start)" + "\n" + r"(?P=comment_start) Copyright \(c\) (?P20\d\d)(-(?P20\d\d))?, " + + r"Lars Asplund lars\.anders\.asplund@gmail\.com") + +RE_LOG_DATE = re.compile(r'Date:\s*(?P20\d\d)-\d\d-\d\d') + +FIRST_YEAR = 2014 +LAST_YEAR = 2019 + + +class TestLicense(unittest.TestCase): + """ + Test that each file in the repository contains a valid license + header with a correct year range. + The correct year range is computed based on the commit history. + """ + + def test_that_a_valid_license_exists_in_source_files_and_that_global_licensing_information_is_correct(self): + for file_name in find_licensed_files(): + code = ostools.read_file(file_name) + self._check_license(code, file_name) + if splitext(file_name)[1] in ('.vhd', '.vhdl', '.v', '.sv'): + self._check_no_trailing_whitespace(code, file_name) + + def test_that_license_file_matches_vunit_license_text(self): + with catch_warnings(): + simplefilter("ignore", category=DeprecationWarning) + with open(join(ROOT, 'LICENSE.txt'), "rU") as lic: + self.assertEqual(lic.read(), license_text()) + + def _check_license(self, code, file_name): + """ + Check that the license header of file_name is valid + """ + + match = RE_LICENSE_NOTICE.search(code) + self.assertIsNotNone(match, "Failed to find license notice in %s" % file_name) + self.assertEqual(int(match.group('first_year')), FIRST_YEAR, + 'Expected copyright year range to start with %d in %s' % (FIRST_YEAR, file_name)) + self.assertEqual(int(match.group('last_year')), LAST_YEAR, + 'Expected copyright year range to end with %d in %s' % (LAST_YEAR, file_name)) + + @staticmethod + def _check_no_trailing_whitespace(code, file_name): + """ + Check that there is no trailing whitespace within the code + """ + for idx, line in enumerate(code.splitlines()): + sline = line.rstrip() + if sline == line: + continue + line_prefix = "%i: " % (idx + 1) + print("Trailing whitespace violation in %s" % file_name) + print(line_prefix + line) + for _ in range(len(line_prefix) + len(sline)): + print(" ", end="") + for _ in range(len(line) - len(sline)): + print("~", end="") + print() + raise AssertionError("Line %i of %s contains trailing whitespace" % (idx + 1, file_name)) + + +def fix_license(file_name): + """ + Fix license notice in file + """ + + with open(file_name, "r") as fptr: + text = fptr.read() + + replacement = "Copyright (c) %i-%i, Lars Asplund lars.anders.asplund@gmail.com" % (FIRST_YEAR, LAST_YEAR) + + text = re.sub(r"Copyright \(c\) (?P20\d\d)(-(?P20\d\d))?, " + r"Lars Asplund lars\.anders\.asplund@gmail\.com", + replacement, + text) + + with open(file_name, "w") as fptr: + fptr.write(text) + + +def find_licensed_files(): + """ + Return all licensed files + """ + licensed_files = [] + osvvm_directory = abspath(join(VHDL_PATH, 'osvvm')) + json4vhdl_directory = abspath(join(VHDL_PATH, 'JSON-for-VHDL')) + for root, _, files in walk(ROOT): + for file_name in files: + if 'preprocessed' in root: + continue + if 'codecs' in root: + continue + if root == join(ROOT, "docs"): + continue + if join(ROOT, ".tox") in root: + continue + if is_prefix_of(osvvm_directory, abspath(join(root, file_name))): + continue + if is_prefix_of(json4vhdl_directory, abspath(join(root, file_name))): + continue + if splitext(file_name)[1] in ('.vhd', '.vhdl', '.py', '.v', '.sv'): + licensed_files.append(join(root, file_name)) + return licensed_files + + +def is_prefix_of(prefix, of_path): + """ + Return True if 'prefix' is a prefix of 'of_path' + """ + return commonprefix([prefix, of_path]) == prefix + + +def main(): + """ + Fix license notice in all licensed files + """ + for file_name in find_licensed_files(): + fix_license(file_name) + + +if __name__ == "__main__": + main() diff --git a/vunit/test/lint/test_pycodestyle.py b/vunit/test/lint/test_pycodestyle.py index 07fada8d3..921f7e968 100644 --- a/vunit/test/lint/test_pycodestyle.py +++ b/vunit/test/lint/test_pycodestyle.py @@ -1,41 +1,41 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -PEP8 check -""" - -import unittest -from subprocess import check_call -import sys -from glob import glob -from os.path import join -from vunit import ROOT - - -class TestPycodestyle(unittest.TestCase): - """ - Test that all python code follows PEP8 Python coding standard - """ - @staticmethod - def test_pycodestyle(): - check_call([sys.executable, "-m", "pycodestyle", - "--show-source", - "--show-pep8", - "--max-line-length=120", - # W503 mutually exclusive with W504 - # E722 bare except checked by pylint - "--ignore=E402,W503,E722"] + get_files_and_folders()) - - -def get_files_and_folders(): - """ - Return all files and folders which shall be arguments to pycodestyle and pylint - """ - ret = [join(ROOT, "vunit")] - ret += list(glob(join(ROOT, "*.py"))) - ret += list(glob(join(ROOT, "tools", "*.py"))) - return ret +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +PEP8 check +""" + +import unittest +from subprocess import check_call +import sys +from glob import glob +from os.path import join +from vunit import ROOT + + +class TestPycodestyle(unittest.TestCase): + """ + Test that all python code follows PEP8 Python coding standard + """ + @staticmethod + def test_pycodestyle(): + check_call([sys.executable, "-m", "pycodestyle", + "--show-source", + "--show-pep8", + "--max-line-length=120", + # W503 mutually exclusive with W504 + # E722 bare except checked by pylint + "--ignore=E402,W503,E722"] + get_files_and_folders()) + + +def get_files_and_folders(): + """ + Return all files and folders which shall be arguments to pycodestyle and pylint + """ + ret = [join(ROOT, "vunit")] + ret += list(glob(join(ROOT, "*.py"))) + ret += list(glob(join(ROOT, "tools", "*.py"))) + return ret diff --git a/vunit/test/lint/test_pylint.py b/vunit/test/lint/test_pylint.py index 61f9e77ae..0ba87599c 100644 --- a/vunit/test/lint/test_pylint.py +++ b/vunit/test/lint/test_pylint.py @@ -1,27 +1,27 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Pylint check -""" - - -import unittest -from subprocess import check_call -from os.path import join, dirname -import sys -from vunit.test.lint.test_pycodestyle import get_files_and_folders - - -class TestPylint(unittest.TestCase): - """ - Check that there are no pylint errors or warnings - """ - @staticmethod - def test_pylint(): - check_call([sys.executable, "-m", "pylint", - "--rcfile=" + join(dirname(__file__), "pylintrc")] - + get_files_and_folders()) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Pylint check +""" + + +import unittest +from subprocess import check_call +from os.path import join, dirname +import sys +from vunit.test.lint.test_pycodestyle import get_files_and_folders + + +class TestPylint(unittest.TestCase): + """ + Check that there are no pylint errors or warnings + """ + @staticmethod + def test_pylint(): + check_call([sys.executable, "-m", "pylint", + "--rcfile=" + join(dirname(__file__), "pylintrc")] + + get_files_and_folders()) diff --git a/vunit/test/lint/test_readme.py b/vunit/test/lint/test_readme.py index aa78884f1..4201b55da 100644 --- a/vunit/test/lint/test_readme.py +++ b/vunit/test/lint/test_readme.py @@ -1,27 +1,27 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Check that README.rst matches VUnit docstring -""" - -import unittest -from warnings import simplefilter, catch_warnings -from os.path import join -from vunit import ROOT -from vunit.about import doc - - -class TestReadMe(unittest.TestCase): - """ - Check that README.rst matches VUnit docstring - """ - - def test_that_readme_file_matches_vunit_docstring(self): - with catch_warnings(): - simplefilter("ignore", category=DeprecationWarning) - with open(join(ROOT, 'README.rst'), "rU") as readme: - self.assertEqual(readme.read(), doc()) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Check that README.rst matches VUnit docstring +""" + +import unittest +from warnings import simplefilter, catch_warnings +from os.path import join +from vunit import ROOT +from vunit.about import doc + + +class TestReadMe(unittest.TestCase): + """ + Check that README.rst matches VUnit docstring + """ + + def test_that_readme_file_matches_vunit_docstring(self): + with catch_warnings(): + simplefilter("ignore", category=DeprecationWarning) + with open(join(ROOT, 'README.rst'), "rU") as readme: + self.assertEqual(readme.read(), doc()) diff --git a/vunit/test/mock_2or3.py b/vunit/test/mock_2or3.py index 3310395a2..f8168c6f7 100644 --- a/vunit/test/mock_2or3.py +++ b/vunit/test/mock_2or3.py @@ -1,17 +1,17 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Wrapper arround mock to handle Python 3.x and 2.7 -""" - - -try: - # Python 3.x (builtin) - from unittest import mock # pylint: disable=import-error, no-name-in-module, unused-import -except ImportError: - # Python 2.7 (needs separate install) - import mock # pylint: disable=import-error, unused-import +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Wrapper arround mock to handle Python 3.x and 2.7 +""" + + +try: + # Python 3.x (builtin) + from unittest import mock # pylint: disable=import-error, no-name-in-module, unused-import +except ImportError: + # Python 2.7 (needs separate install) + import mock # pylint: disable=import-error, unused-import diff --git a/vunit/test/unit/__init__.py b/vunit/test/unit/__init__.py index a66885c1d..17b27373b 100644 --- a/vunit/test/unit/__init__.py +++ b/vunit/test/unit/__init__.py @@ -1,5 +1,5 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com diff --git a/vunit/test/unit/non_utf8_printer.py b/vunit/test/unit/non_utf8_printer.py index ee2600a51..ef5a2c669 100644 --- a/vunit/test/unit/non_utf8_printer.py +++ b/vunit/test/unit/non_utf8_printer.py @@ -1,17 +1,17 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -# pylint: skip-file -from sys import stdout - -if hasattr(stdout, "buffer"): - # Python 3 - stdout.buffer.write(b'a' + b"\x87" + b'c') -else: - # Python 2.7 - stdout.write(b'a' + b"\x87" + b'c') - -stdout.write("\n") +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +# pylint: skip-file +from sys import stdout + +if hasattr(stdout, "buffer"): + # Python 3 + stdout.buffer.write(b'a' + b"\x87" + b'c') +else: + # Python 2.7 + stdout.write(b'a' + b"\x87" + b'c') + +stdout.write("\n") diff --git a/vunit/test/unit/test_activehdl_interface.py b/vunit/test/unit/test_activehdl_interface.py index 8fc9e1624..a69855ea4 100644 --- a/vunit/test/unit/test_activehdl_interface.py +++ b/vunit/test/unit/test_activehdl_interface.py @@ -1,233 +1,233 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test the ActiveHDL interface -""" - - -import unittest -from os.path import join, dirname, exists -import os -from shutil import rmtree -from vunit.activehdl_interface import ActiveHDLInterface -from vunit.test.mock_2or3 import mock -from vunit.project import Project -from vunit.ostools import renew_path, write_file - - -class TestActiveHDLInterface(unittest.TestCase): - """ - Test the ActiveHDL interface - """ - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.activehdl_interface.Process", autospec=True) - def test_compile_project_vhdl(self, process, check_output): - simif = ActiveHDLInterface(prefix="prefix", - output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - project.add_source_file("file.vhd", "lib", file_type="vhdl") - simif.compile_project(project) - process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], - cwd=self.output_path, - env=simif.get_env()) - process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], - cwd=self.output_path, - env=simif.get_env()) - check_output.assert_called_once_with( - [join('prefix', 'vcom'), - '-quiet', - '-j', - self.output_path, - '-2008', - '-work', - 'lib', - 'file.vhd'], - env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.activehdl_interface.Process", autospec=True) - def test_compile_project_vhdl_extra_flags(self, process, check_output): - simif = ActiveHDLInterface(prefix="prefix", - output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - source_file = project.add_source_file("file.vhd", "lib", file_type="vhdl") - source_file.set_compile_option("activehdl.vcom_flags", ["custom", "flags"]) - simif.compile_project(project) - process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], - cwd=self.output_path, - env=simif.get_env()) - process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], - cwd=self.output_path, - env=simif.get_env()) - check_output.assert_called_once_with([join('prefix', 'vcom'), - '-quiet', - '-j', - self.output_path, - 'custom', - 'flags', - '-2008', - '-work', - 'lib', - 'file.vhd'], - env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.activehdl_interface.Process", autospec=True) - def test_compile_project_verilog(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") - simif = ActiveHDLInterface(prefix="prefix", - output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.v", "") - project.add_source_file("file.v", "lib", file_type="verilog") - simif.compile_project(project) - process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], - cwd=self.output_path, - env=simif.get_env()) - process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], - cwd=self.output_path, - env=simif.get_env()) - check_output.assert_called_once_with([join('prefix', 'vlog'), - '-quiet', - '-lc', - library_cfg, - '-work', - 'lib', - 'file.v', - '-l', 'lib'], - env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.activehdl_interface.Process", autospec=True) - def test_compile_project_system_verilog(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") - simif = ActiveHDLInterface(prefix="prefix", - output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.sv", "") - project.add_source_file("file.sv", "lib", file_type="systemverilog") - simif.compile_project(project) - process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], - cwd=self.output_path, - env=simif.get_env()) - process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], - cwd=self.output_path, - env=simif.get_env()) - check_output.assert_called_once_with([join('prefix', 'vlog'), - '-quiet', - '-lc', - library_cfg, - '-work', - 'lib', - 'file.sv', - '-l', 'lib'], - env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.activehdl_interface.Process", autospec=True) - def test_compile_project_verilog_extra_flags(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") - simif = ActiveHDLInterface(prefix="prefix", - output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.v", "") - source_file = project.add_source_file("file.v", "lib", file_type="verilog") - source_file.set_compile_option("activehdl.vlog_flags", ["custom", "flags"]) - simif.compile_project(project) - process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], - cwd=self.output_path, - env=simif.get_env()) - process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], - cwd=self.output_path, - env=simif.get_env()) - check_output.assert_called_once_with([join('prefix', 'vlog'), - '-quiet', - '-lc', - library_cfg, - 'custom', - 'flags', - '-work', - 'lib', - 'file.v', - '-l', 'lib'], - env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.activehdl_interface.Process", autospec=True) - def test_compile_project_verilog_include(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") - simif = ActiveHDLInterface(prefix="prefix", - output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.v", "") - project.add_source_file("file.v", "lib", file_type="verilog", include_dirs=["include"]) - simif.compile_project(project) - process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], - cwd=self.output_path, - env=simif.get_env()) - process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], - cwd=self.output_path, - env=simif.get_env()) - check_output.assert_called_once_with([join('prefix', 'vlog'), - '-quiet', - '-lc', - library_cfg, - '-work', - 'lib', - 'file.v', - '-l', 'lib', - '+incdir+include'], - env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.activehdl_interface.Process", autospec=True) - def test_compile_project_verilog_define(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") - simif = ActiveHDLInterface(prefix="prefix", - output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.v", "") - project.add_source_file("file.v", "lib", file_type="verilog", defines={"defname": "defval"}) - simif.compile_project(project) - process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], - cwd=self.output_path, - env=simif.get_env()) - process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], - cwd=self.output_path, - env=simif.get_env()) - check_output.assert_called_once_with([join('prefix', 'vlog'), - '-quiet', - '-lc', - library_cfg, - '-work', - 'lib', - 'file.v', - '-l', 'lib', - '+define+defname=defval'], - env=simif.get_env()) - - def setUp(self): - self.output_path = join(dirname(__file__), "test_activehdl_out") - renew_path(self.output_path) - self.project = Project() - self.cwd = os.getcwd() - os.chdir(self.output_path) - - def tearDown(self): - os.chdir(self.cwd) - if exists(self.output_path): - rmtree(self.output_path) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test the ActiveHDL interface +""" + + +import unittest +from os.path import join, dirname, exists +import os +from shutil import rmtree +from vunit.activehdl_interface import ActiveHDLInterface +from vunit.test.mock_2or3 import mock +from vunit.project import Project +from vunit.ostools import renew_path, write_file + + +class TestActiveHDLInterface(unittest.TestCase): + """ + Test the ActiveHDL interface + """ + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.activehdl_interface.Process", autospec=True) + def test_compile_project_vhdl(self, process, check_output): + simif = ActiveHDLInterface(prefix="prefix", + output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + project.add_source_file("file.vhd", "lib", file_type="vhdl") + simif.compile_project(project) + process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], + cwd=self.output_path, + env=simif.get_env()) + process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], + cwd=self.output_path, + env=simif.get_env()) + check_output.assert_called_once_with( + [join('prefix', 'vcom'), + '-quiet', + '-j', + self.output_path, + '-2008', + '-work', + 'lib', + 'file.vhd'], + env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.activehdl_interface.Process", autospec=True) + def test_compile_project_vhdl_extra_flags(self, process, check_output): + simif = ActiveHDLInterface(prefix="prefix", + output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + source_file = project.add_source_file("file.vhd", "lib", file_type="vhdl") + source_file.set_compile_option("activehdl.vcom_flags", ["custom", "flags"]) + simif.compile_project(project) + process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], + cwd=self.output_path, + env=simif.get_env()) + process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], + cwd=self.output_path, + env=simif.get_env()) + check_output.assert_called_once_with([join('prefix', 'vcom'), + '-quiet', + '-j', + self.output_path, + 'custom', + 'flags', + '-2008', + '-work', + 'lib', + 'file.vhd'], + env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.activehdl_interface.Process", autospec=True) + def test_compile_project_verilog(self, process, check_output): + library_cfg = join(self.output_path, "library.cfg") + simif = ActiveHDLInterface(prefix="prefix", + output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.v", "") + project.add_source_file("file.v", "lib", file_type="verilog") + simif.compile_project(project) + process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], + cwd=self.output_path, + env=simif.get_env()) + process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], + cwd=self.output_path, + env=simif.get_env()) + check_output.assert_called_once_with([join('prefix', 'vlog'), + '-quiet', + '-lc', + library_cfg, + '-work', + 'lib', + 'file.v', + '-l', 'lib'], + env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.activehdl_interface.Process", autospec=True) + def test_compile_project_system_verilog(self, process, check_output): + library_cfg = join(self.output_path, "library.cfg") + simif = ActiveHDLInterface(prefix="prefix", + output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.sv", "") + project.add_source_file("file.sv", "lib", file_type="systemverilog") + simif.compile_project(project) + process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], + cwd=self.output_path, + env=simif.get_env()) + process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], + cwd=self.output_path, + env=simif.get_env()) + check_output.assert_called_once_with([join('prefix', 'vlog'), + '-quiet', + '-lc', + library_cfg, + '-work', + 'lib', + 'file.sv', + '-l', 'lib'], + env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.activehdl_interface.Process", autospec=True) + def test_compile_project_verilog_extra_flags(self, process, check_output): + library_cfg = join(self.output_path, "library.cfg") + simif = ActiveHDLInterface(prefix="prefix", + output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.v", "") + source_file = project.add_source_file("file.v", "lib", file_type="verilog") + source_file.set_compile_option("activehdl.vlog_flags", ["custom", "flags"]) + simif.compile_project(project) + process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], + cwd=self.output_path, + env=simif.get_env()) + process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], + cwd=self.output_path, + env=simif.get_env()) + check_output.assert_called_once_with([join('prefix', 'vlog'), + '-quiet', + '-lc', + library_cfg, + 'custom', + 'flags', + '-work', + 'lib', + 'file.v', + '-l', 'lib'], + env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.activehdl_interface.Process", autospec=True) + def test_compile_project_verilog_include(self, process, check_output): + library_cfg = join(self.output_path, "library.cfg") + simif = ActiveHDLInterface(prefix="prefix", + output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.v", "") + project.add_source_file("file.v", "lib", file_type="verilog", include_dirs=["include"]) + simif.compile_project(project) + process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], + cwd=self.output_path, + env=simif.get_env()) + process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], + cwd=self.output_path, + env=simif.get_env()) + check_output.assert_called_once_with([join('prefix', 'vlog'), + '-quiet', + '-lc', + library_cfg, + '-work', + 'lib', + 'file.v', + '-l', 'lib', + '+incdir+include'], + env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.activehdl_interface.Process", autospec=True) + def test_compile_project_verilog_define(self, process, check_output): + library_cfg = join(self.output_path, "library.cfg") + simif = ActiveHDLInterface(prefix="prefix", + output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.v", "") + project.add_source_file("file.v", "lib", file_type="verilog", defines={"defname": "defval"}) + simif.compile_project(project) + process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], + cwd=self.output_path, + env=simif.get_env()) + process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], + cwd=self.output_path, + env=simif.get_env()) + check_output.assert_called_once_with([join('prefix', 'vlog'), + '-quiet', + '-lc', + library_cfg, + '-work', + 'lib', + 'file.v', + '-l', 'lib', + '+define+defname=defval'], + env=simif.get_env()) + + def setUp(self): + self.output_path = join(dirname(__file__), "test_activehdl_out") + renew_path(self.output_path) + self.project = Project() + self.cwd = os.getcwd() + os.chdir(self.output_path) + + def tearDown(self): + os.chdir(self.cwd) + if exists(self.output_path): + rmtree(self.output_path) diff --git a/vunit/test/unit/test_builtins.py b/vunit/test/unit/test_builtins.py index fa096a51f..56c0c413e 100644 --- a/vunit/test/unit/test_builtins.py +++ b/vunit/test/unit/test_builtins.py @@ -1,58 +1,58 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test builtins.py -""" - -import unittest -from vunit.test.mock_2or3 import mock -from vunit.builtins import BuiltinsAdder - - -class TestBuiltinsAdder(unittest.TestCase): - """ - Test BuiltinsAdder class - """ - - @staticmethod - def test_add_type(): - adder = BuiltinsAdder() - function = mock.Mock() - adder.add_type("foo", function) - adder.add("foo", dict(argument=1)) - function.assert_called_once_with(argument=1) - - def test_adds_dependencies(self): - adder = BuiltinsAdder() - function1 = mock.Mock() - function2 = mock.Mock() - function3 = mock.Mock() - function4 = mock.Mock() - adder.add_type("foo1", function1) - adder.add_type("foo2", function2, ["foo1"]) - adder.add_type("foo3", function3, ["foo2"]) - adder.add_type("foo4", function4) - adder.add("foo3", dict(argument=1)) - adder.add("foo2") - function1.assert_called_once_with() - function2.assert_called_once_with() - function3.assert_called_once_with(argument=1) - self.assertFalse(function4.called) - - def test_runtime_error_on_add_with_different_args(self): - adder = BuiltinsAdder() - function = mock.Mock() - adder.add_type("foo", function) - adder.add("foo", dict(argument=1)) - try: - adder.add("foo", dict(argument=2)) - except RuntimeError as exc: - self.assertEqual(str(exc), - "Optional builtin %r added with arguments %r has already been added with arguments %r" - % ("foo", dict(argument=2), dict(argument=1))) - else: - self.fail("RuntimeError not raised") +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test builtins.py +""" + +import unittest +from vunit.test.mock_2or3 import mock +from vunit.builtins import BuiltinsAdder + + +class TestBuiltinsAdder(unittest.TestCase): + """ + Test BuiltinsAdder class + """ + + @staticmethod + def test_add_type(): + adder = BuiltinsAdder() + function = mock.Mock() + adder.add_type("foo", function) + adder.add("foo", dict(argument=1)) + function.assert_called_once_with(argument=1) + + def test_adds_dependencies(self): + adder = BuiltinsAdder() + function1 = mock.Mock() + function2 = mock.Mock() + function3 = mock.Mock() + function4 = mock.Mock() + adder.add_type("foo1", function1) + adder.add_type("foo2", function2, ["foo1"]) + adder.add_type("foo3", function3, ["foo2"]) + adder.add_type("foo4", function4) + adder.add("foo3", dict(argument=1)) + adder.add("foo2") + function1.assert_called_once_with() + function2.assert_called_once_with() + function3.assert_called_once_with(argument=1) + self.assertFalse(function4.called) + + def test_runtime_error_on_add_with_different_args(self): + adder = BuiltinsAdder() + function = mock.Mock() + adder.add_type("foo", function) + adder.add("foo", dict(argument=1)) + try: + adder.add("foo", dict(argument=2)) + except RuntimeError as exc: + self.assertEqual(str(exc), + "Optional builtin %r added with arguments %r has already been added with arguments %r" + % ("foo", dict(argument=2), dict(argument=1))) + else: + self.fail("RuntimeError not raised") diff --git a/vunit/test/unit/test_cds_file.py b/vunit/test/unit/test_cds_file.py index 6b400cf71..56ffa2fec 100644 --- a/vunit/test/unit/test_cds_file.py +++ b/vunit/test/unit/test_cds_file.py @@ -1,110 +1,110 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test handling of Cadence Incisive .cds files -""" - -import unittest -from vunit.test.mock_2or3 import mock -from vunit.cds_file import CDSFile - - -class TestCDSFile(unittest.TestCase): - """ - Test handling of Cadence Incisive .cds files - """ - - def test_parses_cds_file(self): - cds = self._create_cds_file(CDS_FILE_CONTENTS) - self.assertEqual(sorted(cds.items()), [ - ("lib", "./vunit_out/incisive/libraries/lib"), - ("tb_uart_lib", "./vunit_out/incisive/libraries/tb_uart_lib"), - ("uart_lib", "./vunit_out/incisive/libraries/uart_lib"), - ("vunit_lib", "./vunit_out/incisive/libraries/vunit_lib"), - ("worklib", "./vunit_out/incisive/libraries/worklib")]) - - def test_writes_cds_file(self): - cds = self._create_cds_file(CDS_FILE_CONTENTS) - self._check_written_as(cds, CDS_FILE_CONTENTS) - - def test_remove_and_add_libraries(self): - cds = self._create_cds_file(CDS_FILE_CONTENTS) - del cds["lib"] - del cds["tb_uart_lib"] - del cds["uart_lib"] - del cds["vunit_lib"] - del cds["worklib"] - - self._check_written_as(cds, """ -# -# cds.lib: Defines the locations of compiled libraries. -# -softinclude $CDS_INST_DIR/tools/inca/files/cds.lib - -""") - cds["foo"] = "bar" - - self._check_written_as(cds, """ -# -# cds.lib: Defines the locations of compiled libraries. -# -softinclude $CDS_INST_DIR/tools/inca/files/cds.lib - -define foo "bar" -""") - - def test_ignores_case(self): - cds = self._create_cds_file('DeFiNe foo bar') - self.assertEqual(list(cds.keys()), ["foo"]) - self.assertEqual(cds["foo"], 'bar') - - def test_unquotes_define(self): - cds = self._create_cds_file('define foo "bar xyz"') - self.assertEqual(cds["foo"], 'bar xyz') - - def test_does_not_unquotes_define(self): - cds = self._create_cds_file('define foo bar') - self.assertEqual(cds["foo"], 'bar') - - def test_quotes_define(self): - cds = CDSFile() - cds["foo"] = 'bar xyz' - self._check_written_as(cds, 'define foo "bar xyz"\n') - - @staticmethod - def _create_cds_file(contents): - """ - Create a CDSFile object with 'contents' - """ - with mock.patch("vunit.cds_file.read_file", autospec=True) as read_file: - read_file.return_value = contents - return CDSFile.parse("file_name") - - def _check_written_as(self, cds, contents): - """ - Check that the CDSFile object writes the 'contents to the file - """ - with mock.patch("vunit.cds_file.write_file", autospec=True) as write_file: - cds.write("filename") - self.assertEqual(len(write_file.mock_calls), 1) - args = write_file.mock_calls[0][1] - self.assertEqual(args[0], "filename") - self.assertEqual(args[1], contents) - - -CDS_FILE_CONTENTS = """ -# -# cds.lib: Defines the locations of compiled libraries. -# -softinclude $CDS_INST_DIR/tools/inca/files/cds.lib - -define lib "./vunit_out/incisive/libraries/lib" -define tb_uart_lib "./vunit_out/incisive/libraries/tb_uart_lib" -define uart_lib "./vunit_out/incisive/libraries/uart_lib" -define vunit_lib "./vunit_out/incisive/libraries/vunit_lib" -define worklib "./vunit_out/incisive/libraries/worklib" -""" +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test handling of Cadence Incisive .cds files +""" + +import unittest +from vunit.test.mock_2or3 import mock +from vunit.cds_file import CDSFile + + +class TestCDSFile(unittest.TestCase): + """ + Test handling of Cadence Incisive .cds files + """ + + def test_parses_cds_file(self): + cds = self._create_cds_file(CDS_FILE_CONTENTS) + self.assertEqual(sorted(cds.items()), [ + ("lib", "./vunit_out/incisive/libraries/lib"), + ("tb_uart_lib", "./vunit_out/incisive/libraries/tb_uart_lib"), + ("uart_lib", "./vunit_out/incisive/libraries/uart_lib"), + ("vunit_lib", "./vunit_out/incisive/libraries/vunit_lib"), + ("worklib", "./vunit_out/incisive/libraries/worklib")]) + + def test_writes_cds_file(self): + cds = self._create_cds_file(CDS_FILE_CONTENTS) + self._check_written_as(cds, CDS_FILE_CONTENTS) + + def test_remove_and_add_libraries(self): + cds = self._create_cds_file(CDS_FILE_CONTENTS) + del cds["lib"] + del cds["tb_uart_lib"] + del cds["uart_lib"] + del cds["vunit_lib"] + del cds["worklib"] + + self._check_written_as(cds, """ +# +# cds.lib: Defines the locations of compiled libraries. +# +softinclude $CDS_INST_DIR/tools/inca/files/cds.lib + +""") + cds["foo"] = "bar" + + self._check_written_as(cds, """ +# +# cds.lib: Defines the locations of compiled libraries. +# +softinclude $CDS_INST_DIR/tools/inca/files/cds.lib + +define foo "bar" +""") + + def test_ignores_case(self): + cds = self._create_cds_file('DeFiNe foo bar') + self.assertEqual(list(cds.keys()), ["foo"]) + self.assertEqual(cds["foo"], 'bar') + + def test_unquotes_define(self): + cds = self._create_cds_file('define foo "bar xyz"') + self.assertEqual(cds["foo"], 'bar xyz') + + def test_does_not_unquotes_define(self): + cds = self._create_cds_file('define foo bar') + self.assertEqual(cds["foo"], 'bar') + + def test_quotes_define(self): + cds = CDSFile() + cds["foo"] = 'bar xyz' + self._check_written_as(cds, 'define foo "bar xyz"\n') + + @staticmethod + def _create_cds_file(contents): + """ + Create a CDSFile object with 'contents' + """ + with mock.patch("vunit.cds_file.read_file", autospec=True) as read_file: + read_file.return_value = contents + return CDSFile.parse("file_name") + + def _check_written_as(self, cds, contents): + """ + Check that the CDSFile object writes the 'contents to the file + """ + with mock.patch("vunit.cds_file.write_file", autospec=True) as write_file: + cds.write("filename") + self.assertEqual(len(write_file.mock_calls), 1) + args = write_file.mock_calls[0][1] + self.assertEqual(args[0], "filename") + self.assertEqual(args[1], contents) + + +CDS_FILE_CONTENTS = """ +# +# cds.lib: Defines the locations of compiled libraries. +# +softinclude $CDS_INST_DIR/tools/inca/files/cds.lib + +define lib "./vunit_out/incisive/libraries/lib" +define tb_uart_lib "./vunit_out/incisive/libraries/tb_uart_lib" +define uart_lib "./vunit_out/incisive/libraries/uart_lib" +define vunit_lib "./vunit_out/incisive/libraries/vunit_lib" +define worklib "./vunit_out/incisive/libraries/worklib" +""" diff --git a/vunit/test/unit/test_check_preprocessor.py b/vunit/test/unit/test_check_preprocessor.py index 5d7a5d412..926223d52 100644 --- a/vunit/test/unit/test_check_preprocessor.py +++ b/vunit/test/unit/test_check_preprocessor.py @@ -1,191 +1,191 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test the check preprocessor -""" - - -import unittest -from vunit.check_preprocessor import CheckPreprocessor - - -class TestCheckPreprocessor(unittest.TestCase): - """ - Test the check preprocessor - """ - - def setUp(self): - self._check_preprocessor = CheckPreprocessor() - - def _verify_result(self, code, expected_result): - """ - Assert that the code after preprocessing is equal to the expected_result - """ - result = self._check_preprocessor.run(code, 'foo.vhd') - self.assertEqual(result, expected_result) - - def test_that_only_relation_checks_are_preprocessed_so_that_parsing_is_simplified(self): - code = """ -check_relation(a /= b); -check(a /= b or c); -- Same as (a /= b) or c. Not a relation -""" - expected_result = """ -check_relation(a /= b, context_msg => %s); -check(a /= b or c); -- Same as (a /= b) or c. Not a relation -""" % make_context_msg('a', '/=', 'b') - - self._verify_result(code, expected_result) - - def test_that_generated_message_is_combined_with_a_custom_message(self): - code = """ -check_relation(age("John") >= 0, "Age must not be negative."); -""" - expected_result = """ -check_relation(age("John") >= 0, "Age must not be negative.", context_msg => %s); -""" % make_context_msg('age("John")', '>=', '0') - - self._verify_result(code, expected_result) - - def test_that_the_top_level_relation_is_correctly_identified_as_the_main_relation(self): - code = """ -check_relation(foo(a > b) = c); -check_relation((a > b) = c); -check_relation(a > (b = c)); -check_relation(( (a > b)) );""" - - expected_result = """ -check_relation(foo(a > b) = c, context_msg => %s); -check_relation((a > b) = c, context_msg => %s); -check_relation(a > (b = c), context_msg => %s); -check_relation(( (a > b)) , context_msg => %s);""" % (make_context_msg('foo(a > b)', '=', 'c'), - make_context_msg('(a > b)', '=', 'c'), - make_context_msg('a', '>', '(b = c)'), - make_context_msg('a', '>', 'b')) - - self._verify_result(code, expected_result) - - def test_that_parsing_is_not_fooled_by_strings_containing_characters_relevant_to_parsing(self): - code = """ -check_relation(41 = ascii(')'), "Incorrect ascii for ')'"); -check_relation(9 = len("Smile :-)"), "Incorrect length of :-) message"); -check_relation(8 = len("Heart => <3"), "Incorrect length of <3 message"); -check_relation(a = integer'(9), "This wasn't expected"); -check_relation(a = std_logic'('1'), "This wasn't expected"); -check_relation(a = func('(','x'), "This wasn't expected"); -check_relation(a = std_logic'('1'+'1'), "This wasn't expected"); -check_relation(39 = ascii('''), "Incorrect ascii for '''"); -check_relation(byte'high = 7);""" - - expected_result = """ -check_relation(41 = ascii(')'), "Incorrect ascii for ')'", context_msg => %s); -check_relation(9 = len("Smile :-)"), "Incorrect length of :-) message", context_msg => %s); -check_relation(8 = len("Heart => <3"), "Incorrect length of <3 message", context_msg => %s); -check_relation(a = integer'(9), "This wasn't expected", context_msg => %s); -check_relation(a = std_logic'('1'), "This wasn't expected", context_msg => %s); -check_relation(a = func('(','x'), "This wasn't expected", context_msg => %s); -check_relation(a = std_logic'('1'+'1'), "This wasn't expected", context_msg => %s); -check_relation(39 = ascii('''), "Incorrect ascii for '''", context_msg => %s); -check_relation(byte'high = 7, context_msg => %s);""" % (make_context_msg('41', '=', "ascii(')')"), - make_context_msg('9', '=', 'len("Smile :-)")'), - make_context_msg('8', '=', 'len("Heart => <3")'), - make_context_msg('a', '=', "integer'(9)"), - make_context_msg('a', '=', "std_logic'('1')"), - make_context_msg('a', '=', "func('(','x')"), - make_context_msg('a', '=', "std_logic'('1'+'1')"), - make_context_msg('39', '=', "ascii(''')"), - make_context_msg("byte'high", '=', '7')) - - self._verify_result(code, expected_result) - - def test_that_parsing_is_not_fooled_by_embedded_comments(self): - code = """ -check_relation(42 = -- Meaning of life :-) - hex(66)); -check_relation(42 = /* Meaning of - life :-) */ - hex(66)); -""" - expected_result = """ -check_relation(42 = -- Meaning of life :-) - hex(66), context_msg => %s); -check_relation(42 = /* Meaning of - life :-) */ - hex(66), context_msg => %s); -""" % (make_context_msg('42', '=', 'hex(66)'), make_context_msg('42', '=', 'hex(66)')) - - self._verify_result(code, expected_result) - - def test_that_a_call_without_a_relational_operator_raises_an_error(self): - code = """ -check_relation(a + b - c); -""" - self.assertRaises(SyntaxError, self._check_preprocessor.run, code, 'foo.vhd') - - def test_that_multiple_parameters_can_be_given(self): - code = """ -check_relation(a = b, level => warning); -check_relation(my_checker, a = b); -check_relation(msg => "Error!", expr => a('<') = b);""" - - expected_result = """ -check_relation(a = b, level => warning, context_msg => %s); -check_relation(my_checker, a = b, context_msg => %s); -check_relation(msg => "Error!", expr => a('<') = b, context_msg => %s);""" % (make_context_msg('a', '=', 'b'), - make_context_msg('a', '=', 'b'), - make_context_msg("a('<')", '=', 'b')) - - self._verify_result(code, expected_result) - - def test_that_all_vhdl_2008_relational_operators_are_recognized(self): - code = """ -check_relation(a = b); -check_relation(a /= b); -check_relation(a < b); -check_relation(a <= b); -check_relation(a > b); -check_relation(a >= b); -check_relation(a ?= b); -check_relation(a ?/= b); -check_relation(a ?< b); -check_relation(a ?<= b); -check_relation(a ?> b); -check_relation(a ?>= b);""" - - expected_result = """ -check_relation(a = b, context_msg => %s); -check_relation(a /= b, context_msg => %s); -check_relation(a < b, context_msg => %s); -check_relation(a <= b, context_msg => %s); -check_relation(a > b, context_msg => %s); -check_relation(a >= b, context_msg => %s); -check_relation(a ?= b, context_msg => %s); -check_relation(a ?/= b, context_msg => %s); -check_relation(a ?< b, context_msg => %s); -check_relation(a ?<= b, context_msg => %s); -check_relation(a ?> b, context_msg => %s); -check_relation(a ?>= b, context_msg => %s);""" % (make_context_msg("a", '=', 'b'), - make_context_msg("a", '/=', 'b'), - make_context_msg("a", '<', 'b'), - make_context_msg("a", '<=', 'b'), - make_context_msg("a", '>', 'b'), - make_context_msg("a", '>=', 'b'), - make_context_msg("a", '?=', 'b'), - make_context_msg("a", '?/=', 'b'), - make_context_msg("a", '?<', 'b'), - make_context_msg("a", '?<=', 'b'), - make_context_msg("a", '?>', 'b'), - make_context_msg("a", '?>=', 'b')) - self._verify_result(code, expected_result) - - -def make_context_msg(left, relation, right): - return ('"Expected %s %s %s. Left is " & to_string(%s) & ". Right is " & to_string(%s) & "."' % - (left.replace('"', '""'), - relation, - right.replace('"', '""'), - left, - right)) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test the check preprocessor +""" + + +import unittest +from vunit.check_preprocessor import CheckPreprocessor + + +class TestCheckPreprocessor(unittest.TestCase): + """ + Test the check preprocessor + """ + + def setUp(self): + self._check_preprocessor = CheckPreprocessor() + + def _verify_result(self, code, expected_result): + """ + Assert that the code after preprocessing is equal to the expected_result + """ + result = self._check_preprocessor.run(code, 'foo.vhd') + self.assertEqual(result, expected_result) + + def test_that_only_relation_checks_are_preprocessed_so_that_parsing_is_simplified(self): + code = """ +check_relation(a /= b); +check(a /= b or c); -- Same as (a /= b) or c. Not a relation +""" + expected_result = """ +check_relation(a /= b, context_msg => %s); +check(a /= b or c); -- Same as (a /= b) or c. Not a relation +""" % make_context_msg('a', '/=', 'b') + + self._verify_result(code, expected_result) + + def test_that_generated_message_is_combined_with_a_custom_message(self): + code = """ +check_relation(age("John") >= 0, "Age must not be negative."); +""" + expected_result = """ +check_relation(age("John") >= 0, "Age must not be negative.", context_msg => %s); +""" % make_context_msg('age("John")', '>=', '0') + + self._verify_result(code, expected_result) + + def test_that_the_top_level_relation_is_correctly_identified_as_the_main_relation(self): + code = """ +check_relation(foo(a > b) = c); +check_relation((a > b) = c); +check_relation(a > (b = c)); +check_relation(( (a > b)) );""" + + expected_result = """ +check_relation(foo(a > b) = c, context_msg => %s); +check_relation((a > b) = c, context_msg => %s); +check_relation(a > (b = c), context_msg => %s); +check_relation(( (a > b)) , context_msg => %s);""" % (make_context_msg('foo(a > b)', '=', 'c'), + make_context_msg('(a > b)', '=', 'c'), + make_context_msg('a', '>', '(b = c)'), + make_context_msg('a', '>', 'b')) + + self._verify_result(code, expected_result) + + def test_that_parsing_is_not_fooled_by_strings_containing_characters_relevant_to_parsing(self): + code = """ +check_relation(41 = ascii(')'), "Incorrect ascii for ')'"); +check_relation(9 = len("Smile :-)"), "Incorrect length of :-) message"); +check_relation(8 = len("Heart => <3"), "Incorrect length of <3 message"); +check_relation(a = integer'(9), "This wasn't expected"); +check_relation(a = std_logic'('1'), "This wasn't expected"); +check_relation(a = func('(','x'), "This wasn't expected"); +check_relation(a = std_logic'('1'+'1'), "This wasn't expected"); +check_relation(39 = ascii('''), "Incorrect ascii for '''"); +check_relation(byte'high = 7);""" + + expected_result = """ +check_relation(41 = ascii(')'), "Incorrect ascii for ')'", context_msg => %s); +check_relation(9 = len("Smile :-)"), "Incorrect length of :-) message", context_msg => %s); +check_relation(8 = len("Heart => <3"), "Incorrect length of <3 message", context_msg => %s); +check_relation(a = integer'(9), "This wasn't expected", context_msg => %s); +check_relation(a = std_logic'('1'), "This wasn't expected", context_msg => %s); +check_relation(a = func('(','x'), "This wasn't expected", context_msg => %s); +check_relation(a = std_logic'('1'+'1'), "This wasn't expected", context_msg => %s); +check_relation(39 = ascii('''), "Incorrect ascii for '''", context_msg => %s); +check_relation(byte'high = 7, context_msg => %s);""" % (make_context_msg('41', '=', "ascii(')')"), + make_context_msg('9', '=', 'len("Smile :-)")'), + make_context_msg('8', '=', 'len("Heart => <3")'), + make_context_msg('a', '=', "integer'(9)"), + make_context_msg('a', '=', "std_logic'('1')"), + make_context_msg('a', '=', "func('(','x')"), + make_context_msg('a', '=', "std_logic'('1'+'1')"), + make_context_msg('39', '=', "ascii(''')"), + make_context_msg("byte'high", '=', '7')) + + self._verify_result(code, expected_result) + + def test_that_parsing_is_not_fooled_by_embedded_comments(self): + code = """ +check_relation(42 = -- Meaning of life :-) + hex(66)); +check_relation(42 = /* Meaning of + life :-) */ + hex(66)); +""" + expected_result = """ +check_relation(42 = -- Meaning of life :-) + hex(66), context_msg => %s); +check_relation(42 = /* Meaning of + life :-) */ + hex(66), context_msg => %s); +""" % (make_context_msg('42', '=', 'hex(66)'), make_context_msg('42', '=', 'hex(66)')) + + self._verify_result(code, expected_result) + + def test_that_a_call_without_a_relational_operator_raises_an_error(self): + code = """ +check_relation(a + b - c); +""" + self.assertRaises(SyntaxError, self._check_preprocessor.run, code, 'foo.vhd') + + def test_that_multiple_parameters_can_be_given(self): + code = """ +check_relation(a = b, level => warning); +check_relation(my_checker, a = b); +check_relation(msg => "Error!", expr => a('<') = b);""" + + expected_result = """ +check_relation(a = b, level => warning, context_msg => %s); +check_relation(my_checker, a = b, context_msg => %s); +check_relation(msg => "Error!", expr => a('<') = b, context_msg => %s);""" % (make_context_msg('a', '=', 'b'), + make_context_msg('a', '=', 'b'), + make_context_msg("a('<')", '=', 'b')) + + self._verify_result(code, expected_result) + + def test_that_all_vhdl_2008_relational_operators_are_recognized(self): + code = """ +check_relation(a = b); +check_relation(a /= b); +check_relation(a < b); +check_relation(a <= b); +check_relation(a > b); +check_relation(a >= b); +check_relation(a ?= b); +check_relation(a ?/= b); +check_relation(a ?< b); +check_relation(a ?<= b); +check_relation(a ?> b); +check_relation(a ?>= b);""" + + expected_result = """ +check_relation(a = b, context_msg => %s); +check_relation(a /= b, context_msg => %s); +check_relation(a < b, context_msg => %s); +check_relation(a <= b, context_msg => %s); +check_relation(a > b, context_msg => %s); +check_relation(a >= b, context_msg => %s); +check_relation(a ?= b, context_msg => %s); +check_relation(a ?/= b, context_msg => %s); +check_relation(a ?< b, context_msg => %s); +check_relation(a ?<= b, context_msg => %s); +check_relation(a ?> b, context_msg => %s); +check_relation(a ?>= b, context_msg => %s);""" % (make_context_msg("a", '=', 'b'), + make_context_msg("a", '/=', 'b'), + make_context_msg("a", '<', 'b'), + make_context_msg("a", '<=', 'b'), + make_context_msg("a", '>', 'b'), + make_context_msg("a", '>=', 'b'), + make_context_msg("a", '?=', 'b'), + make_context_msg("a", '?/=', 'b'), + make_context_msg("a", '?<', 'b'), + make_context_msg("a", '?<=', 'b'), + make_context_msg("a", '?>', 'b'), + make_context_msg("a", '?>=', 'b')) + self._verify_result(code, expected_result) + + +def make_context_msg(left, relation, right): + return ('"Expected %s %s %s. Left is " & to_string(%s) & ". Right is " & to_string(%s) & "."' % + (left.replace('"', '""'), + relation, + right.replace('"', '""'), + left, + right)) diff --git a/vunit/test/unit/test_configuration.py b/vunit/test/unit/test_configuration.py index 232b5e7c2..b4f08911d 100644 --- a/vunit/test/unit/test_configuration.py +++ b/vunit/test/unit/test_configuration.py @@ -1,243 +1,243 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -# pylint: disable=too-many-public-methods - -""" -Tests the test test_bench module -""" - - -import unittest -import contextlib -from os.path import join -from vunit.configuration import (Configuration, - AttributeException) -from vunit.test.mock_2or3 import mock -from vunit.test.common import (with_tempdir, - create_tempdir) -from vunit.test.unit.test_test_bench import Entity - - -class TestConfiguration(unittest.TestCase): - """ - Test the configuration module - """ - - @mock.patch("vunit.configuration.LOGGER") - def test_warning_on_setting_missing_generic(self, mock_logger): - with _create_config() as config: - config.set_generic("name123", "value123") - warning_calls = mock_logger.warning.call_args_list - self.assertEqual(len(warning_calls), 1) - call_args = warning_calls[0][0] - self.assertIn("lib", call_args) - self.assertIn("tb_entity", call_args) - self.assertIn("name123", call_args) - self.assertIn("value123", call_args) - - def test_error_on_setting_unknown_sim_option(self): - with _create_config() as config: - self.assertRaises(ValueError, config.set_sim_option, "name123", "value123") - - def test_error_on_setting_illegal_value_sim_option(self): - with _create_config() as config: - self.assertRaises(ValueError, config.set_sim_option, "vhdl_assert_stop_level", "illegal") - - def test_sim_option_is_not_mutated(self): - with _create_config() as config: - options = ["--foo"] - config.set_sim_option("ghdl.sim_flags", options) - options[0] = "--bar" - self.assertEqual(config.sim_options["ghdl.sim_flags"], ["--foo"]) - - def test_does_not_add_tb_path_generic(self): - with _create_config() as config: - self.assertNotIn("tb_path", config.generics) - - @with_tempdir - def test_adds_tb_path_generic(self, tempdir): - design_unit_tb_path = Entity('tb_entity_without_tb_path', - file_name=join(tempdir, "file.vhd")) - tb_path = join(tempdir, "other_path") - design_unit_tb_path.original_file_name = join(tb_path, "original_file.vhd") - design_unit_tb_path.generic_names = ["runner_cfg", "tb_path"] - config_tb_path = Configuration('name', design_unit_tb_path) - self.assertEqual(config_tb_path.generics["tb_path"], (tb_path + "/").replace("\\", "/")) - - def test_constructor_adds_no_attributes(self): - with _create_config() as config: - self.assertEqual({}, config.attributes) - - def test_constructor_adds_supplied_attributes(self): - with _create_config(attributes={"foo": "bar"}) as config: - self.assertEqual({"foo": "bar"}, config.attributes) - - def test_set_attribute_must_start_with_dot(self): - with _create_config() as config: - self.assertRaises(AttributeException, config.set_attribute, "foo", "bar") - - def test_call_post_check_none(self): - self.assertEqual(self._call_post_check(None, output_path="output_path", read_output=None), True) - - def test_call_post_check_false(self): - def post_check(): - return False - self.assertEqual(self._call_post_check(post_check, output_path="output_path", read_output=None), False) - - def test_call_post_check_true(self): - def post_check(): - return True - self.assertEqual(self._call_post_check(post_check, output_path="output_path", read_output=None), True) - - def test_call_post_check_no_return(self): - def post_check(): - pass - self.assertEqual(self._call_post_check(post_check, output_path="output_path", read_output=None), False) - - def test_call_post_check_with_output_path(self): - - def post_check(output_path): - """ - Pre config with output path - """ - self.assertEqual(output_path, "output_path") - raise WasHere - - self.assertRaises(WasHere, self._call_post_check, post_check, output_path="output_path", read_output=None) - - def test_call_post_check_with_no_output(self): - def read_output(): - """ - Should never be called - """ - assert False - - def post_check(): - """ - Pre config with output path - """ - raise WasHere - - self.assertRaises(WasHere, - self._call_post_check, post_check, output_path="output_path", read_output=read_output) - - def test_call_post_check_with_output(self): - - output_string = "123___foo\n\nbar" - - def read_output(): - """ - Should never be called - """ - return output_string - - def post_check(output): - """ - Pre config with output path - """ - self.assertEqual(output, output_string) - raise WasHere - - self.assertRaises(WasHere, - self._call_post_check, post_check, output_path="output_path", read_output=read_output) - - def test_call_pre_config_none(self): - self.assertEqual(self._call_pre_config(None, "output_path", "simulator_output_path"), True) - - def test_call_pre_config_false(self): - def pre_config(): - return False - self.assertEqual(self._call_pre_config(pre_config, "output_path", "simulator_output_path"), False) - - def test_call_pre_config_true(self): - def pre_config(): - return True - self.assertEqual(self._call_pre_config(pre_config, "output_path", "simulator_output_path"), True) - - def test_call_pre_config_no_return(self): - def pre_config(): - pass - self.assertEqual(self._call_pre_config(pre_config, "output_path", "simulator_output_path"), False) - - def test_call_pre_config_with_output_path(self): - - def pre_config(output_path): - """ - Pre config with output path - """ - self.assertEqual(output_path, "output_path") - raise WasHere - - self.assertRaises(WasHere, self._call_pre_config, pre_config, "output_path", "simulator_output_path") - - def test_call_pre_config_with_simulator_output_path(self): - - def pre_config(output_path, simulator_output_path): - """ - Pre config with output path - """ - self.assertEqual(output_path, "output_path") - self.assertEqual(simulator_output_path, "simulator_output_path") - raise WasHere - - self.assertRaises(WasHere, self._call_pre_config, pre_config, "output_path", "simulator_output_path") - - def test_call_pre_config_class_method(self): - - class MyClass(object): - """ - Class to test pre_config method - """ - def __init__(self, value): - self.value = value - - def pre_config(self, output_path, simulator_output_path): - """ - Pre config with output path - """ - assert self.value == 2 - assert output_path == "output_path" - assert simulator_output_path == "simulator_output_path" - raise WasHere - - self.assertRaises(WasHere, - self._call_pre_config, - MyClass(value=2).pre_config, "output_path", "simulator_output_path") - - @staticmethod - def _call_pre_config(pre_config, output_path, simulator_output_path): - """ - Helper method to test call_pre_config method - """ - with _create_config(pre_config=pre_config) as config: - return config.call_pre_config(output_path, simulator_output_path) - - @staticmethod - def _call_post_check(post_check, **kwargs): - """ - Helper method to test call_post_check method - """ - with _create_config(post_check=post_check) as config: - return config.call_post_check(**kwargs) - - -@contextlib.contextmanager -def _create_config(**kwargs): - """ - Helper function to create a config - """ - with create_tempdir() as tempdir: - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd")) - design_unit.generic_names = ["runner_cfg"] - yield Configuration('name', design_unit, **kwargs) - - -class WasHere(Exception): - """ - Exception raised to prove code was executed - """ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +# pylint: disable=too-many-public-methods + +""" +Tests the test test_bench module +""" + + +import unittest +import contextlib +from os.path import join +from vunit.configuration import (Configuration, + AttributeException) +from vunit.test.mock_2or3 import mock +from vunit.test.common import (with_tempdir, + create_tempdir) +from vunit.test.unit.test_test_bench import Entity + + +class TestConfiguration(unittest.TestCase): + """ + Test the configuration module + """ + + @mock.patch("vunit.configuration.LOGGER") + def test_warning_on_setting_missing_generic(self, mock_logger): + with _create_config() as config: + config.set_generic("name123", "value123") + warning_calls = mock_logger.warning.call_args_list + self.assertEqual(len(warning_calls), 1) + call_args = warning_calls[0][0] + self.assertIn("lib", call_args) + self.assertIn("tb_entity", call_args) + self.assertIn("name123", call_args) + self.assertIn("value123", call_args) + + def test_error_on_setting_unknown_sim_option(self): + with _create_config() as config: + self.assertRaises(ValueError, config.set_sim_option, "name123", "value123") + + def test_error_on_setting_illegal_value_sim_option(self): + with _create_config() as config: + self.assertRaises(ValueError, config.set_sim_option, "vhdl_assert_stop_level", "illegal") + + def test_sim_option_is_not_mutated(self): + with _create_config() as config: + options = ["--foo"] + config.set_sim_option("ghdl.sim_flags", options) + options[0] = "--bar" + self.assertEqual(config.sim_options["ghdl.sim_flags"], ["--foo"]) + + def test_does_not_add_tb_path_generic(self): + with _create_config() as config: + self.assertNotIn("tb_path", config.generics) + + @with_tempdir + def test_adds_tb_path_generic(self, tempdir): + design_unit_tb_path = Entity('tb_entity_without_tb_path', + file_name=join(tempdir, "file.vhd")) + tb_path = join(tempdir, "other_path") + design_unit_tb_path.original_file_name = join(tb_path, "original_file.vhd") + design_unit_tb_path.generic_names = ["runner_cfg", "tb_path"] + config_tb_path = Configuration('name', design_unit_tb_path) + self.assertEqual(config_tb_path.generics["tb_path"], (tb_path + "/").replace("\\", "/")) + + def test_constructor_adds_no_attributes(self): + with _create_config() as config: + self.assertEqual({}, config.attributes) + + def test_constructor_adds_supplied_attributes(self): + with _create_config(attributes={"foo": "bar"}) as config: + self.assertEqual({"foo": "bar"}, config.attributes) + + def test_set_attribute_must_start_with_dot(self): + with _create_config() as config: + self.assertRaises(AttributeException, config.set_attribute, "foo", "bar") + + def test_call_post_check_none(self): + self.assertEqual(self._call_post_check(None, output_path="output_path", read_output=None), True) + + def test_call_post_check_false(self): + def post_check(): + return False + self.assertEqual(self._call_post_check(post_check, output_path="output_path", read_output=None), False) + + def test_call_post_check_true(self): + def post_check(): + return True + self.assertEqual(self._call_post_check(post_check, output_path="output_path", read_output=None), True) + + def test_call_post_check_no_return(self): + def post_check(): + pass + self.assertEqual(self._call_post_check(post_check, output_path="output_path", read_output=None), False) + + def test_call_post_check_with_output_path(self): + + def post_check(output_path): + """ + Pre config with output path + """ + self.assertEqual(output_path, "output_path") + raise WasHere + + self.assertRaises(WasHere, self._call_post_check, post_check, output_path="output_path", read_output=None) + + def test_call_post_check_with_no_output(self): + def read_output(): + """ + Should never be called + """ + assert False + + def post_check(): + """ + Pre config with output path + """ + raise WasHere + + self.assertRaises(WasHere, + self._call_post_check, post_check, output_path="output_path", read_output=read_output) + + def test_call_post_check_with_output(self): + + output_string = "123___foo\n\nbar" + + def read_output(): + """ + Should never be called + """ + return output_string + + def post_check(output): + """ + Pre config with output path + """ + self.assertEqual(output, output_string) + raise WasHere + + self.assertRaises(WasHere, + self._call_post_check, post_check, output_path="output_path", read_output=read_output) + + def test_call_pre_config_none(self): + self.assertEqual(self._call_pre_config(None, "output_path", "simulator_output_path"), True) + + def test_call_pre_config_false(self): + def pre_config(): + return False + self.assertEqual(self._call_pre_config(pre_config, "output_path", "simulator_output_path"), False) + + def test_call_pre_config_true(self): + def pre_config(): + return True + self.assertEqual(self._call_pre_config(pre_config, "output_path", "simulator_output_path"), True) + + def test_call_pre_config_no_return(self): + def pre_config(): + pass + self.assertEqual(self._call_pre_config(pre_config, "output_path", "simulator_output_path"), False) + + def test_call_pre_config_with_output_path(self): + + def pre_config(output_path): + """ + Pre config with output path + """ + self.assertEqual(output_path, "output_path") + raise WasHere + + self.assertRaises(WasHere, self._call_pre_config, pre_config, "output_path", "simulator_output_path") + + def test_call_pre_config_with_simulator_output_path(self): + + def pre_config(output_path, simulator_output_path): + """ + Pre config with output path + """ + self.assertEqual(output_path, "output_path") + self.assertEqual(simulator_output_path, "simulator_output_path") + raise WasHere + + self.assertRaises(WasHere, self._call_pre_config, pre_config, "output_path", "simulator_output_path") + + def test_call_pre_config_class_method(self): + + class MyClass(object): + """ + Class to test pre_config method + """ + def __init__(self, value): + self.value = value + + def pre_config(self, output_path, simulator_output_path): + """ + Pre config with output path + """ + assert self.value == 2 + assert output_path == "output_path" + assert simulator_output_path == "simulator_output_path" + raise WasHere + + self.assertRaises(WasHere, + self._call_pre_config, + MyClass(value=2).pre_config, "output_path", "simulator_output_path") + + @staticmethod + def _call_pre_config(pre_config, output_path, simulator_output_path): + """ + Helper method to test call_pre_config method + """ + with _create_config(pre_config=pre_config) as config: + return config.call_pre_config(output_path, simulator_output_path) + + @staticmethod + def _call_post_check(post_check, **kwargs): + """ + Helper method to test call_post_check method + """ + with _create_config(post_check=post_check) as config: + return config.call_post_check(**kwargs) + + +@contextlib.contextmanager +def _create_config(**kwargs): + """ + Helper function to create a config + """ + with create_tempdir() as tempdir: + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd")) + design_unit.generic_names = ["runner_cfg"] + yield Configuration('name', design_unit, **kwargs) + + +class WasHere(Exception): + """ + Exception raised to prove code was executed + """ diff --git a/vunit/test/unit/test_csv_logs.py b/vunit/test/unit/test_csv_logs.py index e5bc88305..4a6db3e4f 100644 --- a/vunit/test/unit/test_csv_logs.py +++ b/vunit/test/unit/test_csv_logs.py @@ -1,141 +1,141 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test csv log functionality -""" - -import unittest -from shutil import rmtree -from os import remove -from os.path import join -from tempfile import NamedTemporaryFile, mkdtemp -from vunit.csv_logs import CsvLogs - - -class TestCsvLogs(unittest.TestCase): - """ - Test csv log functionality - """ - - def setUp(self): - self._log_files = [] - self._all_fields_dir = mkdtemp() - self._few_fields_dir = mkdtemp() - self._all_fields_files = join(self._all_fields_dir, '*.csv') - self._few_fields_files = join(self._few_fields_dir, '*.csv') - - def make_log(directory, contents): - with NamedTemporaryFile('w+', delete=False, dir=directory, suffix='.csv') as file_obj: - file_obj.write(contents) - self._log_files.append(file_obj.name) - - make_log(self._all_fields_dir, - "0,10 fs,info,bar.vhd,17,src1,This is an info entry.\n" - "10,20 ps,failure,foo.vhd,42,src2,This is a failure entry.\n" - "21,30 ns,error,foo.vhd,42,src2,This is an error entry.\n") - - make_log(self._all_fields_dir, "") - - make_log(self._all_fields_dir, - "4,100 fs,info,zoo.vhd,17,src3,This is an info entry.\n" - "30,50 ns,failure,ying.vhd,42,src4,This is a failure entry.\n" - "31,70 ns,error,yang.vhd,42,src5,This is an error entry.\n") - - make_log(self._few_fields_dir, - "0,10 fs,info,src1,This is an info entry.\n" - "10,20 ps,failure,src2,This is a failure entry.\n" - "21,30 ns,error,src2,This is an error entry.\n") - - make_log(self._few_fields_dir, - "4,100 fs,info,src3,This is an info entry.\n" - "30,50 ns,failure,src4,This is a failure entry.\n" - "31,70 ns,error,src5,This is an error entry.\n") - - @staticmethod - def _write_to_file_and_read_back_result(csv_logs): - """ - Create a temporary file just to write the csv log results to it - so that it can be read back and returned - - @TODO skip writing the file to disk and stub it - """ - out_fp = NamedTemporaryFile(delete=False) - out_fp.close() - csv_logs.write(out_fp.name) - - with open(out_fp.name, "r") as fread: - result = fread.read() - remove(out_fp.name) - - return result - - def test_should_sort_several_csv_files(self): - csvlogs = CsvLogs(self._all_fields_files) - - result = self._write_to_file_and_read_back_result(csvlogs) - expected_result = """#,Time,Level,File,Line,Source,Message -0,10 fs,info,bar.vhd,17,src1,This is an info entry. -4,100 fs,info,zoo.vhd,17,src3,This is an info entry. -10,20 ps,failure,foo.vhd,42,src2,This is a failure entry. -21,30 ns,error,foo.vhd,42,src2,This is an error entry. -30,50 ns,failure,ying.vhd,42,src4,This is a failure entry. -31,70 ns,error,yang.vhd,42,src5,This is an error entry. -""" - self.assertEqual(result, expected_result) - - def test_should_sort_single_csv_file(self): - csvlogs = CsvLogs(self._log_files[0]) - - result = self._write_to_file_and_read_back_result(csvlogs) - expected_result = """#,Time,Level,File,Line,Source,Message -0,10 fs,info,bar.vhd,17,src1,This is an info entry. -10,20 ps,failure,foo.vhd,42,src2,This is a failure entry. -21,30 ns,error,foo.vhd,42,src2,This is an error entry. -""" - self.assertEqual(result, expected_result) - - def test_should_sort_single_empty_csv_file(self): - csvlogs = CsvLogs(self._log_files[1]) - - result = self._write_to_file_and_read_back_result(csvlogs) - expected_result = """#,Time,Level,File,Line,Source,Message -""" - self.assertEqual(result, expected_result) - - def test_should_be_possible_to_add_csv_files(self): - csvlogs = CsvLogs() - for i in range(3): - csvlogs.add(self._log_files[i]) - - result = self._write_to_file_and_read_back_result(csvlogs) - expected_result = """#,Time,Level,File,Line,Source,Message -0,10 fs,info,bar.vhd,17,src1,This is an info entry. -4,100 fs,info,zoo.vhd,17,src3,This is an info entry. -10,20 ps,failure,foo.vhd,42,src2,This is a failure entry. -21,30 ns,error,foo.vhd,42,src2,This is an error entry. -30,50 ns,failure,ying.vhd,42,src4,This is a failure entry. -31,70 ns,error,yang.vhd,42,src5,This is an error entry. -""" - self.assertEqual(result, expected_result) - - def test_should_sort_several_csv_files_with_non_default_fields(self): - csvlogs = CsvLogs(self._few_fields_files, ['#', 'Time', 'Level', 'Source', 'Message']) - - result = self._write_to_file_and_read_back_result(csvlogs) - expected_result = """#,Time,Level,Source,Message -0,10 fs,info,src1,This is an info entry. -4,100 fs,info,src3,This is an info entry. -10,20 ps,failure,src2,This is a failure entry. -21,30 ns,error,src2,This is an error entry. -30,50 ns,failure,src4,This is a failure entry. -31,70 ns,error,src5,This is an error entry. -""" - self.assertEqual(result, expected_result) - - def tearDown(self): - rmtree(self._all_fields_dir) - rmtree(self._few_fields_dir) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test csv log functionality +""" + +import unittest +from shutil import rmtree +from os import remove +from os.path import join +from tempfile import NamedTemporaryFile, mkdtemp +from vunit.csv_logs import CsvLogs + + +class TestCsvLogs(unittest.TestCase): + """ + Test csv log functionality + """ + + def setUp(self): + self._log_files = [] + self._all_fields_dir = mkdtemp() + self._few_fields_dir = mkdtemp() + self._all_fields_files = join(self._all_fields_dir, '*.csv') + self._few_fields_files = join(self._few_fields_dir, '*.csv') + + def make_log(directory, contents): + with NamedTemporaryFile('w+', delete=False, dir=directory, suffix='.csv') as file_obj: + file_obj.write(contents) + self._log_files.append(file_obj.name) + + make_log(self._all_fields_dir, + "0,10 fs,info,bar.vhd,17,src1,This is an info entry.\n" + "10,20 ps,failure,foo.vhd,42,src2,This is a failure entry.\n" + "21,30 ns,error,foo.vhd,42,src2,This is an error entry.\n") + + make_log(self._all_fields_dir, "") + + make_log(self._all_fields_dir, + "4,100 fs,info,zoo.vhd,17,src3,This is an info entry.\n" + "30,50 ns,failure,ying.vhd,42,src4,This is a failure entry.\n" + "31,70 ns,error,yang.vhd,42,src5,This is an error entry.\n") + + make_log(self._few_fields_dir, + "0,10 fs,info,src1,This is an info entry.\n" + "10,20 ps,failure,src2,This is a failure entry.\n" + "21,30 ns,error,src2,This is an error entry.\n") + + make_log(self._few_fields_dir, + "4,100 fs,info,src3,This is an info entry.\n" + "30,50 ns,failure,src4,This is a failure entry.\n" + "31,70 ns,error,src5,This is an error entry.\n") + + @staticmethod + def _write_to_file_and_read_back_result(csv_logs): + """ + Create a temporary file just to write the csv log results to it + so that it can be read back and returned + + @TODO skip writing the file to disk and stub it + """ + out_fp = NamedTemporaryFile(delete=False) + out_fp.close() + csv_logs.write(out_fp.name) + + with open(out_fp.name, "r") as fread: + result = fread.read() + remove(out_fp.name) + + return result + + def test_should_sort_several_csv_files(self): + csvlogs = CsvLogs(self._all_fields_files) + + result = self._write_to_file_and_read_back_result(csvlogs) + expected_result = """#,Time,Level,File,Line,Source,Message +0,10 fs,info,bar.vhd,17,src1,This is an info entry. +4,100 fs,info,zoo.vhd,17,src3,This is an info entry. +10,20 ps,failure,foo.vhd,42,src2,This is a failure entry. +21,30 ns,error,foo.vhd,42,src2,This is an error entry. +30,50 ns,failure,ying.vhd,42,src4,This is a failure entry. +31,70 ns,error,yang.vhd,42,src5,This is an error entry. +""" + self.assertEqual(result, expected_result) + + def test_should_sort_single_csv_file(self): + csvlogs = CsvLogs(self._log_files[0]) + + result = self._write_to_file_and_read_back_result(csvlogs) + expected_result = """#,Time,Level,File,Line,Source,Message +0,10 fs,info,bar.vhd,17,src1,This is an info entry. +10,20 ps,failure,foo.vhd,42,src2,This is a failure entry. +21,30 ns,error,foo.vhd,42,src2,This is an error entry. +""" + self.assertEqual(result, expected_result) + + def test_should_sort_single_empty_csv_file(self): + csvlogs = CsvLogs(self._log_files[1]) + + result = self._write_to_file_and_read_back_result(csvlogs) + expected_result = """#,Time,Level,File,Line,Source,Message +""" + self.assertEqual(result, expected_result) + + def test_should_be_possible_to_add_csv_files(self): + csvlogs = CsvLogs() + for i in range(3): + csvlogs.add(self._log_files[i]) + + result = self._write_to_file_and_read_back_result(csvlogs) + expected_result = """#,Time,Level,File,Line,Source,Message +0,10 fs,info,bar.vhd,17,src1,This is an info entry. +4,100 fs,info,zoo.vhd,17,src3,This is an info entry. +10,20 ps,failure,foo.vhd,42,src2,This is a failure entry. +21,30 ns,error,foo.vhd,42,src2,This is an error entry. +30,50 ns,failure,ying.vhd,42,src4,This is a failure entry. +31,70 ns,error,yang.vhd,42,src5,This is an error entry. +""" + self.assertEqual(result, expected_result) + + def test_should_sort_several_csv_files_with_non_default_fields(self): + csvlogs = CsvLogs(self._few_fields_files, ['#', 'Time', 'Level', 'Source', 'Message']) + + result = self._write_to_file_and_read_back_result(csvlogs) + expected_result = """#,Time,Level,Source,Message +0,10 fs,info,src1,This is an info entry. +4,100 fs,info,src3,This is an info entry. +10,20 ps,failure,src2,This is a failure entry. +21,30 ns,error,src2,This is an error entry. +30,50 ns,failure,src4,This is a failure entry. +31,70 ns,error,src5,This is an error entry. +""" + self.assertEqual(result, expected_result) + + def tearDown(self): + rmtree(self._all_fields_dir) + rmtree(self._few_fields_dir) diff --git a/vunit/test/unit/test_database.py b/vunit/test/unit/test_database.py index 63901824a..5fc1d3f4d 100644 --- a/vunit/test/unit/test_database.py +++ b/vunit/test/unit/test_database.py @@ -1,104 +1,104 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test the database related classes -""" - -import unittest -from os.path import join -from vunit.database import DataBase, PickledDataBase -from vunit.test.common import with_tempdir - - -class TestDataBase(unittest.TestCase): - """ - The the byte based database - """ - - key1 = b"key2" - key2 = b"key1" - value1 = b"value1" - value2 = b"value2" - - @staticmethod - def create_database(tempdir, new=False): - return DataBase(join(tempdir, "database"), new=new) - - def _test_add_items(self, tempdir): - """ - Re-usable test case - """ - database = self.create_database(tempdir) - - self.assertTrue(self.key1 not in database) - self.assertTrue(self.key2 not in database) - database[self.key1] = self.value1 - - self.assertTrue(self.key1 in database) - self.assertTrue(self.key2 not in database) - self.assertEqual(database[self.key1], self.value1) - - database[self.key2] = self.value2 - self.assertTrue(self.key1 in database) - self.assertTrue(self.key2 in database) - self.assertEqual(database[self.key1], self.value1) - self.assertEqual(database[self.key2], self.value2) - - @with_tempdir - def test_add_items(self, tempdir): - self._test_add_items(tempdir) - - @with_tempdir - def test_is_persistent(self, tempdir): - self._test_add_items(tempdir) - database = self.create_database(tempdir) - self.assertEqual(database[self.key1], self.value1) - self.assertEqual(database[self.key2], self.value2) - - @with_tempdir - def test_new_database_is_created(self, tempdir): - self._test_add_items(tempdir) - database = self.create_database(tempdir, new=True) - self.assertTrue(self.key1 not in database) - self.assertTrue(self.key2 not in database) - - @with_tempdir - def test_missing_key_raises_keyerror(self, tempdir): - database = self.create_database(tempdir) - self.assertRaises(KeyError, lambda: database[self.key1]) - - @with_tempdir - def test_can_overwrite_key(self, tempdir): - database = self.create_database(tempdir) - - database[self.key1] = self.value1 - database[self.key2] = self.value2 - self.assertEqual(database[self.key1], self.value1) - self.assertEqual(database[self.key2], self.value2) - - database[self.key1] = self.value2 - self.assertEqual(database[self.key1], self.value2) - self.assertEqual(database[self.key2], self.value2) - - database[self.key2] = self.value1 - self.assertEqual(database[self.key1], self.value2) - self.assertEqual(database[self.key2], self.value1) - - -class TestPickedDataBase(TestDataBase): - """ - Test the picked database - - Re-uses test from TestDataBase class - """ - - value1 = (1, "foo", set([1, 2, 3])) - value2 = (3, 4, 5, ("foo", "bar")) - - @staticmethod - def create_database(tempdir, new=False): - return PickledDataBase(TestDataBase.create_database(tempdir, new)) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test the database related classes +""" + +import unittest +from os.path import join +from vunit.database import DataBase, PickledDataBase +from vunit.test.common import with_tempdir + + +class TestDataBase(unittest.TestCase): + """ + The the byte based database + """ + + key1 = b"key2" + key2 = b"key1" + value1 = b"value1" + value2 = b"value2" + + @staticmethod + def create_database(tempdir, new=False): + return DataBase(join(tempdir, "database"), new=new) + + def _test_add_items(self, tempdir): + """ + Re-usable test case + """ + database = self.create_database(tempdir) + + self.assertTrue(self.key1 not in database) + self.assertTrue(self.key2 not in database) + database[self.key1] = self.value1 + + self.assertTrue(self.key1 in database) + self.assertTrue(self.key2 not in database) + self.assertEqual(database[self.key1], self.value1) + + database[self.key2] = self.value2 + self.assertTrue(self.key1 in database) + self.assertTrue(self.key2 in database) + self.assertEqual(database[self.key1], self.value1) + self.assertEqual(database[self.key2], self.value2) + + @with_tempdir + def test_add_items(self, tempdir): + self._test_add_items(tempdir) + + @with_tempdir + def test_is_persistent(self, tempdir): + self._test_add_items(tempdir) + database = self.create_database(tempdir) + self.assertEqual(database[self.key1], self.value1) + self.assertEqual(database[self.key2], self.value2) + + @with_tempdir + def test_new_database_is_created(self, tempdir): + self._test_add_items(tempdir) + database = self.create_database(tempdir, new=True) + self.assertTrue(self.key1 not in database) + self.assertTrue(self.key2 not in database) + + @with_tempdir + def test_missing_key_raises_keyerror(self, tempdir): + database = self.create_database(tempdir) + self.assertRaises(KeyError, lambda: database[self.key1]) + + @with_tempdir + def test_can_overwrite_key(self, tempdir): + database = self.create_database(tempdir) + + database[self.key1] = self.value1 + database[self.key2] = self.value2 + self.assertEqual(database[self.key1], self.value1) + self.assertEqual(database[self.key2], self.value2) + + database[self.key1] = self.value2 + self.assertEqual(database[self.key1], self.value2) + self.assertEqual(database[self.key2], self.value2) + + database[self.key2] = self.value1 + self.assertEqual(database[self.key1], self.value2) + self.assertEqual(database[self.key2], self.value1) + + +class TestPickedDataBase(TestDataBase): + """ + Test the picked database + + Re-uses test from TestDataBase class + """ + + value1 = (1, "foo", set([1, 2, 3])) + value2 = (3, 4, 5, ("foo", "bar")) + + @staticmethod + def create_database(tempdir, new=False): + return PickledDataBase(TestDataBase.create_database(tempdir, new)) diff --git a/vunit/test/unit/test_dependency_graph.py b/vunit/test/unit/test_dependency_graph.py index 202355703..2877ec7b6 100644 --- a/vunit/test/unit/test_dependency_graph.py +++ b/vunit/test/unit/test_dependency_graph.py @@ -1,154 +1,154 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test the DependencyGraph -""" - -import unittest -from vunit.dependency_graph import (DependencyGraph, - CircularDependencyException) - - -class TestDependencyGraph(unittest.TestCase): - """ - Test the DependencyGraph - """ - - def test_should_return_empty_compile_order_for_no_nodes(self): - graph = DependencyGraph() - self.assertEqual(graph.toposort(), [], 'Should return empty list') - - def test_should_return_list_of_nodes_when_there_are_no_dependencies(self): - nodes = ['a', 'b', 'c', 'd'] - graph = DependencyGraph() - self._add_nodes_and_dependencies(graph, nodes, []) - result = graph.toposort() - self.assertEqual(result.sort(), nodes.sort(), 'Should return the node list in any order') - - def test_should_sort_in_topological_order_when_there_are_dependencies(self): - nodes = ['a', 'b', 'c', 'd', 'e', 'f'] - dependencies = [('a', 'b'), ('a', 'c'), ('b', 'd'), ('e', 'f')] - graph = DependencyGraph() - self._add_nodes_and_dependencies(graph, nodes, dependencies) - result = graph.toposort() - self._check_result(result, dependencies) - - def test_should_raise_runtime_error_exception_on_self_dependency(self): - nodes = ['a', 'b', 'c', 'd'] - dependencies = [('a', 'b'), ('a', 'c'), ('b', 'd'), ('d', 'd')] - graph = DependencyGraph() - self._add_nodes_and_dependencies(graph, nodes, dependencies) - - try: - graph.toposort() - except CircularDependencyException as exc: - self.assertEqual(exc.path, ['d', 'd']) - else: - self.fail("Exception not raised") - - def test_should_raise_runtime_error_exception_on_long_circular_dependency(self): - nodes = ['a', 'b', 'c', 'd'] - dependencies = [('a', 'b'), ('a', 'c'), ('b', 'd'), ('d', 'a')] - graph = DependencyGraph() - self._add_nodes_and_dependencies(graph, nodes, dependencies) - - try: - graph.toposort() - except CircularDependencyException as exc: - self.assertEqual(exc.path, ['a', 'b', 'd', 'a']) - else: - self.fail("Exception not raised") - - def test_should_resort_after_additions(self): - nodes = ['a', 'b', 'c', 'd', 'e', 'f'] - dependencies = [('a', 'b'), ('a', 'c'), ('b', 'd'), ('e', 'f')] - graph = DependencyGraph() - self._add_nodes_and_dependencies(graph, nodes, dependencies) - graph.toposort() - dependencies = [('a', 'b'), ('a', 'c'), ('b', 'd'), ('e', 'f'), ('b', 'g')] - graph.add_node('g') - graph.add_dependency('b', 'g') - result = graph.toposort() - self._check_result(result, dependencies) - - def test_get_direct_dependencies_should_return_empty_set_when_no_dependendencies(self): - nodes = ['a', 'b', 'c'] - dependencies = [] - graph = DependencyGraph() - self._add_nodes_and_dependencies(graph, nodes, dependencies) - result = graph.get_direct_dependencies('b') - self.assertTrue(isinstance(result, (set))) - self.assertFalse(result) - - def test_get_direct_dependencies_should_return_dependendencies_set(self): - nodes = ['a', 'b', 'c', 'd'] - dependencies = [('a', 'b'), ('a', 'c')] - graph = DependencyGraph() - self._add_nodes_and_dependencies(graph, nodes, dependencies) - result = graph.get_direct_dependencies('c') - self.assertFalse('b' in result) - self.assertTrue('a' in result) - - def test_get_dependent(self): - nodes = ['a', 'b', 'c', 'd', 'e'] - dependencies = [('a', 'b'), ('b', 'c'), ('c', 'd'), ('e', 'd')] - graph = DependencyGraph() - self._add_nodes_and_dependencies(graph, nodes, dependencies) - self.assertEqual(graph.get_dependent(set('a')), set(('a', 'b', 'c', 'd'))) - self.assertEqual(graph.get_dependent(set('e')), set(('d', 'e'))) - - def test_get_dependencies(self): - nodes = ['a', 'b', 'c', 'd', 'e'] - dependencies = [('b', 'a'), ('c', 'b'), ('d', 'c'), ('d', 'e')] - graph = DependencyGraph() - self._add_nodes_and_dependencies(graph, nodes, dependencies) - self.assertEqual(graph.get_dependencies(set('a')), set(('a', 'b', 'c', 'd'))) - self.assertEqual(graph.get_dependencies(set('e')), set(('d', 'e'))) - - def test_get_dependent_detects_circular_dependencies(self): - nodes = ['a', 'b', 'c'] - dependencies = [('a', 'b'), ('b', 'c'), ('c', 'a')] - graph = DependencyGraph() - self._add_nodes_and_dependencies(graph, nodes, dependencies) - - try: - graph.get_dependent(set('a')) - except CircularDependencyException as exc: - self.assertEqual(exc.path, ['a', 'b', 'c', 'a']) - else: - self.fail("Exception not raised") - - def test_get_dependencies_detects_circular_dependencies(self): - nodes = ['a', 'b', 'c'] - dependencies = [('b', 'a'), ('c', 'b'), ('a', 'c')] - graph = DependencyGraph() - self._add_nodes_and_dependencies(graph, nodes, dependencies) - - try: - graph.get_dependencies(set('a')) - except CircularDependencyException as exc: - self.assertEqual(exc.path, ['a', 'b', 'c', 'a']) - else: - self.fail("Exception not raised") - - def _check_result(self, result, dependencies): - """ - Check that the resulting has an order such that - dependent files are located after their dependencies - """ - for dep1, dep2 in dependencies: - self.assertTrue(result.index(dep1) < result.index(dep2), "%s is not before %s" % (dep1, dep2)) - - @staticmethod - def _add_nodes_and_dependencies(graph, nodes, dependencies): - """ - Add nodes and dependencies to the graph - """ - for node in nodes: - graph.add_node(node) - for dep in dependencies: - graph.add_dependency(dep[0], dep[1]) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test the DependencyGraph +""" + +import unittest +from vunit.dependency_graph import (DependencyGraph, + CircularDependencyException) + + +class TestDependencyGraph(unittest.TestCase): + """ + Test the DependencyGraph + """ + + def test_should_return_empty_compile_order_for_no_nodes(self): + graph = DependencyGraph() + self.assertEqual(graph.toposort(), [], 'Should return empty list') + + def test_should_return_list_of_nodes_when_there_are_no_dependencies(self): + nodes = ['a', 'b', 'c', 'd'] + graph = DependencyGraph() + self._add_nodes_and_dependencies(graph, nodes, []) + result = graph.toposort() + self.assertEqual(result.sort(), nodes.sort(), 'Should return the node list in any order') + + def test_should_sort_in_topological_order_when_there_are_dependencies(self): + nodes = ['a', 'b', 'c', 'd', 'e', 'f'] + dependencies = [('a', 'b'), ('a', 'c'), ('b', 'd'), ('e', 'f')] + graph = DependencyGraph() + self._add_nodes_and_dependencies(graph, nodes, dependencies) + result = graph.toposort() + self._check_result(result, dependencies) + + def test_should_raise_runtime_error_exception_on_self_dependency(self): + nodes = ['a', 'b', 'c', 'd'] + dependencies = [('a', 'b'), ('a', 'c'), ('b', 'd'), ('d', 'd')] + graph = DependencyGraph() + self._add_nodes_and_dependencies(graph, nodes, dependencies) + + try: + graph.toposort() + except CircularDependencyException as exc: + self.assertEqual(exc.path, ['d', 'd']) + else: + self.fail("Exception not raised") + + def test_should_raise_runtime_error_exception_on_long_circular_dependency(self): + nodes = ['a', 'b', 'c', 'd'] + dependencies = [('a', 'b'), ('a', 'c'), ('b', 'd'), ('d', 'a')] + graph = DependencyGraph() + self._add_nodes_and_dependencies(graph, nodes, dependencies) + + try: + graph.toposort() + except CircularDependencyException as exc: + self.assertEqual(exc.path, ['a', 'b', 'd', 'a']) + else: + self.fail("Exception not raised") + + def test_should_resort_after_additions(self): + nodes = ['a', 'b', 'c', 'd', 'e', 'f'] + dependencies = [('a', 'b'), ('a', 'c'), ('b', 'd'), ('e', 'f')] + graph = DependencyGraph() + self._add_nodes_and_dependencies(graph, nodes, dependencies) + graph.toposort() + dependencies = [('a', 'b'), ('a', 'c'), ('b', 'd'), ('e', 'f'), ('b', 'g')] + graph.add_node('g') + graph.add_dependency('b', 'g') + result = graph.toposort() + self._check_result(result, dependencies) + + def test_get_direct_dependencies_should_return_empty_set_when_no_dependendencies(self): + nodes = ['a', 'b', 'c'] + dependencies = [] + graph = DependencyGraph() + self._add_nodes_and_dependencies(graph, nodes, dependencies) + result = graph.get_direct_dependencies('b') + self.assertTrue(isinstance(result, (set))) + self.assertFalse(result) + + def test_get_direct_dependencies_should_return_dependendencies_set(self): + nodes = ['a', 'b', 'c', 'd'] + dependencies = [('a', 'b'), ('a', 'c')] + graph = DependencyGraph() + self._add_nodes_and_dependencies(graph, nodes, dependencies) + result = graph.get_direct_dependencies('c') + self.assertFalse('b' in result) + self.assertTrue('a' in result) + + def test_get_dependent(self): + nodes = ['a', 'b', 'c', 'd', 'e'] + dependencies = [('a', 'b'), ('b', 'c'), ('c', 'd'), ('e', 'd')] + graph = DependencyGraph() + self._add_nodes_and_dependencies(graph, nodes, dependencies) + self.assertEqual(graph.get_dependent(set('a')), set(('a', 'b', 'c', 'd'))) + self.assertEqual(graph.get_dependent(set('e')), set(('d', 'e'))) + + def test_get_dependencies(self): + nodes = ['a', 'b', 'c', 'd', 'e'] + dependencies = [('b', 'a'), ('c', 'b'), ('d', 'c'), ('d', 'e')] + graph = DependencyGraph() + self._add_nodes_and_dependencies(graph, nodes, dependencies) + self.assertEqual(graph.get_dependencies(set('a')), set(('a', 'b', 'c', 'd'))) + self.assertEqual(graph.get_dependencies(set('e')), set(('d', 'e'))) + + def test_get_dependent_detects_circular_dependencies(self): + nodes = ['a', 'b', 'c'] + dependencies = [('a', 'b'), ('b', 'c'), ('c', 'a')] + graph = DependencyGraph() + self._add_nodes_and_dependencies(graph, nodes, dependencies) + + try: + graph.get_dependent(set('a')) + except CircularDependencyException as exc: + self.assertEqual(exc.path, ['a', 'b', 'c', 'a']) + else: + self.fail("Exception not raised") + + def test_get_dependencies_detects_circular_dependencies(self): + nodes = ['a', 'b', 'c'] + dependencies = [('b', 'a'), ('c', 'b'), ('a', 'c')] + graph = DependencyGraph() + self._add_nodes_and_dependencies(graph, nodes, dependencies) + + try: + graph.get_dependencies(set('a')) + except CircularDependencyException as exc: + self.assertEqual(exc.path, ['a', 'b', 'c', 'a']) + else: + self.fail("Exception not raised") + + def _check_result(self, result, dependencies): + """ + Check that the resulting has an order such that + dependent files are located after their dependencies + """ + for dep1, dep2 in dependencies: + self.assertTrue(result.index(dep1) < result.index(dep2), "%s is not before %s" % (dep1, dep2)) + + @staticmethod + def _add_nodes_and_dependencies(graph, nodes, dependencies): + """ + Add nodes and dependencies to the graph + """ + for node in nodes: + graph.add_node(node) + for dep in dependencies: + graph.add_dependency(dep[0], dep[1]) diff --git a/vunit/test/unit/test_ghdl_interface.py b/vunit/test/unit/test_ghdl_interface.py index 071d6a843..eb264d307 100644 --- a/vunit/test/unit/test_ghdl_interface.py +++ b/vunit/test/unit/test_ghdl_interface.py @@ -1,203 +1,203 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test the GHDL interface -""" - -import unittest -from os.path import join, dirname, exists -import os -from shutil import rmtree -from vunit.ghdl_interface import GHDLInterface -from vunit.test.mock_2or3 import mock -from vunit.project import Project -from vunit.ostools import renew_path, write_file -from vunit.exceptions import CompileError -from vunit.test.unit.test_test_bench import Entity -from vunit.configuration import Configuration - - -class TestGHDLInterface(unittest.TestCase): - """ - Test the GHDL interface - """ - - @mock.patch("vunit.ghdl_interface.GHDLInterface.find_executable") - def test_runtime_error_on_missing_gtkwave(self, find_executable): - executables = {} - - def find_executable_side_effect(name): - return executables[name] - - find_executable.side_effect = find_executable_side_effect - - executables["gtkwave"] = ["path"] - GHDLInterface(prefix="prefix", output_path="") - - executables["gtkwave"] = [] - GHDLInterface(prefix="prefix", output_path="") - self.assertRaises(RuntimeError, GHDLInterface, prefix="prefix", output_path="", gui=True) - - @mock.patch('subprocess.check_output', autospec=True) - def test_parses_llvm_backend(self, check_output): - version = b"""\ -GHDL 0.33dev (20141104) [Dunoon edition] - Compiled with GNAT Version: 4.8 - llvm code generator -Written by Tristan Gingold. - -Copyright (C) 2003 - 2014 Tristan Gingold. -GHDL is free software, covered by the GNU General Public License. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -""" - check_output.return_value = version - self.assertEqual(GHDLInterface.determine_backend("prefix"), "llvm") - - @mock.patch('subprocess.check_output', autospec=True) - def test_parses_mcode_backend(self, check_output): - version = b"""\ -GHDL 0.33dev (20141104) [Dunoon edition] - Compiled with GNAT Version: 4.9.2 - mcode code generator -Written by Tristan Gingold. - -Copyright (C) 2003 - 2014 Tristan Gingold. -GHDL is free software, covered by the GNU General Public License. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -""" - check_output.return_value = version - self.assertEqual(GHDLInterface.determine_backend("prefix"), "mcode") - - @mock.patch('subprocess.check_output', autospec=True) - def test_parses_gcc_backend(self, check_output): - version = b"""\ -GHDL 0.31 (20140108) [Dunoon edition] - Compiled with GNAT Version: 4.8 - GCC back-end code generator -Written by Tristan Gingold. - -Copyright (C) 2003 - 2014 Tristan Gingold. -GHDL is free software, covered by the GNU General Public License. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -""" - check_output.return_value = version - self.assertEqual(GHDLInterface.determine_backend("prefix"), "gcc") - - @mock.patch('subprocess.check_output', autospec=True) - def test_assertion_on_unknown_backend(self, check_output): - version = b"""\ -GHDL 0.31 (20140108) [Dunoon edition] - Compiled with GNAT Version: 4.8 - xyz code generator -Written by Tristan Gingold. - -Copyright (C) 2003 - 2014 Tristan Gingold. -GHDL is free software, covered by the GNU General Public License. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.""" - - check_output.return_value = version - self.assertRaises(AssertionError, GHDLInterface.determine_backend, "prefix") - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - def test_compile_project_2008(self, check_output): # pylint: disable=no-self-use - simif = GHDLInterface(prefix="prefix", output_path="") - write_file("file.vhd", "") - - project = Project() - project.add_library("lib", "lib_path") - project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="2008") - simif.compile_project(project) - check_output.assert_called_once_with( - [join("prefix", 'ghdl'), '-a', '--workdir=lib_path', '--work=lib', - '--std=08', '-Plib_path', 'file.vhd'], env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - def test_compile_project_2002(self, check_output): # pylint: disable=no-self-use - simif = GHDLInterface(prefix="prefix", output_path="") - write_file("file.vhd", "") - - project = Project() - project.add_library("lib", "lib_path") - project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="2002") - simif.compile_project(project) - check_output.assert_called_once_with( - [join("prefix", 'ghdl'), '-a', '--workdir=lib_path', '--work=lib', - '--std=02', '-Plib_path', 'file.vhd'], env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - def test_compile_project_93(self, check_output): # pylint: disable=no-self-use - simif = GHDLInterface(prefix="prefix", output_path="") - write_file("file.vhd", "") - - project = Project() - project.add_library("lib", "lib_path") - project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="93") - simif.compile_project(project) - check_output.assert_called_once_with( - [join("prefix", 'ghdl'), '-a', '--workdir=lib_path', '--work=lib', - '--std=93', '-Plib_path', 'file.vhd'], env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - def test_compile_project_extra_flags(self, check_output): # pylint: disable=no-self-use - simif = GHDLInterface(prefix="prefix", output_path="") - write_file("file.vhd", "") - - project = Project() - project.add_library("lib", "lib_path") - source_file = project.add_source_file("file.vhd", "lib", file_type="vhdl") - source_file.set_compile_option("ghdl.flags", ["custom", "flags"]) - simif.compile_project(project) - check_output.assert_called_once_with( - [join("prefix", 'ghdl'), '-a', '--workdir=lib_path', '--work=lib', '--std=08', - '-Plib_path', 'custom', 'flags', 'file.vhd'], env=simif.get_env()) - - def test_elaborate_e_project(self): - design_unit = Entity('tb_entity', file_name=join("tempdir", "file.vhd")) - design_unit.original_file_name = join("tempdir", "other_path", "original_file.vhd") - design_unit.generic_names = ["runner_cfg", "tb_path"] - - config = Configuration("name", design_unit, sim_options={"ghdl.elab_e": True}) - - simif = GHDLInterface(prefix="prefix", output_path="") - simif._vhdl_standard = "2008" # pylint: disable=protected-access - simif._project = Project() # pylint: disable=protected-access - simif._project.add_library("lib", "lib_path") # pylint: disable=protected-access - - self.assertEqual( - simif._get_command(config, join('output_path', 'ghdl'), True), # pylint: disable=protected-access - [ - join('prefix', 'ghdl'), - '-e', - '--std=08', - '--work=lib', - '--workdir=lib_path', - '-Plib_path', - '-o', join('output_path', 'ghdl', 'tb_entity-arch'), - 'tb_entity', 'arch' - ] - ) - - def test_compile_project_verilog_error(self): - simif = GHDLInterface(prefix="prefix", output_path="") - write_file("file.v", "") - - project = Project() - project.add_library("lib", "lib_path") - project.add_source_file("file.v", "lib", file_type="verilog") - self.assertRaises(CompileError, simif.compile_project, project) - - def setUp(self): - self.output_path = join(dirname(__file__), "test_ghdl_interface_out") - renew_path(self.output_path) - self.project = Project() - self.cwd = os.getcwd() - os.chdir(self.output_path) - - def tearDown(self): - os.chdir(self.cwd) - if exists(self.output_path): - rmtree(self.output_path) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test the GHDL interface +""" + +import unittest +from os.path import join, dirname, exists +import os +from shutil import rmtree +from vunit.ghdl_interface import GHDLInterface +from vunit.test.mock_2or3 import mock +from vunit.project import Project +from vunit.ostools import renew_path, write_file +from vunit.exceptions import CompileError +from vunit.test.unit.test_test_bench import Entity +from vunit.configuration import Configuration + + +class TestGHDLInterface(unittest.TestCase): + """ + Test the GHDL interface + """ + + @mock.patch("vunit.ghdl_interface.GHDLInterface.find_executable") + def test_runtime_error_on_missing_gtkwave(self, find_executable): + executables = {} + + def find_executable_side_effect(name): + return executables[name] + + find_executable.side_effect = find_executable_side_effect + + executables["gtkwave"] = ["path"] + GHDLInterface(prefix="prefix", output_path="") + + executables["gtkwave"] = [] + GHDLInterface(prefix="prefix", output_path="") + self.assertRaises(RuntimeError, GHDLInterface, prefix="prefix", output_path="", gui=True) + + @mock.patch('subprocess.check_output', autospec=True) + def test_parses_llvm_backend(self, check_output): + version = b"""\ +GHDL 0.33dev (20141104) [Dunoon edition] + Compiled with GNAT Version: 4.8 + llvm code generator +Written by Tristan Gingold. + +Copyright (C) 2003 - 2014 Tristan Gingold. +GHDL is free software, covered by the GNU General Public License. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +""" + check_output.return_value = version + self.assertEqual(GHDLInterface.determine_backend("prefix"), "llvm") + + @mock.patch('subprocess.check_output', autospec=True) + def test_parses_mcode_backend(self, check_output): + version = b"""\ +GHDL 0.33dev (20141104) [Dunoon edition] + Compiled with GNAT Version: 4.9.2 + mcode code generator +Written by Tristan Gingold. + +Copyright (C) 2003 - 2014 Tristan Gingold. +GHDL is free software, covered by the GNU General Public License. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +""" + check_output.return_value = version + self.assertEqual(GHDLInterface.determine_backend("prefix"), "mcode") + + @mock.patch('subprocess.check_output', autospec=True) + def test_parses_gcc_backend(self, check_output): + version = b"""\ +GHDL 0.31 (20140108) [Dunoon edition] + Compiled with GNAT Version: 4.8 + GCC back-end code generator +Written by Tristan Gingold. + +Copyright (C) 2003 - 2014 Tristan Gingold. +GHDL is free software, covered by the GNU General Public License. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +""" + check_output.return_value = version + self.assertEqual(GHDLInterface.determine_backend("prefix"), "gcc") + + @mock.patch('subprocess.check_output', autospec=True) + def test_assertion_on_unknown_backend(self, check_output): + version = b"""\ +GHDL 0.31 (20140108) [Dunoon edition] + Compiled with GNAT Version: 4.8 + xyz code generator +Written by Tristan Gingold. + +Copyright (C) 2003 - 2014 Tristan Gingold. +GHDL is free software, covered by the GNU General Public License. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.""" + + check_output.return_value = version + self.assertRaises(AssertionError, GHDLInterface.determine_backend, "prefix") + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + def test_compile_project_2008(self, check_output): # pylint: disable=no-self-use + simif = GHDLInterface(prefix="prefix", output_path="") + write_file("file.vhd", "") + + project = Project() + project.add_library("lib", "lib_path") + project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="2008") + simif.compile_project(project) + check_output.assert_called_once_with( + [join("prefix", 'ghdl'), '-a', '--workdir=lib_path', '--work=lib', + '--std=08', '-Plib_path', 'file.vhd'], env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + def test_compile_project_2002(self, check_output): # pylint: disable=no-self-use + simif = GHDLInterface(prefix="prefix", output_path="") + write_file("file.vhd", "") + + project = Project() + project.add_library("lib", "lib_path") + project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="2002") + simif.compile_project(project) + check_output.assert_called_once_with( + [join("prefix", 'ghdl'), '-a', '--workdir=lib_path', '--work=lib', + '--std=02', '-Plib_path', 'file.vhd'], env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + def test_compile_project_93(self, check_output): # pylint: disable=no-self-use + simif = GHDLInterface(prefix="prefix", output_path="") + write_file("file.vhd", "") + + project = Project() + project.add_library("lib", "lib_path") + project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="93") + simif.compile_project(project) + check_output.assert_called_once_with( + [join("prefix", 'ghdl'), '-a', '--workdir=lib_path', '--work=lib', + '--std=93', '-Plib_path', 'file.vhd'], env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + def test_compile_project_extra_flags(self, check_output): # pylint: disable=no-self-use + simif = GHDLInterface(prefix="prefix", output_path="") + write_file("file.vhd", "") + + project = Project() + project.add_library("lib", "lib_path") + source_file = project.add_source_file("file.vhd", "lib", file_type="vhdl") + source_file.set_compile_option("ghdl.flags", ["custom", "flags"]) + simif.compile_project(project) + check_output.assert_called_once_with( + [join("prefix", 'ghdl'), '-a', '--workdir=lib_path', '--work=lib', '--std=08', + '-Plib_path', 'custom', 'flags', 'file.vhd'], env=simif.get_env()) + + def test_elaborate_e_project(self): + design_unit = Entity('tb_entity', file_name=join("tempdir", "file.vhd")) + design_unit.original_file_name = join("tempdir", "other_path", "original_file.vhd") + design_unit.generic_names = ["runner_cfg", "tb_path"] + + config = Configuration("name", design_unit, sim_options={"ghdl.elab_e": True}) + + simif = GHDLInterface(prefix="prefix", output_path="") + simif._vhdl_standard = "2008" # pylint: disable=protected-access + simif._project = Project() # pylint: disable=protected-access + simif._project.add_library("lib", "lib_path") # pylint: disable=protected-access + + self.assertEqual( + simif._get_command(config, join('output_path', 'ghdl'), True), # pylint: disable=protected-access + [ + join('prefix', 'ghdl'), + '-e', + '--std=08', + '--work=lib', + '--workdir=lib_path', + '-Plib_path', + '-o', join('output_path', 'ghdl', 'tb_entity-arch'), + 'tb_entity', 'arch' + ] + ) + + def test_compile_project_verilog_error(self): + simif = GHDLInterface(prefix="prefix", output_path="") + write_file("file.v", "") + + project = Project() + project.add_library("lib", "lib_path") + project.add_source_file("file.v", "lib", file_type="verilog") + self.assertRaises(CompileError, simif.compile_project, project) + + def setUp(self): + self.output_path = join(dirname(__file__), "test_ghdl_interface_out") + renew_path(self.output_path) + self.project = Project() + self.cwd = os.getcwd() + os.chdir(self.output_path) + + def tearDown(self): + os.chdir(self.cwd) + if exists(self.output_path): + rmtree(self.output_path) diff --git a/vunit/test/unit/test_incisive_interface.py b/vunit/test/unit/test_incisive_interface.py index 508ed4976..ce86590ea 100644 --- a/vunit/test/unit/test_incisive_interface.py +++ b/vunit/test/unit/test_incisive_interface.py @@ -1,844 +1,844 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -# pylint: disable=too-many-public-methods - -""" -Test the Incisive interface -""" - - -import unittest -from os.path import join, dirname, exists, basename -import os -from shutil import rmtree -from vunit.incisive_interface import IncisiveInterface -from vunit.test.mock_2or3 import mock -from vunit.project import Project -from vunit.ostools import renew_path, write_file, read_file -from vunit.test_bench import Configuration - - -class TestIncisiveInterface(unittest.TestCase): - """ - Test the Incisive interface - """ - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - def test_compile_project_vhdl_2008(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="2008") - simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_vhdl_file_lib.args") - check_output.assert_called_once_with( - [join('prefix', 'irun'), '-f', args_file], - env=simif.get_env()) - self.assertEqual(read_file(args_file).splitlines(), - ['-compile', - '-nocopyright', - '-licqueue', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-v200x -extv200x', - '-work work', - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join(self.output_path, "irun_compile_vhdl_file_lib.log"), - '-quiet', - '-nclibdirname ""', - '-makelib lib_path', - '"file.vhd"', - '-endlib']) - - self.assertEqual(read_file(join(self.output_path, "cds.lib")), """\ -## cds.lib: Defines the locations of compiled libraries. -softinclude cds_root_irun/tools/inca/files/cds.lib -# needed for referencing the library 'basic' for cells 'cds_alias', 'cds_thru' etc. in analog models: -# NOTE: 'virtuoso' executable not found! -# define basic ".../tools/dfII/etc/cdslib/basic" -define lib "lib_path" -define work "%s/libraries/work" -""" % self.output_path) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - def test_compile_project_vhdl_2002(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="2002") - simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_vhdl_file_lib.args") - check_output.assert_called_once_with( - [join('prefix', 'irun'), '-f', args_file], - env=simif.get_env()) - self.assertEqual(read_file(args_file).splitlines(), - ['-compile', - '-nocopyright', - '-licqueue', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-v200x -extv200x', - '-work work', - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join(self.output_path, "irun_compile_vhdl_file_lib.log"), - '-quiet', - '-nclibdirname ""', - '-makelib lib_path', - '"file.vhd"', - '-endlib']) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - def test_compile_project_vhdl_93(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="93") - simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_vhdl_file_lib.args") - check_output.assert_called_once_with( - [join('prefix', 'irun'), '-f', args_file], - env=simif.get_env()) - self.assertEqual(read_file(args_file).splitlines(), - ['-compile', - '-nocopyright', - '-licqueue', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-v93', - '-work work', - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join(self.output_path, "irun_compile_vhdl_file_lib.log"), - '-quiet', - '-nclibdirname ""', - '-makelib lib_path', - '"file.vhd"', - '-endlib']) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - def test_compile_project_vhdl_extra_flags(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - source_file = project.add_source_file("file.vhd", "lib", file_type="vhdl") - source_file.set_compile_option("incisive.irun_vhdl_flags", ["custom", "flags"]) - simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_vhdl_file_lib.args") - check_output.assert_called_once_with( - [join('prefix', 'irun'), '-f', args_file], - env=simif.get_env()) - self.assertEqual(read_file(args_file).splitlines(), - ['-compile', - '-nocopyright', - '-licqueue', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-v200x -extv200x', - '-work work', - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join(self.output_path, "irun_compile_vhdl_file_lib.log"), - '-quiet', - 'custom', 'flags', - '-nclibdirname ""', - '-makelib lib_path', - '"file.vhd"', - '-endlib']) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - def test_compile_project_vhdl_hdlvar(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path, hdlvar="custom_hdlvar") - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - project.add_source_file("file.vhd", "lib", file_type="vhdl") - simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_vhdl_file_lib.args") - check_output.assert_called_once_with( - [join('prefix', 'irun'), '-f', args_file], - env=simif.get_env()) - self.assertEqual(read_file(args_file).splitlines(), - ['-compile', - '-nocopyright', - '-licqueue', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-v200x -extv200x', - '-work work', - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-hdlvar "custom_hdlvar"', - '-log "%s"' % join(self.output_path, "irun_compile_vhdl_file_lib.log"), - '-quiet', - '-nclibdirname ""', - '-makelib lib_path', - '"file.vhd"', - '-endlib']) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - def test_compile_project_verilog(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.v", "") - project.add_source_file("file.v", "lib", file_type="verilog") - simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") - check_output.assert_called_once_with( - [join('prefix', 'irun'), '-f', args_file], - env=simif.get_env()) - self.assertEqual(read_file(args_file).splitlines(), - ['-compile', - '-nocopyright', - '-licqueue', - '-nowarn UEXPSC', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-work work', - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join(self.output_path, "irun_compile_verilog_file_lib.log"), - '-quiet', - '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', - '-nclibdirname ""', - '-makelib lib', - '"file.v"', - '-endlib']) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - def test_compile_project_system_verilog(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.sv", "") - project.add_source_file("file.sv", "lib", file_type="systemverilog") - simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") - check_output.assert_called_once_with( - [join('prefix', 'irun'), '-f', args_file], - env=simif.get_env()) - self.assertEqual(read_file(args_file).splitlines(), - ['-compile', - '-nocopyright', - '-licqueue', - '-nowarn UEXPSC', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-work work', - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join(self.output_path, "irun_compile_verilog_file_lib.log"), - '-quiet', - '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', - '-nclibdirname ""', - '-makelib lib', - '"file.sv"', - '-endlib']) - - self.assertEqual(read_file(join(self.output_path, "cds.lib")), """\ -## cds.lib: Defines the locations of compiled libraries. -softinclude cds_root_irun/tools/inca/files/cds.lib -# needed for referencing the library 'basic' for cells 'cds_alias', 'cds_thru' etc. in analog models: -# NOTE: 'virtuoso' executable not found! -# define basic ".../tools/dfII/etc/cdslib/basic" -define lib "lib_path" -define work "%s/libraries/work" -""" % self.output_path) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - def test_compile_project_verilog_extra_flags(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.v", "") - source_file = project.add_source_file("file.v", "lib", file_type="verilog") - source_file.set_compile_option("incisive.irun_verilog_flags", ["custom", "flags"]) - simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") - check_output.assert_called_once_with( - [join('prefix', 'irun'), '-f', args_file], - env=simif.get_env()) - self.assertEqual(read_file(args_file).splitlines(), - ['-compile', - '-nocopyright', - '-licqueue', - '-nowarn UEXPSC', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-work work', - 'custom', 'flags', - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join(self.output_path, "irun_compile_verilog_file_lib.log"), - '-quiet', - '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', - '-nclibdirname ""', - '-makelib lib', - '"file.v"', - '-endlib']) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - def test_compile_project_verilog_include(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.v", "") - project.add_source_file("file.v", "lib", file_type="verilog", include_dirs=["include"]) - simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") - check_output.assert_called_once_with( - [join('prefix', 'irun'), '-f', args_file], - env=simif.get_env()) - self.assertEqual(read_file(args_file).splitlines(), - ['-compile', - '-nocopyright', - '-licqueue', - '-nowarn UEXPSC', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-work work', - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join(self.output_path, "irun_compile_verilog_file_lib.log"), - '-quiet', - '-incdir "include"', - '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', - '-nclibdirname ""', - '-makelib lib', - '"file.v"', - '-endlib']) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - def test_compile_project_verilog_define(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.v", "") - project.add_source_file("file.v", "lib", file_type="verilog", defines=dict(defname="defval")) - simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") - check_output.assert_called_once_with( - [join('prefix', 'irun'), '-f', args_file], - env=simif.get_env()) - self.assertEqual(read_file(args_file).splitlines(), - ['-compile', - '-nocopyright', - '-licqueue', - '-nowarn UEXPSC', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-work work', - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join(self.output_path, "irun_compile_verilog_file_lib.log"), - '-quiet', - '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', - '-define defname=defval', - '-nclibdirname ""', - '-makelib lib', - '"file.v"', - '-endlib']) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - def test_compile_project_verilog_hdlvar(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path, hdlvar="custom_hdlvar") - project = Project() - project.add_library("lib", "lib_path") - write_file("file.v", "") - project.add_source_file("file.v", "lib", file_type="verilog", defines=dict(defname="defval")) - simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") - check_output.assert_called_once_with( - [join('prefix', 'irun'), '-f', args_file], - env=simif.get_env()) - self.assertEqual(read_file(args_file).splitlines(), - ['-compile', - '-nocopyright', - '-licqueue', - '-nowarn UEXPSC', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-work work', - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-hdlvar "custom_hdlvar"', - '-log "%s"' % join(self.output_path, "irun_compile_verilog_file_lib.log"), - '-quiet', - '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', - '-define defname=defval', - '-nclibdirname ""', - '-makelib lib', - '"file.v"', - '-endlib']) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - def test_create_cds_lib(self, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - IncisiveInterface(prefix="prefix", output_path=self.output_path) - self.assertEqual(read_file(join(self.output_path, "cds.lib")), """\ -## cds.lib: Defines the locations of compiled libraries. -softinclude cds_root_irun/tools/inca/files/cds.lib -# needed for referencing the library 'basic' for cells 'cds_alias', 'cds_thru' etc. in analog models: -# NOTE: 'virtuoso' executable not found! -# define basic ".../tools/dfII/etc/cdslib/basic" -define work "%s/libraries/work" -""" % self.output_path) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - def test_create_cds_lib_virtuoso(self, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = "cds_root_virtuoso" - IncisiveInterface(prefix="prefix", output_path=self.output_path) - self.assertEqual(read_file(join(self.output_path, "cds.lib")), """\ -## cds.lib: Defines the locations of compiled libraries. -softinclude cds_root_irun/tools/inca/files/cds.lib -# needed for referencing the library 'basic' for cells 'cds_alias', 'cds_thru' etc. in analog models: -define basic "cds_root_virtuoso/tools/dfII/etc/cdslib/basic" -define work "%s/libraries/work" -""" % self.output_path) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.incisive_interface.run_command", autospec=True, return_value=True) - def test_simulate_vhdl(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) - - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - project.add_source_file("file.vhd", "lib", file_type="vhdl") - - with mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") as dummy: - simif.compile_project(project) - - config = make_config() - self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) - elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') - simulate_args_file = join('suite_output_path', simif.name, 'irun_simulate.args') - run_command.assert_has_calls([ - mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), - env=simif.get_env()), - mock.call([join('prefix', 'irun'), '-f', basename(simulate_args_file)], - cwd=dirname(simulate_args_file), - env=simif.get_env()), - ]) - - self.assertEqual( - read_file(elaborate_args_file).splitlines(), - ['-elaborate', - '-nocopyright', - '-licqueue', - '-errormax 10', - '-nowarn WRMNZD', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-ncerror EVBBOL', - '-ncerror EVBSTR', - '-ncerror EVBNAT', - '-work work', - '-nclibdirname "%s"' % join(self.output_path, "libraries"), - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join("suite_output_path", simif.name, "irun_elaborate.log"), - '-quiet', - '-reflib "lib_path"', - '-access +r', - '-input "@run"', - '-top lib.ent:arch']) - - self.assertEqual( - read_file(simulate_args_file).splitlines(), - ['-nocopyright', - '-licqueue', - '-errormax 10', - '-nowarn WRMNZD', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-ncerror EVBBOL', - '-ncerror EVBSTR', - '-ncerror EVBNAT', - '-work work', - '-nclibdirname "%s"' % join(self.output_path, "libraries"), - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join("suite_output_path", simif.name, "irun_simulate.log"), - '-quiet', - '-reflib "lib_path"', - '-access +r', - '-input "@run"', - '-top lib.ent:arch']) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.incisive_interface.run_command", autospec=True, return_value=True) - def test_simulate_verilog(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) - - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - project.add_source_file("file.vhd", "lib", file_type="vhdl") - - with mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") as dummy: - simif.compile_project(project) - - config = make_config(verilog=True) - self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) - elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') - simulate_args_file = join('suite_output_path', simif.name, 'irun_simulate.args') - run_command.assert_has_calls([ - mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), - env=simif.get_env()), - mock.call([join('prefix', 'irun'), '-f', basename(simulate_args_file)], - cwd=dirname(simulate_args_file), - env=simif.get_env()), - ]) - - self.assertEqual( - read_file(elaborate_args_file).splitlines(), - ['-elaborate', - '-nocopyright', - '-licqueue', - '-errormax 10', - '-nowarn WRMNZD', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-ncerror EVBBOL', - '-ncerror EVBSTR', - '-ncerror EVBNAT', - '-work work', - '-nclibdirname "%s"' % join(self.output_path, "libraries"), - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join("suite_output_path", simif.name, "irun_elaborate.log"), - '-quiet', - '-reflib "lib_path"', - '-access +r', - '-input "@run"', - '-top lib.modulename:sv']) - - self.assertEqual( - read_file(simulate_args_file).splitlines(), - ['-nocopyright', - '-licqueue', - '-errormax 10', - '-nowarn WRMNZD', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-ncerror EVBBOL', - '-ncerror EVBSTR', - '-ncerror EVBNAT', - '-work work', - '-nclibdirname "%s"' % join(self.output_path, "libraries"), - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join("suite_output_path", simif.name, "irun_simulate.log"), - '-quiet', - '-reflib "lib_path"', - '-access +r', - '-input "@run"', - '-top lib.modulename:sv']) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.incisive_interface.run_command", autospec=True, return_value=True) - def test_simulate_extra_flags(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) - config = make_config(sim_options={"incisive.irun_sim_flags": ["custom", "flags"]}) - self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) - elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') - simulate_args_file = join('suite_output_path', simif.name, 'irun_simulate.args') - run_command.assert_has_calls([ - mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), - env=simif.get_env()), - mock.call([join('prefix', 'irun'), '-f', basename(simulate_args_file)], - cwd=dirname(simulate_args_file), - env=simif.get_env()), - ]) - - args = read_file(elaborate_args_file).splitlines() - self.assertIn("custom", args) - self.assertIn("flags", args) - - args = read_file(simulate_args_file).splitlines() - self.assertIn("custom", args) - self.assertIn("flags", args) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.incisive_interface.run_command", autospec=True, return_value=True) - def test_simulate_generics_and_parameters(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) - config = make_config(verilog=True, - generics={"genstr": "genval", - "genint": 1, - "genbool": True}) - self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) - elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') - simulate_args_file = join('suite_output_path', simif.name, 'irun_simulate.args') - run_command.assert_has_calls([ - mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), - env=simif.get_env()), - mock.call([join('prefix', 'irun'), '-f', basename(simulate_args_file)], - cwd=dirname(simulate_args_file), - env=simif.get_env()), - ]) - - for args_file in [elaborate_args_file, simulate_args_file]: - args = read_file(args_file).splitlines() - self.assertIn('-gpg "modulename.genstr => \\"genval\\""', args) - self.assertIn('-gpg "modulename.genint => 1"', args) - self.assertIn('-gpg "modulename.genbool => \\"True\\""', args) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.incisive_interface.run_command", autospec=True, return_value=True) - def test_simulate_hdlvar(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path, - hdlvar="custom_hdlvar") - config = make_config() - self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) - elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') - simulate_args_file = join('suite_output_path', simif.name, 'irun_simulate.args') - run_command.assert_has_calls([ - mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), - env=simif.get_env()), - mock.call([join('prefix', 'irun'), '-f', basename(simulate_args_file)], - cwd=dirname(simulate_args_file), - env=simif.get_env()), - ]) - - for args_file in [elaborate_args_file, simulate_args_file]: - args = read_file(args_file).splitlines() - self.assertIn('-hdlvar "custom_hdlvar"', args) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.incisive_interface.run_command", autospec=True, return_value=True) - def test_elaborate(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) - config = make_config(verilog=True) - self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config, elaborate_only=True)) - elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') - run_command.assert_has_calls([ - mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), - env=simif.get_env()), - ]) - - self.assertEqual( - read_file(elaborate_args_file).splitlines(), - ['-elaborate', - '-nocopyright', - '-licqueue', - '-errormax 10', - '-nowarn WRMNZD', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-ncerror EVBBOL', - '-ncerror EVBSTR', - '-ncerror EVBNAT', - '-work work', - '-nclibdirname "%s"' % join(self.output_path, "libraries"), - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join("suite_output_path", simif.name, "irun_elaborate.log"), - '-quiet', - '-access +r', - '-input "@run"', - '-top lib.modulename:sv']) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.incisive_interface.run_command", autospec=True, return_value=False) - def test_elaborate_fail(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) - config = make_config() - self.assertFalse(simif.simulate("suite_output_path", "test_suite_name", config)) - elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') - run_command.assert_has_calls([ - mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), - env=simif.get_env()), - ]) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.incisive_interface.run_command", autospec=True, side_effect=[True, False]) - def test_simulate_fail(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) - config = make_config() - self.assertFalse(simif.simulate("suite_output_path", "test_suite_name", config)) - elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') - simulate_args_file = join('suite_output_path', simif.name, 'irun_simulate.args') - run_command.assert_has_calls([ - mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), - env=simif.get_env()), - mock.call([join('prefix', 'irun'), '-f', basename(simulate_args_file)], - cwd=dirname(simulate_args_file), - env=simif.get_env()), - ]) - - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") - @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") - @mock.patch("vunit.incisive_interface.run_command", autospec=True, return_value=True) - def test_simulate_gui(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): - find_cds_root_irun.return_value = "cds_root_irun" - find_cds_root_virtuoso.return_value = None - - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - project.add_source_file("file.vhd", "lib", file_type="vhdl") - - simif = IncisiveInterface(prefix="prefix", output_path=self.output_path, gui=True) - with mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") as dummy: - simif.compile_project(project) - config = make_config() - self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) - elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') - simulate_args_file = join('suite_output_path', simif.name, 'irun_simulate.args') - run_command.assert_has_calls([ - mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), - env=simif.get_env()), - mock.call([join('prefix', 'irun'), '-f', basename(simulate_args_file)], - cwd=dirname(simulate_args_file), - env=simif.get_env()), - ]) - self.assertEqual( - read_file(elaborate_args_file).splitlines(), - ['-elaborate', - '-nocopyright', - '-licqueue', - '-errormax 10', - '-nowarn WRMNZD', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-ncerror EVBBOL', - '-ncerror EVBSTR', - '-ncerror EVBNAT', - '-work work', - '-nclibdirname "%s"' % join(self.output_path, "libraries"), - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join("suite_output_path", simif.name, "irun_elaborate.log"), - '-quiet', - '-reflib "lib_path"', - '-access +rwc', - '-gui', - '-top lib.ent:arch']) - - self.assertEqual( - read_file(simulate_args_file).splitlines(), - ['-nocopyright', - '-licqueue', - '-errormax 10', - '-nowarn WRMNZD', - '-nowarn DLCPTH', - '-nowarn DLCVAR', - '-ncerror EVBBOL', - '-ncerror EVBSTR', - '-ncerror EVBNAT', - '-work work', - '-nclibdirname "%s"' % join(self.output_path, "libraries"), - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join("suite_output_path", simif.name, "irun_simulate.log"), - '-quiet', - '-reflib "lib_path"', - '-access +rwc', - '-gui', - '-top lib.ent:arch']) - - def setUp(self): - self.output_path = join(dirname(__file__), "test_incisive_out") - renew_path(self.output_path) - self.project = Project() - self.cwd = os.getcwd() - os.chdir(self.output_path) - - def tearDown(self): - os.chdir(self.cwd) - if exists(self.output_path): - rmtree(self.output_path) - - -def make_config(sim_options=None, generics=None, verilog=False): - """ - Utility to reduce boiler plate in tests - """ - cfg = mock.Mock(spec=Configuration) - cfg.library_name = "lib" - - if verilog: - cfg.entity_name = "modulename" - cfg.architecture_name = None - else: - cfg.entity_name = "ent" - cfg.architecture_name = "arch" - - cfg.sim_options = {} if sim_options is None else sim_options - cfg.generics = {} if generics is None else generics - return cfg +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +# pylint: disable=too-many-public-methods + +""" +Test the Incisive interface +""" + + +import unittest +from os.path import join, dirname, exists, basename +import os +from shutil import rmtree +from vunit.incisive_interface import IncisiveInterface +from vunit.test.mock_2or3 import mock +from vunit.project import Project +from vunit.ostools import renew_path, write_file, read_file +from vunit.test_bench import Configuration + + +class TestIncisiveInterface(unittest.TestCase): + """ + Test the Incisive interface + """ + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + def test_compile_project_vhdl_2008(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="2008") + simif.compile_project(project) + args_file = join(self.output_path, "irun_compile_vhdl_file_lib.args") + check_output.assert_called_once_with( + [join('prefix', 'irun'), '-f', args_file], + env=simif.get_env()) + self.assertEqual(read_file(args_file).splitlines(), + ['-compile', + '-nocopyright', + '-licqueue', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-v200x -extv200x', + '-work work', + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-log "%s"' % join(self.output_path, "irun_compile_vhdl_file_lib.log"), + '-quiet', + '-nclibdirname ""', + '-makelib lib_path', + '"file.vhd"', + '-endlib']) + + self.assertEqual(read_file(join(self.output_path, "cds.lib")), """\ +## cds.lib: Defines the locations of compiled libraries. +softinclude cds_root_irun/tools/inca/files/cds.lib +# needed for referencing the library 'basic' for cells 'cds_alias', 'cds_thru' etc. in analog models: +# NOTE: 'virtuoso' executable not found! +# define basic ".../tools/dfII/etc/cdslib/basic" +define lib "lib_path" +define work "%s/libraries/work" +""" % self.output_path) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + def test_compile_project_vhdl_2002(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="2002") + simif.compile_project(project) + args_file = join(self.output_path, "irun_compile_vhdl_file_lib.args") + check_output.assert_called_once_with( + [join('prefix', 'irun'), '-f', args_file], + env=simif.get_env()) + self.assertEqual(read_file(args_file).splitlines(), + ['-compile', + '-nocopyright', + '-licqueue', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-v200x -extv200x', + '-work work', + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-log "%s"' % join(self.output_path, "irun_compile_vhdl_file_lib.log"), + '-quiet', + '-nclibdirname ""', + '-makelib lib_path', + '"file.vhd"', + '-endlib']) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + def test_compile_project_vhdl_93(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="93") + simif.compile_project(project) + args_file = join(self.output_path, "irun_compile_vhdl_file_lib.args") + check_output.assert_called_once_with( + [join('prefix', 'irun'), '-f', args_file], + env=simif.get_env()) + self.assertEqual(read_file(args_file).splitlines(), + ['-compile', + '-nocopyright', + '-licqueue', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-v93', + '-work work', + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-log "%s"' % join(self.output_path, "irun_compile_vhdl_file_lib.log"), + '-quiet', + '-nclibdirname ""', + '-makelib lib_path', + '"file.vhd"', + '-endlib']) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + def test_compile_project_vhdl_extra_flags(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + source_file = project.add_source_file("file.vhd", "lib", file_type="vhdl") + source_file.set_compile_option("incisive.irun_vhdl_flags", ["custom", "flags"]) + simif.compile_project(project) + args_file = join(self.output_path, "irun_compile_vhdl_file_lib.args") + check_output.assert_called_once_with( + [join('prefix', 'irun'), '-f', args_file], + env=simif.get_env()) + self.assertEqual(read_file(args_file).splitlines(), + ['-compile', + '-nocopyright', + '-licqueue', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-v200x -extv200x', + '-work work', + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-log "%s"' % join(self.output_path, "irun_compile_vhdl_file_lib.log"), + '-quiet', + 'custom', 'flags', + '-nclibdirname ""', + '-makelib lib_path', + '"file.vhd"', + '-endlib']) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + def test_compile_project_vhdl_hdlvar(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path, hdlvar="custom_hdlvar") + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + project.add_source_file("file.vhd", "lib", file_type="vhdl") + simif.compile_project(project) + args_file = join(self.output_path, "irun_compile_vhdl_file_lib.args") + check_output.assert_called_once_with( + [join('prefix', 'irun'), '-f', args_file], + env=simif.get_env()) + self.assertEqual(read_file(args_file).splitlines(), + ['-compile', + '-nocopyright', + '-licqueue', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-v200x -extv200x', + '-work work', + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-hdlvar "custom_hdlvar"', + '-log "%s"' % join(self.output_path, "irun_compile_vhdl_file_lib.log"), + '-quiet', + '-nclibdirname ""', + '-makelib lib_path', + '"file.vhd"', + '-endlib']) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + def test_compile_project_verilog(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.v", "") + project.add_source_file("file.v", "lib", file_type="verilog") + simif.compile_project(project) + args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") + check_output.assert_called_once_with( + [join('prefix', 'irun'), '-f', args_file], + env=simif.get_env()) + self.assertEqual(read_file(args_file).splitlines(), + ['-compile', + '-nocopyright', + '-licqueue', + '-nowarn UEXPSC', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-work work', + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-log "%s"' % join(self.output_path, "irun_compile_verilog_file_lib.log"), + '-quiet', + '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', + '-nclibdirname ""', + '-makelib lib', + '"file.v"', + '-endlib']) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + def test_compile_project_system_verilog(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.sv", "") + project.add_source_file("file.sv", "lib", file_type="systemverilog") + simif.compile_project(project) + args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") + check_output.assert_called_once_with( + [join('prefix', 'irun'), '-f', args_file], + env=simif.get_env()) + self.assertEqual(read_file(args_file).splitlines(), + ['-compile', + '-nocopyright', + '-licqueue', + '-nowarn UEXPSC', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-work work', + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-log "%s"' % join(self.output_path, "irun_compile_verilog_file_lib.log"), + '-quiet', + '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', + '-nclibdirname ""', + '-makelib lib', + '"file.sv"', + '-endlib']) + + self.assertEqual(read_file(join(self.output_path, "cds.lib")), """\ +## cds.lib: Defines the locations of compiled libraries. +softinclude cds_root_irun/tools/inca/files/cds.lib +# needed for referencing the library 'basic' for cells 'cds_alias', 'cds_thru' etc. in analog models: +# NOTE: 'virtuoso' executable not found! +# define basic ".../tools/dfII/etc/cdslib/basic" +define lib "lib_path" +define work "%s/libraries/work" +""" % self.output_path) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + def test_compile_project_verilog_extra_flags(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.v", "") + source_file = project.add_source_file("file.v", "lib", file_type="verilog") + source_file.set_compile_option("incisive.irun_verilog_flags", ["custom", "flags"]) + simif.compile_project(project) + args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") + check_output.assert_called_once_with( + [join('prefix', 'irun'), '-f', args_file], + env=simif.get_env()) + self.assertEqual(read_file(args_file).splitlines(), + ['-compile', + '-nocopyright', + '-licqueue', + '-nowarn UEXPSC', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-work work', + 'custom', 'flags', + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-log "%s"' % join(self.output_path, "irun_compile_verilog_file_lib.log"), + '-quiet', + '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', + '-nclibdirname ""', + '-makelib lib', + '"file.v"', + '-endlib']) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + def test_compile_project_verilog_include(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.v", "") + project.add_source_file("file.v", "lib", file_type="verilog", include_dirs=["include"]) + simif.compile_project(project) + args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") + check_output.assert_called_once_with( + [join('prefix', 'irun'), '-f', args_file], + env=simif.get_env()) + self.assertEqual(read_file(args_file).splitlines(), + ['-compile', + '-nocopyright', + '-licqueue', + '-nowarn UEXPSC', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-work work', + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-log "%s"' % join(self.output_path, "irun_compile_verilog_file_lib.log"), + '-quiet', + '-incdir "include"', + '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', + '-nclibdirname ""', + '-makelib lib', + '"file.v"', + '-endlib']) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + def test_compile_project_verilog_define(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.v", "") + project.add_source_file("file.v", "lib", file_type="verilog", defines=dict(defname="defval")) + simif.compile_project(project) + args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") + check_output.assert_called_once_with( + [join('prefix', 'irun'), '-f', args_file], + env=simif.get_env()) + self.assertEqual(read_file(args_file).splitlines(), + ['-compile', + '-nocopyright', + '-licqueue', + '-nowarn UEXPSC', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-work work', + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-log "%s"' % join(self.output_path, "irun_compile_verilog_file_lib.log"), + '-quiet', + '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', + '-define defname=defval', + '-nclibdirname ""', + '-makelib lib', + '"file.v"', + '-endlib']) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + def test_compile_project_verilog_hdlvar(self, check_output, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path, hdlvar="custom_hdlvar") + project = Project() + project.add_library("lib", "lib_path") + write_file("file.v", "") + project.add_source_file("file.v", "lib", file_type="verilog", defines=dict(defname="defval")) + simif.compile_project(project) + args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") + check_output.assert_called_once_with( + [join('prefix', 'irun'), '-f', args_file], + env=simif.get_env()) + self.assertEqual(read_file(args_file).splitlines(), + ['-compile', + '-nocopyright', + '-licqueue', + '-nowarn UEXPSC', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-work work', + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-hdlvar "custom_hdlvar"', + '-log "%s"' % join(self.output_path, "irun_compile_verilog_file_lib.log"), + '-quiet', + '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', + '-define defname=defval', + '-nclibdirname ""', + '-makelib lib', + '"file.v"', + '-endlib']) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + def test_create_cds_lib(self, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + IncisiveInterface(prefix="prefix", output_path=self.output_path) + self.assertEqual(read_file(join(self.output_path, "cds.lib")), """\ +## cds.lib: Defines the locations of compiled libraries. +softinclude cds_root_irun/tools/inca/files/cds.lib +# needed for referencing the library 'basic' for cells 'cds_alias', 'cds_thru' etc. in analog models: +# NOTE: 'virtuoso' executable not found! +# define basic ".../tools/dfII/etc/cdslib/basic" +define work "%s/libraries/work" +""" % self.output_path) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + def test_create_cds_lib_virtuoso(self, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = "cds_root_virtuoso" + IncisiveInterface(prefix="prefix", output_path=self.output_path) + self.assertEqual(read_file(join(self.output_path, "cds.lib")), """\ +## cds.lib: Defines the locations of compiled libraries. +softinclude cds_root_irun/tools/inca/files/cds.lib +# needed for referencing the library 'basic' for cells 'cds_alias', 'cds_thru' etc. in analog models: +define basic "cds_root_virtuoso/tools/dfII/etc/cdslib/basic" +define work "%s/libraries/work" +""" % self.output_path) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.incisive_interface.run_command", autospec=True, return_value=True) + def test_simulate_vhdl(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) + + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + project.add_source_file("file.vhd", "lib", file_type="vhdl") + + with mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") as dummy: + simif.compile_project(project) + + config = make_config() + self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) + elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') + simulate_args_file = join('suite_output_path', simif.name, 'irun_simulate.args') + run_command.assert_has_calls([ + mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], + cwd=dirname(elaborate_args_file), + env=simif.get_env()), + mock.call([join('prefix', 'irun'), '-f', basename(simulate_args_file)], + cwd=dirname(simulate_args_file), + env=simif.get_env()), + ]) + + self.assertEqual( + read_file(elaborate_args_file).splitlines(), + ['-elaborate', + '-nocopyright', + '-licqueue', + '-errormax 10', + '-nowarn WRMNZD', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-ncerror EVBBOL', + '-ncerror EVBSTR', + '-ncerror EVBNAT', + '-work work', + '-nclibdirname "%s"' % join(self.output_path, "libraries"), + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-log "%s"' % join("suite_output_path", simif.name, "irun_elaborate.log"), + '-quiet', + '-reflib "lib_path"', + '-access +r', + '-input "@run"', + '-top lib.ent:arch']) + + self.assertEqual( + read_file(simulate_args_file).splitlines(), + ['-nocopyright', + '-licqueue', + '-errormax 10', + '-nowarn WRMNZD', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-ncerror EVBBOL', + '-ncerror EVBSTR', + '-ncerror EVBNAT', + '-work work', + '-nclibdirname "%s"' % join(self.output_path, "libraries"), + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-log "%s"' % join("suite_output_path", simif.name, "irun_simulate.log"), + '-quiet', + '-reflib "lib_path"', + '-access +r', + '-input "@run"', + '-top lib.ent:arch']) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.incisive_interface.run_command", autospec=True, return_value=True) + def test_simulate_verilog(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) + + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + project.add_source_file("file.vhd", "lib", file_type="vhdl") + + with mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") as dummy: + simif.compile_project(project) + + config = make_config(verilog=True) + self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) + elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') + simulate_args_file = join('suite_output_path', simif.name, 'irun_simulate.args') + run_command.assert_has_calls([ + mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], + cwd=dirname(elaborate_args_file), + env=simif.get_env()), + mock.call([join('prefix', 'irun'), '-f', basename(simulate_args_file)], + cwd=dirname(simulate_args_file), + env=simif.get_env()), + ]) + + self.assertEqual( + read_file(elaborate_args_file).splitlines(), + ['-elaborate', + '-nocopyright', + '-licqueue', + '-errormax 10', + '-nowarn WRMNZD', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-ncerror EVBBOL', + '-ncerror EVBSTR', + '-ncerror EVBNAT', + '-work work', + '-nclibdirname "%s"' % join(self.output_path, "libraries"), + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-log "%s"' % join("suite_output_path", simif.name, "irun_elaborate.log"), + '-quiet', + '-reflib "lib_path"', + '-access +r', + '-input "@run"', + '-top lib.modulename:sv']) + + self.assertEqual( + read_file(simulate_args_file).splitlines(), + ['-nocopyright', + '-licqueue', + '-errormax 10', + '-nowarn WRMNZD', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-ncerror EVBBOL', + '-ncerror EVBSTR', + '-ncerror EVBNAT', + '-work work', + '-nclibdirname "%s"' % join(self.output_path, "libraries"), + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-log "%s"' % join("suite_output_path", simif.name, "irun_simulate.log"), + '-quiet', + '-reflib "lib_path"', + '-access +r', + '-input "@run"', + '-top lib.modulename:sv']) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.incisive_interface.run_command", autospec=True, return_value=True) + def test_simulate_extra_flags(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) + config = make_config(sim_options={"incisive.irun_sim_flags": ["custom", "flags"]}) + self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) + elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') + simulate_args_file = join('suite_output_path', simif.name, 'irun_simulate.args') + run_command.assert_has_calls([ + mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], + cwd=dirname(elaborate_args_file), + env=simif.get_env()), + mock.call([join('prefix', 'irun'), '-f', basename(simulate_args_file)], + cwd=dirname(simulate_args_file), + env=simif.get_env()), + ]) + + args = read_file(elaborate_args_file).splitlines() + self.assertIn("custom", args) + self.assertIn("flags", args) + + args = read_file(simulate_args_file).splitlines() + self.assertIn("custom", args) + self.assertIn("flags", args) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.incisive_interface.run_command", autospec=True, return_value=True) + def test_simulate_generics_and_parameters(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) + config = make_config(verilog=True, + generics={"genstr": "genval", + "genint": 1, + "genbool": True}) + self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) + elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') + simulate_args_file = join('suite_output_path', simif.name, 'irun_simulate.args') + run_command.assert_has_calls([ + mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], + cwd=dirname(elaborate_args_file), + env=simif.get_env()), + mock.call([join('prefix', 'irun'), '-f', basename(simulate_args_file)], + cwd=dirname(simulate_args_file), + env=simif.get_env()), + ]) + + for args_file in [elaborate_args_file, simulate_args_file]: + args = read_file(args_file).splitlines() + self.assertIn('-gpg "modulename.genstr => \\"genval\\""', args) + self.assertIn('-gpg "modulename.genint => 1"', args) + self.assertIn('-gpg "modulename.genbool => \\"True\\""', args) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.incisive_interface.run_command", autospec=True, return_value=True) + def test_simulate_hdlvar(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path, + hdlvar="custom_hdlvar") + config = make_config() + self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) + elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') + simulate_args_file = join('suite_output_path', simif.name, 'irun_simulate.args') + run_command.assert_has_calls([ + mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], + cwd=dirname(elaborate_args_file), + env=simif.get_env()), + mock.call([join('prefix', 'irun'), '-f', basename(simulate_args_file)], + cwd=dirname(simulate_args_file), + env=simif.get_env()), + ]) + + for args_file in [elaborate_args_file, simulate_args_file]: + args = read_file(args_file).splitlines() + self.assertIn('-hdlvar "custom_hdlvar"', args) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.incisive_interface.run_command", autospec=True, return_value=True) + def test_elaborate(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) + config = make_config(verilog=True) + self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config, elaborate_only=True)) + elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') + run_command.assert_has_calls([ + mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], + cwd=dirname(elaborate_args_file), + env=simif.get_env()), + ]) + + self.assertEqual( + read_file(elaborate_args_file).splitlines(), + ['-elaborate', + '-nocopyright', + '-licqueue', + '-errormax 10', + '-nowarn WRMNZD', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-ncerror EVBBOL', + '-ncerror EVBSTR', + '-ncerror EVBNAT', + '-work work', + '-nclibdirname "%s"' % join(self.output_path, "libraries"), + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-log "%s"' % join("suite_output_path", simif.name, "irun_elaborate.log"), + '-quiet', + '-access +r', + '-input "@run"', + '-top lib.modulename:sv']) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.incisive_interface.run_command", autospec=True, return_value=False) + def test_elaborate_fail(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) + config = make_config() + self.assertFalse(simif.simulate("suite_output_path", "test_suite_name", config)) + elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') + run_command.assert_has_calls([ + mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], + cwd=dirname(elaborate_args_file), + env=simif.get_env()), + ]) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.incisive_interface.run_command", autospec=True, side_effect=[True, False]) + def test_simulate_fail(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) + config = make_config() + self.assertFalse(simif.simulate("suite_output_path", "test_suite_name", config)) + elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') + simulate_args_file = join('suite_output_path', simif.name, 'irun_simulate.args') + run_command.assert_has_calls([ + mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], + cwd=dirname(elaborate_args_file), + env=simif.get_env()), + mock.call([join('prefix', 'irun'), '-f', basename(simulate_args_file)], + cwd=dirname(simulate_args_file), + env=simif.get_env()), + ]) + + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.incisive_interface.IncisiveInterface.find_cds_root_irun") + @mock.patch("vunit.incisive_interface.run_command", autospec=True, return_value=True) + def test_simulate_gui(self, run_command, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + project.add_source_file("file.vhd", "lib", file_type="vhdl") + + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path, gui=True) + with mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") as dummy: + simif.compile_project(project) + config = make_config() + self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) + elaborate_args_file = join('suite_output_path', simif.name, 'irun_elaborate.args') + simulate_args_file = join('suite_output_path', simif.name, 'irun_simulate.args') + run_command.assert_has_calls([ + mock.call([join('prefix', 'irun'), '-f', basename(elaborate_args_file)], + cwd=dirname(elaborate_args_file), + env=simif.get_env()), + mock.call([join('prefix', 'irun'), '-f', basename(simulate_args_file)], + cwd=dirname(simulate_args_file), + env=simif.get_env()), + ]) + self.assertEqual( + read_file(elaborate_args_file).splitlines(), + ['-elaborate', + '-nocopyright', + '-licqueue', + '-errormax 10', + '-nowarn WRMNZD', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-ncerror EVBBOL', + '-ncerror EVBSTR', + '-ncerror EVBNAT', + '-work work', + '-nclibdirname "%s"' % join(self.output_path, "libraries"), + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-log "%s"' % join("suite_output_path", simif.name, "irun_elaborate.log"), + '-quiet', + '-reflib "lib_path"', + '-access +rwc', + '-gui', + '-top lib.ent:arch']) + + self.assertEqual( + read_file(simulate_args_file).splitlines(), + ['-nocopyright', + '-licqueue', + '-errormax 10', + '-nowarn WRMNZD', + '-nowarn DLCPTH', + '-nowarn DLCVAR', + '-ncerror EVBBOL', + '-ncerror EVBSTR', + '-ncerror EVBNAT', + '-work work', + '-nclibdirname "%s"' % join(self.output_path, "libraries"), + '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-log "%s"' % join("suite_output_path", simif.name, "irun_simulate.log"), + '-quiet', + '-reflib "lib_path"', + '-access +rwc', + '-gui', + '-top lib.ent:arch']) + + def setUp(self): + self.output_path = join(dirname(__file__), "test_incisive_out") + renew_path(self.output_path) + self.project = Project() + self.cwd = os.getcwd() + os.chdir(self.output_path) + + def tearDown(self): + os.chdir(self.cwd) + if exists(self.output_path): + rmtree(self.output_path) + + +def make_config(sim_options=None, generics=None, verilog=False): + """ + Utility to reduce boiler plate in tests + """ + cfg = mock.Mock(spec=Configuration) + cfg.library_name = "lib" + + if verilog: + cfg.entity_name = "modulename" + cfg.architecture_name = None + else: + cfg.entity_name = "ent" + cfg.architecture_name = "arch" + + cfg.sim_options = {} if sim_options is None else sim_options + cfg.generics = {} if generics is None else generics + return cfg diff --git a/vunit/test/unit/test_location_preprocessor.py b/vunit/test/unit/test_location_preprocessor.py index 0b4030f96..88f455aa9 100644 --- a/vunit/test/unit/test_location_preprocessor.py +++ b/vunit/test/unit/test_location_preprocessor.py @@ -1,196 +1,196 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test the location preprocessor -""" - - -import unittest -from vunit.location_preprocessor import LocationPreprocessor - - -class TestLocationPreprocessor(unittest.TestCase): - """ - Test the location preprocessor - """ - - def setUp(self): - self._location_preprocessor = LocationPreprocessor() - self._location_preprocessor.add_subprogram('sub_prog') - self._location_preprocessor.add_subprogram('unwanted_sub_prog') - self._location_preprocessor.remove_subprogram('unwanted_sub_prog') - self._location_preprocessor.remove_subprogram('log') - - def _verify_result(self, code, expected_result): - """ - Assert that the code after preprocessing is equal to the expected_result - """ - result = self._location_preprocessor.run(code, 'foo.vhd') - self.assertEqual(result, expected_result) - - def test_that_procedure_calls_are_found(self): - code = """ -sub_prog("0"); -my_protected_type.sub_prog("1"); - sub_prog("2"); - sub_prog ("3"); - sub_prog (" 4 "); - sub_prog (" 5 ") ; -sub_prog("6", - "7"); - sub_prog; -""" - expected_result = """ -sub_prog("0", line_num => 2, file_name => "foo.vhd"); -my_protected_type.sub_prog("1", line_num => 3, file_name => "foo.vhd"); - sub_prog("2", line_num => 4, file_name => "foo.vhd"); - sub_prog ("3", line_num => 5, file_name => "foo.vhd"); - sub_prog (" 4 ", line_num => 6, file_name => "foo.vhd"); - sub_prog (" 5 ", line_num => 7, file_name => "foo.vhd") ; -sub_prog("6", - "7", line_num => 8, file_name => "foo.vhd"); - sub_prog(line_num => 10, file_name => "foo.vhd"); -""" - self._verify_result(code, expected_result) - - def test_that_function_calls_are_found(self): - code = """ -a:=sub_prog("0"); -a2:=my_protected_type.sub_prog("1"); - b :=sub_prog("2"); - c := sub_prog ("3"); - d := sub_prog (" 4 "); - e<=sub_prog (" 5 ") ; -f <= sub_prog; -g := h + sub_prog + 3; -- DOESN'T SUPPORT FUNCTION CALLS WITHOUT PARAMETERS NOT FOLLOWED BY SEMICOLON -i := j * (sub_prog(1, 2) + 17) + 8; -k := l * (sub_prog(1, - 2) + 17) + 8; -""" - expected_result = """ -a:=sub_prog("0", line_num => 2, file_name => "foo.vhd"); -a2:=my_protected_type.sub_prog("1", line_num => 3, file_name => "foo.vhd"); - b :=sub_prog("2", line_num => 4, file_name => "foo.vhd"); - c := sub_prog ("3", line_num => 5, file_name => "foo.vhd"); - d := sub_prog (" 4 ", line_num => 6, file_name => "foo.vhd"); - e<=sub_prog (" 5 ", line_num => 7, file_name => "foo.vhd") ; -f <= sub_prog(line_num => 8, file_name => "foo.vhd"); -g := h + sub_prog + 3; -- DOESN'T SUPPORT FUNCTION CALLS WITHOUT PARAMETERS NOT FOLLOWED BY SEMICOLON -i := j * (sub_prog(1, 2, line_num => 10, file_name => "foo.vhd") + 17) + 8; -k := l * (sub_prog(1, - 2, line_num => 11, file_name => "foo.vhd") + 17) + 8; -""" - self._verify_result(code, expected_result) - - def test_that_subprograms_can_be_excluded(self): - code = """ -a:=sub_prog("0"); -log("1"); -unwanted_sub_prog("2"); -""" - expected_result = """ -a:=sub_prog("0", line_num => 2, file_name => "foo.vhd"); -log("1"); -unwanted_sub_prog("2"); -""" - self._verify_result(code, expected_result) - - def test_that_an_unknown_subprogram_cannot_be_removed(self): - self.assertRaises(RuntimeError, self._location_preprocessor.remove_subprogram, 'unknown_sub_prog') - - def test_that_similar_sub_program_names_are_ignored(self): - code = """ -another_sub_prog("6"); -sub_prog_2; -""" - self._verify_result(code, expected_result=code) - - def test_that_sub_program_declarations_are_ignored(self): - code = """ -procedure sub_prog(foo1); - function sub_prog (foo3) ; -""" - self._verify_result(code, expected_result=code) - - def test_that_sub_program_definitions_are_ignored(self): - code = """ -procedure sub_prog(foo4) is -begin - null; -end; -function sub_prog(foo4) return boolean is -begin - return true; -end; -""" - - self._verify_result(code, expected_result=code) - - def test_that_already_located_calls_are_left_untouched(self): - code = """ -procedure sub_prog(foo4) is -begin - sub_prog("foo", line_num=> 17); -end; -procedure sub_prog(foo4) is -begin - sub_prog("foo", - file_name=>"foo.vhd"); -end; -procedure sub_prog(foo4) is -begin - sub_prog("foo", line_num => 17, file_name => "foo.vhd"); -end; -""" - expected_result = """ -procedure sub_prog(foo4) is -begin - sub_prog("foo", line_num=> 17, file_name => "foo.vhd"); -end; -procedure sub_prog(foo4) is -begin - sub_prog("foo", - file_name=>"foo.vhd", line_num => 8); -end; -procedure sub_prog(foo4) is -begin - sub_prog("foo", line_num => 17, file_name => "foo.vhd"); -end; -""" - self._verify_result(code, expected_result) - - def test_that_asserts_with_severity_level_are_not_affected_despite_name_conflict_with_log_functions(self): - code = """ -assert False report "Failed" severity warning; -assert False report "Failed" severity error; -assert False report "Failed" severity failure; -""" - - self._verify_result(code, expected_result=code) - - def test_that_assignments_to_signals_and_variables_with_listed_subprogram_names_are_ignored(self): - code = """ -sub_prog := true; -sub_prog <= true; -debug(1 to 2):="00"; -debug(1 to 2) <= "00"; -debug(1 to 2) - <= "00"; -(debug(1 to 2), foo) <= "0011"; -- AGGREGATE ASSIGNMENTS ARE NOT HANDLED -""" - - expected_result = """ -sub_prog := true; -sub_prog <= true; -debug(1 to 2):="00"; -debug(1 to 2) <= "00"; -debug(1 to 2) - <= "00"; -(debug(1 to 2, line_num => 8, file_name => "foo.vhd"), foo) <= "0011"; -- AGGREGATE ASSIGNMENTS ARE NOT HANDLED -""" - - self._verify_result(code, expected_result) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test the location preprocessor +""" + + +import unittest +from vunit.location_preprocessor import LocationPreprocessor + + +class TestLocationPreprocessor(unittest.TestCase): + """ + Test the location preprocessor + """ + + def setUp(self): + self._location_preprocessor = LocationPreprocessor() + self._location_preprocessor.add_subprogram('sub_prog') + self._location_preprocessor.add_subprogram('unwanted_sub_prog') + self._location_preprocessor.remove_subprogram('unwanted_sub_prog') + self._location_preprocessor.remove_subprogram('log') + + def _verify_result(self, code, expected_result): + """ + Assert that the code after preprocessing is equal to the expected_result + """ + result = self._location_preprocessor.run(code, 'foo.vhd') + self.assertEqual(result, expected_result) + + def test_that_procedure_calls_are_found(self): + code = """ +sub_prog("0"); +my_protected_type.sub_prog("1"); + sub_prog("2"); + sub_prog ("3"); + sub_prog (" 4 "); + sub_prog (" 5 ") ; +sub_prog("6", + "7"); + sub_prog; +""" + expected_result = """ +sub_prog("0", line_num => 2, file_name => "foo.vhd"); +my_protected_type.sub_prog("1", line_num => 3, file_name => "foo.vhd"); + sub_prog("2", line_num => 4, file_name => "foo.vhd"); + sub_prog ("3", line_num => 5, file_name => "foo.vhd"); + sub_prog (" 4 ", line_num => 6, file_name => "foo.vhd"); + sub_prog (" 5 ", line_num => 7, file_name => "foo.vhd") ; +sub_prog("6", + "7", line_num => 8, file_name => "foo.vhd"); + sub_prog(line_num => 10, file_name => "foo.vhd"); +""" + self._verify_result(code, expected_result) + + def test_that_function_calls_are_found(self): + code = """ +a:=sub_prog("0"); +a2:=my_protected_type.sub_prog("1"); + b :=sub_prog("2"); + c := sub_prog ("3"); + d := sub_prog (" 4 "); + e<=sub_prog (" 5 ") ; +f <= sub_prog; +g := h + sub_prog + 3; -- DOESN'T SUPPORT FUNCTION CALLS WITHOUT PARAMETERS NOT FOLLOWED BY SEMICOLON +i := j * (sub_prog(1, 2) + 17) + 8; +k := l * (sub_prog(1, + 2) + 17) + 8; +""" + expected_result = """ +a:=sub_prog("0", line_num => 2, file_name => "foo.vhd"); +a2:=my_protected_type.sub_prog("1", line_num => 3, file_name => "foo.vhd"); + b :=sub_prog("2", line_num => 4, file_name => "foo.vhd"); + c := sub_prog ("3", line_num => 5, file_name => "foo.vhd"); + d := sub_prog (" 4 ", line_num => 6, file_name => "foo.vhd"); + e<=sub_prog (" 5 ", line_num => 7, file_name => "foo.vhd") ; +f <= sub_prog(line_num => 8, file_name => "foo.vhd"); +g := h + sub_prog + 3; -- DOESN'T SUPPORT FUNCTION CALLS WITHOUT PARAMETERS NOT FOLLOWED BY SEMICOLON +i := j * (sub_prog(1, 2, line_num => 10, file_name => "foo.vhd") + 17) + 8; +k := l * (sub_prog(1, + 2, line_num => 11, file_name => "foo.vhd") + 17) + 8; +""" + self._verify_result(code, expected_result) + + def test_that_subprograms_can_be_excluded(self): + code = """ +a:=sub_prog("0"); +log("1"); +unwanted_sub_prog("2"); +""" + expected_result = """ +a:=sub_prog("0", line_num => 2, file_name => "foo.vhd"); +log("1"); +unwanted_sub_prog("2"); +""" + self._verify_result(code, expected_result) + + def test_that_an_unknown_subprogram_cannot_be_removed(self): + self.assertRaises(RuntimeError, self._location_preprocessor.remove_subprogram, 'unknown_sub_prog') + + def test_that_similar_sub_program_names_are_ignored(self): + code = """ +another_sub_prog("6"); +sub_prog_2; +""" + self._verify_result(code, expected_result=code) + + def test_that_sub_program_declarations_are_ignored(self): + code = """ +procedure sub_prog(foo1); + function sub_prog (foo3) ; +""" + self._verify_result(code, expected_result=code) + + def test_that_sub_program_definitions_are_ignored(self): + code = """ +procedure sub_prog(foo4) is +begin + null; +end; +function sub_prog(foo4) return boolean is +begin + return true; +end; +""" + + self._verify_result(code, expected_result=code) + + def test_that_already_located_calls_are_left_untouched(self): + code = """ +procedure sub_prog(foo4) is +begin + sub_prog("foo", line_num=> 17); +end; +procedure sub_prog(foo4) is +begin + sub_prog("foo", + file_name=>"foo.vhd"); +end; +procedure sub_prog(foo4) is +begin + sub_prog("foo", line_num => 17, file_name => "foo.vhd"); +end; +""" + expected_result = """ +procedure sub_prog(foo4) is +begin + sub_prog("foo", line_num=> 17, file_name => "foo.vhd"); +end; +procedure sub_prog(foo4) is +begin + sub_prog("foo", + file_name=>"foo.vhd", line_num => 8); +end; +procedure sub_prog(foo4) is +begin + sub_prog("foo", line_num => 17, file_name => "foo.vhd"); +end; +""" + self._verify_result(code, expected_result) + + def test_that_asserts_with_severity_level_are_not_affected_despite_name_conflict_with_log_functions(self): + code = """ +assert False report "Failed" severity warning; +assert False report "Failed" severity error; +assert False report "Failed" severity failure; +""" + + self._verify_result(code, expected_result=code) + + def test_that_assignments_to_signals_and_variables_with_listed_subprogram_names_are_ignored(self): + code = """ +sub_prog := true; +sub_prog <= true; +debug(1 to 2):="00"; +debug(1 to 2) <= "00"; +debug(1 to 2) + <= "00"; +(debug(1 to 2), foo) <= "0011"; -- AGGREGATE ASSIGNMENTS ARE NOT HANDLED +""" + + expected_result = """ +sub_prog := true; +sub_prog <= true; +debug(1 to 2):="00"; +debug(1 to 2) <= "00"; +debug(1 to 2) + <= "00"; +(debug(1 to 2, line_num => 8, file_name => "foo.vhd"), foo) <= "0011"; -- AGGREGATE ASSIGNMENTS ARE NOT HANDLED +""" + + self._verify_result(code, expected_result) diff --git a/vunit/test/unit/test_modelsim_interface.py b/vunit/test/unit/test_modelsim_interface.py index 515950edf..72317bee6 100644 --- a/vunit/test/unit/test_modelsim_interface.py +++ b/vunit/test/unit/test_modelsim_interface.py @@ -1,286 +1,286 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test the ModelSim interface -""" - - -import unittest -from os.path import join, dirname, exists -import os -from shutil import rmtree -from vunit.modelsim_interface import ModelSimInterface -from vunit.test.mock_2or3 import mock -from vunit.test.common import set_env -from vunit.project import Project -from vunit.ostools import renew_path, write_file - - -class TestModelSimInterface(unittest.TestCase): - """ - Test the ModelSim interface - """ - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.modelsim_interface.Process", autospec=True) - def test_compile_project_vhdl_2008(self, process, check_output): - simif = ModelSimInterface(prefix=self.prefix_path, - output_path=self.output_path, - persistent=False) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="2008") - simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] - process.assert_called_once_with(process_args, env=simif.get_env()) - check_args = [join(self.prefix_path, 'vcom'), '-quiet', '-modelsimini', - join(self.output_path, "modelsim.ini"), '-2008', - '-work', 'lib', 'file.vhd'] - check_output.assert_called_once_with(check_args, env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.modelsim_interface.Process", autospec=True) - def test_compile_project_vhdl_2002(self, process, check_output): - simif = ModelSimInterface(prefix=self.prefix_path, - output_path=self.output_path, - persistent=False) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="2002") - simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] - process.assert_called_once_with(process_args, env=simif.get_env()) - check_args = [join(self.prefix_path, 'vcom'), '-quiet', '-modelsimini', - join(self.output_path, "modelsim.ini"), '-2002', - '-work', 'lib', 'file.vhd'] - check_output.assert_called_once_with(check_args, env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.modelsim_interface.Process", autospec=True) - def test_compile_project_vhdl_93(self, process, check_output): - simif = ModelSimInterface(prefix=self.prefix_path, - output_path=self.output_path, - persistent=False) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="93") - simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] - process.assert_called_once_with(process_args, env=simif.get_env()) - check_args = [join(self.prefix_path, 'vcom'), '-quiet', '-modelsimini', - join(self.output_path, "modelsim.ini"), '-93', - '-work', 'lib', 'file.vhd'] - check_output.assert_called_once_with(check_args, env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.modelsim_interface.Process", autospec=True) - def test_compile_project_vhdl_extra_flags(self, process, check_output): - simif = ModelSimInterface(prefix=self.prefix_path, - output_path=self.output_path, - persistent=False) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - source_file = project.add_source_file("file.vhd", "lib", file_type="vhdl") - source_file.set_compile_option("modelsim.vcom_flags", ["custom", "flags"]) - simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] - process.assert_called_once_with(process_args, env=simif.get_env()) - check_args = [join(self.prefix_path, 'vcom'), '-quiet', '-modelsimini', - join(self.output_path, "modelsim.ini"), 'custom', - 'flags', '-2008', '-work', 'lib', 'file.vhd'] - check_output.assert_called_once_with(check_args, env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.modelsim_interface.Process", autospec=True) - def test_compile_project_verilog(self, process, check_output): - simif = ModelSimInterface(prefix=self.prefix_path, - output_path=self.output_path, - persistent=False) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.v", "") - project.add_source_file("file.v", "lib", file_type="verilog") - simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] - process.assert_called_once_with(process_args, env=simif.get_env()) - check_args = [join(self.prefix_path, 'vlog'), '-quiet', '-modelsimini', - join(self.output_path, "modelsim.ini"), '-work', 'lib', - 'file.v', '-L', 'lib'] - check_output.assert_called_once_with(check_args, env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.modelsim_interface.Process", autospec=True) - def test_compile_project_system_verilog(self, process, check_output): - simif = ModelSimInterface(prefix=self.prefix_path, - output_path=self.output_path, - persistent=False) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.sv", "") - project.add_source_file("file.sv", "lib", file_type="systemverilog") - simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] - process.assert_called_once_with(process_args, env=simif.get_env()) - check_args = [join(self.prefix_path, 'vlog'), '-quiet', '-modelsimini', - join(self.output_path, "modelsim.ini"), '-sv', - '-work', 'lib', 'file.sv', '-L', 'lib'] - check_output.assert_called_once_with(check_args, env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.modelsim_interface.Process", autospec=True) - def test_compile_project_verilog_extra_flags(self, process, check_output): - simif = ModelSimInterface(prefix=self.prefix_path, - output_path=self.output_path, - persistent=False) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.v", "") - source_file = project.add_source_file("file.v", "lib", file_type="verilog") - source_file.set_compile_option("modelsim.vlog_flags", ["custom", "flags"]) - simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] - process.assert_called_once_with(process_args, env=simif.get_env()) - check_args = [join(self.prefix_path, 'vlog'), '-quiet', '-modelsimini', - join(self.output_path, "modelsim.ini"), 'custom', 'flags', - '-work', 'lib', 'file.v', '-L', 'lib'] - check_output.assert_called_once_with(check_args, env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.modelsim_interface.Process", autospec=True) - def test_compile_project_verilog_include(self, process, check_output): - simif = ModelSimInterface(prefix=self.prefix_path, - output_path=self.output_path, - persistent=False) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.v", "") - project.add_source_file("file.v", "lib", file_type="verilog", include_dirs=["include"]) - simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] - process.assert_called_once_with(process_args, env=simif.get_env()) - check_args = [join(self.prefix_path, 'vlog'), '-quiet', '-modelsimini', - join(self.output_path, "modelsim.ini"), '-work', 'lib', - 'file.v', '-L', 'lib', '+incdir+include'] - check_output.assert_called_once_with(check_args, env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.modelsim_interface.Process", autospec=True) - def test_compile_project_verilog_define(self, process, check_output): - simif = ModelSimInterface(prefix=self.prefix_path, - output_path=self.output_path, - persistent=False) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.v", "") - project.add_source_file("file.v", "lib", file_type="verilog", defines={"defname": "defval"}) - simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] - process.assert_called_once_with(process_args, env=simif.get_env()) - process_args = [join(self.prefix_path, 'vlog'), '-quiet', '-modelsimini', - join(self.output_path, "modelsim.ini"), '-work', 'lib', - 'file.v', '-L', 'lib', '+define+defname=defval'] - check_output.assert_called_once_with(process_args, env=simif.get_env()) - - def test_copies_modelsim_ini_file_from_install(self): - modelsim_ini = join(self.output_path, "modelsim.ini") - installed_modelsim_ini = join(self.prefix_path, "..", "modelsim.ini") - user_modelsim_ini = join(self.test_path, "my_modelsim.ini") - - with open(installed_modelsim_ini, "w") as fptr: - fptr.write("installed") - - with open(user_modelsim_ini, "w") as fptr: - fptr.write("user") - - ModelSimInterface(prefix=self.prefix_path, - output_path=self.output_path, - persistent=False) - with open(modelsim_ini, "r") as fptr: - self.assertEqual(fptr.read(), "installed") - - def test_copies_modelsim_ini_file_from_user(self): - modelsim_ini = join(self.output_path, "modelsim.ini") - installed_modelsim_ini = join(self.prefix_path, "..", "modelsim.ini") - user_modelsim_ini = join(self.test_path, "my_modelsim.ini") - - with open(installed_modelsim_ini, "w") as fptr: - fptr.write("installed") - - with open(user_modelsim_ini, "w") as fptr: - fptr.write("user") - - with set_env(VUNIT_MODELSIM_INI=user_modelsim_ini): - ModelSimInterface(prefix=self.prefix_path, - output_path=self.output_path, - persistent=False) - - with open(modelsim_ini, "r") as fptr: - self.assertEqual(fptr.read(), "user") - - def test_overwrites_modelsim_ini_file_from_install(self): - modelsim_ini = join(self.output_path, "modelsim.ini") - installed_modelsim_ini = join(self.prefix_path, "..", "modelsim.ini") - user_modelsim_ini = join(self.test_path, "my_modelsim.ini") - - with open(modelsim_ini, "w") as fptr: - fptr.write("existing") - - with open(installed_modelsim_ini, "w") as fptr: - fptr.write("installed") - - with open(user_modelsim_ini, "w") as fptr: - fptr.write("user") - - ModelSimInterface(prefix=self.prefix_path, - output_path=self.output_path, - persistent=False) - with open(modelsim_ini, "r") as fptr: - self.assertEqual(fptr.read(), "installed") - - def test_overwrites_modelsim_ini_file_from_user(self): - modelsim_ini = join(self.output_path, "modelsim.ini") - installed_modelsim_ini = join(self.prefix_path, "..", "modelsim.ini") - user_modelsim_ini = join(self.test_path, "my_modelsim.ini") - - with open(modelsim_ini, "w") as fptr: - fptr.write("existing") - - with open(installed_modelsim_ini, "w") as fptr: - fptr.write("installed") - - with open(user_modelsim_ini, "w") as fptr: - fptr.write("user") - - with set_env(VUNIT_MODELSIM_INI=user_modelsim_ini): - ModelSimInterface(prefix=self.prefix_path, - output_path=self.output_path, - persistent=False) - - with open(modelsim_ini, "r") as fptr: - self.assertEqual(fptr.read(), "user") - - def setUp(self): - self.test_path = join(dirname(__file__), "test_modelsim_out") - self.output_path = join(self.test_path, "modelsim") - self.prefix_path = join(self.test_path, "prefix", "bin") - renew_path(self.test_path) - renew_path(self.output_path) - renew_path(self.prefix_path) - installed_modelsim_ini = join(self.prefix_path, "..", "modelsim.ini") - write_file(installed_modelsim_ini, "[Library]") - self.project = Project() - self.cwd = os.getcwd() - os.chdir(self.test_path) - - def tearDown(self): - os.chdir(self.cwd) - if exists(self.test_path): - rmtree(self.test_path) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test the ModelSim interface +""" + + +import unittest +from os.path import join, dirname, exists +import os +from shutil import rmtree +from vunit.modelsim_interface import ModelSimInterface +from vunit.test.mock_2or3 import mock +from vunit.test.common import set_env +from vunit.project import Project +from vunit.ostools import renew_path, write_file + + +class TestModelSimInterface(unittest.TestCase): + """ + Test the ModelSim interface + """ + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.modelsim_interface.Process", autospec=True) + def test_compile_project_vhdl_2008(self, process, check_output): + simif = ModelSimInterface(prefix=self.prefix_path, + output_path=self.output_path, + persistent=False) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="2008") + simif.compile_project(project) + process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process.assert_called_once_with(process_args, env=simif.get_env()) + check_args = [join(self.prefix_path, 'vcom'), '-quiet', '-modelsimini', + join(self.output_path, "modelsim.ini"), '-2008', + '-work', 'lib', 'file.vhd'] + check_output.assert_called_once_with(check_args, env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.modelsim_interface.Process", autospec=True) + def test_compile_project_vhdl_2002(self, process, check_output): + simif = ModelSimInterface(prefix=self.prefix_path, + output_path=self.output_path, + persistent=False) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="2002") + simif.compile_project(project) + process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process.assert_called_once_with(process_args, env=simif.get_env()) + check_args = [join(self.prefix_path, 'vcom'), '-quiet', '-modelsimini', + join(self.output_path, "modelsim.ini"), '-2002', + '-work', 'lib', 'file.vhd'] + check_output.assert_called_once_with(check_args, env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.modelsim_interface.Process", autospec=True) + def test_compile_project_vhdl_93(self, process, check_output): + simif = ModelSimInterface(prefix=self.prefix_path, + output_path=self.output_path, + persistent=False) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard="93") + simif.compile_project(project) + process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process.assert_called_once_with(process_args, env=simif.get_env()) + check_args = [join(self.prefix_path, 'vcom'), '-quiet', '-modelsimini', + join(self.output_path, "modelsim.ini"), '-93', + '-work', 'lib', 'file.vhd'] + check_output.assert_called_once_with(check_args, env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.modelsim_interface.Process", autospec=True) + def test_compile_project_vhdl_extra_flags(self, process, check_output): + simif = ModelSimInterface(prefix=self.prefix_path, + output_path=self.output_path, + persistent=False) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + source_file = project.add_source_file("file.vhd", "lib", file_type="vhdl") + source_file.set_compile_option("modelsim.vcom_flags", ["custom", "flags"]) + simif.compile_project(project) + process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process.assert_called_once_with(process_args, env=simif.get_env()) + check_args = [join(self.prefix_path, 'vcom'), '-quiet', '-modelsimini', + join(self.output_path, "modelsim.ini"), 'custom', + 'flags', '-2008', '-work', 'lib', 'file.vhd'] + check_output.assert_called_once_with(check_args, env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.modelsim_interface.Process", autospec=True) + def test_compile_project_verilog(self, process, check_output): + simif = ModelSimInterface(prefix=self.prefix_path, + output_path=self.output_path, + persistent=False) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.v", "") + project.add_source_file("file.v", "lib", file_type="verilog") + simif.compile_project(project) + process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process.assert_called_once_with(process_args, env=simif.get_env()) + check_args = [join(self.prefix_path, 'vlog'), '-quiet', '-modelsimini', + join(self.output_path, "modelsim.ini"), '-work', 'lib', + 'file.v', '-L', 'lib'] + check_output.assert_called_once_with(check_args, env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.modelsim_interface.Process", autospec=True) + def test_compile_project_system_verilog(self, process, check_output): + simif = ModelSimInterface(prefix=self.prefix_path, + output_path=self.output_path, + persistent=False) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.sv", "") + project.add_source_file("file.sv", "lib", file_type="systemverilog") + simif.compile_project(project) + process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process.assert_called_once_with(process_args, env=simif.get_env()) + check_args = [join(self.prefix_path, 'vlog'), '-quiet', '-modelsimini', + join(self.output_path, "modelsim.ini"), '-sv', + '-work', 'lib', 'file.sv', '-L', 'lib'] + check_output.assert_called_once_with(check_args, env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.modelsim_interface.Process", autospec=True) + def test_compile_project_verilog_extra_flags(self, process, check_output): + simif = ModelSimInterface(prefix=self.prefix_path, + output_path=self.output_path, + persistent=False) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.v", "") + source_file = project.add_source_file("file.v", "lib", file_type="verilog") + source_file.set_compile_option("modelsim.vlog_flags", ["custom", "flags"]) + simif.compile_project(project) + process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process.assert_called_once_with(process_args, env=simif.get_env()) + check_args = [join(self.prefix_path, 'vlog'), '-quiet', '-modelsimini', + join(self.output_path, "modelsim.ini"), 'custom', 'flags', + '-work', 'lib', 'file.v', '-L', 'lib'] + check_output.assert_called_once_with(check_args, env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.modelsim_interface.Process", autospec=True) + def test_compile_project_verilog_include(self, process, check_output): + simif = ModelSimInterface(prefix=self.prefix_path, + output_path=self.output_path, + persistent=False) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.v", "") + project.add_source_file("file.v", "lib", file_type="verilog", include_dirs=["include"]) + simif.compile_project(project) + process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process.assert_called_once_with(process_args, env=simif.get_env()) + check_args = [join(self.prefix_path, 'vlog'), '-quiet', '-modelsimini', + join(self.output_path, "modelsim.ini"), '-work', 'lib', + 'file.v', '-L', 'lib', '+incdir+include'] + check_output.assert_called_once_with(check_args, env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.modelsim_interface.Process", autospec=True) + def test_compile_project_verilog_define(self, process, check_output): + simif = ModelSimInterface(prefix=self.prefix_path, + output_path=self.output_path, + persistent=False) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.v", "") + project.add_source_file("file.v", "lib", file_type="verilog", defines={"defname": "defval"}) + simif.compile_project(project) + process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process.assert_called_once_with(process_args, env=simif.get_env()) + process_args = [join(self.prefix_path, 'vlog'), '-quiet', '-modelsimini', + join(self.output_path, "modelsim.ini"), '-work', 'lib', + 'file.v', '-L', 'lib', '+define+defname=defval'] + check_output.assert_called_once_with(process_args, env=simif.get_env()) + + def test_copies_modelsim_ini_file_from_install(self): + modelsim_ini = join(self.output_path, "modelsim.ini") + installed_modelsim_ini = join(self.prefix_path, "..", "modelsim.ini") + user_modelsim_ini = join(self.test_path, "my_modelsim.ini") + + with open(installed_modelsim_ini, "w") as fptr: + fptr.write("installed") + + with open(user_modelsim_ini, "w") as fptr: + fptr.write("user") + + ModelSimInterface(prefix=self.prefix_path, + output_path=self.output_path, + persistent=False) + with open(modelsim_ini, "r") as fptr: + self.assertEqual(fptr.read(), "installed") + + def test_copies_modelsim_ini_file_from_user(self): + modelsim_ini = join(self.output_path, "modelsim.ini") + installed_modelsim_ini = join(self.prefix_path, "..", "modelsim.ini") + user_modelsim_ini = join(self.test_path, "my_modelsim.ini") + + with open(installed_modelsim_ini, "w") as fptr: + fptr.write("installed") + + with open(user_modelsim_ini, "w") as fptr: + fptr.write("user") + + with set_env(VUNIT_MODELSIM_INI=user_modelsim_ini): + ModelSimInterface(prefix=self.prefix_path, + output_path=self.output_path, + persistent=False) + + with open(modelsim_ini, "r") as fptr: + self.assertEqual(fptr.read(), "user") + + def test_overwrites_modelsim_ini_file_from_install(self): + modelsim_ini = join(self.output_path, "modelsim.ini") + installed_modelsim_ini = join(self.prefix_path, "..", "modelsim.ini") + user_modelsim_ini = join(self.test_path, "my_modelsim.ini") + + with open(modelsim_ini, "w") as fptr: + fptr.write("existing") + + with open(installed_modelsim_ini, "w") as fptr: + fptr.write("installed") + + with open(user_modelsim_ini, "w") as fptr: + fptr.write("user") + + ModelSimInterface(prefix=self.prefix_path, + output_path=self.output_path, + persistent=False) + with open(modelsim_ini, "r") as fptr: + self.assertEqual(fptr.read(), "installed") + + def test_overwrites_modelsim_ini_file_from_user(self): + modelsim_ini = join(self.output_path, "modelsim.ini") + installed_modelsim_ini = join(self.prefix_path, "..", "modelsim.ini") + user_modelsim_ini = join(self.test_path, "my_modelsim.ini") + + with open(modelsim_ini, "w") as fptr: + fptr.write("existing") + + with open(installed_modelsim_ini, "w") as fptr: + fptr.write("installed") + + with open(user_modelsim_ini, "w") as fptr: + fptr.write("user") + + with set_env(VUNIT_MODELSIM_INI=user_modelsim_ini): + ModelSimInterface(prefix=self.prefix_path, + output_path=self.output_path, + persistent=False) + + with open(modelsim_ini, "r") as fptr: + self.assertEqual(fptr.read(), "user") + + def setUp(self): + self.test_path = join(dirname(__file__), "test_modelsim_out") + self.output_path = join(self.test_path, "modelsim") + self.prefix_path = join(self.test_path, "prefix", "bin") + renew_path(self.test_path) + renew_path(self.output_path) + renew_path(self.prefix_path) + installed_modelsim_ini = join(self.prefix_path, "..", "modelsim.ini") + write_file(installed_modelsim_ini, "[Library]") + self.project = Project() + self.cwd = os.getcwd() + os.chdir(self.test_path) + + def tearDown(self): + os.chdir(self.cwd) + if exists(self.test_path): + rmtree(self.test_path) diff --git a/vunit/test/unit/test_ostools.py b/vunit/test/unit/test_ostools.py index f2c714355..9a9adacc0 100644 --- a/vunit/test/unit/test_ostools.py +++ b/vunit/test/unit/test_ostools.py @@ -1,95 +1,95 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test the os-dependent functionality wrappers -""" - - -from unittest import TestCase -from shutil import rmtree -from os.path import exists, dirname, join, abspath -import sys -from vunit.ostools import Process, renew_path - - -class TestOSTools(TestCase): - """ - Test the os-dependent functionality wrappers - """ - - def setUp(self): - self.tmp_dir = join(dirname(__file__), "test_ostools_tmp") - renew_path(self.tmp_dir) - - def tearDown(self): - if exists(self.tmp_dir): - rmtree(self.tmp_dir) - - def make_file(self, file_name, contents): - """ - Create a file in the temporary directory with contents - Returns the absolute path to the file. - """ - full_file_name = abspath(join(self.tmp_dir, file_name)) - with open(full_file_name, "w") as outfile: - outfile.write(contents) - return full_file_name - - def test_run_basic_subprocess(self): - python_script = self.make_file("run_basic.py", r""" -from sys import stdout -stdout.write("foo\n") -stdout.write("bar\n") -""") - - output = [] - process = Process([sys.executable, python_script]) - process.consume_output(output.append) - self.assertEqual(output, ["foo", "bar"]) - - def test_run_error_subprocess(self): - python_script = self.make_file("run_err.py", r""" -from sys import stdout -stdout.write("error\n") -exit(1) -""") - process = Process([sys.executable, python_script]) - output = [] - self.assertRaises(Process.NonZeroExitCode, - process.consume_output, output.append) - self.assertEqual(output, ["error"]) - - def test_parses_stderr(self): - python_script = self.make_file("run_err.py", r""" -from sys import stderr -stderr.write("error\n") -""") - process = Process([sys.executable, python_script]) - output = [] - process.consume_output(output.append) - self.assertEqual(output, ["error"]) - - def test_output_is_parallel(self): - python_script = self.make_file("run_timeout.py", r""" -from time import sleep -from sys import stdout -stdout.write("message\n") -stdout.flush() -sleep(1000) -""") - - process = Process([sys.executable, python_script]) - message = process.next_line() - process.terminate() - self.assertEqual(message, "message") - - def test_non_utf8_in_output(self): - python_script = join(dirname(__file__), "non_utf8_printer.py") - output = [] - process = Process([sys.executable, python_script]) - process.consume_output(output.append) - self.assertEqual(output, ["ac"]) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test the os-dependent functionality wrappers +""" + + +from unittest import TestCase +from shutil import rmtree +from os.path import exists, dirname, join, abspath +import sys +from vunit.ostools import Process, renew_path + + +class TestOSTools(TestCase): + """ + Test the os-dependent functionality wrappers + """ + + def setUp(self): + self.tmp_dir = join(dirname(__file__), "test_ostools_tmp") + renew_path(self.tmp_dir) + + def tearDown(self): + if exists(self.tmp_dir): + rmtree(self.tmp_dir) + + def make_file(self, file_name, contents): + """ + Create a file in the temporary directory with contents + Returns the absolute path to the file. + """ + full_file_name = abspath(join(self.tmp_dir, file_name)) + with open(full_file_name, "w") as outfile: + outfile.write(contents) + return full_file_name + + def test_run_basic_subprocess(self): + python_script = self.make_file("run_basic.py", r""" +from sys import stdout +stdout.write("foo\n") +stdout.write("bar\n") +""") + + output = [] + process = Process([sys.executable, python_script]) + process.consume_output(output.append) + self.assertEqual(output, ["foo", "bar"]) + + def test_run_error_subprocess(self): + python_script = self.make_file("run_err.py", r""" +from sys import stdout +stdout.write("error\n") +exit(1) +""") + process = Process([sys.executable, python_script]) + output = [] + self.assertRaises(Process.NonZeroExitCode, + process.consume_output, output.append) + self.assertEqual(output, ["error"]) + + def test_parses_stderr(self): + python_script = self.make_file("run_err.py", r""" +from sys import stderr +stderr.write("error\n") +""") + process = Process([sys.executable, python_script]) + output = [] + process.consume_output(output.append) + self.assertEqual(output, ["error"]) + + def test_output_is_parallel(self): + python_script = self.make_file("run_timeout.py", r""" +from time import sleep +from sys import stdout +stdout.write("message\n") +stdout.flush() +sleep(1000) +""") + + process = Process([sys.executable, python_script]) + message = process.next_line() + process.terminate() + self.assertEqual(message, "message") + + def test_non_utf8_in_output(self): + python_script = join(dirname(__file__), "non_utf8_printer.py") + output = [] + process = Process([sys.executable, python_script]) + process.consume_output(output.append) + self.assertEqual(output, ["ac"]) diff --git a/vunit/test/unit/test_project.py b/vunit/test/unit/test_project.py index 1945dd7a2..ddc06b799 100644 --- a/vunit/test/unit/test_project.py +++ b/vunit/test/unit/test_project.py @@ -1,1572 +1,1572 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -# pylint: disable=too-many-lines - -""" -Test the project functionality -""" - - -import unittest -from shutil import rmtree -from os.path import join, exists, dirname -import os -from time import sleep -import itertools -from vunit.test.mock_2or3 import mock -from vunit.exceptions import CompileError -from vunit.ostools import renew_path, write_file -from vunit.project import Project, file_type_of - - -class TestProject(unittest.TestCase): # pylint: disable=too-many-public-methods - """ - Test the Project class - """ - - def setUp(self): - self.output_path = join(dirname(__file__), "test_project_out") - renew_path(self.output_path) - self.project = Project() - self.cwd = os.getcwd() - os.chdir(self.output_path) - - def tearDown(self): - os.chdir(self.cwd) - if exists(self.output_path): - rmtree(self.output_path) - - def test_parses_entity_architecture(self): - self.project.add_library("lib", "work_path") - - # Add architecture before entity to test that they are paired later - self.add_source_file("lib", "file2.vhd", """\ -architecture arch3 of foo is -begin -end architecture; -""") - - file1 = self.add_source_file("lib", "file1.vhd", """\ -entity foo is -end entity; - -architecture arch of foo is -begin -end architecture; - -architecture arch2 of foo is -begin -end architecture; -""") - - self.assert_has_entity(file1, "foo", - architecture_names=["arch", "arch2", "arch3"]) - self.add_source_file("lib", "file3.vhd", """\ -architecture arch4 of foo is -begin -end architecture; -""") - - self.assert_has_entity(file1, "foo", - architecture_names=["arch", "arch2", "arch3", "arch4"]) - self.assert_has_architecture("file1.vhd", "arch", "foo") - self.assert_has_architecture("file1.vhd", "arch2", "foo") - self.assert_has_architecture("file2.vhd", "arch3", "foo") - self.assert_has_architecture("file3.vhd", "arch4", "foo") - - def test_parses_entity_architecture_with_generics(self): - self.project.add_library("lib", "work_path") - file1 = self.add_source_file("lib", "file1.vhd", """\ -entity foo is - generic ( - testing_that_foo : boolean; - testing_that_bar : boolean); -end entity; - -architecture arch of foo is -begin -end architecture; -""") - - self.assert_has_entity(file1, "foo", - generic_names=["testing_that_bar", "testing_that_foo"], - architecture_names=["arch"]) - self.assert_has_architecture("file1.vhd", "arch", "foo") - - def test_parses_package(self): - self.project.add_library("lib", "work_path") - self.add_source_file("lib", "file1.vhd", """\ -package foo is -end package; - -package body foo is -begin -end package body; -""") - self.assert_has_package("file1.vhd", "foo") - self.assert_has_package_body("file1.vhd", "foo") - - @mock.patch("vunit.project.LOGGER") - def test_recovers_from_parse_error(self, logger): - self.project.add_library("lib", "work_path") - source_file = self.add_source_file("lib", "file.vhd", """\ -entity foo is - port (;); -end entity; -""") - logger.error.assert_called_once_with("Failed to parse %s", "file.vhd") - self.assertEqual(source_file.design_units, []) - - def test_finds_entity_instantiation_dependencies(self): - file1, file2, file3 = self.create_dummy_three_file_project() - - self.assert_compiles(file1, before=file2) - self.assert_compiles(file2, before=file3) - - def test_primary_with_same_name_in_multiple_libraries_secondary_dependency(self): - self.project.add_library("lib1", "lib1_path") - self.project.add_library("lib2", "lib2_path") - - foo_arch = self.add_source_file("lib1", "foo_arch.vhd", """ -architecture arch of foo is -begin -end architecture; -""") - - foo1_ent = self.add_source_file("lib1", "foo1_ent.vhd", """ -entity foo is -port (signal bar : boolean); -end entity; -""") - - self.add_source_file("lib2", "foo2_ent.vhd", """ -entity foo is -end entity; -""") - - self.assert_compiles(foo1_ent, before=foo_arch) - - def test_multiple_identical_file_names_with_different_path_in_same_library(self): - self.project.add_library("lib", "lib_path") - a_foo = self.add_source_file("lib", join("a", "foo.vhd"), """ -entity a_foo is -end entity; -""") - - b_foo = self.add_source_file("lib", join("b", "foo.vhd"), """ -entity b_foo is -end entity; -""") - self.assert_should_recompile([a_foo, b_foo]) - self.update(a_foo) - self.assert_should_recompile([b_foo]) - self.update(b_foo) - self.assert_should_recompile([]) - - def test_finds_entity_architecture_dependencies(self): - self.project.add_library("lib", "lib_path") - entity = self.add_source_file("lib", "entity.vhd", """ -entity foo is -end entity; -""") - - arch1 = self.add_source_file("lib", "arch1.vhd", """ -architecture arch1 of foo is -begin -end architecture; -""") - - arch2 = self.add_source_file("lib", "arch2.vhd", """ -architecture arch2 of foo is -begin -end architecture; -""") - self.assert_compiles(entity, before=arch1) - self.assert_compiles(entity, before=arch2) - - def test_finds_package_dependencies(self): - self.project.add_library("lib", "lib_path") - package = self.add_source_file("lib", "package.vhd", """ -package foo is -end package; -""") - - body = self.add_source_file("lib", "body.vhd", """ -package body foo is -begin -end package body; -""") - - self.assert_compiles(package, before=body) - - def create_module_package_and_body(self, add_body=True): - """ - Help function to create a three file project - with a package, a package body and a module using the package - """ - self.project.add_library("lib", "lib_path") - - package = self.add_source_file("lib", "package.vhd", """ -package pkg is -end package; -""") - - body = None - if add_body: - body = self.add_source_file("lib", "body.vhd", """ -package body pkg is -begin -end package body; -""") - - self.project.add_library("lib2", "work_path") - module = self.add_source_file("lib2", "module.vhd", """ -library lib; -use lib.pkg.all; - -entity module is -end entity; - -architecture arch of module is -begin -end architecture; -""") - return package, body, module - - def test_finds_use_package_dependencies_case_insensitive(self): - for library_clause, use_clause in itertools.combinations(("lib", "Lib"), 2): - self.project = Project() - self.project.add_library("Lib", "lib_path") - - package = self.add_source_file("Lib", "package.vhd", """ -package pkg is -end package; - -package body PKG is -begin -end package body; -""") - - self.project.add_library("lib2", "lib2_path") - module = self.add_source_file("lib2", "module.vhd", """ -library {library_clause}; -use {use_clause}.PKG.all; - """.format(library_clause=library_clause, use_clause=use_clause)) - self.assert_compiles(package, before=module) - - def test_error_on_case_insensitive_library_name_conflict(self): - self.project.add_library("Lib", "lib_path1") - try: - self.project.add_library("lib", "lib_path1") - except RuntimeError as exception: - self.assertEqual(str(exception), - "Library name 'lib' not case-insensitive unique. " - "Library name 'Lib' previously defined") - else: - raise AssertionError("RuntimeError not raised") - - def test_finds_use_package_dependencies(self): - package, body, module = self.create_module_package_and_body() - self.assert_compiles(package, before=body) - self.assert_compiles(package, before=module) - self.assert_not_compiles(body, before=module) - - def test_finds_extra_package_body_dependencies(self): - self.project = Project(depend_on_package_body=True) - package, body, module = self.create_module_package_and_body() - self.assert_compiles(package, before=body) - self.assert_compiles(body, before=module) - self.assert_compiles(package, before=module) - - def test_that_package_can_have_no_body(self): - self.project = Project(depend_on_package_body=True) - package, _, module = self.create_module_package_and_body(add_body=False) - self.assert_compiles(package, before=module) - - def test_package_instantiation_dependencies_on_generic_package(self): - self.project.add_library("pkg_lib", "pkg_lib_path") - pkg = self.add_source_file("pkg_lib", "pkg.vhd", """ -package pkg is -end package; - """) - - self.project.add_library("lib", "lib_path") - ent = self.add_source_file("lib", "ent.vhd", """ -library pkg_lib; - -entity ent is -end entity; - -architecture a of ent is - package pkg_inst is new pkg_lib.pkg; -begin -end architecture; -""") - - self.assert_compiles(pkg, before=ent) - - def test_package_instantiation_dependencies_on_instantiated_package(self): - self.project.add_library("lib", "lib_path") - generic_pkg = self.add_source_file("lib", "generic_pkg.vhd", """ -package generic_pkg is - generic (foo : boolean); -end package; -""") - instance_pkg = self.add_source_file("lib", "instance_pkg.vhd", """ -package instance_pkg is new work.generic_pkg - generic map (foo => false); -""") - user = self.add_source_file("lib", "user.vhd", """ -use work.instance_pkg; -""") - self.assert_compiles(generic_pkg, before=instance_pkg) - self.assert_compiles(instance_pkg, before=user) - - def test_finds_context_dependencies(self): - self.project.add_library("lib", "lib_path") - context = self.add_source_file("lib", "context.vhd", """ -context foo is -end context; -""") - - self.project.add_library("lib2", "work_path") - module = self.add_source_file("lib2", "module.vhd", """ -library lib; -context lib.foo; - -entity module is -end entity; - -architecture arch of module is -begin -end architecture; -""") - - self.assert_compiles(context, before=module) - - def test_finds_configuration_dependencies(self): - self.project.add_library("lib", "lib_path") - cfg = self.add_source_file("lib", "cfg.vhd", """ -configuration cfg of ent is -end configuration; -""") - - ent = self.add_source_file("lib", "ent.vhd", """ -entity ent is -end entity; -""") - - ent_a1 = self.add_source_file("lib", "ent_a1.vhd", """ -architecture a1 of ent is -begin -end architecture; -""") - - ent_a2 = self.add_source_file("lib", "ent_a2.vhd", """ -architecture a2 of ent is -begin -end architecture; -""") - - self.assert_compiles(ent, before=cfg) - self.assert_compiles(ent_a1, before=cfg) - self.assert_compiles(ent_a2, before=cfg) - - def test_finds_configuration_reference_dependencies(self): - self.project.add_library("lib", "lib_path") - cfg = self.add_source_file("lib", "cfg.vhd", """ -configuration cfg of ent is -end configuration; -""") - - self.add_source_file("lib", "ent.vhd", """ -entity ent is -end entity; -""") - - self.add_source_file("lib", "ent_a.vhd", """ -architecture a of ent is -begin -end architecture; -""") - - top = self.add_source_file("lib", "top.vhd", """ -entity top is -end entity; - -architecture a of top is - for inst : comp use configuration work.cfg; -begin - inst : comp; -end architecture; -""") - - self.assert_compiles(cfg, before=top) - - def test_specific_architecture_reference_dependencies(self): - """ - GHDL dependes also on architecture when specificially mentioned - """ - self.project.add_library("lib", "lib_path") - - self.add_source_file("lib", "ent.vhd", """ -entity ent is -end entity; -""") - - ent_a1 = self.add_source_file("lib", "ent_a1.vhd", """ -architecture a1 of ent is -begin -end architecture; -""") - - ent_a2 = self.add_source_file("lib", "ent_a2.vhd", """ -architecture a2 of ent is -begin -end architecture; -""") - - top1 = self.add_source_file("lib", "top1.vhd", """ -entity top1 is -end entity; - -architecture a of top1 is -begin - inst : entity work.ent(a1); -end architecture; -""") - - top2 = self.add_source_file("lib", "top2.vhd", """ -entity top2 is -end entity; - -architecture a of top2 is - for inst : comp use entity work.ent(a2); -begin - inst : comp; -end architecture; -""") - - self.assert_compiles(ent_a1, before=top1) - self.assert_compiles(ent_a2, before=top2) - - @mock.patch("vunit.project.LOGGER") - def test_warning_on_missing_specific_architecture_reference(self, mock_logger): - self.project.add_library("lib", "lib_path") - - self.add_source_file("lib", "ent.vhd", """ -entity ent is -end entity; -""") - - self.add_source_file("lib", "arch.vhd", """ -architecture a1 of ent is -begin -end architecture; -""") - - self.add_source_file("lib", "top.vhd", """ -entity top1 is -end entity; - -architecture a of top1 is -begin - inst1 : entity work.ent(a1); - inst2 : entity work.ent(a2); # Missing -end architecture; -""") - - self.project.get_files_in_compile_order() - warning_calls = mock_logger.warning.call_args_list - log_msg = warning_calls[0][0][0] % warning_calls[0][0][1:] - self.assertEqual(len(warning_calls), 1) - self.assertIn("top.vhd", log_msg) - self.assertIn("a2", log_msg) - self.assertIn("lib.ent", log_msg) - - @mock.patch("vunit.project.LOGGER") - def test_error_on_duplicate_file(self, logger): - self.project.add_library("lib", "lib_path") - file1 = self.add_source_file("lib", "file.vhd", "entity foo is end entity;") - self.assertRaises(RuntimeError, self.add_source_file, "lib", "file.vhd", "entity foo is end entity foo;") - - # No extra design unit of file added - lib = self.project.get_library("lib") - self.assertEqual([ent.name for ent in lib.get_entities()], ["foo"]) - self.assertEqual(lib.get_source_file("file.vhd"), file1) - self.assertEqual(self.project.get_source_files_in_order(), [file1]) - self.assertFalse(logger.warning.called) - - @mock.patch("vunit.project.LOGGER") - def test_no_error_on_duplicate_identical_file(self, logger): - self.project.add_library("lib", "lib_path") - file1 = self.add_source_file("lib", "file.vhd", "entity foo is end entity;") - file2 = self.add_source_file("lib", "file.vhd", "entity foo is end entity;") - self.assertEqual(id(file1), id(file2)) - - # No extra design unit of file added - lib = self.project.get_library("lib") - self.assertEqual([ent.name for ent in lib.get_entities()], ["foo"]) - self.assertEqual(lib.get_source_file("file.vhd"), file1) - self.assertEqual(self.project.get_source_files_in_order(), [file1]) - self.assertTrue(logger.info.called) - - def _test_warning_on_duplicate(self, code, message, verilog=False): - """ - Utility function to test adding the same duplicate code under - file.vhd and file_copy.vhd where the duplication should cause a warning message. - """ - suffix = "v" if verilog else "vhd" - - self.add_source_file("lib", "file." + suffix, code) - - with mock.patch("vunit.project.LOGGER") as mock_logger: - self.add_source_file("lib", "file_copy." + suffix, code) - warning_calls = mock_logger.warning.call_args_list - log_msg = warning_calls[0][0][0] % warning_calls[0][0][1:] - self.assertEqual(len(warning_calls), 1) - self.assertEqual(log_msg, message) - - def test_warning_on_duplicate_entity(self): - self.project.add_library("lib", "lib_path") - self._test_warning_on_duplicate( - """ -entity ent is -end entity; -""", - "file_copy.vhd: entity 'ent' previously defined in file.vhd") - - def test_warning_on_duplicate_package(self): - self.project.add_library("lib", "lib_path") - self._test_warning_on_duplicate( - """ -package pkg is -end package; -""", - "file_copy.vhd: package 'pkg' previously defined in file.vhd") - - def test_warning_on_duplicate_configuration(self): - self.project.add_library("lib", "lib_path") - self._test_warning_on_duplicate( - """ -configuration cfg of ent is -end configuration; -""", - "file_copy.vhd: configuration 'cfg' previously defined in file.vhd") - - def test_warning_on_duplicate_package_body(self): - self.project.add_library("lib", "lib_path") - self.add_source_file("lib", "pkg.vhd", """ -package pkg is -end package; -""") - - self._test_warning_on_duplicate( - """ -package body pkg is -end package bodY; -""", - "file_copy.vhd: package body 'pkg' previously defined in file.vhd") - - def test_warning_on_duplicate_architecture(self): - self.project.add_library("lib", "lib_path") - self.add_source_file("lib", "ent.vhd", """ -entity ent is -end entity; -""") - - self.add_source_file("lib", "arch.vhd", """ -architecture a_no_duplicate of ent is -begin -end architecture; -""") - - self._test_warning_on_duplicate( - """ -architecture a of ent is -begin -end architecture; -""", - "file_copy.vhd: architecture 'a' previously defined in file.vhd") - - def test_warning_on_duplicate_context(self): - self.project.add_library("lib", "lib_path") - self._test_warning_on_duplicate( - """ -context ctx is -end context; -""", - "file_copy.vhd: context 'ctx' previously defined in file.vhd") - - def test_error_on_adding_duplicate_library(self): - self.project.add_library(logical_name="lib", directory="dir") - self.assertRaises(ValueError, self.project.add_library, - logical_name="lib", directory="dir") - - def test_warning_on_duplicate_verilog_module(self): - self.project.add_library("lib", "lib_path") - self._test_warning_on_duplicate( - """ -module foo; -endmodule -""", - "file_copy.v: module 'foo' previously defined in file.v", - verilog=True) - - def test_warning_on_duplicate_verilog_package(self): - self.project.add_library("lib", "lib_path") - self._test_warning_on_duplicate( - """ -package pkg; -endpackage -""", - "file_copy.v: package 'pkg' previously defined in file.v", - verilog=True) - - def test_should_recompile_all_files_initially(self): - file1, file2, file3 = self.create_dummy_three_file_project() - self.assert_should_recompile([file1, file2, file3]) - self.assert_should_recompile([file1, file2, file3]) - - def test_updating_creates_hash_files(self): - files = self.create_dummy_three_file_project() - - for source_file in files: - self.update(source_file) - self.assertTrue(exists(self.hash_file_name_of(source_file))) - - def test_should_not_recompile_updated_files(self): - file1, file2, file3 = self.create_dummy_three_file_project() - - self.update(file1) - self.assert_should_recompile([file2, file3]) - - self.update(file2) - self.assert_should_recompile([file3]) - - self.update(file3) - self.assert_should_recompile([]) - - def test_should_recompile_files_affected_by_change(self): - file1, file2, file3 = self.create_dummy_three_file_project() - - self.update(file1) - self.update(file2) - self.update(file3) - self.assert_should_recompile([]) - - file1, file2, file3 = self.create_dummy_three_file_project() - self.assert_should_recompile([]) - - file1, file2, file3 = self.create_dummy_three_file_project(update_file1=True) - self.assert_should_recompile([file1, file2, file3]) - - def test_should_recompile_files_after_changing_compile_options(self): - file1, file2, file3 = self.create_dummy_three_file_project() - - self.update(file1) - self.update(file2) - self.update(file3) - self.assert_should_recompile([]) - - file2.set_compile_option("ghdl.flags", ["--no-vital-checks"]) - self.assert_should_recompile([file2, file3]) - - def test_should_recompile_files_after_changing_vhdl_standard(self): - write_file("file_name.vhd", "") - - self.project = Project() - self.project.add_library("lib", "lib_path") - source_file = self.project.add_source_file("file_name.vhd", library_name="lib", vhdl_standard='2008') - self.assert_should_recompile([source_file]) - self.update(source_file) - self.assert_should_recompile([]) - - self.project = Project() - self.project.add_library("lib", "lib_path") - source_file = self.project.add_source_file("file_name.vhd", library_name="lib", vhdl_standard='2002') - self.assert_should_recompile([source_file]) - - def test_add_compile_option(self): - self.project.add_library("lib", "lib_path") - file1 = self.add_source_file("lib", "file.vhd", "") - file1.add_compile_option("ghdl.flags", ["--foo"]) - self.assertEqual(file1.get_compile_option("ghdl.flags"), ["--foo"]) - file1.add_compile_option("ghdl.flags", ["--bar"]) - self.assertEqual(file1.get_compile_option("ghdl.flags"), ["--foo", "--bar"]) - file1.set_compile_option("ghdl.flags", ["--xyz"]) - self.assertEqual(file1.get_compile_option("ghdl.flags"), ["--xyz"]) - - def test_add_compile_option_does_not_mutate_argument(self): - self.project.add_library("lib", "lib_path") - file1 = self.add_source_file("lib", "file.vhd", "") - options = ["--foo"] - file1.add_compile_option("ghdl.flags", options) - options[0] = "--xyz" - self.assertEqual(file1.get_compile_option("ghdl.flags"), ["--foo"]) - file1.add_compile_option("ghdl.flags", ["--bar"]) - self.assertEqual(options, ["--xyz"]) - - def test_set_compile_option_does_not_mutate_argument(self): - self.project.add_library("lib", "lib_path") - file1 = self.add_source_file("lib", "file.vhd", "") - options = ["--foo"] - file1.set_compile_option("ghdl.flags", options) - options[0] = "--xyz" - self.assertEqual(file1.get_compile_option("ghdl.flags"), ["--foo"]) - - def test_compile_option_validation(self): - self.project.add_library("lib", "lib_path") - source_file = self.add_source_file("lib", "file.vhd", "") - self.assertRaises(ValueError, source_file.set_compile_option, "foo", None) - self.assertRaises(ValueError, source_file.set_compile_option, "ghdl.flags", None) - self.assertRaises(ValueError, source_file.add_compile_option, "ghdl.flags", None) - self.assertRaises(ValueError, source_file.get_compile_option, "foo") - - def test_should_recompile_files_affected_by_change_with_later_timestamp(self): - file1, file2, file3 = self.create_dummy_three_file_project() - - self.update(file1) - self.update(file2) - self.update(file3) - self.assert_should_recompile([]) - - file1, file2, file3 = self.create_dummy_three_file_project() - self.assert_should_recompile([]) - - file1, file2, file3 = self.create_dummy_three_file_project(update_file1=True) - self.assert_should_recompile([file1, file2, file3]) - - tick() - self.update(file1) - self.assert_should_recompile([file2, file3]) - - def test_should_recompile_files_missing_hash(self): - file1, file2, file3 = self.create_dummy_three_file_project() - - self.update(file1) - self.update(file2) - self.update(file3) - self.assert_should_recompile([]) - - os.remove(self.hash_file_name_of(file2)) - self.assert_should_recompile([file2, file3]) - - def test_finds_component_instantiation_dependencies(self): - self.project.add_library("toplib", "work_path") - top = self.add_source_file("toplib", "top.vhd", """\ -entity top is -end entity; - -architecture arch of top is -begin - labelFoo : component foo - generic map(WIDTH => 16) - port map(clk => '1', - rst => '0', - in_vec => record_reg.input_signal, - output => some_signal(UPPER_CONSTANT-1 downto LOWER_CONSTANT+1)); - - label2Foo : foo2 - port map(clk => '1', - rst => '0', - output => "00"); -end architecture; -""") - - comp1 = self.add_source_file("toplib", "comp1.vhd", """\ -entity foo is -end entity; -""") - - comp2 = self.add_source_file("toplib", "comp2.vhd", """\ -entity foo2 is -end entity; - -architecture arch of foo2 is -begin -end architecture; -""") - comp1_arch = self.add_source_file("toplib", "comp1_arch.vhd", """\ -architecture arch of foo is -begin -end architecture; -""") - - self.assert_has_component_instantiation("top.vhd", "foo") - self.assert_has_component_instantiation("top.vhd", "foo2") - dependencies = self.project.get_dependencies_in_compile_order([top], implementation_dependencies=True) - self.assertIn(comp1, dependencies) - self.assertIn(comp1_arch, dependencies) - self.assertIn(comp2, dependencies) - - def test_get_dependencies_in_compile_order_without_target(self): - self.create_dummy_three_file_project() - deps = self.project.get_dependencies_in_compile_order() - self.assertEqual(len(deps), 3) - self.assertTrue(deps[0] == self.project.get_source_files_in_order()[0]) - self.assertTrue(deps[1] == self.project.get_source_files_in_order()[1]) - self.assertTrue(deps[2] == self.project.get_source_files_in_order()[2]) - - def test_get_dependencies_in_compile_order_with_target(self): - self.create_dummy_three_file_project() - deps = self.project.get_dependencies_in_compile_order( - target_files=[self.project.get_source_files_in_order()[1]]) - self.assertEqual(len(deps), 2) - self.assertTrue(deps[0] == self.project.get_source_files_in_order()[0]) - self.assertTrue(deps[1] == self.project.get_source_files_in_order()[1]) - - # To test that indirect dependencies are included - deps = self.project.get_dependencies_in_compile_order( - target_files=[self.project.get_source_files_in_order()[2]]) - self.assertEqual(len(deps), 3) - self.assertTrue(deps[0] == self.project.get_source_files_in_order()[0]) - self.assertTrue(deps[1] == self.project.get_source_files_in_order()[1]) - self.assertTrue(deps[2] == self.project.get_source_files_in_order()[2]) - - def test_compiles_same_file_into_different_libraries(self): - pkgs = [] - second_pkgs = [] - self.project.add_library("lib", "lib_path") - - other_pkg = self.add_source_file("lib", "other_pkg.vhd", """ -package other_pkg is -end package other_pkg; -""") - - for lib in ["lib1", "lib2"]: - self.project.add_library(lib, lib + "_path") - pkgs.append(self.add_source_file(lib, "pkg.vhd", """ -library lib; -use lib.other_pkg.all; - -package pkg is -end package pkg; -""")) - - second_pkgs.append(self.add_source_file(lib, lib + "_pkg.vhd", """ -use work.pkg.all; - -package second_pkg is -end package second_pkg; -""")) - - self.assertNotEqual(self.hash_file_name_of(pkgs[0]), - self.hash_file_name_of(pkgs[1])) - self.assertEqual(len(self.project.get_files_in_compile_order()), 5) - self.assert_compiles(other_pkg, before=pkgs[0]) - self.assert_compiles(other_pkg, before=pkgs[1]) - self.assert_compiles(pkgs[0], before=second_pkgs[0]) - self.assert_compiles(pkgs[1], before=second_pkgs[1]) - - def test_has_verilog_module(self): - self.project.add_library("lib", "lib_path") - self.add_source_file("lib", "module.v", """\ -module name; -endmodule -""") - library = self.project.get_library("lib") - modules = library.get_modules() - self.assertEqual(len(modules), 1) - - def test_finds_verilog_package_import_dependencies(self): - self.project.add_library("lib", "lib_path") - pkg = self.add_source_file("lib", "pkg.sv", """\ -package pkg; -endpackage -""") - module = self.add_source_file("lib", "module.sv", """\ -module name; - import pkg::*; -endmodule -""") - self.assert_compiles(pkg, before=module) - - def test_finds_verilog_package_reference_dependencies(self): - self.project.add_library("lib", "lib_path") - pkg = self.add_source_file("lib", "pkg.sv", """\ -package pkg; -endpackage -""") - module = self.add_source_file("lib", "module.sv", """\ -module name; - pkg::func(); -endmodule -""") - self.assert_compiles(pkg, before=module) - - def test_verilog_package_reference_is_case_sensitive(self): - self.project = Project() - self.project.add_library("lib", "lib_path") - pkg = self.add_source_file("lib", "pkg.sv", """\ -package Pkg; -endpackage -""") - module = self.add_source_file("lib", "module.sv", """\ -module name; - pkg::func(); -endmodule -""") - self.assert_not_compiles(pkg, before=module) - - self.project = Project() - self.project.add_library("lib", "lib_path") - pkg = self.add_source_file("lib", "pkg.sv", """\ -package pkg; -endpackage -""") - module = self.add_source_file("lib", "module.sv", """\ -module name; - Pkg::func(); -endmodule -""") - self.assert_not_compiles(pkg, before=module) - - def test_finds_verilog_module_instantiation_dependencies(self): - self.project.add_library("lib", "lib_path") - module1 = self.add_source_file("lib", "module1.sv", """\ -module module1; -endmodule -""") - module2 = self.add_source_file("lib", "module2.sv", """\ -module module2; - module1 inst(); -endmodule -""") - self.assert_compiles(module1, before=module2) - - def test_verilog_module_instantiation_is_case_sensitive(self): - self.project = Project() - self.project.add_library("lib", "lib_path") - module1 = self.add_source_file("lib", "module1.sv", """\ -module Module1; -endmodule -""") - module2 = self.add_source_file("lib", "module2.sv", """\ -module module2; - module1 inst(); -endmodule -""") - self.assert_not_compiles(module1, before=module2) - - self.project = Project() - self.project.add_library("lib", "lib_path") - module1 = self.add_source_file("lib", "module1.sv", """\ -module module1; -endmodule -""") - module2 = self.add_source_file("lib", "module2.sv", """\ -module module2; - Module1 inst(); -endmodule -""") - self.assert_not_compiles(module1, before=module2) - - def test_finds_verilog_module_instantiation_dependencies_in_vhdl(self): - self.project.add_library("lib1", "lib_path") - self.project.add_library("lib2", "lib_path") - module1 = self.add_source_file("lib1", "module1.sv", """\ -module module1; -endmodule -""") - module2 = self.add_source_file("lib2", "module2.vhd", """\ -library lib1; - -entity ent is -end entity; - -architecture a of ent is -begin - inst : entity lib1.module1; -end architecture; -""") - self.assert_compiles(module1, before=module2) - - def test_finds_verilog_include_dependencies(self): - def create_project(): - """ - Create the test project - """ - self.project = Project() - self.project.add_library("lib", "lib_path") - return self.add_source_file("lib", "module.sv", """\ -`include "include.svh" -""") - - write_file("include.svh", """\ -module name; -endmodule -""") - module = create_project() - self.assert_should_recompile([module]) - - for src_file in self.project.get_files_in_compile_order(): - self.update(src_file) - create_project() - self.assert_should_recompile([]) - - write_file("include.svh", """\ -module other_name; -endmodule -""") - module = create_project() - self.assert_should_recompile([module]) - - def test_verilog_defines_affects_dependency_scanning(self): - self.project.add_library("lib", "lib_path") - self.add_source_file("lib", "module.v", """\ -`ifdef foo -module mod; -endmodule -`endif -""") - library = self.project.get_library("lib") - modules = library.get_modules() - self.assertEqual(len(modules), 0) - - self.project = Project() - self.project.add_library("lib", "lib_path") - self.add_source_file("lib", "module.v", """\ -`ifdef foo -module mod; -endmodule -`endif -""", defines={"foo": ""}) - library = self.project.get_library("lib") - modules = library.get_modules() - self.assertEqual(len(modules), 1) - - def test_recompile_when_updating_defines(self): - contents1 = """ -module mod1; -endmodule -""" - contents2 = """ -module mod2; -endmodule -""" - self.project = Project() - self.project.add_library("lib", "lib_path") - mod1 = self.add_source_file("lib", "module1.v", contents1) - mod2 = self.add_source_file("lib", "module2.v", contents2) - self.assert_should_recompile([mod1, mod2]) - self.update(mod1) - self.update(mod2) - self.assert_should_recompile([]) - - self.project = Project() - self.project.add_library("lib", "lib_path") - mod1 = self.add_source_file("lib", "module1.v", contents1, - defines={"foo": "bar"}) - mod2 = self.add_source_file("lib", "module2.v", contents2) - self.assert_should_recompile([mod1]) - self.update(mod1) - self.update(mod2) - self.assert_should_recompile([]) - - self.project = Project() - self.project.add_library("lib", "lib_path") - mod1 = self.add_source_file("lib", "module1.v", contents1, - defines={"foo": "other_bar"}) - mod2 = self.add_source_file("lib", "module2.v", contents2) - self.assert_should_recompile([mod1]) - self.update(mod1) - self.update(mod2) - self.assert_should_recompile([]) - - def test_manual_dependencies(self): - self.project.add_library("lib", "lib_path") - ent1 = self.add_source_file("lib", "ent1.vhd", """\ -entity ent1 is -end ent1; - -architecture arch of ent1 is -begin -end architecture; -""") - - ent2 = self.add_source_file("lib", "ent2.vhd", """\ -entity ent2 is -end ent2; - -architecture arch of ent2 is -begin -end architecture; -""") - - self.project.add_manual_dependency(ent2, depends_on=ent1) - self.assert_compiles(ent1, before=ent2) - - @mock.patch("vunit.project.LOGGER", autospec=True) - def test_circular_dependencies_causes_error(self, logger): - self.project.add_library("lib", "lib_path") - self.add_source_file("lib", "ent1.vhd", """\ -entity ent1 is -end ent1; - -architecture arch of ent1 is -begin - ent2_inst : entity work.ent2; -end architecture; -""") - - self.add_source_file("lib", "ent2.vhd", """\ -entity ent2 is -end ent2; - -architecture arch of ent2 is -begin - ent1_inst : entity work.ent1; -end architecture; -""") - - self.assertRaises(CompileError, self.project.get_files_in_compile_order) - logger.error.assert_called_once_with( - "Found circular dependency:\n%s", - "ent1.vhd ->\n" - "ent2.vhd ->\n" - "ent1.vhd") - - def test_order_of_adding_libraries_is_kept(self): - for order in itertools.combinations(range(4), 4): - project = Project() - for idx in order: - project.add_library("lib%i" % idx, "lib%i_path" % idx) - - library_names = [lib.name for lib in project.get_libraries()] - self.assertEqual(library_names, ["lib%i" % idx for idx in order]) - - def test_file_type_of(self): - self.assertEqual(file_type_of("file.vhd"), "vhdl") - self.assertEqual(file_type_of("file.vhdl"), "vhdl") - self.assertEqual(file_type_of("file.sv"), "systemverilog") - self.assertEqual(file_type_of("file.v"), "verilog") - self.assertEqual(file_type_of("file.vams"), "verilog") - self.assertRaises(RuntimeError, file_type_of, "file.foo") - - def test_circular_dependencies_through_libraries(self): - """ - Create a projected containing two identical files in two separated - library and instantiate an entity from the first library. - """ - self.project = Project() - self.project.add_library("lib_1", "work_path") - self.project.add_library("lib_2", "work_path") - self.project.add_library("lib", "work_path") - text_file_1_2 = """\ - library ieee; - use ieee.std_logic_1164.all; - - entity buffer1 is - port (Q : out std_logic); - end entity; - - architecture arch of buffer1 is begin - Q <= '1'; - end architecture; - - library ieee; - use ieee.std_logic_1164.all; - - entity buffer2 is - port (Q : out std_logic); - end entity; - - architecture arch of buffer2 is - component buffer1 - port (Q : out std_logic); - end component buffer1; - - begin - my_buffer_i : buffer1 - port map (Q => Q); - end architecture; - """ - self.add_source_file("lib_1", "file1.vhd", text_file_1_2) - self.add_source_file("lib_2", "file2.vhd", text_file_1_2) - file3 = self.add_source_file("lib", "file3.vhd", """\ - library lib_1; - - entity your_buffer is - end entity; - - architecture arch of your_buffer is - begin - my_buffer_i : entity lib_1.buffer1; - end architecture; - """) - self.project.get_dependencies_in_compile_order([file3], implementation_dependencies=True) - - def test_dependencies_on_multiple_libraries(self): - """ - Create a projected containing two identical files in two separated - library and instantiate an entity from the first library. - """ - self.project = Project() - self.project.add_library("lib_1", "work_path") - self.project.add_library("lib_2", "work_path") - self.project.add_library("lib", "work_path") - text_file_1_2 = """\ -library ieee;use ieee.std_logic_1164.all; -entity buffer1 is port (D : in std_logic;Q : out std_logic);end entity; -architecture arch of buffer1 is begin Q <= D; end architecture; -library ieee;use ieee.std_logic_1164.all; -entity buffer2 is port (D : in std_logic; Q : out std_logic);end entity; -architecture arch of buffer2 is -component buffer1 port (D : in std_logic;Q : out std_logic);end component buffer1; -begin my_buffer_i : buffer1 port map (D => D,Q => Q);end architecture; - """ - self.add_source_file("lib_1", "file1.vhd", text_file_1_2) - lib2_file1_vhd = self.add_source_file("lib_2", "file1.vhd", text_file_1_2) - file3 = self.add_source_file("lib", "file3.vhd", """\ -library ieee;use ieee.std_logic_1164.all; -library lib_1;entity your_buffer is port (D : in std_logic; Q : out std_logic);end entity; -architecture arch of your_buffer is -component buffer1 port (D : in std_logic;Q : out std_logic);end component buffer1; -begin my_buffer_i : buffer1 port map (D => D,Q => Q);end architecture; - """) - dep_files = self.project.get_dependencies_in_compile_order([file3], implementation_dependencies=True) - self.assertNotIn(lib2_file1_vhd, dep_files) - - def test_dependencies_on_separated_architecture(self): - """ - Create a projected containing an entity file separated from architecture file. - Dependency should involve also architecture. - """ - self.project = Project() - self.project.add_library("lib", "work_path") - self.add_source_file("lib", "file1.vhd", """\ -library ieee; -use ieee.std_logic_1164.all; - -entity buffer1 is - port (D : in std_logic; - Q : out std_logic); -end entity; - """) - - file1_arch_vhd = self.add_source_file("lib", "file1_arch.vhd", """\ -library ieee; -use ieee.std_logic_1164.all; - -architecture arch of buffer1 is -begin - Q <= D; -end architecture; - """) - - file3 = self.add_source_file("lib", "file3.vhd", """\ -library ieee; -use ieee.std_logic_1164.all; - -entity your_buffer is -port (D : in std_logic; Q : out std_logic); -end entity; - -architecture arch of your_buffer is -begin -my_buffer_i : entity work.buffer1 - port map (D => D,Q => Q); -end architecture; - """) - dep_files = self.project.get_dependencies_in_compile_order([file3], implementation_dependencies=True) - self.assertIn(file1_arch_vhd, dep_files) - - def test_dependencies_on_verilog_component(self): - """ - Create a projected containing an verilog file separated. - Dependency should involve it. - """ - self.project = Project() - self.project.add_library("lib", "work_path") - file1_v = self.add_source_file("lib", "file1.v", """\ -module buffer1 (input D,output Q); -assign Q = D; -endmodule - """) - file3 = self.add_source_file("lib", "file3.vhd", """\ -library ieee;use ieee.std_logic_1164.all; -entity your_buffer is port (D : in std_logic;Q : out std_logic);end entity; -architecture arch of your_buffer is -component buffer1 port (D : in std_logic;Q : out std_logic);end component buffer1; -begin my_buffer_i : buffer1 port map (D => D,Q => Q);end architecture; - """) - dep_files = self.project.get_dependencies_in_compile_order([file3], implementation_dependencies=True) - self.assertIn(file1_v, dep_files) - - def create_dummy_three_file_project(self, update_file1=False): - """ - Create a projected containing three dummy files - optionally only updating file1 - """ - self.project = Project() - self.project.add_library("lib", "work_path") - - if update_file1: - file1 = self.add_source_file("lib", "file1.vhd", """\ -entity module1 is -end entity; - -architecture arch of module1 is -begin -end architecture; -""") - else: - file1 = self.add_source_file("lib", "file1.vhd", """\ -entity module1 is -end entity; - -architecture arch of module1 is -begin - report "Updated"; -end architecture; -""") - file2 = self.add_source_file("lib", "file2.vhd", """\ -entity module2 is -end entity; - -architecture arch of module2 is -begin - module1_inst : entity lib.module1; -end architecture; -""") - - file3 = self.add_source_file("lib", "file3.vhd", """\ -entity module3 is -end entity; - -architecture arch of module3 is -begin - module1_inst : entity work.module2; -end architecture; -""") - return file1, file2, file3 - - def test_add_source_file_has_vhdl_standard(self): - write_file("file.vhd", "") - - for std in ('93', '2002', '2008'): - project = Project() - project.add_library("lib", "lib_path") - source_file = project.add_source_file("file.vhd", - library_name="lib", file_type='vhdl', vhdl_standard=std) - self.assertEqual(source_file.get_vhdl_standard(), std) - - def test_add_source_file_detects_illegal_vhdl_standard(self): - write_file("file.vhd", "") - - project = Project() - project.add_library("lib", "lib_path") - self.assertRaises(ValueError, project.add_source_file, "file.vhd", - library_name="lib", file_type='vhdl', vhdl_standard='2007') - - def test_add_source_file_has_no_parse_vhdl(self): - - for no_parse in (True, False): - project = Project() - file_name = "file.vhd" - write_file(file_name, """ - entity ent is - end entity; - """) - project.add_library("lib", "work_path") - source_file = project.add_source_file(file_name, - "lib", - file_type=file_type_of(file_name), - no_parse=no_parse) - self.assertEqual(len(source_file.design_units), int(not no_parse)) - - def test_add_source_file_has_no_parse_verilog(self): - - for no_parse in (True, False): - project = Project() - file_name = "file.v" - write_file(file_name, """ - module mod; - endmodule - """) - project.add_library("lib", "work_path") - source_file = project.add_source_file(file_name, - "lib", - file_type=file_type_of(file_name), - no_parse=no_parse) - self.assertEqual(len(source_file.design_units), int(not no_parse)) - - @mock.patch("vunit.project.LOGGER") - def test_no_warning_builtin_library_reference(self, mock_logger): - self.project.add_library("lib", "lib_path") - - self.add_source_file("lib", "ent.vhd", """ -use std.foo.all; -use ieee.bar.all; -use builtin_lib.all; -""") - - self.project.add_builtin_library("builtin_lib") - self.project.get_files_in_compile_order() - warning_calls = mock_logger.warning.call_args_list - self.assertEqual(len(warning_calls), 0) - - def test_add_external_library(self): - os.makedirs("lib_path") - self.project.add_library("lib", "lib_path", is_external=True) - - def test_add_external_library_must_exist(self): - try: - self.project.add_library("lib2", "lib_path2", is_external=True) - except ValueError as err: - self.assertEqual(str(err), "External library 'lib_path2' does not exist") - else: - assert False, "ValueError not raised" - - def test_add_external_library_must_be_a_directory(self): - write_file("lib_path3", "") - try: - self.project.add_library("lib3", "lib_path3", is_external=True) - except ValueError as err: - self.assertEqual(str(err), "External library must be a directory. Got 'lib_path3'") - else: - assert False, "ValueError not raised" - - def add_source_file(self, library_name, file_name, contents, defines=None): - """ - Convenient wrapper arround project.add_source_file - """ - write_file(file_name, contents) - source_file = self.project.add_source_file(file_name, - library_name, - file_type=file_type_of(file_name), - defines=defines) - return source_file - - def hash_file_name_of(self, source_file): - """ - Get the hash file name of a source_file - """ - return self.project._hash_file_name_of(source_file) # pylint: disable=protected-access - - def update(self, source_file): - """ - Wrapper arround project.update - """ - self.project.update(source_file) - - def assert_should_recompile(self, source_files): - self.assert_count_equal(source_files, self.project.get_files_in_compile_order()) - - def assert_compiles(self, source_file, before): - """ - Assert that the compile order of source_file is before the file named 'before'. - """ - for src_file in self.project.get_files_in_compile_order(): - self.update(src_file) - self.assert_should_recompile([]) - tick() - self.update(source_file) - self.assertIn(before, self.project.get_files_in_compile_order()) - - def assert_not_compiles(self, source_file, before): - """ - Assert that the compile order of source_file is not before the file named 'before'. - """ - for src_file in self.project.get_files_in_compile_order(): - self.update(src_file) - self.assert_should_recompile([]) - tick() - self.update(source_file) - self.assertNotIn(before, self.project.get_files_in_compile_order()) - - def assert_has_package_body(self, source_file_name, package_name): - """ - Assert that there is a package body with package_name withing source_file_name - """ - unit = self._find_design_unit(source_file_name, - "package body", - package_name, - False, package_name) - self.assertIsNotNone(unit) - - def assert_has_package(self, source_file_name, name): - """ - Assert that there is a package with name withing source_file_name - """ - unit = self._find_design_unit(source_file_name, - "package", - name) - self.assertIsNotNone(unit) - - def assert_has_entity(self, source_file, name, - generic_names=None, - architecture_names=None): - """ - Assert that there is an entity with name withing source_file - that has architectures with architecture_names. - """ - generic_names = [] if generic_names is None else generic_names - architecture_names = [] if architecture_names is None else architecture_names - - for entity in source_file.library.get_entities(): - if entity.name == name: - self.assert_count_equal(entity.generic_names, generic_names) - self.assert_count_equal(entity.architecture_names, architecture_names) - return - - self.assertFalse("Did not find entity " + name + "in " + source_file.name) - - def assert_has_architecture(self, source_file_name, name, entity_name): - """ - Assert that there is an architecture with name of entity_name within source_file_name - """ - unit = self._find_design_unit(source_file_name, - "architecture", - name, False, entity_name) - self.assertIsNotNone(unit) - - def assert_has_component_instantiation(self, source_file_name, component_name): - """ - Assert that there is a component instantion with component with source_file_name - """ - found_comp = False - for source_file in self.project.get_source_files_in_order(): - for component in source_file.depending_components: - if component == component_name: - found_comp = True - - self.assertTrue(found_comp, "Did not find component " + component_name + " in " + source_file_name) - - def _find_design_unit(self, # pylint: disable=too-many-arguments - source_file_name, - design_unit_type, - design_unit_name, - is_primary=True, - primary_design_unit_name=None): - """ - Utility fnction to find and return a design unit - """ - - for source_file in self.project.get_source_files_in_order(): - for design_unit in source_file.design_units: - if design_unit.unit_type != design_unit_type: - continue - if design_unit.name != design_unit_name: - continue - - self.assertEqual(design_unit.is_primary, is_primary) - self.assertEqual(source_file.name, source_file_name) - if not is_primary: - self.assertEqual(design_unit.primary_design_unit, primary_design_unit_name) - return design_unit - - return None - - def assert_count_equal(self, values1, values2): - # Python 2.7 compatability - self.assertEqual(sorted(values1), sorted(values2)) - - -def tick(): - sleep(0.01) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +# pylint: disable=too-many-lines + +""" +Test the project functionality +""" + + +import unittest +from shutil import rmtree +from os.path import join, exists, dirname +import os +from time import sleep +import itertools +from vunit.test.mock_2or3 import mock +from vunit.exceptions import CompileError +from vunit.ostools import renew_path, write_file +from vunit.project import Project, file_type_of + + +class TestProject(unittest.TestCase): # pylint: disable=too-many-public-methods + """ + Test the Project class + """ + + def setUp(self): + self.output_path = join(dirname(__file__), "test_project_out") + renew_path(self.output_path) + self.project = Project() + self.cwd = os.getcwd() + os.chdir(self.output_path) + + def tearDown(self): + os.chdir(self.cwd) + if exists(self.output_path): + rmtree(self.output_path) + + def test_parses_entity_architecture(self): + self.project.add_library("lib", "work_path") + + # Add architecture before entity to test that they are paired later + self.add_source_file("lib", "file2.vhd", """\ +architecture arch3 of foo is +begin +end architecture; +""") + + file1 = self.add_source_file("lib", "file1.vhd", """\ +entity foo is +end entity; + +architecture arch of foo is +begin +end architecture; + +architecture arch2 of foo is +begin +end architecture; +""") + + self.assert_has_entity(file1, "foo", + architecture_names=["arch", "arch2", "arch3"]) + self.add_source_file("lib", "file3.vhd", """\ +architecture arch4 of foo is +begin +end architecture; +""") + + self.assert_has_entity(file1, "foo", + architecture_names=["arch", "arch2", "arch3", "arch4"]) + self.assert_has_architecture("file1.vhd", "arch", "foo") + self.assert_has_architecture("file1.vhd", "arch2", "foo") + self.assert_has_architecture("file2.vhd", "arch3", "foo") + self.assert_has_architecture("file3.vhd", "arch4", "foo") + + def test_parses_entity_architecture_with_generics(self): + self.project.add_library("lib", "work_path") + file1 = self.add_source_file("lib", "file1.vhd", """\ +entity foo is + generic ( + testing_that_foo : boolean; + testing_that_bar : boolean); +end entity; + +architecture arch of foo is +begin +end architecture; +""") + + self.assert_has_entity(file1, "foo", + generic_names=["testing_that_bar", "testing_that_foo"], + architecture_names=["arch"]) + self.assert_has_architecture("file1.vhd", "arch", "foo") + + def test_parses_package(self): + self.project.add_library("lib", "work_path") + self.add_source_file("lib", "file1.vhd", """\ +package foo is +end package; + +package body foo is +begin +end package body; +""") + self.assert_has_package("file1.vhd", "foo") + self.assert_has_package_body("file1.vhd", "foo") + + @mock.patch("vunit.project.LOGGER") + def test_recovers_from_parse_error(self, logger): + self.project.add_library("lib", "work_path") + source_file = self.add_source_file("lib", "file.vhd", """\ +entity foo is + port (;); +end entity; +""") + logger.error.assert_called_once_with("Failed to parse %s", "file.vhd") + self.assertEqual(source_file.design_units, []) + + def test_finds_entity_instantiation_dependencies(self): + file1, file2, file3 = self.create_dummy_three_file_project() + + self.assert_compiles(file1, before=file2) + self.assert_compiles(file2, before=file3) + + def test_primary_with_same_name_in_multiple_libraries_secondary_dependency(self): + self.project.add_library("lib1", "lib1_path") + self.project.add_library("lib2", "lib2_path") + + foo_arch = self.add_source_file("lib1", "foo_arch.vhd", """ +architecture arch of foo is +begin +end architecture; +""") + + foo1_ent = self.add_source_file("lib1", "foo1_ent.vhd", """ +entity foo is +port (signal bar : boolean); +end entity; +""") + + self.add_source_file("lib2", "foo2_ent.vhd", """ +entity foo is +end entity; +""") + + self.assert_compiles(foo1_ent, before=foo_arch) + + def test_multiple_identical_file_names_with_different_path_in_same_library(self): + self.project.add_library("lib", "lib_path") + a_foo = self.add_source_file("lib", join("a", "foo.vhd"), """ +entity a_foo is +end entity; +""") + + b_foo = self.add_source_file("lib", join("b", "foo.vhd"), """ +entity b_foo is +end entity; +""") + self.assert_should_recompile([a_foo, b_foo]) + self.update(a_foo) + self.assert_should_recompile([b_foo]) + self.update(b_foo) + self.assert_should_recompile([]) + + def test_finds_entity_architecture_dependencies(self): + self.project.add_library("lib", "lib_path") + entity = self.add_source_file("lib", "entity.vhd", """ +entity foo is +end entity; +""") + + arch1 = self.add_source_file("lib", "arch1.vhd", """ +architecture arch1 of foo is +begin +end architecture; +""") + + arch2 = self.add_source_file("lib", "arch2.vhd", """ +architecture arch2 of foo is +begin +end architecture; +""") + self.assert_compiles(entity, before=arch1) + self.assert_compiles(entity, before=arch2) + + def test_finds_package_dependencies(self): + self.project.add_library("lib", "lib_path") + package = self.add_source_file("lib", "package.vhd", """ +package foo is +end package; +""") + + body = self.add_source_file("lib", "body.vhd", """ +package body foo is +begin +end package body; +""") + + self.assert_compiles(package, before=body) + + def create_module_package_and_body(self, add_body=True): + """ + Help function to create a three file project + with a package, a package body and a module using the package + """ + self.project.add_library("lib", "lib_path") + + package = self.add_source_file("lib", "package.vhd", """ +package pkg is +end package; +""") + + body = None + if add_body: + body = self.add_source_file("lib", "body.vhd", """ +package body pkg is +begin +end package body; +""") + + self.project.add_library("lib2", "work_path") + module = self.add_source_file("lib2", "module.vhd", """ +library lib; +use lib.pkg.all; + +entity module is +end entity; + +architecture arch of module is +begin +end architecture; +""") + return package, body, module + + def test_finds_use_package_dependencies_case_insensitive(self): + for library_clause, use_clause in itertools.combinations(("lib", "Lib"), 2): + self.project = Project() + self.project.add_library("Lib", "lib_path") + + package = self.add_source_file("Lib", "package.vhd", """ +package pkg is +end package; + +package body PKG is +begin +end package body; +""") + + self.project.add_library("lib2", "lib2_path") + module = self.add_source_file("lib2", "module.vhd", """ +library {library_clause}; +use {use_clause}.PKG.all; + """.format(library_clause=library_clause, use_clause=use_clause)) + self.assert_compiles(package, before=module) + + def test_error_on_case_insensitive_library_name_conflict(self): + self.project.add_library("Lib", "lib_path1") + try: + self.project.add_library("lib", "lib_path1") + except RuntimeError as exception: + self.assertEqual(str(exception), + "Library name 'lib' not case-insensitive unique. " + "Library name 'Lib' previously defined") + else: + raise AssertionError("RuntimeError not raised") + + def test_finds_use_package_dependencies(self): + package, body, module = self.create_module_package_and_body() + self.assert_compiles(package, before=body) + self.assert_compiles(package, before=module) + self.assert_not_compiles(body, before=module) + + def test_finds_extra_package_body_dependencies(self): + self.project = Project(depend_on_package_body=True) + package, body, module = self.create_module_package_and_body() + self.assert_compiles(package, before=body) + self.assert_compiles(body, before=module) + self.assert_compiles(package, before=module) + + def test_that_package_can_have_no_body(self): + self.project = Project(depend_on_package_body=True) + package, _, module = self.create_module_package_and_body(add_body=False) + self.assert_compiles(package, before=module) + + def test_package_instantiation_dependencies_on_generic_package(self): + self.project.add_library("pkg_lib", "pkg_lib_path") + pkg = self.add_source_file("pkg_lib", "pkg.vhd", """ +package pkg is +end package; + """) + + self.project.add_library("lib", "lib_path") + ent = self.add_source_file("lib", "ent.vhd", """ +library pkg_lib; + +entity ent is +end entity; + +architecture a of ent is + package pkg_inst is new pkg_lib.pkg; +begin +end architecture; +""") + + self.assert_compiles(pkg, before=ent) + + def test_package_instantiation_dependencies_on_instantiated_package(self): + self.project.add_library("lib", "lib_path") + generic_pkg = self.add_source_file("lib", "generic_pkg.vhd", """ +package generic_pkg is + generic (foo : boolean); +end package; +""") + instance_pkg = self.add_source_file("lib", "instance_pkg.vhd", """ +package instance_pkg is new work.generic_pkg + generic map (foo => false); +""") + user = self.add_source_file("lib", "user.vhd", """ +use work.instance_pkg; +""") + self.assert_compiles(generic_pkg, before=instance_pkg) + self.assert_compiles(instance_pkg, before=user) + + def test_finds_context_dependencies(self): + self.project.add_library("lib", "lib_path") + context = self.add_source_file("lib", "context.vhd", """ +context foo is +end context; +""") + + self.project.add_library("lib2", "work_path") + module = self.add_source_file("lib2", "module.vhd", """ +library lib; +context lib.foo; + +entity module is +end entity; + +architecture arch of module is +begin +end architecture; +""") + + self.assert_compiles(context, before=module) + + def test_finds_configuration_dependencies(self): + self.project.add_library("lib", "lib_path") + cfg = self.add_source_file("lib", "cfg.vhd", """ +configuration cfg of ent is +end configuration; +""") + + ent = self.add_source_file("lib", "ent.vhd", """ +entity ent is +end entity; +""") + + ent_a1 = self.add_source_file("lib", "ent_a1.vhd", """ +architecture a1 of ent is +begin +end architecture; +""") + + ent_a2 = self.add_source_file("lib", "ent_a2.vhd", """ +architecture a2 of ent is +begin +end architecture; +""") + + self.assert_compiles(ent, before=cfg) + self.assert_compiles(ent_a1, before=cfg) + self.assert_compiles(ent_a2, before=cfg) + + def test_finds_configuration_reference_dependencies(self): + self.project.add_library("lib", "lib_path") + cfg = self.add_source_file("lib", "cfg.vhd", """ +configuration cfg of ent is +end configuration; +""") + + self.add_source_file("lib", "ent.vhd", """ +entity ent is +end entity; +""") + + self.add_source_file("lib", "ent_a.vhd", """ +architecture a of ent is +begin +end architecture; +""") + + top = self.add_source_file("lib", "top.vhd", """ +entity top is +end entity; + +architecture a of top is + for inst : comp use configuration work.cfg; +begin + inst : comp; +end architecture; +""") + + self.assert_compiles(cfg, before=top) + + def test_specific_architecture_reference_dependencies(self): + """ + GHDL dependes also on architecture when specificially mentioned + """ + self.project.add_library("lib", "lib_path") + + self.add_source_file("lib", "ent.vhd", """ +entity ent is +end entity; +""") + + ent_a1 = self.add_source_file("lib", "ent_a1.vhd", """ +architecture a1 of ent is +begin +end architecture; +""") + + ent_a2 = self.add_source_file("lib", "ent_a2.vhd", """ +architecture a2 of ent is +begin +end architecture; +""") + + top1 = self.add_source_file("lib", "top1.vhd", """ +entity top1 is +end entity; + +architecture a of top1 is +begin + inst : entity work.ent(a1); +end architecture; +""") + + top2 = self.add_source_file("lib", "top2.vhd", """ +entity top2 is +end entity; + +architecture a of top2 is + for inst : comp use entity work.ent(a2); +begin + inst : comp; +end architecture; +""") + + self.assert_compiles(ent_a1, before=top1) + self.assert_compiles(ent_a2, before=top2) + + @mock.patch("vunit.project.LOGGER") + def test_warning_on_missing_specific_architecture_reference(self, mock_logger): + self.project.add_library("lib", "lib_path") + + self.add_source_file("lib", "ent.vhd", """ +entity ent is +end entity; +""") + + self.add_source_file("lib", "arch.vhd", """ +architecture a1 of ent is +begin +end architecture; +""") + + self.add_source_file("lib", "top.vhd", """ +entity top1 is +end entity; + +architecture a of top1 is +begin + inst1 : entity work.ent(a1); + inst2 : entity work.ent(a2); # Missing +end architecture; +""") + + self.project.get_files_in_compile_order() + warning_calls = mock_logger.warning.call_args_list + log_msg = warning_calls[0][0][0] % warning_calls[0][0][1:] + self.assertEqual(len(warning_calls), 1) + self.assertIn("top.vhd", log_msg) + self.assertIn("a2", log_msg) + self.assertIn("lib.ent", log_msg) + + @mock.patch("vunit.project.LOGGER") + def test_error_on_duplicate_file(self, logger): + self.project.add_library("lib", "lib_path") + file1 = self.add_source_file("lib", "file.vhd", "entity foo is end entity;") + self.assertRaises(RuntimeError, self.add_source_file, "lib", "file.vhd", "entity foo is end entity foo;") + + # No extra design unit of file added + lib = self.project.get_library("lib") + self.assertEqual([ent.name for ent in lib.get_entities()], ["foo"]) + self.assertEqual(lib.get_source_file("file.vhd"), file1) + self.assertEqual(self.project.get_source_files_in_order(), [file1]) + self.assertFalse(logger.warning.called) + + @mock.patch("vunit.project.LOGGER") + def test_no_error_on_duplicate_identical_file(self, logger): + self.project.add_library("lib", "lib_path") + file1 = self.add_source_file("lib", "file.vhd", "entity foo is end entity;") + file2 = self.add_source_file("lib", "file.vhd", "entity foo is end entity;") + self.assertEqual(id(file1), id(file2)) + + # No extra design unit of file added + lib = self.project.get_library("lib") + self.assertEqual([ent.name for ent in lib.get_entities()], ["foo"]) + self.assertEqual(lib.get_source_file("file.vhd"), file1) + self.assertEqual(self.project.get_source_files_in_order(), [file1]) + self.assertTrue(logger.info.called) + + def _test_warning_on_duplicate(self, code, message, verilog=False): + """ + Utility function to test adding the same duplicate code under + file.vhd and file_copy.vhd where the duplication should cause a warning message. + """ + suffix = "v" if verilog else "vhd" + + self.add_source_file("lib", "file." + suffix, code) + + with mock.patch("vunit.project.LOGGER") as mock_logger: + self.add_source_file("lib", "file_copy." + suffix, code) + warning_calls = mock_logger.warning.call_args_list + log_msg = warning_calls[0][0][0] % warning_calls[0][0][1:] + self.assertEqual(len(warning_calls), 1) + self.assertEqual(log_msg, message) + + def test_warning_on_duplicate_entity(self): + self.project.add_library("lib", "lib_path") + self._test_warning_on_duplicate( + """ +entity ent is +end entity; +""", + "file_copy.vhd: entity 'ent' previously defined in file.vhd") + + def test_warning_on_duplicate_package(self): + self.project.add_library("lib", "lib_path") + self._test_warning_on_duplicate( + """ +package pkg is +end package; +""", + "file_copy.vhd: package 'pkg' previously defined in file.vhd") + + def test_warning_on_duplicate_configuration(self): + self.project.add_library("lib", "lib_path") + self._test_warning_on_duplicate( + """ +configuration cfg of ent is +end configuration; +""", + "file_copy.vhd: configuration 'cfg' previously defined in file.vhd") + + def test_warning_on_duplicate_package_body(self): + self.project.add_library("lib", "lib_path") + self.add_source_file("lib", "pkg.vhd", """ +package pkg is +end package; +""") + + self._test_warning_on_duplicate( + """ +package body pkg is +end package bodY; +""", + "file_copy.vhd: package body 'pkg' previously defined in file.vhd") + + def test_warning_on_duplicate_architecture(self): + self.project.add_library("lib", "lib_path") + self.add_source_file("lib", "ent.vhd", """ +entity ent is +end entity; +""") + + self.add_source_file("lib", "arch.vhd", """ +architecture a_no_duplicate of ent is +begin +end architecture; +""") + + self._test_warning_on_duplicate( + """ +architecture a of ent is +begin +end architecture; +""", + "file_copy.vhd: architecture 'a' previously defined in file.vhd") + + def test_warning_on_duplicate_context(self): + self.project.add_library("lib", "lib_path") + self._test_warning_on_duplicate( + """ +context ctx is +end context; +""", + "file_copy.vhd: context 'ctx' previously defined in file.vhd") + + def test_error_on_adding_duplicate_library(self): + self.project.add_library(logical_name="lib", directory="dir") + self.assertRaises(ValueError, self.project.add_library, + logical_name="lib", directory="dir") + + def test_warning_on_duplicate_verilog_module(self): + self.project.add_library("lib", "lib_path") + self._test_warning_on_duplicate( + """ +module foo; +endmodule +""", + "file_copy.v: module 'foo' previously defined in file.v", + verilog=True) + + def test_warning_on_duplicate_verilog_package(self): + self.project.add_library("lib", "lib_path") + self._test_warning_on_duplicate( + """ +package pkg; +endpackage +""", + "file_copy.v: package 'pkg' previously defined in file.v", + verilog=True) + + def test_should_recompile_all_files_initially(self): + file1, file2, file3 = self.create_dummy_three_file_project() + self.assert_should_recompile([file1, file2, file3]) + self.assert_should_recompile([file1, file2, file3]) + + def test_updating_creates_hash_files(self): + files = self.create_dummy_three_file_project() + + for source_file in files: + self.update(source_file) + self.assertTrue(exists(self.hash_file_name_of(source_file))) + + def test_should_not_recompile_updated_files(self): + file1, file2, file3 = self.create_dummy_three_file_project() + + self.update(file1) + self.assert_should_recompile([file2, file3]) + + self.update(file2) + self.assert_should_recompile([file3]) + + self.update(file3) + self.assert_should_recompile([]) + + def test_should_recompile_files_affected_by_change(self): + file1, file2, file3 = self.create_dummy_three_file_project() + + self.update(file1) + self.update(file2) + self.update(file3) + self.assert_should_recompile([]) + + file1, file2, file3 = self.create_dummy_three_file_project() + self.assert_should_recompile([]) + + file1, file2, file3 = self.create_dummy_three_file_project(update_file1=True) + self.assert_should_recompile([file1, file2, file3]) + + def test_should_recompile_files_after_changing_compile_options(self): + file1, file2, file3 = self.create_dummy_three_file_project() + + self.update(file1) + self.update(file2) + self.update(file3) + self.assert_should_recompile([]) + + file2.set_compile_option("ghdl.flags", ["--no-vital-checks"]) + self.assert_should_recompile([file2, file3]) + + def test_should_recompile_files_after_changing_vhdl_standard(self): + write_file("file_name.vhd", "") + + self.project = Project() + self.project.add_library("lib", "lib_path") + source_file = self.project.add_source_file("file_name.vhd", library_name="lib", vhdl_standard='2008') + self.assert_should_recompile([source_file]) + self.update(source_file) + self.assert_should_recompile([]) + + self.project = Project() + self.project.add_library("lib", "lib_path") + source_file = self.project.add_source_file("file_name.vhd", library_name="lib", vhdl_standard='2002') + self.assert_should_recompile([source_file]) + + def test_add_compile_option(self): + self.project.add_library("lib", "lib_path") + file1 = self.add_source_file("lib", "file.vhd", "") + file1.add_compile_option("ghdl.flags", ["--foo"]) + self.assertEqual(file1.get_compile_option("ghdl.flags"), ["--foo"]) + file1.add_compile_option("ghdl.flags", ["--bar"]) + self.assertEqual(file1.get_compile_option("ghdl.flags"), ["--foo", "--bar"]) + file1.set_compile_option("ghdl.flags", ["--xyz"]) + self.assertEqual(file1.get_compile_option("ghdl.flags"), ["--xyz"]) + + def test_add_compile_option_does_not_mutate_argument(self): + self.project.add_library("lib", "lib_path") + file1 = self.add_source_file("lib", "file.vhd", "") + options = ["--foo"] + file1.add_compile_option("ghdl.flags", options) + options[0] = "--xyz" + self.assertEqual(file1.get_compile_option("ghdl.flags"), ["--foo"]) + file1.add_compile_option("ghdl.flags", ["--bar"]) + self.assertEqual(options, ["--xyz"]) + + def test_set_compile_option_does_not_mutate_argument(self): + self.project.add_library("lib", "lib_path") + file1 = self.add_source_file("lib", "file.vhd", "") + options = ["--foo"] + file1.set_compile_option("ghdl.flags", options) + options[0] = "--xyz" + self.assertEqual(file1.get_compile_option("ghdl.flags"), ["--foo"]) + + def test_compile_option_validation(self): + self.project.add_library("lib", "lib_path") + source_file = self.add_source_file("lib", "file.vhd", "") + self.assertRaises(ValueError, source_file.set_compile_option, "foo", None) + self.assertRaises(ValueError, source_file.set_compile_option, "ghdl.flags", None) + self.assertRaises(ValueError, source_file.add_compile_option, "ghdl.flags", None) + self.assertRaises(ValueError, source_file.get_compile_option, "foo") + + def test_should_recompile_files_affected_by_change_with_later_timestamp(self): + file1, file2, file3 = self.create_dummy_three_file_project() + + self.update(file1) + self.update(file2) + self.update(file3) + self.assert_should_recompile([]) + + file1, file2, file3 = self.create_dummy_three_file_project() + self.assert_should_recompile([]) + + file1, file2, file3 = self.create_dummy_three_file_project(update_file1=True) + self.assert_should_recompile([file1, file2, file3]) + + tick() + self.update(file1) + self.assert_should_recompile([file2, file3]) + + def test_should_recompile_files_missing_hash(self): + file1, file2, file3 = self.create_dummy_three_file_project() + + self.update(file1) + self.update(file2) + self.update(file3) + self.assert_should_recompile([]) + + os.remove(self.hash_file_name_of(file2)) + self.assert_should_recompile([file2, file3]) + + def test_finds_component_instantiation_dependencies(self): + self.project.add_library("toplib", "work_path") + top = self.add_source_file("toplib", "top.vhd", """\ +entity top is +end entity; + +architecture arch of top is +begin + labelFoo : component foo + generic map(WIDTH => 16) + port map(clk => '1', + rst => '0', + in_vec => record_reg.input_signal, + output => some_signal(UPPER_CONSTANT-1 downto LOWER_CONSTANT+1)); + + label2Foo : foo2 + port map(clk => '1', + rst => '0', + output => "00"); +end architecture; +""") + + comp1 = self.add_source_file("toplib", "comp1.vhd", """\ +entity foo is +end entity; +""") + + comp2 = self.add_source_file("toplib", "comp2.vhd", """\ +entity foo2 is +end entity; + +architecture arch of foo2 is +begin +end architecture; +""") + comp1_arch = self.add_source_file("toplib", "comp1_arch.vhd", """\ +architecture arch of foo is +begin +end architecture; +""") + + self.assert_has_component_instantiation("top.vhd", "foo") + self.assert_has_component_instantiation("top.vhd", "foo2") + dependencies = self.project.get_dependencies_in_compile_order([top], implementation_dependencies=True) + self.assertIn(comp1, dependencies) + self.assertIn(comp1_arch, dependencies) + self.assertIn(comp2, dependencies) + + def test_get_dependencies_in_compile_order_without_target(self): + self.create_dummy_three_file_project() + deps = self.project.get_dependencies_in_compile_order() + self.assertEqual(len(deps), 3) + self.assertTrue(deps[0] == self.project.get_source_files_in_order()[0]) + self.assertTrue(deps[1] == self.project.get_source_files_in_order()[1]) + self.assertTrue(deps[2] == self.project.get_source_files_in_order()[2]) + + def test_get_dependencies_in_compile_order_with_target(self): + self.create_dummy_three_file_project() + deps = self.project.get_dependencies_in_compile_order( + target_files=[self.project.get_source_files_in_order()[1]]) + self.assertEqual(len(deps), 2) + self.assertTrue(deps[0] == self.project.get_source_files_in_order()[0]) + self.assertTrue(deps[1] == self.project.get_source_files_in_order()[1]) + + # To test that indirect dependencies are included + deps = self.project.get_dependencies_in_compile_order( + target_files=[self.project.get_source_files_in_order()[2]]) + self.assertEqual(len(deps), 3) + self.assertTrue(deps[0] == self.project.get_source_files_in_order()[0]) + self.assertTrue(deps[1] == self.project.get_source_files_in_order()[1]) + self.assertTrue(deps[2] == self.project.get_source_files_in_order()[2]) + + def test_compiles_same_file_into_different_libraries(self): + pkgs = [] + second_pkgs = [] + self.project.add_library("lib", "lib_path") + + other_pkg = self.add_source_file("lib", "other_pkg.vhd", """ +package other_pkg is +end package other_pkg; +""") + + for lib in ["lib1", "lib2"]: + self.project.add_library(lib, lib + "_path") + pkgs.append(self.add_source_file(lib, "pkg.vhd", """ +library lib; +use lib.other_pkg.all; + +package pkg is +end package pkg; +""")) + + second_pkgs.append(self.add_source_file(lib, lib + "_pkg.vhd", """ +use work.pkg.all; + +package second_pkg is +end package second_pkg; +""")) + + self.assertNotEqual(self.hash_file_name_of(pkgs[0]), + self.hash_file_name_of(pkgs[1])) + self.assertEqual(len(self.project.get_files_in_compile_order()), 5) + self.assert_compiles(other_pkg, before=pkgs[0]) + self.assert_compiles(other_pkg, before=pkgs[1]) + self.assert_compiles(pkgs[0], before=second_pkgs[0]) + self.assert_compiles(pkgs[1], before=second_pkgs[1]) + + def test_has_verilog_module(self): + self.project.add_library("lib", "lib_path") + self.add_source_file("lib", "module.v", """\ +module name; +endmodule +""") + library = self.project.get_library("lib") + modules = library.get_modules() + self.assertEqual(len(modules), 1) + + def test_finds_verilog_package_import_dependencies(self): + self.project.add_library("lib", "lib_path") + pkg = self.add_source_file("lib", "pkg.sv", """\ +package pkg; +endpackage +""") + module = self.add_source_file("lib", "module.sv", """\ +module name; + import pkg::*; +endmodule +""") + self.assert_compiles(pkg, before=module) + + def test_finds_verilog_package_reference_dependencies(self): + self.project.add_library("lib", "lib_path") + pkg = self.add_source_file("lib", "pkg.sv", """\ +package pkg; +endpackage +""") + module = self.add_source_file("lib", "module.sv", """\ +module name; + pkg::func(); +endmodule +""") + self.assert_compiles(pkg, before=module) + + def test_verilog_package_reference_is_case_sensitive(self): + self.project = Project() + self.project.add_library("lib", "lib_path") + pkg = self.add_source_file("lib", "pkg.sv", """\ +package Pkg; +endpackage +""") + module = self.add_source_file("lib", "module.sv", """\ +module name; + pkg::func(); +endmodule +""") + self.assert_not_compiles(pkg, before=module) + + self.project = Project() + self.project.add_library("lib", "lib_path") + pkg = self.add_source_file("lib", "pkg.sv", """\ +package pkg; +endpackage +""") + module = self.add_source_file("lib", "module.sv", """\ +module name; + Pkg::func(); +endmodule +""") + self.assert_not_compiles(pkg, before=module) + + def test_finds_verilog_module_instantiation_dependencies(self): + self.project.add_library("lib", "lib_path") + module1 = self.add_source_file("lib", "module1.sv", """\ +module module1; +endmodule +""") + module2 = self.add_source_file("lib", "module2.sv", """\ +module module2; + module1 inst(); +endmodule +""") + self.assert_compiles(module1, before=module2) + + def test_verilog_module_instantiation_is_case_sensitive(self): + self.project = Project() + self.project.add_library("lib", "lib_path") + module1 = self.add_source_file("lib", "module1.sv", """\ +module Module1; +endmodule +""") + module2 = self.add_source_file("lib", "module2.sv", """\ +module module2; + module1 inst(); +endmodule +""") + self.assert_not_compiles(module1, before=module2) + + self.project = Project() + self.project.add_library("lib", "lib_path") + module1 = self.add_source_file("lib", "module1.sv", """\ +module module1; +endmodule +""") + module2 = self.add_source_file("lib", "module2.sv", """\ +module module2; + Module1 inst(); +endmodule +""") + self.assert_not_compiles(module1, before=module2) + + def test_finds_verilog_module_instantiation_dependencies_in_vhdl(self): + self.project.add_library("lib1", "lib_path") + self.project.add_library("lib2", "lib_path") + module1 = self.add_source_file("lib1", "module1.sv", """\ +module module1; +endmodule +""") + module2 = self.add_source_file("lib2", "module2.vhd", """\ +library lib1; + +entity ent is +end entity; + +architecture a of ent is +begin + inst : entity lib1.module1; +end architecture; +""") + self.assert_compiles(module1, before=module2) + + def test_finds_verilog_include_dependencies(self): + def create_project(): + """ + Create the test project + """ + self.project = Project() + self.project.add_library("lib", "lib_path") + return self.add_source_file("lib", "module.sv", """\ +`include "include.svh" +""") + + write_file("include.svh", """\ +module name; +endmodule +""") + module = create_project() + self.assert_should_recompile([module]) + + for src_file in self.project.get_files_in_compile_order(): + self.update(src_file) + create_project() + self.assert_should_recompile([]) + + write_file("include.svh", """\ +module other_name; +endmodule +""") + module = create_project() + self.assert_should_recompile([module]) + + def test_verilog_defines_affects_dependency_scanning(self): + self.project.add_library("lib", "lib_path") + self.add_source_file("lib", "module.v", """\ +`ifdef foo +module mod; +endmodule +`endif +""") + library = self.project.get_library("lib") + modules = library.get_modules() + self.assertEqual(len(modules), 0) + + self.project = Project() + self.project.add_library("lib", "lib_path") + self.add_source_file("lib", "module.v", """\ +`ifdef foo +module mod; +endmodule +`endif +""", defines={"foo": ""}) + library = self.project.get_library("lib") + modules = library.get_modules() + self.assertEqual(len(modules), 1) + + def test_recompile_when_updating_defines(self): + contents1 = """ +module mod1; +endmodule +""" + contents2 = """ +module mod2; +endmodule +""" + self.project = Project() + self.project.add_library("lib", "lib_path") + mod1 = self.add_source_file("lib", "module1.v", contents1) + mod2 = self.add_source_file("lib", "module2.v", contents2) + self.assert_should_recompile([mod1, mod2]) + self.update(mod1) + self.update(mod2) + self.assert_should_recompile([]) + + self.project = Project() + self.project.add_library("lib", "lib_path") + mod1 = self.add_source_file("lib", "module1.v", contents1, + defines={"foo": "bar"}) + mod2 = self.add_source_file("lib", "module2.v", contents2) + self.assert_should_recompile([mod1]) + self.update(mod1) + self.update(mod2) + self.assert_should_recompile([]) + + self.project = Project() + self.project.add_library("lib", "lib_path") + mod1 = self.add_source_file("lib", "module1.v", contents1, + defines={"foo": "other_bar"}) + mod2 = self.add_source_file("lib", "module2.v", contents2) + self.assert_should_recompile([mod1]) + self.update(mod1) + self.update(mod2) + self.assert_should_recompile([]) + + def test_manual_dependencies(self): + self.project.add_library("lib", "lib_path") + ent1 = self.add_source_file("lib", "ent1.vhd", """\ +entity ent1 is +end ent1; + +architecture arch of ent1 is +begin +end architecture; +""") + + ent2 = self.add_source_file("lib", "ent2.vhd", """\ +entity ent2 is +end ent2; + +architecture arch of ent2 is +begin +end architecture; +""") + + self.project.add_manual_dependency(ent2, depends_on=ent1) + self.assert_compiles(ent1, before=ent2) + + @mock.patch("vunit.project.LOGGER", autospec=True) + def test_circular_dependencies_causes_error(self, logger): + self.project.add_library("lib", "lib_path") + self.add_source_file("lib", "ent1.vhd", """\ +entity ent1 is +end ent1; + +architecture arch of ent1 is +begin + ent2_inst : entity work.ent2; +end architecture; +""") + + self.add_source_file("lib", "ent2.vhd", """\ +entity ent2 is +end ent2; + +architecture arch of ent2 is +begin + ent1_inst : entity work.ent1; +end architecture; +""") + + self.assertRaises(CompileError, self.project.get_files_in_compile_order) + logger.error.assert_called_once_with( + "Found circular dependency:\n%s", + "ent1.vhd ->\n" + "ent2.vhd ->\n" + "ent1.vhd") + + def test_order_of_adding_libraries_is_kept(self): + for order in itertools.combinations(range(4), 4): + project = Project() + for idx in order: + project.add_library("lib%i" % idx, "lib%i_path" % idx) + + library_names = [lib.name for lib in project.get_libraries()] + self.assertEqual(library_names, ["lib%i" % idx for idx in order]) + + def test_file_type_of(self): + self.assertEqual(file_type_of("file.vhd"), "vhdl") + self.assertEqual(file_type_of("file.vhdl"), "vhdl") + self.assertEqual(file_type_of("file.sv"), "systemverilog") + self.assertEqual(file_type_of("file.v"), "verilog") + self.assertEqual(file_type_of("file.vams"), "verilog") + self.assertRaises(RuntimeError, file_type_of, "file.foo") + + def test_circular_dependencies_through_libraries(self): + """ + Create a projected containing two identical files in two separated + library and instantiate an entity from the first library. + """ + self.project = Project() + self.project.add_library("lib_1", "work_path") + self.project.add_library("lib_2", "work_path") + self.project.add_library("lib", "work_path") + text_file_1_2 = """\ + library ieee; + use ieee.std_logic_1164.all; + + entity buffer1 is + port (Q : out std_logic); + end entity; + + architecture arch of buffer1 is begin + Q <= '1'; + end architecture; + + library ieee; + use ieee.std_logic_1164.all; + + entity buffer2 is + port (Q : out std_logic); + end entity; + + architecture arch of buffer2 is + component buffer1 + port (Q : out std_logic); + end component buffer1; + + begin + my_buffer_i : buffer1 + port map (Q => Q); + end architecture; + """ + self.add_source_file("lib_1", "file1.vhd", text_file_1_2) + self.add_source_file("lib_2", "file2.vhd", text_file_1_2) + file3 = self.add_source_file("lib", "file3.vhd", """\ + library lib_1; + + entity your_buffer is + end entity; + + architecture arch of your_buffer is + begin + my_buffer_i : entity lib_1.buffer1; + end architecture; + """) + self.project.get_dependencies_in_compile_order([file3], implementation_dependencies=True) + + def test_dependencies_on_multiple_libraries(self): + """ + Create a projected containing two identical files in two separated + library and instantiate an entity from the first library. + """ + self.project = Project() + self.project.add_library("lib_1", "work_path") + self.project.add_library("lib_2", "work_path") + self.project.add_library("lib", "work_path") + text_file_1_2 = """\ +library ieee;use ieee.std_logic_1164.all; +entity buffer1 is port (D : in std_logic;Q : out std_logic);end entity; +architecture arch of buffer1 is begin Q <= D; end architecture; +library ieee;use ieee.std_logic_1164.all; +entity buffer2 is port (D : in std_logic; Q : out std_logic);end entity; +architecture arch of buffer2 is +component buffer1 port (D : in std_logic;Q : out std_logic);end component buffer1; +begin my_buffer_i : buffer1 port map (D => D,Q => Q);end architecture; + """ + self.add_source_file("lib_1", "file1.vhd", text_file_1_2) + lib2_file1_vhd = self.add_source_file("lib_2", "file1.vhd", text_file_1_2) + file3 = self.add_source_file("lib", "file3.vhd", """\ +library ieee;use ieee.std_logic_1164.all; +library lib_1;entity your_buffer is port (D : in std_logic; Q : out std_logic);end entity; +architecture arch of your_buffer is +component buffer1 port (D : in std_logic;Q : out std_logic);end component buffer1; +begin my_buffer_i : buffer1 port map (D => D,Q => Q);end architecture; + """) + dep_files = self.project.get_dependencies_in_compile_order([file3], implementation_dependencies=True) + self.assertNotIn(lib2_file1_vhd, dep_files) + + def test_dependencies_on_separated_architecture(self): + """ + Create a projected containing an entity file separated from architecture file. + Dependency should involve also architecture. + """ + self.project = Project() + self.project.add_library("lib", "work_path") + self.add_source_file("lib", "file1.vhd", """\ +library ieee; +use ieee.std_logic_1164.all; + +entity buffer1 is + port (D : in std_logic; + Q : out std_logic); +end entity; + """) + + file1_arch_vhd = self.add_source_file("lib", "file1_arch.vhd", """\ +library ieee; +use ieee.std_logic_1164.all; + +architecture arch of buffer1 is +begin + Q <= D; +end architecture; + """) + + file3 = self.add_source_file("lib", "file3.vhd", """\ +library ieee; +use ieee.std_logic_1164.all; + +entity your_buffer is +port (D : in std_logic; Q : out std_logic); +end entity; + +architecture arch of your_buffer is +begin +my_buffer_i : entity work.buffer1 + port map (D => D,Q => Q); +end architecture; + """) + dep_files = self.project.get_dependencies_in_compile_order([file3], implementation_dependencies=True) + self.assertIn(file1_arch_vhd, dep_files) + + def test_dependencies_on_verilog_component(self): + """ + Create a projected containing an verilog file separated. + Dependency should involve it. + """ + self.project = Project() + self.project.add_library("lib", "work_path") + file1_v = self.add_source_file("lib", "file1.v", """\ +module buffer1 (input D,output Q); +assign Q = D; +endmodule + """) + file3 = self.add_source_file("lib", "file3.vhd", """\ +library ieee;use ieee.std_logic_1164.all; +entity your_buffer is port (D : in std_logic;Q : out std_logic);end entity; +architecture arch of your_buffer is +component buffer1 port (D : in std_logic;Q : out std_logic);end component buffer1; +begin my_buffer_i : buffer1 port map (D => D,Q => Q);end architecture; + """) + dep_files = self.project.get_dependencies_in_compile_order([file3], implementation_dependencies=True) + self.assertIn(file1_v, dep_files) + + def create_dummy_three_file_project(self, update_file1=False): + """ + Create a projected containing three dummy files + optionally only updating file1 + """ + self.project = Project() + self.project.add_library("lib", "work_path") + + if update_file1: + file1 = self.add_source_file("lib", "file1.vhd", """\ +entity module1 is +end entity; + +architecture arch of module1 is +begin +end architecture; +""") + else: + file1 = self.add_source_file("lib", "file1.vhd", """\ +entity module1 is +end entity; + +architecture arch of module1 is +begin + report "Updated"; +end architecture; +""") + file2 = self.add_source_file("lib", "file2.vhd", """\ +entity module2 is +end entity; + +architecture arch of module2 is +begin + module1_inst : entity lib.module1; +end architecture; +""") + + file3 = self.add_source_file("lib", "file3.vhd", """\ +entity module3 is +end entity; + +architecture arch of module3 is +begin + module1_inst : entity work.module2; +end architecture; +""") + return file1, file2, file3 + + def test_add_source_file_has_vhdl_standard(self): + write_file("file.vhd", "") + + for std in ('93', '2002', '2008'): + project = Project() + project.add_library("lib", "lib_path") + source_file = project.add_source_file("file.vhd", + library_name="lib", file_type='vhdl', vhdl_standard=std) + self.assertEqual(source_file.get_vhdl_standard(), std) + + def test_add_source_file_detects_illegal_vhdl_standard(self): + write_file("file.vhd", "") + + project = Project() + project.add_library("lib", "lib_path") + self.assertRaises(ValueError, project.add_source_file, "file.vhd", + library_name="lib", file_type='vhdl', vhdl_standard='2007') + + def test_add_source_file_has_no_parse_vhdl(self): + + for no_parse in (True, False): + project = Project() + file_name = "file.vhd" + write_file(file_name, """ + entity ent is + end entity; + """) + project.add_library("lib", "work_path") + source_file = project.add_source_file(file_name, + "lib", + file_type=file_type_of(file_name), + no_parse=no_parse) + self.assertEqual(len(source_file.design_units), int(not no_parse)) + + def test_add_source_file_has_no_parse_verilog(self): + + for no_parse in (True, False): + project = Project() + file_name = "file.v" + write_file(file_name, """ + module mod; + endmodule + """) + project.add_library("lib", "work_path") + source_file = project.add_source_file(file_name, + "lib", + file_type=file_type_of(file_name), + no_parse=no_parse) + self.assertEqual(len(source_file.design_units), int(not no_parse)) + + @mock.patch("vunit.project.LOGGER") + def test_no_warning_builtin_library_reference(self, mock_logger): + self.project.add_library("lib", "lib_path") + + self.add_source_file("lib", "ent.vhd", """ +use std.foo.all; +use ieee.bar.all; +use builtin_lib.all; +""") + + self.project.add_builtin_library("builtin_lib") + self.project.get_files_in_compile_order() + warning_calls = mock_logger.warning.call_args_list + self.assertEqual(len(warning_calls), 0) + + def test_add_external_library(self): + os.makedirs("lib_path") + self.project.add_library("lib", "lib_path", is_external=True) + + def test_add_external_library_must_exist(self): + try: + self.project.add_library("lib2", "lib_path2", is_external=True) + except ValueError as err: + self.assertEqual(str(err), "External library 'lib_path2' does not exist") + else: + assert False, "ValueError not raised" + + def test_add_external_library_must_be_a_directory(self): + write_file("lib_path3", "") + try: + self.project.add_library("lib3", "lib_path3", is_external=True) + except ValueError as err: + self.assertEqual(str(err), "External library must be a directory. Got 'lib_path3'") + else: + assert False, "ValueError not raised" + + def add_source_file(self, library_name, file_name, contents, defines=None): + """ + Convenient wrapper arround project.add_source_file + """ + write_file(file_name, contents) + source_file = self.project.add_source_file(file_name, + library_name, + file_type=file_type_of(file_name), + defines=defines) + return source_file + + def hash_file_name_of(self, source_file): + """ + Get the hash file name of a source_file + """ + return self.project._hash_file_name_of(source_file) # pylint: disable=protected-access + + def update(self, source_file): + """ + Wrapper arround project.update + """ + self.project.update(source_file) + + def assert_should_recompile(self, source_files): + self.assert_count_equal(source_files, self.project.get_files_in_compile_order()) + + def assert_compiles(self, source_file, before): + """ + Assert that the compile order of source_file is before the file named 'before'. + """ + for src_file in self.project.get_files_in_compile_order(): + self.update(src_file) + self.assert_should_recompile([]) + tick() + self.update(source_file) + self.assertIn(before, self.project.get_files_in_compile_order()) + + def assert_not_compiles(self, source_file, before): + """ + Assert that the compile order of source_file is not before the file named 'before'. + """ + for src_file in self.project.get_files_in_compile_order(): + self.update(src_file) + self.assert_should_recompile([]) + tick() + self.update(source_file) + self.assertNotIn(before, self.project.get_files_in_compile_order()) + + def assert_has_package_body(self, source_file_name, package_name): + """ + Assert that there is a package body with package_name withing source_file_name + """ + unit = self._find_design_unit(source_file_name, + "package body", + package_name, + False, package_name) + self.assertIsNotNone(unit) + + def assert_has_package(self, source_file_name, name): + """ + Assert that there is a package with name withing source_file_name + """ + unit = self._find_design_unit(source_file_name, + "package", + name) + self.assertIsNotNone(unit) + + def assert_has_entity(self, source_file, name, + generic_names=None, + architecture_names=None): + """ + Assert that there is an entity with name withing source_file + that has architectures with architecture_names. + """ + generic_names = [] if generic_names is None else generic_names + architecture_names = [] if architecture_names is None else architecture_names + + for entity in source_file.library.get_entities(): + if entity.name == name: + self.assert_count_equal(entity.generic_names, generic_names) + self.assert_count_equal(entity.architecture_names, architecture_names) + return + + self.assertFalse("Did not find entity " + name + "in " + source_file.name) + + def assert_has_architecture(self, source_file_name, name, entity_name): + """ + Assert that there is an architecture with name of entity_name within source_file_name + """ + unit = self._find_design_unit(source_file_name, + "architecture", + name, False, entity_name) + self.assertIsNotNone(unit) + + def assert_has_component_instantiation(self, source_file_name, component_name): + """ + Assert that there is a component instantion with component with source_file_name + """ + found_comp = False + for source_file in self.project.get_source_files_in_order(): + for component in source_file.depending_components: + if component == component_name: + found_comp = True + + self.assertTrue(found_comp, "Did not find component " + component_name + " in " + source_file_name) + + def _find_design_unit(self, # pylint: disable=too-many-arguments + source_file_name, + design_unit_type, + design_unit_name, + is_primary=True, + primary_design_unit_name=None): + """ + Utility fnction to find and return a design unit + """ + + for source_file in self.project.get_source_files_in_order(): + for design_unit in source_file.design_units: + if design_unit.unit_type != design_unit_type: + continue + if design_unit.name != design_unit_name: + continue + + self.assertEqual(design_unit.is_primary, is_primary) + self.assertEqual(source_file.name, source_file_name) + if not is_primary: + self.assertEqual(design_unit.primary_design_unit, primary_design_unit_name) + return design_unit + + return None + + def assert_count_equal(self, values1, values2): + # Python 2.7 compatability + self.assertEqual(sorted(values1), sorted(values2)) + + +def tick(): + sleep(0.01) diff --git a/vunit/test/unit/test_rivierapro_interface.py b/vunit/test/unit/test_rivierapro_interface.py index 497ca0c85..29d79879d 100644 --- a/vunit/test/unit/test_rivierapro_interface.py +++ b/vunit/test/unit/test_rivierapro_interface.py @@ -1,215 +1,215 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test the RivieraPro interface -""" - - -import unittest -from os.path import join, dirname, exists -import os -from shutil import rmtree -from vunit.rivierapro_interface import RivieraProInterface -from vunit.test.mock_2or3 import mock -from vunit.project import Project -from vunit.ostools import renew_path, write_file - - -class TestRivieraProInterface(unittest.TestCase): - """ - Test the RivieraPro interface - """ - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.rivierapro_interface.Process", autospec=True) - def test_compile_project_vhdl(self, process, check_output): - simif = RivieraProInterface(prefix="prefix", - output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - project.add_source_file("file.vhd", "lib", file_type="vhdl") - simif.compile_project(project) - process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], - cwd=self.output_path, env=simif.get_env()) - process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], - cwd=self.output_path, env=simif.get_env()) - check_output.assert_called_once_with( - [join('prefix', 'vcom'), - '-quiet', - '-j', - self.output_path, - '-2008', - '-work', - 'lib', - 'file.vhd'], env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.rivierapro_interface.Process", autospec=True) - def test_compile_project_vhdl_extra_flags(self, process, check_output): - simif = RivieraProInterface(prefix="prefix", - output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - source_file = project.add_source_file("file.vhd", "lib", file_type="vhdl") - source_file.set_compile_option("rivierapro.vcom_flags", ["custom", "flags"]) - simif.compile_project(project) - process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], - cwd=self.output_path, env=simif.get_env()) - process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], - cwd=self.output_path, env=simif.get_env()) - check_output.assert_called_once_with([join('prefix', 'vcom'), - '-quiet', - '-j', - self.output_path, - 'custom', - 'flags', - '-2008', - '-work', - 'lib', - 'file.vhd'], env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.rivierapro_interface.Process", autospec=True) - def test_compile_project_verilog(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") - simif = RivieraProInterface(prefix="prefix", - output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.v", "") - project.add_source_file("file.v", "lib", file_type="verilog") - simif.compile_project(project) - process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], - cwd=self.output_path, env=simif.get_env()) - process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], - cwd=self.output_path, env=simif.get_env()) - check_output.assert_called_once_with([join('prefix', 'vlog'), - '-quiet', - '-lc', - library_cfg, - '-work', - 'lib', - 'file.v', - '-l', 'lib'], - env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.rivierapro_interface.Process", autospec=True) - def test_compile_project_system_verilog(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") - simif = RivieraProInterface(prefix="prefix", - output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.sv", "") - project.add_source_file("file.sv", "lib", file_type="systemverilog") - simif.compile_project(project) - process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], - cwd=self.output_path, env=simif.get_env()) - process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], - cwd=self.output_path, env=simif.get_env()) - check_output.assert_called_once_with([join('prefix', 'vlog'), - '-quiet', - '-lc', - library_cfg, - '-sv2k12', - '-work', - 'lib', - 'file.sv', - '-l', 'lib'], - env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.rivierapro_interface.Process", autospec=True) - def test_compile_project_verilog_extra_flags(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") - simif = RivieraProInterface(prefix="prefix", - output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.v", "") - source_file = project.add_source_file("file.v", "lib", file_type="verilog") - source_file.set_compile_option("rivierapro.vlog_flags", ["custom", "flags"]) - simif.compile_project(project) - process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], - cwd=self.output_path, env=simif.get_env()) - process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], - cwd=self.output_path, env=simif.get_env()) - check_output.assert_called_once_with([join('prefix', 'vlog'), - '-quiet', - '-lc', - library_cfg, - 'custom', - 'flags', - '-work', - 'lib', - 'file.v', - '-l', 'lib'], env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.rivierapro_interface.Process", autospec=True) - def test_compile_project_verilog_include(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") - simif = RivieraProInterface(prefix="prefix", - output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.v", "") - project.add_source_file("file.v", "lib", file_type="verilog", include_dirs=["include"]) - simif.compile_project(project) - process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], - cwd=self.output_path, env=None) - process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], - cwd=self.output_path, env=simif.get_env()) - check_output.assert_called_once_with([join('prefix', 'vlog'), - '-quiet', - '-lc', - library_cfg, - '-work', - 'lib', - 'file.v', - '-l', 'lib', - '+incdir+include'], env=simif.get_env()) - - @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") - @mock.patch("vunit.rivierapro_interface.Process", autospec=True) - def test_compile_project_verilog_define(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") - simif = RivieraProInterface(prefix="prefix", - output_path=self.output_path) - project = Project() - project.add_library("lib", "lib_path") - write_file("file.v", "") - project.add_source_file("file.v", "lib", file_type="verilog", defines={"defname": "defval"}) - simif.compile_project(project) - process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], - cwd=self.output_path, env=simif.get_env()) - process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], - cwd=self.output_path, env=simif.get_env()) - check_output.assert_called_once_with([join('prefix', 'vlog'), - '-quiet', - '-lc', - library_cfg, - '-work', - 'lib', - 'file.v', - '-l', 'lib', - '+define+defname=defval'], env=simif.get_env()) - - def setUp(self): - self.output_path = join(dirname(__file__), "test_rivierapro_out") - renew_path(self.output_path) - self.project = Project() - self.cwd = os.getcwd() - os.chdir(self.output_path) - - def tearDown(self): - os.chdir(self.cwd) - if exists(self.output_path): - rmtree(self.output_path) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test the RivieraPro interface +""" + + +import unittest +from os.path import join, dirname, exists +import os +from shutil import rmtree +from vunit.rivierapro_interface import RivieraProInterface +from vunit.test.mock_2or3 import mock +from vunit.project import Project +from vunit.ostools import renew_path, write_file + + +class TestRivieraProInterface(unittest.TestCase): + """ + Test the RivieraPro interface + """ + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.rivierapro_interface.Process", autospec=True) + def test_compile_project_vhdl(self, process, check_output): + simif = RivieraProInterface(prefix="prefix", + output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + project.add_source_file("file.vhd", "lib", file_type="vhdl") + simif.compile_project(project) + process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], + cwd=self.output_path, env=simif.get_env()) + process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], + cwd=self.output_path, env=simif.get_env()) + check_output.assert_called_once_with( + [join('prefix', 'vcom'), + '-quiet', + '-j', + self.output_path, + '-2008', + '-work', + 'lib', + 'file.vhd'], env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.rivierapro_interface.Process", autospec=True) + def test_compile_project_vhdl_extra_flags(self, process, check_output): + simif = RivieraProInterface(prefix="prefix", + output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + source_file = project.add_source_file("file.vhd", "lib", file_type="vhdl") + source_file.set_compile_option("rivierapro.vcom_flags", ["custom", "flags"]) + simif.compile_project(project) + process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], + cwd=self.output_path, env=simif.get_env()) + process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], + cwd=self.output_path, env=simif.get_env()) + check_output.assert_called_once_with([join('prefix', 'vcom'), + '-quiet', + '-j', + self.output_path, + 'custom', + 'flags', + '-2008', + '-work', + 'lib', + 'file.vhd'], env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.rivierapro_interface.Process", autospec=True) + def test_compile_project_verilog(self, process, check_output): + library_cfg = join(self.output_path, "library.cfg") + simif = RivieraProInterface(prefix="prefix", + output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.v", "") + project.add_source_file("file.v", "lib", file_type="verilog") + simif.compile_project(project) + process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], + cwd=self.output_path, env=simif.get_env()) + process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], + cwd=self.output_path, env=simif.get_env()) + check_output.assert_called_once_with([join('prefix', 'vlog'), + '-quiet', + '-lc', + library_cfg, + '-work', + 'lib', + 'file.v', + '-l', 'lib'], + env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.rivierapro_interface.Process", autospec=True) + def test_compile_project_system_verilog(self, process, check_output): + library_cfg = join(self.output_path, "library.cfg") + simif = RivieraProInterface(prefix="prefix", + output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.sv", "") + project.add_source_file("file.sv", "lib", file_type="systemverilog") + simif.compile_project(project) + process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], + cwd=self.output_path, env=simif.get_env()) + process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], + cwd=self.output_path, env=simif.get_env()) + check_output.assert_called_once_with([join('prefix', 'vlog'), + '-quiet', + '-lc', + library_cfg, + '-sv2k12', + '-work', + 'lib', + 'file.sv', + '-l', 'lib'], + env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.rivierapro_interface.Process", autospec=True) + def test_compile_project_verilog_extra_flags(self, process, check_output): + library_cfg = join(self.output_path, "library.cfg") + simif = RivieraProInterface(prefix="prefix", + output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.v", "") + source_file = project.add_source_file("file.v", "lib", file_type="verilog") + source_file.set_compile_option("rivierapro.vlog_flags", ["custom", "flags"]) + simif.compile_project(project) + process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], + cwd=self.output_path, env=simif.get_env()) + process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], + cwd=self.output_path, env=simif.get_env()) + check_output.assert_called_once_with([join('prefix', 'vlog'), + '-quiet', + '-lc', + library_cfg, + 'custom', + 'flags', + '-work', + 'lib', + 'file.v', + '-l', 'lib'], env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.rivierapro_interface.Process", autospec=True) + def test_compile_project_verilog_include(self, process, check_output): + library_cfg = join(self.output_path, "library.cfg") + simif = RivieraProInterface(prefix="prefix", + output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.v", "") + project.add_source_file("file.v", "lib", file_type="verilog", include_dirs=["include"]) + simif.compile_project(project) + process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], + cwd=self.output_path, env=None) + process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], + cwd=self.output_path, env=simif.get_env()) + check_output.assert_called_once_with([join('prefix', 'vlog'), + '-quiet', + '-lc', + library_cfg, + '-work', + 'lib', + 'file.v', + '-l', 'lib', + '+incdir+include'], env=simif.get_env()) + + @mock.patch("vunit.simulator_interface.check_output", autospec=True, return_value="") + @mock.patch("vunit.rivierapro_interface.Process", autospec=True) + def test_compile_project_verilog_define(self, process, check_output): + library_cfg = join(self.output_path, "library.cfg") + simif = RivieraProInterface(prefix="prefix", + output_path=self.output_path) + project = Project() + project.add_library("lib", "lib_path") + write_file("file.v", "") + project.add_source_file("file.v", "lib", file_type="verilog", defines={"defname": "defval"}) + simif.compile_project(project) + process.assert_any_call([join("prefix", "vlib"), "lib", "lib_path"], + cwd=self.output_path, env=simif.get_env()) + process.assert_called_with([join("prefix", "vmap"), "lib", "lib_path"], + cwd=self.output_path, env=simif.get_env()) + check_output.assert_called_once_with([join('prefix', 'vlog'), + '-quiet', + '-lc', + library_cfg, + '-work', + 'lib', + 'file.v', + '-l', 'lib', + '+define+defname=defval'], env=simif.get_env()) + + def setUp(self): + self.output_path = join(dirname(__file__), "test_rivierapro_out") + renew_path(self.output_path) + self.project = Project() + self.cwd = os.getcwd() + os.chdir(self.output_path) + + def tearDown(self): + os.chdir(self.cwd) + if exists(self.output_path): + rmtree(self.output_path) diff --git a/vunit/test/unit/test_simulator_interface.py b/vunit/test/unit/test_simulator_interface.py index f6d9ccf46..ef164e24c 100644 --- a/vunit/test/unit/test_simulator_interface.py +++ b/vunit/test/unit/test_simulator_interface.py @@ -1,280 +1,280 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test the SimulatorInterface class -""" - -import unittest -from os.path import join, dirname, exists -import os -import subprocess -from shutil import rmtree -from vunit.project import Project -from vunit.simulator_interface import (SimulatorInterface, - BooleanOption, - ListOfStringOption, - StringOption, - VHDLAssertLevelOption) -from vunit.test.mock_2or3 import mock -from vunit.exceptions import CompileError -from vunit.ostools import renew_path, write_file - - -class TestSimulatorInterface(unittest.TestCase): - """ - Test the SimulatorInterface class - """ - - def test_compile_source_files(self): - simif = create_simulator_interface() - simif.compile_source_file_command.side_effect = iter([["command1"], ["command2"]]) - project = Project() - project.add_library("lib", "lib_path") - write_file("file1.vhd", "") - file1 = project.add_source_file("file1.vhd", "lib", file_type="vhdl") - write_file("file2.vhd", "") - file2 = project.add_source_file("file2.vhd", "lib", file_type="vhdl") - project.add_manual_dependency(file2, depends_on=file1) - - with mock.patch("vunit.simulator_interface.check_output", autospec=True) as check_output: - check_output.side_effect = iter(["", ""]) - printer = MockPrinter() - simif.compile_source_files(project, printer=printer) - check_output.assert_has_calls([mock.call(["command1"], env=simif.get_env()), - mock.call(["command2"], env=simif.get_env())]) - self.assertEqual(printer.output, """\ -Compiling into lib: file1.vhd passed -Compiling into lib: file2.vhd passed -Compile passed -""") - self.assertEqual(project.get_files_in_compile_order(incremental=True), []) - - def test_compile_source_files_continue_on_error(self): - simif = create_simulator_interface() - - project = Project() - project.add_library("lib", "lib_path") - write_file("file1.vhd", "") - file1 = project.add_source_file("file1.vhd", "lib", file_type="vhdl") - write_file("file2.vhd", "") - file2 = project.add_source_file("file2.vhd", "lib", file_type="vhdl") - write_file("file3.vhd", "") - file3 = project.add_source_file("file3.vhd", "lib", file_type="vhdl") - project.add_manual_dependency(file2, depends_on=file1) - - def compile_source_file_command(source_file): - """ - Dummy compile command - """ - if source_file == file1: - return ["command1"] - - if source_file == file2: - return ["command2"] - - if source_file == file3: - return ["command3"] - - raise AssertionError - - def check_output_side_effect(command, env=None): # pylint: disable=missing-docstring, unused-argument - if command == ["command1"]: - raise subprocess.CalledProcessError(returncode=-1, cmd=command, output="bad stuff") - - return "" - - simif.compile_source_file_command.side_effect = compile_source_file_command - - with mock.patch("vunit.simulator_interface.check_output", autospec=True) as check_output: - check_output.side_effect = check_output_side_effect - printer = MockPrinter() - self.assertRaises(CompileError, simif.compile_source_files, - project, printer=printer, continue_on_error=True) - self.assertEqual(printer.output, """\ -Compiling into lib: file3.vhd passed -Compiling into lib: file1.vhd failed -=== Command used: === -command1 - -=== Command output: === -bad stuff -Compiling into lib: file2.vhd skipped -Compile failed -""") - self.assertEqual(len(check_output.mock_calls), 2) - check_output.assert_has_calls([mock.call(["command1"], env=simif.get_env()), - mock.call(["command3"], env=simif.get_env())], any_order=True) - self.assertEqual(project.get_files_in_compile_order(incremental=True), [file1, file2]) - - def test_compile_source_files_check_output_error(self): - simif = create_simulator_interface() - simif.compile_source_file_command.return_value = ["command"] - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - source_file = project.add_source_file("file.vhd", "lib", file_type="vhdl") - - with mock.patch("vunit.simulator_interface.check_output", autospec=True) as check_output: - - def check_output_side_effect(command, env=None): # pylint: disable=missing-docstring, unused-argument - raise subprocess.CalledProcessError(returncode=-1, cmd=command, output="bad stuff") - - check_output.side_effect = check_output_side_effect - printer = MockPrinter() - self.assertRaises(CompileError, simif.compile_source_files, project, printer=printer) - self.assertEqual(printer.output, """\ -Compiling into lib: file.vhd failed -=== Command used: === -command - -=== Command output: === -bad stuff -Compile failed -""") - check_output.assert_called_once_with(["command"], env=simif.get_env()) - self.assertEqual(project.get_files_in_compile_order(incremental=True), [source_file]) - - def test_compile_source_files_create_command_error(self): - simif = create_simulator_interface() - project = Project() - project.add_library("lib", "lib_path") - write_file("file.vhd", "") - source_file = project.add_source_file("file.vhd", "lib", file_type="vhdl") - - with mock.patch("vunit.simulator_interface.check_output", autospec=True) as check_output: - check_output.return_value = "" - - def raise_compile_error(source_file): # pylint: disable=unused-argument - raise CompileError - - simif.compile_source_file_command.side_effect = raise_compile_error - self.assertRaises(CompileError, simif.compile_source_files, project) - self.assertEqual(project.get_files_in_compile_order(incremental=True), [source_file]) - - @mock.patch("os.environ", autospec=True) - def test_find_prefix(self, environ): - - class MySimulatorInterface(SimulatorInterface): # pylint: disable=abstract-method - """ - Dummy simulator interface for testing - """ - name = "simname" - prefix_from_path = None - - @classmethod - def find_prefix_from_path(cls): - return cls.prefix_from_path - - simif = MySimulatorInterface(output_path="output_path", gui=False) - simif.name = "simname" - environ.get.return_value = None - self.assertEqual(simif.find_prefix(), None) - environ.get.assert_called_once_with("VUNIT_SIMNAME_PATH", None) - - environ.reset_mock() - environ.get.return_value = "simname/bin" - self.assertEqual(simif.find_prefix(), "simname/bin") - environ.get.assert_called_once_with("VUNIT_SIMNAME_PATH", None) - - environ.reset_mock() - environ.get.return_value = None - MySimulatorInterface.prefix_from_path = "prefix_from_path" - self.assertEqual(simif.find_prefix(), "prefix_from_path") - environ.get.assert_called_once_with("VUNIT_SIMNAME_PATH", None) - - def setUp(self): - self.output_path = join(dirname(__file__), "test_simulator_interface__out") - renew_path(self.output_path) - self.project = Project() - self.cwd = os.getcwd() - os.chdir(self.output_path) - - def tearDown(self): - os.chdir(self.cwd) - if exists(self.output_path): - rmtree(self.output_path) - - -class TestOptions(unittest.TestCase): - """ - The the compile and simulation options validators - """ - - def test_boolean_option(self): - option = BooleanOption("optname") - self._test_ok(option, True) - self._test_ok(option, False) - self._test_not_ok(option, None, - "Option 'optname' must be a boolean. Got None") - - def test_string_option(self): - option = StringOption("optname") - self._test_ok(option, "hello") - self._test_ok(option, u"hello") - self._test_not_ok(option, False, - "Option 'optname' must be a string. Got False") - self._test_not_ok(option, ["foo"], - "Option 'optname' must be a string. Got ['foo']") - - def test_list_of_string_option(self): - option = ListOfStringOption("optname") - self._test_ok(option, ["hello", "foo"]) - self._test_ok(option, [u"hello"]) - self._test_not_ok(option, [True], - "Option 'optname' must be a list of strings. " - "Got [True]") - self._test_not_ok(option, [["foo"]], - "Option 'optname' must be a list of strings. " - "Got [['foo']]") - self._test_not_ok(option, "foo", - "Option 'optname' must be a list of strings. " - "Got 'foo'") - - def test_vhdl_assert_level(self): - option = VHDLAssertLevelOption() - self._test_ok(option, "warning") - self._test_ok(option, "error") - self._test_ok(option, "failure") - - self._test_not_ok(option, "foo", - "Option 'vhdl_assert_stop_level' must be one of " - "('warning', 'error', 'failure'). Got 'foo'") - - @staticmethod - def _test_ok(option, value): - option.validate(value) - - def _test_not_ok(self, option, value, message): - """ - Test taht setting option to value is not OK with message - """ - try: - option.validate(value) - except ValueError as err: - self.assertEqual(str(err), message) - else: - assert False - - -def create_simulator_interface(): - """ - Create a simulator interface with fake method - """ - simif = SimulatorInterface(output_path="output_path", gui=False) - simif.compile_source_file_command = mock.create_autospec(simif.compile_source_file_command) - return simif - - -class MockPrinter(object): - """ - Mock printer that accumulates the calls as a string - """ - def __init__(self): - self.output = "" - - def write(self, text, output_file=None, fg=None, bg=None): # pylint: disable=unused-argument - self.output += text +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test the SimulatorInterface class +""" + +import unittest +from os.path import join, dirname, exists +import os +import subprocess +from shutil import rmtree +from vunit.project import Project +from vunit.simulator_interface import (SimulatorInterface, + BooleanOption, + ListOfStringOption, + StringOption, + VHDLAssertLevelOption) +from vunit.test.mock_2or3 import mock +from vunit.exceptions import CompileError +from vunit.ostools import renew_path, write_file + + +class TestSimulatorInterface(unittest.TestCase): + """ + Test the SimulatorInterface class + """ + + def test_compile_source_files(self): + simif = create_simulator_interface() + simif.compile_source_file_command.side_effect = iter([["command1"], ["command2"]]) + project = Project() + project.add_library("lib", "lib_path") + write_file("file1.vhd", "") + file1 = project.add_source_file("file1.vhd", "lib", file_type="vhdl") + write_file("file2.vhd", "") + file2 = project.add_source_file("file2.vhd", "lib", file_type="vhdl") + project.add_manual_dependency(file2, depends_on=file1) + + with mock.patch("vunit.simulator_interface.check_output", autospec=True) as check_output: + check_output.side_effect = iter(["", ""]) + printer = MockPrinter() + simif.compile_source_files(project, printer=printer) + check_output.assert_has_calls([mock.call(["command1"], env=simif.get_env()), + mock.call(["command2"], env=simif.get_env())]) + self.assertEqual(printer.output, """\ +Compiling into lib: file1.vhd passed +Compiling into lib: file2.vhd passed +Compile passed +""") + self.assertEqual(project.get_files_in_compile_order(incremental=True), []) + + def test_compile_source_files_continue_on_error(self): + simif = create_simulator_interface() + + project = Project() + project.add_library("lib", "lib_path") + write_file("file1.vhd", "") + file1 = project.add_source_file("file1.vhd", "lib", file_type="vhdl") + write_file("file2.vhd", "") + file2 = project.add_source_file("file2.vhd", "lib", file_type="vhdl") + write_file("file3.vhd", "") + file3 = project.add_source_file("file3.vhd", "lib", file_type="vhdl") + project.add_manual_dependency(file2, depends_on=file1) + + def compile_source_file_command(source_file): + """ + Dummy compile command + """ + if source_file == file1: + return ["command1"] + + if source_file == file2: + return ["command2"] + + if source_file == file3: + return ["command3"] + + raise AssertionError + + def check_output_side_effect(command, env=None): # pylint: disable=missing-docstring, unused-argument + if command == ["command1"]: + raise subprocess.CalledProcessError(returncode=-1, cmd=command, output="bad stuff") + + return "" + + simif.compile_source_file_command.side_effect = compile_source_file_command + + with mock.patch("vunit.simulator_interface.check_output", autospec=True) as check_output: + check_output.side_effect = check_output_side_effect + printer = MockPrinter() + self.assertRaises(CompileError, simif.compile_source_files, + project, printer=printer, continue_on_error=True) + self.assertEqual(printer.output, """\ +Compiling into lib: file3.vhd passed +Compiling into lib: file1.vhd failed +=== Command used: === +command1 + +=== Command output: === +bad stuff +Compiling into lib: file2.vhd skipped +Compile failed +""") + self.assertEqual(len(check_output.mock_calls), 2) + check_output.assert_has_calls([mock.call(["command1"], env=simif.get_env()), + mock.call(["command3"], env=simif.get_env())], any_order=True) + self.assertEqual(project.get_files_in_compile_order(incremental=True), [file1, file2]) + + def test_compile_source_files_check_output_error(self): + simif = create_simulator_interface() + simif.compile_source_file_command.return_value = ["command"] + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + source_file = project.add_source_file("file.vhd", "lib", file_type="vhdl") + + with mock.patch("vunit.simulator_interface.check_output", autospec=True) as check_output: + + def check_output_side_effect(command, env=None): # pylint: disable=missing-docstring, unused-argument + raise subprocess.CalledProcessError(returncode=-1, cmd=command, output="bad stuff") + + check_output.side_effect = check_output_side_effect + printer = MockPrinter() + self.assertRaises(CompileError, simif.compile_source_files, project, printer=printer) + self.assertEqual(printer.output, """\ +Compiling into lib: file.vhd failed +=== Command used: === +command + +=== Command output: === +bad stuff +Compile failed +""") + check_output.assert_called_once_with(["command"], env=simif.get_env()) + self.assertEqual(project.get_files_in_compile_order(incremental=True), [source_file]) + + def test_compile_source_files_create_command_error(self): + simif = create_simulator_interface() + project = Project() + project.add_library("lib", "lib_path") + write_file("file.vhd", "") + source_file = project.add_source_file("file.vhd", "lib", file_type="vhdl") + + with mock.patch("vunit.simulator_interface.check_output", autospec=True) as check_output: + check_output.return_value = "" + + def raise_compile_error(source_file): # pylint: disable=unused-argument + raise CompileError + + simif.compile_source_file_command.side_effect = raise_compile_error + self.assertRaises(CompileError, simif.compile_source_files, project) + self.assertEqual(project.get_files_in_compile_order(incremental=True), [source_file]) + + @mock.patch("os.environ", autospec=True) + def test_find_prefix(self, environ): + + class MySimulatorInterface(SimulatorInterface): # pylint: disable=abstract-method + """ + Dummy simulator interface for testing + """ + name = "simname" + prefix_from_path = None + + @classmethod + def find_prefix_from_path(cls): + return cls.prefix_from_path + + simif = MySimulatorInterface(output_path="output_path", gui=False) + simif.name = "simname" + environ.get.return_value = None + self.assertEqual(simif.find_prefix(), None) + environ.get.assert_called_once_with("VUNIT_SIMNAME_PATH", None) + + environ.reset_mock() + environ.get.return_value = "simname/bin" + self.assertEqual(simif.find_prefix(), "simname/bin") + environ.get.assert_called_once_with("VUNIT_SIMNAME_PATH", None) + + environ.reset_mock() + environ.get.return_value = None + MySimulatorInterface.prefix_from_path = "prefix_from_path" + self.assertEqual(simif.find_prefix(), "prefix_from_path") + environ.get.assert_called_once_with("VUNIT_SIMNAME_PATH", None) + + def setUp(self): + self.output_path = join(dirname(__file__), "test_simulator_interface__out") + renew_path(self.output_path) + self.project = Project() + self.cwd = os.getcwd() + os.chdir(self.output_path) + + def tearDown(self): + os.chdir(self.cwd) + if exists(self.output_path): + rmtree(self.output_path) + + +class TestOptions(unittest.TestCase): + """ + The the compile and simulation options validators + """ + + def test_boolean_option(self): + option = BooleanOption("optname") + self._test_ok(option, True) + self._test_ok(option, False) + self._test_not_ok(option, None, + "Option 'optname' must be a boolean. Got None") + + def test_string_option(self): + option = StringOption("optname") + self._test_ok(option, "hello") + self._test_ok(option, u"hello") + self._test_not_ok(option, False, + "Option 'optname' must be a string. Got False") + self._test_not_ok(option, ["foo"], + "Option 'optname' must be a string. Got ['foo']") + + def test_list_of_string_option(self): + option = ListOfStringOption("optname") + self._test_ok(option, ["hello", "foo"]) + self._test_ok(option, [u"hello"]) + self._test_not_ok(option, [True], + "Option 'optname' must be a list of strings. " + "Got [True]") + self._test_not_ok(option, [["foo"]], + "Option 'optname' must be a list of strings. " + "Got [['foo']]") + self._test_not_ok(option, "foo", + "Option 'optname' must be a list of strings. " + "Got 'foo'") + + def test_vhdl_assert_level(self): + option = VHDLAssertLevelOption() + self._test_ok(option, "warning") + self._test_ok(option, "error") + self._test_ok(option, "failure") + + self._test_not_ok(option, "foo", + "Option 'vhdl_assert_stop_level' must be one of " + "('warning', 'error', 'failure'). Got 'foo'") + + @staticmethod + def _test_ok(option, value): + option.validate(value) + + def _test_not_ok(self, option, value, message): + """ + Test taht setting option to value is not OK with message + """ + try: + option.validate(value) + except ValueError as err: + self.assertEqual(str(err), message) + else: + assert False + + +def create_simulator_interface(): + """ + Create a simulator interface with fake method + """ + simif = SimulatorInterface(output_path="output_path", gui=False) + simif.compile_source_file_command = mock.create_autospec(simif.compile_source_file_command) + return simif + + +class MockPrinter(object): + """ + Mock printer that accumulates the calls as a string + """ + def __init__(self): + self.output = "" + + def write(self, text, output_file=None, fg=None, bg=None): # pylint: disable=unused-argument + self.output += text diff --git a/vunit/test/unit/test_test_bench.py b/vunit/test/unit/test_test_bench.py index 2f721407e..27d8e2377 100644 --- a/vunit/test/unit/test_test_bench.py +++ b/vunit/test/unit/test_test_bench.py @@ -1,847 +1,847 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -# pylint: disable=too-many-public-methods - -""" -Tests the test test_bench module -""" - - -import unittest -from os.path import join - -from vunit.test_bench import (TestBench, - _remove_verilog_comments, - _find_tests, - _get_line_offsets, - _lookup_lineno, - _find_attributes, - _find_tests_and_attributes, - Test, - FileLocation, - Attribute, - LegacyAttribute) -from vunit.configuration import AttributeException -from vunit.ostools import write_file -from vunit.test.mock_2or3 import mock -from vunit.test.common import (with_tempdir, - get_vhdl_test_bench) - - -class TestTestBench(unittest.TestCase): - """ - Tests the test bench - """ - - @with_tempdir - def test_that_single_vhdl_test_is_created(self, tempdir): - design_unit = Entity('tb_entity', file_name=join(tempdir, "file.vhd")) - test_bench = TestBench(design_unit) - tests = self.create_tests(test_bench) - self.assert_has_tests(tests, ["lib.tb_entity.all"]) - - @staticmethod - @with_tempdir - def test_no_architecture_at_creation(tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd"), - no_arch=True) - TestBench(design_unit) - - @with_tempdir - def test_no_architecture_gives_runtime_error(self, tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd"), - no_arch=True) - test_bench = TestBench(design_unit) - try: - self.create_tests(test_bench) - except RuntimeError as exc: - self.assertEqual(str(exc), - "Test bench 'tb_entity' has no architecture.") - else: - assert False, "RuntimeError not raised" - - @with_tempdir - def test_that_single_verilog_test_is_created(self, tempdir): - design_unit = Module('tb_module', file_name=join(tempdir, "file.v")) - test_bench = TestBench(design_unit) - tests = self.create_tests(test_bench) - self.assert_has_tests(tests, ["lib.tb_module.all"]) - - @with_tempdir - def test_create_default_test(self, tempdir): - design_unit = Entity('tb_entity', file_name=join(tempdir, "file.vhd")) - design_unit.generic_names = ["runner_cfg"] - test_bench = TestBench(design_unit) - tests = self.create_tests(test_bench) - self.assert_has_tests(tests, ["lib.tb_entity.all"]) - - @with_tempdir - def test_multiple_architectures_are_not_allowed_for_test_bench(self, tempdir): - design_unit = Entity('tb_entity', file_name=join(tempdir, "file.vhd")) - design_unit.generic_names = ["runner_cfg"] - design_unit.add_architecture('arch2', file_name=join(tempdir, "arch2.vhd")) - try: - TestBench(design_unit) - except RuntimeError as exc: - self.assertEqual(str(exc), - "Test bench not allowed to have multiple architectures. " - "Entity tb_entity has arch:file.vhd, arch2:arch2.vhd") - else: - assert False, "RuntimeError not raised" - - @with_tempdir - def test_creates_tests_vhdl(self, tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd"), - contents='''\ -if run("Test 1") ---if run("Test 2") -if run("Test 3") -if run("Test 4") -if run("Test 5") or run("Test 6") -if run ("Test 7") -if run( "Test 8" ) -if ((run("Test 9"))) -if my_protected_variable.run("Test 10") -''') - design_unit.generic_names = ["runner_cfg"] - test_bench = TestBench(design_unit) - tests = self.create_tests(test_bench) - self.assert_has_tests(tests, - ["lib.tb_entity.Test 1", - "lib.tb_entity.Test 3", - "lib.tb_entity.Test 4", - "lib.tb_entity.Test 5", - "lib.tb_entity.Test 6", - "lib.tb_entity.Test 7", - "lib.tb_entity.Test 8", - "lib.tb_entity.Test 9"]) - - @with_tempdir - def test_creates_tests_verilog(self, tempdir): - design_unit = Module('tb_module', - file_name=join(tempdir, "file.v"), - contents='''\ -`TEST_CASE("Test 1") -`TEST_CASE ("Test 2") -`TEST_CASE( "Test 3" ) -// `TEST_CASE("Test 4") -/* - `TEST_CASE("Test 5") -*/ -/* `TEST_CASE("Test 6") */ -`TEST_CASE("Test 7") -/* comment */ -`TEST_CASE("Test 8") -/* comment */ -''') - design_unit.generic_names = ["runner_cfg"] - test_bench = TestBench(design_unit) - tests = self.create_tests(test_bench) - self.assert_has_tests(tests, - ["lib.tb_module.Test 1", - "lib.tb_module.Test 2", - "lib.tb_module.Test 3", - "lib.tb_module.Test 7", - "lib.tb_module.Test 8"]) - - @with_tempdir - def test_keyerror_on_non_existent_test(self, tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd"), - contents='if run("Test")') - design_unit.generic_names = ["runner_cfg", "name"] - test_bench = TestBench(design_unit) - self.assertRaises(KeyError, test_bench.get_test_case, "Non existent test") - - @with_tempdir - def test_creates_tests_when_adding_architecture_late(self, tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd"), - no_arch=True) - design_unit.generic_names = ["runner_cfg"] - test_bench = TestBench(design_unit) - - design_unit.add_architecture("arch", - file_name=join(tempdir, "arch.vhd"), - contents='''\ -if run("Test_1") ---if run("Test_2") -if run("Test_3") -''') - tests = self.create_tests(test_bench) - self.assert_has_tests(tests, - ["lib.tb_entity.Test_1", - "lib.tb_entity.Test_3"]) - - @with_tempdir - def test_scan_tests_from_file(self, tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd")) - design_unit.generic_names = ["runner_cfg"] - test_bench = TestBench(design_unit) - - file_name = join(tempdir, "file.vhd") - write_file(file_name, '''\ -if run("Test_1") -if run("Test_2") -''') - test_bench.scan_tests_from_file(file_name) - tests = self.create_tests(test_bench) - self.assert_has_tests(tests, - ["lib.tb_entity.Test_1", - "lib.tb_entity.Test_2"]) - - @with_tempdir - def test_scan_tests_from_file_location_unix(self, tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd")) - design_unit.generic_names = ["runner_cfg"] - test_bench = TestBench(design_unit) - - file_name = join(tempdir, "file.vhd") - code = 'foo \n bar \n if run("Test_1")' - write_file(file_name, code) - test_bench.scan_tests_from_file(file_name) - tests = self.create_tests(test_bench) - test_info = tests[0].test_information - location = test_info['lib.tb_entity.Test_1'].location - assert location.offset == code.find("Test_1") - assert location.length == len("Test_1") - - @with_tempdir - def test_scan_tests_from_file_location_dos(self, tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd")) - design_unit.generic_names = ["runner_cfg"] - test_bench = TestBench(design_unit) - - file_name = join(tempdir, "file.vhd") - code = 'foo \r\n bar \r\n if run("Test_1")' - write_file(file_name, code) - test_bench.scan_tests_from_file(file_name) - tests = self.create_tests(test_bench) - test_info = tests[0].test_information - location = test_info['lib.tb_entity.Test_1'].location - assert location.offset == code.find("Test_1") - assert location.length == len("Test_1") - - @with_tempdir - def test_scan_tests_from_missing_file(self, tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd")) - design_unit.generic_names = ["runner_cfg"] - test_bench = TestBench(design_unit) - - try: - test_bench.scan_tests_from_file("missing.vhd") - except ValueError as exc: - self.assertEqual(str(exc), - "File 'missing.vhd' does not exist") - else: - assert False, "ValueError not raised" - - @with_tempdir - def test_does_not_add_all_suffix_with_named_configurations(self, tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd")) - design_unit.generic_names = ["runner_cfg"] - test_bench = TestBench(design_unit) - - tests = self.create_tests(test_bench) - self.assert_has_tests(tests, ["lib.tb_entity.all"]) - - test_bench.add_config(name="config", generics=dict()) - tests = self.create_tests(test_bench) - self.assert_has_tests(tests, ["lib.tb_entity.config"]) - - @with_tempdir - def test_that_run_in_same_simulation_attribute_works(self, tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd"), - contents='''\ --- vunit: run_all_in_same_sim -if run("Test_1") -if run("Test_2") ---if run("Test_3") -''') - design_unit.generic_names = ["runner_cfg"] - test_bench = TestBench(design_unit) - tests = self.create_tests(test_bench) - self.assert_has_tests(tests, - [("lib.tb_entity", ("lib.tb_entity.Test_1", "lib.tb_entity.Test_2"))]) - - @with_tempdir - def test_add_config(self, tempdir): - design_unit = Entity('tb_entity', file_name=join(tempdir, "file.vhd")) - design_unit.generic_names = ["runner_cfg", "value", "global_value"] - test_bench = TestBench(design_unit) - - test_bench.set_generic("global_value", "global value") - - test_bench.add_config(name="value=1", - generics=dict(value=1, - global_value="local value")) - test_bench.add_config(name="value=2", - generics=dict(value=2), - attributes={".foo": "bar"}) - - self.assertRaises(AttributeException, test_bench.add_config, name="c3", - attributes={"foo", "bar"}) - - tests = self.create_tests(test_bench) - self.assert_has_tests(tests, ["lib.tb_entity.value=1", - "lib.tb_entity.value=2"]) - - self.assertEqual(get_config_of(tests, "lib.tb_entity.value=1").generics, - {"value": 1, "global_value": "local value"}) - self.assertEqual(get_config_of(tests, "lib.tb_entity.value=1").attributes, - {}) - self.assertEqual(get_config_of(tests, "lib.tb_entity.value=2").generics, - {"value": 2, "global_value": "global value"}) - self.assertEqual(get_config_of(tests, "lib.tb_entity.value=2").attributes, - {".foo": "bar"}) - - @with_tempdir - def test_test_case_add_config(self, tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd"), - contents=''' -if run("test 1") -if run("test 2") - ''') - design_unit.generic_names = ["runner_cfg", "value", "global_value"] - test_bench = TestBench(design_unit) - test_bench.set_generic("global_value", "global value") - test_bench.set_sim_option("disable_ieee_warnings", True) - - test_case = test_bench.get_test_case('test 2') - test_case.add_config(name="c1", - generics=dict(value=1, - global_value="local value")) - test_case.add_config(name="c2", - generics=dict(value=2), - sim_options=dict(disable_ieee_warnings=False), - attributes={".foo": "bar"}) - - self.assertRaises(AttributeException, test_case.add_config, name="c3", - attributes={"foo", "bar"}) - - tests = self.create_tests(test_bench) - self.assert_has_tests(tests, ["lib.tb_entity.test 1", - "lib.tb_entity.c1.test 2", - "lib.tb_entity.c2.test 2"]) - - config_test1 = get_config_of(tests, "lib.tb_entity.test 1") - config_c1_test2 = get_config_of(tests, "lib.tb_entity.c1.test 2") - config_c2_test2 = get_config_of(tests, "lib.tb_entity.c2.test 2") - self.assertEqual(config_test1.generics, - {"global_value": "global value"}) - self.assertEqual(config_c1_test2.attributes, - {}) - self.assertEqual(config_c1_test2.generics, - {"value": 1, "global_value": "local value"}) - self.assertEqual(config_c2_test2.attributes, - {".foo": "bar"}) - self.assertEqual(config_c2_test2.generics, - {"value": 2, "global_value": "global value"}) - - @with_tempdir - def test_runtime_error_on_configuration_of_individual_test_with_same_sim(self, tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd"), - contents='''\ --- vunit: run_all_in_same_sim -if run("Test 1") -if run("Test 2") -''') - design_unit.generic_names = ["runner_cfg", "name"] - test_bench = TestBench(design_unit) - test_case = test_bench.get_test_case("Test 1") - self.assertRaises(RuntimeError, test_case.set_generic, "name", "value") - self.assertRaises(RuntimeError, test_case.set_sim_option, "name", "value") - self.assertRaises(RuntimeError, test_case.add_config, "name") - - @with_tempdir - def test_run_all_in_same_sim_can_be_configured(self, tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd"), - contents='''\ --- vunit: run_all_in_same_sim -if run("Test 1") -if run("Test 2") -''') - design_unit.generic_names = ["runner_cfg", "name"] - test_bench = TestBench(design_unit) - test_bench.set_generic("name", "value") - test_bench.add_config("cfg") - tests = self.create_tests(test_bench) - self.assert_has_tests(tests, - [("lib.tb_entity.cfg", - ("lib.tb_entity.cfg.Test 1", "lib.tb_entity.cfg.Test 2"))]) - self.assertEqual(get_config_of(tests, "lib.tb_entity.cfg").generics, - {"name": "value"}) - - @with_tempdir - def test_global_user_attributes_not_supported_yet(self, tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd"), - contents='''\ --- vunit: .attr0 -if run("Test 1") -if run("Test 2") -''') - design_unit.generic_names = ["runner_cfg"] - - try: - TestBench(design_unit) - except RuntimeError as exc: - self.assertEqual(str(exc), - "File global attributes are not yet supported: .attr0 in %s line 1" - % join(tempdir, "file.vhd")) - else: - assert False, "RuntimeError not raised" - - @with_tempdir - def test_error_on_global_attributes_on_tests(self, tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd"), - contents='''\ -if run("Test 1") --- vunit: run_all_in_same_sim -if run("Test 2") -''') - design_unit.generic_names = ["runner_cfg"] - - try: - TestBench(design_unit) - except RuntimeError as exc: - self.assertEqual( - str(exc), - "Attribute run_all_in_same_sim is global and cannot be associated with test Test 1: %s line 2" - % join(tempdir, "file.vhd")) - else: - assert False, "RuntimeError not raised" - - @with_tempdir - def test_test_information(self, tempdir): - file_name = join(tempdir, "file.vhd") - - for same_sim in [True, False]: - contents = get_vhdl_test_bench("tb_entity", - tests=["Test 1", "Test 2"], - same_sim=same_sim) - - design_unit = Entity('tb_entity', - file_name=file_name, - contents=contents) - design_unit.generic_names = ["runner_cfg", "name"] - test_bench = TestBench(design_unit) - test_suites = self.create_tests(test_bench) - - if same_sim: - self.assertEqual(len(test_suites), 1) - else: - self.assertEqual(len(test_suites), 2) - - self.assertEqual(set(item - for test_suite in test_suites - for item in test_suite.test_information.items()), - set([("lib.tb_entity.Test 1", Test("Test 1", _file_location(file_name, 'Test 1'))), - ("lib.tb_entity.Test 2", Test("Test 2", _file_location(file_name, 'Test 2')))])) - - @with_tempdir - def test_fail_on_unknown_sim_option(self, tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd")) - design_unit.generic_names = ["runner_cfg"] - test_bench = TestBench(design_unit) - self.assertRaises(ValueError, test_bench.set_sim_option, "unknown", "value") - - def test_remove_verilog_comments(self): - self.assertEqual(_remove_verilog_comments("a\n// foo \nb"), - "a\n \nb") - self.assertEqual(_remove_verilog_comments("a\n/* foo\n \n */ \nb"), - "a\n \n \n \nb") - - def test_get_line_offsets(self): - self.assertEqual(_get_line_offsets(""), []) - self.assertEqual(_get_line_offsets("1"), [0]) - self.assertEqual(_get_line_offsets("12\n3"), [0, 3]) - self.assertEqual(_get_line_offsets("12\n3\n"), [0, 3]) - self.assertEqual(_get_line_offsets("12\n3\n4"), [0, 3, 5]) - - def test_lookup_lineno(self): - offsets = _get_line_offsets("12\n3\n4") - self.assertEqual(_lookup_lineno(0, offsets), 1) - self.assertEqual(_lookup_lineno(1, offsets), 1) - self.assertEqual(_lookup_lineno(2, offsets), 1) - self.assertEqual(_lookup_lineno(3, offsets), 2) - self.assertEqual(_lookup_lineno(4, offsets), 2) - self.assertEqual(_lookup_lineno(5, offsets), 3) - - def test_find_explicit_tests_vhdl(self): - test1, test2 = _find_tests("""\ - -- if run("No test") - if run("Test 1") - if run("Test 2") - """, file_name="file_name.vhd") - - self.assertEqual(test1.name, "Test 1") - self.assertEqual(test1.location.file_name, "file_name.vhd") - self.assertEqual(test1.location.lineno, 2) - - self.assertEqual(test2.name, "Test 2") - self.assertEqual(test2.location.file_name, "file_name.vhd") - self.assertEqual(test2.location.lineno, 3) - - def test_find_explicit_tests_verilog(self): - test1, test2 = _find_tests("""\ - /* `TEST_CASE("No test") - - */ - // `TEST_CASE("No test") - `TEST_CASE("Test 1") - `TEST_CASE("Test 2") - """, file_name="file_name.sv") - - self.assertEqual(test1.name, "Test 1") - self.assertEqual(test1.location.file_name, "file_name.sv") - self.assertEqual(test1.location.lineno, 5) - - self.assertEqual(test2.name, "Test 2") - self.assertEqual(test2.location.file_name, "file_name.sv") - self.assertEqual(test2.location.lineno, 6) - - def test_find_implicit_test_vhdl(self): - with mock.patch("vunit.test_bench.LOGGER") as logger: - test, = _find_tests("""\ - - test_runner_setup( - """, file_name="file_name.vhd") - self.assertEqual(test.name, None) - self.assertEqual(test.location.file_name, "file_name.vhd") - self.assertEqual(test.location.lineno, 2) - assert not logger.warning.called - - with mock.patch("vunit.test_bench.LOGGER") as logger: - test, = _find_tests("""\ - - test_runner_setup - """, file_name="file_name.vhd") - self.assertEqual(test.name, None) - self.assertEqual(test.location.file_name, "file_name.vhd") - self.assertEqual(test.location.lineno, 1) - assert logger.warning.called - - with mock.patch("vunit.test_bench.LOGGER") as logger: - test, = _find_tests("""\ - - """, file_name="file_name.vhd") - self.assertEqual(test.name, None) - self.assertEqual(test.location.file_name, "file_name.vhd") - self.assertEqual(test.location.lineno, 1) - assert logger.warning.called - - def test_find_implicit_test_verilog(self): - with mock.patch("vunit.test_bench.LOGGER") as logger: - test, = _find_tests("""\ - - `TEST_SUITE""", file_name="file_name.sv") - self.assertEqual(test.name, None) - self.assertEqual(test.location.file_name, "file_name.sv") - self.assertEqual(test.location.lineno, 2) - assert not logger.warning.called - - with mock.patch("vunit.test_bench.LOGGER") as logger: - test, = _find_tests("""\ - TEST_SUITE - """, file_name="file_name.sv") - self.assertEqual(test.name, None) - self.assertEqual(test.location.file_name, "file_name.sv") - self.assertEqual(test.location.lineno, 1) - assert logger.warning.called - - with mock.patch("vunit.test_bench.LOGGER") as logger: - test, = _find_tests("""\ - """, file_name="file_name.sv") - self.assertEqual(test.name, None) - self.assertEqual(test.location.file_name, "file_name.sv") - self.assertEqual(test.location.lineno, 1) - assert logger.warning.called - - @mock.patch("vunit.test_bench.LOGGER") - def test_duplicate_tests_cause_error(self, mock_logger): - file_name = "file.vhd" - self.assertRaises(RuntimeError, _find_tests, '''\ -if run("Test_1") ---if run("Test_1") -if run("Test_3") -if run("Test_2") -if run("Test_3") -if run("Test_3") -if run("Test_2") - ''', file_name=file_name) - - error_calls = mock_logger.error.call_args_list - self.assertEqual(len(error_calls), 3) - - msg = error_calls[0][0][0] % error_calls[0][0][1:] - self.assertEqual(msg, 'Duplicate test "Test_3" in %s line 5 previously defined on line 3' % file_name) - - msg = error_calls[1][0][0] % error_calls[1][0][1:] - self.assertEqual(msg, 'Duplicate test "Test_3" in %s line 6 previously defined on line 3' % file_name) - - msg = error_calls[2][0][0] % error_calls[2][0][1:] - self.assertEqual(msg, 'Duplicate test "Test_2" in %s line 7 previously defined on line 4' % file_name) - - def test_find_attributes(self): - code = """ -// vunit: run_all_in_same_sim -// vunit:fail_on_warning - """ - - attributes = _find_attributes(code, file_name="file.vhd") - self.assertEqual(attributes, [Attribute("run_all_in_same_sim", None, - _code_file_location(code, 'run_all_in_same_sim', "file.vhd")), - Attribute("fail_on_warning", None, - _code_file_location(code, 'fail_on_warning', "file.vhd"))]) - - def test_find_user_attributes(self): - code = """ -// vunit: .foo -// vunit: .foo-bar - """ - attributes = _find_attributes(code, file_name="file.vhd") - - self.assertEqual(attributes, [Attribute(".foo", None, - _code_file_location(code, ".foo", "file.vhd")), - Attribute(".foo-bar", None, - _code_file_location(code, ".foo-bar", "file.vhd"))]) - - def test_invalid_attributes(self): - try: - _find_attributes("""\ - - -// vunit: invalid - """, file_name="file.vhd") - except RuntimeError as exc: - self.assertEqual(str(exc), "Invalid attribute 'invalid' in file.vhd line 3") - else: - assert False, "RuntimeError not raised" - - def test_find_legacy_pragma(self): - code = """ -// vunit_pragma run_all_in_same_sim -// vunit_pragma fail_on_warning - """ - attributes = _find_attributes(code, file_name="file.vhd") - - self.assertEqual(attributes, [LegacyAttribute("run_all_in_same_sim", None, - _code_file_location(code, 'run_all_in_same_sim', "file.vhd")), - LegacyAttribute("fail_on_warning", None, - _code_file_location(code, 'fail_on_warning', "file.vhd"))]) - - def test_associate_tests_and_attributes(self): - (test1, test2), attributes = _find_tests_and_attributes("""\ -// vunit: .arg0 - if run("test1") -// vunit: .arg1 -// vunit: .arg1b - if run("test2") // vunit: .arg2 -// vunit: .arg2b - """, file_name="file.vhd") - - self.assertEqual([attr.name for attr in attributes], [".arg0"]) - - self.assertEqual(test1.name, "test1") - self.assertEqual(test1.location.file_name, "file.vhd") - self.assertEqual(test1.location.lineno, 2) - self.assertEqual([attr.name for attr in test1.attributes], [".arg1", ".arg1b"]) - - self.assertEqual(test2.name, "test2") - self.assertEqual(test2.location.file_name, "file.vhd") - self.assertEqual(test2.location.lineno, 5) - self.assertEqual([attr.name for attr in test2.attributes], [".arg2", ".arg2b"]) - - def test_duplicate_attributes_ok(self): - (test1, test2), attributes = _find_tests_and_attributes("""\ -// vunit: .arg0 - if run("test1") -// vunit: .arg0 - if run("test2") -// vunit: .arg0 - """, file_name="file.vhd") - - self.assertEqual([attr.name for attr in attributes], [".arg0"]) - - self.assertEqual(test1.name, "test1") - self.assertEqual(test1.location.file_name, "file.vhd") - self.assertEqual(test1.location.lineno, 2) - self.assertEqual([attr.name for attr in test1.attributes], [".arg0"]) - - self.assertEqual(test2.name, "test2") - self.assertEqual(test2.location.file_name, "file.vhd") - self.assertEqual(test2.location.lineno, 4) - self.assertEqual([attr.name for attr in test2.attributes], [".arg0"]) - - def test_duplicate_test_attributes_not_ok(self): - try: - _find_tests_and_attributes("""\ - if run("test1") -// vunit: .arg0 -// vunit: .arg0 - """, file_name="file.vhd") - except RuntimeError as exc: - self.assertEqual(str(exc), - "Duplicate attribute .arg0 of test test1 in file.vhd line 3, previously defined on line 2") - else: - assert False, "RuntimeError not raised" - - def test_duplicate_global_attributes_not_ok(self): - try: - _find_tests_and_attributes("""\ -// vunit: .arg0 -// vunit: .arg0 - if run("test1") - """, file_name="file.vhd") - except RuntimeError as exc: - self.assertEqual(str(exc), - "Duplicate attribute .arg0 of file.vhd line 2, previously defined on line 1") - else: - assert False, "RuntimeError not raised" - - def test_does_not_associate_tests_and_legacy_attributes(self): - code = """\ - if run("test1") -// vunit_pragma run_all_in_same_sim -// vunit_pragma fail_on_warning - """ - (test1,), attributes = _find_tests_and_attributes(code, file_name="file.vhd") - - self.assertEqual(attributes, [LegacyAttribute("run_all_in_same_sim", None, - _code_file_location(code, "run_all_in_same_sim", "file.vhd")), - LegacyAttribute("fail_on_warning", None, - _code_file_location(code, "fail_on_warning", "file.vhd"))]) - - self.assertEqual(test1.name, "test1") - self.assertEqual(test1.location.file_name, "file.vhd") - self.assertEqual(test1.location.lineno, 1) - self.assertEqual(test1.attributes, []) - - def assert_has_tests(self, test_list, tests): - """ - Asser that the test_list contains tests. - A test can be either a string to represent a single test or a - tuple to represent multiple tests within a test suite. - """ - self.assertEqual(len(test_list), len(tests)) - for test1, test2 in zip(test_list, tests): - if isinstance(test2, tuple): - name, test_cases = test2 - self.assertEqual(test1.name, name) - self.assertEqual(test1.test_names, list(test_cases)) - else: - self.assertEqual(test1.name, test2) - self.assertEqual(test1.test_names, [test2]) - - @staticmethod - def create_tests(test_bench): - """ - Helper method to reduce boiler plate - """ - return test_bench.create_tests("simulator_if", elaborate_only=False) - - -def get_config_of(tests, test_name): - """ - Find generic values of test - """ - for test in tests: - if test.name == test_name: - try: - return test._test_case._run._config # pylint: disable=protected-access - except AttributeError: - return test._run._config # pylint: disable=protected-access - raise KeyError(test_name) - - -class Module(object): - """ - Mock Module - """ - def __init__(self, name, file_name, contents=''): - self.name = name - self.library_name = "lib" - self.is_entity = False - self.is_module = True - self.generic_names = [] - self.file_name = file_name - self.original_file_name = file_name - write_file(file_name, contents) - - -class Entity(object): # pylint: disable=too-many-instance-attributes - """ - Mock Entity - """ - def __init__(self, name, file_name, contents='', no_arch=False): - self.name = name - self.library_name = "lib" - self.is_entity = True - self.is_module = False - self.architecture_names = {} - - if not no_arch: - self.architecture_names = {"arch": file_name} - - self.generic_names = [] - self.file_name = file_name - self.original_file_name = file_name - write_file(file_name, contents) - self._add_architecture_callback = None - - def set_add_architecture_callback(self, callback): - """ - Set callback to be called when an architecture is added - """ - assert self._add_architecture_callback is None - self._add_architecture_callback = callback - - def add_architecture(self, name, file_name, contents=""): - """ - Add architecture to this entity - """ - self.architecture_names[name] = file_name - write_file(file_name, contents) - - if self._add_architecture_callback is not None: - self._add_architecture_callback() - - -def _code_file_location(code, string, file_name=None): - """ - Create a FileLocation by finding the string within the code - """ - offset = code.find(string) - return FileLocation(file_name=file_name, - offset=offset, - length=len(string), - lineno=1 + sum((1 for char in code[:offset] if char == '\n'))) - - -def _file_location(file_name, string): - """ - Create a FileLocation by finding the string within file_name - """ - - with open(file_name, "r") as fptr: - code = fptr.read() - return _code_file_location(code, string, file_name) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +# pylint: disable=too-many-public-methods + +""" +Tests the test test_bench module +""" + + +import unittest +from os.path import join + +from vunit.test_bench import (TestBench, + _remove_verilog_comments, + _find_tests, + _get_line_offsets, + _lookup_lineno, + _find_attributes, + _find_tests_and_attributes, + Test, + FileLocation, + Attribute, + LegacyAttribute) +from vunit.configuration import AttributeException +from vunit.ostools import write_file +from vunit.test.mock_2or3 import mock +from vunit.test.common import (with_tempdir, + get_vhdl_test_bench) + + +class TestTestBench(unittest.TestCase): + """ + Tests the test bench + """ + + @with_tempdir + def test_that_single_vhdl_test_is_created(self, tempdir): + design_unit = Entity('tb_entity', file_name=join(tempdir, "file.vhd")) + test_bench = TestBench(design_unit) + tests = self.create_tests(test_bench) + self.assert_has_tests(tests, ["lib.tb_entity.all"]) + + @staticmethod + @with_tempdir + def test_no_architecture_at_creation(tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd"), + no_arch=True) + TestBench(design_unit) + + @with_tempdir + def test_no_architecture_gives_runtime_error(self, tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd"), + no_arch=True) + test_bench = TestBench(design_unit) + try: + self.create_tests(test_bench) + except RuntimeError as exc: + self.assertEqual(str(exc), + "Test bench 'tb_entity' has no architecture.") + else: + assert False, "RuntimeError not raised" + + @with_tempdir + def test_that_single_verilog_test_is_created(self, tempdir): + design_unit = Module('tb_module', file_name=join(tempdir, "file.v")) + test_bench = TestBench(design_unit) + tests = self.create_tests(test_bench) + self.assert_has_tests(tests, ["lib.tb_module.all"]) + + @with_tempdir + def test_create_default_test(self, tempdir): + design_unit = Entity('tb_entity', file_name=join(tempdir, "file.vhd")) + design_unit.generic_names = ["runner_cfg"] + test_bench = TestBench(design_unit) + tests = self.create_tests(test_bench) + self.assert_has_tests(tests, ["lib.tb_entity.all"]) + + @with_tempdir + def test_multiple_architectures_are_not_allowed_for_test_bench(self, tempdir): + design_unit = Entity('tb_entity', file_name=join(tempdir, "file.vhd")) + design_unit.generic_names = ["runner_cfg"] + design_unit.add_architecture('arch2', file_name=join(tempdir, "arch2.vhd")) + try: + TestBench(design_unit) + except RuntimeError as exc: + self.assertEqual(str(exc), + "Test bench not allowed to have multiple architectures. " + "Entity tb_entity has arch:file.vhd, arch2:arch2.vhd") + else: + assert False, "RuntimeError not raised" + + @with_tempdir + def test_creates_tests_vhdl(self, tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd"), + contents='''\ +if run("Test 1") +--if run("Test 2") +if run("Test 3") +if run("Test 4") +if run("Test 5") or run("Test 6") +if run ("Test 7") +if run( "Test 8" ) +if ((run("Test 9"))) +if my_protected_variable.run("Test 10") +''') + design_unit.generic_names = ["runner_cfg"] + test_bench = TestBench(design_unit) + tests = self.create_tests(test_bench) + self.assert_has_tests(tests, + ["lib.tb_entity.Test 1", + "lib.tb_entity.Test 3", + "lib.tb_entity.Test 4", + "lib.tb_entity.Test 5", + "lib.tb_entity.Test 6", + "lib.tb_entity.Test 7", + "lib.tb_entity.Test 8", + "lib.tb_entity.Test 9"]) + + @with_tempdir + def test_creates_tests_verilog(self, tempdir): + design_unit = Module('tb_module', + file_name=join(tempdir, "file.v"), + contents='''\ +`TEST_CASE("Test 1") +`TEST_CASE ("Test 2") +`TEST_CASE( "Test 3" ) +// `TEST_CASE("Test 4") +/* + `TEST_CASE("Test 5") +*/ +/* `TEST_CASE("Test 6") */ +`TEST_CASE("Test 7") +/* comment */ +`TEST_CASE("Test 8") +/* comment */ +''') + design_unit.generic_names = ["runner_cfg"] + test_bench = TestBench(design_unit) + tests = self.create_tests(test_bench) + self.assert_has_tests(tests, + ["lib.tb_module.Test 1", + "lib.tb_module.Test 2", + "lib.tb_module.Test 3", + "lib.tb_module.Test 7", + "lib.tb_module.Test 8"]) + + @with_tempdir + def test_keyerror_on_non_existent_test(self, tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd"), + contents='if run("Test")') + design_unit.generic_names = ["runner_cfg", "name"] + test_bench = TestBench(design_unit) + self.assertRaises(KeyError, test_bench.get_test_case, "Non existent test") + + @with_tempdir + def test_creates_tests_when_adding_architecture_late(self, tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd"), + no_arch=True) + design_unit.generic_names = ["runner_cfg"] + test_bench = TestBench(design_unit) + + design_unit.add_architecture("arch", + file_name=join(tempdir, "arch.vhd"), + contents='''\ +if run("Test_1") +--if run("Test_2") +if run("Test_3") +''') + tests = self.create_tests(test_bench) + self.assert_has_tests(tests, + ["lib.tb_entity.Test_1", + "lib.tb_entity.Test_3"]) + + @with_tempdir + def test_scan_tests_from_file(self, tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd")) + design_unit.generic_names = ["runner_cfg"] + test_bench = TestBench(design_unit) + + file_name = join(tempdir, "file.vhd") + write_file(file_name, '''\ +if run("Test_1") +if run("Test_2") +''') + test_bench.scan_tests_from_file(file_name) + tests = self.create_tests(test_bench) + self.assert_has_tests(tests, + ["lib.tb_entity.Test_1", + "lib.tb_entity.Test_2"]) + + @with_tempdir + def test_scan_tests_from_file_location_unix(self, tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd")) + design_unit.generic_names = ["runner_cfg"] + test_bench = TestBench(design_unit) + + file_name = join(tempdir, "file.vhd") + code = 'foo \n bar \n if run("Test_1")' + write_file(file_name, code) + test_bench.scan_tests_from_file(file_name) + tests = self.create_tests(test_bench) + test_info = tests[0].test_information + location = test_info['lib.tb_entity.Test_1'].location + assert location.offset == code.find("Test_1") + assert location.length == len("Test_1") + + @with_tempdir + def test_scan_tests_from_file_location_dos(self, tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd")) + design_unit.generic_names = ["runner_cfg"] + test_bench = TestBench(design_unit) + + file_name = join(tempdir, "file.vhd") + code = 'foo \r\n bar \r\n if run("Test_1")' + write_file(file_name, code) + test_bench.scan_tests_from_file(file_name) + tests = self.create_tests(test_bench) + test_info = tests[0].test_information + location = test_info['lib.tb_entity.Test_1'].location + assert location.offset == code.find("Test_1") + assert location.length == len("Test_1") + + @with_tempdir + def test_scan_tests_from_missing_file(self, tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd")) + design_unit.generic_names = ["runner_cfg"] + test_bench = TestBench(design_unit) + + try: + test_bench.scan_tests_from_file("missing.vhd") + except ValueError as exc: + self.assertEqual(str(exc), + "File 'missing.vhd' does not exist") + else: + assert False, "ValueError not raised" + + @with_tempdir + def test_does_not_add_all_suffix_with_named_configurations(self, tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd")) + design_unit.generic_names = ["runner_cfg"] + test_bench = TestBench(design_unit) + + tests = self.create_tests(test_bench) + self.assert_has_tests(tests, ["lib.tb_entity.all"]) + + test_bench.add_config(name="config", generics=dict()) + tests = self.create_tests(test_bench) + self.assert_has_tests(tests, ["lib.tb_entity.config"]) + + @with_tempdir + def test_that_run_in_same_simulation_attribute_works(self, tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd"), + contents='''\ +-- vunit: run_all_in_same_sim +if run("Test_1") +if run("Test_2") +--if run("Test_3") +''') + design_unit.generic_names = ["runner_cfg"] + test_bench = TestBench(design_unit) + tests = self.create_tests(test_bench) + self.assert_has_tests(tests, + [("lib.tb_entity", ("lib.tb_entity.Test_1", "lib.tb_entity.Test_2"))]) + + @with_tempdir + def test_add_config(self, tempdir): + design_unit = Entity('tb_entity', file_name=join(tempdir, "file.vhd")) + design_unit.generic_names = ["runner_cfg", "value", "global_value"] + test_bench = TestBench(design_unit) + + test_bench.set_generic("global_value", "global value") + + test_bench.add_config(name="value=1", + generics=dict(value=1, + global_value="local value")) + test_bench.add_config(name="value=2", + generics=dict(value=2), + attributes={".foo": "bar"}) + + self.assertRaises(AttributeException, test_bench.add_config, name="c3", + attributes={"foo", "bar"}) + + tests = self.create_tests(test_bench) + self.assert_has_tests(tests, ["lib.tb_entity.value=1", + "lib.tb_entity.value=2"]) + + self.assertEqual(get_config_of(tests, "lib.tb_entity.value=1").generics, + {"value": 1, "global_value": "local value"}) + self.assertEqual(get_config_of(tests, "lib.tb_entity.value=1").attributes, + {}) + self.assertEqual(get_config_of(tests, "lib.tb_entity.value=2").generics, + {"value": 2, "global_value": "global value"}) + self.assertEqual(get_config_of(tests, "lib.tb_entity.value=2").attributes, + {".foo": "bar"}) + + @with_tempdir + def test_test_case_add_config(self, tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd"), + contents=''' +if run("test 1") +if run("test 2") + ''') + design_unit.generic_names = ["runner_cfg", "value", "global_value"] + test_bench = TestBench(design_unit) + test_bench.set_generic("global_value", "global value") + test_bench.set_sim_option("disable_ieee_warnings", True) + + test_case = test_bench.get_test_case('test 2') + test_case.add_config(name="c1", + generics=dict(value=1, + global_value="local value")) + test_case.add_config(name="c2", + generics=dict(value=2), + sim_options=dict(disable_ieee_warnings=False), + attributes={".foo": "bar"}) + + self.assertRaises(AttributeException, test_case.add_config, name="c3", + attributes={"foo", "bar"}) + + tests = self.create_tests(test_bench) + self.assert_has_tests(tests, ["lib.tb_entity.test 1", + "lib.tb_entity.c1.test 2", + "lib.tb_entity.c2.test 2"]) + + config_test1 = get_config_of(tests, "lib.tb_entity.test 1") + config_c1_test2 = get_config_of(tests, "lib.tb_entity.c1.test 2") + config_c2_test2 = get_config_of(tests, "lib.tb_entity.c2.test 2") + self.assertEqual(config_test1.generics, + {"global_value": "global value"}) + self.assertEqual(config_c1_test2.attributes, + {}) + self.assertEqual(config_c1_test2.generics, + {"value": 1, "global_value": "local value"}) + self.assertEqual(config_c2_test2.attributes, + {".foo": "bar"}) + self.assertEqual(config_c2_test2.generics, + {"value": 2, "global_value": "global value"}) + + @with_tempdir + def test_runtime_error_on_configuration_of_individual_test_with_same_sim(self, tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd"), + contents='''\ +-- vunit: run_all_in_same_sim +if run("Test 1") +if run("Test 2") +''') + design_unit.generic_names = ["runner_cfg", "name"] + test_bench = TestBench(design_unit) + test_case = test_bench.get_test_case("Test 1") + self.assertRaises(RuntimeError, test_case.set_generic, "name", "value") + self.assertRaises(RuntimeError, test_case.set_sim_option, "name", "value") + self.assertRaises(RuntimeError, test_case.add_config, "name") + + @with_tempdir + def test_run_all_in_same_sim_can_be_configured(self, tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd"), + contents='''\ +-- vunit: run_all_in_same_sim +if run("Test 1") +if run("Test 2") +''') + design_unit.generic_names = ["runner_cfg", "name"] + test_bench = TestBench(design_unit) + test_bench.set_generic("name", "value") + test_bench.add_config("cfg") + tests = self.create_tests(test_bench) + self.assert_has_tests(tests, + [("lib.tb_entity.cfg", + ("lib.tb_entity.cfg.Test 1", "lib.tb_entity.cfg.Test 2"))]) + self.assertEqual(get_config_of(tests, "lib.tb_entity.cfg").generics, + {"name": "value"}) + + @with_tempdir + def test_global_user_attributes_not_supported_yet(self, tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd"), + contents='''\ +-- vunit: .attr0 +if run("Test 1") +if run("Test 2") +''') + design_unit.generic_names = ["runner_cfg"] + + try: + TestBench(design_unit) + except RuntimeError as exc: + self.assertEqual(str(exc), + "File global attributes are not yet supported: .attr0 in %s line 1" + % join(tempdir, "file.vhd")) + else: + assert False, "RuntimeError not raised" + + @with_tempdir + def test_error_on_global_attributes_on_tests(self, tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd"), + contents='''\ +if run("Test 1") +-- vunit: run_all_in_same_sim +if run("Test 2") +''') + design_unit.generic_names = ["runner_cfg"] + + try: + TestBench(design_unit) + except RuntimeError as exc: + self.assertEqual( + str(exc), + "Attribute run_all_in_same_sim is global and cannot be associated with test Test 1: %s line 2" + % join(tempdir, "file.vhd")) + else: + assert False, "RuntimeError not raised" + + @with_tempdir + def test_test_information(self, tempdir): + file_name = join(tempdir, "file.vhd") + + for same_sim in [True, False]: + contents = get_vhdl_test_bench("tb_entity", + tests=["Test 1", "Test 2"], + same_sim=same_sim) + + design_unit = Entity('tb_entity', + file_name=file_name, + contents=contents) + design_unit.generic_names = ["runner_cfg", "name"] + test_bench = TestBench(design_unit) + test_suites = self.create_tests(test_bench) + + if same_sim: + self.assertEqual(len(test_suites), 1) + else: + self.assertEqual(len(test_suites), 2) + + self.assertEqual(set(item + for test_suite in test_suites + for item in test_suite.test_information.items()), + set([("lib.tb_entity.Test 1", Test("Test 1", _file_location(file_name, 'Test 1'))), + ("lib.tb_entity.Test 2", Test("Test 2", _file_location(file_name, 'Test 2')))])) + + @with_tempdir + def test_fail_on_unknown_sim_option(self, tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd")) + design_unit.generic_names = ["runner_cfg"] + test_bench = TestBench(design_unit) + self.assertRaises(ValueError, test_bench.set_sim_option, "unknown", "value") + + def test_remove_verilog_comments(self): + self.assertEqual(_remove_verilog_comments("a\n// foo \nb"), + "a\n \nb") + self.assertEqual(_remove_verilog_comments("a\n/* foo\n \n */ \nb"), + "a\n \n \n \nb") + + def test_get_line_offsets(self): + self.assertEqual(_get_line_offsets(""), []) + self.assertEqual(_get_line_offsets("1"), [0]) + self.assertEqual(_get_line_offsets("12\n3"), [0, 3]) + self.assertEqual(_get_line_offsets("12\n3\n"), [0, 3]) + self.assertEqual(_get_line_offsets("12\n3\n4"), [0, 3, 5]) + + def test_lookup_lineno(self): + offsets = _get_line_offsets("12\n3\n4") + self.assertEqual(_lookup_lineno(0, offsets), 1) + self.assertEqual(_lookup_lineno(1, offsets), 1) + self.assertEqual(_lookup_lineno(2, offsets), 1) + self.assertEqual(_lookup_lineno(3, offsets), 2) + self.assertEqual(_lookup_lineno(4, offsets), 2) + self.assertEqual(_lookup_lineno(5, offsets), 3) + + def test_find_explicit_tests_vhdl(self): + test1, test2 = _find_tests("""\ + -- if run("No test") + if run("Test 1") + if run("Test 2") + """, file_name="file_name.vhd") + + self.assertEqual(test1.name, "Test 1") + self.assertEqual(test1.location.file_name, "file_name.vhd") + self.assertEqual(test1.location.lineno, 2) + + self.assertEqual(test2.name, "Test 2") + self.assertEqual(test2.location.file_name, "file_name.vhd") + self.assertEqual(test2.location.lineno, 3) + + def test_find_explicit_tests_verilog(self): + test1, test2 = _find_tests("""\ + /* `TEST_CASE("No test") + + */ + // `TEST_CASE("No test") + `TEST_CASE("Test 1") + `TEST_CASE("Test 2") + """, file_name="file_name.sv") + + self.assertEqual(test1.name, "Test 1") + self.assertEqual(test1.location.file_name, "file_name.sv") + self.assertEqual(test1.location.lineno, 5) + + self.assertEqual(test2.name, "Test 2") + self.assertEqual(test2.location.file_name, "file_name.sv") + self.assertEqual(test2.location.lineno, 6) + + def test_find_implicit_test_vhdl(self): + with mock.patch("vunit.test_bench.LOGGER") as logger: + test, = _find_tests("""\ + + test_runner_setup( + """, file_name="file_name.vhd") + self.assertEqual(test.name, None) + self.assertEqual(test.location.file_name, "file_name.vhd") + self.assertEqual(test.location.lineno, 2) + assert not logger.warning.called + + with mock.patch("vunit.test_bench.LOGGER") as logger: + test, = _find_tests("""\ + + test_runner_setup + """, file_name="file_name.vhd") + self.assertEqual(test.name, None) + self.assertEqual(test.location.file_name, "file_name.vhd") + self.assertEqual(test.location.lineno, 1) + assert logger.warning.called + + with mock.patch("vunit.test_bench.LOGGER") as logger: + test, = _find_tests("""\ + + """, file_name="file_name.vhd") + self.assertEqual(test.name, None) + self.assertEqual(test.location.file_name, "file_name.vhd") + self.assertEqual(test.location.lineno, 1) + assert logger.warning.called + + def test_find_implicit_test_verilog(self): + with mock.patch("vunit.test_bench.LOGGER") as logger: + test, = _find_tests("""\ + + `TEST_SUITE""", file_name="file_name.sv") + self.assertEqual(test.name, None) + self.assertEqual(test.location.file_name, "file_name.sv") + self.assertEqual(test.location.lineno, 2) + assert not logger.warning.called + + with mock.patch("vunit.test_bench.LOGGER") as logger: + test, = _find_tests("""\ + TEST_SUITE + """, file_name="file_name.sv") + self.assertEqual(test.name, None) + self.assertEqual(test.location.file_name, "file_name.sv") + self.assertEqual(test.location.lineno, 1) + assert logger.warning.called + + with mock.patch("vunit.test_bench.LOGGER") as logger: + test, = _find_tests("""\ + """, file_name="file_name.sv") + self.assertEqual(test.name, None) + self.assertEqual(test.location.file_name, "file_name.sv") + self.assertEqual(test.location.lineno, 1) + assert logger.warning.called + + @mock.patch("vunit.test_bench.LOGGER") + def test_duplicate_tests_cause_error(self, mock_logger): + file_name = "file.vhd" + self.assertRaises(RuntimeError, _find_tests, '''\ +if run("Test_1") +--if run("Test_1") +if run("Test_3") +if run("Test_2") +if run("Test_3") +if run("Test_3") +if run("Test_2") + ''', file_name=file_name) + + error_calls = mock_logger.error.call_args_list + self.assertEqual(len(error_calls), 3) + + msg = error_calls[0][0][0] % error_calls[0][0][1:] + self.assertEqual(msg, 'Duplicate test "Test_3" in %s line 5 previously defined on line 3' % file_name) + + msg = error_calls[1][0][0] % error_calls[1][0][1:] + self.assertEqual(msg, 'Duplicate test "Test_3" in %s line 6 previously defined on line 3' % file_name) + + msg = error_calls[2][0][0] % error_calls[2][0][1:] + self.assertEqual(msg, 'Duplicate test "Test_2" in %s line 7 previously defined on line 4' % file_name) + + def test_find_attributes(self): + code = """ +// vunit: run_all_in_same_sim +// vunit:fail_on_warning + """ + + attributes = _find_attributes(code, file_name="file.vhd") + self.assertEqual(attributes, [Attribute("run_all_in_same_sim", None, + _code_file_location(code, 'run_all_in_same_sim', "file.vhd")), + Attribute("fail_on_warning", None, + _code_file_location(code, 'fail_on_warning', "file.vhd"))]) + + def test_find_user_attributes(self): + code = """ +// vunit: .foo +// vunit: .foo-bar + """ + attributes = _find_attributes(code, file_name="file.vhd") + + self.assertEqual(attributes, [Attribute(".foo", None, + _code_file_location(code, ".foo", "file.vhd")), + Attribute(".foo-bar", None, + _code_file_location(code, ".foo-bar", "file.vhd"))]) + + def test_invalid_attributes(self): + try: + _find_attributes("""\ + + +// vunit: invalid + """, file_name="file.vhd") + except RuntimeError as exc: + self.assertEqual(str(exc), "Invalid attribute 'invalid' in file.vhd line 3") + else: + assert False, "RuntimeError not raised" + + def test_find_legacy_pragma(self): + code = """ +// vunit_pragma run_all_in_same_sim +// vunit_pragma fail_on_warning + """ + attributes = _find_attributes(code, file_name="file.vhd") + + self.assertEqual(attributes, [LegacyAttribute("run_all_in_same_sim", None, + _code_file_location(code, 'run_all_in_same_sim', "file.vhd")), + LegacyAttribute("fail_on_warning", None, + _code_file_location(code, 'fail_on_warning', "file.vhd"))]) + + def test_associate_tests_and_attributes(self): + (test1, test2), attributes = _find_tests_and_attributes("""\ +// vunit: .arg0 + if run("test1") +// vunit: .arg1 +// vunit: .arg1b + if run("test2") // vunit: .arg2 +// vunit: .arg2b + """, file_name="file.vhd") + + self.assertEqual([attr.name for attr in attributes], [".arg0"]) + + self.assertEqual(test1.name, "test1") + self.assertEqual(test1.location.file_name, "file.vhd") + self.assertEqual(test1.location.lineno, 2) + self.assertEqual([attr.name for attr in test1.attributes], [".arg1", ".arg1b"]) + + self.assertEqual(test2.name, "test2") + self.assertEqual(test2.location.file_name, "file.vhd") + self.assertEqual(test2.location.lineno, 5) + self.assertEqual([attr.name for attr in test2.attributes], [".arg2", ".arg2b"]) + + def test_duplicate_attributes_ok(self): + (test1, test2), attributes = _find_tests_and_attributes("""\ +// vunit: .arg0 + if run("test1") +// vunit: .arg0 + if run("test2") +// vunit: .arg0 + """, file_name="file.vhd") + + self.assertEqual([attr.name for attr in attributes], [".arg0"]) + + self.assertEqual(test1.name, "test1") + self.assertEqual(test1.location.file_name, "file.vhd") + self.assertEqual(test1.location.lineno, 2) + self.assertEqual([attr.name for attr in test1.attributes], [".arg0"]) + + self.assertEqual(test2.name, "test2") + self.assertEqual(test2.location.file_name, "file.vhd") + self.assertEqual(test2.location.lineno, 4) + self.assertEqual([attr.name for attr in test2.attributes], [".arg0"]) + + def test_duplicate_test_attributes_not_ok(self): + try: + _find_tests_and_attributes("""\ + if run("test1") +// vunit: .arg0 +// vunit: .arg0 + """, file_name="file.vhd") + except RuntimeError as exc: + self.assertEqual(str(exc), + "Duplicate attribute .arg0 of test test1 in file.vhd line 3, previously defined on line 2") + else: + assert False, "RuntimeError not raised" + + def test_duplicate_global_attributes_not_ok(self): + try: + _find_tests_and_attributes("""\ +// vunit: .arg0 +// vunit: .arg0 + if run("test1") + """, file_name="file.vhd") + except RuntimeError as exc: + self.assertEqual(str(exc), + "Duplicate attribute .arg0 of file.vhd line 2, previously defined on line 1") + else: + assert False, "RuntimeError not raised" + + def test_does_not_associate_tests_and_legacy_attributes(self): + code = """\ + if run("test1") +// vunit_pragma run_all_in_same_sim +// vunit_pragma fail_on_warning + """ + (test1,), attributes = _find_tests_and_attributes(code, file_name="file.vhd") + + self.assertEqual(attributes, [LegacyAttribute("run_all_in_same_sim", None, + _code_file_location(code, "run_all_in_same_sim", "file.vhd")), + LegacyAttribute("fail_on_warning", None, + _code_file_location(code, "fail_on_warning", "file.vhd"))]) + + self.assertEqual(test1.name, "test1") + self.assertEqual(test1.location.file_name, "file.vhd") + self.assertEqual(test1.location.lineno, 1) + self.assertEqual(test1.attributes, []) + + def assert_has_tests(self, test_list, tests): + """ + Asser that the test_list contains tests. + A test can be either a string to represent a single test or a + tuple to represent multiple tests within a test suite. + """ + self.assertEqual(len(test_list), len(tests)) + for test1, test2 in zip(test_list, tests): + if isinstance(test2, tuple): + name, test_cases = test2 + self.assertEqual(test1.name, name) + self.assertEqual(test1.test_names, list(test_cases)) + else: + self.assertEqual(test1.name, test2) + self.assertEqual(test1.test_names, [test2]) + + @staticmethod + def create_tests(test_bench): + """ + Helper method to reduce boiler plate + """ + return test_bench.create_tests("simulator_if", elaborate_only=False) + + +def get_config_of(tests, test_name): + """ + Find generic values of test + """ + for test in tests: + if test.name == test_name: + try: + return test._test_case._run._config # pylint: disable=protected-access + except AttributeError: + return test._run._config # pylint: disable=protected-access + raise KeyError(test_name) + + +class Module(object): + """ + Mock Module + """ + def __init__(self, name, file_name, contents=''): + self.name = name + self.library_name = "lib" + self.is_entity = False + self.is_module = True + self.generic_names = [] + self.file_name = file_name + self.original_file_name = file_name + write_file(file_name, contents) + + +class Entity(object): # pylint: disable=too-many-instance-attributes + """ + Mock Entity + """ + def __init__(self, name, file_name, contents='', no_arch=False): + self.name = name + self.library_name = "lib" + self.is_entity = True + self.is_module = False + self.architecture_names = {} + + if not no_arch: + self.architecture_names = {"arch": file_name} + + self.generic_names = [] + self.file_name = file_name + self.original_file_name = file_name + write_file(file_name, contents) + self._add_architecture_callback = None + + def set_add_architecture_callback(self, callback): + """ + Set callback to be called when an architecture is added + """ + assert self._add_architecture_callback is None + self._add_architecture_callback = callback + + def add_architecture(self, name, file_name, contents=""): + """ + Add architecture to this entity + """ + self.architecture_names[name] = file_name + write_file(file_name, contents) + + if self._add_architecture_callback is not None: + self._add_architecture_callback() + + +def _code_file_location(code, string, file_name=None): + """ + Create a FileLocation by finding the string within the code + """ + offset = code.find(string) + return FileLocation(file_name=file_name, + offset=offset, + length=len(string), + lineno=1 + sum((1 for char in code[:offset] if char == '\n'))) + + +def _file_location(file_name, string): + """ + Create a FileLocation by finding the string within file_name + """ + + with open(file_name, "r") as fptr: + code = fptr.read() + return _code_file_location(code, string, file_name) diff --git a/vunit/test/unit/test_test_bench_list.py b/vunit/test/unit/test_test_bench_list.py index 3c26c90e7..590e6c0cf 100644 --- a/vunit/test/unit/test_test_bench_list.py +++ b/vunit/test/unit/test_test_bench_list.py @@ -1,99 +1,99 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -# pylint: disable=too-many-public-methods - -""" -Tests the test test_bench module -""" - - -import unittest -from os.path import join - -from vunit.test_bench_list import TestBenchList, tb_filter -from vunit.test.unit.test_test_bench import Entity, Module -from vunit.test.mock_2or3 import mock -from vunit.test.common import with_tempdir - - -class TestTestBenchList(unittest.TestCase): - """ - Tests the test bench - """ - - def test_get_test_benches_in_empty_library(self): - tb_list = TestBenchList() - self.assertEqual(tb_list.get_test_benches_in_library("lib"), []) - - @with_tempdir - def test_tb_filter_requires_runner_cfg(self, tempdir): - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd")) - design_unit.generic_names = ["runner_cfg"] - self.assertTrue(tb_filter(design_unit)) - - design_unit = Entity('tb_entity', - file_name=join(tempdir, "file.vhd")) - design_unit.generic_names = [] - self.assertFalse(tb_filter(design_unit)) - - design_unit = Module('tb_module', - file_name=join(tempdir, "file.vhd")) - design_unit.generic_names = ["runner_cfg"] - self.assertTrue(tb_filter(design_unit)) - - design_unit = Module('tb_module', - file_name=join(tempdir, "file.vhd")) - design_unit.generic_names = [] - self.assertFalse(tb_filter(design_unit)) - - @with_tempdir - def test_tb_filter_match_prefix_and_suffix_only(self, tempdir): - """ - Issue #263 - """ - with mock.patch("vunit.test_bench_list.LOGGER", autospec=True) as logger: - design_unit = Entity("mul_tbl_scale", - file_name=join(tempdir, "file.vhd")) - self.assertFalse(tb_filter(design_unit)) - self.assertFalse(logger.warning.called) - - @with_tempdir - def test_tb_filter_warning_on_missing_runner_cfg_when_matching_tb_pattern(self, tempdir): - design_unit = Module('tb_module_not_ok', - file_name=join(tempdir, "file.vhd")) - design_unit.generic_names = [] - - with mock.patch("vunit.test_bench_list.LOGGER", autospec=True) as logger: - self.assertFalse(tb_filter(design_unit)) - logger.warning.assert_has_calls([ - mock.call('%s %s matches testbench name regex %s ' - 'but has no %s runner_cfg and will therefore not be run.\n' - 'in file %s', - 'Module', - 'tb_module_not_ok', - '^(tb_.*)|(.*_tb)$', - 'parameter', - design_unit.file_name)]) - - @with_tempdir - def test_tb_filter_warning_on_runner_cfg_but_not_matching_tb_pattern(self, tempdir): - design_unit = Entity('entity_ok_but_warning', - file_name=join(tempdir, "file.vhd")) - design_unit.generic_names = ["runner_cfg"] - - with mock.patch("vunit.test_bench_list.LOGGER", autospec=True) as logger: - self.assertTrue(tb_filter(design_unit)) - logger.warning.assert_has_calls([ - mock.call('%s %s has runner_cfg %s but the file name and the %s name does not match regex %s\n' - 'in file %s', - 'Entity', - 'entity_ok_but_warning', - 'generic', - 'entity', - '^(tb_.*)|(.*_tb)$', - design_unit.file_name)]) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +# pylint: disable=too-many-public-methods + +""" +Tests the test test_bench module +""" + + +import unittest +from os.path import join + +from vunit.test_bench_list import TestBenchList, tb_filter +from vunit.test.unit.test_test_bench import Entity, Module +from vunit.test.mock_2or3 import mock +from vunit.test.common import with_tempdir + + +class TestTestBenchList(unittest.TestCase): + """ + Tests the test bench + """ + + def test_get_test_benches_in_empty_library(self): + tb_list = TestBenchList() + self.assertEqual(tb_list.get_test_benches_in_library("lib"), []) + + @with_tempdir + def test_tb_filter_requires_runner_cfg(self, tempdir): + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd")) + design_unit.generic_names = ["runner_cfg"] + self.assertTrue(tb_filter(design_unit)) + + design_unit = Entity('tb_entity', + file_name=join(tempdir, "file.vhd")) + design_unit.generic_names = [] + self.assertFalse(tb_filter(design_unit)) + + design_unit = Module('tb_module', + file_name=join(tempdir, "file.vhd")) + design_unit.generic_names = ["runner_cfg"] + self.assertTrue(tb_filter(design_unit)) + + design_unit = Module('tb_module', + file_name=join(tempdir, "file.vhd")) + design_unit.generic_names = [] + self.assertFalse(tb_filter(design_unit)) + + @with_tempdir + def test_tb_filter_match_prefix_and_suffix_only(self, tempdir): + """ + Issue #263 + """ + with mock.patch("vunit.test_bench_list.LOGGER", autospec=True) as logger: + design_unit = Entity("mul_tbl_scale", + file_name=join(tempdir, "file.vhd")) + self.assertFalse(tb_filter(design_unit)) + self.assertFalse(logger.warning.called) + + @with_tempdir + def test_tb_filter_warning_on_missing_runner_cfg_when_matching_tb_pattern(self, tempdir): + design_unit = Module('tb_module_not_ok', + file_name=join(tempdir, "file.vhd")) + design_unit.generic_names = [] + + with mock.patch("vunit.test_bench_list.LOGGER", autospec=True) as logger: + self.assertFalse(tb_filter(design_unit)) + logger.warning.assert_has_calls([ + mock.call('%s %s matches testbench name regex %s ' + 'but has no %s runner_cfg and will therefore not be run.\n' + 'in file %s', + 'Module', + 'tb_module_not_ok', + '^(tb_.*)|(.*_tb)$', + 'parameter', + design_unit.file_name)]) + + @with_tempdir + def test_tb_filter_warning_on_runner_cfg_but_not_matching_tb_pattern(self, tempdir): + design_unit = Entity('entity_ok_but_warning', + file_name=join(tempdir, "file.vhd")) + design_unit.generic_names = ["runner_cfg"] + + with mock.patch("vunit.test_bench_list.LOGGER", autospec=True) as logger: + self.assertTrue(tb_filter(design_unit)) + logger.warning.assert_has_calls([ + mock.call('%s %s has runner_cfg %s but the file name and the %s name does not match regex %s\n' + 'in file %s', + 'Entity', + 'entity_ok_but_warning', + 'generic', + 'entity', + '^(tb_.*)|(.*_tb)$', + design_unit.file_name)]) diff --git a/vunit/test/unit/test_test_report.py b/vunit/test/unit/test_test_report.py index 4437cacdd..d11f15d9a 100644 --- a/vunit/test/unit/test_test_report.py +++ b/vunit/test/unit/test_test_report.py @@ -1,308 +1,308 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test the test report functionality -""" - -from unittest import TestCase -from xml.etree import ElementTree -from os.path import join, dirname -import os -from vunit.test_report import TestReport, PASSED, SKIPPED, FAILED - - -class TestTestReport(TestCase): - """ - Test the TestReport class - """ - - def setUp(self): - self.printer = StubPrinter() - - self.output_file_contents = 'Output file contents\n&13!--"<\\xml>' - self.output_file_name = join(dirname(__file__), "test_report_output.txt") - with open(self.output_file_name, "w") as fwrite: - fwrite.write(self.output_file_contents) - - def report_to_str(self, report): - """ - Helper function to create a string with color tags of the report - """ - - self.printer.reset() - report.print_str() - return self.printer.report_str - - def test_report_with_all_passed_tests(self): - report = self._report_with_all_passed_tests() - report.set_real_total_time(1.0) - self.assertEqual(self.report_to_str(report), """\ -==== Summary ======================== -{gi}pass{x} passed_test0 (1.0 seconds) -{gi}pass{x} passed_test1 (2.0 seconds) -===================================== -{gi}pass{x} 2 of 2 -===================================== -Total time was 3.0 seconds -Elapsed time was 1.0 seconds -===================================== -{gi}All passed!{x} -""") - self.assertTrue(report.all_ok()) - self.assertTrue(report.result_of("passed_test0").passed) - self.assertTrue(report.result_of("passed_test1").passed) - self.assertRaises(KeyError, - report.result_of, "invalid_test") - - def test_report_with_missing_tests(self): - report = self._report_with_missing_tests() - report.set_real_total_time(1.0) - self.assertEqual(self.report_to_str(report), """\ -==== Summary ======================== -{gi}pass{x} passed_test0 (1.0 seconds) -{gi}pass{x} passed_test1 (2.0 seconds) -===================================== -{gi}pass{x} 2 of 2 -===================================== -Total time was 3.0 seconds -Elapsed time was 1.0 seconds -===================================== -{gi}All passed!{x} -{rgi}WARNING: Test execution aborted after running 2 out of 3 tests{x} -""") - self.assertTrue(report.all_ok()) - self.assertTrue(report.result_of("passed_test0").passed) - self.assertTrue(report.result_of("passed_test1").passed) - self.assertRaises(KeyError, - report.result_of, "invalid_test") - - def test_report_with_failed_tests(self): - report = self._report_with_some_failed_tests() - report.set_real_total_time(12.0) - self.assertEqual(self.report_to_str(report), """\ -==== Summary ======================== -{gi}pass{x} passed_test (2.0 seconds) -{ri}fail{x} failed_test0 (11.1 seconds) -{ri}fail{x} failed_test1 (3.0 seconds) -===================================== -{gi}pass{x} 1 of 3 -{ri}fail{x} 2 of 3 -===================================== -Total time was 16.1 seconds -Elapsed time was 12.0 seconds -===================================== -{ri}Some failed!{x} -""") - self.assertFalse(report.all_ok()) - self.assertTrue(report.result_of("passed_test").passed) - self.assertTrue(report.result_of("failed_test0").failed) - self.assertTrue(report.result_of("failed_test1").failed) - - def test_report_with_skipped_tests(self): - report = self._report_with_some_skipped_tests() - report.set_real_total_time(3.0) - self.assertEqual(self.report_to_str(report), """\ -==== Summary ======================== -{gi}pass{x} passed_test (1.0 seconds) -{rgi}skip{x} skipped_test (0.0 seconds) -{ri}fail{x} failed_test (3.0 seconds) -===================================== -{gi}pass{x} 1 of 3 -{rgi}skip{x} 1 of 3 -{ri}fail{x} 1 of 3 -===================================== -Total time was 4.0 seconds -Elapsed time was 3.0 seconds -===================================== -{ri}Some failed!{x} -""") - self.assertFalse(report.all_ok()) - self.assertTrue(report.result_of("passed_test").passed) - self.assertTrue(report.result_of("skipped_test").skipped) - self.assertTrue(report.result_of("failed_test").failed) - - def test_report_with_no_tests(self): - report = self._new_report() - self.assertEqual(self.report_to_str(report), """\ -{rgi}No tests were run!{x} -""") - self.assertTrue(report.all_ok()) - - def assert_has_test(self, root, name, time, status, # pylint: disable=too-many-arguments - output=None, fmt='jenkins'): # pylint: disable=too-many-arguments - """ - Assert that junit report xml tree contains a test - """ - output = self.output_file_contents if output is None else output - for test in root.findall("testcase"): - if test.attrib["name"] == name: - self.assertEqual(test.attrib["time"], time) - if status == "passed": - self.assertEqual(len(test.findall("*")), 1) - self.assertEqual(len(test.findall("system-out")), 1) - elif status == "skipped": - self.assertEqual(len(test.findall("*")), 2) - self.assertEqual(len(test.findall("skipped")), 1) - self.assertEqual(len(test.findall("system-out")), 1) - elif status == "failed": - self.assertEqual(len(test.findall("*")), 2) - self.assertEqual(len(test.findall("failure")), 1) - self.assertEqual(len(test.findall("system-out")), 1) - else: - assert False - - if status == 'failed' and fmt == 'bamboo': - self.assertEqual(test.find("failure").text, output) - else: - self.assertEqual(test.find("system-out").text, output) - break - else: - assert False - - def test_junit_report_with_all_passed_tests(self): - report = self._report_with_all_passed_tests() - root = ElementTree.fromstring(report.to_junit_xml_str()) - self.assertEqual(root.tag, "testsuite") - self.assertEqual(len(root.findall("*")), 2) - self.assert_has_test(root, "passed_test0", time="1.0", status="passed") - self.assert_has_test(root, "passed_test1", time="2.0", status="passed") - - def test_junit_report_with__with_missing_output_file(self): - report = self._report_with_all_passed_tests() - os.remove(self.output_file_name) - fail_output = "Failed to read output file: %s" % self.output_file_name - root = ElementTree.fromstring(report.to_junit_xml_str()) - self.assertEqual(root.tag, "testsuite") - self.assertEqual(len(root.findall("*")), 2) - self.assert_has_test(root, "passed_test0", time="1.0", status="passed", - output=fail_output) - self.assert_has_test(root, "passed_test1", time="2.0", status="passed", - output=fail_output) - - def test_junit_report_with_some_failed_tests(self): - report = self._report_with_some_failed_tests() - root = ElementTree.fromstring(report.to_junit_xml_str()) - self.assertEqual(root.tag, "testsuite") - self.assertEqual(len(root.findall("*")), 3) - self.assert_has_test(root, "failed_test0", time="11.1", status="failed") - self.assert_has_test(root, "passed_test", time="2.0", status="passed") - self.assert_has_test(root, "failed_test1", time="3.0", status="failed") - - def test_junit_report_with_some_failed_tests_bamboo_fmt(self): - report = self._report_with_some_failed_tests() - root = ElementTree.fromstring(report.to_junit_xml_str(xunit_xml_format='bamboo')) - self.assertEqual(root.tag, "testsuite") - self.assertEqual(len(root.findall("*")), 3) - self.assert_has_test(root, "failed_test0", time="11.1", status="failed", fmt='bamboo') - self.assert_has_test(root, "passed_test", time="2.0", status="passed", fmt='bamboo') - self.assert_has_test(root, "failed_test1", time="3.0", status="failed", fmt='bamboo') - - def test_junit_report_with_some_skipped_tests(self): - report = self._report_with_some_skipped_tests() - root = ElementTree.fromstring(report.to_junit_xml_str()) - self.assertEqual(root.tag, "testsuite") - self.assertEqual(len(root.findall("*")), 3) - self.assert_has_test(root, "skipped_test", time="0.0", status="skipped") - self.assert_has_test(root, "passed_test", time="1.0", status="passed") - self.assert_has_test(root, "failed_test", time="3.0", status="failed") - - def test_junit_report_with_testcase_classname(self): - report = self._new_report() - report.add_result("test", PASSED, time=1.0, - output_file_name=self.output_file_name) - report.add_result("lib.entity", PASSED, time=1.0, - output_file_name=self.output_file_name) - report.add_result("lib.entity.test", PASSED, time=1.0, - output_file_name=self.output_file_name) - report.add_result("lib.entity.config.test", PASSED, time=1.0, - output_file_name=self.output_file_name) - root = ElementTree.fromstring(report.to_junit_xml_str()) - names = set((elem.attrib.get("classname", None), elem.attrib.get("name", None)) - for elem in root.findall("testcase")) - self.assertEqual(names, set([(None, "test"), - ("lib", "entity"), - ("lib.entity", "test"), - ("lib.entity.config", "test")])) - - def _report_with_all_passed_tests(self): - " @returns A report with all passed tests " - report = self._new_report() - report.add_result("passed_test0", PASSED, time=1.0, - output_file_name=self.output_file_name) - report.add_result("passed_test1", PASSED, time=2.0, - output_file_name=self.output_file_name) - report.set_expected_num_tests(2) - return report - - def _report_with_missing_tests(self): - " @returns A report with all passed tests " - report = self._new_report() - report.add_result("passed_test0", PASSED, time=1.0, - output_file_name=self.output_file_name) - report.add_result("passed_test1", PASSED, time=2.0, - output_file_name=self.output_file_name) - report.set_expected_num_tests(3) - return report - - def _report_with_some_failed_tests(self): - " @returns A report with some failed tests " - report = self._new_report() - report.add_result("failed_test0", FAILED, time=11.12, - output_file_name=self.output_file_name) - report.add_result("passed_test", PASSED, time=2.0, - output_file_name=self.output_file_name) - report.add_result("failed_test1", FAILED, time=3.0, - output_file_name=self.output_file_name) - report.set_expected_num_tests(3) - return report - - def _report_with_some_skipped_tests(self): - " @returns A report with some skipped tests " - report = self._new_report() - report.add_result("passed_test", PASSED, time=1.0, - output_file_name=self.output_file_name) - report.add_result("skipped_test", SKIPPED, time=0.0, - output_file_name=self.output_file_name) - report.add_result("failed_test", FAILED, time=3.0, - output_file_name=self.output_file_name) - report.set_expected_num_tests(3) - return report - - def _new_report(self): - return TestReport(self.printer) - - -class StubPrinter(object): - """ - A stub of a ColorPrinter - """ - def __init__(self): - self.report_str = "" - - def reset(self): - self.report_str = "" - - def write(self, text, fg=None, bg=None): - """ - ColorPrinter write stub - """ - if fg is not None or bg is not None: - self.report_str += "{" - - if fg is not None: - self.report_str += fg - - if bg is not None: - self.report_str += bg.upper() - - if fg is not None or bg is not None: - self.report_str += "}" - - self.report_str += text - - if fg is not None or bg is not None: - self.report_str += "{x}" +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test the test report functionality +""" + +from unittest import TestCase +from xml.etree import ElementTree +from os.path import join, dirname +import os +from vunit.test_report import TestReport, PASSED, SKIPPED, FAILED + + +class TestTestReport(TestCase): + """ + Test the TestReport class + """ + + def setUp(self): + self.printer = StubPrinter() + + self.output_file_contents = 'Output file contents\n&13!--"<\\xml>' + self.output_file_name = join(dirname(__file__), "test_report_output.txt") + with open(self.output_file_name, "w") as fwrite: + fwrite.write(self.output_file_contents) + + def report_to_str(self, report): + """ + Helper function to create a string with color tags of the report + """ + + self.printer.reset() + report.print_str() + return self.printer.report_str + + def test_report_with_all_passed_tests(self): + report = self._report_with_all_passed_tests() + report.set_real_total_time(1.0) + self.assertEqual(self.report_to_str(report), """\ +==== Summary ======================== +{gi}pass{x} passed_test0 (1.0 seconds) +{gi}pass{x} passed_test1 (2.0 seconds) +===================================== +{gi}pass{x} 2 of 2 +===================================== +Total time was 3.0 seconds +Elapsed time was 1.0 seconds +===================================== +{gi}All passed!{x} +""") + self.assertTrue(report.all_ok()) + self.assertTrue(report.result_of("passed_test0").passed) + self.assertTrue(report.result_of("passed_test1").passed) + self.assertRaises(KeyError, + report.result_of, "invalid_test") + + def test_report_with_missing_tests(self): + report = self._report_with_missing_tests() + report.set_real_total_time(1.0) + self.assertEqual(self.report_to_str(report), """\ +==== Summary ======================== +{gi}pass{x} passed_test0 (1.0 seconds) +{gi}pass{x} passed_test1 (2.0 seconds) +===================================== +{gi}pass{x} 2 of 2 +===================================== +Total time was 3.0 seconds +Elapsed time was 1.0 seconds +===================================== +{gi}All passed!{x} +{rgi}WARNING: Test execution aborted after running 2 out of 3 tests{x} +""") + self.assertTrue(report.all_ok()) + self.assertTrue(report.result_of("passed_test0").passed) + self.assertTrue(report.result_of("passed_test1").passed) + self.assertRaises(KeyError, + report.result_of, "invalid_test") + + def test_report_with_failed_tests(self): + report = self._report_with_some_failed_tests() + report.set_real_total_time(12.0) + self.assertEqual(self.report_to_str(report), """\ +==== Summary ======================== +{gi}pass{x} passed_test (2.0 seconds) +{ri}fail{x} failed_test0 (11.1 seconds) +{ri}fail{x} failed_test1 (3.0 seconds) +===================================== +{gi}pass{x} 1 of 3 +{ri}fail{x} 2 of 3 +===================================== +Total time was 16.1 seconds +Elapsed time was 12.0 seconds +===================================== +{ri}Some failed!{x} +""") + self.assertFalse(report.all_ok()) + self.assertTrue(report.result_of("passed_test").passed) + self.assertTrue(report.result_of("failed_test0").failed) + self.assertTrue(report.result_of("failed_test1").failed) + + def test_report_with_skipped_tests(self): + report = self._report_with_some_skipped_tests() + report.set_real_total_time(3.0) + self.assertEqual(self.report_to_str(report), """\ +==== Summary ======================== +{gi}pass{x} passed_test (1.0 seconds) +{rgi}skip{x} skipped_test (0.0 seconds) +{ri}fail{x} failed_test (3.0 seconds) +===================================== +{gi}pass{x} 1 of 3 +{rgi}skip{x} 1 of 3 +{ri}fail{x} 1 of 3 +===================================== +Total time was 4.0 seconds +Elapsed time was 3.0 seconds +===================================== +{ri}Some failed!{x} +""") + self.assertFalse(report.all_ok()) + self.assertTrue(report.result_of("passed_test").passed) + self.assertTrue(report.result_of("skipped_test").skipped) + self.assertTrue(report.result_of("failed_test").failed) + + def test_report_with_no_tests(self): + report = self._new_report() + self.assertEqual(self.report_to_str(report), """\ +{rgi}No tests were run!{x} +""") + self.assertTrue(report.all_ok()) + + def assert_has_test(self, root, name, time, status, # pylint: disable=too-many-arguments + output=None, fmt='jenkins'): # pylint: disable=too-many-arguments + """ + Assert that junit report xml tree contains a test + """ + output = self.output_file_contents if output is None else output + for test in root.findall("testcase"): + if test.attrib["name"] == name: + self.assertEqual(test.attrib["time"], time) + if status == "passed": + self.assertEqual(len(test.findall("*")), 1) + self.assertEqual(len(test.findall("system-out")), 1) + elif status == "skipped": + self.assertEqual(len(test.findall("*")), 2) + self.assertEqual(len(test.findall("skipped")), 1) + self.assertEqual(len(test.findall("system-out")), 1) + elif status == "failed": + self.assertEqual(len(test.findall("*")), 2) + self.assertEqual(len(test.findall("failure")), 1) + self.assertEqual(len(test.findall("system-out")), 1) + else: + assert False + + if status == 'failed' and fmt == 'bamboo': + self.assertEqual(test.find("failure").text, output) + else: + self.assertEqual(test.find("system-out").text, output) + break + else: + assert False + + def test_junit_report_with_all_passed_tests(self): + report = self._report_with_all_passed_tests() + root = ElementTree.fromstring(report.to_junit_xml_str()) + self.assertEqual(root.tag, "testsuite") + self.assertEqual(len(root.findall("*")), 2) + self.assert_has_test(root, "passed_test0", time="1.0", status="passed") + self.assert_has_test(root, "passed_test1", time="2.0", status="passed") + + def test_junit_report_with__with_missing_output_file(self): + report = self._report_with_all_passed_tests() + os.remove(self.output_file_name) + fail_output = "Failed to read output file: %s" % self.output_file_name + root = ElementTree.fromstring(report.to_junit_xml_str()) + self.assertEqual(root.tag, "testsuite") + self.assertEqual(len(root.findall("*")), 2) + self.assert_has_test(root, "passed_test0", time="1.0", status="passed", + output=fail_output) + self.assert_has_test(root, "passed_test1", time="2.0", status="passed", + output=fail_output) + + def test_junit_report_with_some_failed_tests(self): + report = self._report_with_some_failed_tests() + root = ElementTree.fromstring(report.to_junit_xml_str()) + self.assertEqual(root.tag, "testsuite") + self.assertEqual(len(root.findall("*")), 3) + self.assert_has_test(root, "failed_test0", time="11.1", status="failed") + self.assert_has_test(root, "passed_test", time="2.0", status="passed") + self.assert_has_test(root, "failed_test1", time="3.0", status="failed") + + def test_junit_report_with_some_failed_tests_bamboo_fmt(self): + report = self._report_with_some_failed_tests() + root = ElementTree.fromstring(report.to_junit_xml_str(xunit_xml_format='bamboo')) + self.assertEqual(root.tag, "testsuite") + self.assertEqual(len(root.findall("*")), 3) + self.assert_has_test(root, "failed_test0", time="11.1", status="failed", fmt='bamboo') + self.assert_has_test(root, "passed_test", time="2.0", status="passed", fmt='bamboo') + self.assert_has_test(root, "failed_test1", time="3.0", status="failed", fmt='bamboo') + + def test_junit_report_with_some_skipped_tests(self): + report = self._report_with_some_skipped_tests() + root = ElementTree.fromstring(report.to_junit_xml_str()) + self.assertEqual(root.tag, "testsuite") + self.assertEqual(len(root.findall("*")), 3) + self.assert_has_test(root, "skipped_test", time="0.0", status="skipped") + self.assert_has_test(root, "passed_test", time="1.0", status="passed") + self.assert_has_test(root, "failed_test", time="3.0", status="failed") + + def test_junit_report_with_testcase_classname(self): + report = self._new_report() + report.add_result("test", PASSED, time=1.0, + output_file_name=self.output_file_name) + report.add_result("lib.entity", PASSED, time=1.0, + output_file_name=self.output_file_name) + report.add_result("lib.entity.test", PASSED, time=1.0, + output_file_name=self.output_file_name) + report.add_result("lib.entity.config.test", PASSED, time=1.0, + output_file_name=self.output_file_name) + root = ElementTree.fromstring(report.to_junit_xml_str()) + names = set((elem.attrib.get("classname", None), elem.attrib.get("name", None)) + for elem in root.findall("testcase")) + self.assertEqual(names, set([(None, "test"), + ("lib", "entity"), + ("lib.entity", "test"), + ("lib.entity.config", "test")])) + + def _report_with_all_passed_tests(self): + " @returns A report with all passed tests " + report = self._new_report() + report.add_result("passed_test0", PASSED, time=1.0, + output_file_name=self.output_file_name) + report.add_result("passed_test1", PASSED, time=2.0, + output_file_name=self.output_file_name) + report.set_expected_num_tests(2) + return report + + def _report_with_missing_tests(self): + " @returns A report with all passed tests " + report = self._new_report() + report.add_result("passed_test0", PASSED, time=1.0, + output_file_name=self.output_file_name) + report.add_result("passed_test1", PASSED, time=2.0, + output_file_name=self.output_file_name) + report.set_expected_num_tests(3) + return report + + def _report_with_some_failed_tests(self): + " @returns A report with some failed tests " + report = self._new_report() + report.add_result("failed_test0", FAILED, time=11.12, + output_file_name=self.output_file_name) + report.add_result("passed_test", PASSED, time=2.0, + output_file_name=self.output_file_name) + report.add_result("failed_test1", FAILED, time=3.0, + output_file_name=self.output_file_name) + report.set_expected_num_tests(3) + return report + + def _report_with_some_skipped_tests(self): + " @returns A report with some skipped tests " + report = self._new_report() + report.add_result("passed_test", PASSED, time=1.0, + output_file_name=self.output_file_name) + report.add_result("skipped_test", SKIPPED, time=0.0, + output_file_name=self.output_file_name) + report.add_result("failed_test", FAILED, time=3.0, + output_file_name=self.output_file_name) + report.set_expected_num_tests(3) + return report + + def _new_report(self): + return TestReport(self.printer) + + +class StubPrinter(object): + """ + A stub of a ColorPrinter + """ + def __init__(self): + self.report_str = "" + + def reset(self): + self.report_str = "" + + def write(self, text, fg=None, bg=None): + """ + ColorPrinter write stub + """ + if fg is not None or bg is not None: + self.report_str += "{" + + if fg is not None: + self.report_str += fg + + if bg is not None: + self.report_str += bg.upper() + + if fg is not None or bg is not None: + self.report_str += "}" + + self.report_str += text + + if fg is not None or bg is not None: + self.report_str += "{x}" diff --git a/vunit/test/unit/test_test_runner.py b/vunit/test/unit/test_test_runner.py index 71f7da1dc..4ac4f3366 100644 --- a/vunit/test/unit/test_test_runner.py +++ b/vunit/test/unit/test_test_runner.py @@ -1,221 +1,221 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test the test runner -""" - - -from __future__ import print_function - -import unittest -from os.path import join, abspath - -from vunit.hashing import hash_string -from vunit.test_runner import TestRunner, create_output_path -from vunit.test_report import TestReport -from vunit.test_list import TestList -from vunit.test.mock_2or3 import mock -from vunit.test.common import with_tempdir - - -class TestTestRunner(unittest.TestCase): - """ - Test the test runner - """ - - @with_tempdir - def test_runs_testcases_in_order(self, tempdir): - report = TestReport() - runner = TestRunner(report, tempdir) - - order = [] - test_case1 = self.create_test("test1", True, order=order) - test_case2 = self.create_test("test2", False, order=order) - test_case3 = self.create_test("test3", True, order=order) - test_list = TestList() - test_list.add_test(test_case1) - test_list.add_test(test_case2) - test_list.add_test(test_case3) - runner.run(test_list) - self.assertEqual(test_case1.output_path, create_output_path(tempdir, "test1")) - self.assertEqual(test_case2.output_path, create_output_path(tempdir, "test2")) - self.assertEqual(test_case3.output_path, create_output_path(tempdir, "test3")) - self.assertEqual(order, ["test1", "test2", "test3"]) - self.assertTrue(report.result_of("test1").passed) - self.assertTrue(report.result_of("test2").failed) - self.assertTrue(report.result_of("test3").passed) - - @with_tempdir - def test_fail_fast(self, tempdir): - report = TestReport() - runner = TestRunner(report, tempdir, fail_fast=True) - - order = [] - test_case1 = self.create_test("test1", True, order=order) - test_case2 = self.create_test("test2", False, order=order) - test_case3 = self.create_test("test3", True, order=order) - test_list = TestList() - test_list.add_test(test_case1) - test_list.add_test(test_case2) - test_list.add_test(test_case3) - try: - runner.run(test_list) - except KeyboardInterrupt: - pass - self.assertEqual(test_case1.output_path, create_output_path(tempdir, "test1")) - self.assertEqual(test_case2.output_path, create_output_path(tempdir, "test2")) - self.assertEqual(test_case3.called, False) - self.assertEqual(order, ["test1", "test2"]) - self.assertTrue(report.result_of("test1").passed) - self.assertTrue(report.result_of("test2").failed) - - @with_tempdir - def test_handles_python_exeception(self, tempdir): - report = TestReport() - runner = TestRunner(report, tempdir) - - test_case = self.create_test("test", True) - test_list = TestList() - test_list.add_test(test_case) - - def side_effect(*args, **kwargs): # pylint: disable=unused-argument - raise KeyError - - test_case.run_side_effect = side_effect - runner.run(test_list) - self.assertTrue(report.result_of("test").failed) - - @with_tempdir - def test_collects_output(self, tempdir): - report = TestReport() - runner = TestRunner(report, tempdir) - - test_case = self.create_test("test", True) - test_list = TestList() - test_list.add_test(test_case) - - output = "Output string, , \n" - - def side_effect(*args, **kwargs): # pylint: disable=unused-argument - """ - Side effect that print output to stdout - """ - print(output, end="") - return True - - test_case.run_side_effect = side_effect - runner.run(test_list) - self.assertTrue(report.result_of("test").passed) - self.assertEqual(report.result_of("test").output, output) - - @with_tempdir - def test_can_read_output(self, tempdir): - report = TestReport() - runner = TestRunner(report, tempdir) - - test_case = self.create_test("test", True) - test_list = TestList() - test_list.add_test(test_case) - - def side_effect(read_output, **kwargs): # pylint: disable=unused-argument - """ - Side effect that print output to stdout - """ - print("out1", end="") - print("out2", end="") - assert read_output() == "out1out2" - print("out3", end="") - print("out4", end="") - assert read_output() == "out1out2out3out4" - print("out5", end="") - return True - - test_case.run_side_effect = side_effect - runner.run(test_list) - self.assertTrue(report.result_of("test").passed) - self.assertEqual(report.result_of("test").output, "out1out2out3out4out5") - - def test_create_output_path_on_linux(self): - with mock.patch("sys.platform", new="linux"): - with mock.patch("os.environ", new={}): - output_path = "output_path" - test_name = "_" * 400 - test_output = create_output_path(output_path, test_name) - self.assertEqual(test_output, join(abspath(output_path), test_name + "_" + hash_string(test_name))) - - output_path = "output_path" - test_name = "123._-+" - test_output = create_output_path(output_path, test_name) - self.assertEqual(test_output, join(abspath(output_path), test_name + "_" + hash_string(test_name))) - - output_path = "output_path" - test_name = "#<>:" - safe_name = "____" - test_output = create_output_path(output_path, test_name) - self.assertEqual(test_output, join(abspath(output_path), safe_name + "_" + hash_string(test_name))) - - def test_create_output_path_on_windows(self): - with mock.patch("sys.platform", new="win32"): - with mock.patch("os.environ", new={}): - output_path = "output_path" - test_name = "_" * 400 - test_output = create_output_path(output_path, test_name) - self.assertEqual(len(test_output), 260 - 100 + 1) - - with mock.patch("os.environ", new={"VUNIT_TEST_OUTPUT_PATH_MARGIN": "-1000"}): - output_path = "output_path" - test_name = "_" * 400 - test_output = create_output_path(output_path, test_name) - self.assertEqual(test_output, join(abspath(output_path), test_name + "_" + hash_string(test_name))) - - with mock.patch("os.environ", new={"VUNIT_SHORT_TEST_OUTPUT_PATHS": ""}): - output_path = "output_path" - test_name = "_" * 400 - test_output = create_output_path(output_path, test_name) - self.assertEqual(test_output, join(abspath(output_path), hash_string(test_name))) - - @staticmethod - def create_test(name, passed, order=None): - """ - Utility function to create a mocked test with name - that is either passed or failed - """ - - def run_side_effect(*args, **kwargs): # pylint: disable=unused-argument - """ - Side effect that registers that is has been run - """ - if order is not None: - order.append(name) - return passed - - test_case = TestCaseMock(name=name, - run_side_effect=run_side_effect) - return test_case - - -class TestCaseMock(object): - """ - A test case mock class - """ - - def __init__(self, name, run_side_effect): - self.name = name - self.output_path = None - self.read_output = None - self.called = False - self.run_side_effect = run_side_effect - - def run(self, output_path, read_output): - """ - Mock run method that just records the arguments - """ - assert not self.called - self.called = True - self.output_path = output_path - self.read_output = read_output - return self.run_side_effect(output_path=output_path, read_output=read_output) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test the test runner +""" + + +from __future__ import print_function + +import unittest +from os.path import join, abspath + +from vunit.hashing import hash_string +from vunit.test_runner import TestRunner, create_output_path +from vunit.test_report import TestReport +from vunit.test_list import TestList +from vunit.test.mock_2or3 import mock +from vunit.test.common import with_tempdir + + +class TestTestRunner(unittest.TestCase): + """ + Test the test runner + """ + + @with_tempdir + def test_runs_testcases_in_order(self, tempdir): + report = TestReport() + runner = TestRunner(report, tempdir) + + order = [] + test_case1 = self.create_test("test1", True, order=order) + test_case2 = self.create_test("test2", False, order=order) + test_case3 = self.create_test("test3", True, order=order) + test_list = TestList() + test_list.add_test(test_case1) + test_list.add_test(test_case2) + test_list.add_test(test_case3) + runner.run(test_list) + self.assertEqual(test_case1.output_path, create_output_path(tempdir, "test1")) + self.assertEqual(test_case2.output_path, create_output_path(tempdir, "test2")) + self.assertEqual(test_case3.output_path, create_output_path(tempdir, "test3")) + self.assertEqual(order, ["test1", "test2", "test3"]) + self.assertTrue(report.result_of("test1").passed) + self.assertTrue(report.result_of("test2").failed) + self.assertTrue(report.result_of("test3").passed) + + @with_tempdir + def test_fail_fast(self, tempdir): + report = TestReport() + runner = TestRunner(report, tempdir, fail_fast=True) + + order = [] + test_case1 = self.create_test("test1", True, order=order) + test_case2 = self.create_test("test2", False, order=order) + test_case3 = self.create_test("test3", True, order=order) + test_list = TestList() + test_list.add_test(test_case1) + test_list.add_test(test_case2) + test_list.add_test(test_case3) + try: + runner.run(test_list) + except KeyboardInterrupt: + pass + self.assertEqual(test_case1.output_path, create_output_path(tempdir, "test1")) + self.assertEqual(test_case2.output_path, create_output_path(tempdir, "test2")) + self.assertEqual(test_case3.called, False) + self.assertEqual(order, ["test1", "test2"]) + self.assertTrue(report.result_of("test1").passed) + self.assertTrue(report.result_of("test2").failed) + + @with_tempdir + def test_handles_python_exeception(self, tempdir): + report = TestReport() + runner = TestRunner(report, tempdir) + + test_case = self.create_test("test", True) + test_list = TestList() + test_list.add_test(test_case) + + def side_effect(*args, **kwargs): # pylint: disable=unused-argument + raise KeyError + + test_case.run_side_effect = side_effect + runner.run(test_list) + self.assertTrue(report.result_of("test").failed) + + @with_tempdir + def test_collects_output(self, tempdir): + report = TestReport() + runner = TestRunner(report, tempdir) + + test_case = self.create_test("test", True) + test_list = TestList() + test_list.add_test(test_case) + + output = "Output string, , \n" + + def side_effect(*args, **kwargs): # pylint: disable=unused-argument + """ + Side effect that print output to stdout + """ + print(output, end="") + return True + + test_case.run_side_effect = side_effect + runner.run(test_list) + self.assertTrue(report.result_of("test").passed) + self.assertEqual(report.result_of("test").output, output) + + @with_tempdir + def test_can_read_output(self, tempdir): + report = TestReport() + runner = TestRunner(report, tempdir) + + test_case = self.create_test("test", True) + test_list = TestList() + test_list.add_test(test_case) + + def side_effect(read_output, **kwargs): # pylint: disable=unused-argument + """ + Side effect that print output to stdout + """ + print("out1", end="") + print("out2", end="") + assert read_output() == "out1out2" + print("out3", end="") + print("out4", end="") + assert read_output() == "out1out2out3out4" + print("out5", end="") + return True + + test_case.run_side_effect = side_effect + runner.run(test_list) + self.assertTrue(report.result_of("test").passed) + self.assertEqual(report.result_of("test").output, "out1out2out3out4out5") + + def test_create_output_path_on_linux(self): + with mock.patch("sys.platform", new="linux"): + with mock.patch("os.environ", new={}): + output_path = "output_path" + test_name = "_" * 400 + test_output = create_output_path(output_path, test_name) + self.assertEqual(test_output, join(abspath(output_path), test_name + "_" + hash_string(test_name))) + + output_path = "output_path" + test_name = "123._-+" + test_output = create_output_path(output_path, test_name) + self.assertEqual(test_output, join(abspath(output_path), test_name + "_" + hash_string(test_name))) + + output_path = "output_path" + test_name = "#<>:" + safe_name = "____" + test_output = create_output_path(output_path, test_name) + self.assertEqual(test_output, join(abspath(output_path), safe_name + "_" + hash_string(test_name))) + + def test_create_output_path_on_windows(self): + with mock.patch("sys.platform", new="win32"): + with mock.patch("os.environ", new={}): + output_path = "output_path" + test_name = "_" * 400 + test_output = create_output_path(output_path, test_name) + self.assertEqual(len(test_output), 260 - 100 + 1) + + with mock.patch("os.environ", new={"VUNIT_TEST_OUTPUT_PATH_MARGIN": "-1000"}): + output_path = "output_path" + test_name = "_" * 400 + test_output = create_output_path(output_path, test_name) + self.assertEqual(test_output, join(abspath(output_path), test_name + "_" + hash_string(test_name))) + + with mock.patch("os.environ", new={"VUNIT_SHORT_TEST_OUTPUT_PATHS": ""}): + output_path = "output_path" + test_name = "_" * 400 + test_output = create_output_path(output_path, test_name) + self.assertEqual(test_output, join(abspath(output_path), hash_string(test_name))) + + @staticmethod + def create_test(name, passed, order=None): + """ + Utility function to create a mocked test with name + that is either passed or failed + """ + + def run_side_effect(*args, **kwargs): # pylint: disable=unused-argument + """ + Side effect that registers that is has been run + """ + if order is not None: + order.append(name) + return passed + + test_case = TestCaseMock(name=name, + run_side_effect=run_side_effect) + return test_case + + +class TestCaseMock(object): + """ + A test case mock class + """ + + def __init__(self, name, run_side_effect): + self.name = name + self.output_path = None + self.read_output = None + self.called = False + self.run_side_effect = run_side_effect + + def run(self, output_path, read_output): + """ + Mock run method that just records the arguments + """ + assert not self.called + self.called = True + self.output_path = output_path + self.read_output = read_output + return self.run_side_effect(output_path=output_path, read_output=read_output) diff --git a/vunit/test/unit/test_test_suites.py b/vunit/test/unit/test_test_suites.py index 0f34fff21..8975b50ad 100644 --- a/vunit/test/unit/test_test_suites.py +++ b/vunit/test/unit/test_test_suites.py @@ -1,154 +1,154 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test the test suites -""" - -from os.path import join -from unittest import TestCase -from vunit.test_suites import (TestRun) -from vunit.test_report import (PASSED, SKIPPED, FAILED) -from vunit.test.common import create_tempdir -from vunit.simulator_interface import SimulatorInterface - - -class TestTestSuites(TestCase): - """ - Test the test suites - """ - - def test_missing_results_fails_all(self): - self._read_test_results({"test1": FAILED, "test2": FAILED}, None) - - def test_read_results_all_passed(self): - self._read_test_results({"test1": PASSED, "test2": PASSED}, """\ -test_start:test1 -test_start:test2 -test_suite_done -""") - - def test_read_results_suite_not_done(self): - self._read_test_results({"test1": PASSED, "test2": FAILED}, """\ -test_start:test1 -test_start:test2 -""") - - self._read_test_results({"test1": FAILED, "test2": PASSED}, """\ -test_start:test2 -test_start:test1 -""") - - def test_read_results_skipped_test(self): - self._read_test_results({"test1": PASSED, "test2": SKIPPED, "test3": SKIPPED}, """\ -test_start:test1 -test_suite_done -""") - - def test_read_results_anonynmous_test_pass(self): - self._read_test_results({None: PASSED}, """\ -test_suite_done -""") - - def test_read_results_anonynmous_test_fail(self): - self._read_test_results({None: FAILED}, """\ -""") - - def test_read_results_unknown_test(self): - try: - self._read_test_results(["test1"], """\ -test_start:test1 -test_start:test3 -test_suite_done""") - except RuntimeError as exc: - self.assertIn("unknown test case test3", str(exc)) - else: - assert False, "RuntimeError not raised" - - def _read_test_results(self, expected, contents): - """ - Helper method to test the read_test_results function - """ - with create_tempdir() as path: - file_name = join(path, "vunit_results") - if contents is not None: - with open(file_name, "w") as fptr: - fptr.write(contents) - - run = TestRun(simulator_if=None, - config=None, - elaborate_only=False, - test_suite_name=None, - test_cases=expected) - results = run._read_test_results(file_name=file_name) # pylint: disable=protected-access - self.assertEqual(results, expected) - return results - - def test_exit_code(self): - """ - Test that results are overwritten when none is FAILED but the exit code is nonzero - """ - - def test(contents, results, expected=None, werechecked=None): - """ - Test the four combinations of 'sim_ok' and 'has_valid_exit_code' - """ - if werechecked is None: - werechecked = [True, True, True, True] - self._test_exit_code(contents, results, True, False, werechecked[0]) - self._test_exit_code(contents, results, False, False, werechecked[1]) - self._test_exit_code(contents, results, True, True, werechecked[2]) - val = results - if expected is not None: - val = expected - self._test_exit_code(contents, val, False, True, werechecked[3]) - - test( - """\ntest_start:test1\ntest_suite_done\n""", - {"test1": PASSED}, - {"test1": FAILED}, - [False, False, False, True] - ) - - test( - """\ntest_start:test1\ntest_suite_done\n""", - {"test1": PASSED, "test2": SKIPPED}, - {"test1": FAILED, "test2": SKIPPED}, - [False, False, False, True] - ) - - test("""\ntest_start:test1\n""", {"test1": FAILED, "test2": SKIPPED}) - contents = """\ntest_start:test1\ntest_start:test2\n""" - test(contents, {"test1": PASSED, "test2": FAILED}) - test(contents, {"test1": PASSED, "test2": FAILED, "test3": SKIPPED}) - - def _test_exit_code(self, contents, expected, sim_ok=True, has_valid_exit_code=False, waschecked=False): - """ - Helper method to test the check_results function - """ - with create_tempdir() as path: - file_name = join(path, "vunit_results") - if contents is not None: - with open(file_name, "w") as fptr: - fptr.write(contents) - - sim_if = SimulatorInterface - @staticmethod - def func(): - return has_valid_exit_code - sim_if.has_valid_exit_code = func - - run = TestRun(simulator_if=sim_if, - config=None, - elaborate_only=False, - test_suite_name=None, - test_cases=expected) - - results = run._read_test_results(file_name=file_name) # pylint: disable=protected-access - self.assertEqual( - run._check_results(results, sim_ok), # pylint: disable=protected-access - (waschecked, expected) - ) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test the test suites +""" + +from os.path import join +from unittest import TestCase +from vunit.test_suites import (TestRun) +from vunit.test_report import (PASSED, SKIPPED, FAILED) +from vunit.test.common import create_tempdir +from vunit.simulator_interface import SimulatorInterface + + +class TestTestSuites(TestCase): + """ + Test the test suites + """ + + def test_missing_results_fails_all(self): + self._read_test_results({"test1": FAILED, "test2": FAILED}, None) + + def test_read_results_all_passed(self): + self._read_test_results({"test1": PASSED, "test2": PASSED}, """\ +test_start:test1 +test_start:test2 +test_suite_done +""") + + def test_read_results_suite_not_done(self): + self._read_test_results({"test1": PASSED, "test2": FAILED}, """\ +test_start:test1 +test_start:test2 +""") + + self._read_test_results({"test1": FAILED, "test2": PASSED}, """\ +test_start:test2 +test_start:test1 +""") + + def test_read_results_skipped_test(self): + self._read_test_results({"test1": PASSED, "test2": SKIPPED, "test3": SKIPPED}, """\ +test_start:test1 +test_suite_done +""") + + def test_read_results_anonynmous_test_pass(self): + self._read_test_results({None: PASSED}, """\ +test_suite_done +""") + + def test_read_results_anonynmous_test_fail(self): + self._read_test_results({None: FAILED}, """\ +""") + + def test_read_results_unknown_test(self): + try: + self._read_test_results(["test1"], """\ +test_start:test1 +test_start:test3 +test_suite_done""") + except RuntimeError as exc: + self.assertIn("unknown test case test3", str(exc)) + else: + assert False, "RuntimeError not raised" + + def _read_test_results(self, expected, contents): + """ + Helper method to test the read_test_results function + """ + with create_tempdir() as path: + file_name = join(path, "vunit_results") + if contents is not None: + with open(file_name, "w") as fptr: + fptr.write(contents) + + run = TestRun(simulator_if=None, + config=None, + elaborate_only=False, + test_suite_name=None, + test_cases=expected) + results = run._read_test_results(file_name=file_name) # pylint: disable=protected-access + self.assertEqual(results, expected) + return results + + def test_exit_code(self): + """ + Test that results are overwritten when none is FAILED but the exit code is nonzero + """ + + def test(contents, results, expected=None, werechecked=None): + """ + Test the four combinations of 'sim_ok' and 'has_valid_exit_code' + """ + if werechecked is None: + werechecked = [True, True, True, True] + self._test_exit_code(contents, results, True, False, werechecked[0]) + self._test_exit_code(contents, results, False, False, werechecked[1]) + self._test_exit_code(contents, results, True, True, werechecked[2]) + val = results + if expected is not None: + val = expected + self._test_exit_code(contents, val, False, True, werechecked[3]) + + test( + """\ntest_start:test1\ntest_suite_done\n""", + {"test1": PASSED}, + {"test1": FAILED}, + [False, False, False, True] + ) + + test( + """\ntest_start:test1\ntest_suite_done\n""", + {"test1": PASSED, "test2": SKIPPED}, + {"test1": FAILED, "test2": SKIPPED}, + [False, False, False, True] + ) + + test("""\ntest_start:test1\n""", {"test1": FAILED, "test2": SKIPPED}) + contents = """\ntest_start:test1\ntest_start:test2\n""" + test(contents, {"test1": PASSED, "test2": FAILED}) + test(contents, {"test1": PASSED, "test2": FAILED, "test3": SKIPPED}) + + def _test_exit_code(self, contents, expected, sim_ok=True, has_valid_exit_code=False, waschecked=False): + """ + Helper method to test the check_results function + """ + with create_tempdir() as path: + file_name = join(path, "vunit_results") + if contents is not None: + with open(file_name, "w") as fptr: + fptr.write(contents) + + sim_if = SimulatorInterface + @staticmethod + def func(): + return has_valid_exit_code + sim_if.has_valid_exit_code = func + + run = TestRun(simulator_if=sim_if, + config=None, + elaborate_only=False, + test_suite_name=None, + test_cases=expected) + + results = run._read_test_results(file_name=file_name) # pylint: disable=protected-access + self.assertEqual( + run._check_results(results, sim_ok), # pylint: disable=protected-access + (waschecked, expected) + ) diff --git a/vunit/test/unit/test_tokenizer.py b/vunit/test/unit/test_tokenizer.py index 7633ca834..40abd59ae 100644 --- a/vunit/test/unit/test_tokenizer.py +++ b/vunit/test/unit/test_tokenizer.py @@ -1,124 +1,124 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test of the general tokenizer -""" - -from unittest import TestCase -from vunit.parsing.tokenizer import describe_location -from vunit.test.mock_2or3 import mock - - -class TestTokenizer(TestCase): - """ - Test of the general tokenizer - """ - - def test_describes_single_char_location(self): - self.assertEqual( - _describe_location("""\ -S -"""), """\ -at filename0 line 1: -S -~""") - - def test_describes_single_char_location_within(self): - self.assertEqual( - _describe_location("""\ - S -"""), """\ -at filename0 line 1: - S - ~""") - - def test_describes_multi_char_location(self): - self.assertEqual( - _describe_location("""\ -S E -"""), """\ -at filename0 line 1: -S E -~~~""") - - def test_describes_multi_char_location_within(self): - self.assertEqual( - _describe_location("""\ - S E -"""), """\ -at filename0 line 1: - S E - ~~~""") - - def test_describes_multi_line_location(self): - self.assertEqual( - _describe_location("""\ - S____ - E -"""), """\ -at filename0 line 1: - S____ - ~~~~~""") - - def test_describes_multi_file_location(self): - self.assertEqual( - _describe_location("""\ - - S__E""", """\ - - - SE"""), """\ -from filename0 line 2: - S__E - ~~~~ -at filename1 line 3: - SE - ~~""") - - def test_describe_location_none(self): - self.assertEqual(describe_location(None), - "Unknown location") - - def test_describe_missing_location(self): - self.assertEqual(describe_location((("missing.svh", (0, 0)), None)), - "Unknown location in missing.svh") - - def test_describe_none_filename_location(self): - self.assertEqual(describe_location(((None, (0, 0)), None)), - "Unknown Python string") - - -def _describe_location(*codes): - """ - Helper to test describe_location - """ - contents = {} - location = None - for idx, code in enumerate(codes): - filename = "filename%i" % idx - contents[filename] = code - start = code.index("S") - - if "E" in code: - end = code.index("E") - else: - end = start - - location = ((filename, (start, end)), location) - - with mock.patch("vunit.parsing.tokenizer.read_file", autospec=True) as mock_read_file: - with mock.patch("vunit.parsing.tokenizer.file_exists", autospec=True) as mock_file_exists: - def file_exists_side_effect(filename): - return filename in contents - - def read_file_side_effect(filename): - return contents[filename] - mock_file_exists.side_effect = file_exists_side_effect - mock_read_file.side_effect = read_file_side_effect - - retval = describe_location(location=location) - return retval +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test of the general tokenizer +""" + +from unittest import TestCase +from vunit.parsing.tokenizer import describe_location +from vunit.test.mock_2or3 import mock + + +class TestTokenizer(TestCase): + """ + Test of the general tokenizer + """ + + def test_describes_single_char_location(self): + self.assertEqual( + _describe_location("""\ +S +"""), """\ +at filename0 line 1: +S +~""") + + def test_describes_single_char_location_within(self): + self.assertEqual( + _describe_location("""\ + S +"""), """\ +at filename0 line 1: + S + ~""") + + def test_describes_multi_char_location(self): + self.assertEqual( + _describe_location("""\ +S E +"""), """\ +at filename0 line 1: +S E +~~~""") + + def test_describes_multi_char_location_within(self): + self.assertEqual( + _describe_location("""\ + S E +"""), """\ +at filename0 line 1: + S E + ~~~""") + + def test_describes_multi_line_location(self): + self.assertEqual( + _describe_location("""\ + S____ + E +"""), """\ +at filename0 line 1: + S____ + ~~~~~""") + + def test_describes_multi_file_location(self): + self.assertEqual( + _describe_location("""\ + + S__E""", """\ + + + SE"""), """\ +from filename0 line 2: + S__E + ~~~~ +at filename1 line 3: + SE + ~~""") + + def test_describe_location_none(self): + self.assertEqual(describe_location(None), + "Unknown location") + + def test_describe_missing_location(self): + self.assertEqual(describe_location((("missing.svh", (0, 0)), None)), + "Unknown location in missing.svh") + + def test_describe_none_filename_location(self): + self.assertEqual(describe_location(((None, (0, 0)), None)), + "Unknown Python string") + + +def _describe_location(*codes): + """ + Helper to test describe_location + """ + contents = {} + location = None + for idx, code in enumerate(codes): + filename = "filename%i" % idx + contents[filename] = code + start = code.index("S") + + if "E" in code: + end = code.index("E") + else: + end = start + + location = ((filename, (start, end)), location) + + with mock.patch("vunit.parsing.tokenizer.read_file", autospec=True) as mock_read_file: + with mock.patch("vunit.parsing.tokenizer.file_exists", autospec=True) as mock_file_exists: + def file_exists_side_effect(filename): + return filename in contents + + def read_file_side_effect(filename): + return contents[filename] + mock_file_exists.side_effect = file_exists_side_effect + mock_read_file.side_effect = read_file_side_effect + + retval = describe_location(location=location) + return retval diff --git a/vunit/test/unit/test_ui.py b/vunit/test/unit/test_ui.py index bd8d1b117..a94a5d97c 100644 --- a/vunit/test/unit/test_ui.py +++ b/vunit/test/unit/test_ui.py @@ -1,1118 +1,1118 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com -# -# pylint: disable=too-many-public-methods, too-many-lines - -""" -Acceptance test of the VUnit public interface class -""" - -from __future__ import print_function -import unittest -from string import Template -import os -from os.path import join, dirname, basename, exists, abspath -import json -import re -from re import MULTILINE -from shutil import rmtree -from vunit.ui import VUnit -from vunit.project import VHDL_EXTENSIONS, VERILOG_EXTENSIONS -from vunit.test.mock_2or3 import mock -from vunit.test.common import (set_env, - with_tempdir, - create_vhdl_test_bench_file) -from vunit.ostools import renew_path -from vunit.builtins import add_verilog_include_dir -from vunit.simulator_interface import SimulatorInterface - - -class TestUi(unittest.TestCase): - """ - Testing the VUnit public interface class - """ - def setUp(self): - self.tmp_path = join(dirname(__file__), "test_ui_tmp") - renew_path(self.tmp_path) - self.cwd = os.getcwd() - os.chdir(self.tmp_path) - - self._output_path = join(self.tmp_path, 'output') - self._preprocessed_path = join(self._output_path, "preprocessed") - - def tearDown(self): - os.chdir(self.cwd) - if exists(self.tmp_path): - rmtree(self.tmp_path) - - def test_global_custom_preprocessors_should_be_applied_in_the_order_they_are_added(self): - ui = self._create_ui() - ui.add_library('lib') - ui.add_preprocessor(VUnitfier()) - ui.add_preprocessor(ParentalControl()) - - file_name = self.create_entity_file() - ui.add_source_files(file_name, 'lib') - - pp_source = Template(""" -library vunit_lib; -context vunit_lib.vunit_context; - -entity $entity is -end entity; - -architecture arch of $entity is -begin - log("Hello World"); - check_relation(1 /= 2); - log("Here I am!"); -- VUnitfier preprocessor: Report turned of[BEEP]eeping original code. -end architecture; -""") - with open(join(self._preprocessed_path, 'lib', basename(file_name))) as fread: - self.assertEqual(fread.read(), pp_source.substitute(entity='ent0', file=basename(file_name))) - - def test_global_check_and_location_preprocessors_should_be_applied_after_global_custom_preprocessors(self): - ui = self._create_ui() - ui.add_library('lib') - ui.enable_location_preprocessing() - ui.enable_check_preprocessing() - ui.add_preprocessor(TestPreprocessor()) - - file_name = self.create_entity_file() - ui.add_source_files(file_name, 'lib') - - pp_source = Template("""\ --- check_relation(a = b, line_num => 1, file_name => "$file", \ -context_msg => "Expected a = b. Left is " & to_string(a) & ". Right is " & to_string(b) & "."); - -library vunit_lib; -context vunit_lib.vunit_context; - -entity $entity is -end entity; - -architecture arch of $entity is -begin - log("Hello World", line_num => 11, file_name => "$file"); - check_relation(1 /= 2, line_num => 12, file_name => "$file", \ -context_msg => "Expected 1 /= 2. Left is " & to_string(1) & ". Right is " & to_string(2) & "."); - report "Here I am!"; -end architecture; -""") - with open(join(self._preprocessed_path, 'lib', basename(file_name))) as fread: - self.assertEqual(fread.read(), pp_source.substitute(entity='ent0', file=basename(file_name))) - - def test_locally_specified_preprocessors_should_be_used_instead_of_any_globally_defined_preprocessors(self): - ui = self._create_ui() - ui.add_library('lib') - ui.enable_location_preprocessing() - ui.enable_check_preprocessing() - ui.add_preprocessor(TestPreprocessor()) - - file_name1 = self.create_entity_file(1) - file_name2 = self.create_entity_file(2) - - ui.add_source_files(file_name1, 'lib', []) - ui.add_source_files(file_name2, 'lib', [VUnitfier()]) - - pp_source = Template(""" -library vunit_lib; -context vunit_lib.vunit_context; - -entity $entity is -end entity; - -architecture arch of $entity is -begin - log("Hello World"); - check_relation(1 /= 2); - $report -end architecture; -""") - self.assertFalse(exists(join(self._preprocessed_path, 'lib', basename(file_name1)))) - with open(join(self._preprocessed_path, 'lib', basename(file_name2))) as fread: - expectd = pp_source.substitute( - entity='ent2', - report='log("Here I am!"); -- VUnitfier preprocessor: Report turned off, keeping original code.') - self.assertEqual(fread.read(), expectd) - - @mock.patch("vunit.ui.LOGGER.error", autospec=True) - def test_recovers_from_preprocessing_error(self, logger): - ui = self._create_ui() - ui.add_library('lib') - ui.enable_location_preprocessing() - ui.enable_check_preprocessing() - - source_with_error = Template(""" -library vunit_lib; -context vunit_lib.vunit_context; - -entity $entity is -end entity; - -architecture arch of $entity is -begin - log("Hello World"; - check_relation(1 /= 2); - report "Here I am!"; -end architecture; -""") - file_name = join(self.tmp_path, "ent1.vhd") - contents = source_with_error.substitute(entity="ent1") - self.create_file(file_name, contents) - - ui.add_source_file(file_name, 'lib') - logger.assert_called_once_with("Failed to preprocess %s", file_name) - pp_file = join(self._preprocessed_path, 'lib', basename(file_name)) - self.assertFalse(exists(pp_file)) - - def test_supported_source_file_suffixes(self): - """Test adding a supported filetype, of any case, is accepted.""" - ui = self._create_ui() - ui.add_library('lib') - accepted_extensions = VHDL_EXTENSIONS + VERILOG_EXTENSIONS - allowable_extensions = [ext for ext in accepted_extensions] - allowable_extensions.extend([ext.upper() for ext in accepted_extensions]) - allowable_extensions.append(VHDL_EXTENSIONS[0][0] + VHDL_EXTENSIONS[0][1].upper() - + VHDL_EXTENSIONS[0][2:]) # mixed case - for idx, ext in enumerate(allowable_extensions): - file_name = self.create_entity_file(idx, ext) - ui.add_source_files(file_name, 'lib') - - def test_unsupported_source_file_suffixes(self): - """Test adding an unsupported filetype is rejected""" - ui = self._create_ui() - ui.add_library('lib') - unsupported_name = "unsupported.docx" - self.create_file(unsupported_name) - self.assertRaises(RuntimeError, ui.add_source_files, unsupported_name, 'lib') - - def test_exception_on_adding_zero_files(self): - ui = self._create_ui() - lib = ui.add_library("lib") - self.assertRaisesRegex(ValueError, "Pattern.*missing1.vhd.*", - lib.add_source_files, join(dirname(__file__), 'missing1.vhd')) - self.assertRaisesRegex(ValueError, "File.*missing2.vhd.*", - lib.add_source_file, join(dirname(__file__), 'missing2.vhd')) - - def test_no_exception_on_adding_zero_files_when_allowed(self): - ui = self._create_ui() - lib = ui.add_library("lib") - lib.add_source_files(join(dirname(__file__), 'missing.vhd'), allow_empty=True) - - def test_get_test_benchs_and_test(self): - ui = self._create_ui() - lib = ui.add_library("lib") - self.create_file('tb_ent.vhd', ''' -entity tb_ent is - generic (runner_cfg : string); -end entity; - -architecture a of tb_ent is -begin - main : process - begin - if run("test1") then - elsif run("test2") then - end if; - end process; -end architecture; - ''') - - self.create_file('tb_ent2.vhd', ''' -entity tb_ent2 is - generic (runner_cfg : string); -end entity; - -architecture a of tb_ent2 is -begin -end architecture; - ''') - - ui = self._create_ui() - lib = ui.add_library("lib") - lib.add_source_file("tb_ent.vhd") - lib.add_source_file("tb_ent2.vhd") - self.assertEqual(lib.test_bench("tb_ent").name, "tb_ent") - self.assertEqual(lib.test_bench("tb_ent2").name, "tb_ent2") - self.assertEqual(lib.test_bench("tb_ent").library.name, "lib") - - self.assertEqual([test_bench.name for test_bench in lib.get_test_benches()], - ["tb_ent", "tb_ent2"]) - self.assertEqual([test_bench.name for test_bench in lib.get_test_benches("*2")], - ["tb_ent2"]) - - self.assertEqual(lib.test_bench("tb_ent").test("test1").name, "test1") - self.assertEqual(lib.test_bench("tb_ent").test("test2").name, "test2") - - self.assertEqual([test.name for test in lib.test_bench("tb_ent").get_tests()], - ["test1", "test2"]) - self.assertEqual([test.name for test in lib.test_bench("tb_ent").get_tests("*1")], - ["test1"]) - self.assertEqual([test.name for test in lib.test_bench("tb_ent2").get_tests()], - []) - - def test_get_entities_case_insensitive(self): - ui = self._create_ui() - lib = ui.add_library("lib") - self.create_file('tb_ent.vhd', ''' -entity tb_Ent is - generic (runner_cfg : string); -end entity; - ''') - lib.add_source_file("tb_ent.vhd") - self.assertEqual(lib.test_bench("tb_Ent").name, "tb_ent") - self.assertEqual(lib.test_bench("TB_ENT").name, "tb_ent") - self.assertEqual(lib.test_bench("tb_ent").name, "tb_ent") - - self.assertEqual(lib.entity("tb_Ent").name, "tb_ent") - self.assertEqual(lib.entity("TB_ENT").name, "tb_ent") - self.assertEqual(lib.entity("tb_ent").name, "tb_ent") - - def test_add_source_files(self): - files = ["file1.vhd", - "file2.vhd", - "file3.vhd", - "foo_file.vhd"] - - for file_name in files: - self.create_file(file_name) - - ui = self._create_ui() - lib = ui.add_library("lib") - lib.add_source_files("file*.vhd") - lib.add_source_files("foo_file.vhd") - for file_name in files: - assert lib.get_source_file(file_name).name.endswith(file_name) - - ui = self._create_ui() - lib = ui.add_library("lib") - lib.add_source_files(["file*.vhd", "foo_file.vhd"]) - for file_name in files: - lib.get_source_file(file_name).name.endswith(file_name) - - ui = self._create_ui() - lib = ui.add_library("lib") - lib.add_source_files(("file*.vhd", "foo_file.vhd")) - for file_name in files: - lib.get_source_file(file_name).name.endswith(file_name) - - ui = self._create_ui() - lib = ui.add_library("lib") - lib.add_source_files(iter(["file*.vhd", "foo_file.vhd"])) - for file_name in files: - lib.get_source_file(file_name).name.endswith(file_name) - - def test_add_source_files_from_csv(self): - csv = """ - lib, tb_example.vhdl - lib1 , tb_example1.vhd - lib2, tb_example2.vhd - lib3,"tb,ex3.vhd" - """ - - libraries = ['lib', 'lib1', 'lib2', 'lib3'] - files = ['tb_example.vhdl', 'tb_example1.vhd', 'tb_example2.vhd', 'tb,ex3.vhd'] - - self.create_csv_file('test_csv.csv', csv) - for file_name in files: - self.create_file(file_name) - - ui = self._create_ui() - ui.add_source_files_from_csv('test_csv.csv') - - for index, library_name in enumerate(libraries): - file_name = files[index] - file_name_from_ui = ui.get_source_file(file_name, library_name) - self.assertIsNotNone(file_name_from_ui) - - def test_add_source_files_from_csv_return(self): - csv = """ - lib, tb_example.vhd - lib, tb_example1.vhd - lib, tb_example2.vhd - """ - - list_of_files = ['tb_example.vhd', 'tb_example1.vhd', 'tb_example2.vhd'] - - for index, file_ in enumerate(list_of_files): - self.create_file(file_, str(index)) - - self.create_csv_file('test_returns.csv', csv) - ui = self._create_ui() - - source_files = ui.add_source_files_from_csv('test_returns.csv') - self.assertEqual([source_file.name for source_file in source_files], list_of_files) - - def test_add_source_files_errors(self): - ui = self._create_ui() - lib = ui.add_library("lib") - self.create_file("file.vhd") - self.assertRaisesRegex(ValueError, r"missing\.vhd", lib.add_source_files, ["missing.vhd", "file.vhd"]) - self.assertRaisesRegex(ValueError, r"missing\.vhd", lib.add_source_files, "missing.vhd") - - def test_get_source_files(self): - ui = self._create_ui() - lib1 = ui.add_library("lib1") - lib2 = ui.add_library("lib2") - file_name = self.create_entity_file() - lib1.add_source_file(file_name) - lib2.add_source_file(file_name) - - self.assertEqual(len(ui.get_source_files(file_name)), 2) - self.assertEqual(len(lib1.get_source_files(file_name)), 1) - self.assertEqual(len(lib2.get_source_files(file_name)), 1) - - ui.get_source_file(file_name, library_name="lib1") - ui.get_source_file(file_name, library_name="lib2") - lib1.get_source_file(file_name) - lib2.get_source_file(file_name) - - def test_get_compile_order_smoke_test(self): - ui = self._create_ui() - lib1 = ui.add_library("lib1") - lib2 = ui.add_library("lib2") - file_name = self.create_entity_file() - lib1.add_source_file(file_name) - lib2.add_source_file(file_name) - - # Without argument - compile_order = ui.get_compile_order() - self.assertEqual(len(compile_order), 2) - self.assertEqual(compile_order[0].name, file_name) - self.assertEqual(compile_order[1].name, file_name) - - # With argument - compile_order = ui.get_compile_order([lib1.get_source_file(file_name)]) - self.assertEqual(len(compile_order), 1) - self.assertEqual(compile_order[0].name, file_name) - - def test_list_files_flag(self): - ui = self._create_ui("--files") - lib1 = ui.add_library("lib1") - lib2 = ui.add_library("lib2") - file_name = self.create_entity_file() - lib1.add_source_file(file_name) - lib2.add_source_file(file_name) - - with mock.patch("sys.stdout", autospec=True) as stdout: - self._run_main(ui) - text = "".join([call[1][0] for call in stdout.write.mock_calls]) - # @TODO not always in the same order in Python3 due to dependency graph - self.assertEqual(set(text.splitlines()), - set("""\ -lib2, ent0.vhd -lib1, ent0.vhd -Listed 2 files""".splitlines())) - - @with_tempdir - def test_filtering_tests(self, tempdir): - def setup(ui): - " Setup the project " - lib = ui.add_library("lib") - file_name = join(tempdir, "tb_filter.vhd") - create_vhdl_test_bench_file("tb_filter", file_name, - tests=["Test 1", "Test 2", "Test 3", "Test 4"], - test_attributes={ - "Test 1": [".attr0"], - "Test 2": [".attr0", ".attr1"], - "Test 3": [".attr1"], - "Test 4": [] - }) - lib.add_source_file(file_name) - lib.test_bench("tb_filter").test("Test 4").set_attribute(".attr2", None) - - def check_stdout(ui, expected): - " Check that stdout matches expected " - with mock.patch("sys.stdout", autospec=True) as stdout: - self._run_main(ui) - text = "".join([call[1][0] for call in stdout.write.mock_calls]) - # @TODO not always in the same order in Python3 due to dependency graph - print(text) - self.assertEqual(set(text.splitlines()), - set(expected.splitlines())) - - ui = self._create_ui("--list", "*Test 1") - setup(ui) - check_stdout(ui, "lib.tb_filter.Test 1\nListed 1 tests") - - ui = self._create_ui("--list", "*Test*") - setup(ui) - check_stdout(ui, - "lib.tb_filter.Test 1\n" - "lib.tb_filter.Test 2\n" - "lib.tb_filter.Test 3\n" - "lib.tb_filter.Test 4\n" - "Listed 4 tests") - - ui = self._create_ui("--list", "*2*") - setup(ui) - check_stdout(ui, - "lib.tb_filter.Test 2\n" - "Listed 1 tests") - - ui = self._create_ui("--list", "--with-attribute=.attr0") - setup(ui) - check_stdout(ui, - "lib.tb_filter.Test 1\n" - "lib.tb_filter.Test 2\n" - "Listed 2 tests") - - ui = self._create_ui("--list", "--with-attribute=.attr2") - setup(ui) - check_stdout(ui, - "lib.tb_filter.Test 4\n" - "Listed 1 tests") - - ui = self._create_ui("--list", "--with-attributes", ".attr0", "--with-attributes", ".attr1") - setup(ui) - check_stdout(ui, - "lib.tb_filter.Test 2\n" - "Listed 1 tests") - - ui = self._create_ui("--list", "--without-attributes", ".attr0") - setup(ui) - check_stdout(ui, - "lib.tb_filter.Test 3\n" - "lib.tb_filter.Test 4\n" - "Listed 2 tests") - - ui = self._create_ui("--list", "--without-attributes", ".attr0", "--without-attributes", ".attr1") - setup(ui) - check_stdout(ui, - "lib.tb_filter.Test 4\n" - "Listed 1 tests") - - ui = self._create_ui("--list", - "--with-attributes", ".attr0", - "--without-attributes", ".attr1") - setup(ui) - check_stdout(ui, - "lib.tb_filter.Test 1\n" - "Listed 1 tests") - - @with_tempdir - def test_export_json(self, tempdir): - json_file = join(tempdir, "export.json") - - ui = self._create_ui("--export-json", json_file) - lib1 = ui.add_library("lib1") - lib2 = ui.add_library("lib2") - - file_name1 = join(tempdir, "tb_foo.vhd") - create_vhdl_test_bench_file("tb_foo", file_name1) - lib1.add_source_file(file_name1) - - file_name2 = join(tempdir, "tb_bar.vhd") - create_vhdl_test_bench_file("tb_bar", file_name2, - tests=["Test one", "Test two"], - test_attributes={"Test one": [".attr0"]}) - lib2.add_source_file(file_name2) - lib2.test_bench("tb_bar").set_attribute(".attr1", "bar") - - self._run_main(ui) - - with open(json_file, "r") as fptr: - data = json.load(fptr) - - # Check known keys - self.assertEqual(set(data.keys()), - set(["export_format_version", - "files", - "tests"])) - - # Check that export format is semantic version with integer values - self.assertEqual(set(data["export_format_version"].keys()), - set(("major", "minor", "patch"))) - assert all(isinstance(value, int) - for value in data["export_format_version"].values()) - - # Check the contents of the files section - self.assertEqual(set((item["library_name"], item["file_name"]) - for item in data["files"]), - set([("lib1", abspath(file_name1)), - ("lib2", abspath(file_name2))])) - - # Check the contents of the tests section - self.assertEqual({item["name"]: (item["location"], item["attributes"]) for item in data["tests"]}, - {"lib1.tb_foo.all": ({"file_name": file_name1, "offset": 180, "length": 18}, - {}), - "lib2.tb_bar.Test one": ({"file_name": file_name2, "offset": 235, "length": 8}, - {".attr0": None, ".attr1": "bar"}), - "lib2.tb_bar.Test two": ({"file_name": file_name2, "offset": 283, "length": 8}, - {".attr1": "bar"})}) - - def test_library_attributes(self): - ui = self._create_ui() - lib1 = ui.add_library("lib1") - self.assertEqual(lib1.name, "lib1") - - def test_source_file_attributes(self): - ui = self._create_ui() - lib = ui.add_library("lib") - self.create_file("file_name.vhd") - source_file = lib.add_source_file("file_name.vhd") - self.assertEqual(source_file.name, "file_name.vhd") - self.assertEqual(source_file.library.name, "lib") - - def test_get_source_files_errors(self): - ui = self._create_ui() - lib1 = ui.add_library("lib1") - lib2 = ui.add_library("lib2") - file_name = self.create_entity_file() - lib1.add_source_file(file_name) - lib2.add_source_file(file_name) - non_existant_name = "non_existant" - - self.assertRaisesRegex(ValueError, ".*%s.*allow_empty.*" % non_existant_name, - ui.get_source_files, non_existant_name) - self.assertEqual(len(ui.get_source_files(non_existant_name, allow_empty=True)), 0) - - self.assertRaisesRegex(ValueError, ".*named.*%s.*multiple.*library_name.*" % file_name, - ui.get_source_file, file_name) - - self.assertRaisesRegex(ValueError, ".*Found no file named.*%s'" % non_existant_name, - ui.get_source_file, non_existant_name) - - self.assertRaisesRegex(ValueError, ".*Found no file named.*%s.* in library 'lib1'" % non_existant_name, - ui.get_source_file, non_existant_name, "lib1") - - def test_add_single_file_manual_dependencies(self): - ui = self._create_ui() - lib = ui.add_library("lib") - file_name1 = self.create_entity_file(1) - file_name2 = self.create_entity_file(2) - file1 = lib.add_source_file(file_name1) - file2 = lib.add_source_file(file_name2) - self.assertEqual(names(ui.get_compile_order([file1])), names([file1])) - self.assertEqual(names(ui.get_compile_order([file2])), names([file2])) - file1.add_dependency_on(file2) - self.assertEqual(names(ui.get_compile_order([file1])), names([file2, file1])) - self.assertEqual(names(ui.get_compile_order([file2])), names([file2])) - - def test_add_multiple_file_manual_dependencies(self): - ui = self._create_ui() - lib = ui.add_library("lib") - self.create_file("foo1.vhd") - self.create_file("foo2.vhd") - self.create_file("foo3.vhd") - self.create_file("bar.vhd") - foo_files = lib.add_source_files("foo*.vhd") - bar_file = lib.add_source_file("bar.vhd") - - self.assertEqual(names(ui.get_compile_order([bar_file])), names([bar_file])) - bar_file.add_dependency_on(foo_files) - self.assertEqual(sorted(names(ui.get_compile_order([bar_file]))), sorted(names(foo_files + [bar_file]))) - self.assertEqual(ui.get_compile_order([bar_file])[-1].name, bar_file.name) - - def test_add_fileset_manual_dependencies(self): - ui = self._create_ui() - lib = ui.add_library("lib") - self.create_file("foo1.vhd") - self.create_file("foo2.vhd") - self.create_file("foo3.vhd") - self.create_file("bar.vhd") - foo_files = lib.add_source_files("foo*.vhd") - bar_file = lib.add_source_file("bar.vhd") - - for foo_file in foo_files: - self.assertEqual(names(ui.get_compile_order([foo_file])), names([foo_file])) - - foo_files.add_dependency_on(bar_file) - - for foo_file in foo_files: - self.assertEqual(names(ui.get_compile_order([foo_file])), names([bar_file, foo_file])) - - def test_add_source_files_has_include_dirs(self): - file_name = "verilog.v" - include_dirs = ["include_dir"] - all_include_dirs = add_verilog_include_dir(include_dirs) - self.create_file(file_name) - - def check(action): - """ - Helper to check that project method was called - """ - ui = self._create_ui() - with mock.patch.object(ui, "_project", autospec=True) as project: - project.has_library.return_value = True - lib = ui.library("lib") - action(ui, lib) - project.add_source_file.assert_called_once_with(abspath("verilog.v"), - "lib", - file_type="verilog", - include_dirs=all_include_dirs, - defines=None, - vhdl_standard=None, - no_parse=False) - check(lambda ui, _: ui.add_source_files(file_name, "lib", include_dirs=include_dirs)) - check(lambda ui, _: ui.add_source_file(file_name, "lib", include_dirs=include_dirs)) - check(lambda _, lib: lib.add_source_files(file_name, include_dirs=include_dirs)) - check(lambda _, lib: lib.add_source_file(file_name, include_dirs=include_dirs)) - - def test_add_source_files_has_defines(self): - file_name = "verilog.v" - self.create_file(file_name) - all_include_dirs = add_verilog_include_dir([]) - defines = {"foo": "bar"} - - def check(action): - """ - Helper to check that project method was called - """ - ui = self._create_ui() - with mock.patch.object(ui, "_project", autospec=True) as project: - project.has_library.return_value = True - lib = ui.library("lib") - action(ui, lib) - project.add_source_file.assert_called_once_with(abspath("verilog.v"), - "lib", - file_type="verilog", - include_dirs=all_include_dirs, - defines=defines, - vhdl_standard=None, - no_parse=False) - check(lambda ui, _: ui.add_source_files(file_name, "lib", defines=defines)) - check(lambda ui, _: ui.add_source_file(file_name, "lib", defines=defines)) - check(lambda _, lib: lib.add_source_files(file_name, defines=defines)) - check(lambda _, lib: lib.add_source_file(file_name, defines=defines)) - - def test_add_source_files_has_no_parse(self): - file_name = "verilog.v" - self.create_file(file_name) - all_include_dirs = add_verilog_include_dir([]) - - for no_parse in (True, False): - for method in range(4): - - ui = self._create_ui() - with mock.patch.object(ui, "_project", autospec=True) as project: - project.has_library.return_value = True - - if method == 0: - ui.add_source_files(file_name, "lib", no_parse=no_parse) - elif method == 1: - ui.add_source_file(file_name, "lib", no_parse=no_parse) - elif method == 2: - lib = ui.library("lib") - lib.add_source_files(file_name, no_parse=no_parse) - elif method == 3: - lib = ui.library("lib") - lib.add_source_file(file_name, no_parse=no_parse) - - project.add_source_file.assert_called_once_with(abspath("verilog.v"), - "lib", - file_type="verilog", - include_dirs=all_include_dirs, - defines=None, - vhdl_standard=None, - no_parse=no_parse) - - def test_compile_options(self): - file_name = "foo.vhd" - self.create_file(file_name) - ui = self._create_ui() - lib = ui.add_library("lib") - source_file = lib.add_source_file(file_name) - - # Use methods on all types of interface objects - for obj in [source_file, ui, lib, lib.get_source_files(file_name)]: - obj.set_compile_option("ghdl.flags", []) - self.assertEqual(source_file.get_compile_option("ghdl.flags"), []) - - obj.add_compile_option("ghdl.flags", ["1"]) - self.assertEqual(source_file.get_compile_option("ghdl.flags"), ["1"]) - - obj.add_compile_option("ghdl.flags", ["2"]) - self.assertEqual(source_file.get_compile_option("ghdl.flags"), ["1", "2"]) - - obj.set_compile_option("ghdl.flags", ["3"]) - self.assertEqual(source_file.get_compile_option("ghdl.flags"), ["3"]) - - def test_default_vhdl_standard_is_used(self): - file_name = "foo.vhd" - self.create_file(file_name) - with set_env(VUNIT_VHDL_STANDARD="93"): - ui = self._create_ui() - lib = ui.add_library("lib") - source_file = lib.add_source_file(file_name) - self.assertEqual(source_file.vhdl_standard, "93") - - def test_library_default_vhdl_standard_is_used(self): - file_name = "foo.vhd" - self.create_file(file_name) - - for method in range(4): - with set_env(VUNIT_VHDL_STANDARD="93"): - ui = self._create_ui() - - if method == 0: - lib = ui.add_library("lib", vhdl_standard="2002") - source_file = lib.add_source_file(file_name) - elif method == 1: - lib = ui.add_library("lib", vhdl_standard="2002") - source_file = ui.add_source_file(file_name, "lib") - elif method == 2: - lib = ui.add_library("lib", vhdl_standard="2002") - source_file = lib.add_source_files(file_name)[0] - elif method == 3: - lib = ui.add_library("lib", vhdl_standard="2002") - source_file = ui.add_source_files(file_name, "lib")[0] - - self.assertEqual(source_file.vhdl_standard, "2002") - - def test_add_source_file_vhdl_standard_is_used(self): - file_name = "foo.vhd" - self.create_file(file_name) - - for method in range(4): - with set_env(VUNIT_VHDL_STANDARD="93"): - ui = self._create_ui() - lib = ui.add_library("lib", vhdl_standard="2002") - - if method == 0: - source_file = lib.add_source_file(file_name, vhdl_standard="2008") - elif method == 1: - source_file = lib.add_source_files(file_name, vhdl_standard="2008")[0] - elif method == 2: - source_file = ui.add_source_file(file_name, "lib", vhdl_standard="2008") - elif method == 3: - source_file = ui.add_source_files(file_name, "lib", vhdl_standard="2008")[0] - - self.assertEqual(source_file.vhdl_standard, "2008") - - def test_verilog_file_has_vhdl_standard_none(self): - file_name = "foo.v" - self.create_file(file_name) - ui = self._create_ui() - lib = ui.add_library("lib") - source_file = lib.add_source_file(file_name) - self.assertEqual(source_file.vhdl_standard, None) - - @mock.patch("vunit.test_bench_list.LOGGER", autospec=True) - def test_warning_on_no_tests(self, logger): - ui = self._create_ui("--compile") - self._run_main(ui) - self.assertEqual(logger.warning.mock_calls, []) - logger.reset_mock() - - ui = self._create_ui("--list") - self._run_main(ui) - self.assertEqual(len(logger.warning.mock_calls), 1) - self.assertTrue("Found no test benches" in str(logger.warning.mock_calls)) - logger.reset_mock() - - ui = self._create_ui() - self._run_main(ui) - self.assertEqual(len(logger.warning.mock_calls), 1) - self.assertTrue("Found no test benches" in str(logger.warning.mock_calls)) - logger.reset_mock() - - def test_post_run(self): - post_run = mock.Mock() - - ui = self._create_ui() - self._run_main(ui, post_run=post_run) - self.assertTrue(post_run.called) - - for no_run_arg in ['--compile', '--files', '--list']: - post_run.reset_mock() - ui = self._create_ui(no_run_arg) - self._run_main(ui, post_run=post_run) - self.assertFalse(post_run.called) - - def test_error_on_adding_duplicate_library(self): - ui = self._create_ui() - ui.add_library("lib") - self.assertRaises(ValueError, ui.add_library, "lib") - - def test_allow_adding_duplicate_library(self): - ui = self._create_ui() - - file_name = "file.vhd" - self.create_file(file_name) - - file_name2 = "file2.vhd" - self.create_file(file_name2) - - lib1 = ui.add_library("lib") - source_file1 = lib1.add_source_file(file_name) - self.assertEqual([source_file.name for source_file in lib1.get_source_files()], - [source_file1.name]) - - lib2 = ui.add_library("lib", allow_duplicate=True) - for lib in [lib1, lib2]: - self.assertEqual([source_file.name for source_file in lib.get_source_files()], - [source_file1.name]) - - source_file2 = lib2.add_source_file(file_name2) - for lib in [lib1, lib2]: - self.assertEqual({source_file.name for source_file in lib.get_source_files()}, - {source_file1.name, source_file2.name}) - - def test_scan_tests_from_other_file(self): - for tb_type in ["vhdl", "verilog"]: - for tests_type in ["vhdl", "verilog"]: - ui = self._create_ui() - lib = ui.add_library("lib") - - if tb_type == "vhdl": - tb_file_name = "tb_top.vhd" - self.create_file(tb_file_name, """ -entity tb_top is - generic (runner_cfg : string); -end entity; - -architecture a of tb_top is -begin -end architecture; - """) - else: - tb_file_name = "tb_top.sv" - self.create_file(tb_file_name, """ -module tb_top - parameter string runner_cfg = ""; - tests #(.nested_runner_cfg(runner_cfg)) tests_inst(); -endmodule; - """) - - if tests_type == "vhdl": - tests_file_name = "tests.vhd" - self.create_file(tests_file_name, """ -entity tests is - generic (nested_runner_cfg : string); -end entity; - -architecture a of tests is -begin - main : process - if run("test1") then - elsif run("test2") - end if; - end process; -end architecture; - """) - else: - tests_file_name = "tests.sv" - self.create_file(tests_file_name, """ -`include "vunit_defines.svh" -module tests; - `NESTED_TEST_SUITE begin - `TEST_CASE("test1") begin - end - `TEST_CASE("test2") begin - end - end; -endmodule - """) - - lib.add_source_file(tb_file_name) - lib.add_source_file(tests_file_name) - - # tb_top is a single test case in itself - self.assertEqual(ui.library("lib").test_bench("tb_top").get_tests(), []) - - if tb_type == "vhdl": - test_bench = lib.entity("tb_top") - else: - test_bench = lib.module("tb_top") - test_bench.scan_tests_from_file(tests_file_name) - - self.assertEqual([test.name - for test in ui.library("lib").test_bench("tb_top").get_tests()], - ["test1", - "test2"]) - - def test_scan_tests_from_other_file_missing(self): - ui = self._create_ui() - lib = ui.add_library("lib") - tb_file_name = "tb_top.sv" - self.create_file(tb_file_name, """ -`include "vunit_defines.svh" -module tb_top; - `TEST_SUITE begin - `TEST_CASE("test1") begin - end - end; -endmodule - """) - lib.add_source_file(tb_file_name) - self.assertRaises(ValueError, lib.test_bench("tb_top").scan_tests_from_file, "missing.sv") - - def test_can_list_tests_without_simulator(self): - with set_env(): - ui = self._create_ui_real_sim("--list") - self._run_main(ui, 0) - - def test_can_list_files_without_simulator(self): - with set_env(): - ui = self._create_ui_real_sim("--files") - self._run_main(ui, 0) - - @mock.patch("vunit.ui.LOGGER", autospec=True) - def test_compile_without_simulator_fails(self, logger): - with set_env(): - ui = self._create_ui_real_sim("--compile") - self._run_main(ui, 1) - self.assertEqual(len(logger.error.mock_calls), 1) - self.assertTrue("No available simulator detected" in str(logger.error.mock_calls)) - - @mock.patch("vunit.ui.LOGGER", autospec=True) - def test_simulate_without_simulator_fails(self, logger): - with set_env(): - ui = self._create_ui_real_sim() - self._run_main(ui, 1) - self.assertEqual(len(logger.error.mock_calls), 1) - self.assertTrue("No available simulator detected" in str(logger.error.mock_calls)) - - def test_set_sim_option_before_adding_file(self): - """ - From GitHub issue #250 - """ - ui = self._create_ui() - lib = ui.add_library("lib") - for method in (lib.set_sim_option, ui.set_sim_option): - method("disable_ieee_warnings", True, allow_empty=True) - self.assertRaises(ValueError, method, "disable_ieee_warnings", True) - - for method in (lib.set_compile_option, lib.add_compile_option, ui.set_compile_option, ui.add_compile_option): - method("ghdl.elab_flags", [], allow_empty=True) - self.assertRaises(ValueError, method, "ghdl.elab_flags", []) - - def _create_ui(self, *args): - """ Create an instance of the VUnit public interface class """ - with mock.patch("vunit.ui.SIMULATOR_FACTORY.select_simulator", - new=lambda: MockSimulator): - return self._create_ui_real_sim(*args) - - def _create_ui_real_sim(self, *args): - """ Create an instance of the VUnit public interface class """ - return VUnit.from_argv(argv=["--output-path=%s" % self._output_path, - "--clean"] + list(args), - compile_builtins=False) - - def _run_main(self, ui, code=0, post_run=None): - """ - Run ui.main and expect exit code - """ - try: - ui.main(post_run=post_run) - except SystemExit as exc: - self.assertEqual(exc.code, code) - - def create_entity_file(self, idx=0, file_suffix='.vhd'): - """ - Create and a temporary file containing the same source code - but with different entity names depending on the index - """ - source = Template(""" -library vunit_lib; -context vunit_lib.vunit_context; - -entity $entity is -end entity; - -architecture arch of $entity is -begin - log("Hello World"); - check_relation(1 /= 2); - report "Here I am!"; -end architecture; -""") - - entity_name = "ent%i" % idx - file_name = entity_name + file_suffix - self.create_file(file_name, - source.substitute(entity=entity_name)) - return file_name - - @staticmethod - def create_file(file_name, contents=""): - """ - Creata file in the temporary path with given contents - """ - with open(file_name, "w") as fptr: - fptr.write(contents) - - @staticmethod - def create_csv_file(file_name, contents=''): - """ - Create a temporary csv description file with given contents - """ - with open(file_name, "w") as fprt: - fprt.write(contents) - - def assertRaisesRegex(self, *args, **kwargs): # pylint: disable=invalid-name,arguments-differ - """ - Python 2/3 compatability - """ - if hasattr(unittest.TestCase, "assertRaisesRegex"): - unittest.TestCase.assertRaisesRegex(self, *args, **kwargs) # pylint: disable=no-member - else: - unittest.TestCase.assertRaisesRegexp(self, *args, **kwargs) # pylint: disable=deprecated-method - - -class TestPreprocessor(object): - """ - A preprocessor that appends a check_relation call before the orginal code - """ - def __init__(self): - pass - - @staticmethod - def run(code, file_name): # pylint: disable=unused-argument - return '-- check_relation(a = b);\n' + code - - -class VUnitfier(object): - """ - A preprocessor that replaces report statments with log calls - """ - def __init__(self): - self._report_pattern = re.compile(r'^(?P\s*)report\s*(?P"[^"]*")\s*;', MULTILINE) - - def run(self, code, file_name): # pylint: disable=unused-argument - return self._report_pattern.sub( - r'\glog(\g); -- VUnitfier preprocessor: Report turned off, keeping original code.', code) - - -class ParentalControl(object): - """ - A preprocessor that replaces f..k with [BEEP] - """ - def __init__(self): - self._fword_pattern = re.compile(r'f..k') - - def run(self, code, file_name): # pylint: disable=unused-argument - return self._fword_pattern.sub(r'[BEEP]', code) - - -class MockSimulator(SimulatorInterface): - """ - Mock of a SimulatorInterface - """ - name = "mock" - - @staticmethod - def from_args(output_path, *args, **kwargs): # pylint: disable=unused-argument - return MockSimulator(output_path="", gui=False) - - package_users_depend_on_bodies = False - - @staticmethod - def compile_source_file_command(source_file): # pylint: disable=unused-argument - return True - - @staticmethod - def simulate(output_path, test_suite_name, config, elaborate_only): # pylint: disable=unused-argument - return True - - -def names(lst): - """ - Return a list of the .name attribute of the objects in the list - """ - return [obj.name for obj in lst] +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +# +# pylint: disable=too-many-public-methods, too-many-lines + +""" +Acceptance test of the VUnit public interface class +""" + +from __future__ import print_function +import unittest +from string import Template +import os +from os.path import join, dirname, basename, exists, abspath +import json +import re +from re import MULTILINE +from shutil import rmtree +from vunit.ui import VUnit +from vunit.project import VHDL_EXTENSIONS, VERILOG_EXTENSIONS +from vunit.test.mock_2or3 import mock +from vunit.test.common import (set_env, + with_tempdir, + create_vhdl_test_bench_file) +from vunit.ostools import renew_path +from vunit.builtins import add_verilog_include_dir +from vunit.simulator_interface import SimulatorInterface + + +class TestUi(unittest.TestCase): + """ + Testing the VUnit public interface class + """ + def setUp(self): + self.tmp_path = join(dirname(__file__), "test_ui_tmp") + renew_path(self.tmp_path) + self.cwd = os.getcwd() + os.chdir(self.tmp_path) + + self._output_path = join(self.tmp_path, 'output') + self._preprocessed_path = join(self._output_path, "preprocessed") + + def tearDown(self): + os.chdir(self.cwd) + if exists(self.tmp_path): + rmtree(self.tmp_path) + + def test_global_custom_preprocessors_should_be_applied_in_the_order_they_are_added(self): + ui = self._create_ui() + ui.add_library('lib') + ui.add_preprocessor(VUnitfier()) + ui.add_preprocessor(ParentalControl()) + + file_name = self.create_entity_file() + ui.add_source_files(file_name, 'lib') + + pp_source = Template(""" +library vunit_lib; +context vunit_lib.vunit_context; + +entity $entity is +end entity; + +architecture arch of $entity is +begin + log("Hello World"); + check_relation(1 /= 2); + log("Here I am!"); -- VUnitfier preprocessor: Report turned of[BEEP]eeping original code. +end architecture; +""") + with open(join(self._preprocessed_path, 'lib', basename(file_name))) as fread: + self.assertEqual(fread.read(), pp_source.substitute(entity='ent0', file=basename(file_name))) + + def test_global_check_and_location_preprocessors_should_be_applied_after_global_custom_preprocessors(self): + ui = self._create_ui() + ui.add_library('lib') + ui.enable_location_preprocessing() + ui.enable_check_preprocessing() + ui.add_preprocessor(TestPreprocessor()) + + file_name = self.create_entity_file() + ui.add_source_files(file_name, 'lib') + + pp_source = Template("""\ +-- check_relation(a = b, line_num => 1, file_name => "$file", \ +context_msg => "Expected a = b. Left is " & to_string(a) & ". Right is " & to_string(b) & "."); + +library vunit_lib; +context vunit_lib.vunit_context; + +entity $entity is +end entity; + +architecture arch of $entity is +begin + log("Hello World", line_num => 11, file_name => "$file"); + check_relation(1 /= 2, line_num => 12, file_name => "$file", \ +context_msg => "Expected 1 /= 2. Left is " & to_string(1) & ". Right is " & to_string(2) & "."); + report "Here I am!"; +end architecture; +""") + with open(join(self._preprocessed_path, 'lib', basename(file_name))) as fread: + self.assertEqual(fread.read(), pp_source.substitute(entity='ent0', file=basename(file_name))) + + def test_locally_specified_preprocessors_should_be_used_instead_of_any_globally_defined_preprocessors(self): + ui = self._create_ui() + ui.add_library('lib') + ui.enable_location_preprocessing() + ui.enable_check_preprocessing() + ui.add_preprocessor(TestPreprocessor()) + + file_name1 = self.create_entity_file(1) + file_name2 = self.create_entity_file(2) + + ui.add_source_files(file_name1, 'lib', []) + ui.add_source_files(file_name2, 'lib', [VUnitfier()]) + + pp_source = Template(""" +library vunit_lib; +context vunit_lib.vunit_context; + +entity $entity is +end entity; + +architecture arch of $entity is +begin + log("Hello World"); + check_relation(1 /= 2); + $report +end architecture; +""") + self.assertFalse(exists(join(self._preprocessed_path, 'lib', basename(file_name1)))) + with open(join(self._preprocessed_path, 'lib', basename(file_name2))) as fread: + expectd = pp_source.substitute( + entity='ent2', + report='log("Here I am!"); -- VUnitfier preprocessor: Report turned off, keeping original code.') + self.assertEqual(fread.read(), expectd) + + @mock.patch("vunit.ui.LOGGER.error", autospec=True) + def test_recovers_from_preprocessing_error(self, logger): + ui = self._create_ui() + ui.add_library('lib') + ui.enable_location_preprocessing() + ui.enable_check_preprocessing() + + source_with_error = Template(""" +library vunit_lib; +context vunit_lib.vunit_context; + +entity $entity is +end entity; + +architecture arch of $entity is +begin + log("Hello World"; + check_relation(1 /= 2); + report "Here I am!"; +end architecture; +""") + file_name = join(self.tmp_path, "ent1.vhd") + contents = source_with_error.substitute(entity="ent1") + self.create_file(file_name, contents) + + ui.add_source_file(file_name, 'lib') + logger.assert_called_once_with("Failed to preprocess %s", file_name) + pp_file = join(self._preprocessed_path, 'lib', basename(file_name)) + self.assertFalse(exists(pp_file)) + + def test_supported_source_file_suffixes(self): + """Test adding a supported filetype, of any case, is accepted.""" + ui = self._create_ui() + ui.add_library('lib') + accepted_extensions = VHDL_EXTENSIONS + VERILOG_EXTENSIONS + allowable_extensions = [ext for ext in accepted_extensions] + allowable_extensions.extend([ext.upper() for ext in accepted_extensions]) + allowable_extensions.append(VHDL_EXTENSIONS[0][0] + VHDL_EXTENSIONS[0][1].upper() + + VHDL_EXTENSIONS[0][2:]) # mixed case + for idx, ext in enumerate(allowable_extensions): + file_name = self.create_entity_file(idx, ext) + ui.add_source_files(file_name, 'lib') + + def test_unsupported_source_file_suffixes(self): + """Test adding an unsupported filetype is rejected""" + ui = self._create_ui() + ui.add_library('lib') + unsupported_name = "unsupported.docx" + self.create_file(unsupported_name) + self.assertRaises(RuntimeError, ui.add_source_files, unsupported_name, 'lib') + + def test_exception_on_adding_zero_files(self): + ui = self._create_ui() + lib = ui.add_library("lib") + self.assertRaisesRegex(ValueError, "Pattern.*missing1.vhd.*", + lib.add_source_files, join(dirname(__file__), 'missing1.vhd')) + self.assertRaisesRegex(ValueError, "File.*missing2.vhd.*", + lib.add_source_file, join(dirname(__file__), 'missing2.vhd')) + + def test_no_exception_on_adding_zero_files_when_allowed(self): + ui = self._create_ui() + lib = ui.add_library("lib") + lib.add_source_files(join(dirname(__file__), 'missing.vhd'), allow_empty=True) + + def test_get_test_benchs_and_test(self): + ui = self._create_ui() + lib = ui.add_library("lib") + self.create_file('tb_ent.vhd', ''' +entity tb_ent is + generic (runner_cfg : string); +end entity; + +architecture a of tb_ent is +begin + main : process + begin + if run("test1") then + elsif run("test2") then + end if; + end process; +end architecture; + ''') + + self.create_file('tb_ent2.vhd', ''' +entity tb_ent2 is + generic (runner_cfg : string); +end entity; + +architecture a of tb_ent2 is +begin +end architecture; + ''') + + ui = self._create_ui() + lib = ui.add_library("lib") + lib.add_source_file("tb_ent.vhd") + lib.add_source_file("tb_ent2.vhd") + self.assertEqual(lib.test_bench("tb_ent").name, "tb_ent") + self.assertEqual(lib.test_bench("tb_ent2").name, "tb_ent2") + self.assertEqual(lib.test_bench("tb_ent").library.name, "lib") + + self.assertEqual([test_bench.name for test_bench in lib.get_test_benches()], + ["tb_ent", "tb_ent2"]) + self.assertEqual([test_bench.name for test_bench in lib.get_test_benches("*2")], + ["tb_ent2"]) + + self.assertEqual(lib.test_bench("tb_ent").test("test1").name, "test1") + self.assertEqual(lib.test_bench("tb_ent").test("test2").name, "test2") + + self.assertEqual([test.name for test in lib.test_bench("tb_ent").get_tests()], + ["test1", "test2"]) + self.assertEqual([test.name for test in lib.test_bench("tb_ent").get_tests("*1")], + ["test1"]) + self.assertEqual([test.name for test in lib.test_bench("tb_ent2").get_tests()], + []) + + def test_get_entities_case_insensitive(self): + ui = self._create_ui() + lib = ui.add_library("lib") + self.create_file('tb_ent.vhd', ''' +entity tb_Ent is + generic (runner_cfg : string); +end entity; + ''') + lib.add_source_file("tb_ent.vhd") + self.assertEqual(lib.test_bench("tb_Ent").name, "tb_ent") + self.assertEqual(lib.test_bench("TB_ENT").name, "tb_ent") + self.assertEqual(lib.test_bench("tb_ent").name, "tb_ent") + + self.assertEqual(lib.entity("tb_Ent").name, "tb_ent") + self.assertEqual(lib.entity("TB_ENT").name, "tb_ent") + self.assertEqual(lib.entity("tb_ent").name, "tb_ent") + + def test_add_source_files(self): + files = ["file1.vhd", + "file2.vhd", + "file3.vhd", + "foo_file.vhd"] + + for file_name in files: + self.create_file(file_name) + + ui = self._create_ui() + lib = ui.add_library("lib") + lib.add_source_files("file*.vhd") + lib.add_source_files("foo_file.vhd") + for file_name in files: + assert lib.get_source_file(file_name).name.endswith(file_name) + + ui = self._create_ui() + lib = ui.add_library("lib") + lib.add_source_files(["file*.vhd", "foo_file.vhd"]) + for file_name in files: + lib.get_source_file(file_name).name.endswith(file_name) + + ui = self._create_ui() + lib = ui.add_library("lib") + lib.add_source_files(("file*.vhd", "foo_file.vhd")) + for file_name in files: + lib.get_source_file(file_name).name.endswith(file_name) + + ui = self._create_ui() + lib = ui.add_library("lib") + lib.add_source_files(iter(["file*.vhd", "foo_file.vhd"])) + for file_name in files: + lib.get_source_file(file_name).name.endswith(file_name) + + def test_add_source_files_from_csv(self): + csv = """ + lib, tb_example.vhdl + lib1 , tb_example1.vhd + lib2, tb_example2.vhd + lib3,"tb,ex3.vhd" + """ + + libraries = ['lib', 'lib1', 'lib2', 'lib3'] + files = ['tb_example.vhdl', 'tb_example1.vhd', 'tb_example2.vhd', 'tb,ex3.vhd'] + + self.create_csv_file('test_csv.csv', csv) + for file_name in files: + self.create_file(file_name) + + ui = self._create_ui() + ui.add_source_files_from_csv('test_csv.csv') + + for index, library_name in enumerate(libraries): + file_name = files[index] + file_name_from_ui = ui.get_source_file(file_name, library_name) + self.assertIsNotNone(file_name_from_ui) + + def test_add_source_files_from_csv_return(self): + csv = """ + lib, tb_example.vhd + lib, tb_example1.vhd + lib, tb_example2.vhd + """ + + list_of_files = ['tb_example.vhd', 'tb_example1.vhd', 'tb_example2.vhd'] + + for index, file_ in enumerate(list_of_files): + self.create_file(file_, str(index)) + + self.create_csv_file('test_returns.csv', csv) + ui = self._create_ui() + + source_files = ui.add_source_files_from_csv('test_returns.csv') + self.assertEqual([source_file.name for source_file in source_files], list_of_files) + + def test_add_source_files_errors(self): + ui = self._create_ui() + lib = ui.add_library("lib") + self.create_file("file.vhd") + self.assertRaisesRegex(ValueError, r"missing\.vhd", lib.add_source_files, ["missing.vhd", "file.vhd"]) + self.assertRaisesRegex(ValueError, r"missing\.vhd", lib.add_source_files, "missing.vhd") + + def test_get_source_files(self): + ui = self._create_ui() + lib1 = ui.add_library("lib1") + lib2 = ui.add_library("lib2") + file_name = self.create_entity_file() + lib1.add_source_file(file_name) + lib2.add_source_file(file_name) + + self.assertEqual(len(ui.get_source_files(file_name)), 2) + self.assertEqual(len(lib1.get_source_files(file_name)), 1) + self.assertEqual(len(lib2.get_source_files(file_name)), 1) + + ui.get_source_file(file_name, library_name="lib1") + ui.get_source_file(file_name, library_name="lib2") + lib1.get_source_file(file_name) + lib2.get_source_file(file_name) + + def test_get_compile_order_smoke_test(self): + ui = self._create_ui() + lib1 = ui.add_library("lib1") + lib2 = ui.add_library("lib2") + file_name = self.create_entity_file() + lib1.add_source_file(file_name) + lib2.add_source_file(file_name) + + # Without argument + compile_order = ui.get_compile_order() + self.assertEqual(len(compile_order), 2) + self.assertEqual(compile_order[0].name, file_name) + self.assertEqual(compile_order[1].name, file_name) + + # With argument + compile_order = ui.get_compile_order([lib1.get_source_file(file_name)]) + self.assertEqual(len(compile_order), 1) + self.assertEqual(compile_order[0].name, file_name) + + def test_list_files_flag(self): + ui = self._create_ui("--files") + lib1 = ui.add_library("lib1") + lib2 = ui.add_library("lib2") + file_name = self.create_entity_file() + lib1.add_source_file(file_name) + lib2.add_source_file(file_name) + + with mock.patch("sys.stdout", autospec=True) as stdout: + self._run_main(ui) + text = "".join([call[1][0] for call in stdout.write.mock_calls]) + # @TODO not always in the same order in Python3 due to dependency graph + self.assertEqual(set(text.splitlines()), + set("""\ +lib2, ent0.vhd +lib1, ent0.vhd +Listed 2 files""".splitlines())) + + @with_tempdir + def test_filtering_tests(self, tempdir): + def setup(ui): + " Setup the project " + lib = ui.add_library("lib") + file_name = join(tempdir, "tb_filter.vhd") + create_vhdl_test_bench_file("tb_filter", file_name, + tests=["Test 1", "Test 2", "Test 3", "Test 4"], + test_attributes={ + "Test 1": [".attr0"], + "Test 2": [".attr0", ".attr1"], + "Test 3": [".attr1"], + "Test 4": [] + }) + lib.add_source_file(file_name) + lib.test_bench("tb_filter").test("Test 4").set_attribute(".attr2", None) + + def check_stdout(ui, expected): + " Check that stdout matches expected " + with mock.patch("sys.stdout", autospec=True) as stdout: + self._run_main(ui) + text = "".join([call[1][0] for call in stdout.write.mock_calls]) + # @TODO not always in the same order in Python3 due to dependency graph + print(text) + self.assertEqual(set(text.splitlines()), + set(expected.splitlines())) + + ui = self._create_ui("--list", "*Test 1") + setup(ui) + check_stdout(ui, "lib.tb_filter.Test 1\nListed 1 tests") + + ui = self._create_ui("--list", "*Test*") + setup(ui) + check_stdout(ui, + "lib.tb_filter.Test 1\n" + "lib.tb_filter.Test 2\n" + "lib.tb_filter.Test 3\n" + "lib.tb_filter.Test 4\n" + "Listed 4 tests") + + ui = self._create_ui("--list", "*2*") + setup(ui) + check_stdout(ui, + "lib.tb_filter.Test 2\n" + "Listed 1 tests") + + ui = self._create_ui("--list", "--with-attribute=.attr0") + setup(ui) + check_stdout(ui, + "lib.tb_filter.Test 1\n" + "lib.tb_filter.Test 2\n" + "Listed 2 tests") + + ui = self._create_ui("--list", "--with-attribute=.attr2") + setup(ui) + check_stdout(ui, + "lib.tb_filter.Test 4\n" + "Listed 1 tests") + + ui = self._create_ui("--list", "--with-attributes", ".attr0", "--with-attributes", ".attr1") + setup(ui) + check_stdout(ui, + "lib.tb_filter.Test 2\n" + "Listed 1 tests") + + ui = self._create_ui("--list", "--without-attributes", ".attr0") + setup(ui) + check_stdout(ui, + "lib.tb_filter.Test 3\n" + "lib.tb_filter.Test 4\n" + "Listed 2 tests") + + ui = self._create_ui("--list", "--without-attributes", ".attr0", "--without-attributes", ".attr1") + setup(ui) + check_stdout(ui, + "lib.tb_filter.Test 4\n" + "Listed 1 tests") + + ui = self._create_ui("--list", + "--with-attributes", ".attr0", + "--without-attributes", ".attr1") + setup(ui) + check_stdout(ui, + "lib.tb_filter.Test 1\n" + "Listed 1 tests") + + @with_tempdir + def test_export_json(self, tempdir): + json_file = join(tempdir, "export.json") + + ui = self._create_ui("--export-json", json_file) + lib1 = ui.add_library("lib1") + lib2 = ui.add_library("lib2") + + file_name1 = join(tempdir, "tb_foo.vhd") + create_vhdl_test_bench_file("tb_foo", file_name1) + lib1.add_source_file(file_name1) + + file_name2 = join(tempdir, "tb_bar.vhd") + create_vhdl_test_bench_file("tb_bar", file_name2, + tests=["Test one", "Test two"], + test_attributes={"Test one": [".attr0"]}) + lib2.add_source_file(file_name2) + lib2.test_bench("tb_bar").set_attribute(".attr1", "bar") + + self._run_main(ui) + + with open(json_file, "r") as fptr: + data = json.load(fptr) + + # Check known keys + self.assertEqual(set(data.keys()), + set(["export_format_version", + "files", + "tests"])) + + # Check that export format is semantic version with integer values + self.assertEqual(set(data["export_format_version"].keys()), + set(("major", "minor", "patch"))) + assert all(isinstance(value, int) + for value in data["export_format_version"].values()) + + # Check the contents of the files section + self.assertEqual(set((item["library_name"], item["file_name"]) + for item in data["files"]), + set([("lib1", abspath(file_name1)), + ("lib2", abspath(file_name2))])) + + # Check the contents of the tests section + self.assertEqual({item["name"]: (item["location"], item["attributes"]) for item in data["tests"]}, + {"lib1.tb_foo.all": ({"file_name": file_name1, "offset": 180, "length": 18}, + {}), + "lib2.tb_bar.Test one": ({"file_name": file_name2, "offset": 235, "length": 8}, + {".attr0": None, ".attr1": "bar"}), + "lib2.tb_bar.Test two": ({"file_name": file_name2, "offset": 283, "length": 8}, + {".attr1": "bar"})}) + + def test_library_attributes(self): + ui = self._create_ui() + lib1 = ui.add_library("lib1") + self.assertEqual(lib1.name, "lib1") + + def test_source_file_attributes(self): + ui = self._create_ui() + lib = ui.add_library("lib") + self.create_file("file_name.vhd") + source_file = lib.add_source_file("file_name.vhd") + self.assertEqual(source_file.name, "file_name.vhd") + self.assertEqual(source_file.library.name, "lib") + + def test_get_source_files_errors(self): + ui = self._create_ui() + lib1 = ui.add_library("lib1") + lib2 = ui.add_library("lib2") + file_name = self.create_entity_file() + lib1.add_source_file(file_name) + lib2.add_source_file(file_name) + non_existant_name = "non_existant" + + self.assertRaisesRegex(ValueError, ".*%s.*allow_empty.*" % non_existant_name, + ui.get_source_files, non_existant_name) + self.assertEqual(len(ui.get_source_files(non_existant_name, allow_empty=True)), 0) + + self.assertRaisesRegex(ValueError, ".*named.*%s.*multiple.*library_name.*" % file_name, + ui.get_source_file, file_name) + + self.assertRaisesRegex(ValueError, ".*Found no file named.*%s'" % non_existant_name, + ui.get_source_file, non_existant_name) + + self.assertRaisesRegex(ValueError, ".*Found no file named.*%s.* in library 'lib1'" % non_existant_name, + ui.get_source_file, non_existant_name, "lib1") + + def test_add_single_file_manual_dependencies(self): + ui = self._create_ui() + lib = ui.add_library("lib") + file_name1 = self.create_entity_file(1) + file_name2 = self.create_entity_file(2) + file1 = lib.add_source_file(file_name1) + file2 = lib.add_source_file(file_name2) + self.assertEqual(names(ui.get_compile_order([file1])), names([file1])) + self.assertEqual(names(ui.get_compile_order([file2])), names([file2])) + file1.add_dependency_on(file2) + self.assertEqual(names(ui.get_compile_order([file1])), names([file2, file1])) + self.assertEqual(names(ui.get_compile_order([file2])), names([file2])) + + def test_add_multiple_file_manual_dependencies(self): + ui = self._create_ui() + lib = ui.add_library("lib") + self.create_file("foo1.vhd") + self.create_file("foo2.vhd") + self.create_file("foo3.vhd") + self.create_file("bar.vhd") + foo_files = lib.add_source_files("foo*.vhd") + bar_file = lib.add_source_file("bar.vhd") + + self.assertEqual(names(ui.get_compile_order([bar_file])), names([bar_file])) + bar_file.add_dependency_on(foo_files) + self.assertEqual(sorted(names(ui.get_compile_order([bar_file]))), sorted(names(foo_files + [bar_file]))) + self.assertEqual(ui.get_compile_order([bar_file])[-1].name, bar_file.name) + + def test_add_fileset_manual_dependencies(self): + ui = self._create_ui() + lib = ui.add_library("lib") + self.create_file("foo1.vhd") + self.create_file("foo2.vhd") + self.create_file("foo3.vhd") + self.create_file("bar.vhd") + foo_files = lib.add_source_files("foo*.vhd") + bar_file = lib.add_source_file("bar.vhd") + + for foo_file in foo_files: + self.assertEqual(names(ui.get_compile_order([foo_file])), names([foo_file])) + + foo_files.add_dependency_on(bar_file) + + for foo_file in foo_files: + self.assertEqual(names(ui.get_compile_order([foo_file])), names([bar_file, foo_file])) + + def test_add_source_files_has_include_dirs(self): + file_name = "verilog.v" + include_dirs = ["include_dir"] + all_include_dirs = add_verilog_include_dir(include_dirs) + self.create_file(file_name) + + def check(action): + """ + Helper to check that project method was called + """ + ui = self._create_ui() + with mock.patch.object(ui, "_project", autospec=True) as project: + project.has_library.return_value = True + lib = ui.library("lib") + action(ui, lib) + project.add_source_file.assert_called_once_with(abspath("verilog.v"), + "lib", + file_type="verilog", + include_dirs=all_include_dirs, + defines=None, + vhdl_standard=None, + no_parse=False) + check(lambda ui, _: ui.add_source_files(file_name, "lib", include_dirs=include_dirs)) + check(lambda ui, _: ui.add_source_file(file_name, "lib", include_dirs=include_dirs)) + check(lambda _, lib: lib.add_source_files(file_name, include_dirs=include_dirs)) + check(lambda _, lib: lib.add_source_file(file_name, include_dirs=include_dirs)) + + def test_add_source_files_has_defines(self): + file_name = "verilog.v" + self.create_file(file_name) + all_include_dirs = add_verilog_include_dir([]) + defines = {"foo": "bar"} + + def check(action): + """ + Helper to check that project method was called + """ + ui = self._create_ui() + with mock.patch.object(ui, "_project", autospec=True) as project: + project.has_library.return_value = True + lib = ui.library("lib") + action(ui, lib) + project.add_source_file.assert_called_once_with(abspath("verilog.v"), + "lib", + file_type="verilog", + include_dirs=all_include_dirs, + defines=defines, + vhdl_standard=None, + no_parse=False) + check(lambda ui, _: ui.add_source_files(file_name, "lib", defines=defines)) + check(lambda ui, _: ui.add_source_file(file_name, "lib", defines=defines)) + check(lambda _, lib: lib.add_source_files(file_name, defines=defines)) + check(lambda _, lib: lib.add_source_file(file_name, defines=defines)) + + def test_add_source_files_has_no_parse(self): + file_name = "verilog.v" + self.create_file(file_name) + all_include_dirs = add_verilog_include_dir([]) + + for no_parse in (True, False): + for method in range(4): + + ui = self._create_ui() + with mock.patch.object(ui, "_project", autospec=True) as project: + project.has_library.return_value = True + + if method == 0: + ui.add_source_files(file_name, "lib", no_parse=no_parse) + elif method == 1: + ui.add_source_file(file_name, "lib", no_parse=no_parse) + elif method == 2: + lib = ui.library("lib") + lib.add_source_files(file_name, no_parse=no_parse) + elif method == 3: + lib = ui.library("lib") + lib.add_source_file(file_name, no_parse=no_parse) + + project.add_source_file.assert_called_once_with(abspath("verilog.v"), + "lib", + file_type="verilog", + include_dirs=all_include_dirs, + defines=None, + vhdl_standard=None, + no_parse=no_parse) + + def test_compile_options(self): + file_name = "foo.vhd" + self.create_file(file_name) + ui = self._create_ui() + lib = ui.add_library("lib") + source_file = lib.add_source_file(file_name) + + # Use methods on all types of interface objects + for obj in [source_file, ui, lib, lib.get_source_files(file_name)]: + obj.set_compile_option("ghdl.flags", []) + self.assertEqual(source_file.get_compile_option("ghdl.flags"), []) + + obj.add_compile_option("ghdl.flags", ["1"]) + self.assertEqual(source_file.get_compile_option("ghdl.flags"), ["1"]) + + obj.add_compile_option("ghdl.flags", ["2"]) + self.assertEqual(source_file.get_compile_option("ghdl.flags"), ["1", "2"]) + + obj.set_compile_option("ghdl.flags", ["3"]) + self.assertEqual(source_file.get_compile_option("ghdl.flags"), ["3"]) + + def test_default_vhdl_standard_is_used(self): + file_name = "foo.vhd" + self.create_file(file_name) + with set_env(VUNIT_VHDL_STANDARD="93"): + ui = self._create_ui() + lib = ui.add_library("lib") + source_file = lib.add_source_file(file_name) + self.assertEqual(source_file.vhdl_standard, "93") + + def test_library_default_vhdl_standard_is_used(self): + file_name = "foo.vhd" + self.create_file(file_name) + + for method in range(4): + with set_env(VUNIT_VHDL_STANDARD="93"): + ui = self._create_ui() + + if method == 0: + lib = ui.add_library("lib", vhdl_standard="2002") + source_file = lib.add_source_file(file_name) + elif method == 1: + lib = ui.add_library("lib", vhdl_standard="2002") + source_file = ui.add_source_file(file_name, "lib") + elif method == 2: + lib = ui.add_library("lib", vhdl_standard="2002") + source_file = lib.add_source_files(file_name)[0] + elif method == 3: + lib = ui.add_library("lib", vhdl_standard="2002") + source_file = ui.add_source_files(file_name, "lib")[0] + + self.assertEqual(source_file.vhdl_standard, "2002") + + def test_add_source_file_vhdl_standard_is_used(self): + file_name = "foo.vhd" + self.create_file(file_name) + + for method in range(4): + with set_env(VUNIT_VHDL_STANDARD="93"): + ui = self._create_ui() + lib = ui.add_library("lib", vhdl_standard="2002") + + if method == 0: + source_file = lib.add_source_file(file_name, vhdl_standard="2008") + elif method == 1: + source_file = lib.add_source_files(file_name, vhdl_standard="2008")[0] + elif method == 2: + source_file = ui.add_source_file(file_name, "lib", vhdl_standard="2008") + elif method == 3: + source_file = ui.add_source_files(file_name, "lib", vhdl_standard="2008")[0] + + self.assertEqual(source_file.vhdl_standard, "2008") + + def test_verilog_file_has_vhdl_standard_none(self): + file_name = "foo.v" + self.create_file(file_name) + ui = self._create_ui() + lib = ui.add_library("lib") + source_file = lib.add_source_file(file_name) + self.assertEqual(source_file.vhdl_standard, None) + + @mock.patch("vunit.test_bench_list.LOGGER", autospec=True) + def test_warning_on_no_tests(self, logger): + ui = self._create_ui("--compile") + self._run_main(ui) + self.assertEqual(logger.warning.mock_calls, []) + logger.reset_mock() + + ui = self._create_ui("--list") + self._run_main(ui) + self.assertEqual(len(logger.warning.mock_calls), 1) + self.assertTrue("Found no test benches" in str(logger.warning.mock_calls)) + logger.reset_mock() + + ui = self._create_ui() + self._run_main(ui) + self.assertEqual(len(logger.warning.mock_calls), 1) + self.assertTrue("Found no test benches" in str(logger.warning.mock_calls)) + logger.reset_mock() + + def test_post_run(self): + post_run = mock.Mock() + + ui = self._create_ui() + self._run_main(ui, post_run=post_run) + self.assertTrue(post_run.called) + + for no_run_arg in ['--compile', '--files', '--list']: + post_run.reset_mock() + ui = self._create_ui(no_run_arg) + self._run_main(ui, post_run=post_run) + self.assertFalse(post_run.called) + + def test_error_on_adding_duplicate_library(self): + ui = self._create_ui() + ui.add_library("lib") + self.assertRaises(ValueError, ui.add_library, "lib") + + def test_allow_adding_duplicate_library(self): + ui = self._create_ui() + + file_name = "file.vhd" + self.create_file(file_name) + + file_name2 = "file2.vhd" + self.create_file(file_name2) + + lib1 = ui.add_library("lib") + source_file1 = lib1.add_source_file(file_name) + self.assertEqual([source_file.name for source_file in lib1.get_source_files()], + [source_file1.name]) + + lib2 = ui.add_library("lib", allow_duplicate=True) + for lib in [lib1, lib2]: + self.assertEqual([source_file.name for source_file in lib.get_source_files()], + [source_file1.name]) + + source_file2 = lib2.add_source_file(file_name2) + for lib in [lib1, lib2]: + self.assertEqual({source_file.name for source_file in lib.get_source_files()}, + {source_file1.name, source_file2.name}) + + def test_scan_tests_from_other_file(self): + for tb_type in ["vhdl", "verilog"]: + for tests_type in ["vhdl", "verilog"]: + ui = self._create_ui() + lib = ui.add_library("lib") + + if tb_type == "vhdl": + tb_file_name = "tb_top.vhd" + self.create_file(tb_file_name, """ +entity tb_top is + generic (runner_cfg : string); +end entity; + +architecture a of tb_top is +begin +end architecture; + """) + else: + tb_file_name = "tb_top.sv" + self.create_file(tb_file_name, """ +module tb_top + parameter string runner_cfg = ""; + tests #(.nested_runner_cfg(runner_cfg)) tests_inst(); +endmodule; + """) + + if tests_type == "vhdl": + tests_file_name = "tests.vhd" + self.create_file(tests_file_name, """ +entity tests is + generic (nested_runner_cfg : string); +end entity; + +architecture a of tests is +begin + main : process + if run("test1") then + elsif run("test2") + end if; + end process; +end architecture; + """) + else: + tests_file_name = "tests.sv" + self.create_file(tests_file_name, """ +`include "vunit_defines.svh" +module tests; + `NESTED_TEST_SUITE begin + `TEST_CASE("test1") begin + end + `TEST_CASE("test2") begin + end + end; +endmodule + """) + + lib.add_source_file(tb_file_name) + lib.add_source_file(tests_file_name) + + # tb_top is a single test case in itself + self.assertEqual(ui.library("lib").test_bench("tb_top").get_tests(), []) + + if tb_type == "vhdl": + test_bench = lib.entity("tb_top") + else: + test_bench = lib.module("tb_top") + test_bench.scan_tests_from_file(tests_file_name) + + self.assertEqual([test.name + for test in ui.library("lib").test_bench("tb_top").get_tests()], + ["test1", + "test2"]) + + def test_scan_tests_from_other_file_missing(self): + ui = self._create_ui() + lib = ui.add_library("lib") + tb_file_name = "tb_top.sv" + self.create_file(tb_file_name, """ +`include "vunit_defines.svh" +module tb_top; + `TEST_SUITE begin + `TEST_CASE("test1") begin + end + end; +endmodule + """) + lib.add_source_file(tb_file_name) + self.assertRaises(ValueError, lib.test_bench("tb_top").scan_tests_from_file, "missing.sv") + + def test_can_list_tests_without_simulator(self): + with set_env(): + ui = self._create_ui_real_sim("--list") + self._run_main(ui, 0) + + def test_can_list_files_without_simulator(self): + with set_env(): + ui = self._create_ui_real_sim("--files") + self._run_main(ui, 0) + + @mock.patch("vunit.ui.LOGGER", autospec=True) + def test_compile_without_simulator_fails(self, logger): + with set_env(): + ui = self._create_ui_real_sim("--compile") + self._run_main(ui, 1) + self.assertEqual(len(logger.error.mock_calls), 1) + self.assertTrue("No available simulator detected" in str(logger.error.mock_calls)) + + @mock.patch("vunit.ui.LOGGER", autospec=True) + def test_simulate_without_simulator_fails(self, logger): + with set_env(): + ui = self._create_ui_real_sim() + self._run_main(ui, 1) + self.assertEqual(len(logger.error.mock_calls), 1) + self.assertTrue("No available simulator detected" in str(logger.error.mock_calls)) + + def test_set_sim_option_before_adding_file(self): + """ + From GitHub issue #250 + """ + ui = self._create_ui() + lib = ui.add_library("lib") + for method in (lib.set_sim_option, ui.set_sim_option): + method("disable_ieee_warnings", True, allow_empty=True) + self.assertRaises(ValueError, method, "disable_ieee_warnings", True) + + for method in (lib.set_compile_option, lib.add_compile_option, ui.set_compile_option, ui.add_compile_option): + method("ghdl.elab_flags", [], allow_empty=True) + self.assertRaises(ValueError, method, "ghdl.elab_flags", []) + + def _create_ui(self, *args): + """ Create an instance of the VUnit public interface class """ + with mock.patch("vunit.ui.SIMULATOR_FACTORY.select_simulator", + new=lambda: MockSimulator): + return self._create_ui_real_sim(*args) + + def _create_ui_real_sim(self, *args): + """ Create an instance of the VUnit public interface class """ + return VUnit.from_argv(argv=["--output-path=%s" % self._output_path, + "--clean"] + list(args), + compile_builtins=False) + + def _run_main(self, ui, code=0, post_run=None): + """ + Run ui.main and expect exit code + """ + try: + ui.main(post_run=post_run) + except SystemExit as exc: + self.assertEqual(exc.code, code) + + def create_entity_file(self, idx=0, file_suffix='.vhd'): + """ + Create and a temporary file containing the same source code + but with different entity names depending on the index + """ + source = Template(""" +library vunit_lib; +context vunit_lib.vunit_context; + +entity $entity is +end entity; + +architecture arch of $entity is +begin + log("Hello World"); + check_relation(1 /= 2); + report "Here I am!"; +end architecture; +""") + + entity_name = "ent%i" % idx + file_name = entity_name + file_suffix + self.create_file(file_name, + source.substitute(entity=entity_name)) + return file_name + + @staticmethod + def create_file(file_name, contents=""): + """ + Creata file in the temporary path with given contents + """ + with open(file_name, "w") as fptr: + fptr.write(contents) + + @staticmethod + def create_csv_file(file_name, contents=''): + """ + Create a temporary csv description file with given contents + """ + with open(file_name, "w") as fprt: + fprt.write(contents) + + def assertRaisesRegex(self, *args, **kwargs): # pylint: disable=invalid-name,arguments-differ + """ + Python 2/3 compatability + """ + if hasattr(unittest.TestCase, "assertRaisesRegex"): + unittest.TestCase.assertRaisesRegex(self, *args, **kwargs) # pylint: disable=no-member + else: + unittest.TestCase.assertRaisesRegexp(self, *args, **kwargs) # pylint: disable=deprecated-method + + +class TestPreprocessor(object): + """ + A preprocessor that appends a check_relation call before the orginal code + """ + def __init__(self): + pass + + @staticmethod + def run(code, file_name): # pylint: disable=unused-argument + return '-- check_relation(a = b);\n' + code + + +class VUnitfier(object): + """ + A preprocessor that replaces report statments with log calls + """ + def __init__(self): + self._report_pattern = re.compile(r'^(?P\s*)report\s*(?P"[^"]*")\s*;', MULTILINE) + + def run(self, code, file_name): # pylint: disable=unused-argument + return self._report_pattern.sub( + r'\glog(\g); -- VUnitfier preprocessor: Report turned off, keeping original code.', code) + + +class ParentalControl(object): + """ + A preprocessor that replaces f..k with [BEEP] + """ + def __init__(self): + self._fword_pattern = re.compile(r'f..k') + + def run(self, code, file_name): # pylint: disable=unused-argument + return self._fword_pattern.sub(r'[BEEP]', code) + + +class MockSimulator(SimulatorInterface): + """ + Mock of a SimulatorInterface + """ + name = "mock" + + @staticmethod + def from_args(output_path, *args, **kwargs): # pylint: disable=unused-argument + return MockSimulator(output_path="", gui=False) + + package_users_depend_on_bodies = False + + @staticmethod + def compile_source_file_command(source_file): # pylint: disable=unused-argument + return True + + @staticmethod + def simulate(output_path, test_suite_name, config, elaborate_only): # pylint: disable=unused-argument + return True + + +def names(lst): + """ + Return a list of the .name attribute of the objects in the list + """ + return [obj.name for obj in lst] diff --git a/vunit/test/unit/test_verilog_parser.py b/vunit/test/unit/test_verilog_parser.py index 393e01aea..a2c80eda3 100644 --- a/vunit/test/unit/test_verilog_parser.py +++ b/vunit/test/unit/test_verilog_parser.py @@ -1,357 +1,357 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test of the Verilog parser -""" - -from unittest import TestCase -import os -from os.path import join, dirname, exists -import time -import shutil -from vunit.ostools import renew_path -from vunit.parsing.verilog.parser import VerilogParser -from vunit.test.mock_2or3 import mock - - -class TestVerilogParser(TestCase): # pylint: disable=too-many-public-methods - """ - Test of the Verilog parser - """ - - def setUp(self): - self.output_path = join(dirname(__file__), "test_verilog_parser_out") - renew_path(self.output_path) - self.cwd = os.getcwd() - os.chdir(self.output_path) - - def tearDown(self): - os.chdir(self.cwd) - shutil.rmtree(self.output_path) - - def test_parsing_empty(self): - design_file = self.parse("") - self.assertEqual(design_file.modules, []) - - def test_parse_module(self): - modules = self.parse("""\ -module true1; - my_module hello - "module false"; -endmodule -/* -module false -*/ - module true2; // module false -endmodule module true3 -endmodule -""").modules - self.assertEqual(len(modules), 3) - self.assertEqual(modules[0].name, "true1") - self.assertEqual(modules[1].name, "true2") - self.assertEqual(modules[2].name, "true3") - - def test_parse_parameter_without_type(self): - modules = self.parse("""\ -module foo; - parameter param1; - parameter param2 = 1; -endmodule -""").modules - self.assertEqual(len(modules), 1) - module = modules[0] - self.assertEqual(module.name, "foo") - self.assertEqual(len(module.parameters), 2) - param1, param2 = module.parameters - self.assertEqual(param1, "param1") - self.assertEqual(param2, "param2") - - def test_parse_parameter_with_type(self): - modules = self.parse("""\ -module foo; - parameter string param1; - parameter integer param2 = 1; -endmodule -""").modules - self.assertEqual(len(modules), 1) - module = modules[0] - self.assertEqual(module.name, "foo") - self.assertEqual(len(module.parameters), 2) - param1, param2 = module.parameters - self.assertEqual(param1, "param1") - self.assertEqual(param2, "param2") - - def test_nested_modules_are_ignored(self): - modules = self.parse("""\ -module foo; - parameter string param1; - module nested; - parameter integer param_nested; - endmodule - parameter string param2; -endmodule -""").modules - self.assertEqual(len(modules), 1) - module = modules[0] - self.assertEqual(module.name, "foo") - self.assertEqual(len(module.parameters), 2) - param1, param2 = module.parameters - self.assertEqual(param1, "param1") - self.assertEqual(param2, "param2") - - def test_parse_package(self): - packages = self.parse("""\ -package true1; -endpackage package true2; endpackage -""").packages - self.assertEqual(len(packages), 2) - self.assertEqual(packages[0].name, "true1") - self.assertEqual(packages[1].name, "true2") - - def test_parse_imports(self): - imports = self.parse("""\ -import true1; -package pkg; - import true2::*; -endpackage -""").imports - self.assertEqual(len(imports), 2) - self.assertEqual(imports[0], "true1") - self.assertEqual(imports[1], "true2") - - def test_parse_package_references(self): - package_references = self.parse("""\ -import false1; -import false1::false2::*; -package pkg; - true1::func(true2::bar()); - true3::foo(); -endpackage -""").package_references - self.assertEqual(len(package_references), 3) - self.assertEqual(package_references[0], "true1") - self.assertEqual(package_references[1], "true2") - self.assertEqual(package_references[2], "true3") - - @mock.patch("vunit.parsing.verilog.parser.LOGGER", autospec=True) - def test_parse_import_with_bad_argument(self, logger): - imports = self.parse("""\ -import; -""").imports - self.assertEqual(len(imports), 0) - logger.warning.assert_called_once_with( - "import bad argument\n%s", - "at file_name.sv line 1:\n" - "import;\n" - " ~") - - @mock.patch("vunit.parsing.verilog.parser.LOGGER", autospec=True) - def test_parse_import_eof(self, logger): - imports = self.parse("""\ -import -""").imports - self.assertEqual(len(imports), 0) - logger.warning.assert_called_once_with( - "EOF reached when parsing import\n%s", - "at file_name.sv line 1:\n" - "import\n" - "~~~~~~") - - def test_parse_instances(self): - instances = self.parse("""\ -module name; - true1 instance_name1(); - true2 instance_name2(.foo(bar)); - true3 #(.param(1)) instance_name3(.foo(bar)); -endmodule -""").instances - self.assertEqual(len(instances), 3) - self.assertEqual(instances[0], "true1") - self.assertEqual(instances[1], "true2") - self.assertEqual(instances[2], "true3") - - def test_parse_instances_after_block_label(self): - instances = self.parse("""\ -module name; -genvar i; - generate - for( i=0; i < 10; i = i + 1 ) - begin: INST_GEN - true1 instance_name1(); - end : INST_GEN - true2 instance_name2(); - endgenerate -endmodule -""").instances - self.assertEqual(len(instances), 2) - self.assertEqual(instances[0], "true1") - self.assertEqual(instances[1], "true2") - - def test_parse_instances_without_crashing(self): - instances = self.parse("""\ -module name; -endmodule identifier -""").instances - self.assertEqual(len(instances), 0) - - def test_can_set_pre_defined_defines(self): - code = """\ -`ifdef foo -`foo -endmodule; -`endif -""" - - result = self.parse(code, defines={"foo": "module mod1;"}) - self.assertEqual(len(result.modules), 1) - self.assertEqual(result.modules[0].name, "mod1") - - def test_result_is_cached(self): - code = """\ -`include "missing.sv" -module name; - true1 instance_name1(); - true2 instance_name2(.foo(bar)); - true3 #(.param(1)) instance_name3(.foo(bar)); -endmodule -""" - cache = {} - result = self.parse(code, cache=cache) - instances = result.instances - self.assertEqual(len(instances), 3) - self.assertEqual(instances[0], "true1") - self.assertEqual(instances[1], "true2") - self.assertEqual(instances[2], "true3") - - new_result = self.parse(code, cache=cache) - self.assertEqual(id(result), id(new_result)) - cache.clear() - - new_result = self.parse(code, cache=cache) - self.assertNotEqual(id(result), id(new_result)) - - def test_cached_parsing_updated_by_changing_file(self): - code = """\ -module mod1; -endmodule -""" - cache = {} - result = self.parse(code, cache=cache) - self.assertEqual(len(result.modules), 1) - self.assertEqual(result.modules[0].name, "mod1") - - tick() - - code = """\ -module mod2; -endmodule -""" - result = self.parse(code, cache=cache) - self.assertEqual(len(result.modules), 1) - self.assertEqual(result.modules[0].name, "mod2") - - def test_cached_parsing_updated_by_includes(self): - self.write_file("include.svh", """ -module mod; -endmodule; -""") - code = """\ -`include "include.svh" -""" - cache = {} - result = self.parse(code, cache=cache, include_paths=[self.output_path]) - self.assertEqual(len(result.modules), 1) - self.assertEqual(result.modules[0].name, "mod") - - tick() - - self.write_file("include.svh", """ -module mod1; -endmodule; - -module mod2; -endmodule; -""") - result = self.parse(code, cache=cache, include_paths=[self.output_path]) - self.assertEqual(len(result.modules), 2) - self.assertEqual(result.modules[0].name, "mod1") - self.assertEqual(result.modules[1].name, "mod2") - - def test_cached_parsing_updated_by_higher_priority_file(self): - cache = {} - include_paths = [self.output_path, join(self.output_path, "lower_prio")] - - self.write_file(join("lower_prio", "include.svh"), """ -module mod_lower_prio; -endmodule; -""") - - code = """\ -`include "include.svh" -""" - - result = self.parse(code, cache=cache, include_paths=include_paths) - self.assertEqual(len(result.modules), 1) - self.assertEqual(result.modules[0].name, "mod_lower_prio") - - self.write_file("include.svh", """ -module mod_higher_prio; -endmodule; -""") - result = self.parse(code, cache=cache, include_paths=include_paths) - self.assertEqual(len(result.modules), 1) - self.assertEqual(result.modules[0].name, "mod_higher_prio") - - def test_cached_parsing_updated_by_other_defines(self): - cache = {} - - code = """\ -`ifdef foo -module `foo -endmodule; -`endif -""" - - result = self.parse(code, cache=cache) - self.assertEqual(len(result.modules), 0) - - result = self.parse(code, cache=cache, defines={"foo": "mod1"}) - self.assertEqual(len(result.modules), 1) - self.assertEqual(result.modules[0].name, "mod1") - - result = self.parse(code, cache=cache, defines={"foo": "mod2"}) - self.assertEqual(len(result.modules), 1) - self.assertEqual(result.modules[0].name, "mod2") - - def write_file(self, file_name, contents): - """ - Write file with contents into output path - """ - full_name = join(self.output_path, file_name) - full_path = dirname(full_name) - if not exists(full_path): - os.makedirs(full_path) - with open(full_name, "w") as fptr: - fptr.write(contents) - - def parse(self, code, include_paths=None, cache=None, defines=None): - """ - Helper function to parse - """ - self.write_file("file_name.sv", code) - cache = cache if cache is not None else {} - parser = VerilogParser(database=cache) - include_paths = include_paths if include_paths is not None else [] - design_file = parser.parse("file_name.sv", include_paths, defines) - return design_file - - -def tick(): - """ - To get a different file modification time - """ - time.sleep(0.01) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test of the Verilog parser +""" + +from unittest import TestCase +import os +from os.path import join, dirname, exists +import time +import shutil +from vunit.ostools import renew_path +from vunit.parsing.verilog.parser import VerilogParser +from vunit.test.mock_2or3 import mock + + +class TestVerilogParser(TestCase): # pylint: disable=too-many-public-methods + """ + Test of the Verilog parser + """ + + def setUp(self): + self.output_path = join(dirname(__file__), "test_verilog_parser_out") + renew_path(self.output_path) + self.cwd = os.getcwd() + os.chdir(self.output_path) + + def tearDown(self): + os.chdir(self.cwd) + shutil.rmtree(self.output_path) + + def test_parsing_empty(self): + design_file = self.parse("") + self.assertEqual(design_file.modules, []) + + def test_parse_module(self): + modules = self.parse("""\ +module true1; + my_module hello + "module false"; +endmodule +/* +module false +*/ + module true2; // module false +endmodule module true3 +endmodule +""").modules + self.assertEqual(len(modules), 3) + self.assertEqual(modules[0].name, "true1") + self.assertEqual(modules[1].name, "true2") + self.assertEqual(modules[2].name, "true3") + + def test_parse_parameter_without_type(self): + modules = self.parse("""\ +module foo; + parameter param1; + parameter param2 = 1; +endmodule +""").modules + self.assertEqual(len(modules), 1) + module = modules[0] + self.assertEqual(module.name, "foo") + self.assertEqual(len(module.parameters), 2) + param1, param2 = module.parameters + self.assertEqual(param1, "param1") + self.assertEqual(param2, "param2") + + def test_parse_parameter_with_type(self): + modules = self.parse("""\ +module foo; + parameter string param1; + parameter integer param2 = 1; +endmodule +""").modules + self.assertEqual(len(modules), 1) + module = modules[0] + self.assertEqual(module.name, "foo") + self.assertEqual(len(module.parameters), 2) + param1, param2 = module.parameters + self.assertEqual(param1, "param1") + self.assertEqual(param2, "param2") + + def test_nested_modules_are_ignored(self): + modules = self.parse("""\ +module foo; + parameter string param1; + module nested; + parameter integer param_nested; + endmodule + parameter string param2; +endmodule +""").modules + self.assertEqual(len(modules), 1) + module = modules[0] + self.assertEqual(module.name, "foo") + self.assertEqual(len(module.parameters), 2) + param1, param2 = module.parameters + self.assertEqual(param1, "param1") + self.assertEqual(param2, "param2") + + def test_parse_package(self): + packages = self.parse("""\ +package true1; +endpackage package true2; endpackage +""").packages + self.assertEqual(len(packages), 2) + self.assertEqual(packages[0].name, "true1") + self.assertEqual(packages[1].name, "true2") + + def test_parse_imports(self): + imports = self.parse("""\ +import true1; +package pkg; + import true2::*; +endpackage +""").imports + self.assertEqual(len(imports), 2) + self.assertEqual(imports[0], "true1") + self.assertEqual(imports[1], "true2") + + def test_parse_package_references(self): + package_references = self.parse("""\ +import false1; +import false1::false2::*; +package pkg; + true1::func(true2::bar()); + true3::foo(); +endpackage +""").package_references + self.assertEqual(len(package_references), 3) + self.assertEqual(package_references[0], "true1") + self.assertEqual(package_references[1], "true2") + self.assertEqual(package_references[2], "true3") + + @mock.patch("vunit.parsing.verilog.parser.LOGGER", autospec=True) + def test_parse_import_with_bad_argument(self, logger): + imports = self.parse("""\ +import; +""").imports + self.assertEqual(len(imports), 0) + logger.warning.assert_called_once_with( + "import bad argument\n%s", + "at file_name.sv line 1:\n" + "import;\n" + " ~") + + @mock.patch("vunit.parsing.verilog.parser.LOGGER", autospec=True) + def test_parse_import_eof(self, logger): + imports = self.parse("""\ +import +""").imports + self.assertEqual(len(imports), 0) + logger.warning.assert_called_once_with( + "EOF reached when parsing import\n%s", + "at file_name.sv line 1:\n" + "import\n" + "~~~~~~") + + def test_parse_instances(self): + instances = self.parse("""\ +module name; + true1 instance_name1(); + true2 instance_name2(.foo(bar)); + true3 #(.param(1)) instance_name3(.foo(bar)); +endmodule +""").instances + self.assertEqual(len(instances), 3) + self.assertEqual(instances[0], "true1") + self.assertEqual(instances[1], "true2") + self.assertEqual(instances[2], "true3") + + def test_parse_instances_after_block_label(self): + instances = self.parse("""\ +module name; +genvar i; + generate + for( i=0; i < 10; i = i + 1 ) + begin: INST_GEN + true1 instance_name1(); + end : INST_GEN + true2 instance_name2(); + endgenerate +endmodule +""").instances + self.assertEqual(len(instances), 2) + self.assertEqual(instances[0], "true1") + self.assertEqual(instances[1], "true2") + + def test_parse_instances_without_crashing(self): + instances = self.parse("""\ +module name; +endmodule identifier +""").instances + self.assertEqual(len(instances), 0) + + def test_can_set_pre_defined_defines(self): + code = """\ +`ifdef foo +`foo +endmodule; +`endif +""" + + result = self.parse(code, defines={"foo": "module mod1;"}) + self.assertEqual(len(result.modules), 1) + self.assertEqual(result.modules[0].name, "mod1") + + def test_result_is_cached(self): + code = """\ +`include "missing.sv" +module name; + true1 instance_name1(); + true2 instance_name2(.foo(bar)); + true3 #(.param(1)) instance_name3(.foo(bar)); +endmodule +""" + cache = {} + result = self.parse(code, cache=cache) + instances = result.instances + self.assertEqual(len(instances), 3) + self.assertEqual(instances[0], "true1") + self.assertEqual(instances[1], "true2") + self.assertEqual(instances[2], "true3") + + new_result = self.parse(code, cache=cache) + self.assertEqual(id(result), id(new_result)) + cache.clear() + + new_result = self.parse(code, cache=cache) + self.assertNotEqual(id(result), id(new_result)) + + def test_cached_parsing_updated_by_changing_file(self): + code = """\ +module mod1; +endmodule +""" + cache = {} + result = self.parse(code, cache=cache) + self.assertEqual(len(result.modules), 1) + self.assertEqual(result.modules[0].name, "mod1") + + tick() + + code = """\ +module mod2; +endmodule +""" + result = self.parse(code, cache=cache) + self.assertEqual(len(result.modules), 1) + self.assertEqual(result.modules[0].name, "mod2") + + def test_cached_parsing_updated_by_includes(self): + self.write_file("include.svh", """ +module mod; +endmodule; +""") + code = """\ +`include "include.svh" +""" + cache = {} + result = self.parse(code, cache=cache, include_paths=[self.output_path]) + self.assertEqual(len(result.modules), 1) + self.assertEqual(result.modules[0].name, "mod") + + tick() + + self.write_file("include.svh", """ +module mod1; +endmodule; + +module mod2; +endmodule; +""") + result = self.parse(code, cache=cache, include_paths=[self.output_path]) + self.assertEqual(len(result.modules), 2) + self.assertEqual(result.modules[0].name, "mod1") + self.assertEqual(result.modules[1].name, "mod2") + + def test_cached_parsing_updated_by_higher_priority_file(self): + cache = {} + include_paths = [self.output_path, join(self.output_path, "lower_prio")] + + self.write_file(join("lower_prio", "include.svh"), """ +module mod_lower_prio; +endmodule; +""") + + code = """\ +`include "include.svh" +""" + + result = self.parse(code, cache=cache, include_paths=include_paths) + self.assertEqual(len(result.modules), 1) + self.assertEqual(result.modules[0].name, "mod_lower_prio") + + self.write_file("include.svh", """ +module mod_higher_prio; +endmodule; +""") + result = self.parse(code, cache=cache, include_paths=include_paths) + self.assertEqual(len(result.modules), 1) + self.assertEqual(result.modules[0].name, "mod_higher_prio") + + def test_cached_parsing_updated_by_other_defines(self): + cache = {} + + code = """\ +`ifdef foo +module `foo +endmodule; +`endif +""" + + result = self.parse(code, cache=cache) + self.assertEqual(len(result.modules), 0) + + result = self.parse(code, cache=cache, defines={"foo": "mod1"}) + self.assertEqual(len(result.modules), 1) + self.assertEqual(result.modules[0].name, "mod1") + + result = self.parse(code, cache=cache, defines={"foo": "mod2"}) + self.assertEqual(len(result.modules), 1) + self.assertEqual(result.modules[0].name, "mod2") + + def write_file(self, file_name, contents): + """ + Write file with contents into output path + """ + full_name = join(self.output_path, file_name) + full_path = dirname(full_name) + if not exists(full_path): + os.makedirs(full_path) + with open(full_name, "w") as fptr: + fptr.write(contents) + + def parse(self, code, include_paths=None, cache=None, defines=None): + """ + Helper function to parse + """ + self.write_file("file_name.sv", code) + cache = cache if cache is not None else {} + parser = VerilogParser(database=cache) + include_paths = include_paths if include_paths is not None else [] + design_file = parser.parse("file_name.sv", include_paths, defines) + return design_file + + +def tick(): + """ + To get a different file modification time + """ + time.sleep(0.01) diff --git a/vunit/test/unit/test_verilog_preprocessor.py b/vunit/test/unit/test_verilog_preprocessor.py index 81a374cb7..9fa586404 100644 --- a/vunit/test/unit/test_verilog_preprocessor.py +++ b/vunit/test/unit/test_verilog_preprocessor.py @@ -1,881 +1,881 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -# pylint: disable=too-many-public-methods -# pylint: disable=unused-wildcard-import -# pylint: disable=wildcard-import - -""" -Test of the Verilog preprocessor -""" - -from os.path import join, dirname, exists -import os -from unittest import TestCase -import shutil -from vunit.ostools import renew_path, write_file -from vunit.parsing.verilog.preprocess import VerilogPreprocessor, Macro -from vunit.parsing.verilog.tokenizer import VerilogTokenizer -from vunit.parsing.tokenizer import Token -from vunit.test.mock_2or3 import mock - - -class TestVerilogPreprocessor(TestCase): - """ - Test of the Verilog preprocessor - """ - - def setUp(self): - self.output_path = join(dirname(__file__), "test_verilog_preprocessor_out") - renew_path(self.output_path) - self.cwd = os.getcwd() - os.chdir(self.output_path) - - def tearDown(self): - os.chdir(self.cwd) - shutil.rmtree(self.output_path) - - def test_non_preprocess_tokens_are_kept(self): - result = self.preprocess('"hello"ident/*comment*///comment') - result.assert_has_tokens('"hello"ident/*comment*///comment') - result.assert_no_defines() - - def test_preprocess_define_without_value(self): - result = self.preprocess("`define foo") - result.assert_has_tokens("") - result.assert_has_defines({"foo": Macro("foo")}) - - result = self.preprocess("`define foo\nkeep") - result.assert_has_tokens("keep") - result.assert_has_defines({"foo": Macro("foo")}) - - def test_preprocess_define_with_value(self): - result = self.preprocess("`define foo bar \"abc\"") - result.assert_has_tokens("") - result.assert_has_defines({"foo": Macro("foo", tokenize("bar \"abc\""))}) - - def test_preprocess_define_with_lpar_value(self): - result = self.preprocess("`define foo (bar)") - result.assert_has_tokens("") - result.assert_has_defines({"foo": Macro("foo", tokenize("(bar)"))}) - - def test_preprocess_define_with_one_arg(self): - result = self.preprocess("`define foo(arg)arg 123") - result.assert_has_tokens("") - result.assert_has_defines({"foo": Macro("foo", tokenize("arg 123"), args=("arg",))}) - - def test_preprocess_define_with_one_arg_ignores_initial_space(self): - result = self.preprocess("`define foo(arg) arg 123") - result.assert_has_tokens("") - result.assert_has_defines({"foo": Macro("foo", tokenize("arg 123"), args=("arg",))}) - - def test_preprocess_define_with_multiple_args(self): - result = self.preprocess("`define foo( arg1, arg2)arg1 arg2") - result.assert_has_tokens("") - result.assert_has_defines({"foo": Macro("foo", tokenize("arg1 arg2"), args=("arg1", "arg2"))}) - - def test_preprocess_define_with_default_values(self): - result = self.preprocess("`define foo(arg1, arg2=default)arg1 arg2") - result.assert_has_tokens("") - result.assert_has_defines({"foo": Macro("foo", - tokenize("arg1 arg2"), - args=("arg1", "arg2"), - defaults={"arg2": tokenize("default")})}) - - def test_preprocess_substitute_define_without_args(self): - result = self.preprocess("""\ -`define foo bar \"abc\" -`foo""") - result.assert_has_tokens("bar \"abc\"") - - def test_preprocess_substitute_define_with_one_arg(self): - result = self.preprocess("""\ -`define foo(arg)arg 123 -`foo(hello hey)""") - result.assert_has_tokens("hello hey 123") - - def test_preprocess_substitute_define_with_space_before_arg(self): - result = self.preprocess("""\ -`define foo(arg) arg -`foo (hello)""") - result.assert_has_tokens("hello") - - def test_preprocess_substitute_define_no_args(self): - result = self.preprocess("""\ -`define foo bar -`foo (hello)""") - result.assert_has_tokens("bar (hello)") - - def test_preprocess_substitute_define_with_multile_args(self): - result = self.preprocess("""\ -`define foo(arg1, arg2)arg1,arg2 -`foo(1 2, hello)""") - result.assert_has_tokens("1 2, hello") - - def test_preprocess_substitute_define_with_default_values(self): - result = self.preprocess("""\ -`define foo(arg1, arg2=default)arg1 arg2 -`foo(1)""") - result.assert_has_tokens("1 default") - - def test_preprocess_include_directive(self): - self.write_file("include.svh", "hello hey") - result = self.preprocess('`include "include.svh"', - include_paths=[self.output_path]) - result.assert_has_tokens("hello hey") - result.assert_included_files([join(self.output_path, "include.svh")]) - - def test_detects_circular_includes(self): - self.write_file("include1.svh", '`include "include2.svh"') - self.write_file("include2.svh", '`include "include1.svh"') - result = self.preprocess('`include "include1.svh"', - include_paths=[self.output_path]) - result.logger.error.assert_called_once_with( - 'Circular `include of include2.svh detected\n%s', - 'from fn.v line 1:\n' - '`include "include1.svh"\n' - '~~~~~~~~\n' - 'from include1.svh line 1:\n' - '`include "include2.svh"\n' - '~~~~~~~~\n' - 'from include2.svh line 1:\n' - '`include "include1.svh"\n' - '~~~~~~~~\n' - 'at include1.svh line 1:\n' - '`include "include2.svh"\n' - ' ~~~~~~~~~~~~~~') - - def test_detects_circular_include_of_self(self): - self.write_file("include.svh", '`include "include.svh"') - result = self.preprocess('`include "include.svh"', - include_paths=[self.output_path]) - result.logger.error.assert_called_once_with( - 'Circular `include of include.svh detected\n%s', - 'from fn.v line 1:\n' - '`include "include.svh"\n' - '~~~~~~~~\n' - 'from include.svh line 1:\n' - '`include "include.svh"\n' - '~~~~~~~~\n' - 'at include.svh line 1:\n' - '`include "include.svh"\n' - ' ~~~~~~~~~~~~~') - - def test_does_not_detect_non_circular_includes(self): - self.write_file("include3.svh", 'keep') - self.write_file("include1.svh", '`include "include3.svh"\n`include "include2.svh"') - self.write_file("include2.svh", '`include "include3.svh"') - result = self.preprocess('`include "include1.svh"\n`include "include2.svh"', - include_paths=[self.output_path]) - result.assert_no_log() - - def test_detects_circular_macro_expansion_of_self(self): - result = self.preprocess(''' -`define foo `foo -`foo -''') - result.logger.error.assert_called_once_with( - 'Circular macro expansion of foo detected\n%s', - 'from fn.v line 3:\n' - '`foo\n' - '~~~~\n' - 'from fn.v line 2:\n' - '`define foo `foo\n' - ' ~~~~\n' - 'at fn.v line 2:\n' - '`define foo `foo\n' - ' ~~~~') - - def test_detects_circular_macro_expansion(self): - result = self.preprocess(''' -`define foo `bar -`define bar `foo -`foo -''') - result.logger.error.assert_called_once_with( - 'Circular macro expansion of bar detected\n%s', - 'from fn.v line 4:\n' - '`foo\n' - '~~~~\n' - 'from fn.v line 2:\n' - '`define foo `bar\n' - ' ~~~~\n' - 'from fn.v line 3:\n' - '`define bar `foo\n' - ' ~~~~\n' - 'at fn.v line 2:\n' - '`define foo `bar\n' - ' ~~~~') - - def test_does_not_detect_non_circular_macro_expansion(self): - result = self.preprocess(''' -`define foo bar -`foo -`foo -''') - result.assert_no_log() - - def test_preprocess_include_directive_from_define(self): - self.write_file("include.svh", "hello hey") - result = self.preprocess('''\ -`define inc "include.svh" -`include `inc''', - include_paths=[self.output_path]) - result.assert_has_tokens('hello hey') - result.assert_included_files([join(self.output_path, "include.svh")]) - - def test_preprocess_include_directive_from_define_with_args(self): - self.write_file("include.svh", "hello hey") - result = self.preprocess('''\ -`define inc(a) a -`include `inc("include.svh")''', include_paths=[self.output_path]) - result.assert_has_tokens('hello hey') - result.assert_included_files([join(self.output_path, "include.svh")]) - - def test_preprocess_macros_are_recursively_expanded(self): - result = self.preprocess('''\ -`define foo `bar -`define bar xyz -`foo -`define bar abc -`foo -''', - include_paths=[self.output_path]) - result.assert_has_tokens('xyz\nabc\n') - - def test_ifndef_taken(self): - result = self.preprocess('''\ -`ifndef foo -taken -`endif -keep''') - result.assert_has_tokens("taken\nkeep") - - def test_ifdef_taken(self): - result = self.preprocess('''\ -`define foo -`ifdef foo -taken -`endif -keep''') - result.assert_has_tokens("taken\nkeep") - - def test_ifdef_else_taken(self): - result = self.preprocess('''\ -`define foo -`ifdef foo -taken -`else -else -`endif -keep''') - result.assert_has_tokens("taken\nkeep") - - def test_ifdef_not_taken(self): - result = self.preprocess('''\ -`ifdef foo -taken -`endif -keep''') - result.assert_has_tokens("keep") - - def test_ifdef_else_not_taken(self): - result = self.preprocess('''\ -`ifdef foo -taken -`else -else -`endif -keep''') - result.assert_has_tokens("else\nkeep") - - def test_ifdef_elsif_taken(self): - result = self.preprocess('''\ -`define foo -`ifdef foo -taken -`elsif bar -elsif_taken -`else -else_taken -`endif -keep''') - result.assert_has_tokens("taken\nkeep") - - def test_ifdef_elsif_elseif_taken(self): - result = self.preprocess('''\ -`define bar -`ifdef foo -taken -`elsif bar -elsif_taken -`else -else_taken -`endif -keep''') - result.assert_has_tokens("elsif_taken\nkeep") - - def test_ifdef_elsif_else_taken(self): - result = self.preprocess('''\ -`ifdef foo -taken -`elsif bar -elsif_taken -`else -else_taken -`endif -keep''') - result.assert_has_tokens("else_taken\nkeep") - - def test_nested_ifdef(self): - result = self.preprocess('''\ -`define foo -`ifdef foo -outer_before -`ifdef bar -inner_ifndef -`else -inner_else -`endif -`ifdef bar -inner_ifndef -`elsif foo -inner_elsif -`endif -outer_after -`endif -keep''') - result.assert_has_tokens("outer_before\n" - "inner_else\n" - "inner_elsif\n" - "outer_after\n" - "keep") - - def test_preprocess_broken_define(self): - result = self.preprocess("`define") - result.assert_has_tokens("") - result.assert_no_defines() - result.logger.warning.assert_called_once_with( - "Verilog `define without argument\n%s", - "at fn.v line 1:\n" - "`define\n" - "~~~~~~~") - - def test_preprocess_broken_define_first_argument(self): - result = self.preprocess('`define "foo"') - result.assert_has_tokens("") - result.assert_no_defines() - result.logger.warning.assert_called_once_with( - "Verilog `define invalid name\n%s", - "at fn.v line 1:\n" - '`define "foo"\n' - " ~~~~~") - - def test_preprocess_broken_define_argument_list(self): - result = self.preprocess('`define foo(') - result.assert_has_tokens("") - result.assert_no_defines() - result.logger.warning.assert_called_once_with( - "EOF reached when parsing `define argument list\n%s", - "at fn.v line 1:\n" - '`define foo(\n' - " ~") - - result = self.preprocess('`define foo(a') - result.assert_has_tokens("") - result.assert_no_defines() - result.logger.warning.assert_called_once_with( - "EOF reached when parsing `define argument list\n%s", - "at fn.v line 1:\n" - '`define foo(a\n' - " ~") - - result = self.preprocess('`define foo(a=') - result.assert_has_tokens("") - result.assert_no_defines() - result.logger.warning.assert_called_once_with( - "EOF reached when parsing `define argument list\n%s", - "at fn.v line 1:\n" - '`define foo(a=\n' - " ~") - - result = self.preprocess('`define foo(a=b') - result.assert_has_tokens("") - result.assert_no_defines() - result.logger.warning.assert_called_once_with( - "EOF reached when parsing `define argument list\n%s", - "at fn.v line 1:\n" - '`define foo(a=b\n' - " ~") - - result = self.preprocess('`define foo(a=)') - result.assert_has_tokens("") - result.assert_no_defines() - result.logger.warning.assert_called_once_with( - "EOF reached when parsing `define argument list\n%s", - "at fn.v line 1:\n" - '`define foo(a=)\n' - " ~") - - result = self.preprocess('`define foo("a"') - result.assert_has_tokens("") - result.assert_no_defines() - result.logger.warning.assert_called_once_with( - "EOF reached when parsing `define argument list\n%s", - "at fn.v line 1:\n" - '`define foo("a"\n' - " ~") - - result = self.preprocess('`define foo("a"=') - result.assert_has_tokens("") - result.assert_no_defines() - result.logger.warning.assert_called_once_with( - "EOF reached when parsing `define argument list\n%s", - "at fn.v line 1:\n" - '`define foo("a"=\n' - " ~") - - def test_preprocess_substitute_define_broken_args(self): - result = self.preprocess("""\ -`define foo(arg1, arg2)arg1,arg2 -`foo(1 2)""") - result.assert_has_tokens("") - - result = self.preprocess("""\ -`define foo(arg1, arg2)arg1,arg2 -`foo""") - result.assert_has_tokens("") - - result = self.preprocess("""\ -`define foo(arg1, arg2)arg1,arg2 -`foo(""") - result.assert_has_tokens("") - - result = self.preprocess("""\ -`define foo(arg1, arg2)arg1,arg2 -`foo(1""") - result.assert_has_tokens("") - - def test_preprocess_substitute_define_missing_argument(self): - result = self.preprocess("""\ -`define foo(arg1, arg2)arg1,arg2 -`foo(1)""") - result.assert_has_tokens("") - result.logger.warning.assert_called_once_with( - "Missing value for argument arg2\n%s", - "at fn.v line 2:\n" - '`foo(1)\n' - "~~~~") - - def test_preprocess_substitute_define_too_many_argument(self): - result = self.preprocess("""\ -`define foo(arg1)arg1 -`foo(1, 2)""") - result.assert_has_tokens("") - result.logger.warning.assert_called_once_with( - "Too many arguments got 2 expected 1\n%s", - "at fn.v line 2:\n" - '`foo(1, 2)\n' - "~~~~") - - def test_preprocess_substitute_define_with_nested_argument(self): - result = self.preprocess( - "`define foo(arg1, arg2)arg1\n" - "`foo([1, 2], 3)") - self.assertFalse(result.logger.warning.called) - result.assert_has_tokens("[1, 2]") - - result = self.preprocess( - "`define foo(arg1, arg2)arg1\n" - "`foo({1, 2}, 3)") - self.assertFalse(result.logger.warning.called) - result.assert_has_tokens("{1, 2}") - - result = self.preprocess( - "`define foo(arg1, arg2)arg1\n" - "`foo((1, 2), 3)") - self.assertFalse(result.logger.warning.called) - result.assert_has_tokens("(1, 2)") - - result = self.preprocess( - "`define foo(arg1)arg1\n" - "`foo((1, 2))") - self.assertFalse(result.logger.warning.called) - result.assert_has_tokens("(1, 2)") - - # Not OK in simulator but we let the simulator - # tell the user that this is a problem - result = self.preprocess( - "`define foo(arg1)arg1\n" - "`foo([1, 2)") - self.assertFalse(result.logger.warning.called) - result.assert_has_tokens("[1, 2") - - def test_preprocess_substitute_define_eof(self): - result = self.preprocess( - "`define foo(arg1, arg2)arg1,arg2\n" - "`foo(1 2") - result.assert_has_tokens("") - result.logger.warning.assert_called_once_with( - "EOF reached when parsing `define actuals\n%s", - "at fn.v line 2:\n" - '`foo(1 2\n' - "~~~~") - - result = self.preprocess( - "`define foo(arg1, arg2)arg1,arg2\n" - "`foo((1 2)") - result.assert_has_tokens("") - result.logger.warning.assert_called_once_with( - "EOF reached when parsing `define actuals\n%s", - "at fn.v line 2:\n" - '`foo((1 2)\n' - "~~~~") - - def test_substitute_undefined(self): - result = self.preprocess('`foo') - result.assert_has_tokens("") - # Debug since there are many custon `names in tools - result.logger.debug.assert_called_once_with( - "Verilog undefined name\n%s", - "at fn.v line 1:\n" - '`foo\n' - "~~~~") - - def test_preprocess_include_directive_missing_file(self): - result = self.preprocess('`include "missing.svh"', - include_paths=[self.output_path]) - result.assert_has_tokens("") - result.assert_included_files([]) - # Is debug message since there are so many builtin includes in tools - result.logger.debug.assert_called_once_with( - "Could not find `include file missing.svh\n%s", - "at fn.v line 1:\n" - '`include "missing.svh"\n' - " ~~~~~~~~~~~~~") - - def test_preprocess_include_directive_missing_argument(self): - result = self.preprocess('`include', - include_paths=[self.output_path]) - result.assert_has_tokens("") - result.assert_included_files([]) - result.logger.warning.assert_called_once_with( - "EOF reached when parsing `include argument\n%s", - "at fn.v line 1:\n" - '`include\n' - "~~~~~~~~") - - def test_preprocess_include_directive_bad_argument(self): - self.write_file("include.svh", "hello hey") - result = self.preprocess('`include foo "include.svh"', - include_paths=[self.output_path]) - result.assert_has_tokens(' "include.svh"') - result.assert_included_files([]) - result.logger.warning.assert_called_once_with( - "Verilog `include bad argument\n%s", - "at fn.v line 1:\n" - '`include foo "include.svh"\n' - " ~~~") - - def test_preprocess_include_directive_from_define_bad_argument(self): - result = self.preprocess('''\ -`define inc foo -`include `inc -keep''', - include_paths=[self.output_path]) - result.assert_has_tokens('\nkeep') - result.assert_included_files([]) - result.logger.warning.assert_called_once_with( - "Verilog `include has bad argument\n%s", - "from fn.v line 2:\n" - '`include `inc\n' - ' ~~~~\n' - "at fn.v line 1:\n" - '`define inc foo\n' - " ~~~") - - def test_preprocess_include_directive_from_empty_define(self): - result = self.preprocess('''\ -`define inc -`include `inc -keep''', include_paths=[self.output_path]) - result.assert_has_tokens('\nkeep') - result.assert_included_files([]) - result.logger.warning.assert_called_once_with( - "Verilog `include has bad argument, empty define `inc\n%s", - "at fn.v line 2:\n" - '`include `inc\n' - " ~~~~") - - def test_preprocess_include_directive_from_define_not_defined(self): - result = self.preprocess('`include `inc', include_paths=[self.output_path]) - result.assert_has_tokens('') - result.assert_included_files([]) - result.logger.warning.assert_called_once_with( - "Verilog `include argument not defined\n%s", - "at fn.v line 1:\n" - '`include `inc\n' - " ~~~~") - - def test_preprocess_error_in_include_file(self): - self.write_file("include.svh", '`include foo') - result = self.preprocess('\n\n`include "include.svh"', - include_paths=[self.output_path]) - result.assert_has_tokens('\n\n') - result.assert_included_files([join(self.output_path, "include.svh")]) - result.logger.warning.assert_called_once_with( - "Verilog `include bad argument\n%s", - "from fn.v line 3:\n" - '`include "include.svh"\n' - "~~~~~~~~\n" - "at include.svh line 1:\n" - '`include foo\n' - ' ~~~') - - def test_preprocess_error_in_expanded_define(self): - result = self.preprocess('''\ -`define foo `include wrong -`foo -''', include_paths=[self.output_path]) - result.assert_has_tokens('\n') - result.assert_included_files([]) - result.logger.warning.assert_called_once_with( - "Verilog `include bad argument\n%s", - "from fn.v line 2:\n" - '`foo\n' - '~~~~\n' - "at fn.v line 1:\n" - '`define foo `include wrong\n' - " ~~~~~") - - def test_ifdef_eof(self): - result = self.preprocess('''\ -`ifdef foo -taken''') - result.assert_has_tokens("") - result.logger.warning.assert_called_once_with( - "EOF reached when parsing `ifdef\n%s", - "at fn.v line 1:\n" - '`ifdef foo\n' - '~~~~~~') - - def test_ifdef_bad_argument(self): - result = self.preprocess('''\ -`ifdef "hello" -keep''') - result.assert_has_tokens("\nkeep") - result.logger.warning.assert_called_once_with( - "Bad argument to `ifdef\n%s", - "at fn.v line 1:\n" - '`ifdef "hello"\n' - ' ~~~~~~~') - - def test_elsif_bad_argument(self): - result = self.preprocess('''\ -`ifdef bar -`elsif "hello" -keep''') - result.assert_has_tokens("\nkeep") - result.logger.warning.assert_called_once_with( - "Bad argument to `elsif\n%s", - "at fn.v line 2:\n" - '`elsif "hello"\n' - ' ~~~~~~~') - - def test_undefineall(self): - result = self.preprocess('''\ -`define foo keep -`define bar keep2 -`foo -`undefineall''') - result.assert_has_tokens("keep\n") - result.assert_no_defines() - - def test_resetall(self): - result = self.preprocess('''\ -`define foo keep -`define bar keep2 -`foo -`resetall''') - result.assert_has_tokens("keep\n") - result.assert_no_defines() - - def test_undef(self): - result = self.preprocess('''\ -`define foo keep -`define bar keep2 -`foo -`undef foo''') - result.assert_has_tokens("keep\n") - result.assert_has_defines({"bar": Macro("bar", tokenize("keep2"))}) - - def test_undef_eof(self): - result = self.preprocess('`undef') - result.assert_has_tokens("") - result.assert_no_defines() - result.logger.warning.assert_called_once_with( - "EOF reached when parsing `undef\n%s", - "at fn.v line 1:\n" - '`undef\n' - '~~~~~~') - - def test_undef_bad_argument(self): - result = self.preprocess('`undef "foo"') - result.assert_has_tokens("") - result.assert_no_defines() - result.logger.warning.assert_called_once_with( - "Bad argument to `undef\n%s", - "at fn.v line 1:\n" - '`undef "foo"\n' - ' ~~~~~') - - def test_undef_not_defined(self): - result = self.preprocess('`undef foo') - result.assert_has_tokens("") - result.assert_no_defines() - result.logger.warning.assert_called_once_with( - "`undef argument was not previously defined\n%s", - "at fn.v line 1:\n" - '`undef foo\n' - ' ~~~') - - def test_ignores_celldefine(self): - result = self.preprocess('`celldefine`endcelldefine keep') - result.assert_has_tokens(" keep") - result.assert_no_log() - - def test_ignores_timescale(self): - result = self.preprocess('`timescale 1 ns / 1 ps\nkeep') - result.assert_has_tokens("\nkeep") - result.assert_no_log() - - def test_ignores_default_nettype(self): - result = self.preprocess('`default_nettype none\nkeep') - result.assert_has_tokens("\nkeep") - result.assert_no_log() - - def test_ignores_nounconnected_drive(self): - result = self.preprocess('`nounconnected_drive keep') - result.assert_has_tokens(" keep") - result.assert_no_log() - - def test_ignores_protected_region(self): - result = self.preprocess("""\ -keep_before -`pragma protect begin_protected -ASDADSJAKSJDKSAJDISA -`pragma protect end_protected -keep_end""") - - result.assert_has_tokens("keep_before\n\nkeep_end") - result.assert_no_log() - - def preprocess(self, code, file_name="fn.v", include_paths=None): - """ - Tokenize & Preprocess - """ - tokenizer = VerilogTokenizer() - preprocessor = VerilogPreprocessor(tokenizer) - write_file(file_name, code) - tokens = tokenizer.tokenize(code, file_name=file_name) - defines = {} - included_files = [] - with mock.patch("vunit.parsing.verilog.preprocess.LOGGER", autospec=True) as logger: - tokens = preprocessor.preprocess(tokens, defines, include_paths, included_files) - return PreprocessResult(self, tokens, defines, - [file_name for _, file_name in included_files if file_name is not None], - logger) - - def write_file(self, file_name, contents): - """ - Write file with contents into output path - """ - full_name = join(self.output_path, file_name) - full_path = dirname(full_name) - if not exists(full_path): - os.makedirs(full_path) - with open(full_name, "w") as fptr: - fptr.write(contents) - - -class PreprocessResult(object): - """ - Helper object to test preprocessing - """ - - def __init__(self, # pylint: disable=too-many-arguments - test, tokens, defines, included_files, logger): - self.test = test - self.tokens = tokens - self.defines = defines - self.included_files = included_files - self.logger = logger - - def assert_has_tokens(self, code, noloc=True): - """ - Check that tokens are the same as code - """ - expected = tokenize(code) - - if noloc: - self.test.assertEqual(strip_loc(self.tokens), strip_loc(expected)) - else: - self.test.assertEqual(self.tokens, expected) - return self - - def assert_no_defines(self): - """ - Assert that there were no defines - """ - self.test.assertEqual(self.defines, {}) - - def assert_included_files(self, included_files): - """ - Assert that these files where included - """ - self.test.assertEqual(self.included_files, included_files) - - def assert_has_defines(self, defines): - """ - Assert that these defines were made - """ - self.test.assertEqual(self.defines.keys(), defines.keys()) - - def macro_strip_loc(define): - """ - Strip location information from a Macro - """ - define.tokens = strip_loc(define.tokens) - for key, value in define.defaults.items(): - define.defaults[key] = strip_loc(value) - - for key in self.defines: - self.test.assertEqual(macro_strip_loc(self.defines[key]), - macro_strip_loc(defines[key])) - - def assert_no_log(self): - """ - Assert that no log call were made - """ - self.test.assertEqual(self.logger.debug.mock_calls, []) - self.test.assertEqual(self.logger.info.mock_calls, []) - self.test.assertEqual(self.logger.warning.mock_calls, []) - self.test.assertEqual(self.logger.error.mock_calls, []) - - -def tokenize(code, file_name="fn.v"): - """ - Tokenize - """ - tokenizer = VerilogTokenizer() - return tokenizer.tokenize(code, file_name=file_name) - - -def strip_loc(tokens): - """ - Strip location information - """ - return [Token(token.kind, token.value, None) for token in tokens] +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +# pylint: disable=too-many-public-methods +# pylint: disable=unused-wildcard-import +# pylint: disable=wildcard-import + +""" +Test of the Verilog preprocessor +""" + +from os.path import join, dirname, exists +import os +from unittest import TestCase +import shutil +from vunit.ostools import renew_path, write_file +from vunit.parsing.verilog.preprocess import VerilogPreprocessor, Macro +from vunit.parsing.verilog.tokenizer import VerilogTokenizer +from vunit.parsing.tokenizer import Token +from vunit.test.mock_2or3 import mock + + +class TestVerilogPreprocessor(TestCase): + """ + Test of the Verilog preprocessor + """ + + def setUp(self): + self.output_path = join(dirname(__file__), "test_verilog_preprocessor_out") + renew_path(self.output_path) + self.cwd = os.getcwd() + os.chdir(self.output_path) + + def tearDown(self): + os.chdir(self.cwd) + shutil.rmtree(self.output_path) + + def test_non_preprocess_tokens_are_kept(self): + result = self.preprocess('"hello"ident/*comment*///comment') + result.assert_has_tokens('"hello"ident/*comment*///comment') + result.assert_no_defines() + + def test_preprocess_define_without_value(self): + result = self.preprocess("`define foo") + result.assert_has_tokens("") + result.assert_has_defines({"foo": Macro("foo")}) + + result = self.preprocess("`define foo\nkeep") + result.assert_has_tokens("keep") + result.assert_has_defines({"foo": Macro("foo")}) + + def test_preprocess_define_with_value(self): + result = self.preprocess("`define foo bar \"abc\"") + result.assert_has_tokens("") + result.assert_has_defines({"foo": Macro("foo", tokenize("bar \"abc\""))}) + + def test_preprocess_define_with_lpar_value(self): + result = self.preprocess("`define foo (bar)") + result.assert_has_tokens("") + result.assert_has_defines({"foo": Macro("foo", tokenize("(bar)"))}) + + def test_preprocess_define_with_one_arg(self): + result = self.preprocess("`define foo(arg)arg 123") + result.assert_has_tokens("") + result.assert_has_defines({"foo": Macro("foo", tokenize("arg 123"), args=("arg",))}) + + def test_preprocess_define_with_one_arg_ignores_initial_space(self): + result = self.preprocess("`define foo(arg) arg 123") + result.assert_has_tokens("") + result.assert_has_defines({"foo": Macro("foo", tokenize("arg 123"), args=("arg",))}) + + def test_preprocess_define_with_multiple_args(self): + result = self.preprocess("`define foo( arg1, arg2)arg1 arg2") + result.assert_has_tokens("") + result.assert_has_defines({"foo": Macro("foo", tokenize("arg1 arg2"), args=("arg1", "arg2"))}) + + def test_preprocess_define_with_default_values(self): + result = self.preprocess("`define foo(arg1, arg2=default)arg1 arg2") + result.assert_has_tokens("") + result.assert_has_defines({"foo": Macro("foo", + tokenize("arg1 arg2"), + args=("arg1", "arg2"), + defaults={"arg2": tokenize("default")})}) + + def test_preprocess_substitute_define_without_args(self): + result = self.preprocess("""\ +`define foo bar \"abc\" +`foo""") + result.assert_has_tokens("bar \"abc\"") + + def test_preprocess_substitute_define_with_one_arg(self): + result = self.preprocess("""\ +`define foo(arg)arg 123 +`foo(hello hey)""") + result.assert_has_tokens("hello hey 123") + + def test_preprocess_substitute_define_with_space_before_arg(self): + result = self.preprocess("""\ +`define foo(arg) arg +`foo (hello)""") + result.assert_has_tokens("hello") + + def test_preprocess_substitute_define_no_args(self): + result = self.preprocess("""\ +`define foo bar +`foo (hello)""") + result.assert_has_tokens("bar (hello)") + + def test_preprocess_substitute_define_with_multile_args(self): + result = self.preprocess("""\ +`define foo(arg1, arg2)arg1,arg2 +`foo(1 2, hello)""") + result.assert_has_tokens("1 2, hello") + + def test_preprocess_substitute_define_with_default_values(self): + result = self.preprocess("""\ +`define foo(arg1, arg2=default)arg1 arg2 +`foo(1)""") + result.assert_has_tokens("1 default") + + def test_preprocess_include_directive(self): + self.write_file("include.svh", "hello hey") + result = self.preprocess('`include "include.svh"', + include_paths=[self.output_path]) + result.assert_has_tokens("hello hey") + result.assert_included_files([join(self.output_path, "include.svh")]) + + def test_detects_circular_includes(self): + self.write_file("include1.svh", '`include "include2.svh"') + self.write_file("include2.svh", '`include "include1.svh"') + result = self.preprocess('`include "include1.svh"', + include_paths=[self.output_path]) + result.logger.error.assert_called_once_with( + 'Circular `include of include2.svh detected\n%s', + 'from fn.v line 1:\n' + '`include "include1.svh"\n' + '~~~~~~~~\n' + 'from include1.svh line 1:\n' + '`include "include2.svh"\n' + '~~~~~~~~\n' + 'from include2.svh line 1:\n' + '`include "include1.svh"\n' + '~~~~~~~~\n' + 'at include1.svh line 1:\n' + '`include "include2.svh"\n' + ' ~~~~~~~~~~~~~~') + + def test_detects_circular_include_of_self(self): + self.write_file("include.svh", '`include "include.svh"') + result = self.preprocess('`include "include.svh"', + include_paths=[self.output_path]) + result.logger.error.assert_called_once_with( + 'Circular `include of include.svh detected\n%s', + 'from fn.v line 1:\n' + '`include "include.svh"\n' + '~~~~~~~~\n' + 'from include.svh line 1:\n' + '`include "include.svh"\n' + '~~~~~~~~\n' + 'at include.svh line 1:\n' + '`include "include.svh"\n' + ' ~~~~~~~~~~~~~') + + def test_does_not_detect_non_circular_includes(self): + self.write_file("include3.svh", 'keep') + self.write_file("include1.svh", '`include "include3.svh"\n`include "include2.svh"') + self.write_file("include2.svh", '`include "include3.svh"') + result = self.preprocess('`include "include1.svh"\n`include "include2.svh"', + include_paths=[self.output_path]) + result.assert_no_log() + + def test_detects_circular_macro_expansion_of_self(self): + result = self.preprocess(''' +`define foo `foo +`foo +''') + result.logger.error.assert_called_once_with( + 'Circular macro expansion of foo detected\n%s', + 'from fn.v line 3:\n' + '`foo\n' + '~~~~\n' + 'from fn.v line 2:\n' + '`define foo `foo\n' + ' ~~~~\n' + 'at fn.v line 2:\n' + '`define foo `foo\n' + ' ~~~~') + + def test_detects_circular_macro_expansion(self): + result = self.preprocess(''' +`define foo `bar +`define bar `foo +`foo +''') + result.logger.error.assert_called_once_with( + 'Circular macro expansion of bar detected\n%s', + 'from fn.v line 4:\n' + '`foo\n' + '~~~~\n' + 'from fn.v line 2:\n' + '`define foo `bar\n' + ' ~~~~\n' + 'from fn.v line 3:\n' + '`define bar `foo\n' + ' ~~~~\n' + 'at fn.v line 2:\n' + '`define foo `bar\n' + ' ~~~~') + + def test_does_not_detect_non_circular_macro_expansion(self): + result = self.preprocess(''' +`define foo bar +`foo +`foo +''') + result.assert_no_log() + + def test_preprocess_include_directive_from_define(self): + self.write_file("include.svh", "hello hey") + result = self.preprocess('''\ +`define inc "include.svh" +`include `inc''', + include_paths=[self.output_path]) + result.assert_has_tokens('hello hey') + result.assert_included_files([join(self.output_path, "include.svh")]) + + def test_preprocess_include_directive_from_define_with_args(self): + self.write_file("include.svh", "hello hey") + result = self.preprocess('''\ +`define inc(a) a +`include `inc("include.svh")''', include_paths=[self.output_path]) + result.assert_has_tokens('hello hey') + result.assert_included_files([join(self.output_path, "include.svh")]) + + def test_preprocess_macros_are_recursively_expanded(self): + result = self.preprocess('''\ +`define foo `bar +`define bar xyz +`foo +`define bar abc +`foo +''', + include_paths=[self.output_path]) + result.assert_has_tokens('xyz\nabc\n') + + def test_ifndef_taken(self): + result = self.preprocess('''\ +`ifndef foo +taken +`endif +keep''') + result.assert_has_tokens("taken\nkeep") + + def test_ifdef_taken(self): + result = self.preprocess('''\ +`define foo +`ifdef foo +taken +`endif +keep''') + result.assert_has_tokens("taken\nkeep") + + def test_ifdef_else_taken(self): + result = self.preprocess('''\ +`define foo +`ifdef foo +taken +`else +else +`endif +keep''') + result.assert_has_tokens("taken\nkeep") + + def test_ifdef_not_taken(self): + result = self.preprocess('''\ +`ifdef foo +taken +`endif +keep''') + result.assert_has_tokens("keep") + + def test_ifdef_else_not_taken(self): + result = self.preprocess('''\ +`ifdef foo +taken +`else +else +`endif +keep''') + result.assert_has_tokens("else\nkeep") + + def test_ifdef_elsif_taken(self): + result = self.preprocess('''\ +`define foo +`ifdef foo +taken +`elsif bar +elsif_taken +`else +else_taken +`endif +keep''') + result.assert_has_tokens("taken\nkeep") + + def test_ifdef_elsif_elseif_taken(self): + result = self.preprocess('''\ +`define bar +`ifdef foo +taken +`elsif bar +elsif_taken +`else +else_taken +`endif +keep''') + result.assert_has_tokens("elsif_taken\nkeep") + + def test_ifdef_elsif_else_taken(self): + result = self.preprocess('''\ +`ifdef foo +taken +`elsif bar +elsif_taken +`else +else_taken +`endif +keep''') + result.assert_has_tokens("else_taken\nkeep") + + def test_nested_ifdef(self): + result = self.preprocess('''\ +`define foo +`ifdef foo +outer_before +`ifdef bar +inner_ifndef +`else +inner_else +`endif +`ifdef bar +inner_ifndef +`elsif foo +inner_elsif +`endif +outer_after +`endif +keep''') + result.assert_has_tokens("outer_before\n" + "inner_else\n" + "inner_elsif\n" + "outer_after\n" + "keep") + + def test_preprocess_broken_define(self): + result = self.preprocess("`define") + result.assert_has_tokens("") + result.assert_no_defines() + result.logger.warning.assert_called_once_with( + "Verilog `define without argument\n%s", + "at fn.v line 1:\n" + "`define\n" + "~~~~~~~") + + def test_preprocess_broken_define_first_argument(self): + result = self.preprocess('`define "foo"') + result.assert_has_tokens("") + result.assert_no_defines() + result.logger.warning.assert_called_once_with( + "Verilog `define invalid name\n%s", + "at fn.v line 1:\n" + '`define "foo"\n' + " ~~~~~") + + def test_preprocess_broken_define_argument_list(self): + result = self.preprocess('`define foo(') + result.assert_has_tokens("") + result.assert_no_defines() + result.logger.warning.assert_called_once_with( + "EOF reached when parsing `define argument list\n%s", + "at fn.v line 1:\n" + '`define foo(\n' + " ~") + + result = self.preprocess('`define foo(a') + result.assert_has_tokens("") + result.assert_no_defines() + result.logger.warning.assert_called_once_with( + "EOF reached when parsing `define argument list\n%s", + "at fn.v line 1:\n" + '`define foo(a\n' + " ~") + + result = self.preprocess('`define foo(a=') + result.assert_has_tokens("") + result.assert_no_defines() + result.logger.warning.assert_called_once_with( + "EOF reached when parsing `define argument list\n%s", + "at fn.v line 1:\n" + '`define foo(a=\n' + " ~") + + result = self.preprocess('`define foo(a=b') + result.assert_has_tokens("") + result.assert_no_defines() + result.logger.warning.assert_called_once_with( + "EOF reached when parsing `define argument list\n%s", + "at fn.v line 1:\n" + '`define foo(a=b\n' + " ~") + + result = self.preprocess('`define foo(a=)') + result.assert_has_tokens("") + result.assert_no_defines() + result.logger.warning.assert_called_once_with( + "EOF reached when parsing `define argument list\n%s", + "at fn.v line 1:\n" + '`define foo(a=)\n' + " ~") + + result = self.preprocess('`define foo("a"') + result.assert_has_tokens("") + result.assert_no_defines() + result.logger.warning.assert_called_once_with( + "EOF reached when parsing `define argument list\n%s", + "at fn.v line 1:\n" + '`define foo("a"\n' + " ~") + + result = self.preprocess('`define foo("a"=') + result.assert_has_tokens("") + result.assert_no_defines() + result.logger.warning.assert_called_once_with( + "EOF reached when parsing `define argument list\n%s", + "at fn.v line 1:\n" + '`define foo("a"=\n' + " ~") + + def test_preprocess_substitute_define_broken_args(self): + result = self.preprocess("""\ +`define foo(arg1, arg2)arg1,arg2 +`foo(1 2)""") + result.assert_has_tokens("") + + result = self.preprocess("""\ +`define foo(arg1, arg2)arg1,arg2 +`foo""") + result.assert_has_tokens("") + + result = self.preprocess("""\ +`define foo(arg1, arg2)arg1,arg2 +`foo(""") + result.assert_has_tokens("") + + result = self.preprocess("""\ +`define foo(arg1, arg2)arg1,arg2 +`foo(1""") + result.assert_has_tokens("") + + def test_preprocess_substitute_define_missing_argument(self): + result = self.preprocess("""\ +`define foo(arg1, arg2)arg1,arg2 +`foo(1)""") + result.assert_has_tokens("") + result.logger.warning.assert_called_once_with( + "Missing value for argument arg2\n%s", + "at fn.v line 2:\n" + '`foo(1)\n' + "~~~~") + + def test_preprocess_substitute_define_too_many_argument(self): + result = self.preprocess("""\ +`define foo(arg1)arg1 +`foo(1, 2)""") + result.assert_has_tokens("") + result.logger.warning.assert_called_once_with( + "Too many arguments got 2 expected 1\n%s", + "at fn.v line 2:\n" + '`foo(1, 2)\n' + "~~~~") + + def test_preprocess_substitute_define_with_nested_argument(self): + result = self.preprocess( + "`define foo(arg1, arg2)arg1\n" + "`foo([1, 2], 3)") + self.assertFalse(result.logger.warning.called) + result.assert_has_tokens("[1, 2]") + + result = self.preprocess( + "`define foo(arg1, arg2)arg1\n" + "`foo({1, 2}, 3)") + self.assertFalse(result.logger.warning.called) + result.assert_has_tokens("{1, 2}") + + result = self.preprocess( + "`define foo(arg1, arg2)arg1\n" + "`foo((1, 2), 3)") + self.assertFalse(result.logger.warning.called) + result.assert_has_tokens("(1, 2)") + + result = self.preprocess( + "`define foo(arg1)arg1\n" + "`foo((1, 2))") + self.assertFalse(result.logger.warning.called) + result.assert_has_tokens("(1, 2)") + + # Not OK in simulator but we let the simulator + # tell the user that this is a problem + result = self.preprocess( + "`define foo(arg1)arg1\n" + "`foo([1, 2)") + self.assertFalse(result.logger.warning.called) + result.assert_has_tokens("[1, 2") + + def test_preprocess_substitute_define_eof(self): + result = self.preprocess( + "`define foo(arg1, arg2)arg1,arg2\n" + "`foo(1 2") + result.assert_has_tokens("") + result.logger.warning.assert_called_once_with( + "EOF reached when parsing `define actuals\n%s", + "at fn.v line 2:\n" + '`foo(1 2\n' + "~~~~") + + result = self.preprocess( + "`define foo(arg1, arg2)arg1,arg2\n" + "`foo((1 2)") + result.assert_has_tokens("") + result.logger.warning.assert_called_once_with( + "EOF reached when parsing `define actuals\n%s", + "at fn.v line 2:\n" + '`foo((1 2)\n' + "~~~~") + + def test_substitute_undefined(self): + result = self.preprocess('`foo') + result.assert_has_tokens("") + # Debug since there are many custon `names in tools + result.logger.debug.assert_called_once_with( + "Verilog undefined name\n%s", + "at fn.v line 1:\n" + '`foo\n' + "~~~~") + + def test_preprocess_include_directive_missing_file(self): + result = self.preprocess('`include "missing.svh"', + include_paths=[self.output_path]) + result.assert_has_tokens("") + result.assert_included_files([]) + # Is debug message since there are so many builtin includes in tools + result.logger.debug.assert_called_once_with( + "Could not find `include file missing.svh\n%s", + "at fn.v line 1:\n" + '`include "missing.svh"\n' + " ~~~~~~~~~~~~~") + + def test_preprocess_include_directive_missing_argument(self): + result = self.preprocess('`include', + include_paths=[self.output_path]) + result.assert_has_tokens("") + result.assert_included_files([]) + result.logger.warning.assert_called_once_with( + "EOF reached when parsing `include argument\n%s", + "at fn.v line 1:\n" + '`include\n' + "~~~~~~~~") + + def test_preprocess_include_directive_bad_argument(self): + self.write_file("include.svh", "hello hey") + result = self.preprocess('`include foo "include.svh"', + include_paths=[self.output_path]) + result.assert_has_tokens(' "include.svh"') + result.assert_included_files([]) + result.logger.warning.assert_called_once_with( + "Verilog `include bad argument\n%s", + "at fn.v line 1:\n" + '`include foo "include.svh"\n' + " ~~~") + + def test_preprocess_include_directive_from_define_bad_argument(self): + result = self.preprocess('''\ +`define inc foo +`include `inc +keep''', + include_paths=[self.output_path]) + result.assert_has_tokens('\nkeep') + result.assert_included_files([]) + result.logger.warning.assert_called_once_with( + "Verilog `include has bad argument\n%s", + "from fn.v line 2:\n" + '`include `inc\n' + ' ~~~~\n' + "at fn.v line 1:\n" + '`define inc foo\n' + " ~~~") + + def test_preprocess_include_directive_from_empty_define(self): + result = self.preprocess('''\ +`define inc +`include `inc +keep''', include_paths=[self.output_path]) + result.assert_has_tokens('\nkeep') + result.assert_included_files([]) + result.logger.warning.assert_called_once_with( + "Verilog `include has bad argument, empty define `inc\n%s", + "at fn.v line 2:\n" + '`include `inc\n' + " ~~~~") + + def test_preprocess_include_directive_from_define_not_defined(self): + result = self.preprocess('`include `inc', include_paths=[self.output_path]) + result.assert_has_tokens('') + result.assert_included_files([]) + result.logger.warning.assert_called_once_with( + "Verilog `include argument not defined\n%s", + "at fn.v line 1:\n" + '`include `inc\n' + " ~~~~") + + def test_preprocess_error_in_include_file(self): + self.write_file("include.svh", '`include foo') + result = self.preprocess('\n\n`include "include.svh"', + include_paths=[self.output_path]) + result.assert_has_tokens('\n\n') + result.assert_included_files([join(self.output_path, "include.svh")]) + result.logger.warning.assert_called_once_with( + "Verilog `include bad argument\n%s", + "from fn.v line 3:\n" + '`include "include.svh"\n' + "~~~~~~~~\n" + "at include.svh line 1:\n" + '`include foo\n' + ' ~~~') + + def test_preprocess_error_in_expanded_define(self): + result = self.preprocess('''\ +`define foo `include wrong +`foo +''', include_paths=[self.output_path]) + result.assert_has_tokens('\n') + result.assert_included_files([]) + result.logger.warning.assert_called_once_with( + "Verilog `include bad argument\n%s", + "from fn.v line 2:\n" + '`foo\n' + '~~~~\n' + "at fn.v line 1:\n" + '`define foo `include wrong\n' + " ~~~~~") + + def test_ifdef_eof(self): + result = self.preprocess('''\ +`ifdef foo +taken''') + result.assert_has_tokens("") + result.logger.warning.assert_called_once_with( + "EOF reached when parsing `ifdef\n%s", + "at fn.v line 1:\n" + '`ifdef foo\n' + '~~~~~~') + + def test_ifdef_bad_argument(self): + result = self.preprocess('''\ +`ifdef "hello" +keep''') + result.assert_has_tokens("\nkeep") + result.logger.warning.assert_called_once_with( + "Bad argument to `ifdef\n%s", + "at fn.v line 1:\n" + '`ifdef "hello"\n' + ' ~~~~~~~') + + def test_elsif_bad_argument(self): + result = self.preprocess('''\ +`ifdef bar +`elsif "hello" +keep''') + result.assert_has_tokens("\nkeep") + result.logger.warning.assert_called_once_with( + "Bad argument to `elsif\n%s", + "at fn.v line 2:\n" + '`elsif "hello"\n' + ' ~~~~~~~') + + def test_undefineall(self): + result = self.preprocess('''\ +`define foo keep +`define bar keep2 +`foo +`undefineall''') + result.assert_has_tokens("keep\n") + result.assert_no_defines() + + def test_resetall(self): + result = self.preprocess('''\ +`define foo keep +`define bar keep2 +`foo +`resetall''') + result.assert_has_tokens("keep\n") + result.assert_no_defines() + + def test_undef(self): + result = self.preprocess('''\ +`define foo keep +`define bar keep2 +`foo +`undef foo''') + result.assert_has_tokens("keep\n") + result.assert_has_defines({"bar": Macro("bar", tokenize("keep2"))}) + + def test_undef_eof(self): + result = self.preprocess('`undef') + result.assert_has_tokens("") + result.assert_no_defines() + result.logger.warning.assert_called_once_with( + "EOF reached when parsing `undef\n%s", + "at fn.v line 1:\n" + '`undef\n' + '~~~~~~') + + def test_undef_bad_argument(self): + result = self.preprocess('`undef "foo"') + result.assert_has_tokens("") + result.assert_no_defines() + result.logger.warning.assert_called_once_with( + "Bad argument to `undef\n%s", + "at fn.v line 1:\n" + '`undef "foo"\n' + ' ~~~~~') + + def test_undef_not_defined(self): + result = self.preprocess('`undef foo') + result.assert_has_tokens("") + result.assert_no_defines() + result.logger.warning.assert_called_once_with( + "`undef argument was not previously defined\n%s", + "at fn.v line 1:\n" + '`undef foo\n' + ' ~~~') + + def test_ignores_celldefine(self): + result = self.preprocess('`celldefine`endcelldefine keep') + result.assert_has_tokens(" keep") + result.assert_no_log() + + def test_ignores_timescale(self): + result = self.preprocess('`timescale 1 ns / 1 ps\nkeep') + result.assert_has_tokens("\nkeep") + result.assert_no_log() + + def test_ignores_default_nettype(self): + result = self.preprocess('`default_nettype none\nkeep') + result.assert_has_tokens("\nkeep") + result.assert_no_log() + + def test_ignores_nounconnected_drive(self): + result = self.preprocess('`nounconnected_drive keep') + result.assert_has_tokens(" keep") + result.assert_no_log() + + def test_ignores_protected_region(self): + result = self.preprocess("""\ +keep_before +`pragma protect begin_protected +ASDADSJAKSJDKSAJDISA +`pragma protect end_protected +keep_end""") + + result.assert_has_tokens("keep_before\n\nkeep_end") + result.assert_no_log() + + def preprocess(self, code, file_name="fn.v", include_paths=None): + """ + Tokenize & Preprocess + """ + tokenizer = VerilogTokenizer() + preprocessor = VerilogPreprocessor(tokenizer) + write_file(file_name, code) + tokens = tokenizer.tokenize(code, file_name=file_name) + defines = {} + included_files = [] + with mock.patch("vunit.parsing.verilog.preprocess.LOGGER", autospec=True) as logger: + tokens = preprocessor.preprocess(tokens, defines, include_paths, included_files) + return PreprocessResult(self, tokens, defines, + [file_name for _, file_name in included_files if file_name is not None], + logger) + + def write_file(self, file_name, contents): + """ + Write file with contents into output path + """ + full_name = join(self.output_path, file_name) + full_path = dirname(full_name) + if not exists(full_path): + os.makedirs(full_path) + with open(full_name, "w") as fptr: + fptr.write(contents) + + +class PreprocessResult(object): + """ + Helper object to test preprocessing + """ + + def __init__(self, # pylint: disable=too-many-arguments + test, tokens, defines, included_files, logger): + self.test = test + self.tokens = tokens + self.defines = defines + self.included_files = included_files + self.logger = logger + + def assert_has_tokens(self, code, noloc=True): + """ + Check that tokens are the same as code + """ + expected = tokenize(code) + + if noloc: + self.test.assertEqual(strip_loc(self.tokens), strip_loc(expected)) + else: + self.test.assertEqual(self.tokens, expected) + return self + + def assert_no_defines(self): + """ + Assert that there were no defines + """ + self.test.assertEqual(self.defines, {}) + + def assert_included_files(self, included_files): + """ + Assert that these files where included + """ + self.test.assertEqual(self.included_files, included_files) + + def assert_has_defines(self, defines): + """ + Assert that these defines were made + """ + self.test.assertEqual(self.defines.keys(), defines.keys()) + + def macro_strip_loc(define): + """ + Strip location information from a Macro + """ + define.tokens = strip_loc(define.tokens) + for key, value in define.defaults.items(): + define.defaults[key] = strip_loc(value) + + for key in self.defines: + self.test.assertEqual(macro_strip_loc(self.defines[key]), + macro_strip_loc(defines[key])) + + def assert_no_log(self): + """ + Assert that no log call were made + """ + self.test.assertEqual(self.logger.debug.mock_calls, []) + self.test.assertEqual(self.logger.info.mock_calls, []) + self.test.assertEqual(self.logger.warning.mock_calls, []) + self.test.assertEqual(self.logger.error.mock_calls, []) + + +def tokenize(code, file_name="fn.v"): + """ + Tokenize + """ + tokenizer = VerilogTokenizer() + return tokenizer.tokenize(code, file_name=file_name) + + +def strip_loc(tokens): + """ + Strip location information + """ + return [Token(token.kind, token.value, None) for token in tokens] diff --git a/vunit/test/unit/test_verilog_tokenizer.py b/vunit/test/unit/test_verilog_tokenizer.py index 9186ade50..3d7eb2493 100644 --- a/vunit/test/unit/test_verilog_tokenizer.py +++ b/vunit/test/unit/test_verilog_tokenizer.py @@ -1,156 +1,156 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -# pylint: disable=unused-wildcard-import -# pylint: disable=wildcard-import - -""" -Test of the Verilog tokenizer -""" - -from unittest import TestCase -from vunit.parsing.verilog.tokenizer import VerilogTokenizer -from vunit.parsing.verilog.tokens import * - - -class TestVerilogTokenizer(TestCase): - """ - Test of the Verilog tokenizer - """ - - def test_tokenizes_define(self): - self.check("`define name", - [PREPROCESSOR(value="define"), - WHITESPACE(value=" "), - IDENTIFIER(value="name")]) - - def test_tokenizes_string_literal(self): - self.check('"hello"', - [STRING(value='hello')]) - - self.check('"hel""lo"', - [STRING(value='hel'), - STRING(value='lo')]) - - self.check(r'"h\"ello"', - [STRING(value='h"ello')]) - - self.check(r'"h\"ello"', - [STRING(value='h"ello')]) - - self.check(r'"\"ello"', - [STRING(value='"ello')]) - - self.check(r'"\"\""', - [STRING(value='""')]) - - self.check(r'''"hi -there"''', - [STRING(value='hi\nthere')]) - - self.check(r'''"hi\ -there"''', - [STRING(value='hithere')]) - - def test_tokenizes_single_line_comment(self): - self.check("// asd", - [COMMENT(value=" asd")]) - - self.check("asd// asd", - [IDENTIFIER(value="asd"), - COMMENT(value=" asd")]) - - self.check("asd// asd //", - [IDENTIFIER(value="asd"), - COMMENT(value=" asd //")]) - - def test_tokenizes_multi_line_comment(self): - self.check("/* asd */", - [MULTI_COMMENT(value=" asd ")]) - - self.check("/* /* asd */", - [MULTI_COMMENT(value=" /* asd ")]) - - self.check("/* /* asd */", - [MULTI_COMMENT(value=" /* asd ")]) - - self.check("/* 1 \n 2 */", - [MULTI_COMMENT(value=" 1 \n 2 ")]) - - self.check("/* 1 \r\n 2 */", - [MULTI_COMMENT(value=" 1 \r\n 2 ")]) - - def test_tokenizes_semi_colon(self): - self.check("asd;", - [IDENTIFIER(value="asd"), - SEMI_COLON(value='')]) - - def test_tokenizes_newline(self): - self.check("asd\n", - [IDENTIFIER(value="asd"), - NEWLINE(value='')]) - - def test_tokenizes_comma(self): - self.check(",", - [COMMA(value='')]) - - def test_tokenizes_parenthesis(self): - self.check("()", - [LPAR(value=''), - RPAR(value='')]) - - def test_tokenizes_hash(self): - self.check("#", - [HASH(value='')]) - - def test_tokenizes_equal(self): - self.check("=", - [EQUAL(value='')]) - - def test_escaped_newline_ignored(self): - self.check("a\\\nb", - [IDENTIFIER(value='a'), - IDENTIFIER(value='b')]) - - def test_tokenizes_keywords(self): - self.check("module", - [MODULE(value='')]) - self.check("endmodule", - [ENDMODULE(value='')]) - self.check("package", - [PACKAGE(value='')]) - self.check("endpackage", - [ENDPACKAGE(value='')]) - self.check("parameter", - [PARAMETER(value='')]) - self.check("import", - [IMPORT(value='')]) - - def test_has_location_information(self): - self.check("`define foo", [ - PREPROCESSOR(value="define", location=(("fn.v", (0, 6)), None)), - WHITESPACE(value=" ", location=(("fn.v", (7, 7)), None)), - IDENTIFIER(value="foo", location=(("fn.v", (8, 10)), None)), - ], strip_loc=False) - - def setUp(self): - self.tokenizer = VerilogTokenizer() - - def check(self, code, tokens, strip_loc=True): - """ - Helper method to test tokenizer - Tokenize code and check that it matches tokens - optionally strip location information in comparison - """ - - def preprocess(tokens): # pylint: disable=missing-docstring - if strip_loc: - return [token.kind(token.value, None) for token in tokens] - - return tokens - - self.assertEqual(preprocess(list(self.tokenizer.tokenize(code, "fn.v"))), - tokens) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +# pylint: disable=unused-wildcard-import +# pylint: disable=wildcard-import + +""" +Test of the Verilog tokenizer +""" + +from unittest import TestCase +from vunit.parsing.verilog.tokenizer import VerilogTokenizer +from vunit.parsing.verilog.tokens import * + + +class TestVerilogTokenizer(TestCase): + """ + Test of the Verilog tokenizer + """ + + def test_tokenizes_define(self): + self.check("`define name", + [PREPROCESSOR(value="define"), + WHITESPACE(value=" "), + IDENTIFIER(value="name")]) + + def test_tokenizes_string_literal(self): + self.check('"hello"', + [STRING(value='hello')]) + + self.check('"hel""lo"', + [STRING(value='hel'), + STRING(value='lo')]) + + self.check(r'"h\"ello"', + [STRING(value='h"ello')]) + + self.check(r'"h\"ello"', + [STRING(value='h"ello')]) + + self.check(r'"\"ello"', + [STRING(value='"ello')]) + + self.check(r'"\"\""', + [STRING(value='""')]) + + self.check(r'''"hi +there"''', + [STRING(value='hi\nthere')]) + + self.check(r'''"hi\ +there"''', + [STRING(value='hithere')]) + + def test_tokenizes_single_line_comment(self): + self.check("// asd", + [COMMENT(value=" asd")]) + + self.check("asd// asd", + [IDENTIFIER(value="asd"), + COMMENT(value=" asd")]) + + self.check("asd// asd //", + [IDENTIFIER(value="asd"), + COMMENT(value=" asd //")]) + + def test_tokenizes_multi_line_comment(self): + self.check("/* asd */", + [MULTI_COMMENT(value=" asd ")]) + + self.check("/* /* asd */", + [MULTI_COMMENT(value=" /* asd ")]) + + self.check("/* /* asd */", + [MULTI_COMMENT(value=" /* asd ")]) + + self.check("/* 1 \n 2 */", + [MULTI_COMMENT(value=" 1 \n 2 ")]) + + self.check("/* 1 \r\n 2 */", + [MULTI_COMMENT(value=" 1 \r\n 2 ")]) + + def test_tokenizes_semi_colon(self): + self.check("asd;", + [IDENTIFIER(value="asd"), + SEMI_COLON(value='')]) + + def test_tokenizes_newline(self): + self.check("asd\n", + [IDENTIFIER(value="asd"), + NEWLINE(value='')]) + + def test_tokenizes_comma(self): + self.check(",", + [COMMA(value='')]) + + def test_tokenizes_parenthesis(self): + self.check("()", + [LPAR(value=''), + RPAR(value='')]) + + def test_tokenizes_hash(self): + self.check("#", + [HASH(value='')]) + + def test_tokenizes_equal(self): + self.check("=", + [EQUAL(value='')]) + + def test_escaped_newline_ignored(self): + self.check("a\\\nb", + [IDENTIFIER(value='a'), + IDENTIFIER(value='b')]) + + def test_tokenizes_keywords(self): + self.check("module", + [MODULE(value='')]) + self.check("endmodule", + [ENDMODULE(value='')]) + self.check("package", + [PACKAGE(value='')]) + self.check("endpackage", + [ENDPACKAGE(value='')]) + self.check("parameter", + [PARAMETER(value='')]) + self.check("import", + [IMPORT(value='')]) + + def test_has_location_information(self): + self.check("`define foo", [ + PREPROCESSOR(value="define", location=(("fn.v", (0, 6)), None)), + WHITESPACE(value=" ", location=(("fn.v", (7, 7)), None)), + IDENTIFIER(value="foo", location=(("fn.v", (8, 10)), None)), + ], strip_loc=False) + + def setUp(self): + self.tokenizer = VerilogTokenizer() + + def check(self, code, tokens, strip_loc=True): + """ + Helper method to test tokenizer + Tokenize code and check that it matches tokens + optionally strip location information in comparison + """ + + def preprocess(tokens): # pylint: disable=missing-docstring + if strip_loc: + return [token.kind(token.value, None) for token in tokens] + + return tokens + + self.assertEqual(preprocess(list(self.tokenizer.tokenize(code, "fn.v"))), + tokens) diff --git a/vunit/test/unit/test_vhdl_parser.py b/vunit/test/unit/test_vhdl_parser.py index f295c95c8..7304f4dfc 100644 --- a/vunit/test/unit/test_vhdl_parser.py +++ b/vunit/test/unit/test_vhdl_parser.py @@ -1,520 +1,520 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Test of the VHDL parser -""" - -from unittest import TestCase -from vunit.vhdl_parser import (VHDLDesignFile, - VHDLInterfaceElement, - VHDLEntity, - VHDLSubtypeIndication, - VHDLEnumerationType, - VHDLArrayType, - VHDLReference, - VHDLRecordType, - remove_comments) - - -class TestVHDLParser(TestCase): # pylint: disable=too-many-public-methods - """ - Test of the VHDL parser - """ - - def test_parsing_empty(self): - design_file = VHDLDesignFile.parse("") - self.assertEqual(design_file.entities, []) - self.assertEqual(design_file.packages, []) - self.assertEqual(design_file.architectures, []) - - def test_parsing_simple_entity(self): - entity = self.parse_single_entity("""\ -entity simple is -end entity; -""") - self.assertEqual(entity.identifier, "simple") - self.assertEqual(entity.ports, []) - self.assertEqual(entity.generics, []) - - def test_parsing_entity_with_package_generic(self): - entity = self.parse_single_entity("""\ -entity ent is - generic ( - package p is new work.pkg generic map (c => 1, b => 2); - package_g : integer - ); -end entity; -""") - self.assertEqual(entity.identifier, "ent") - self.assertEqual(entity.ports, []) - self.assertEqual(len(entity.generics), 1) - self.assertEqual(entity.generics[0].identifier, 'package_g') - self.assertEqual(entity.generics[0].subtype_indication.type_mark, 'integer') - - def test_parsing_entity_with_type_generic(self): - entity = self.parse_single_entity("""\ -entity ent is - generic ( - type t; - type_g : integer - ); -end entity; -""") - self.assertEqual(entity.identifier, "ent") - self.assertEqual(entity.ports, []) - self.assertEqual(len(entity.generics), 1) - self.assertEqual(entity.generics[0].identifier, 'type_g') - self.assertEqual(entity.generics[0].subtype_indication.type_mark, 'integer') - - def test_parsing_entity_with_string_semicolon_colon(self): - entity = self.parse_single_entity("""\ -entity ent is - generic ( - const : string := "a;a"; - const2 : string := ";a""a;a"; - const3 : string := ": a b c :" - ); -end entity; -""") - self.assertEqual(entity.identifier, "ent") - self.assertEqual(entity.ports, []) - self.assertEqual(len(entity.generics), 3) - self.assertEqual(entity.generics[0].identifier, 'const') - self.assertEqual(entity.generics[0].subtype_indication.type_mark, 'string') - self.assertEqual(entity.generics[0].init_value, '"a;a"') - self.assertEqual(entity.generics[1].identifier, 'const2') - self.assertEqual(entity.generics[1].subtype_indication.type_mark, 'string') - self.assertEqual(entity.generics[1].init_value, '";a""a;a"') - self.assertEqual(entity.generics[2].identifier, 'const3') - self.assertEqual(entity.generics[2].subtype_indication.type_mark, 'string') - self.assertEqual(entity.generics[2].init_value, '": a b c :"') - - def test_parsing_entity_with_function_generic(self): - entity = self.parse_single_entity("""\ -entity ent is - generic ( - function f(a : integer; b : integer) return integer; - function_g : boolean; - impure function if(a : integer; b : integer) return integer; - procedure_g : boolean; - procedure p(a : integer; b : integer) - ); -end entity; -""") - self.assertEqual(entity.identifier, "ent") - self.assertEqual(entity.ports, []) - self.assertEqual(len(entity.generics), 2) - self.assertEqual(entity.generics[0].identifier, 'function_g') - self.assertEqual(entity.generics[0].subtype_indication.type_mark, 'boolean') - self.assertEqual(entity.generics[1].identifier, 'procedure_g') - self.assertEqual(entity.generics[1].subtype_indication.type_mark, 'boolean') - - def test_getting_entities_from_design_file(self): - design_file = VHDLDesignFile.parse(""" -entity entity1 is -end entity; - -package package1 is -end package; - -entity entity2 is -end entity; -""") - entities = design_file.entities - self.assertEqual(len(entities), 2) - self.assertEqual(entities[0].identifier, "entity1") - self.assertEqual(entities[1].identifier, "entity2") - - def test_getting_architectures_from_design_file(self): - design_file = VHDLDesignFile.parse(""" -entity foo is -end entity; - -architecture rtl of foo is -begin -end architecture; -""") - self.assertEqual(len(design_file.entities), 1) - self.assertEqual(len(design_file.architectures), 1) - arch = design_file.architectures - self.assertEqual(len(arch), 1) - self.assertEqual(arch[0].entity, "foo") - self.assertEqual(arch[0].identifier, "rtl") - - def test_parsing_references(self): - design_file = VHDLDesignFile.parse(""" -library name1; - use name1.foo.all; - -library ieee ; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -use name1.bla.all; - -library lib1,lib2, lib3; -use lib1.foo, lib2.bar,lib3.xyz; - -context name1.is_identifier; - -entity work1.foo1 -entity work1.foo1(a1) -for all : bar use entity work2.foo2 -for all : bar use entity work2.foo2 (a2) -for foo : bar use configuration work.cfg - -entity foo is -- False -configuration bar of ent -- False - -package new_pkg is new lib.pkg; -""") - self.assertEqual(len(design_file.references), 14) - self.assertEqual(sorted(design_file.references, key=repr), sorted([ - VHDLReference('configuration', 'work', 'cfg', None), - VHDLReference('context', 'name1', 'is_identifier', None), - VHDLReference('entity', 'work1', 'foo1', 'a1'), - VHDLReference('entity', 'work1', 'foo1', None), - VHDLReference('entity', 'work2', 'foo2', 'a2'), - VHDLReference('entity', 'work2', 'foo2', None), - VHDLReference('package', 'ieee', 'numeric_std', 'all'), - VHDLReference('package', 'ieee', 'std_logic_1164', 'all'), - VHDLReference('package', 'lib1', 'foo', None), - VHDLReference('package', 'lib2', 'bar', None), - VHDLReference('package', 'lib3', 'xyz', None), - VHDLReference('package', 'name1', 'bla', 'all'), - VHDLReference('package', 'name1', 'foo', 'all'), - VHDLReference('package', 'lib', 'pkg', None), - ], key=repr)) - - def test_parsing_entity_with_generics(self): - entity = self.parse_single_entity("""\ -entity name is - generic (max_value : integer range 2-2 to 2**10 := (2-19)*4; - enable_foo : boolean - ); -end entity; -""") - self.assertEqual(entity.identifier, "name") - self.assertNotEqual(entity.generics, []) - self.assertEqual(entity.ports, []) - generics = entity.generics - self.assertEqual(len(generics), 2) - - self.assertEqual(generics[0].identifier, "max_value") - self.assertEqual(generics[0].init_value, "(2-19)*4") - self.assertEqual(generics[0].mode, None) - self.assertEqual(generics[0].subtype_indication.code, "integer range 2-2 to 2**10") - self.assertEqual(generics[0].subtype_indication.type_mark, "integer") - # @TODO does not work -# self.assertEqual(generics[0].subtypeIndication.constraint, "range 2-2 to 2**10") - self.assertEqual(generics[1].identifier, "enable_foo") - self.assertEqual(generics[1].init_value, None) - self.assertEqual(generics[1].mode, None) - self.assertEqual(generics[1].subtype_indication.code, "boolean") - self.assertEqual(generics[1].subtype_indication.type_mark, "boolean") - - def test_parsing_entity_with_generics_corner_cases(self): - self.parse_single_entity("""\ -entity name is end entity; -""") - - entity = self.parse_single_entity("""\ -entity name is generic(g : t); end entity; -""") - self.assertEqual(len(entity.generics), 1) - self.assertEqual(entity.generics[0].identifier, "g") - - entity = self.parse_single_entity("""\ -entity name is generic -( -g : t -); -end entity; -""") - self.assertEqual(len(entity.generics), 1) - self.assertEqual(entity.generics[0].identifier, "g") - - entity = self.parse_single_entity("""\ -end architecture; entity name is generic -( -g : t -); -end entity; -""") - self.assertEqual(len(entity.generics), 1) - self.assertEqual(entity.generics[0].identifier, "g") - - entity = self.parse_single_entity("""\ -entity name is foo_generic -( -g : t -); -end entity; -""") - self.assertEqual(len(entity.generics), 0) - - def test_parsing_entity_with_ports(self): - entity = self.parse_single_entity("""\ -entity name is - port (clk : in std_logic; - data : out std_logic_vector(11-1 downto 0)); -end entity; -""") - - self.assertEqual(entity.identifier, "name") - self.assertEqual(entity.generics, []) - self.assertNotEqual(entity.ports, []) - - ports = entity.ports - self.assertEqual(len(ports), 2) - - self.assertEqual(ports[0].identifier, "clk") - self.assertEqual(ports[0].init_value, None) - self.assertEqual(ports[0].mode, "in") - self.assertEqual(ports[0].subtype_indication.code, "std_logic") - self.assertEqual(ports[0].subtype_indication.type_mark, "std_logic") - - self.assertEqual(ports[1].identifier, "data") - self.assertEqual(ports[1].init_value, None) - self.assertEqual(ports[1].mode, "out") - self.assertEqual(ports[1].subtype_indication.code, "std_logic_vector(11-1 downto 0)") - self.assertEqual(ports[1].subtype_indication.type_mark, "std_logic_vector") - self.assertEqual(ports[1].subtype_indication.constraint, "(11-1 downto 0)") - - def test_parsing_simple_package_body(self): - package_body = self.parse_single_package_body("""\ -package body simple is -begin -end package body; -""") - self.assertEqual(package_body.identifier, "simple") - - def test_parsing_simple_package(self): - package = self.parse_single_package("""\ -package simple is -end package; -""") - self.assertEqual(package.identifier, "simple") - - def test_parsing_generic_package(self): - package = self.parse_single_package("""\ -package pkg is - generic (c : integer; - b : bit_vector(4-1 downto 0)); -end package; -""") - self.assertEqual(package.identifier, "pkg") - - def test_parsing_generic_package_instance(self): - package = self.parse_single_package("""\ -package instance_pkg is new work.generic_pkg; -""") - self.assertEqual(package.identifier, "instance_pkg") - - package = self.parse_single_package("""\ - - -package instance_pkg is -new work.generic_pkg; -""") - self.assertEqual(package.identifier, "instance_pkg") - - package = self.parse_single_package("""\ -package instance_pkg is new work.generic_pkg - generic map (foo : boolean); -""") - self.assertEqual(package.identifier, "instance_pkg") - - # Skip nested packages using the heuristic that the package is - # indented from the first column. - design_file = VHDLDesignFile.parse("""\ - package instance_pkg is new work.generic_pkg -""") - self.assertEqual(len(design_file.packages), 0) - - def test_parsing_context(self): - context = self.parse_single_context("""\ -context foo is - library bar; - use bar.bar_pkg.all; -end context; - -context name1.is_identifier; -- Should be ignored -""") - self.assertEqual(context.identifier, "foo") - - context = self.parse_single_context("""\ -context identifier is - library bar; - use bar.bar_pkg.all; -end context identifier; -""") - self.assertEqual(context.identifier, "identifier") - - def test_getting_component_instantiations_from_design_file(self): - design_file = VHDLDesignFile.parse(""" -entity top is -end entity; - -architecture arch of top is -begin - labelFoo : component foo - generic map(WIDTH => 16) - port map(clk => '1', - rst => '0', - in_vec => record_reg.input_signal, - output => some_signal(UPPER_CONSTANT-1 downto LOWER_CONSTANT+1)); - - label2Foo : foo2 - port map(clk => '1', - rst => '0', - output => "00"); - - label3Foo : foo3 port map (clk, rst, X"A"); - -end architecture; - -""") - component_instantiations = design_file.component_instantiations - self.assertEqual(len(component_instantiations), 3) - self.assertEqual(component_instantiations[0], "foo") - self.assertEqual(component_instantiations[1], "foo2") - self.assertEqual(component_instantiations[2], "foo3") - - def test_adding_generics_to_entity(self): - entity = VHDLEntity("name") - entity.add_generic("max_value", "boolean", "20") - self.assertEqual(len(entity.generics), 1) - self.assertEqual(entity.generics[0].identifier, "max_value") - self.assertEqual(entity.generics[0].subtype_indication.type_mark, "boolean") - self.assertEqual(entity.generics[0].init_value, "20") - - def test_adding_ports_to_entity(self): - entity = VHDLEntity("name") - entity.add_port("foo", "inout", "foo_t") - self.assertEqual(len(entity.ports), 1) - self.assertEqual(entity.ports[0].identifier, "foo") - self.assertEqual(entity.ports[0].mode, "inout") - self.assertEqual(entity.ports[0].subtype_indication.type_mark, "foo_t") - - def test_that_enumeration_type_declarations_are_found(self): - code = """\ -type incomplete_type_declaration_t; - type color_t is( blue ,red , green ) ; -- Color type -type animal_t is (cow);""" - - enums = {e.identifier: e.literals for e in VHDLEnumerationType.find(code)} - expect = {'color_t': ['blue', 'red', 'green'], 'animal_t': ['cow']} - self.assertEqual(enums, expect) - - def test_that_array_type_declarations_are_found(self): - code = """\ -type constrained_integer_array_t is array(3 downto 0) of integer; -type unconstrained_fish_array_t is array(integer range <>) of fish_t; -type constrained_badgers_array_t is array ( -1 downto 0 ) of badger_t; -type unconstrained_natural_array_t is array ( integer range <> ) of natural; -""" - arrays = {e.identifier: e.subtype_indication.type_mark - for e in VHDLArrayType.find(code)} - expect = { - 'constrained_integer_array_t': 'integer', - 'unconstrained_fish_array_t': 'fish_t', - 'constrained_badgers_array_t': 'badger_t', - 'unconstrained_natural_array_t': 'natural', - } - self.assertEqual(arrays, expect) - - def test_that_record_type_declarations_are_found(self): - code = """\ -type space_time_t is record - x, y, z : real; - t : time; -end record space_time_t; - -type complex_t is record - im, re : real; -end record; - - type foo is -record - bar:std_logic_vector(7 downto 0) ; - end record ;""" - - records = {e.identifier: e.elements for e in VHDLRecordType.find(code)} - self.assertEqual(len(records), 3) - - self.assertIn('space_time_t', records) - self.assertEqual(records['space_time_t'][0].identifier_list, ['x', 'y', 'z']) - self.assertEqual(records['space_time_t'][0].subtype_indication.type_mark, 'real') - self.assertEqual(records['space_time_t'][1].identifier_list, ['t']) - self.assertEqual(records['space_time_t'][1].subtype_indication.type_mark, 'time') - - self.assertIn('complex_t', records) - self.assertEqual(records['complex_t'][0].identifier_list, ['im', 're']) - self.assertEqual(records['complex_t'][0].subtype_indication.type_mark, 'real') - - self.assertIn('foo', records) - self.assertEqual(records['foo'][0].identifier_list, ['bar']) - self.assertEqual(records['foo'][0].subtype_indication.type_mark, 'std_logic_vector') - self.assertEqual(records['foo'][0].subtype_indication.constraint, '(7 downto 0)') - self.assertTrue(records['foo'][0].subtype_indication.array_type) - - def test_remove_comments(self): - self.assertEqual(remove_comments("a\n-- foo \nb"), - "a\n \nb") - - def parse_single_entity(self, code): - """ - Helper function to parse a single entity - """ - design_file = VHDLDesignFile.parse(code) - self.assertEqual(len(design_file.entities), 1) - return design_file.entities[0] - - def parse_single_package(self, code): - """ - Helper function to parse a single package - """ - design_file = VHDLDesignFile.parse(code) - self.assertEqual(len(design_file.packages), 1) - return design_file.packages[0] - - def parse_single_context(self, code): - """ - Helper function to parse a single context - """ - design_file = VHDLDesignFile.parse(code) - self.assertEqual(len(design_file.contexts), 1) - return design_file.contexts[0] - - def parse_single_package_body(self, code): - """ - Helper function to parse a single package body - """ - design_file = VHDLDesignFile.parse(code) - self.assertEqual(len(design_file.package_bodies), 1) - return design_file.package_bodies[0] - - @staticmethod - def _create_entity(): - """ - Helper function to create a VHDLEntity - """ - data_width = VHDLInterfaceElement("data_width", - VHDLSubtypeIndication.parse("natural := 16")) - - clk = VHDLInterfaceElement("clk", - VHDLSubtypeIndication.parse("std_logic"), - "in") - data = VHDLInterfaceElement("data", - VHDLSubtypeIndication.parse("std_logic_vector(data_width-1 downto 0)"), - "out") - - entity = VHDLEntity(identifier="name", - generics=[data_width], - ports=[clk, data]) - return entity +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test of the VHDL parser +""" + +from unittest import TestCase +from vunit.vhdl_parser import (VHDLDesignFile, + VHDLInterfaceElement, + VHDLEntity, + VHDLSubtypeIndication, + VHDLEnumerationType, + VHDLArrayType, + VHDLReference, + VHDLRecordType, + remove_comments) + + +class TestVHDLParser(TestCase): # pylint: disable=too-many-public-methods + """ + Test of the VHDL parser + """ + + def test_parsing_empty(self): + design_file = VHDLDesignFile.parse("") + self.assertEqual(design_file.entities, []) + self.assertEqual(design_file.packages, []) + self.assertEqual(design_file.architectures, []) + + def test_parsing_simple_entity(self): + entity = self.parse_single_entity("""\ +entity simple is +end entity; +""") + self.assertEqual(entity.identifier, "simple") + self.assertEqual(entity.ports, []) + self.assertEqual(entity.generics, []) + + def test_parsing_entity_with_package_generic(self): + entity = self.parse_single_entity("""\ +entity ent is + generic ( + package p is new work.pkg generic map (c => 1, b => 2); + package_g : integer + ); +end entity; +""") + self.assertEqual(entity.identifier, "ent") + self.assertEqual(entity.ports, []) + self.assertEqual(len(entity.generics), 1) + self.assertEqual(entity.generics[0].identifier, 'package_g') + self.assertEqual(entity.generics[0].subtype_indication.type_mark, 'integer') + + def test_parsing_entity_with_type_generic(self): + entity = self.parse_single_entity("""\ +entity ent is + generic ( + type t; + type_g : integer + ); +end entity; +""") + self.assertEqual(entity.identifier, "ent") + self.assertEqual(entity.ports, []) + self.assertEqual(len(entity.generics), 1) + self.assertEqual(entity.generics[0].identifier, 'type_g') + self.assertEqual(entity.generics[0].subtype_indication.type_mark, 'integer') + + def test_parsing_entity_with_string_semicolon_colon(self): + entity = self.parse_single_entity("""\ +entity ent is + generic ( + const : string := "a;a"; + const2 : string := ";a""a;a"; + const3 : string := ": a b c :" + ); +end entity; +""") + self.assertEqual(entity.identifier, "ent") + self.assertEqual(entity.ports, []) + self.assertEqual(len(entity.generics), 3) + self.assertEqual(entity.generics[0].identifier, 'const') + self.assertEqual(entity.generics[0].subtype_indication.type_mark, 'string') + self.assertEqual(entity.generics[0].init_value, '"a;a"') + self.assertEqual(entity.generics[1].identifier, 'const2') + self.assertEqual(entity.generics[1].subtype_indication.type_mark, 'string') + self.assertEqual(entity.generics[1].init_value, '";a""a;a"') + self.assertEqual(entity.generics[2].identifier, 'const3') + self.assertEqual(entity.generics[2].subtype_indication.type_mark, 'string') + self.assertEqual(entity.generics[2].init_value, '": a b c :"') + + def test_parsing_entity_with_function_generic(self): + entity = self.parse_single_entity("""\ +entity ent is + generic ( + function f(a : integer; b : integer) return integer; + function_g : boolean; + impure function if(a : integer; b : integer) return integer; + procedure_g : boolean; + procedure p(a : integer; b : integer) + ); +end entity; +""") + self.assertEqual(entity.identifier, "ent") + self.assertEqual(entity.ports, []) + self.assertEqual(len(entity.generics), 2) + self.assertEqual(entity.generics[0].identifier, 'function_g') + self.assertEqual(entity.generics[0].subtype_indication.type_mark, 'boolean') + self.assertEqual(entity.generics[1].identifier, 'procedure_g') + self.assertEqual(entity.generics[1].subtype_indication.type_mark, 'boolean') + + def test_getting_entities_from_design_file(self): + design_file = VHDLDesignFile.parse(""" +entity entity1 is +end entity; + +package package1 is +end package; + +entity entity2 is +end entity; +""") + entities = design_file.entities + self.assertEqual(len(entities), 2) + self.assertEqual(entities[0].identifier, "entity1") + self.assertEqual(entities[1].identifier, "entity2") + + def test_getting_architectures_from_design_file(self): + design_file = VHDLDesignFile.parse(""" +entity foo is +end entity; + +architecture rtl of foo is +begin +end architecture; +""") + self.assertEqual(len(design_file.entities), 1) + self.assertEqual(len(design_file.architectures), 1) + arch = design_file.architectures + self.assertEqual(len(arch), 1) + self.assertEqual(arch[0].entity, "foo") + self.assertEqual(arch[0].identifier, "rtl") + + def test_parsing_references(self): + design_file = VHDLDesignFile.parse(""" +library name1; + use name1.foo.all; + +library ieee ; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use name1.bla.all; + +library lib1,lib2, lib3; +use lib1.foo, lib2.bar,lib3.xyz; + +context name1.is_identifier; + +entity work1.foo1 +entity work1.foo1(a1) +for all : bar use entity work2.foo2 +for all : bar use entity work2.foo2 (a2) +for foo : bar use configuration work.cfg + +entity foo is -- False +configuration bar of ent -- False + +package new_pkg is new lib.pkg; +""") + self.assertEqual(len(design_file.references), 14) + self.assertEqual(sorted(design_file.references, key=repr), sorted([ + VHDLReference('configuration', 'work', 'cfg', None), + VHDLReference('context', 'name1', 'is_identifier', None), + VHDLReference('entity', 'work1', 'foo1', 'a1'), + VHDLReference('entity', 'work1', 'foo1', None), + VHDLReference('entity', 'work2', 'foo2', 'a2'), + VHDLReference('entity', 'work2', 'foo2', None), + VHDLReference('package', 'ieee', 'numeric_std', 'all'), + VHDLReference('package', 'ieee', 'std_logic_1164', 'all'), + VHDLReference('package', 'lib1', 'foo', None), + VHDLReference('package', 'lib2', 'bar', None), + VHDLReference('package', 'lib3', 'xyz', None), + VHDLReference('package', 'name1', 'bla', 'all'), + VHDLReference('package', 'name1', 'foo', 'all'), + VHDLReference('package', 'lib', 'pkg', None), + ], key=repr)) + + def test_parsing_entity_with_generics(self): + entity = self.parse_single_entity("""\ +entity name is + generic (max_value : integer range 2-2 to 2**10 := (2-19)*4; + enable_foo : boolean + ); +end entity; +""") + self.assertEqual(entity.identifier, "name") + self.assertNotEqual(entity.generics, []) + self.assertEqual(entity.ports, []) + generics = entity.generics + self.assertEqual(len(generics), 2) + + self.assertEqual(generics[0].identifier, "max_value") + self.assertEqual(generics[0].init_value, "(2-19)*4") + self.assertEqual(generics[0].mode, None) + self.assertEqual(generics[0].subtype_indication.code, "integer range 2-2 to 2**10") + self.assertEqual(generics[0].subtype_indication.type_mark, "integer") + # @TODO does not work +# self.assertEqual(generics[0].subtypeIndication.constraint, "range 2-2 to 2**10") + self.assertEqual(generics[1].identifier, "enable_foo") + self.assertEqual(generics[1].init_value, None) + self.assertEqual(generics[1].mode, None) + self.assertEqual(generics[1].subtype_indication.code, "boolean") + self.assertEqual(generics[1].subtype_indication.type_mark, "boolean") + + def test_parsing_entity_with_generics_corner_cases(self): + self.parse_single_entity("""\ +entity name is end entity; +""") + + entity = self.parse_single_entity("""\ +entity name is generic(g : t); end entity; +""") + self.assertEqual(len(entity.generics), 1) + self.assertEqual(entity.generics[0].identifier, "g") + + entity = self.parse_single_entity("""\ +entity name is generic +( +g : t +); +end entity; +""") + self.assertEqual(len(entity.generics), 1) + self.assertEqual(entity.generics[0].identifier, "g") + + entity = self.parse_single_entity("""\ +end architecture; entity name is generic +( +g : t +); +end entity; +""") + self.assertEqual(len(entity.generics), 1) + self.assertEqual(entity.generics[0].identifier, "g") + + entity = self.parse_single_entity("""\ +entity name is foo_generic +( +g : t +); +end entity; +""") + self.assertEqual(len(entity.generics), 0) + + def test_parsing_entity_with_ports(self): + entity = self.parse_single_entity("""\ +entity name is + port (clk : in std_logic; + data : out std_logic_vector(11-1 downto 0)); +end entity; +""") + + self.assertEqual(entity.identifier, "name") + self.assertEqual(entity.generics, []) + self.assertNotEqual(entity.ports, []) + + ports = entity.ports + self.assertEqual(len(ports), 2) + + self.assertEqual(ports[0].identifier, "clk") + self.assertEqual(ports[0].init_value, None) + self.assertEqual(ports[0].mode, "in") + self.assertEqual(ports[0].subtype_indication.code, "std_logic") + self.assertEqual(ports[0].subtype_indication.type_mark, "std_logic") + + self.assertEqual(ports[1].identifier, "data") + self.assertEqual(ports[1].init_value, None) + self.assertEqual(ports[1].mode, "out") + self.assertEqual(ports[1].subtype_indication.code, "std_logic_vector(11-1 downto 0)") + self.assertEqual(ports[1].subtype_indication.type_mark, "std_logic_vector") + self.assertEqual(ports[1].subtype_indication.constraint, "(11-1 downto 0)") + + def test_parsing_simple_package_body(self): + package_body = self.parse_single_package_body("""\ +package body simple is +begin +end package body; +""") + self.assertEqual(package_body.identifier, "simple") + + def test_parsing_simple_package(self): + package = self.parse_single_package("""\ +package simple is +end package; +""") + self.assertEqual(package.identifier, "simple") + + def test_parsing_generic_package(self): + package = self.parse_single_package("""\ +package pkg is + generic (c : integer; + b : bit_vector(4-1 downto 0)); +end package; +""") + self.assertEqual(package.identifier, "pkg") + + def test_parsing_generic_package_instance(self): + package = self.parse_single_package("""\ +package instance_pkg is new work.generic_pkg; +""") + self.assertEqual(package.identifier, "instance_pkg") + + package = self.parse_single_package("""\ + + +package instance_pkg is +new work.generic_pkg; +""") + self.assertEqual(package.identifier, "instance_pkg") + + package = self.parse_single_package("""\ +package instance_pkg is new work.generic_pkg + generic map (foo : boolean); +""") + self.assertEqual(package.identifier, "instance_pkg") + + # Skip nested packages using the heuristic that the package is + # indented from the first column. + design_file = VHDLDesignFile.parse("""\ + package instance_pkg is new work.generic_pkg +""") + self.assertEqual(len(design_file.packages), 0) + + def test_parsing_context(self): + context = self.parse_single_context("""\ +context foo is + library bar; + use bar.bar_pkg.all; +end context; + +context name1.is_identifier; -- Should be ignored +""") + self.assertEqual(context.identifier, "foo") + + context = self.parse_single_context("""\ +context identifier is + library bar; + use bar.bar_pkg.all; +end context identifier; +""") + self.assertEqual(context.identifier, "identifier") + + def test_getting_component_instantiations_from_design_file(self): + design_file = VHDLDesignFile.parse(""" +entity top is +end entity; + +architecture arch of top is +begin + labelFoo : component foo + generic map(WIDTH => 16) + port map(clk => '1', + rst => '0', + in_vec => record_reg.input_signal, + output => some_signal(UPPER_CONSTANT-1 downto LOWER_CONSTANT+1)); + + label2Foo : foo2 + port map(clk => '1', + rst => '0', + output => "00"); + + label3Foo : foo3 port map (clk, rst, X"A"); + +end architecture; + +""") + component_instantiations = design_file.component_instantiations + self.assertEqual(len(component_instantiations), 3) + self.assertEqual(component_instantiations[0], "foo") + self.assertEqual(component_instantiations[1], "foo2") + self.assertEqual(component_instantiations[2], "foo3") + + def test_adding_generics_to_entity(self): + entity = VHDLEntity("name") + entity.add_generic("max_value", "boolean", "20") + self.assertEqual(len(entity.generics), 1) + self.assertEqual(entity.generics[0].identifier, "max_value") + self.assertEqual(entity.generics[0].subtype_indication.type_mark, "boolean") + self.assertEqual(entity.generics[0].init_value, "20") + + def test_adding_ports_to_entity(self): + entity = VHDLEntity("name") + entity.add_port("foo", "inout", "foo_t") + self.assertEqual(len(entity.ports), 1) + self.assertEqual(entity.ports[0].identifier, "foo") + self.assertEqual(entity.ports[0].mode, "inout") + self.assertEqual(entity.ports[0].subtype_indication.type_mark, "foo_t") + + def test_that_enumeration_type_declarations_are_found(self): + code = """\ +type incomplete_type_declaration_t; + type color_t is( blue ,red , green ) ; -- Color type +type animal_t is (cow);""" + + enums = {e.identifier: e.literals for e in VHDLEnumerationType.find(code)} + expect = {'color_t': ['blue', 'red', 'green'], 'animal_t': ['cow']} + self.assertEqual(enums, expect) + + def test_that_array_type_declarations_are_found(self): + code = """\ +type constrained_integer_array_t is array(3 downto 0) of integer; +type unconstrained_fish_array_t is array(integer range <>) of fish_t; +type constrained_badgers_array_t is array ( -1 downto 0 ) of badger_t; +type unconstrained_natural_array_t is array ( integer range <> ) of natural; +""" + arrays = {e.identifier: e.subtype_indication.type_mark + for e in VHDLArrayType.find(code)} + expect = { + 'constrained_integer_array_t': 'integer', + 'unconstrained_fish_array_t': 'fish_t', + 'constrained_badgers_array_t': 'badger_t', + 'unconstrained_natural_array_t': 'natural', + } + self.assertEqual(arrays, expect) + + def test_that_record_type_declarations_are_found(self): + code = """\ +type space_time_t is record + x, y, z : real; + t : time; +end record space_time_t; + +type complex_t is record + im, re : real; +end record; + + type foo is +record + bar:std_logic_vector(7 downto 0) ; + end record ;""" + + records = {e.identifier: e.elements for e in VHDLRecordType.find(code)} + self.assertEqual(len(records), 3) + + self.assertIn('space_time_t', records) + self.assertEqual(records['space_time_t'][0].identifier_list, ['x', 'y', 'z']) + self.assertEqual(records['space_time_t'][0].subtype_indication.type_mark, 'real') + self.assertEqual(records['space_time_t'][1].identifier_list, ['t']) + self.assertEqual(records['space_time_t'][1].subtype_indication.type_mark, 'time') + + self.assertIn('complex_t', records) + self.assertEqual(records['complex_t'][0].identifier_list, ['im', 're']) + self.assertEqual(records['complex_t'][0].subtype_indication.type_mark, 'real') + + self.assertIn('foo', records) + self.assertEqual(records['foo'][0].identifier_list, ['bar']) + self.assertEqual(records['foo'][0].subtype_indication.type_mark, 'std_logic_vector') + self.assertEqual(records['foo'][0].subtype_indication.constraint, '(7 downto 0)') + self.assertTrue(records['foo'][0].subtype_indication.array_type) + + def test_remove_comments(self): + self.assertEqual(remove_comments("a\n-- foo \nb"), + "a\n \nb") + + def parse_single_entity(self, code): + """ + Helper function to parse a single entity + """ + design_file = VHDLDesignFile.parse(code) + self.assertEqual(len(design_file.entities), 1) + return design_file.entities[0] + + def parse_single_package(self, code): + """ + Helper function to parse a single package + """ + design_file = VHDLDesignFile.parse(code) + self.assertEqual(len(design_file.packages), 1) + return design_file.packages[0] + + def parse_single_context(self, code): + """ + Helper function to parse a single context + """ + design_file = VHDLDesignFile.parse(code) + self.assertEqual(len(design_file.contexts), 1) + return design_file.contexts[0] + + def parse_single_package_body(self, code): + """ + Helper function to parse a single package body + """ + design_file = VHDLDesignFile.parse(code) + self.assertEqual(len(design_file.package_bodies), 1) + return design_file.package_bodies[0] + + @staticmethod + def _create_entity(): + """ + Helper function to create a VHDLEntity + """ + data_width = VHDLInterfaceElement("data_width", + VHDLSubtypeIndication.parse("natural := 16")) + + clk = VHDLInterfaceElement("clk", + VHDLSubtypeIndication.parse("std_logic"), + "in") + data = VHDLInterfaceElement("data", + VHDLSubtypeIndication.parse("std_logic_vector(data_width-1 downto 0)"), + "out") + + entity = VHDLEntity(identifier="name", + generics=[data_width], + ports=[clk, data]) + return entity diff --git a/vunit/test_bench.py b/vunit/test_bench.py index 0459091d0..d551561ce 100644 --- a/vunit/test_bench.py +++ b/vunit/test_bench.py @@ -1,627 +1,627 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Contains classes to represent a test bench and test cases -""" - -import logging -from os.path import basename -import re -import bisect -import collections -from collections import OrderedDict -from vunit.ostools import file_exists -from vunit.cached import cached -from vunit.test_list import TestList -from vunit.vhdl_parser import remove_comments as remove_vhdl_comments -from vunit.test_suites import IndependentSimTestCase, SameSimTestSuite -from vunit.parsing.encodings import HDL_FILE_ENCODING -from vunit.project import file_type_of, VERILOG_FILE_TYPES -from vunit.configuration import Configuration, ConfigurationVisitor, DEFAULT_NAME - - -LOGGER = logging.getLogger(__name__) - - -class TestBench(ConfigurationVisitor): - """ - A VUnit test bench top level - """ - - def __init__(self, design_unit, database=None): - ConfigurationVisitor.__init__(self) - self.design_unit = design_unit - self._database = database - - self._individual_tests = False - self._configs = {} - self._test_cases = [] - self._implicit_test = None - - if design_unit.is_entity: - design_unit.set_add_architecture_callback(self._add_architecture_callback) - if design_unit.architecture_names: - self._add_architecture_callback() - else: - self.scan_tests_from_file(design_unit.file_name) - - def _add_architecture_callback(self): - """ - Called when architectures have been added - """ - self._check_architectures(self.design_unit) - file_name = list(self.design_unit.architecture_names.values())[0] - self.scan_tests_from_file(file_name) - - @property - def name(self): - return self.design_unit.name - - @property - def library_name(self): - return self.design_unit.library_name - - @property - def tests(self): - return self._test_cases - - def get_default_config(self): - """ - Get the default configuration of this test bench - """ - if self._individual_tests: - raise RuntimeError("Test bench %s.%s has individually configured tests" - % (self.library_name, self.name)) - return self._configs[DEFAULT_NAME] - - @staticmethod - def _check_architectures(design_unit): - """ - Check that an entity which has been classified as a VUnit test bench - has exactly one architecture. Raise RuntimeError otherwise. - """ - if design_unit.is_entity: - if not design_unit.architecture_names: - raise RuntimeError("Test bench '%s' has no architecture." - % design_unit.name) - - if len(design_unit.architecture_names) > 1: - raise RuntimeError("Test bench not allowed to have multiple architectures. " - "Entity %s has %s" - % (design_unit.name, - ", ".join("%s:%s" % (name, basename(fname)) - for name, fname in sorted(design_unit.architecture_names.items())))) - - def create_tests(self, simulator_if, elaborate_only, test_list=None): - """ - Create all test cases from this test bench - """ - - self._check_architectures(self.design_unit) - - if test_list is None: - test_list = TestList() - - if self._individual_tests: - for test_case in self._test_cases: - test_case.create_tests(simulator_if, elaborate_only, test_list) - elif self._implicit_test: - for config in self._get_configurations_to_run(): - test_list.add_test( - IndependentSimTestCase( - test=self._implicit_test, - config=config, - simulator_if=simulator_if, - elaborate_only=elaborate_only)) - else: - for config in self._get_configurations_to_run(): - test_list.add_suite( - SameSimTestSuite( - tests=[test.test for test in self._test_cases], - config=config, - simulator_if=simulator_if, - elaborate_only=elaborate_only)) - return test_list - - @property - def test_case_names(self): - return [test.name for test in self._test_cases] - - def get_test_case(self, name): - """ - Return the test case with name or raise KeyError - """ - for test_case in self._test_cases: - if test_case.name == name: - return test_case - raise KeyError(name) - - def get_configuration_dicts(self): - """ - Get all configurations within the test bench - - If running all tests in the same simulation there are no individual test configurations - """ - if self._individual_tests: - configs = [] - for test_case in self._test_cases: - configs += test_case.get_configuration_dicts() - return configs - - return [self._configs] - - def _get_configurations_to_run(self): - """ - Get all simulation runs for this test bench - """ - configs = self._configs.copy() - if len(configs) > 1: - # Remove default configurations when there are more than one - del configs[DEFAULT_NAME] - return configs.values() - - def scan_tests_from_file(self, file_name): - """ - Scan file for test cases and attributes - """ - if not file_exists(file_name): - raise ValueError("File %r does not exist" % file_name) - - def parse(content): - """ - Parse attributes and test case names - """ - tests, attributes = _find_tests_and_attributes(content, file_name) - return tests, attributes - - tests, attributes = cached("test_bench.parse", - parse, - file_name, - encoding=HDL_FILE_ENCODING, - database=self._database, - newline='') - - for attr in attributes: - if _is_user_attribute(attr.name): - raise RuntimeError("File global attributes are not yet supported: %s in %s line %i" - % (attr.name, file_name, attr.location.lineno)) - - for test in tests: - for attr in test.attributes: - if attr.name in _VALID_ATTRIBUTES: - raise RuntimeError("Attribute %s is global and cannot be associated with test %s: %s line %i" - % (attr.name, test.name, file_name, attr.location.lineno)) - - attribute_names = [attr.name for attr in attributes] - - default_config = Configuration(DEFAULT_NAME, self.design_unit) - - if "fail_on_warning" in attribute_names: - default_config.set_sim_option("vhdl_assert_stop_level", "warning") - - self._configs = OrderedDict({default_config.name: default_config}) - - explicit_tests = [test for test in tests if test.is_explicit] - if explicit_tests: - # All tests shall be explicit when there are at least one explicit test - assert len(tests) == len(explicit_tests) - self._implicit_test = None - else: - # There can only be one implicit test - assert len(tests) == 1 - self._implicit_test = tests[0] - - self._individual_tests = "run_all_in_same_sim" not in attribute_names and len(explicit_tests) > 0 - self._test_cases = [TestConfigurationVisitor(test, - self.design_unit, - self._individual_tests, - default_config.copy()) - for test in explicit_tests] - - -class FileLocation(object): - """ - The location of a token within a file - - - file name - - offset and length in characters in the file - """ - - @staticmethod - def from_match(file_name, match, key, line_offsets): - """ - Create FileLocation from regex match key - """ - offset = match.start(key) - length = match.end(key) - match.start(key) - return FileLocation.from_line_offsets(file_name, offset, length, line_offsets) - - @staticmethod - def from_line_offsets(file_name, offset, length, line_offsets): - """ - Create FileLocation with lineno computed from line offsets - """ - return FileLocation(file_name, offset, length, _lookup_lineno(offset, line_offsets)) - - def __init__(self, file_name, offset, length, lineno): - self.file_name = file_name - self.offset = offset - self.length = length - self.lineno = lineno - - def _to_tuple(self): - return (self.file_name, - self.offset, - self.length, - self.lineno) - - def __eq__(self, other): - return self._to_tuple() == other._to_tuple() # pylint: disable=protected-access - - def __repr__(self): - return "FileLocation" + repr(self._to_tuple()) - - def __hash__(self): - return hash(self._to_tuple()) - - -class Test(object): - """ - Holds information about a test in the source code - - - name of test - - location in file - - if it was an explicit or implicit test [1] - - [1]: Explicit tests are those where the user has written run("test name"). - Implicit tests are those when there are no tests in the test bench, just the test suite - """ - - def __init__(self, name, location): - self._name = name - self._location = location - self._attributes = [] - - @property - def name(self): - return self._name - - @property - def location(self): - return self._location - - @property - def is_explicit(self): - return self._name is not None - - def add_attribute(self, attr): - self._attributes.append(attr) - - @property - def attributes(self): - return list(self._attributes) - - @property - def attribute_names(self): - return set((attr.name for attr in self._attributes)) - - def _to_tuple(self): - return (self._name, - self._location, - tuple(self._attributes)) - - def __repr__(self): - return "Test" + repr(self._to_tuple()) - - def __eq__(self, other): - return self._to_tuple() == other._to_tuple() # pylint: disable=protected-access - - def __hash__(self): - return hash(self._to_tuple()) - - -class TestConfigurationVisitor(ConfigurationVisitor): - """ - A means to creates configurations for single test - """ - def __init__(self, test, design_unit, enable_configuration, default_config): - ConfigurationVisitor.__init__(self) - self._test = test - assert test.is_explicit - self.design_unit = design_unit - self._enable_configuration = enable_configuration - self._configs = OrderedDict({default_config.name: default_config}) - - @property - def name(self): - return self._test.name - - @property - def test(self): - return self._test - - def get_default_config(self): - """ - Get the default configuration of this test case - """ - self._check_enabled() - return self._configs[DEFAULT_NAME] - - def _check_enabled(self): - if not self._enable_configuration: - raise RuntimeError("Individual test configuration is not possible with run_all_in_same_sim") - - def get_configuration_dicts(self): - """ - Get all configurations of this test - """ - return [self._configs] - - def _get_configurations_to_run(self): - """ - Get all simulation runs for this test bench - """ - configs = self._configs.copy() - if len(configs) > 1: - # Remove default configurations when there are more than one - del configs[DEFAULT_NAME] - return configs.values() - - def create_tests(self, simulator_if, elaborate_only, test_list=None): - """ - Create all tests from this test case which may be several depending on the number of configurations - """ - for config in self._get_configurations_to_run(): - test_list.add_test( - IndependentSimTestCase( - test=self._test, - config=config, - simulator_if=simulator_if, - elaborate_only=elaborate_only)) - - -_RE_VHDL_TEST_CASE = re.compile(r'(\s|\()+run\s*\(\s*"(?P.*?)"\s*\)', re.IGNORECASE) -_RE_VERILOG_TEST_CASE = re.compile(r'`TEST_CASE\s*\(\s*"(?P.*?)"\s*\)') -_RE_VHDL_TEST_SUITE = re.compile(r'test_runner_setup\s*\(', re.IGNORECASE) -_RE_VERILOG_TEST_SUITE = re.compile(r'`TEST_SUITE\b') - - -def _get_line_offsets(code): - """ - Returns a list with one entry per line returning the offset in the - code where it starts - """ - - offset = 0 - offsets = [] - for line in code.splitlines(): - offsets.append(offset) - offset += len(line) + 1 - - return offsets - - -def _lookup_lineno(offset, offsets): - """ - Convert offset into line number - """ - return bisect.bisect(offsets, offset) - - -def _check_duplicate_tests(tests): - """ - Check for duplicate tests and raise RuntimeError - """ - known_tests = {} - found_duplicates = False - for test in tests: - if test.name in known_tests: - known_test = known_tests[test.name] - LOGGER.error('Duplicate test "%s" in %s line %i previously defined on line %i', - test.name, - test.location.file_name, test.location.lineno, - known_test.location.lineno) - found_duplicates = True - else: - known_tests[test.name] = test - - if found_duplicates: - raise RuntimeError('Duplicate tests where found') - - -def _find_tests(code, file_name, line_offsets=None): - """ - Finds all tests within a file including implicit tests where there - is only a test suite - - returns a list to Test objects - """ - - if line_offsets is None: - line_offsets = _get_line_offsets(code) - - is_verilog = file_type_of(file_name) in VERILOG_FILE_TYPES - - if is_verilog: - code = _remove_verilog_comments(code) - regexp = _RE_VERILOG_TEST_CASE - suite_regexp = _RE_VERILOG_TEST_SUITE - else: - code = remove_vhdl_comments(code) - regexp = _RE_VHDL_TEST_CASE - suite_regexp = _RE_VHDL_TEST_SUITE - - tests = [Test(name=match.group("name"), - location=FileLocation.from_match(file_name, match, "name", line_offsets)) - for match in regexp.finditer(code)] - - _check_duplicate_tests(tests) - - if not tests: - # Implicit test, use the test suite start as lineno - match = suite_regexp.search(code) - - if match: - location = FileLocation.from_match(file_name, match, 0, line_offsets) - else: - LOGGER.warning("Found no tests or test suite within %s", file_name) - location = FileLocation.from_line_offsets(file_name, 0, 0, line_offsets) - - tests = [Test(None, - location=location)] - - return tests - - -def _check_duplicates(attrs, file_name, test_name=None): - """ - Check for duplicate attributes, if test_name is None it is a file global attribute - """ - previous = {} - for attr in attrs: - if attr.name in previous: - - if test_name is None: - loc = "%s line %i" % (file_name, attr.location.lineno) - else: - loc = "test %s in %s line %i" % (test_name, file_name, attr.location.lineno) - - raise RuntimeError("Duplicate attribute %s of %s, previously defined on line %i" - % (attr.name, loc, previous[attr.name].location.lineno)) - - previous[attr.name] = attr - - -def _find_tests_and_attributes(content, file_name): - """ - Parse attributes and test case names - - Attributes are associated with a single test case by being located - after the test case definition. - - Attributes are associated with file by being located above all - test cases. - - NOTE: The legacy vunit_pragma is always associated with the entire file due to legacy reasons - - Returns the tests and global attributes. The tests have been annotated with attributes. - """ - line_offsets = _get_line_offsets(content) - attributes = _find_attributes(content, file_name, line_offsets) - tests = _find_tests(content, file_name, line_offsets) - - tests = sorted(tests, key=lambda test: test.location.offset) - offsets = [test.location.offset for test in tests] - - def associate(attr): - """ - Associate attribute with test case - """ - idx = bisect.bisect_right(offsets, attr.location.offset) - if idx == 0: - return None - return tests[idx - 1] - - global_attributes = [] - for attr in attributes: - if isinstance(attr, LegacyAttribute): - global_attributes.append(attr) - else: - test = associate(attr) - - if test: - test.add_attribute(attr) - else: - global_attributes.append(attr) - - for test in tests: - _check_duplicates(test.attributes, file_name, test_name=test.name) - - _check_duplicates(global_attributes, file_name) - - return tests, global_attributes - - -_RE_ATTR_NAME = r"[a-zA-Z0-9_\-]+" -_RE_ATTRIBUTE = re.compile(r'vunit:\s*(?P\.?' + _RE_ATTR_NAME + r')', - re.IGNORECASE) -_RE_PRAGMA_LEGACY = re.compile(r'vunit_pragma\s+(?P' + _RE_ATTR_NAME + ')', re.IGNORECASE) -_VALID_ATTRIBUTES = ["run_all_in_same_sim", "fail_on_warning"] - - -def _is_user_attribute(name): - return name.startswith(".") - - -def _find_attributes(code, file_name, line_offsets=None): - """ - Return a list of all vunit attributes parsed from the code - - Attributes are either built-in: - // -- vunit: run_all_in_same_sim - - or user defined: - // -- vunit: .foo - - @TODO only look inside comments - """ - - if line_offsets is None: - line_offsets = _get_line_offsets(code) - - attributes = [] - - def _find(attr_class, regex): - """ - Helper method to create attributes from regex - """ - for match in regex.finditer(code): - groups = match.groupdict(default=None) - name = groups['name'] - location = FileLocation.from_match(file_name, match, "name", line_offsets) - - if not _is_user_attribute(name) and name not in _VALID_ATTRIBUTES: - raise RuntimeError( - "Invalid attribute '%s' in %s line %i" % ( - name, - file_name, - location.lineno)) - - attributes.append(attr_class(name, - value=None, - location=location)) - - _find(LegacyAttribute, _RE_PRAGMA_LEGACY) - _find(Attribute, _RE_ATTRIBUTE) - - return attributes - - -# Add value field to be forwards compatible with having attribute values -Attribute = collections.namedtuple("Attribute", ["name", "value", "location"]) -LegacyAttribute = collections.namedtuple("LegacyAttribute", ["name", "value", "location"]) - - -VERILOG_REMOVE_COMMENT_RE = re.compile(r'(//[^\n]*)|(/\*.*?\*/)', - re.DOTALL) - - -def _comment_repl(match): - """ - Replace comment with equal amount of whitespace to make - lexical position unaffected - """ - text = match.group(0) - return "".join(" " if c != "\n" else "\n" - for c in text) - - -def _remove_verilog_comments(code): - """ - Remove all verilog comments - """ - return VERILOG_REMOVE_COMMENT_RE.sub(_comment_repl, code) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Contains classes to represent a test bench and test cases +""" + +import logging +from os.path import basename +import re +import bisect +import collections +from collections import OrderedDict +from vunit.ostools import file_exists +from vunit.cached import cached +from vunit.test_list import TestList +from vunit.vhdl_parser import remove_comments as remove_vhdl_comments +from vunit.test_suites import IndependentSimTestCase, SameSimTestSuite +from vunit.parsing.encodings import HDL_FILE_ENCODING +from vunit.project import file_type_of, VERILOG_FILE_TYPES +from vunit.configuration import Configuration, ConfigurationVisitor, DEFAULT_NAME + + +LOGGER = logging.getLogger(__name__) + + +class TestBench(ConfigurationVisitor): + """ + A VUnit test bench top level + """ + + def __init__(self, design_unit, database=None): + ConfigurationVisitor.__init__(self) + self.design_unit = design_unit + self._database = database + + self._individual_tests = False + self._configs = {} + self._test_cases = [] + self._implicit_test = None + + if design_unit.is_entity: + design_unit.set_add_architecture_callback(self._add_architecture_callback) + if design_unit.architecture_names: + self._add_architecture_callback() + else: + self.scan_tests_from_file(design_unit.file_name) + + def _add_architecture_callback(self): + """ + Called when architectures have been added + """ + self._check_architectures(self.design_unit) + file_name = list(self.design_unit.architecture_names.values())[0] + self.scan_tests_from_file(file_name) + + @property + def name(self): + return self.design_unit.name + + @property + def library_name(self): + return self.design_unit.library_name + + @property + def tests(self): + return self._test_cases + + def get_default_config(self): + """ + Get the default configuration of this test bench + """ + if self._individual_tests: + raise RuntimeError("Test bench %s.%s has individually configured tests" + % (self.library_name, self.name)) + return self._configs[DEFAULT_NAME] + + @staticmethod + def _check_architectures(design_unit): + """ + Check that an entity which has been classified as a VUnit test bench + has exactly one architecture. Raise RuntimeError otherwise. + """ + if design_unit.is_entity: + if not design_unit.architecture_names: + raise RuntimeError("Test bench '%s' has no architecture." + % design_unit.name) + + if len(design_unit.architecture_names) > 1: + raise RuntimeError("Test bench not allowed to have multiple architectures. " + "Entity %s has %s" + % (design_unit.name, + ", ".join("%s:%s" % (name, basename(fname)) + for name, fname in sorted(design_unit.architecture_names.items())))) + + def create_tests(self, simulator_if, elaborate_only, test_list=None): + """ + Create all test cases from this test bench + """ + + self._check_architectures(self.design_unit) + + if test_list is None: + test_list = TestList() + + if self._individual_tests: + for test_case in self._test_cases: + test_case.create_tests(simulator_if, elaborate_only, test_list) + elif self._implicit_test: + for config in self._get_configurations_to_run(): + test_list.add_test( + IndependentSimTestCase( + test=self._implicit_test, + config=config, + simulator_if=simulator_if, + elaborate_only=elaborate_only)) + else: + for config in self._get_configurations_to_run(): + test_list.add_suite( + SameSimTestSuite( + tests=[test.test for test in self._test_cases], + config=config, + simulator_if=simulator_if, + elaborate_only=elaborate_only)) + return test_list + + @property + def test_case_names(self): + return [test.name for test in self._test_cases] + + def get_test_case(self, name): + """ + Return the test case with name or raise KeyError + """ + for test_case in self._test_cases: + if test_case.name == name: + return test_case + raise KeyError(name) + + def get_configuration_dicts(self): + """ + Get all configurations within the test bench + + If running all tests in the same simulation there are no individual test configurations + """ + if self._individual_tests: + configs = [] + for test_case in self._test_cases: + configs += test_case.get_configuration_dicts() + return configs + + return [self._configs] + + def _get_configurations_to_run(self): + """ + Get all simulation runs for this test bench + """ + configs = self._configs.copy() + if len(configs) > 1: + # Remove default configurations when there are more than one + del configs[DEFAULT_NAME] + return configs.values() + + def scan_tests_from_file(self, file_name): + """ + Scan file for test cases and attributes + """ + if not file_exists(file_name): + raise ValueError("File %r does not exist" % file_name) + + def parse(content): + """ + Parse attributes and test case names + """ + tests, attributes = _find_tests_and_attributes(content, file_name) + return tests, attributes + + tests, attributes = cached("test_bench.parse", + parse, + file_name, + encoding=HDL_FILE_ENCODING, + database=self._database, + newline='') + + for attr in attributes: + if _is_user_attribute(attr.name): + raise RuntimeError("File global attributes are not yet supported: %s in %s line %i" + % (attr.name, file_name, attr.location.lineno)) + + for test in tests: + for attr in test.attributes: + if attr.name in _VALID_ATTRIBUTES: + raise RuntimeError("Attribute %s is global and cannot be associated with test %s: %s line %i" + % (attr.name, test.name, file_name, attr.location.lineno)) + + attribute_names = [attr.name for attr in attributes] + + default_config = Configuration(DEFAULT_NAME, self.design_unit) + + if "fail_on_warning" in attribute_names: + default_config.set_sim_option("vhdl_assert_stop_level", "warning") + + self._configs = OrderedDict({default_config.name: default_config}) + + explicit_tests = [test for test in tests if test.is_explicit] + if explicit_tests: + # All tests shall be explicit when there are at least one explicit test + assert len(tests) == len(explicit_tests) + self._implicit_test = None + else: + # There can only be one implicit test + assert len(tests) == 1 + self._implicit_test = tests[0] + + self._individual_tests = "run_all_in_same_sim" not in attribute_names and len(explicit_tests) > 0 + self._test_cases = [TestConfigurationVisitor(test, + self.design_unit, + self._individual_tests, + default_config.copy()) + for test in explicit_tests] + + +class FileLocation(object): + """ + The location of a token within a file + + - file name + - offset and length in characters in the file + """ + + @staticmethod + def from_match(file_name, match, key, line_offsets): + """ + Create FileLocation from regex match key + """ + offset = match.start(key) + length = match.end(key) - match.start(key) + return FileLocation.from_line_offsets(file_name, offset, length, line_offsets) + + @staticmethod + def from_line_offsets(file_name, offset, length, line_offsets): + """ + Create FileLocation with lineno computed from line offsets + """ + return FileLocation(file_name, offset, length, _lookup_lineno(offset, line_offsets)) + + def __init__(self, file_name, offset, length, lineno): + self.file_name = file_name + self.offset = offset + self.length = length + self.lineno = lineno + + def _to_tuple(self): + return (self.file_name, + self.offset, + self.length, + self.lineno) + + def __eq__(self, other): + return self._to_tuple() == other._to_tuple() # pylint: disable=protected-access + + def __repr__(self): + return "FileLocation" + repr(self._to_tuple()) + + def __hash__(self): + return hash(self._to_tuple()) + + +class Test(object): + """ + Holds information about a test in the source code + + - name of test + - location in file + - if it was an explicit or implicit test [1] + + [1]: Explicit tests are those where the user has written run("test name"). + Implicit tests are those when there are no tests in the test bench, just the test suite + """ + + def __init__(self, name, location): + self._name = name + self._location = location + self._attributes = [] + + @property + def name(self): + return self._name + + @property + def location(self): + return self._location + + @property + def is_explicit(self): + return self._name is not None + + def add_attribute(self, attr): + self._attributes.append(attr) + + @property + def attributes(self): + return list(self._attributes) + + @property + def attribute_names(self): + return set((attr.name for attr in self._attributes)) + + def _to_tuple(self): + return (self._name, + self._location, + tuple(self._attributes)) + + def __repr__(self): + return "Test" + repr(self._to_tuple()) + + def __eq__(self, other): + return self._to_tuple() == other._to_tuple() # pylint: disable=protected-access + + def __hash__(self): + return hash(self._to_tuple()) + + +class TestConfigurationVisitor(ConfigurationVisitor): + """ + A means to creates configurations for single test + """ + def __init__(self, test, design_unit, enable_configuration, default_config): + ConfigurationVisitor.__init__(self) + self._test = test + assert test.is_explicit + self.design_unit = design_unit + self._enable_configuration = enable_configuration + self._configs = OrderedDict({default_config.name: default_config}) + + @property + def name(self): + return self._test.name + + @property + def test(self): + return self._test + + def get_default_config(self): + """ + Get the default configuration of this test case + """ + self._check_enabled() + return self._configs[DEFAULT_NAME] + + def _check_enabled(self): + if not self._enable_configuration: + raise RuntimeError("Individual test configuration is not possible with run_all_in_same_sim") + + def get_configuration_dicts(self): + """ + Get all configurations of this test + """ + return [self._configs] + + def _get_configurations_to_run(self): + """ + Get all simulation runs for this test bench + """ + configs = self._configs.copy() + if len(configs) > 1: + # Remove default configurations when there are more than one + del configs[DEFAULT_NAME] + return configs.values() + + def create_tests(self, simulator_if, elaborate_only, test_list=None): + """ + Create all tests from this test case which may be several depending on the number of configurations + """ + for config in self._get_configurations_to_run(): + test_list.add_test( + IndependentSimTestCase( + test=self._test, + config=config, + simulator_if=simulator_if, + elaborate_only=elaborate_only)) + + +_RE_VHDL_TEST_CASE = re.compile(r'(\s|\()+run\s*\(\s*"(?P.*?)"\s*\)', re.IGNORECASE) +_RE_VERILOG_TEST_CASE = re.compile(r'`TEST_CASE\s*\(\s*"(?P.*?)"\s*\)') +_RE_VHDL_TEST_SUITE = re.compile(r'test_runner_setup\s*\(', re.IGNORECASE) +_RE_VERILOG_TEST_SUITE = re.compile(r'`TEST_SUITE\b') + + +def _get_line_offsets(code): + """ + Returns a list with one entry per line returning the offset in the + code where it starts + """ + + offset = 0 + offsets = [] + for line in code.splitlines(): + offsets.append(offset) + offset += len(line) + 1 + + return offsets + + +def _lookup_lineno(offset, offsets): + """ + Convert offset into line number + """ + return bisect.bisect(offsets, offset) + + +def _check_duplicate_tests(tests): + """ + Check for duplicate tests and raise RuntimeError + """ + known_tests = {} + found_duplicates = False + for test in tests: + if test.name in known_tests: + known_test = known_tests[test.name] + LOGGER.error('Duplicate test "%s" in %s line %i previously defined on line %i', + test.name, + test.location.file_name, test.location.lineno, + known_test.location.lineno) + found_duplicates = True + else: + known_tests[test.name] = test + + if found_duplicates: + raise RuntimeError('Duplicate tests where found') + + +def _find_tests(code, file_name, line_offsets=None): + """ + Finds all tests within a file including implicit tests where there + is only a test suite + + returns a list to Test objects + """ + + if line_offsets is None: + line_offsets = _get_line_offsets(code) + + is_verilog = file_type_of(file_name) in VERILOG_FILE_TYPES + + if is_verilog: + code = _remove_verilog_comments(code) + regexp = _RE_VERILOG_TEST_CASE + suite_regexp = _RE_VERILOG_TEST_SUITE + else: + code = remove_vhdl_comments(code) + regexp = _RE_VHDL_TEST_CASE + suite_regexp = _RE_VHDL_TEST_SUITE + + tests = [Test(name=match.group("name"), + location=FileLocation.from_match(file_name, match, "name", line_offsets)) + for match in regexp.finditer(code)] + + _check_duplicate_tests(tests) + + if not tests: + # Implicit test, use the test suite start as lineno + match = suite_regexp.search(code) + + if match: + location = FileLocation.from_match(file_name, match, 0, line_offsets) + else: + LOGGER.warning("Found no tests or test suite within %s", file_name) + location = FileLocation.from_line_offsets(file_name, 0, 0, line_offsets) + + tests = [Test(None, + location=location)] + + return tests + + +def _check_duplicates(attrs, file_name, test_name=None): + """ + Check for duplicate attributes, if test_name is None it is a file global attribute + """ + previous = {} + for attr in attrs: + if attr.name in previous: + + if test_name is None: + loc = "%s line %i" % (file_name, attr.location.lineno) + else: + loc = "test %s in %s line %i" % (test_name, file_name, attr.location.lineno) + + raise RuntimeError("Duplicate attribute %s of %s, previously defined on line %i" + % (attr.name, loc, previous[attr.name].location.lineno)) + + previous[attr.name] = attr + + +def _find_tests_and_attributes(content, file_name): + """ + Parse attributes and test case names + + Attributes are associated with a single test case by being located + after the test case definition. + + Attributes are associated with file by being located above all + test cases. + + NOTE: The legacy vunit_pragma is always associated with the entire file due to legacy reasons + + Returns the tests and global attributes. The tests have been annotated with attributes. + """ + line_offsets = _get_line_offsets(content) + attributes = _find_attributes(content, file_name, line_offsets) + tests = _find_tests(content, file_name, line_offsets) + + tests = sorted(tests, key=lambda test: test.location.offset) + offsets = [test.location.offset for test in tests] + + def associate(attr): + """ + Associate attribute with test case + """ + idx = bisect.bisect_right(offsets, attr.location.offset) + if idx == 0: + return None + return tests[idx - 1] + + global_attributes = [] + for attr in attributes: + if isinstance(attr, LegacyAttribute): + global_attributes.append(attr) + else: + test = associate(attr) + + if test: + test.add_attribute(attr) + else: + global_attributes.append(attr) + + for test in tests: + _check_duplicates(test.attributes, file_name, test_name=test.name) + + _check_duplicates(global_attributes, file_name) + + return tests, global_attributes + + +_RE_ATTR_NAME = r"[a-zA-Z0-9_\-]+" +_RE_ATTRIBUTE = re.compile(r'vunit:\s*(?P\.?' + _RE_ATTR_NAME + r')', + re.IGNORECASE) +_RE_PRAGMA_LEGACY = re.compile(r'vunit_pragma\s+(?P' + _RE_ATTR_NAME + ')', re.IGNORECASE) +_VALID_ATTRIBUTES = ["run_all_in_same_sim", "fail_on_warning"] + + +def _is_user_attribute(name): + return name.startswith(".") + + +def _find_attributes(code, file_name, line_offsets=None): + """ + Return a list of all vunit attributes parsed from the code + + Attributes are either built-in: + // -- vunit: run_all_in_same_sim + + or user defined: + // -- vunit: .foo + + @TODO only look inside comments + """ + + if line_offsets is None: + line_offsets = _get_line_offsets(code) + + attributes = [] + + def _find(attr_class, regex): + """ + Helper method to create attributes from regex + """ + for match in regex.finditer(code): + groups = match.groupdict(default=None) + name = groups['name'] + location = FileLocation.from_match(file_name, match, "name", line_offsets) + + if not _is_user_attribute(name) and name not in _VALID_ATTRIBUTES: + raise RuntimeError( + "Invalid attribute '%s' in %s line %i" % ( + name, + file_name, + location.lineno)) + + attributes.append(attr_class(name, + value=None, + location=location)) + + _find(LegacyAttribute, _RE_PRAGMA_LEGACY) + _find(Attribute, _RE_ATTRIBUTE) + + return attributes + + +# Add value field to be forwards compatible with having attribute values +Attribute = collections.namedtuple("Attribute", ["name", "value", "location"]) +LegacyAttribute = collections.namedtuple("LegacyAttribute", ["name", "value", "location"]) + + +VERILOG_REMOVE_COMMENT_RE = re.compile(r'(//[^\n]*)|(/\*.*?\*/)', + re.DOTALL) + + +def _comment_repl(match): + """ + Replace comment with equal amount of whitespace to make + lexical position unaffected + """ + text = match.group(0) + return "".join(" " if c != "\n" else "\n" + for c in text) + + +def _remove_verilog_comments(code): + """ + Remove all verilog comments + """ + return VERILOG_REMOVE_COMMENT_RE.sub(_comment_repl, code) diff --git a/vunit/test_bench_list.py b/vunit/test_bench_list.py index 189a5ee63..d5686cff6 100644 --- a/vunit/test_bench_list.py +++ b/vunit/test_bench_list.py @@ -1,121 +1,121 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Contains classes to manage the creation of test benches and runnable test cases thereof -""" -import re -import logging -from collections import OrderedDict -from vunit.test_list import TestList -from vunit.test_bench import TestBench - - -LOGGER = logging.getLogger(__name__) - - -class TestBenchList(object): - """ - A list of test benchs - """ - - def __init__(self, database=None): - self._libraries = OrderedDict() - self._database = database - - def add_from_source_file(self, source_file): - """ - Scan test benches from the source file and add to test bench list - """ - for design_unit in source_file.design_units: - if design_unit.is_entity or design_unit.is_module: - if tb_filter is None or tb_filter(design_unit): - if design_unit.is_module or design_unit.is_entity: - self._add_test_bench(TestBench(design_unit, self._database)) - - def _add_test_bench(self, test_bench): - """ - Add the test bench - """ - if test_bench.library_name not in self._libraries: - self._libraries[test_bench.library_name] = OrderedDict() - self._libraries[test_bench.library_name][test_bench.name] = test_bench - - def get_test_bench(self, library_name, name): - return self._libraries[library_name][name] - - def get_test_benches_in_library(self, library_name): - return list(self._libraries.get(library_name, {}).values()) - - def get_test_benches(self): - """ - Get all test benches - """ - result = [] - for test_benches in self._libraries.values(): - for test_bench in test_benches.values(): - result.append(test_bench) - return result - - def create_tests(self, simulator_if, elaborate_only): - """ - Create all test cases from the test benches - """ - test_list = TestList() - for test_bench in self.get_test_benches(): - test_bench.create_tests(simulator_if, elaborate_only, test_list) - return test_list - - def warn_when_empty(self): - """ - Log a warning when there are no test benches - """ - if not self.get_test_benches(): - LOGGER.warning("Found no test benches using current filter rule:\n%s", - tb_filter.__doc__) - - -TB_PATTERN = "^(tb_.*)|(.*_tb)$" -TB_RE = re.compile(TB_PATTERN, - re.IGNORECASE) - - -def tb_filter(design_unit): - """ - Filters entities and modules that have a runner_cfg generic/parameter - - Gives warning when design_unit matches tb_* or *_tb without having a runner_cfg - Gives warning when a design_unit has a runner_cfg but the name does not match tb_* or *_tb - """ - # Above docstring can show up in ui.py warnings - has_runner_cfg = "runner_cfg" in design_unit.generic_names - has_tb_name = TB_RE.match(design_unit.name) is not None - - design_unit_type = "Entity" if design_unit.is_entity else "Module" - generic_type = "generic" if design_unit.is_entity else "parameter" - - if (not has_runner_cfg) and has_tb_name: - LOGGER.warning( - "%s %s matches testbench name regex %s but has no %s runner_cfg and will therefore not be run.\n" - "in file %s", - design_unit_type, - design_unit.name, - TB_PATTERN, - generic_type, - design_unit.file_name) - - elif has_runner_cfg and not has_tb_name: - LOGGER.warning( - "%s %s has runner_cfg %s but the file name and the %s name does not match regex %s\n" - "in file %s", - design_unit_type, - design_unit.name, - generic_type, - design_unit_type.lower(), - TB_PATTERN, - design_unit.file_name) - - return has_runner_cfg +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Contains classes to manage the creation of test benches and runnable test cases thereof +""" +import re +import logging +from collections import OrderedDict +from vunit.test_list import TestList +from vunit.test_bench import TestBench + + +LOGGER = logging.getLogger(__name__) + + +class TestBenchList(object): + """ + A list of test benchs + """ + + def __init__(self, database=None): + self._libraries = OrderedDict() + self._database = database + + def add_from_source_file(self, source_file): + """ + Scan test benches from the source file and add to test bench list + """ + for design_unit in source_file.design_units: + if design_unit.is_entity or design_unit.is_module: + if tb_filter is None or tb_filter(design_unit): + if design_unit.is_module or design_unit.is_entity: + self._add_test_bench(TestBench(design_unit, self._database)) + + def _add_test_bench(self, test_bench): + """ + Add the test bench + """ + if test_bench.library_name not in self._libraries: + self._libraries[test_bench.library_name] = OrderedDict() + self._libraries[test_bench.library_name][test_bench.name] = test_bench + + def get_test_bench(self, library_name, name): + return self._libraries[library_name][name] + + def get_test_benches_in_library(self, library_name): + return list(self._libraries.get(library_name, {}).values()) + + def get_test_benches(self): + """ + Get all test benches + """ + result = [] + for test_benches in self._libraries.values(): + for test_bench in test_benches.values(): + result.append(test_bench) + return result + + def create_tests(self, simulator_if, elaborate_only): + """ + Create all test cases from the test benches + """ + test_list = TestList() + for test_bench in self.get_test_benches(): + test_bench.create_tests(simulator_if, elaborate_only, test_list) + return test_list + + def warn_when_empty(self): + """ + Log a warning when there are no test benches + """ + if not self.get_test_benches(): + LOGGER.warning("Found no test benches using current filter rule:\n%s", + tb_filter.__doc__) + + +TB_PATTERN = "^(tb_.*)|(.*_tb)$" +TB_RE = re.compile(TB_PATTERN, + re.IGNORECASE) + + +def tb_filter(design_unit): + """ + Filters entities and modules that have a runner_cfg generic/parameter + + Gives warning when design_unit matches tb_* or *_tb without having a runner_cfg + Gives warning when a design_unit has a runner_cfg but the name does not match tb_* or *_tb + """ + # Above docstring can show up in ui.py warnings + has_runner_cfg = "runner_cfg" in design_unit.generic_names + has_tb_name = TB_RE.match(design_unit.name) is not None + + design_unit_type = "Entity" if design_unit.is_entity else "Module" + generic_type = "generic" if design_unit.is_entity else "parameter" + + if (not has_runner_cfg) and has_tb_name: + LOGGER.warning( + "%s %s matches testbench name regex %s but has no %s runner_cfg and will therefore not be run.\n" + "in file %s", + design_unit_type, + design_unit.name, + TB_PATTERN, + generic_type, + design_unit.file_name) + + elif has_runner_cfg and not has_tb_name: + LOGGER.warning( + "%s %s has runner_cfg %s but the file name and the %s name does not match regex %s\n" + "in file %s", + design_unit_type, + design_unit.name, + generic_type, + design_unit_type.lower(), + TB_PATTERN, + design_unit.file_name) + + return has_runner_cfg diff --git a/vunit/test_list.py b/vunit/test_list.py index 0cc8df9bf..aa196121f 100644 --- a/vunit/test_list.py +++ b/vunit/test_list.py @@ -1,102 +1,102 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Functionality to handle lists of test suites and filtering of them -""" - -from vunit.test_report import (PASSED, FAILED) - - -class TestList(object): - """ - A list of test suites - """ - def __init__(self): - self._test_suites = [] - - def add_suite(self, test_suite): - self._test_suites.append(test_suite) - - def add_test(self, test_case): - """ - Add a single test that is automatically wrapped into a test suite - """ - test_suite = TestSuiteWrapper(test_case) - self._test_suites.append(test_suite) - - def keep_matches(self, test_filter): - """ - Keep only testcases matching any pattern - """ - self._test_suites = [test for test in self._test_suites - if test.keep_matches(test_filter)] - - @property - def num_tests(self): - """ - Return the number of tests within - """ - num_tests = 0 - for test_suite in self: - num_tests += len(test_suite.test_names) - return num_tests - - @property - def test_names(self): - """ - Return the names of all tests - """ - names = [] - for test_suite in self: - names += test_suite.test_names - return names - - def __iter__(self): - return iter(self._test_suites) - - def __len__(self): - return len(self._test_suites) - - def __getitem__(self, idx): - return self._test_suites[idx] - - -class TestSuiteWrapper(object): - """ - Wrapper which creates a test suite from a single test case - """ - def __init__(self, test_case): - self._test_case = test_case - - @property - def test_names(self): - return [self._test_case.name] - - @property - def name(self): - return self._test_case.name - - @property - def test_configuration(self): - return {self.name: self._test_case.test_configuration} - - @property - def test_information(self): - return {self.name: self._test_case.test_information} - - def keep_matches(self, test_filter): - attributes = self._test_case.attribute_names.copy() - attributes.update(set(self._test_case.test_configuration.attributes.keys())) - return test_filter(name=self._test_case.name, - attribute_names=attributes) - - def run(self, *args, **kwargs): - """ - Run the test suite and return the test results for all test cases - """ - test_ok = self._test_case.run(*args, **kwargs) - return {self._test_case.name: PASSED if test_ok else FAILED} +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Functionality to handle lists of test suites and filtering of them +""" + +from vunit.test_report import (PASSED, FAILED) + + +class TestList(object): + """ + A list of test suites + """ + def __init__(self): + self._test_suites = [] + + def add_suite(self, test_suite): + self._test_suites.append(test_suite) + + def add_test(self, test_case): + """ + Add a single test that is automatically wrapped into a test suite + """ + test_suite = TestSuiteWrapper(test_case) + self._test_suites.append(test_suite) + + def keep_matches(self, test_filter): + """ + Keep only testcases matching any pattern + """ + self._test_suites = [test for test in self._test_suites + if test.keep_matches(test_filter)] + + @property + def num_tests(self): + """ + Return the number of tests within + """ + num_tests = 0 + for test_suite in self: + num_tests += len(test_suite.test_names) + return num_tests + + @property + def test_names(self): + """ + Return the names of all tests + """ + names = [] + for test_suite in self: + names += test_suite.test_names + return names + + def __iter__(self): + return iter(self._test_suites) + + def __len__(self): + return len(self._test_suites) + + def __getitem__(self, idx): + return self._test_suites[idx] + + +class TestSuiteWrapper(object): + """ + Wrapper which creates a test suite from a single test case + """ + def __init__(self, test_case): + self._test_case = test_case + + @property + def test_names(self): + return [self._test_case.name] + + @property + def name(self): + return self._test_case.name + + @property + def test_configuration(self): + return {self.name: self._test_case.test_configuration} + + @property + def test_information(self): + return {self.name: self._test_case.test_information} + + def keep_matches(self, test_filter): + attributes = self._test_case.attribute_names.copy() + attributes.update(set(self._test_case.test_configuration.attributes.keys())) + return test_filter(name=self._test_case.name, + attribute_names=attributes) + + def run(self, *args, **kwargs): + """ + Run the test suite and return the test results for all test cases + """ + test_ok = self._test_case.run(*args, **kwargs) + return {self._test_case.name: PASSED if test_ok else FAILED} diff --git a/vunit/test_report.py b/vunit/test_report.py index 47ae3e6b4..2f79e8fef 100644 --- a/vunit/test_report.py +++ b/vunit/test_report.py @@ -1,318 +1,318 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Provide test reporting functionality -""" - - -from xml.etree import ElementTree -from sys import version_info -import os -import socket -import re -from vunit.color_printer import COLOR_PRINTER -from vunit.ostools import read_file - - -class TestReport(object): - """ - Collect reports from running testcases - """ - def __init__(self, printer=COLOR_PRINTER): - self._test_results = {} - self._test_names_in_order = [] - self._printer = printer - self._real_total_time = 0.0 - self._expected_num_tests = 0 - - def set_real_total_time(self, real_total_time): - """ - Set the real total execution time - """ - self._real_total_time = real_total_time - - def set_expected_num_tests(self, expected_num_tests): - """ - Set the number of tests that we expect to run - """ - self._expected_num_tests = expected_num_tests - - def num_tests(self): - """ - Return the number of tests in the report - """ - return len(self._test_results) - - def add_result(self, *args, **kwargs): - """ - Add a a test result - """ - result = TestResult(*args, **kwargs) - self._test_results[result.name] = result - self._test_names_in_order.append(result.name) - - def _last_test_result(self): - """ - Return the latest test result or fail - """ - return self._test_results[self._test_names_in_order[-1]] - - def _test_results_in_order(self): - """ - Return the test results in the order they were added - """ - for name in self._test_names_in_order: - yield self.result_of(name) - - def print_latest_status(self, total_tests): - """ - Print the latest status including the last test run and the - total number of passed, failed and skipped tests - """ - result = self._last_test_result() - passed, failed, skipped = self._split() - if result.passed: - self._printer.write("pass", fg='gi') - elif result.failed: - self._printer.write("fail", fg='ri') - elif result.skipped: - self._printer.write("skip", fg='rgi') - else: - assert False - - args = [] - args.append("P=%i" % len(passed)) - args.append("S=%i" % len(skipped)) - args.append("F=%i" % len(failed)) - args.append("T=%i" % total_tests) - - self._printer.write(" (%s) %s (%.1f seconds)\n" % - (" ".join(args), - result.name, - result.time)) - - def all_ok(self): - """ - Return true if all test passed - """ - return all(test_result.passed for test_result in self._test_results.values()) - - def has_test(self, test_name): - return test_name in self._test_results - - def result_of(self, test_name): - return self._test_results[test_name] - - def print_str(self): - """ - Print the report as a colored string - """ - - passed, failures, skipped = self._split() - all_tests = passed + skipped + failures - - if not all_tests: - self._printer.write("No tests were run!", fg="rgi") - self._printer.write("\n") - return - - prefix = "==== Summary " - max_len = max(len(test.name) for test in all_tests) - self._printer.write("%s%s\n" % (prefix, "=" * (max(max_len - len(prefix) + 25, 0)))) - for test_result in all_tests: - test_result.print_status(self._printer, padding=max_len) - - self._printer.write("%s\n" % ("=" * (max(max_len + 25, 0)))) - n_failed = len(failures) - n_skipped = len(skipped) - n_passed = len(passed) - total = len(all_tests) - - self._printer.write("pass", fg='gi') - self._printer.write(" %i of %i\n" % (n_passed, total)) - - if n_skipped > 0: - self._printer.write("skip", fg='rgi') - self._printer.write(" %i of %i\n" % (n_skipped, total)) - - if n_failed > 0: - self._printer.write("fail", fg='ri') - self._printer.write(" %i of %i\n" % (n_failed, total)) - self._printer.write("%s\n" % ("=" * (max(max_len + 25, 0)))) - - total_time = sum((result.time for result in self._test_results.values())) - self._printer.write("Total time was %.1f seconds\n" % total_time) - self._printer.write("Elapsed time was %.1f seconds\n" % self._real_total_time) - - self._printer.write("%s\n" % ("=" * (max(max_len + 25, 0)))) - - if n_failed > 0: - self._printer.write("Some failed!", fg='ri') - elif n_skipped > 0: - self._printer.write("Some skipped!", fg='rgxi') - else: - self._printer.write("All passed!", fg='gi') - self._printer.write("\n") - - assert len(all_tests) <= self._expected_num_tests - if len(all_tests) < self._expected_num_tests: - self._printer.write("WARNING: Test execution aborted after running %d out of %d tests" - % (len(all_tests), self._expected_num_tests), fg='rgi') - self._printer.write("\n") - - def _split(self): - """ - Split the test cases into passed and failures - """ - failures = [] - passed = [] - skipped = [] - for result in self._test_results_in_order(): - if result.passed: - passed.append(result) - elif result.failed: - failures.append(result) - elif result.skipped: - skipped.append(result) - - return passed, failures, skipped - - def to_junit_xml_str(self, xunit_xml_format='jenkins'): - """ - Convert test report to a junit xml string - """ - _, failures, skipped = self._split() - - root = ElementTree.Element("testsuite") - root.attrib["name"] = "testsuite" - root.attrib["errors"] = "0" - root.attrib["failures"] = str(len(failures)) - root.attrib["skipped"] = str(len(skipped)) - root.attrib["tests"] = str(len(self._test_results)) - root.attrib["hostname"] = socket.gethostname() - - for result in self._test_results_in_order(): - root.append(result.to_xml(xunit_xml_format)) - - if version_info >= (3, 0): - # Python 3.x - xml = ElementTree.tostring(root, encoding="unicode") - else: - # Python 2.x - xml = ElementTree.tostring(root, encoding="utf-8") - return xml - - -class TestStatus(object): - """ - The status of a test - """ - def __init__(self, name): - self._name = name - - @property - def name(self): - return self._name - - def __eq__(self, other): - return isinstance(other, type(self)) and self.name == other.name - - def __repr__(self): - return "TestStatus(%r)" % self._name - - -PASSED = TestStatus("passed") -SKIPPED = TestStatus("skipped") -FAILED = TestStatus("failed") - - -class TestResult(object): - """ - Represents the result of a single test case - """ - - def __init__(self, name, status, time, output_file_name): - assert status in (PASSED, - FAILED, - SKIPPED) - self.name = name - self._status = status - self.time = time - self._output_file_name = output_file_name - - @property - def output(self): - """ - Return test output - """ - file_exists = os.path.isfile(self._output_file_name) - is_readable = os.access(self._output_file_name, os.R_OK) - if file_exists and is_readable: - return read_file(self._output_file_name) - - return "Failed to read output file: %s" % self._output_file_name - - @property - def passed(self): - return self._status == PASSED - - @property - def skipped(self): - return self._status == SKIPPED - - @property - def failed(self): - return self._status == FAILED - - def print_status(self, printer, padding=0): - """ - Print the status and runtime of this test result - """ - if self.passed: - printer.write("pass", fg='gi') - printer.write(" ") - elif self.failed: - printer.write("fail", fg='ri') - printer.write(" ") - elif self.skipped: - printer.write("skip", fg='rgi') - printer.write(" ") - - my_padding = max(padding - len(self.name), 0) - - printer.write("%s (%.1f seconds)\n" % (self.name + (" " * my_padding), self.time)) - - def to_xml(self, xunit_xml_format): - """ - Convert the test result to ElementTree XML object - """ - test = ElementTree.Element("testcase") - match = re.search(r"(.+)\.([^.]+)$", self.name) - if match: - test.attrib["classname"] = match.group(1) - test.attrib["name"] = match.group(2) - else: - test.attrib["name"] = self.name - test.attrib["time"] = "%.1f" % self.time - - # By default the output is stored in system-out - system_out = ElementTree.SubElement(test, "system-out") - system_out.text = self.output - - if self.failed: - failure = ElementTree.SubElement(test, "failure") - failure.attrib["message"] = "Failed" - - # Store output under if the 'bamboo' format is specified - if xunit_xml_format == 'bamboo': - failure.text = system_out.text - system_out.text = '' - - elif self.skipped: - skipped = ElementTree.SubElement(test, "skipped") - skipped.attrib["message"] = "Skipped" - return test +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Provide test reporting functionality +""" + + +from xml.etree import ElementTree +from sys import version_info +import os +import socket +import re +from vunit.color_printer import COLOR_PRINTER +from vunit.ostools import read_file + + +class TestReport(object): + """ + Collect reports from running testcases + """ + def __init__(self, printer=COLOR_PRINTER): + self._test_results = {} + self._test_names_in_order = [] + self._printer = printer + self._real_total_time = 0.0 + self._expected_num_tests = 0 + + def set_real_total_time(self, real_total_time): + """ + Set the real total execution time + """ + self._real_total_time = real_total_time + + def set_expected_num_tests(self, expected_num_tests): + """ + Set the number of tests that we expect to run + """ + self._expected_num_tests = expected_num_tests + + def num_tests(self): + """ + Return the number of tests in the report + """ + return len(self._test_results) + + def add_result(self, *args, **kwargs): + """ + Add a a test result + """ + result = TestResult(*args, **kwargs) + self._test_results[result.name] = result + self._test_names_in_order.append(result.name) + + def _last_test_result(self): + """ + Return the latest test result or fail + """ + return self._test_results[self._test_names_in_order[-1]] + + def _test_results_in_order(self): + """ + Return the test results in the order they were added + """ + for name in self._test_names_in_order: + yield self.result_of(name) + + def print_latest_status(self, total_tests): + """ + Print the latest status including the last test run and the + total number of passed, failed and skipped tests + """ + result = self._last_test_result() + passed, failed, skipped = self._split() + if result.passed: + self._printer.write("pass", fg='gi') + elif result.failed: + self._printer.write("fail", fg='ri') + elif result.skipped: + self._printer.write("skip", fg='rgi') + else: + assert False + + args = [] + args.append("P=%i" % len(passed)) + args.append("S=%i" % len(skipped)) + args.append("F=%i" % len(failed)) + args.append("T=%i" % total_tests) + + self._printer.write(" (%s) %s (%.1f seconds)\n" % + (" ".join(args), + result.name, + result.time)) + + def all_ok(self): + """ + Return true if all test passed + """ + return all(test_result.passed for test_result in self._test_results.values()) + + def has_test(self, test_name): + return test_name in self._test_results + + def result_of(self, test_name): + return self._test_results[test_name] + + def print_str(self): + """ + Print the report as a colored string + """ + + passed, failures, skipped = self._split() + all_tests = passed + skipped + failures + + if not all_tests: + self._printer.write("No tests were run!", fg="rgi") + self._printer.write("\n") + return + + prefix = "==== Summary " + max_len = max(len(test.name) for test in all_tests) + self._printer.write("%s%s\n" % (prefix, "=" * (max(max_len - len(prefix) + 25, 0)))) + for test_result in all_tests: + test_result.print_status(self._printer, padding=max_len) + + self._printer.write("%s\n" % ("=" * (max(max_len + 25, 0)))) + n_failed = len(failures) + n_skipped = len(skipped) + n_passed = len(passed) + total = len(all_tests) + + self._printer.write("pass", fg='gi') + self._printer.write(" %i of %i\n" % (n_passed, total)) + + if n_skipped > 0: + self._printer.write("skip", fg='rgi') + self._printer.write(" %i of %i\n" % (n_skipped, total)) + + if n_failed > 0: + self._printer.write("fail", fg='ri') + self._printer.write(" %i of %i\n" % (n_failed, total)) + self._printer.write("%s\n" % ("=" * (max(max_len + 25, 0)))) + + total_time = sum((result.time for result in self._test_results.values())) + self._printer.write("Total time was %.1f seconds\n" % total_time) + self._printer.write("Elapsed time was %.1f seconds\n" % self._real_total_time) + + self._printer.write("%s\n" % ("=" * (max(max_len + 25, 0)))) + + if n_failed > 0: + self._printer.write("Some failed!", fg='ri') + elif n_skipped > 0: + self._printer.write("Some skipped!", fg='rgxi') + else: + self._printer.write("All passed!", fg='gi') + self._printer.write("\n") + + assert len(all_tests) <= self._expected_num_tests + if len(all_tests) < self._expected_num_tests: + self._printer.write("WARNING: Test execution aborted after running %d out of %d tests" + % (len(all_tests), self._expected_num_tests), fg='rgi') + self._printer.write("\n") + + def _split(self): + """ + Split the test cases into passed and failures + """ + failures = [] + passed = [] + skipped = [] + for result in self._test_results_in_order(): + if result.passed: + passed.append(result) + elif result.failed: + failures.append(result) + elif result.skipped: + skipped.append(result) + + return passed, failures, skipped + + def to_junit_xml_str(self, xunit_xml_format='jenkins'): + """ + Convert test report to a junit xml string + """ + _, failures, skipped = self._split() + + root = ElementTree.Element("testsuite") + root.attrib["name"] = "testsuite" + root.attrib["errors"] = "0" + root.attrib["failures"] = str(len(failures)) + root.attrib["skipped"] = str(len(skipped)) + root.attrib["tests"] = str(len(self._test_results)) + root.attrib["hostname"] = socket.gethostname() + + for result in self._test_results_in_order(): + root.append(result.to_xml(xunit_xml_format)) + + if version_info >= (3, 0): + # Python 3.x + xml = ElementTree.tostring(root, encoding="unicode") + else: + # Python 2.x + xml = ElementTree.tostring(root, encoding="utf-8") + return xml + + +class TestStatus(object): + """ + The status of a test + """ + def __init__(self, name): + self._name = name + + @property + def name(self): + return self._name + + def __eq__(self, other): + return isinstance(other, type(self)) and self.name == other.name + + def __repr__(self): + return "TestStatus(%r)" % self._name + + +PASSED = TestStatus("passed") +SKIPPED = TestStatus("skipped") +FAILED = TestStatus("failed") + + +class TestResult(object): + """ + Represents the result of a single test case + """ + + def __init__(self, name, status, time, output_file_name): + assert status in (PASSED, + FAILED, + SKIPPED) + self.name = name + self._status = status + self.time = time + self._output_file_name = output_file_name + + @property + def output(self): + """ + Return test output + """ + file_exists = os.path.isfile(self._output_file_name) + is_readable = os.access(self._output_file_name, os.R_OK) + if file_exists and is_readable: + return read_file(self._output_file_name) + + return "Failed to read output file: %s" % self._output_file_name + + @property + def passed(self): + return self._status == PASSED + + @property + def skipped(self): + return self._status == SKIPPED + + @property + def failed(self): + return self._status == FAILED + + def print_status(self, printer, padding=0): + """ + Print the status and runtime of this test result + """ + if self.passed: + printer.write("pass", fg='gi') + printer.write(" ") + elif self.failed: + printer.write("fail", fg='ri') + printer.write(" ") + elif self.skipped: + printer.write("skip", fg='rgi') + printer.write(" ") + + my_padding = max(padding - len(self.name), 0) + + printer.write("%s (%.1f seconds)\n" % (self.name + (" " * my_padding), self.time)) + + def to_xml(self, xunit_xml_format): + """ + Convert the test result to ElementTree XML object + """ + test = ElementTree.Element("testcase") + match = re.search(r"(.+)\.([^.]+)$", self.name) + if match: + test.attrib["classname"] = match.group(1) + test.attrib["name"] = match.group(2) + else: + test.attrib["name"] = self.name + test.attrib["time"] = "%.1f" % self.time + + # By default the output is stored in system-out + system_out = ElementTree.SubElement(test, "system-out") + system_out.text = self.output + + if self.failed: + failure = ElementTree.SubElement(test, "failure") + failure.attrib["message"] = "Failed" + + # Store output under if the 'bamboo' format is specified + if xunit_xml_format == 'bamboo': + failure.text = system_out.text + system_out.text = '' + + elif self.skipped: + skipped = ElementTree.SubElement(test, "skipped") + skipped.attrib["message"] = "Skipped" + return test diff --git a/vunit/test_runner.py b/vunit/test_runner.py index 1d48613f8..1b706840f 100644 --- a/vunit/test_runner.py +++ b/vunit/test_runner.py @@ -1,461 +1,461 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Provided functionality to run a suite of test in a robust way -""" - - -from __future__ import print_function - -import os -from os.path import join, exists, abspath, basename, relpath -import traceback -import threading -import sys -import time -import logging -import string -from contextlib import contextmanager -from vunit import ostools -from vunit.test_report import PASSED, FAILED, SKIPPED -from vunit.hashing import hash_string -LOGGER = logging.getLogger(__name__) - - -class TestRunner(object): # pylint: disable=too-many-instance-attributes - """ - Administer the execution of a list of test suites - """ - - VERBOSITY_QUIET = 0 - VERBOSITY_NORMAL = 1 - VERBOSITY_VERBOSE = 2 - - def __init__(self, # pylint: disable=too-many-arguments - report, output_path, - verbosity=VERBOSITY_NORMAL, - num_threads=1, - fail_fast=False, - dont_catch_exceptions=False, - no_color=False): - self._lock = threading.Lock() - self._fail_fast = fail_fast - self._abort = False - self._local = threading.local() - self._report = report - self._output_path = output_path - assert verbosity in (self.VERBOSITY_QUIET, - self.VERBOSITY_NORMAL, - self.VERBOSITY_VERBOSE) - self._verbosity = verbosity - self._num_threads = num_threads - self._stdout = sys.stdout - self._stdout_ansi = wrap(self._stdout, use_color=not no_color) - self._stderr = sys.stderr - self._dont_catch_exceptions = dont_catch_exceptions - self._no_color = no_color - - ostools.PROGRAM_STATUS.reset() - - @property - def _is_verbose(self): - return self._verbosity == self.VERBOSITY_VERBOSE - - @property - def _is_quiet(self): - return self._verbosity == self.VERBOSITY_QUIET - - def run(self, test_suites): - """ - Run a list of test suites - """ - - if not exists(self._output_path): - os.makedirs(self._output_path) - - self._create_test_mapping_file(test_suites) - - num_tests = 0 - for test_suite in test_suites: - for test_name in test_suite.test_names: - num_tests += 1 - if self._is_verbose: - print("Running test: " + test_name) - - if self._is_verbose: - print("Running %i tests" % num_tests) - print() - - self._report.set_expected_num_tests(num_tests) - - scheduler = TestScheduler(test_suites) - - threads = [] - - # Disable continuous output in parallel mode - write_stdout = self._is_verbose and self._num_threads == 1 - - try: - sys.stdout = ThreadLocalOutput(self._local, self._stdout) - sys.stderr = ThreadLocalOutput(self._local, self._stdout) - - # Start P-1 worker threads - for _ in range(self._num_threads - 1): - new_thread = threading.Thread(target=self._run_thread, - args=(write_stdout, scheduler, num_tests, False)) - threads.append(new_thread) - new_thread.start() - - # Run one worker in main thread such that P=1 is not multithreaded - self._run_thread(write_stdout, scheduler, num_tests, True) - - scheduler.wait_for_finish() - - except KeyboardInterrupt: - LOGGER.debug("TestRunner: Caught Ctrl-C shutting down") - ostools.PROGRAM_STATUS.shutdown() - raise - - finally: - for thread in threads: - thread.join() - - sys.stdout = self._stdout - sys.stderr = self._stderr - LOGGER.debug("TestRunner: Leaving") - - def _run_thread(self, write_stdout, scheduler, num_tests, is_main): - """ - Run worker thread - """ - self._local.output = self._stdout - - while True: - test_suite = None - try: - test_suite = scheduler.next() - - output_path = create_output_path(self._output_path, test_suite.name) - output_file_name = join(output_path, "output.txt") - - with self._stdout_lock(): - for test_name in test_suite.test_names: - print("Starting %s" % test_name) - print("Output file: %s" % relpath(output_file_name)) - - self._run_test_suite(test_suite, - write_stdout, - num_tests, - output_path, - output_file_name) - - except StopIteration: - return - - except KeyboardInterrupt: - # Only main thread should handle KeyboardInterrupt - if is_main: - LOGGER.debug("MainWorkerThread: Caught Ctrl-C shutting down") - raise - - return - - finally: - if test_suite is not None: - scheduler.test_done() - - def _add_skipped_tests(self, test_suite, results, start_time, num_tests, output_file_name): - for name in test_suite.test_names: - results[name] = SKIPPED - self._add_results(test_suite, results, start_time, num_tests, output_file_name) - - def _run_test_suite(self, - test_suite, - write_stdout, - num_tests, - output_path, - output_file_name): - """ - Run the actual test suite - """ - color_output_file_name = join(output_path, "output_with_color.txt") - - output_file = None - color_output_file = None - - start_time = ostools.get_time() - results = self._fail_suite(test_suite) - - try: - ostools.renew_path(output_path) - output_file = wrap(open(output_file_name, "a+"), use_color=False) - output_file.seek(0) - output_file.truncate() - - if write_stdout: - self._local.output = Tee([self._stdout_ansi, output_file]) - else: - color_output_file = open(color_output_file_name, "w") - self._local.output = Tee([color_output_file, output_file]) - - def read_output(): - """ - Called to read the contents of the output file on demand - """ - output_file.flush() - prev = output_file.tell() - output_file.seek(0) - contents = output_file.read() - output_file.seek(prev) - return contents - - results = test_suite.run(output_path=output_path, - read_output=read_output) - except KeyboardInterrupt: - self._add_skipped_tests(test_suite, results, start_time, num_tests, output_file_name) - raise KeyboardInterrupt - except: # pylint: disable=bare-except - if self._dont_catch_exceptions: - raise - - with self._stdout_lock(): - traceback.print_exc() - finally: - self._local.output = self._stdout - - for fptr in [output_file, color_output_file]: - if fptr is None: - continue - - fptr.flush() - fptr.close() - - any_not_passed = any(value != PASSED for value in results.values()) - - with self._stdout_lock(): - - if (color_output_file is not None) and (any_not_passed or self._is_verbose) and not self._is_quiet: - self._print_output(color_output_file_name) - - self._add_results(test_suite, results, start_time, num_tests, output_file_name) - - if self._fail_fast and any_not_passed: - self._abort = True - - def _create_test_mapping_file(self, test_suites): - """ - Create a file mapping test name to test output folder. - This is to allow the user to find the test output folder when it is hashed - """ - mapping_file_name = join(self._output_path, "test_name_to_path_mapping.txt") - - # Load old mapping to remember non-deleted test folders as well - # even when re-running only a single test case - if exists(mapping_file_name): - with open(mapping_file_name, "r") as fptr: - mapping = set(fptr.read().splitlines()) - else: - mapping = set() - - for test_suite in test_suites: - test_output = create_output_path(self._output_path, test_suite.name) - mapping.add("%s %s" % (basename(test_output), test_suite.name)) - - # Sort by everything except hash - mapping = sorted(mapping, key=lambda value: value[value.index(" "):]) - - with open(mapping_file_name, "w") as fptr: - for value in mapping: - fptr.write(value + "\n") - - def _print_output(self, output_file_name): - """ - Print contents of output file if it exists - """ - with open(output_file_name, "r") as fread: - for line in fread.readlines(): - self._stdout_ansi.write(line) - - def _add_results(self, test_suite, results, start_time, num_tests, output_file_name): - """ - Add results to test report - """ - runtime = ostools.get_time() - start_time - time_per_test = runtime / len(results) - - for test_name in test_suite.test_names: - status = results[test_name] - self._report.add_result(test_name, - status, - time_per_test, - output_file_name) - self._report.print_latest_status(total_tests=num_tests) - print() - - @staticmethod - def _fail_suite(test_suite): - """ Return failure for all tests in suite """ - results = {} - for test_name in test_suite.test_names: - results[test_name] = FAILED - return results - - @contextmanager - def _stdout_lock(self): - """ - Enter this lock when printing to stdout - Ensures no additional output is printed during abort - """ - with self._lock: # pylint: disable=not-context-manager - if self._abort: - raise KeyboardInterrupt - yield - - -class Tee(object): - """ - Provide a write method which writes to multiple files - like the unix 'tee' command. - """ - def __init__(self, files): - self._files = files - - def write(self, txt): - for ofile in self._files: - ofile.write(txt) - - def flush(self): - for ofile in self._files: - ofile.flush() - - -class ThreadLocalOutput(object): - """ - Replacement for stdout/err that separates re-directs - output to a thread local file interface - """ - def __init__(self, local, stdout): - self._local = local - self._stdout = stdout - - def write(self, txt): - """ - Write to file object - """ - if hasattr(self._local, "output"): - self._local.output.write(txt) - else: - self._stdout.write(txt) - - def flush(self): - """ - Flush file object - """ - if hasattr(self._local, "output"): - self._local.output.flush() - else: - self._stdout.flush() - - -class TestScheduler(object): - """ - Schedule tests to different treads - """ - - def __init__(self, tests): - self._lock = threading.Lock() - self._tests = tests - self._idx = 0 - self._num_done = 0 - - def __iter__(self): - return self - - def __next__(self): - """ - Iterator in Python 3 - """ - return self.__next__() - - def next(self): - """ - Iterator in Python 2 - """ - ostools.PROGRAM_STATUS.check_for_shutdown() - with self._lock: # pylint: disable=not-context-manager - if self._idx < len(self._tests): - idx = self._idx - self._idx += 1 - return self._tests[idx] - - raise StopIteration - - def test_done(self): - """ - Signal that a test has been done - """ - with self._lock: # pylint: disable=not-context-manager - self._num_done += 1 - - def is_finished(self): - with self._lock: # pylint: disable=not-context-manager - return self._num_done >= len(self._tests) - - def wait_for_finish(self): - """ - Block until all tests have been done - """ - while not self.is_finished(): - time.sleep(0.05) - - -LEGAL_CHARS = string.printable -ILLEGAL_CHARS = ' <>"|:*%?\\/#&;()' - - -def _is_legal(char): - """ - Return true if the character is legal to have in a file name - """ - return (char in LEGAL_CHARS) and (char not in ILLEGAL_CHARS) - - -def create_output_path(output_path, test_suite_name): - """ - Create the full output path of a test case. - Ensure no bad characters and no long path names. - """ - output_path = abspath(output_path) - safe_name = "".join(char if _is_legal(char) else '_' for char in test_suite_name) + "_" - hash_name = hash_string(test_suite_name) - - if "VUNIT_SHORT_TEST_OUTPUT_PATHS" in os.environ: - full_name = hash_name - elif sys.platform == "win32": - max_path = 260 - margin = int(os.environ.get("VUNIT_TEST_OUTPUT_PATH_MARGIN", "100")) - prefix_len = len(output_path) - full_name = safe_name[:min(max_path - margin - prefix_len - len(hash_name), len(safe_name))] + hash_name - else: - full_name = safe_name + hash_name - - return join(output_path, full_name) - - -def wrap(file_obj, use_color=True): - """ - Wrap file_obj in another stream which handles ANSI color codes using colorama - - NOTE: - imports colorama here to avoid dependency from setup.py importing VUnit before colorama is installed - """ - from colorama import AnsiToWin32 - - if use_color: - return AnsiToWin32(file_obj).stream - - return AnsiToWin32(file_obj, strip=True, convert=False).stream +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Provided functionality to run a suite of test in a robust way +""" + + +from __future__ import print_function + +import os +from os.path import join, exists, abspath, basename, relpath +import traceback +import threading +import sys +import time +import logging +import string +from contextlib import contextmanager +from vunit import ostools +from vunit.test_report import PASSED, FAILED, SKIPPED +from vunit.hashing import hash_string +LOGGER = logging.getLogger(__name__) + + +class TestRunner(object): # pylint: disable=too-many-instance-attributes + """ + Administer the execution of a list of test suites + """ + + VERBOSITY_QUIET = 0 + VERBOSITY_NORMAL = 1 + VERBOSITY_VERBOSE = 2 + + def __init__(self, # pylint: disable=too-many-arguments + report, output_path, + verbosity=VERBOSITY_NORMAL, + num_threads=1, + fail_fast=False, + dont_catch_exceptions=False, + no_color=False): + self._lock = threading.Lock() + self._fail_fast = fail_fast + self._abort = False + self._local = threading.local() + self._report = report + self._output_path = output_path + assert verbosity in (self.VERBOSITY_QUIET, + self.VERBOSITY_NORMAL, + self.VERBOSITY_VERBOSE) + self._verbosity = verbosity + self._num_threads = num_threads + self._stdout = sys.stdout + self._stdout_ansi = wrap(self._stdout, use_color=not no_color) + self._stderr = sys.stderr + self._dont_catch_exceptions = dont_catch_exceptions + self._no_color = no_color + + ostools.PROGRAM_STATUS.reset() + + @property + def _is_verbose(self): + return self._verbosity == self.VERBOSITY_VERBOSE + + @property + def _is_quiet(self): + return self._verbosity == self.VERBOSITY_QUIET + + def run(self, test_suites): + """ + Run a list of test suites + """ + + if not exists(self._output_path): + os.makedirs(self._output_path) + + self._create_test_mapping_file(test_suites) + + num_tests = 0 + for test_suite in test_suites: + for test_name in test_suite.test_names: + num_tests += 1 + if self._is_verbose: + print("Running test: " + test_name) + + if self._is_verbose: + print("Running %i tests" % num_tests) + print() + + self._report.set_expected_num_tests(num_tests) + + scheduler = TestScheduler(test_suites) + + threads = [] + + # Disable continuous output in parallel mode + write_stdout = self._is_verbose and self._num_threads == 1 + + try: + sys.stdout = ThreadLocalOutput(self._local, self._stdout) + sys.stderr = ThreadLocalOutput(self._local, self._stdout) + + # Start P-1 worker threads + for _ in range(self._num_threads - 1): + new_thread = threading.Thread(target=self._run_thread, + args=(write_stdout, scheduler, num_tests, False)) + threads.append(new_thread) + new_thread.start() + + # Run one worker in main thread such that P=1 is not multithreaded + self._run_thread(write_stdout, scheduler, num_tests, True) + + scheduler.wait_for_finish() + + except KeyboardInterrupt: + LOGGER.debug("TestRunner: Caught Ctrl-C shutting down") + ostools.PROGRAM_STATUS.shutdown() + raise + + finally: + for thread in threads: + thread.join() + + sys.stdout = self._stdout + sys.stderr = self._stderr + LOGGER.debug("TestRunner: Leaving") + + def _run_thread(self, write_stdout, scheduler, num_tests, is_main): + """ + Run worker thread + """ + self._local.output = self._stdout + + while True: + test_suite = None + try: + test_suite = scheduler.next() + + output_path = create_output_path(self._output_path, test_suite.name) + output_file_name = join(output_path, "output.txt") + + with self._stdout_lock(): + for test_name in test_suite.test_names: + print("Starting %s" % test_name) + print("Output file: %s" % relpath(output_file_name)) + + self._run_test_suite(test_suite, + write_stdout, + num_tests, + output_path, + output_file_name) + + except StopIteration: + return + + except KeyboardInterrupt: + # Only main thread should handle KeyboardInterrupt + if is_main: + LOGGER.debug("MainWorkerThread: Caught Ctrl-C shutting down") + raise + + return + + finally: + if test_suite is not None: + scheduler.test_done() + + def _add_skipped_tests(self, test_suite, results, start_time, num_tests, output_file_name): + for name in test_suite.test_names: + results[name] = SKIPPED + self._add_results(test_suite, results, start_time, num_tests, output_file_name) + + def _run_test_suite(self, + test_suite, + write_stdout, + num_tests, + output_path, + output_file_name): + """ + Run the actual test suite + """ + color_output_file_name = join(output_path, "output_with_color.txt") + + output_file = None + color_output_file = None + + start_time = ostools.get_time() + results = self._fail_suite(test_suite) + + try: + ostools.renew_path(output_path) + output_file = wrap(open(output_file_name, "a+"), use_color=False) + output_file.seek(0) + output_file.truncate() + + if write_stdout: + self._local.output = Tee([self._stdout_ansi, output_file]) + else: + color_output_file = open(color_output_file_name, "w") + self._local.output = Tee([color_output_file, output_file]) + + def read_output(): + """ + Called to read the contents of the output file on demand + """ + output_file.flush() + prev = output_file.tell() + output_file.seek(0) + contents = output_file.read() + output_file.seek(prev) + return contents + + results = test_suite.run(output_path=output_path, + read_output=read_output) + except KeyboardInterrupt: + self._add_skipped_tests(test_suite, results, start_time, num_tests, output_file_name) + raise KeyboardInterrupt + except: # pylint: disable=bare-except + if self._dont_catch_exceptions: + raise + + with self._stdout_lock(): + traceback.print_exc() + finally: + self._local.output = self._stdout + + for fptr in [output_file, color_output_file]: + if fptr is None: + continue + + fptr.flush() + fptr.close() + + any_not_passed = any(value != PASSED for value in results.values()) + + with self._stdout_lock(): + + if (color_output_file is not None) and (any_not_passed or self._is_verbose) and not self._is_quiet: + self._print_output(color_output_file_name) + + self._add_results(test_suite, results, start_time, num_tests, output_file_name) + + if self._fail_fast and any_not_passed: + self._abort = True + + def _create_test_mapping_file(self, test_suites): + """ + Create a file mapping test name to test output folder. + This is to allow the user to find the test output folder when it is hashed + """ + mapping_file_name = join(self._output_path, "test_name_to_path_mapping.txt") + + # Load old mapping to remember non-deleted test folders as well + # even when re-running only a single test case + if exists(mapping_file_name): + with open(mapping_file_name, "r") as fptr: + mapping = set(fptr.read().splitlines()) + else: + mapping = set() + + for test_suite in test_suites: + test_output = create_output_path(self._output_path, test_suite.name) + mapping.add("%s %s" % (basename(test_output), test_suite.name)) + + # Sort by everything except hash + mapping = sorted(mapping, key=lambda value: value[value.index(" "):]) + + with open(mapping_file_name, "w") as fptr: + for value in mapping: + fptr.write(value + "\n") + + def _print_output(self, output_file_name): + """ + Print contents of output file if it exists + """ + with open(output_file_name, "r") as fread: + for line in fread.readlines(): + self._stdout_ansi.write(line) + + def _add_results(self, test_suite, results, start_time, num_tests, output_file_name): + """ + Add results to test report + """ + runtime = ostools.get_time() - start_time + time_per_test = runtime / len(results) + + for test_name in test_suite.test_names: + status = results[test_name] + self._report.add_result(test_name, + status, + time_per_test, + output_file_name) + self._report.print_latest_status(total_tests=num_tests) + print() + + @staticmethod + def _fail_suite(test_suite): + """ Return failure for all tests in suite """ + results = {} + for test_name in test_suite.test_names: + results[test_name] = FAILED + return results + + @contextmanager + def _stdout_lock(self): + """ + Enter this lock when printing to stdout + Ensures no additional output is printed during abort + """ + with self._lock: # pylint: disable=not-context-manager + if self._abort: + raise KeyboardInterrupt + yield + + +class Tee(object): + """ + Provide a write method which writes to multiple files + like the unix 'tee' command. + """ + def __init__(self, files): + self._files = files + + def write(self, txt): + for ofile in self._files: + ofile.write(txt) + + def flush(self): + for ofile in self._files: + ofile.flush() + + +class ThreadLocalOutput(object): + """ + Replacement for stdout/err that separates re-directs + output to a thread local file interface + """ + def __init__(self, local, stdout): + self._local = local + self._stdout = stdout + + def write(self, txt): + """ + Write to file object + """ + if hasattr(self._local, "output"): + self._local.output.write(txt) + else: + self._stdout.write(txt) + + def flush(self): + """ + Flush file object + """ + if hasattr(self._local, "output"): + self._local.output.flush() + else: + self._stdout.flush() + + +class TestScheduler(object): + """ + Schedule tests to different treads + """ + + def __init__(self, tests): + self._lock = threading.Lock() + self._tests = tests + self._idx = 0 + self._num_done = 0 + + def __iter__(self): + return self + + def __next__(self): + """ + Iterator in Python 3 + """ + return self.__next__() + + def next(self): + """ + Iterator in Python 2 + """ + ostools.PROGRAM_STATUS.check_for_shutdown() + with self._lock: # pylint: disable=not-context-manager + if self._idx < len(self._tests): + idx = self._idx + self._idx += 1 + return self._tests[idx] + + raise StopIteration + + def test_done(self): + """ + Signal that a test has been done + """ + with self._lock: # pylint: disable=not-context-manager + self._num_done += 1 + + def is_finished(self): + with self._lock: # pylint: disable=not-context-manager + return self._num_done >= len(self._tests) + + def wait_for_finish(self): + """ + Block until all tests have been done + """ + while not self.is_finished(): + time.sleep(0.05) + + +LEGAL_CHARS = string.printable +ILLEGAL_CHARS = ' <>"|:*%?\\/#&;()' + + +def _is_legal(char): + """ + Return true if the character is legal to have in a file name + """ + return (char in LEGAL_CHARS) and (char not in ILLEGAL_CHARS) + + +def create_output_path(output_path, test_suite_name): + """ + Create the full output path of a test case. + Ensure no bad characters and no long path names. + """ + output_path = abspath(output_path) + safe_name = "".join(char if _is_legal(char) else '_' for char in test_suite_name) + "_" + hash_name = hash_string(test_suite_name) + + if "VUNIT_SHORT_TEST_OUTPUT_PATHS" in os.environ: + full_name = hash_name + elif sys.platform == "win32": + max_path = 260 + margin = int(os.environ.get("VUNIT_TEST_OUTPUT_PATH_MARGIN", "100")) + prefix_len = len(output_path) + full_name = safe_name[:min(max_path - margin - prefix_len - len(hash_name), len(safe_name))] + hash_name + else: + full_name = safe_name + hash_name + + return join(output_path, full_name) + + +def wrap(file_obj, use_color=True): + """ + Wrap file_obj in another stream which handles ANSI color codes using colorama + + NOTE: + imports colorama here to avoid dependency from setup.py importing VUnit before colorama is installed + """ + from colorama import AnsiToWin32 + + if use_color: + return AnsiToWin32(file_obj).stream + + return AnsiToWin32(file_obj, strip=True, convert=False).stream diff --git a/vunit/test_suites.py b/vunit/test_suites.py index 1204d8211..0d995c880 100644 --- a/vunit/test_suites.py +++ b/vunit/test_suites.py @@ -1,314 +1,314 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Contains different kinds of test suites -""" - - -from os.path import join -from vunit import ostools -from vunit.test_report import (PASSED, SKIPPED, FAILED) - - -class IndependentSimTestCase(object): - """ - A test case to be run in an independent simulation - """ - def __init__(self, test, config, simulator_if, elaborate_only=False): - self._name = "%s.%s" % (config.library_name, config.design_unit_name) - - if not config.is_default: - self._name += "." + config.name - - if test.is_explicit: - self._name += "." + test.name - elif config.is_default: - # JUnit XML test reports wants three dotted name hierarchies - self._name += ".all" - - self._configuration = config - - self._test = test - - self._run = TestRun(simulator_if=simulator_if, - config=config, - elaborate_only=elaborate_only, - test_suite_name=self._name, - test_cases=[test.name]) - - @property - def name(self): - return self._name - - @property - def attribute_names(self): - return self._test.attribute_names - - @property - def test_configuration(self): - return self._configuration - - @property - def test_information(self): - """ - Returns the test information - """ - return self._test - - def run(self, *args, **kwargs): - """ - Run the test case using the output_path - """ - results = self._run.run(*args, **kwargs) - return results[self._test.name] == PASSED - - -class SameSimTestSuite(object): - """ - A test suite where multiple test cases are run within the same simulation - """ - - def __init__(self, tests, config, simulator_if, elaborate_only=False): - self._name = "%s.%s" % (config.library_name, config.design_unit_name) - - if not config.is_default: - self._name += "." + config.name - - self._configuration = config - - self._tests = tests - self._run = TestRun(simulator_if=simulator_if, - config=config, - elaborate_only=elaborate_only, - test_suite_name=self._name, - test_cases=[test.name for test in tests]) - - @property - def test_names(self): - return [_full_name(self._name, test.name) - for test in self._tests] - - @property - def test_information(self): - """ - Returns a dictionary mapping full test name to test information object - """ - return {_full_name(self._name, test.name): test - for test in self._tests} - - @property - def name(self): - return self._name - - def keep_matches(self, test_filter): - """ - Keep tests which pattern return False if no remaining tests - """ - def _merge_attributes(attribute_names, attributes): - merged_attributes = attribute_names.copy() - merged_attributes.update(set(attributes.keys())) - return merged_attributes - - self._tests = [test for test in self._tests - if test_filter(name=_full_name(self.name, test.name), - attribute_names=_merge_attributes(test.attribute_names, - self._configuration.attributes))] - self._run.set_test_cases([test.name for test in self._tests]) - return len(self._tests) > 0 - - def run(self, *args, **kwargs): - """ - Run the test suite using output_path - """ - results = self._run.run(*args, **kwargs) - results = {_full_name(self._name, test_name): result - for test_name, result in results.items()} - return results - - -class TestRun(object): - """ - A single simulation run yielding the results for one or several test cases - """ - - def __init__(self, simulator_if, config, elaborate_only, test_suite_name, test_cases): - self._simulator_if = simulator_if - self._config = config - self._elaborate_only = elaborate_only - self._test_suite_name = test_suite_name - self._test_cases = test_cases - - def set_test_cases(self, test_cases): - self._test_cases = test_cases - - def run(self, output_path, read_output): - """ - Run selected test cases within the test suite - - Returns a dictionary of test results - """ - results = {} - for name in self._test_cases: - results[name] = FAILED - - if not self._config.call_pre_config(output_path, - self._simulator_if.output_path): - return results - - # Ensure result file exists - ostools.write_file(get_result_file_name(output_path), "") - - sim_ok = self._simulate(output_path) - - if self._elaborate_only: - status = PASSED if sim_ok else FAILED - return dict((name, status) for name in self._test_cases) - - results = self._read_test_results(file_name=get_result_file_name(output_path)) - - done, results = self._check_results(results, sim_ok) - if done: - return results - - if not self._config.call_post_check(output_path, read_output): - for name in self._test_cases: - results[name] = FAILED - - return results - - def _check_results(self, results, sim_ok): - """ - Test the results and the exit code; return True the status of any test is not PASSED - """ - # If any test failed, return results - for status in results.values(): - if status == FAILED: - return True, results - - # Force fail if all tests pass in the presence of non-zero exit code - if self._simulator_if.has_valid_exit_code() and not sim_ok: - return True, dict((name, FAILED) if results[name] is PASSED else (name, results[name]) for name in results) - - return False, results - - def _simulate(self, output_path): - """ - Add runner_cfg generic values and run simulation - """ - - config = self._config.copy() - - if "output_path" in config.generic_names and "output_path" not in config.generics: - config.generics["output_path"] = '%s/' % output_path.replace("\\", "/") - - runner_cfg = { - "enabled_test_cases": ",".join(encode_test_case(test_case) - for test_case in self._test_cases - if test_case is not None), - "use_color": self._simulator_if.use_color, - "output path": output_path.replace("\\", "/") + "/", - "active python runner": True, - "tb path": config.tb_path.replace("\\", "/") + "/", - } - - # @TODO Warn if runner cfg already set? - config.generics["runner_cfg"] = encode_dict(runner_cfg) - - return self._simulator_if.simulate( - output_path=output_path, - test_suite_name=self._test_suite_name, - config=config, - elaborate_only=self._elaborate_only) - - def _read_test_results(self, file_name): - """ - Read test results from vunit_results file - """ - - results = {} - for name in self._test_cases: - results[name] = FAILED - - if not ostools.file_exists(file_name): - return results - - test_results = ostools.read_file(file_name) - test_starts = [] - test_suite_done = False - - for line in test_results.splitlines(): - - if line.startswith("test_start:"): - test_name = line[len("test_start:"):] - test_starts.append(test_name) - - elif line.startswith("test_suite_done"): - test_suite_done = True - - for idx, test_name in enumerate(test_starts): - last_start = idx == len(test_starts) - 1 - - if test_suite_done or not last_start: - results[test_name] = PASSED - - for test_name in self._test_cases: - - # Anonymous test case - if test_name is None: - results[test_name] = PASSED if test_suite_done else FAILED - continue - - if test_name not in test_starts: - results[test_name] = SKIPPED - - for test_name in results: - if test_name not in self._test_cases: - raise RuntimeError("Got unknown test case %s" % test_name) - - return results - - -def encode_test_case(test_case): - """ - Encode test case name to escape commas. Avoids that test case names including commas - are interpreted as several test cases in the comma separated list of enabled test cases - included in the runner_cfg string. - """ - if test_case is not None: - return test_case.replace(',', ',,') - - return None - - -def encode_dict(dictionary): - """ - Encode dictionary for custom VHDL dictionary parser - """ - def escape(value): - return value.replace(':', '::').replace(',', ',,') - - encoded = [] - for key in sorted(dictionary.keys()): - value = dictionary[key] - encoded.append("%s : %s" % (escape(key), - escape(encode_dict_value(value)))) - return ",".join(encoded) - - -def encode_dict_value(value): # pylint: disable=missing-docstring - if isinstance(value, bool): - return str(value).lower() - - return str(value) - - -def _full_name(test_suite_name, test_case_name): - return test_suite_name + "." + test_case_name - - -def get_result_file_name(output_path): - return join(output_path, "vunit_results") +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Contains different kinds of test suites +""" + + +from os.path import join +from vunit import ostools +from vunit.test_report import (PASSED, SKIPPED, FAILED) + + +class IndependentSimTestCase(object): + """ + A test case to be run in an independent simulation + """ + def __init__(self, test, config, simulator_if, elaborate_only=False): + self._name = "%s.%s" % (config.library_name, config.design_unit_name) + + if not config.is_default: + self._name += "." + config.name + + if test.is_explicit: + self._name += "." + test.name + elif config.is_default: + # JUnit XML test reports wants three dotted name hierarchies + self._name += ".all" + + self._configuration = config + + self._test = test + + self._run = TestRun(simulator_if=simulator_if, + config=config, + elaborate_only=elaborate_only, + test_suite_name=self._name, + test_cases=[test.name]) + + @property + def name(self): + return self._name + + @property + def attribute_names(self): + return self._test.attribute_names + + @property + def test_configuration(self): + return self._configuration + + @property + def test_information(self): + """ + Returns the test information + """ + return self._test + + def run(self, *args, **kwargs): + """ + Run the test case using the output_path + """ + results = self._run.run(*args, **kwargs) + return results[self._test.name] == PASSED + + +class SameSimTestSuite(object): + """ + A test suite where multiple test cases are run within the same simulation + """ + + def __init__(self, tests, config, simulator_if, elaborate_only=False): + self._name = "%s.%s" % (config.library_name, config.design_unit_name) + + if not config.is_default: + self._name += "." + config.name + + self._configuration = config + + self._tests = tests + self._run = TestRun(simulator_if=simulator_if, + config=config, + elaborate_only=elaborate_only, + test_suite_name=self._name, + test_cases=[test.name for test in tests]) + + @property + def test_names(self): + return [_full_name(self._name, test.name) + for test in self._tests] + + @property + def test_information(self): + """ + Returns a dictionary mapping full test name to test information object + """ + return {_full_name(self._name, test.name): test + for test in self._tests} + + @property + def name(self): + return self._name + + def keep_matches(self, test_filter): + """ + Keep tests which pattern return False if no remaining tests + """ + def _merge_attributes(attribute_names, attributes): + merged_attributes = attribute_names.copy() + merged_attributes.update(set(attributes.keys())) + return merged_attributes + + self._tests = [test for test in self._tests + if test_filter(name=_full_name(self.name, test.name), + attribute_names=_merge_attributes(test.attribute_names, + self._configuration.attributes))] + self._run.set_test_cases([test.name for test in self._tests]) + return len(self._tests) > 0 + + def run(self, *args, **kwargs): + """ + Run the test suite using output_path + """ + results = self._run.run(*args, **kwargs) + results = {_full_name(self._name, test_name): result + for test_name, result in results.items()} + return results + + +class TestRun(object): + """ + A single simulation run yielding the results for one or several test cases + """ + + def __init__(self, simulator_if, config, elaborate_only, test_suite_name, test_cases): + self._simulator_if = simulator_if + self._config = config + self._elaborate_only = elaborate_only + self._test_suite_name = test_suite_name + self._test_cases = test_cases + + def set_test_cases(self, test_cases): + self._test_cases = test_cases + + def run(self, output_path, read_output): + """ + Run selected test cases within the test suite + + Returns a dictionary of test results + """ + results = {} + for name in self._test_cases: + results[name] = FAILED + + if not self._config.call_pre_config(output_path, + self._simulator_if.output_path): + return results + + # Ensure result file exists + ostools.write_file(get_result_file_name(output_path), "") + + sim_ok = self._simulate(output_path) + + if self._elaborate_only: + status = PASSED if sim_ok else FAILED + return dict((name, status) for name in self._test_cases) + + results = self._read_test_results(file_name=get_result_file_name(output_path)) + + done, results = self._check_results(results, sim_ok) + if done: + return results + + if not self._config.call_post_check(output_path, read_output): + for name in self._test_cases: + results[name] = FAILED + + return results + + def _check_results(self, results, sim_ok): + """ + Test the results and the exit code; return True the status of any test is not PASSED + """ + # If any test failed, return results + for status in results.values(): + if status == FAILED: + return True, results + + # Force fail if all tests pass in the presence of non-zero exit code + if self._simulator_if.has_valid_exit_code() and not sim_ok: + return True, dict((name, FAILED) if results[name] is PASSED else (name, results[name]) for name in results) + + return False, results + + def _simulate(self, output_path): + """ + Add runner_cfg generic values and run simulation + """ + + config = self._config.copy() + + if "output_path" in config.generic_names and "output_path" not in config.generics: + config.generics["output_path"] = '%s/' % output_path.replace("\\", "/") + + runner_cfg = { + "enabled_test_cases": ",".join(encode_test_case(test_case) + for test_case in self._test_cases + if test_case is not None), + "use_color": self._simulator_if.use_color, + "output path": output_path.replace("\\", "/") + "/", + "active python runner": True, + "tb path": config.tb_path.replace("\\", "/") + "/", + } + + # @TODO Warn if runner cfg already set? + config.generics["runner_cfg"] = encode_dict(runner_cfg) + + return self._simulator_if.simulate( + output_path=output_path, + test_suite_name=self._test_suite_name, + config=config, + elaborate_only=self._elaborate_only) + + def _read_test_results(self, file_name): + """ + Read test results from vunit_results file + """ + + results = {} + for name in self._test_cases: + results[name] = FAILED + + if not ostools.file_exists(file_name): + return results + + test_results = ostools.read_file(file_name) + test_starts = [] + test_suite_done = False + + for line in test_results.splitlines(): + + if line.startswith("test_start:"): + test_name = line[len("test_start:"):] + test_starts.append(test_name) + + elif line.startswith("test_suite_done"): + test_suite_done = True + + for idx, test_name in enumerate(test_starts): + last_start = idx == len(test_starts) - 1 + + if test_suite_done or not last_start: + results[test_name] = PASSED + + for test_name in self._test_cases: + + # Anonymous test case + if test_name is None: + results[test_name] = PASSED if test_suite_done else FAILED + continue + + if test_name not in test_starts: + results[test_name] = SKIPPED + + for test_name in results: + if test_name not in self._test_cases: + raise RuntimeError("Got unknown test case %s" % test_name) + + return results + + +def encode_test_case(test_case): + """ + Encode test case name to escape commas. Avoids that test case names including commas + are interpreted as several test cases in the comma separated list of enabled test cases + included in the runner_cfg string. + """ + if test_case is not None: + return test_case.replace(',', ',,') + + return None + + +def encode_dict(dictionary): + """ + Encode dictionary for custom VHDL dictionary parser + """ + def escape(value): + return value.replace(':', '::').replace(',', ',,') + + encoded = [] + for key in sorted(dictionary.keys()): + value = dictionary[key] + encoded.append("%s : %s" % (escape(key), + escape(encode_dict_value(value)))) + return ",".join(encoded) + + +def encode_dict_value(value): # pylint: disable=missing-docstring + if isinstance(value, bool): + return str(value).lower() + + return str(value) + + +def _full_name(test_suite_name, test_case_name): + return test_suite_name + "." + test_case_name + + +def get_result_file_name(output_path): + return join(output_path, "vunit_results") diff --git a/vunit/ui.py b/vunit/ui.py index 5e4f6f915..73520f2f8 100644 --- a/vunit/ui.py +++ b/vunit/ui.py @@ -1,2019 +1,2019 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -# pylint: disable=too-many-lines - -""" -.. autoclass:: vunit.ui.VUnit() - :members: - :exclude-members: add_preprocessor, - add_builtins - -.. autoclass:: vunit.ui.Library() - :members: - :exclude-members: package - -.. autoclass:: vunit.ui.SourceFileList() - :members: - -.. autoclass:: vunit.ui.SourceFile() - :members: - -.. autoclass:: vunit.ui.TestBench() - :members: - -.. autoclass:: vunit.ui.Test() - :members: - -.. autoclass:: vunit.ui.Results() - :members: - -.. _compile_options: - -Compilation Options -------------------- -Compilation options allow customization of compilation behavior. Since simulators have -differing options available, generic options may be specified through this interface. -The following compilation options are known. - -``ghdl.flags`` - Extra arguments passed to ``ghdl -a`` command during compilation. - Must be a list of strings. - -``incisive.irun_vhdl_flags`` - Extra arguments passed to the Incisive ``irun`` command when compiling VHDL files. - Must be a list of strings. - -``incisive.irun_verilog_flags`` - Extra arguments passed to the Incisive ``irun`` command when compiling Verilog files. - Must be a list of strings. - -``modelsim.vcom_flags`` - Extra arguments passed to ModelSim ``vcom`` command. - Must be a list of strings. - -``modelsim.vlog_flags`` - Extra arguments passed to ModelSim ``vlog`` command. - Must be a list of strings. - -``rivierapro.vcom_flags`` - Extra arguments passed to Riviera PRO ``vcom`` command. - Must be a list of strings. - -``rivierapro.vlog_flags`` - Extra arguments passed to Riviera PRO ``vlog`` command. - Must be a list of strings. - -``activehdl.vcom_flags`` - Extra arguments passed to Active HDL ``vcom`` command. - Must be a list of strings. - -``activehdl.vlog_flags`` - Extra arguments passed to Active HDL ``vcom`` command. - Must be a list of strings. - -.. note:: - Only affects source files added *before* the option is set. - -.. _sim_options: - -Simulation Options -------------------- -Simulation options allow customization of simulation behavior. Since simulators have -differing options available, generic options may be specified through this interface. -The following simulation options are known. - -``vhdl_assert_stop_level`` - Will stop a VHDL simulation for asserts on the provided severity level or higher. - Valid values are ``"warning"``, ``"error"``, and ``"failure"``. This option takes - precedence over the fail_on_warning attribute. - -``disable_ieee_warnings`` - Disable ieee warnings. Must be a boolean value. Default is False. - -.. _coverage: - -``enable_coverage`` - Enables code coverage collection during simulator for the run affected by the sim_option. - Must be a boolean value. Default is False. - - When coverage is enabled VUnit only takes the minimal steps required - to make the simulator creates an unique coverage file for the - simulation run. The VUnit users must still set :ref:`sim - ` and :ref:`compile ` options to - configure the simulator specific coverage options they want. The - reason for this to allow the VUnit users maximum control of their - coverage settings. - - An example of a ``run.py`` file using coverage can be found - :vunit_example:`here `. - - .. note: Supported by RivieraPRO and Modelsim/Questa simulators. - - -``pli`` - A list of PLI file names. - -``ghdl.flags`` - Extra arguments passed to ``ghdl --elab-run`` command *before* executable specific flags. Must be a list of strings. - Must be a list of strings. - -``incisive.irun_sim_flags`` - Extra arguments passed to the Incisive ``irun`` command when loading the design. - Must be a list of strings. - -``modelsim.vsim_flags`` - Extra arguments passed to ``vsim`` when loading the design. - Must be a list of strings. - -``modelsim.vsim_flags.gui`` - Extra arguments passed to ``vsim`` when loading the design in GUI - mode where it takes precedence over ``modelsim.vsim_flags``. - Must be a list of strings. - -``modelsim.init_files.after_load`` - A list of user defined DO/TCL-files that is sourced after the design has been loaded. - During script evaluation the ``vunit_tb_path`` variable is defined - as the path of the folder containing the test bench. - Must be a list of strings. - -``modelsim.init_file.gui`` - A user defined TCL-file that is sourced after the design has been loaded in the GUI. - For example this can be used to configure the waveform viewer. - During script evaluation the ``vunit_tb_path`` variable is defined - as the path of the folder containing the test bench. - Must be a string. - -``rivierapro.vsim_flags`` - Extra arguments passed to ``vsim`` when loading the design. - Must be a list of strings. - -``rivierapro.vsim_flags.gui`` - Extra arguments passed to ``vsim`` when loading the design in GUI - mode where it takes precedence over ``rivierapro.vsim_flags``. - Must be a list of strings. - -``rivierapro.init_files.after_load`` - A list of user defined DO/TCL-files that is sourced after the design has been loaded. - During script evaluation the ``vunit_tb_path`` variable is defined - as the path of the folder containing the test bench. - Must be a list of strings. - -``rivierapro.init_file.gui`` - A user defined TCL-file that is sourced after the design has been loaded in the GUI. - For example this can be used to configure the waveform viewer. - During script evaluation the ``vunit_tb_path`` variable is defined - as the path of the folder containing the test bench. - Must be a string. - -``activehdl.vsim_flags`` - Extra arguments passed to ``vsim`` when loading the design. - Must be a list of strings. - -``activehdl.vsim_flags.gui`` - Extra arguments passed to ``vsim`` when loading the design in GUI - mode where it takes precedence over ``activehdl.vsim_flags``. - Must be a list of strings. - -``activehdl.init_file.gui`` - A user defined TCL-file that is sourced after the design has been loaded in the GUI. - For example this can be used to configure the waveform viewer. - Must be a string. - -``ghdl.elab_flags`` - Extra elaboration flags passed to ``ghdl --elab-run``. - Must be a list of strings. - -``ghdl.sim_flags`` - Extra simulation flags passed to ``ghdl --elab-run``. - Must be a list of strings. - -``ghdl.elab_e`` - With ``--elaborate``, execute ``ghdl -e`` instead of ``ghdl --elab-run --no-run``. - Must be a boolean. - -``ghdl.gtkwave_script.gui`` - A user defined TCL-file that is sourced after the design has been loaded in the GUI. - For example this can be used to configure the waveform viewer. Must be a string. - There are currently limitations in the HEAD revision of GTKWave that prevent the - user from sourcing a list of scripts directly. The following is the current work - around to sourcing multiple user TCL-files: - ``source `` - -.. |compile_option| replace:: - The name of the compile option (See :ref:`Compilation options `) - -.. |simulation_options| replace:: - The name of the simulation option (See :ref:`Simulation options `) - -.. _configurations: - -Configurations --------------- -In VUnit Python API the name ``configuration`` is used to denote the -user controllable configuration of one test run such as -generic/parameter settings, simulation options as well as the -pre_config and post_check :ref:`callback functions `. -User :ref:`attributes ` can also be added as a part of a -configuration. - -Configurations can either be unique for each test case or must be -common for the entire test bench depending on the situation. For test -benches without test such as `tb_example` in the User Guide the -configuration is common for the entire test bench. For test benches -containing tests such as `tb_example_many` the configuration is done -for each test case. If the ``run_all_in_same_sim`` attribute has been used -configuration is performed at the test bench level even if there are -individual test within since they must run in the same simulation. - -In a VUnit all test benches and test cases are created with an unnamed default -configuration which is modified by different methods such as ``set_generic`` etc. -In addition to the unnamed default configuration multiple named configurations -can be derived from it by using the ``add_config`` method. The default -configuration is only run if there are no named configurations. - -.. |configurations| replace:: - :ref:`configurations ` -""" - - -from __future__ import print_function - -import csv -import sys -import traceback -import logging -import json -import os -from os.path import exists, abspath, join, basename, splitext, normpath, dirname -from glob import glob -from fnmatch import fnmatch -from vunit.database import PickledDataBase, DataBase -from vunit import ostools -from vunit.vunit_cli import VUnitCLI -from vunit.simulator_factory import SIMULATOR_FACTORY -from vunit.simulator_interface import (is_string_not_iterable, - SimulatorInterface) -from vunit.color_printer import (COLOR_PRINTER, - NO_COLOR_PRINTER) -from vunit.project import (Project, - file_type_of, - FILE_TYPES, - VERILOG_FILE_TYPES, - check_vhdl_standard) -from vunit.test_runner import TestRunner -from vunit.test_report import TestReport -from vunit.test_bench_list import TestBenchList -from vunit.exceptions import CompileError -from vunit.location_preprocessor import LocationPreprocessor -from vunit.check_preprocessor import CheckPreprocessor -from vunit.parsing.encodings import HDL_FILE_ENCODING -from vunit.builtins import (Builtins, - add_verilog_include_dir) -from vunit.com import codec_generator - -LOGGER = logging.getLogger(__name__) - - -class VUnit(object): # pylint: disable=too-many-instance-attributes, too-many-public-methods - """ - The public interface of VUnit - - :example: - - .. code-block:: python - - from vunit import VUnit - """ - - @classmethod - def from_argv(cls, argv=None, compile_builtins=True, vhdl_standard=None): - """ - Create VUnit instance from command line arguments. - - :param argv: Use explicit argv instead of actual command line argument - :param compile_builtins: Do not compile builtins. Used for VUnit internal testing. - :returns: A :class:`.VUnit` object instance - - :example: - - .. code-block:: python - - from vunit import VUnit - prj = VUnit.from_argv() - - """ - args = VUnitCLI().parse_args(argv=argv) - return cls.from_args(args, compile_builtins=compile_builtins, vhdl_standard=vhdl_standard) - - @classmethod - def from_args(cls, args, compile_builtins=True, vhdl_standard=None): - """ - Create VUnit instance from args namespace. - Intended for users who adds custom command line options. - See :class:`vunit.vunit_cli.VUnitCLI` class to learn about - adding custom command line options. - - :param args: The parsed argument namespace object - :param compile_builtins: Do not compile builtins. Used for VUnit internal testing. - :returns: A :class:`.VUnit` object instance - """ - - return cls(args, compile_builtins=compile_builtins, vhdl_standard=vhdl_standard) - - def __init__(self, args, compile_builtins=True, vhdl_standard=None): - self._args = args - self._configure_logging(args.log_level) - self._output_path = abspath(args.output_path) - - if args.no_color: - self._printer = NO_COLOR_PRINTER - else: - self._printer = COLOR_PRINTER - - def test_filter(name, attribute_names): - keep = any(fnmatch(name, pattern) for pattern in args.test_patterns) - - if args.with_attributes is not None: - keep = keep and set(args.with_attributes).issubset(attribute_names) - - if args.without_attributes is not None: - keep = keep and set(args.without_attributes).isdisjoint(attribute_names) - return keep - - self._test_filter = test_filter - self._vhdl_standard = select_vhdl_standard(vhdl_standard) - - self._external_preprocessors = [] - self._location_preprocessor = None - self._check_preprocessor = None - - self._simulator_class = SIMULATOR_FACTORY.select_simulator() - - # Use default simulator options if no simulator was present - if self._simulator_class is None: - simulator_class = SimulatorInterface - self._simulator_output_path = join(self._output_path, "none") - else: - simulator_class = self._simulator_class - self._simulator_output_path = join(self._output_path, simulator_class.name) - - self._create_output_path(args.clean) - - database = self._create_database() - self._project = Project( - database=database, - depend_on_package_body=simulator_class.package_users_depend_on_bodies) - - self._test_bench_list = TestBenchList(database=database) - - self._builtins = Builtins(self, self._vhdl_standard, simulator_class) - if compile_builtins: - self.add_builtins() - - def _create_database(self): - """ - Create a persistent database to store expensive parse results - - Check for Python version used to create the database is the - same as the running python instance or re-create - """ - project_database_file_name = join(self._output_path, "project_database") - create_new = False - key = b"version" - version = str((9, sys.version)).encode() - database = None - try: - database = DataBase(project_database_file_name) - create_new = (key not in database) or (database[key] != version) - except KeyboardInterrupt: - raise KeyboardInterrupt - except: # pylint: disable=bare-except - traceback.print_exc() - create_new = True - - if create_new: - database = DataBase(project_database_file_name, new=True) - database[key] = version - - return PickledDataBase(database) - - @staticmethod - def _configure_logging(log_level): - """ - Configure logging based on log_level string - """ - level = getattr(logging, log_level.upper()) - logging.basicConfig(filename=None, format='%(levelname)7s - %(message)s', level=level) - - def add_external_library(self, library_name, path, vhdl_standard=None): - """ - Add an externally compiled library as a black-box - - :param library_name: The name of the external library - :param path: The path to the external library directory - :param vhdl_standard: The VHDL standard used to compile files into this library, - if None the VUNIT_VHDL_STANDARD environment variable is used - :returns: The created :class:`.Library` object - - :example: - - .. code-block:: python - - prj.add_external_library("unisim", "path/to/unisim/") - - """ - if vhdl_standard is None: - vhdl_standard = self._vhdl_standard - - self._project.add_library(library_name, abspath(path), vhdl_standard, is_external=True) - return self.library(library_name) - - def add_source_files_from_csv(self, project_csv_path, vhdl_standard=None): - """ - Add a project configuration, mapping all the libraries and files - - :param project_csv_path: path to csv project specification, each line contains the name - of the library and the path to one file 'lib_name,filename' - note that all filenames are relative to the parent folder of the - csv file - :param vhdl_standard: The VHDL standard used to compile file into this library, - if None, the VUNIT_VHDL_STANDARD environment variable is used - :returns: A list of files (:class `.SourceFileList`) that were added - - """ - if vhdl_standard is None: - vhdl_standard = self._vhdl_standard - - libs = set() - files = SourceFileList(list()) - - with open(project_csv_path) as csv_path_file: - for row in csv.reader(csv_path_file): - if len(row) == 2: - lib_name = row[0].strip() - no_normalized_file = row[1].strip() - file_name_ = normpath(join(dirname(project_csv_path), no_normalized_file)) - lib = self.library(lib_name) if lib_name in libs else self.add_library(lib_name) - libs.add(lib_name) - file_ = lib.add_source_file(file_name_) - files.append(file_) - elif len(row) > 2: - LOGGER.error("More than one library and one file in csv description") - return files - - def add_library(self, library_name, vhdl_standard=None, allow_duplicate=False): - """ - Add a library managed by VUnit. - - :param library_name: The name of the library - :param vhdl_standard: The VHDL standard used to compile files into this library, - if None the VUNIT_VHDL_STANDARD environment variable is used - :param allow_duplicate: Set to True to allow the same library - to be added multiple times. Subsequent additions will just - return the previously created library. - :returns: The created :class:`.Library` object - - :example: - - .. code-block:: python - - library = prj.add_library("lib") - - """ - if vhdl_standard is None: - vhdl_standard = self._vhdl_standard - - path = join(self._simulator_output_path, "libraries", library_name) - if not self._project.has_library(library_name): - self._project.add_library(library_name, abspath(path), vhdl_standard) - elif not allow_duplicate: - raise ValueError("Library %s already added. Use allow_duplicate to ignore this error." % library_name) - return self.library(library_name) - - def library(self, library_name): - """ - Get a library - - :param library_name: The name of the library - :returns: A :class:`.Library` object - """ - if not self._project.has_library(library_name): - raise KeyError(library_name) - return Library(library_name, self, self._project, self._test_bench_list) - - def set_attribute(self, name, value, allow_empty=False): - """ - Set a value of attribute in all |configurations| - - :param name: The name of the attribute - :param value: The value of the attribute - :param allow_empty: To disable an error when no test benches were found - - :example: - - .. code-block:: python - - prj.set_attribute(".foo", "bar") - - .. note:: - Only affects test benches added *before* the attribute is set. - """ - test_benches = self._test_bench_list.get_test_benches() - for test_bench in check_not_empty(test_benches, allow_empty, "No test benches found"): - test_bench.set_attribute(name, value) - - def set_generic(self, name, value, allow_empty=False): - """ - Set a value of generic in all |configurations| - - :param name: The name of the generic - :param value: The value of the generic - :param allow_empty: To disable an error when no test benches were found - - :example: - - .. code-block:: python - - prj.set_generic("data_width", 16) - - .. note:: - Only affects test benches added *before* the generic is set. - """ - test_benches = self._test_bench_list.get_test_benches() - for test_bench in check_not_empty(test_benches, allow_empty, "No test benches found"): - test_bench.set_generic(name.lower(), value) - - def set_parameter(self, name, value, allow_empty=False): - """ - Set value of parameter in all |configurations| - - :param name: The name of the parameter - :param value: The value of the parameter - :param allow_empty: To disable an error when no test benches were found - - :example: - - .. code-block:: python - - prj.set_parameter("data_width", 16) - - .. note:: - Only affects test benches added *before* the parameter is set. - """ - test_benches = self._test_bench_list.get_test_benches() - for test_bench in check_not_empty(test_benches, allow_empty, "No test benches found"): - test_bench.set_generic(name, value) - - def set_sim_option(self, name, value, allow_empty=False, overwrite=True): - """ - Set simulation option in all |configurations| - - :param name: |simulation_options| - :param value: The value of the simulation option - :param allow_empty: To disable an error when no test benches were found - :param overwrite: To overwrite the option or append to the existing value - - :example: - - .. code-block:: python - - prj.set_sim_option("ghdl.flags", ["--no-vital-checks"]) - - .. note:: - Only affects test benches added *before* the option is set. - """ - test_benches = self._test_bench_list.get_test_benches() - for test_bench in check_not_empty(test_benches, allow_empty, "No test benches found"): - test_bench.set_sim_option(name, value, overwrite) - - def set_compile_option(self, name, value, allow_empty=False): - """ - Set compile option of all files - - :param name: |compile_option| - :param value: The value of the compile option - :param allow_empty: To disable an error when no source files were found - - :example: - - .. code-block:: python - - prj.set_compile_option("ghdl.flags", ["--no-vital-checks"]) - - - .. note:: - Only affects files added *before* the option is set. - """ - source_files = self._project.get_source_files_in_order() - for source_file in check_not_empty(source_files, allow_empty, "No source files found"): - source_file.set_compile_option(name, value) - - def add_compile_option(self, name, value, allow_empty=False): - """ - Add compile option to all files - - :param name: |compile_option| - :param value: The value of the compile option - :param allow_empty: To disable an error when no source files were found - - .. note:: - Only affects files added *before* the option is set. - """ - source_files = self._project.get_source_files_in_order() - for source_file in check_not_empty(source_files, allow_empty, "No source files found"): - source_file.add_compile_option(name, value) - - def get_source_file(self, file_name, library_name=None): - """ - Get a source file - - :param file_name: The name of the file as a relative or absolute path - :param library_name: The name of a specific library to search if not all libraries - :returns: A :class:`.SourceFile` object - """ - - files = self.get_source_files(file_name, library_name, allow_empty=True) - if len(files) > 1: - raise ValueError("Found file named '%s' in multiple-libraries, " - "add explicit library_name." % file_name) - if not files: - if library_name is None: - raise ValueError("Found no file named '%s'" % file_name) - - raise ValueError("Found no file named '%s' in library '%s'" - % (file_name, library_name)) - return files[0] - - def get_source_files(self, pattern="*", library_name=None, allow_empty=False): - """ - Get a list of source files - - :param pattern: A wildcard pattern matching either an absolute or relative path - :param library_name: The name of a specific library to search if not all libraries - :param allow_empty: To disable an error if no files matched the pattern - :returns: A :class:`.SourceFileList` object - """ - results = [] - for source_file in self._project.get_source_files_in_order(): - if library_name is not None: - if source_file.library.name != library_name: - continue - - if not (fnmatch(abspath(source_file.name), pattern) - or fnmatch(ostools.simplify_path(source_file.name), pattern)): - continue - - results.append(SourceFile(source_file, self._project, self)) - - check_not_empty(results, allow_empty, - ("Pattern %r did not match any file" % pattern) - + (("within library %s" % library_name) if library_name is not None else "")) - - return SourceFileList(results) - - def add_source_files(self, # pylint: disable=too-many-arguments - pattern, library_name, preprocessors=None, include_dirs=None, defines=None, allow_empty=False, - vhdl_standard=None, no_parse=False, file_type=None): - """ - Add source files matching wildcard pattern to library - - :param pattern: A wildcard pattern matching the files to add or a list of files - :param library_name: The name of the library to add files into - :param include_dirs: A list of include directories - :param defines: A dictionary containing Verilog defines to be set - :param allow_empty: To disable an error if no files matched the pattern - :param vhdl_standard: The VHDL standard used to compile these files, - if None VUNIT_VHDL_STANDARD environment variable is used - :param no_parse: Do not parse file(s) for dependency or test scanning purposes - :param file_type: The type of the file; ``"vhdl"``, ``"verilog"`` or ``"systemverilog"``. - Auto-detected by default when set to ``None``. - :returns: A list of files (:class:`.SourceFileList`) which were added - - :example: - - .. code-block:: python - - prj.add_source_files("*.vhd", "lib") - - """ - return self.library(library_name).add_source_files(pattern=pattern, - preprocessors=preprocessors, - include_dirs=include_dirs, - defines=defines, - allow_empty=allow_empty, - vhdl_standard=vhdl_standard, - no_parse=no_parse, - file_type=file_type) - - def add_source_file(self, # pylint: disable=too-many-arguments - file_name, library_name, preprocessors=None, include_dirs=None, defines=None, - vhdl_standard=None, no_parse=False, file_type=None): - """ - Add source file to library - - :param file_name: The name of the file - :param library_name: The name of the library to add the file into - :param include_dirs: A list of include directories - :param defines: A dictionary containing Verilog defines to be set - :param vhdl_standard: The VHDL standard used to compile this file, - if None VUNIT_VHDL_STANDARD environment variable is used - :param no_parse: Do not parse file for dependency or test scanning purposes - :param file_type: The type of the file; ``"vhdl"``, ``"verilog"`` or ``"systemverilog"``. - Auto-detected by default when set to ``None``. - :returns: The :class:`.SourceFile` which was added - - :example: - - .. code-block:: python - - prj.add_source_file("file.vhd", "lib") - - """ - return self.library(library_name).add_source_file(file_name=file_name, - preprocessors=preprocessors, - include_dirs=include_dirs, - defines=defines, - vhdl_standard=vhdl_standard, - no_parse=no_parse, - file_type=file_type) - - def _preprocess(self, library_name, file_name, preprocessors): - """ - Preprocess file_name within library_name using explicit preprocessors - if preprocessors is None then use implicit globally defined processors - """ - # @TODO dependency checking etc... - - if preprocessors is None: - preprocessors = [self._location_preprocessor, self._check_preprocessor] - preprocessors = [p for p in preprocessors if p is not None] - preprocessors = self._external_preprocessors + preprocessors - - if not preprocessors: - return file_name - - try: - code = ostools.read_file(file_name, encoding=HDL_FILE_ENCODING) - for preprocessor in preprocessors: - code = preprocessor.run(code, basename(file_name)) - except KeyboardInterrupt: - raise KeyboardInterrupt - except: # pylint: disable=bare-except - traceback.print_exc() - LOGGER.error("Failed to preprocess %s", file_name) - return file_name - else: - pp_file_name = join(self._preprocessed_path, library_name, basename(file_name)) - - idx = 1 - while ostools.file_exists(pp_file_name): - LOGGER.debug("Preprocessed file exists '%s', adding prefix", pp_file_name) - pp_file_name = join(self._preprocessed_path, - library_name, "%i_%s" % (idx, basename(file_name))) - idx += 1 - - ostools.write_file(pp_file_name, code, encoding=HDL_FILE_ENCODING) - return pp_file_name - - def add_preprocessor(self, preprocessor): - """ - Add a custom preprocessor to be used on all files, must be called before adding any files - """ - self._external_preprocessors.append(preprocessor) - - def enable_location_preprocessing(self, additional_subprograms=None, exclude_subprograms=None): - """ - Inserts file name and line number information into VUnit check and log subprograms calls. Custom - subprograms can also be added. Must be called before adding any files. - - :param additional_subprograms: List of custom subprograms to add the line_num and file_name parameters to. - :param exclude_subprograms: List of VUnit subprograms to exclude from location preprocessing. Used to \ -avoid location preprocessing of other functions sharing name with a VUnit log or check subprogram. - - :example: - - .. code-block:: python - - prj.enable_location_preprocessing(additional_subprograms=['my_check'], - exclude_subprograms=['log']) - - """ - preprocessor = LocationPreprocessor() - if additional_subprograms is not None: - for subprogram in additional_subprograms: - preprocessor.add_subprogram(subprogram) - - if exclude_subprograms is not None: - for subprogram in exclude_subprograms: - preprocessor.remove_subprogram(subprogram) - self._location_preprocessor = preprocessor - - def enable_check_preprocessing(self): - """ - Inserts error context information into VUnit check_relation calls - """ - self._check_preprocessor = CheckPreprocessor() - - def main(self, post_run=None): - """ - Run vunit main function and exit - - :param post_run: A callback function which is called after - running tests. The function must accept a single `results` - argument which is an instance of :class:`.Results` - """ - try: - all_ok = self._main(post_run) - except KeyboardInterrupt: - sys.exit(1) - except CompileError: - sys.exit(1) - except SystemExit: - sys.exit(1) - except: # pylint: disable=bare-except - if self._args.dont_catch_exceptions: - raise - traceback.print_exc() - sys.exit(1) - - if (not all_ok) and (not self._args.exit_0): - sys.exit(1) - - sys.exit(0) - - def _create_tests(self, simulator_if): - """ - Create the test cases - """ - self._test_bench_list.warn_when_empty() - test_list = self._test_bench_list.create_tests(simulator_if, self._args.elaborate) - test_list.keep_matches(self._test_filter) - return test_list - - def _main(self, post_run): - """ - Base vunit main function without performing exit - """ - - if self._args.export_json is not None: - return self._main_export_json(self._args.export_json) - - if self._args.list: - return self._main_list_only() - - if self._args.files: - return self._main_list_files_only() - - if self._args.compile: - return self._main_compile_only() - - all_ok = self._main_run(post_run) - return all_ok - - def _create_simulator_if(self): - """ - Create new simulator instance - """ - - if self._simulator_class is None: - LOGGER.error( - "No available simulator detected.\n" - "Simulator binary folder must be available in PATH environment variable.\n" - "Simulator binary folder can also be set the in VUNIT__PATH environment variable.\n") - exit(1) - - if not exists(self._simulator_output_path): - os.makedirs(self._simulator_output_path) - - return self._simulator_class.from_args(args=self._args, - output_path=self._simulator_output_path) - - def _main_run(self, post_run): - """ - Main with running tests - """ - simulator_if = self._create_simulator_if() - test_list = self._create_tests(simulator_if) - self._compile(simulator_if) - print() - - start_time = ostools.get_time() - report = TestReport(printer=self._printer) - - try: - self._run_test(test_list, report) - except KeyboardInterrupt: - print() - LOGGER.debug("_main: Caught Ctrl-C shutting down") - finally: - del test_list - - report.set_real_total_time(ostools.get_time() - start_time) - report.print_str() - - if post_run is not None: - post_run(results=Results(simulator_if)) - - del simulator_if - - if self._args.xunit_xml is not None: - xml = report.to_junit_xml_str(self._args.xunit_xml_format) - ostools.write_file(self._args.xunit_xml, xml) - - return report.all_ok() - - def _main_list_only(self): - """ - Main function when only listing test cases - """ - test_list = self._create_tests(simulator_if=None) - for test_name in test_list.test_names: - print(test_name) - print("Listed %i tests" % test_list.num_tests) - return True - - def _main_export_json(self, json_file_name): # pylint: disable=too-many-locals - """ - Main function when exporting to JSON - """ - - file_objects = self.get_compile_order() - files = [] - for source_file in file_objects: - files.append(dict(file_name=abspath(source_file.name), - library_name=source_file.library.name)) - - tests = [] - for test_suite in self._create_tests(simulator_if=None): - test_information = test_suite.test_information - test_configuration = test_suite.test_configuration - for name in test_suite.test_names: - info = test_information[name] - config = test_configuration[name] - - attributes = {} - for attr in info.attributes: - attributes[attr.name] = attr.value - - attributes.update(config.attributes) - - tests.append(dict(name=name, - location=dict(file_name=info.location.file_name, - offset=info.location.offset, - length=info.location.length), - attributes=attributes)) - - json_data = dict( - # The semantic version (https://semver.org/) of the JSON export data format - export_format_version=dict(major=1, minor=0, patch=0), - - # The set of files added to the project - files=files, - - # The list of all tests - tests=tests) - - with open(json_file_name, "w") as fptr: - json.dump(json_data, - fptr, - sort_keys=True, - indent=4, - separators=(',', ': ')) - - return True - - def _main_list_files_only(self): - """ - Main function when only listing files - """ - files = self.get_compile_order() - for source_file in files: - print("%s, %s" % (source_file.library.name, source_file.name)) - print("Listed %i files" % len(files)) - return True - - def _main_compile_only(self): - """ - Main function when only compiling - """ - simulator_if = self._create_simulator_if() - self._compile(simulator_if) - return True - - def _create_output_path(self, clean): - """ - Create or re-create the output path if necessary - """ - if clean: - ostools.renew_path(self._output_path) - elif not exists(self._output_path): - os.makedirs(self._output_path) - - ostools.renew_path(self._preprocessed_path) - - @property - def vhdl_standard(self): - return self._vhdl_standard - - @property - def _preprocessed_path(self): - return join(self._output_path, "preprocessed") - - @property - def codecs_path(self): - return join(self._output_path, "codecs") - - def _compile(self, simulator_if): - """ - Compile entire project - """ - simulator_if.compile_project(self._project, - continue_on_error=self._args.keep_compiling, - printer=self._printer) - - def _run_test(self, test_cases, report): - """ - Run the test suites and return the report - """ - - if self._args.verbose: - verbosity = TestRunner.VERBOSITY_VERBOSE - elif self._args.quiet: - verbosity = TestRunner.VERBOSITY_QUIET - else: - verbosity = TestRunner.VERBOSITY_NORMAL - - runner = TestRunner(report, - join(self._output_path, "test_output"), - verbosity=verbosity, - num_threads=self._args.num_threads, - fail_fast=self._args.fail_fast, - dont_catch_exceptions=self._args.dont_catch_exceptions, - no_color=self._args.no_color) - runner.run(test_cases) - - def add_builtins(self): - """ - Add vunit VHDL builtin libraries - """ - self._builtins.add_vhdl_builtins() - - def add_com(self): - """ - Add communication package - """ - self._builtins.add("com") - - def add_array_util(self): - """ - Add array util - """ - self._builtins.add("array_util") - - def add_random(self): - """ - Add random - """ - self._builtins.add("random") - - def add_verification_components(self): - """ - Add verification component library - """ - self._builtins.add("verification_components") - - def add_osvvm(self): - """ - Add osvvm library - """ - self._builtins.add("osvvm") - - def add_json4vhdl(self): - """ - Add JSON-for-VHDL library - """ - self._builtins.add("json4vhdl") - - def get_compile_order(self, source_files=None): - """ - Get the compile order of all or specific source files and - their dependencies. - - A dependency of file **A** in terms of compile order is any - file **B** which **must** be successfully compiled before **A** - can be successfully compiled. - - This is **not** the same as all files required to successfully elaborate **A**. - For example using component instantiation in VHDL there is no - compile order dependency but the component instance will not - elaborate if there is no binding component. - - :param source_files: A list of :class:`.SourceFile` objects or `None` meaing all - :returns: A list of :class:`.SourceFile` objects in compile order. - """ - if source_files is None: - source_files = self.get_source_files(allow_empty=True) - - target_files = [source_file._source_file # pylint: disable=protected-access - for source_file in source_files] - source_files = self._project.get_dependencies_in_compile_order(target_files) - return SourceFileList([SourceFile(source_file, self._project, self) - for source_file in source_files]) - - def get_implementation_subset(self, source_files): - """ - Get the subset of files which are required to successfully - elaborate the list of input files without any missing - dependencies. - - :param source_files: A list of :class:`.SourceFile` objects - :returns: A list of :class:`.SourceFile` objects which is the implementation subset. - """ - target_files = [source_file._source_file # pylint: disable=protected-access - for source_file in source_files] - source_files = self._project.get_dependencies_in_compile_order( - target_files, - implementation_dependencies=True) - return SourceFileList([SourceFile(source_file, self._project, self) - for source_file in source_files]) - - -class Library(object): - """ - User interface of a library - """ - def __init__(self, library_name, parent, project, test_bench_list): - self._library_name = library_name - self._parent = parent - self._project = project - self._test_bench_list = test_bench_list - - @property - def name(self): - """ - The name of the library - """ - return self._library_name - - def set_generic(self, name, value, allow_empty=False): - """ - Set a value of generic within all |configurations| of test benches and tests this library - - :param name: The name of the generic - :param value: The value of the generic - :param allow_empty: To disable an error when no test benches were found - - :example: - - .. code-block:: python - - lib.set_generic("data_width", 16) - - .. note:: - Only affects test benches added *before* the generic is set. - """ - for test_bench in self.get_test_benches(allow_empty=allow_empty): - test_bench.set_generic(name.lower(), value) - - def set_parameter(self, name, value, allow_empty=False): - """ - Set a value of parameter within all |configurations| of test benches and tests this library - - :param name: The name of the parameter - :param value: The value of the parameter - :param allow_empty: To disable an error when no test benches were found - - :example: - - .. code-block:: python - - lib.set_parameter("data_width", 16) - - .. note:: - Only affects test benches added *before* the parameter is set. - """ - for test_bench in self.get_test_benches(allow_empty=allow_empty): - test_bench.set_generic(name, value) - - def set_sim_option(self, name, value, allow_empty=False, overwrite=True): - """ - Set simulation option within all |configurations| of test benches and tests this library - - :param name: |simulation_options| - :param value: The value of the simulation option - :param allow_empty: To disable an error when no test benches were found - :param overwrite: To overwrite the option or append to the existing value - - :example: - - .. code-block:: python - - lib.set_sim_option("ghdl.flags", ["--no-vital-checks"]) - - .. note:: - Only affects test benches added *before* the option is set. - """ - for test_bench in self.get_test_benches(allow_empty=allow_empty): - test_bench.set_sim_option(name, value, overwrite) - - def set_compile_option(self, name, value, allow_empty=False): - """ - Set compile option for all files within the library - - :param name: |compile_option| - :param value: The value of the compile option - :param allow_empty: To disable an error when no source files were found - - :example: - - .. code-block:: python - - lib.set_compile_option("ghdl.flags", ["--no-vital-checks"]) - - - .. note:: - Only affects files added *before* the option is set. - """ - for source_file in self.get_source_files(allow_empty=allow_empty): - source_file.set_compile_option(name, value) - - def add_compile_option(self, name, value, allow_empty=False): - """ - Add compile option to all files within the library - - :param name: |compile_option| - :param value: The value of the compile option - :param allow_empty: To disable an error when no source files were found - - .. note:: - Only affects files added *before* the option is set. - """ - for source_file in self.get_source_files(allow_empty=allow_empty): - source_file.add_compile_option(name, value) - - def get_source_file(self, file_name): - """ - Get a source file within this library - - :param file_name: The name of the file as a relative or absolute path - - :returns: A :class:`.SourceFile` object - """ - return self._parent.get_source_file(file_name, self._library_name) - - def get_source_files(self, pattern="*", allow_empty=False): - """ - Get a list of source files within this libary - - :param pattern: A wildcard pattern matching either an absolute or relative path - :param allow_empty: To disable an error if no files matched the pattern - :returns: A :class:`.SourceFileList` object - """ - return self._parent.get_source_files(pattern, self._library_name, allow_empty) - - def add_source_files(self, # pylint: disable=too-many-arguments - pattern, preprocessors=None, include_dirs=None, defines=None, allow_empty=False, - vhdl_standard=None, no_parse=False, file_type=None): - """ - Add source files matching wildcard pattern to library - - :param pattern: A wildcard pattern match the files to add - :param include_dirs: A list of include directories - :param defines: A dictionary containing Verilog defines to be set - :param allow_empty: To disable an error if no files matched the pattern - :param vhdl_standard: The VHDL standard used to compile these files, if None library default is used - :param no_parse: Do not parse file(s) for dependency or test scanning purposes - :param file_type: The type of the file; ``"vhdl"``, ``"verilog"`` or ``"systemverilog"``. - Auto-detected by default when set to ``None``. - :returns: A list of files (:class:`.SourceFileList`) which were added - - :example: - - .. code-block:: python - - library.add_source_files("*.vhd") - - """ - if is_string_not_iterable(pattern): - patterns = [pattern] - else: - patterns = pattern - - file_names = [] - for pattern_instance in patterns: - new_file_names = glob(pattern_instance) - check_not_empty(new_file_names, allow_empty, "Pattern %r did not match any file" % pattern_instance) - file_names += new_file_names - - return SourceFileList(source_files=[ - self.add_source_file(file_name, preprocessors, include_dirs, defines, vhdl_standard, - no_parse=no_parse, file_type=file_type) - for file_name in file_names]) - - def add_source_file(self, # pylint: disable=too-many-arguments - file_name, preprocessors=None, include_dirs=None, defines=None, - vhdl_standard=None, no_parse=False, file_type=None): - """ - Add source file to library - - :param file_name: The name of the file - :param include_dirs: A list of include directories - :param defines: A dictionary containing Verilog defines to be set - :param vhdl_standard: The VHDL standard used to compile this file, if None library default is used - :param no_parse: Do not parse file for dependency or test scanning purposes - :param file_type: The type of the file; ``"vhdl"``, ``"verilog"`` or ``"systemverilog"``. - Auto-detected by default when set to ``None``. - :returns: The :class:`.SourceFile` which was added - - :example: - - .. code-block:: python - - library.add_source_file("file.vhd") - - """ - file_name = abspath(file_name) - - if file_type is None: - file_type = file_type_of(file_name) - elif file_type not in FILE_TYPES: - raise ValueError("file_type %r not in %r" % (file_type, FILE_TYPES)) - - if file_type in VERILOG_FILE_TYPES: - include_dirs = include_dirs if include_dirs is not None else [] - include_dirs = add_verilog_include_dir(include_dirs) - - new_file_name = self._parent._preprocess( # pylint: disable=protected-access - self._library_name, file_name, preprocessors) - - source_file = self._project.add_source_file(new_file_name, - self._library_name, - file_type=file_type, - include_dirs=include_dirs, - defines=defines, - vhdl_standard=vhdl_standard, - no_parse=no_parse) - # To get correct tb_path generic - source_file.original_name = file_name - - self._test_bench_list.add_from_source_file(source_file) - - return SourceFile(source_file, - self._project, - self._parent) - - def package(self, name): - """ - Get a package within the library - """ - library = self._project.get_library(self._library_name) - design_unit = library.primary_design_units.get(name) - - if design_unit is None: - raise KeyError(name) - if design_unit.unit_type != 'package': - raise KeyError(name) - - return PackageFacade(self._parent, self._library_name, name, design_unit) - - def entity(self, name): - """ - Get an entity within the library - - :param name: The name of the entity - :returns: A :class:`.TestBench` object - :raises: KeyError - """ - name = name.lower() - library = self._project.get_library(self._library_name) - if not library.has_entity(name): - raise KeyError(name) - - return self.test_bench(name) - - def module(self, name): - """ - Get a module within the library - - :param name: The name of the module - :returns: A :class:`.TestBench` object - :raises: KeyError - """ - library = self._project.get_library(self._library_name) - if name not in library.modules: - raise KeyError(name) - - return self.test_bench(name) - - def test_bench(self, name): - """ - Get a test bench within this library - - :param name: The name of the test bench - :returns: A :class:`.TestBench` object - :raises: KeyError - """ - name = name.lower() - - return TestBench(self._test_bench_list.get_test_bench(self._library_name, name), self) - - def get_test_benches(self, pattern="*", allow_empty=False): - """ - Get a list of test benches - - :param pattern: A wildcard pattern matching the test_bench name - :param allow_empty: To disable an error when no test benches were found - :returns: A list of :class:`.TestBench` objects - """ - results = [] - for test_bench in self._test_bench_list.get_test_benches_in_library(self._library_name): - if not fnmatch(abspath(test_bench.name), pattern): - continue - - results.append(TestBench(test_bench, self)) - - return check_not_empty(results, allow_empty, - "No test benches found within library %s" % self._library_name) - - -class TestBench(object): - """ - User interface of a test bench. - A test bench consists of one or more :class:`.Test` cases. Setting options for a test - bench will apply that option all test cases belonging to that test bench. - """ - def __init__(self, test_bench, library): - self._test_bench = test_bench - self._library = library - - @property - def name(self): - """ - :returns: The entity or module name of the test bench - """ - return self._test_bench.name - - @property - def library(self): - """ - :returns: The library that contains this test bench - """ - return self._library - - def set_attribute(self, name, value): - """ - Set a value of attribute within all |configurations| of this test bench or test cases within it - - :param name: The name of the attribute - :param value: The value of the atrribute - - :example: - - .. code-block:: python - - test_bench.set_attribute(".foo", "bar") - - """ - self._test_bench.set_attribute(name, value) - - def set_generic(self, name, value): - """ - Set a value of generic within all |configurations| of this test bench or test cases within it - - :param name: The name of the generic - :param value: The value of the generic - - :example: - - .. code-block:: python - - test_bench.set_generic("data_width", 16) - - """ - self._test_bench.set_generic(name.lower(), value) - - def set_parameter(self, name, value): - """ - Set a value of parameter within all |configurations| of this test bench or test cases within it - - :param name: The name of the parameter - :param value: The value of the parameter - - :example: - - .. code-block:: python - - test_bench.set_parameter("data_width", 16) - - """ - self._test_bench.set_generic(name, value) - - def set_sim_option(self, name, value, overwrite=True): - """ - Set simulation option within all |configurations| of this test bench or test cases within it - - :param name: |simulation_options| - :param value: The value of the simulation option - :param overwrite: To overwrite the option or append to the existing value - - :example: - - .. code-block:: python - - test_bench.set_sim_option("ghdl.flags", ["--no-vital-checks"]) - - """ - self._test_bench.set_sim_option(name, value, overwrite) - - def set_pre_config(self, value): - """ - Set :ref:`pre_config ` function of all - |configurations| of this test bench or test cases within it - - :param value: The pre_config function - """ - self._test_bench.set_pre_config(value) - - def set_post_check(self, value): - """ - Set :ref:`post_check ` function of all - |configurations| of this test bench or test cases within it - - :param value: The post_check function - """ - self._test_bench.set_post_check(value) - - def add_config(self, # pylint: disable=too-many-arguments - name, generics=None, parameters=None, pre_config=None, post_check=None, sim_options=None, - attributes=None): - """ - Add a configuration of this test bench or to all test cases within it by copying the default configuration. - - Multiple configuration may be added one after another. - If no |configurations| are added the default configuration is used. - - :param name: The name of the configuration. Will be added as a suffix on the test name - :param generics: A `dict` containing the generics to be set in addition to the default configuration - :param parameters: A `dict` containing the parameters to be set in addition to the default configuration - :param pre_config: A :ref:`callback function ` to be called before test execution. - :param post_check: A :ref:`callback function ` to be called after test execution. - :param sim_options: A `dict` containing the sim_options to be set in addition to the default configuration - :param attributes: A `dict` containing the attributes to be set in addition to the default configuration - - :example: - - Given a test bench that by default gives rise to the test - ``lib.test_bench`` and the following ``add_config`` calls: - - .. code-block:: python - - for data_width in range(14, 15+1): - for sign in [False, True]: - test_bench.add_config( - name="data_width=%s,sign=%s" % (data_width, sign), - generics=dict(data_width=data_width, sign=sign)) - - The following tests will be created: - - * ``lib.test_bench.data_width=14,sign=False`` - - * ``lib.test_bench.data_width=14,sign=True`` - - * ``lib.test_bench.data_width=15,sign=False`` - - * ``lib.test_bench.data_width=15,sign=True`` - - """ - generics = {} if generics is None else generics - generics = lower_generics(generics) - parameters = {} if parameters is None else parameters - generics.update(parameters) - attributes = {} if attributes is None else attributes - self._test_bench.add_config(name=name, - generics=generics, - pre_config=pre_config, - post_check=post_check, - sim_options=sim_options, - attributes=attributes) - - def test(self, name): - """ - Get a test within this test bench - - :param name: The name of the test - :returns: A :class:`.Test` object - """ - return Test(self._test_bench.get_test_case(name)) - - def get_tests(self, pattern="*"): - """ - Get a list of tests - - :param pattern: A wildcard pattern matching the test name - :returns: A list of :class:`.Test` objects - """ - results = [] - for test_case in self._test_bench.tests: - if not fnmatch(test_case.name, pattern): - continue - - results.append(Test(test_case)) - return results - - def scan_tests_from_file(self, file_name): - """ - Scan tests from another file than the one containg the test - bench. Useful for when the top level test bench does not - contain the tests. - - Such a structure is not the preferred way of doing things in - VUnit but this method exists to accommodate legacy needs. - - :param file_name: The name of another file to scan for tests - - .. warning:: - The nested module containing the tests needs to be given - the ``runner_cfg`` parameter or generic by the - instantiating top level test bench. The nested module - should not call its parameter or generic `runner_cfg` but - rather `nested_runner_cfg` to avoid the VUnit test scanner - detecting and running it as a test bench. In SystemVerilog - the ``NESTED_TEST_SUITE`` macro should be used instead of - the ``TEST_SUITE`` macro. - """ - self._test_bench.scan_tests_from_file(file_name) - - -class PackageFacade(object): - """ - User interface of a Package - """ - def __init__(self, parent, library_name, package_name, design_unit): - self._parent = parent - self._library_name = library_name - self._package_name = package_name - self._design_unit = design_unit - - def generate_codecs(self, codec_package_name=None, used_packages=None, output_file_name=None): - """ - Generates codecs for the datatypes in this Package - """ - if codec_package_name is None: - codec_package_name = self._package_name + '_codecs' - - if output_file_name is None: - codecs_path = join(self._parent.codecs_path, self._library_name) - file_extension = splitext(self._design_unit.source_file.name)[1] - output_file_name = join(codecs_path, codec_package_name + file_extension) - - codec_generator.generate_codecs(self._design_unit, - codec_package_name, - used_packages, - output_file_name) - - return self._parent.add_source_files(output_file_name, self._library_name) - - -class Test(object): - """ - User interface of a single test case - - """ - def __init__(self, test_case): - self._test_case = test_case - - @property - def name(self): - """ - :returns: the entity or module name of the test bench - """ - return self._test_case.name - - def add_config(self, # pylint: disable=too-many-arguments - name, generics=None, parameters=None, pre_config=None, post_check=None, sim_options=None, - attributes=None): - """ - Add a configuration to this test copying the default configuration. - - Multiple configuration may be added one after another. - If no |configurations| are added the default configuration is used. - - :param name: The name of the configuration. Will be added as a suffix on the test name - :param generics: A `dict` containing the generics to be set in addition to the default configuration. - :param parameters: A `dict` containing the parameters to be set in addition to the default configuration. - :param pre_config: A :ref:`callback function ` to be called before test execution. - :param post_check: A :ref:`callback function ` to be called after test execution. - :param sim_options: A `dict` containing the sim_options to be set in addition to the default configuration. - :param attributes: A `dict` containing the attributes to be set in addition to the default configuration. - - :example: - - Given the ``lib.test_bench.test`` test and the following ``add_config`` calls: - - .. code-block:: python - - for data_width in range(14, 15+1): - for sign in [False, True]: - test.add_config( - name="data_width=%s,sign=%s" % (data_width, sign), - generics=dict(data_width=data_width, sign=sign)) - - The following tests will be created: - - * ``lib.test_bench.data_width=14,sign=False.test`` - - * ``lib.test_bench.data_width=14,sign=True.test`` - - * ``lib.test_bench.data_width=15,sign=False.test`` - - * ``lib.test_bench.data_width=15,sign=True.test`` - - """ - generics = {} if generics is None else generics - generics = lower_generics(generics) - parameters = {} if parameters is None else parameters - generics.update(parameters) - attributes = {} if attributes is None else attributes - self._test_case.add_config(name=name, - generics=generics, - pre_config=pre_config, - post_check=post_check, - sim_options=sim_options, - attributes=attributes) - - def set_attribute(self, name, value): - """ - Set a value of attribute within all |configurations| of this test - - :param name: The name of the attribute - :param value: The value of the attribute - - :example: - - .. code-block:: python - - test.set_attribute(".foo", "bar") - - """ - self._test_case.set_attribute(name, value) - - def set_generic(self, name, value): - """ - Set a value of generic within all |configurations| of this test - - :param name: The name of the generic - :param value: The value of the generic - - :example: - - .. code-block:: python - - test.set_generic("data_width", 16) - - """ - self._test_case.set_generic(name.lower(), value) - - def set_parameter(self, name, value): - """ - Set a value of parameter within all |configurations| of this test - - :param name: The name of the parameter - :param value: The value of the parameter - - :example: - - .. code-block:: python - - test.set_parameter("data_width", 16) - - """ - self._test_case.set_generic(name, value) - - def set_sim_option(self, name, value, overwrite=True): - """ - Set simulation option within all |configurations| of this test - - :param name: |simulation_options| - :param value: The value of the simulation option - :param overwrite: To overwrite the option or append to the existing value - - :example: - - .. code-block:: python - - test.set_sim_option("ghdl.flags", ["--no-vital-checks"]) - - """ - self._test_case.set_sim_option(name, value, overwrite) - - def set_pre_config(self, value): - """ - Set :ref:`pre_config ` function of all |configurations| of this test - - :param value: The pre_config function - """ - self._test_case.set_pre_config(value) - - def set_post_check(self, value): - """ - Set :ref:`post_check ` function of all |configurations| of this test - - :param value: The post_check function - """ - self._test_case.set_post_check(value) - - -class SourceFileList(list): - """ - A list of :class:`.SourceFile` - """ - - def __init__(self, source_files): - list.__init__(self, source_files) - - def set_compile_option(self, name, value): - """ - Set compile option for all files in the list - - :param name: |compile_option| - :param value: The value of the compile option - - :example: - - .. code-block:: python - - files.set_compile_option("ghdl.flags", ["--no-vital-checks"]) - """ - for source_file in self: - source_file.set_compile_option(name, value) - - def add_compile_option(self, name, value): - """ - Add compile option to all files in the list - - :param name: |compile_option| - :param value: The value of the compile option - """ - for source_file in self: - source_file.add_compile_option(name, value) - - def add_dependency_on(self, source_file): - """ - Add manual dependency of these files on other file(s) - - :param source_file: The file(s) which this file depends on - - :example: - - .. code-block:: python - - other_file = lib.get_source_file("other_file.vhd") - files.add_dependency_on(other_file) - """ - for my_source_file in self: - my_source_file.add_dependency_on(source_file) - - -class SourceFile(object): - """ - A single file - """ - def __init__(self, source_file, project, ui): - self._source_file = source_file - self._project = project - self._ui = ui - - @property - def name(self): - """ - The name of the SourceFile - """ - return ostools.simplify_path(self._source_file.name) - - @property - def vhdl_standard(self): - """ - The VHDL standard applicable to the file, - None if not a VHDL file - """ - if self._source_file.file_type == "vhdl": - return self._source_file.get_vhdl_standard() - - return None - - @property - def library(self): - """ - The library of the source file - """ - return self._ui.library(self._source_file.library.name) - - def set_compile_option(self, name, value): - """ - Set compile option for this file - - :param name: |compile_option| - :param value: The value of the compile option - - :example: - - .. code-block:: python - - my_file.set_compile_option("ghdl.flags", ["--no-vital-checks"]) - """ - self._source_file.set_compile_option(name, value) - - def add_compile_option(self, name, value): - """ - Add compile option to this file - - :param name: |compile_option| - :param value: The value of the compile option - """ - self._source_file.add_compile_option(name, value) - - def get_compile_option(self, name): - """ - Return compile option of this file - - :param name: |compile_option| - """ - return self._source_file.get_compile_option(name) - - def add_dependency_on(self, source_file): - """ - Add manual dependency of this file other file(s) - - :param source_file: The file(s) which this file depends on - - :example: - - .. code-block:: python - - other_files = lib.get_source_files("*.vhd") - my_file.add_dependency_on(other_files) - """ - if isinstance(source_file, SourceFile): - private_source_file = source_file._source_file # pylint: disable=protected-access - self._project.add_manual_dependency(self._source_file, - depends_on=private_source_file) - elif hasattr(source_file, "__iter__"): - for element in source_file: - self.add_dependency_on(element) - else: - raise ValueError(source_file) - - -class Results(object): - """ - Gives access to results after running tests. - """ - - def __init__(self, simulator_if): - self._simulator_if = simulator_if - - def merge_coverage(self, file_name, args=None): - """ - Create a merged coverage report from the individual coverage files - - :param file_name: The resulting coverage file name. - :param args: The tool arguments for the merge command. Should be a list of strings. - """ - - self._simulator_if.merge_coverage(file_name=file_name, args=args) - - -def select_vhdl_standard(vhdl_standard=None): - """ - Select VHDL standard either from class initialization or according to environment variable VUNIT_VHDL_STANDARD - """ - if vhdl_standard is not None: - check_vhdl_standard(vhdl_standard, from_str="From class initialization") - else: - vhdl_standard = os.environ.get('VUNIT_VHDL_STANDARD', '2008') - check_vhdl_standard(vhdl_standard, from_str="VUNIT_VHDL_STANDARD environment variable") - - return vhdl_standard - - -def lower_generics(generics): - """ - Convert all generics names to lower case to match internal representation. - @TODO Maybe warn in case of conflict. VHDL forbids this though so the user will notice anyway. - """ - return dict((name.lower(), value) for name, value in generics.items()) - - -def check_not_empty(lst, allow_empty, error_msg): - """ - Raise ValueError if the list is empty unless allow_empty is True - Returns the list - """ - if (not allow_empty) and (not lst): - raise ValueError(error_msg - + ". Use allow_empty=True to avoid exception.") - return lst +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +# pylint: disable=too-many-lines + +""" +.. autoclass:: vunit.ui.VUnit() + :members: + :exclude-members: add_preprocessor, + add_builtins + +.. autoclass:: vunit.ui.Library() + :members: + :exclude-members: package + +.. autoclass:: vunit.ui.SourceFileList() + :members: + +.. autoclass:: vunit.ui.SourceFile() + :members: + +.. autoclass:: vunit.ui.TestBench() + :members: + +.. autoclass:: vunit.ui.Test() + :members: + +.. autoclass:: vunit.ui.Results() + :members: + +.. _compile_options: + +Compilation Options +------------------- +Compilation options allow customization of compilation behavior. Since simulators have +differing options available, generic options may be specified through this interface. +The following compilation options are known. + +``ghdl.flags`` + Extra arguments passed to ``ghdl -a`` command during compilation. + Must be a list of strings. + +``incisive.irun_vhdl_flags`` + Extra arguments passed to the Incisive ``irun`` command when compiling VHDL files. + Must be a list of strings. + +``incisive.irun_verilog_flags`` + Extra arguments passed to the Incisive ``irun`` command when compiling Verilog files. + Must be a list of strings. + +``modelsim.vcom_flags`` + Extra arguments passed to ModelSim ``vcom`` command. + Must be a list of strings. + +``modelsim.vlog_flags`` + Extra arguments passed to ModelSim ``vlog`` command. + Must be a list of strings. + +``rivierapro.vcom_flags`` + Extra arguments passed to Riviera PRO ``vcom`` command. + Must be a list of strings. + +``rivierapro.vlog_flags`` + Extra arguments passed to Riviera PRO ``vlog`` command. + Must be a list of strings. + +``activehdl.vcom_flags`` + Extra arguments passed to Active HDL ``vcom`` command. + Must be a list of strings. + +``activehdl.vlog_flags`` + Extra arguments passed to Active HDL ``vcom`` command. + Must be a list of strings. + +.. note:: + Only affects source files added *before* the option is set. + +.. _sim_options: + +Simulation Options +------------------- +Simulation options allow customization of simulation behavior. Since simulators have +differing options available, generic options may be specified through this interface. +The following simulation options are known. + +``vhdl_assert_stop_level`` + Will stop a VHDL simulation for asserts on the provided severity level or higher. + Valid values are ``"warning"``, ``"error"``, and ``"failure"``. This option takes + precedence over the fail_on_warning attribute. + +``disable_ieee_warnings`` + Disable ieee warnings. Must be a boolean value. Default is False. + +.. _coverage: + +``enable_coverage`` + Enables code coverage collection during simulator for the run affected by the sim_option. + Must be a boolean value. Default is False. + + When coverage is enabled VUnit only takes the minimal steps required + to make the simulator creates an unique coverage file for the + simulation run. The VUnit users must still set :ref:`sim + ` and :ref:`compile ` options to + configure the simulator specific coverage options they want. The + reason for this to allow the VUnit users maximum control of their + coverage settings. + + An example of a ``run.py`` file using coverage can be found + :vunit_example:`here `. + + .. note: Supported by RivieraPRO and Modelsim/Questa simulators. + + +``pli`` + A list of PLI file names. + +``ghdl.flags`` + Extra arguments passed to ``ghdl --elab-run`` command *before* executable specific flags. Must be a list of strings. + Must be a list of strings. + +``incisive.irun_sim_flags`` + Extra arguments passed to the Incisive ``irun`` command when loading the design. + Must be a list of strings. + +``modelsim.vsim_flags`` + Extra arguments passed to ``vsim`` when loading the design. + Must be a list of strings. + +``modelsim.vsim_flags.gui`` + Extra arguments passed to ``vsim`` when loading the design in GUI + mode where it takes precedence over ``modelsim.vsim_flags``. + Must be a list of strings. + +``modelsim.init_files.after_load`` + A list of user defined DO/TCL-files that is sourced after the design has been loaded. + During script evaluation the ``vunit_tb_path`` variable is defined + as the path of the folder containing the test bench. + Must be a list of strings. + +``modelsim.init_file.gui`` + A user defined TCL-file that is sourced after the design has been loaded in the GUI. + For example this can be used to configure the waveform viewer. + During script evaluation the ``vunit_tb_path`` variable is defined + as the path of the folder containing the test bench. + Must be a string. + +``rivierapro.vsim_flags`` + Extra arguments passed to ``vsim`` when loading the design. + Must be a list of strings. + +``rivierapro.vsim_flags.gui`` + Extra arguments passed to ``vsim`` when loading the design in GUI + mode where it takes precedence over ``rivierapro.vsim_flags``. + Must be a list of strings. + +``rivierapro.init_files.after_load`` + A list of user defined DO/TCL-files that is sourced after the design has been loaded. + During script evaluation the ``vunit_tb_path`` variable is defined + as the path of the folder containing the test bench. + Must be a list of strings. + +``rivierapro.init_file.gui`` + A user defined TCL-file that is sourced after the design has been loaded in the GUI. + For example this can be used to configure the waveform viewer. + During script evaluation the ``vunit_tb_path`` variable is defined + as the path of the folder containing the test bench. + Must be a string. + +``activehdl.vsim_flags`` + Extra arguments passed to ``vsim`` when loading the design. + Must be a list of strings. + +``activehdl.vsim_flags.gui`` + Extra arguments passed to ``vsim`` when loading the design in GUI + mode where it takes precedence over ``activehdl.vsim_flags``. + Must be a list of strings. + +``activehdl.init_file.gui`` + A user defined TCL-file that is sourced after the design has been loaded in the GUI. + For example this can be used to configure the waveform viewer. + Must be a string. + +``ghdl.elab_flags`` + Extra elaboration flags passed to ``ghdl --elab-run``. + Must be a list of strings. + +``ghdl.sim_flags`` + Extra simulation flags passed to ``ghdl --elab-run``. + Must be a list of strings. + +``ghdl.elab_e`` + With ``--elaborate``, execute ``ghdl -e`` instead of ``ghdl --elab-run --no-run``. + Must be a boolean. + +``ghdl.gtkwave_script.gui`` + A user defined TCL-file that is sourced after the design has been loaded in the GUI. + For example this can be used to configure the waveform viewer. Must be a string. + There are currently limitations in the HEAD revision of GTKWave that prevent the + user from sourcing a list of scripts directly. The following is the current work + around to sourcing multiple user TCL-files: + ``source `` + +.. |compile_option| replace:: + The name of the compile option (See :ref:`Compilation options `) + +.. |simulation_options| replace:: + The name of the simulation option (See :ref:`Simulation options `) + +.. _configurations: + +Configurations +-------------- +In VUnit Python API the name ``configuration`` is used to denote the +user controllable configuration of one test run such as +generic/parameter settings, simulation options as well as the +pre_config and post_check :ref:`callback functions `. +User :ref:`attributes ` can also be added as a part of a +configuration. + +Configurations can either be unique for each test case or must be +common for the entire test bench depending on the situation. For test +benches without test such as `tb_example` in the User Guide the +configuration is common for the entire test bench. For test benches +containing tests such as `tb_example_many` the configuration is done +for each test case. If the ``run_all_in_same_sim`` attribute has been used +configuration is performed at the test bench level even if there are +individual test within since they must run in the same simulation. + +In a VUnit all test benches and test cases are created with an unnamed default +configuration which is modified by different methods such as ``set_generic`` etc. +In addition to the unnamed default configuration multiple named configurations +can be derived from it by using the ``add_config`` method. The default +configuration is only run if there are no named configurations. + +.. |configurations| replace:: + :ref:`configurations ` +""" + + +from __future__ import print_function + +import csv +import sys +import traceback +import logging +import json +import os +from os.path import exists, abspath, join, basename, splitext, normpath, dirname +from glob import glob +from fnmatch import fnmatch +from vunit.database import PickledDataBase, DataBase +from vunit import ostools +from vunit.vunit_cli import VUnitCLI +from vunit.simulator_factory import SIMULATOR_FACTORY +from vunit.simulator_interface import (is_string_not_iterable, + SimulatorInterface) +from vunit.color_printer import (COLOR_PRINTER, + NO_COLOR_PRINTER) +from vunit.project import (Project, + file_type_of, + FILE_TYPES, + VERILOG_FILE_TYPES, + check_vhdl_standard) +from vunit.test_runner import TestRunner +from vunit.test_report import TestReport +from vunit.test_bench_list import TestBenchList +from vunit.exceptions import CompileError +from vunit.location_preprocessor import LocationPreprocessor +from vunit.check_preprocessor import CheckPreprocessor +from vunit.parsing.encodings import HDL_FILE_ENCODING +from vunit.builtins import (Builtins, + add_verilog_include_dir) +from vunit.com import codec_generator + +LOGGER = logging.getLogger(__name__) + + +class VUnit(object): # pylint: disable=too-many-instance-attributes, too-many-public-methods + """ + The public interface of VUnit + + :example: + + .. code-block:: python + + from vunit import VUnit + """ + + @classmethod + def from_argv(cls, argv=None, compile_builtins=True, vhdl_standard=None): + """ + Create VUnit instance from command line arguments. + + :param argv: Use explicit argv instead of actual command line argument + :param compile_builtins: Do not compile builtins. Used for VUnit internal testing. + :returns: A :class:`.VUnit` object instance + + :example: + + .. code-block:: python + + from vunit import VUnit + prj = VUnit.from_argv() + + """ + args = VUnitCLI().parse_args(argv=argv) + return cls.from_args(args, compile_builtins=compile_builtins, vhdl_standard=vhdl_standard) + + @classmethod + def from_args(cls, args, compile_builtins=True, vhdl_standard=None): + """ + Create VUnit instance from args namespace. + Intended for users who adds custom command line options. + See :class:`vunit.vunit_cli.VUnitCLI` class to learn about + adding custom command line options. + + :param args: The parsed argument namespace object + :param compile_builtins: Do not compile builtins. Used for VUnit internal testing. + :returns: A :class:`.VUnit` object instance + """ + + return cls(args, compile_builtins=compile_builtins, vhdl_standard=vhdl_standard) + + def __init__(self, args, compile_builtins=True, vhdl_standard=None): + self._args = args + self._configure_logging(args.log_level) + self._output_path = abspath(args.output_path) + + if args.no_color: + self._printer = NO_COLOR_PRINTER + else: + self._printer = COLOR_PRINTER + + def test_filter(name, attribute_names): + keep = any(fnmatch(name, pattern) for pattern in args.test_patterns) + + if args.with_attributes is not None: + keep = keep and set(args.with_attributes).issubset(attribute_names) + + if args.without_attributes is not None: + keep = keep and set(args.without_attributes).isdisjoint(attribute_names) + return keep + + self._test_filter = test_filter + self._vhdl_standard = select_vhdl_standard(vhdl_standard) + + self._external_preprocessors = [] + self._location_preprocessor = None + self._check_preprocessor = None + + self._simulator_class = SIMULATOR_FACTORY.select_simulator() + + # Use default simulator options if no simulator was present + if self._simulator_class is None: + simulator_class = SimulatorInterface + self._simulator_output_path = join(self._output_path, "none") + else: + simulator_class = self._simulator_class + self._simulator_output_path = join(self._output_path, simulator_class.name) + + self._create_output_path(args.clean) + + database = self._create_database() + self._project = Project( + database=database, + depend_on_package_body=simulator_class.package_users_depend_on_bodies) + + self._test_bench_list = TestBenchList(database=database) + + self._builtins = Builtins(self, self._vhdl_standard, simulator_class) + if compile_builtins: + self.add_builtins() + + def _create_database(self): + """ + Create a persistent database to store expensive parse results + + Check for Python version used to create the database is the + same as the running python instance or re-create + """ + project_database_file_name = join(self._output_path, "project_database") + create_new = False + key = b"version" + version = str((9, sys.version)).encode() + database = None + try: + database = DataBase(project_database_file_name) + create_new = (key not in database) or (database[key] != version) + except KeyboardInterrupt: + raise KeyboardInterrupt + except: # pylint: disable=bare-except + traceback.print_exc() + create_new = True + + if create_new: + database = DataBase(project_database_file_name, new=True) + database[key] = version + + return PickledDataBase(database) + + @staticmethod + def _configure_logging(log_level): + """ + Configure logging based on log_level string + """ + level = getattr(logging, log_level.upper()) + logging.basicConfig(filename=None, format='%(levelname)7s - %(message)s', level=level) + + def add_external_library(self, library_name, path, vhdl_standard=None): + """ + Add an externally compiled library as a black-box + + :param library_name: The name of the external library + :param path: The path to the external library directory + :param vhdl_standard: The VHDL standard used to compile files into this library, + if None the VUNIT_VHDL_STANDARD environment variable is used + :returns: The created :class:`.Library` object + + :example: + + .. code-block:: python + + prj.add_external_library("unisim", "path/to/unisim/") + + """ + if vhdl_standard is None: + vhdl_standard = self._vhdl_standard + + self._project.add_library(library_name, abspath(path), vhdl_standard, is_external=True) + return self.library(library_name) + + def add_source_files_from_csv(self, project_csv_path, vhdl_standard=None): + """ + Add a project configuration, mapping all the libraries and files + + :param project_csv_path: path to csv project specification, each line contains the name + of the library and the path to one file 'lib_name,filename' + note that all filenames are relative to the parent folder of the + csv file + :param vhdl_standard: The VHDL standard used to compile file into this library, + if None, the VUNIT_VHDL_STANDARD environment variable is used + :returns: A list of files (:class `.SourceFileList`) that were added + + """ + if vhdl_standard is None: + vhdl_standard = self._vhdl_standard + + libs = set() + files = SourceFileList(list()) + + with open(project_csv_path) as csv_path_file: + for row in csv.reader(csv_path_file): + if len(row) == 2: + lib_name = row[0].strip() + no_normalized_file = row[1].strip() + file_name_ = normpath(join(dirname(project_csv_path), no_normalized_file)) + lib = self.library(lib_name) if lib_name in libs else self.add_library(lib_name) + libs.add(lib_name) + file_ = lib.add_source_file(file_name_) + files.append(file_) + elif len(row) > 2: + LOGGER.error("More than one library and one file in csv description") + return files + + def add_library(self, library_name, vhdl_standard=None, allow_duplicate=False): + """ + Add a library managed by VUnit. + + :param library_name: The name of the library + :param vhdl_standard: The VHDL standard used to compile files into this library, + if None the VUNIT_VHDL_STANDARD environment variable is used + :param allow_duplicate: Set to True to allow the same library + to be added multiple times. Subsequent additions will just + return the previously created library. + :returns: The created :class:`.Library` object + + :example: + + .. code-block:: python + + library = prj.add_library("lib") + + """ + if vhdl_standard is None: + vhdl_standard = self._vhdl_standard + + path = join(self._simulator_output_path, "libraries", library_name) + if not self._project.has_library(library_name): + self._project.add_library(library_name, abspath(path), vhdl_standard) + elif not allow_duplicate: + raise ValueError("Library %s already added. Use allow_duplicate to ignore this error." % library_name) + return self.library(library_name) + + def library(self, library_name): + """ + Get a library + + :param library_name: The name of the library + :returns: A :class:`.Library` object + """ + if not self._project.has_library(library_name): + raise KeyError(library_name) + return Library(library_name, self, self._project, self._test_bench_list) + + def set_attribute(self, name, value, allow_empty=False): + """ + Set a value of attribute in all |configurations| + + :param name: The name of the attribute + :param value: The value of the attribute + :param allow_empty: To disable an error when no test benches were found + + :example: + + .. code-block:: python + + prj.set_attribute(".foo", "bar") + + .. note:: + Only affects test benches added *before* the attribute is set. + """ + test_benches = self._test_bench_list.get_test_benches() + for test_bench in check_not_empty(test_benches, allow_empty, "No test benches found"): + test_bench.set_attribute(name, value) + + def set_generic(self, name, value, allow_empty=False): + """ + Set a value of generic in all |configurations| + + :param name: The name of the generic + :param value: The value of the generic + :param allow_empty: To disable an error when no test benches were found + + :example: + + .. code-block:: python + + prj.set_generic("data_width", 16) + + .. note:: + Only affects test benches added *before* the generic is set. + """ + test_benches = self._test_bench_list.get_test_benches() + for test_bench in check_not_empty(test_benches, allow_empty, "No test benches found"): + test_bench.set_generic(name.lower(), value) + + def set_parameter(self, name, value, allow_empty=False): + """ + Set value of parameter in all |configurations| + + :param name: The name of the parameter + :param value: The value of the parameter + :param allow_empty: To disable an error when no test benches were found + + :example: + + .. code-block:: python + + prj.set_parameter("data_width", 16) + + .. note:: + Only affects test benches added *before* the parameter is set. + """ + test_benches = self._test_bench_list.get_test_benches() + for test_bench in check_not_empty(test_benches, allow_empty, "No test benches found"): + test_bench.set_generic(name, value) + + def set_sim_option(self, name, value, allow_empty=False, overwrite=True): + """ + Set simulation option in all |configurations| + + :param name: |simulation_options| + :param value: The value of the simulation option + :param allow_empty: To disable an error when no test benches were found + :param overwrite: To overwrite the option or append to the existing value + + :example: + + .. code-block:: python + + prj.set_sim_option("ghdl.flags", ["--no-vital-checks"]) + + .. note:: + Only affects test benches added *before* the option is set. + """ + test_benches = self._test_bench_list.get_test_benches() + for test_bench in check_not_empty(test_benches, allow_empty, "No test benches found"): + test_bench.set_sim_option(name, value, overwrite) + + def set_compile_option(self, name, value, allow_empty=False): + """ + Set compile option of all files + + :param name: |compile_option| + :param value: The value of the compile option + :param allow_empty: To disable an error when no source files were found + + :example: + + .. code-block:: python + + prj.set_compile_option("ghdl.flags", ["--no-vital-checks"]) + + + .. note:: + Only affects files added *before* the option is set. + """ + source_files = self._project.get_source_files_in_order() + for source_file in check_not_empty(source_files, allow_empty, "No source files found"): + source_file.set_compile_option(name, value) + + def add_compile_option(self, name, value, allow_empty=False): + """ + Add compile option to all files + + :param name: |compile_option| + :param value: The value of the compile option + :param allow_empty: To disable an error when no source files were found + + .. note:: + Only affects files added *before* the option is set. + """ + source_files = self._project.get_source_files_in_order() + for source_file in check_not_empty(source_files, allow_empty, "No source files found"): + source_file.add_compile_option(name, value) + + def get_source_file(self, file_name, library_name=None): + """ + Get a source file + + :param file_name: The name of the file as a relative or absolute path + :param library_name: The name of a specific library to search if not all libraries + :returns: A :class:`.SourceFile` object + """ + + files = self.get_source_files(file_name, library_name, allow_empty=True) + if len(files) > 1: + raise ValueError("Found file named '%s' in multiple-libraries, " + "add explicit library_name." % file_name) + if not files: + if library_name is None: + raise ValueError("Found no file named '%s'" % file_name) + + raise ValueError("Found no file named '%s' in library '%s'" + % (file_name, library_name)) + return files[0] + + def get_source_files(self, pattern="*", library_name=None, allow_empty=False): + """ + Get a list of source files + + :param pattern: A wildcard pattern matching either an absolute or relative path + :param library_name: The name of a specific library to search if not all libraries + :param allow_empty: To disable an error if no files matched the pattern + :returns: A :class:`.SourceFileList` object + """ + results = [] + for source_file in self._project.get_source_files_in_order(): + if library_name is not None: + if source_file.library.name != library_name: + continue + + if not (fnmatch(abspath(source_file.name), pattern) + or fnmatch(ostools.simplify_path(source_file.name), pattern)): + continue + + results.append(SourceFile(source_file, self._project, self)) + + check_not_empty(results, allow_empty, + ("Pattern %r did not match any file" % pattern) + + (("within library %s" % library_name) if library_name is not None else "")) + + return SourceFileList(results) + + def add_source_files(self, # pylint: disable=too-many-arguments + pattern, library_name, preprocessors=None, include_dirs=None, defines=None, allow_empty=False, + vhdl_standard=None, no_parse=False, file_type=None): + """ + Add source files matching wildcard pattern to library + + :param pattern: A wildcard pattern matching the files to add or a list of files + :param library_name: The name of the library to add files into + :param include_dirs: A list of include directories + :param defines: A dictionary containing Verilog defines to be set + :param allow_empty: To disable an error if no files matched the pattern + :param vhdl_standard: The VHDL standard used to compile these files, + if None VUNIT_VHDL_STANDARD environment variable is used + :param no_parse: Do not parse file(s) for dependency or test scanning purposes + :param file_type: The type of the file; ``"vhdl"``, ``"verilog"`` or ``"systemverilog"``. + Auto-detected by default when set to ``None``. + :returns: A list of files (:class:`.SourceFileList`) which were added + + :example: + + .. code-block:: python + + prj.add_source_files("*.vhd", "lib") + + """ + return self.library(library_name).add_source_files(pattern=pattern, + preprocessors=preprocessors, + include_dirs=include_dirs, + defines=defines, + allow_empty=allow_empty, + vhdl_standard=vhdl_standard, + no_parse=no_parse, + file_type=file_type) + + def add_source_file(self, # pylint: disable=too-many-arguments + file_name, library_name, preprocessors=None, include_dirs=None, defines=None, + vhdl_standard=None, no_parse=False, file_type=None): + """ + Add source file to library + + :param file_name: The name of the file + :param library_name: The name of the library to add the file into + :param include_dirs: A list of include directories + :param defines: A dictionary containing Verilog defines to be set + :param vhdl_standard: The VHDL standard used to compile this file, + if None VUNIT_VHDL_STANDARD environment variable is used + :param no_parse: Do not parse file for dependency or test scanning purposes + :param file_type: The type of the file; ``"vhdl"``, ``"verilog"`` or ``"systemverilog"``. + Auto-detected by default when set to ``None``. + :returns: The :class:`.SourceFile` which was added + + :example: + + .. code-block:: python + + prj.add_source_file("file.vhd", "lib") + + """ + return self.library(library_name).add_source_file(file_name=file_name, + preprocessors=preprocessors, + include_dirs=include_dirs, + defines=defines, + vhdl_standard=vhdl_standard, + no_parse=no_parse, + file_type=file_type) + + def _preprocess(self, library_name, file_name, preprocessors): + """ + Preprocess file_name within library_name using explicit preprocessors + if preprocessors is None then use implicit globally defined processors + """ + # @TODO dependency checking etc... + + if preprocessors is None: + preprocessors = [self._location_preprocessor, self._check_preprocessor] + preprocessors = [p for p in preprocessors if p is not None] + preprocessors = self._external_preprocessors + preprocessors + + if not preprocessors: + return file_name + + try: + code = ostools.read_file(file_name, encoding=HDL_FILE_ENCODING) + for preprocessor in preprocessors: + code = preprocessor.run(code, basename(file_name)) + except KeyboardInterrupt: + raise KeyboardInterrupt + except: # pylint: disable=bare-except + traceback.print_exc() + LOGGER.error("Failed to preprocess %s", file_name) + return file_name + else: + pp_file_name = join(self._preprocessed_path, library_name, basename(file_name)) + + idx = 1 + while ostools.file_exists(pp_file_name): + LOGGER.debug("Preprocessed file exists '%s', adding prefix", pp_file_name) + pp_file_name = join(self._preprocessed_path, + library_name, "%i_%s" % (idx, basename(file_name))) + idx += 1 + + ostools.write_file(pp_file_name, code, encoding=HDL_FILE_ENCODING) + return pp_file_name + + def add_preprocessor(self, preprocessor): + """ + Add a custom preprocessor to be used on all files, must be called before adding any files + """ + self._external_preprocessors.append(preprocessor) + + def enable_location_preprocessing(self, additional_subprograms=None, exclude_subprograms=None): + """ + Inserts file name and line number information into VUnit check and log subprograms calls. Custom + subprograms can also be added. Must be called before adding any files. + + :param additional_subprograms: List of custom subprograms to add the line_num and file_name parameters to. + :param exclude_subprograms: List of VUnit subprograms to exclude from location preprocessing. Used to \ +avoid location preprocessing of other functions sharing name with a VUnit log or check subprogram. + + :example: + + .. code-block:: python + + prj.enable_location_preprocessing(additional_subprograms=['my_check'], + exclude_subprograms=['log']) + + """ + preprocessor = LocationPreprocessor() + if additional_subprograms is not None: + for subprogram in additional_subprograms: + preprocessor.add_subprogram(subprogram) + + if exclude_subprograms is not None: + for subprogram in exclude_subprograms: + preprocessor.remove_subprogram(subprogram) + self._location_preprocessor = preprocessor + + def enable_check_preprocessing(self): + """ + Inserts error context information into VUnit check_relation calls + """ + self._check_preprocessor = CheckPreprocessor() + + def main(self, post_run=None): + """ + Run vunit main function and exit + + :param post_run: A callback function which is called after + running tests. The function must accept a single `results` + argument which is an instance of :class:`.Results` + """ + try: + all_ok = self._main(post_run) + except KeyboardInterrupt: + sys.exit(1) + except CompileError: + sys.exit(1) + except SystemExit: + sys.exit(1) + except: # pylint: disable=bare-except + if self._args.dont_catch_exceptions: + raise + traceback.print_exc() + sys.exit(1) + + if (not all_ok) and (not self._args.exit_0): + sys.exit(1) + + sys.exit(0) + + def _create_tests(self, simulator_if): + """ + Create the test cases + """ + self._test_bench_list.warn_when_empty() + test_list = self._test_bench_list.create_tests(simulator_if, self._args.elaborate) + test_list.keep_matches(self._test_filter) + return test_list + + def _main(self, post_run): + """ + Base vunit main function without performing exit + """ + + if self._args.export_json is not None: + return self._main_export_json(self._args.export_json) + + if self._args.list: + return self._main_list_only() + + if self._args.files: + return self._main_list_files_only() + + if self._args.compile: + return self._main_compile_only() + + all_ok = self._main_run(post_run) + return all_ok + + def _create_simulator_if(self): + """ + Create new simulator instance + """ + + if self._simulator_class is None: + LOGGER.error( + "No available simulator detected.\n" + "Simulator binary folder must be available in PATH environment variable.\n" + "Simulator binary folder can also be set the in VUNIT__PATH environment variable.\n") + exit(1) + + if not exists(self._simulator_output_path): + os.makedirs(self._simulator_output_path) + + return self._simulator_class.from_args(args=self._args, + output_path=self._simulator_output_path) + + def _main_run(self, post_run): + """ + Main with running tests + """ + simulator_if = self._create_simulator_if() + test_list = self._create_tests(simulator_if) + self._compile(simulator_if) + print() + + start_time = ostools.get_time() + report = TestReport(printer=self._printer) + + try: + self._run_test(test_list, report) + except KeyboardInterrupt: + print() + LOGGER.debug("_main: Caught Ctrl-C shutting down") + finally: + del test_list + + report.set_real_total_time(ostools.get_time() - start_time) + report.print_str() + + if post_run is not None: + post_run(results=Results(simulator_if)) + + del simulator_if + + if self._args.xunit_xml is not None: + xml = report.to_junit_xml_str(self._args.xunit_xml_format) + ostools.write_file(self._args.xunit_xml, xml) + + return report.all_ok() + + def _main_list_only(self): + """ + Main function when only listing test cases + """ + test_list = self._create_tests(simulator_if=None) + for test_name in test_list.test_names: + print(test_name) + print("Listed %i tests" % test_list.num_tests) + return True + + def _main_export_json(self, json_file_name): # pylint: disable=too-many-locals + """ + Main function when exporting to JSON + """ + + file_objects = self.get_compile_order() + files = [] + for source_file in file_objects: + files.append(dict(file_name=abspath(source_file.name), + library_name=source_file.library.name)) + + tests = [] + for test_suite in self._create_tests(simulator_if=None): + test_information = test_suite.test_information + test_configuration = test_suite.test_configuration + for name in test_suite.test_names: + info = test_information[name] + config = test_configuration[name] + + attributes = {} + for attr in info.attributes: + attributes[attr.name] = attr.value + + attributes.update(config.attributes) + + tests.append(dict(name=name, + location=dict(file_name=info.location.file_name, + offset=info.location.offset, + length=info.location.length), + attributes=attributes)) + + json_data = dict( + # The semantic version (https://semver.org/) of the JSON export data format + export_format_version=dict(major=1, minor=0, patch=0), + + # The set of files added to the project + files=files, + + # The list of all tests + tests=tests) + + with open(json_file_name, "w") as fptr: + json.dump(json_data, + fptr, + sort_keys=True, + indent=4, + separators=(',', ': ')) + + return True + + def _main_list_files_only(self): + """ + Main function when only listing files + """ + files = self.get_compile_order() + for source_file in files: + print("%s, %s" % (source_file.library.name, source_file.name)) + print("Listed %i files" % len(files)) + return True + + def _main_compile_only(self): + """ + Main function when only compiling + """ + simulator_if = self._create_simulator_if() + self._compile(simulator_if) + return True + + def _create_output_path(self, clean): + """ + Create or re-create the output path if necessary + """ + if clean: + ostools.renew_path(self._output_path) + elif not exists(self._output_path): + os.makedirs(self._output_path) + + ostools.renew_path(self._preprocessed_path) + + @property + def vhdl_standard(self): + return self._vhdl_standard + + @property + def _preprocessed_path(self): + return join(self._output_path, "preprocessed") + + @property + def codecs_path(self): + return join(self._output_path, "codecs") + + def _compile(self, simulator_if): + """ + Compile entire project + """ + simulator_if.compile_project(self._project, + continue_on_error=self._args.keep_compiling, + printer=self._printer) + + def _run_test(self, test_cases, report): + """ + Run the test suites and return the report + """ + + if self._args.verbose: + verbosity = TestRunner.VERBOSITY_VERBOSE + elif self._args.quiet: + verbosity = TestRunner.VERBOSITY_QUIET + else: + verbosity = TestRunner.VERBOSITY_NORMAL + + runner = TestRunner(report, + join(self._output_path, "test_output"), + verbosity=verbosity, + num_threads=self._args.num_threads, + fail_fast=self._args.fail_fast, + dont_catch_exceptions=self._args.dont_catch_exceptions, + no_color=self._args.no_color) + runner.run(test_cases) + + def add_builtins(self): + """ + Add vunit VHDL builtin libraries + """ + self._builtins.add_vhdl_builtins() + + def add_com(self): + """ + Add communication package + """ + self._builtins.add("com") + + def add_array_util(self): + """ + Add array util + """ + self._builtins.add("array_util") + + def add_random(self): + """ + Add random + """ + self._builtins.add("random") + + def add_verification_components(self): + """ + Add verification component library + """ + self._builtins.add("verification_components") + + def add_osvvm(self): + """ + Add osvvm library + """ + self._builtins.add("osvvm") + + def add_json4vhdl(self): + """ + Add JSON-for-VHDL library + """ + self._builtins.add("json4vhdl") + + def get_compile_order(self, source_files=None): + """ + Get the compile order of all or specific source files and + their dependencies. + + A dependency of file **A** in terms of compile order is any + file **B** which **must** be successfully compiled before **A** + can be successfully compiled. + + This is **not** the same as all files required to successfully elaborate **A**. + For example using component instantiation in VHDL there is no + compile order dependency but the component instance will not + elaborate if there is no binding component. + + :param source_files: A list of :class:`.SourceFile` objects or `None` meaing all + :returns: A list of :class:`.SourceFile` objects in compile order. + """ + if source_files is None: + source_files = self.get_source_files(allow_empty=True) + + target_files = [source_file._source_file # pylint: disable=protected-access + for source_file in source_files] + source_files = self._project.get_dependencies_in_compile_order(target_files) + return SourceFileList([SourceFile(source_file, self._project, self) + for source_file in source_files]) + + def get_implementation_subset(self, source_files): + """ + Get the subset of files which are required to successfully + elaborate the list of input files without any missing + dependencies. + + :param source_files: A list of :class:`.SourceFile` objects + :returns: A list of :class:`.SourceFile` objects which is the implementation subset. + """ + target_files = [source_file._source_file # pylint: disable=protected-access + for source_file in source_files] + source_files = self._project.get_dependencies_in_compile_order( + target_files, + implementation_dependencies=True) + return SourceFileList([SourceFile(source_file, self._project, self) + for source_file in source_files]) + + +class Library(object): + """ + User interface of a library + """ + def __init__(self, library_name, parent, project, test_bench_list): + self._library_name = library_name + self._parent = parent + self._project = project + self._test_bench_list = test_bench_list + + @property + def name(self): + """ + The name of the library + """ + return self._library_name + + def set_generic(self, name, value, allow_empty=False): + """ + Set a value of generic within all |configurations| of test benches and tests this library + + :param name: The name of the generic + :param value: The value of the generic + :param allow_empty: To disable an error when no test benches were found + + :example: + + .. code-block:: python + + lib.set_generic("data_width", 16) + + .. note:: + Only affects test benches added *before* the generic is set. + """ + for test_bench in self.get_test_benches(allow_empty=allow_empty): + test_bench.set_generic(name.lower(), value) + + def set_parameter(self, name, value, allow_empty=False): + """ + Set a value of parameter within all |configurations| of test benches and tests this library + + :param name: The name of the parameter + :param value: The value of the parameter + :param allow_empty: To disable an error when no test benches were found + + :example: + + .. code-block:: python + + lib.set_parameter("data_width", 16) + + .. note:: + Only affects test benches added *before* the parameter is set. + """ + for test_bench in self.get_test_benches(allow_empty=allow_empty): + test_bench.set_generic(name, value) + + def set_sim_option(self, name, value, allow_empty=False, overwrite=True): + """ + Set simulation option within all |configurations| of test benches and tests this library + + :param name: |simulation_options| + :param value: The value of the simulation option + :param allow_empty: To disable an error when no test benches were found + :param overwrite: To overwrite the option or append to the existing value + + :example: + + .. code-block:: python + + lib.set_sim_option("ghdl.flags", ["--no-vital-checks"]) + + .. note:: + Only affects test benches added *before* the option is set. + """ + for test_bench in self.get_test_benches(allow_empty=allow_empty): + test_bench.set_sim_option(name, value, overwrite) + + def set_compile_option(self, name, value, allow_empty=False): + """ + Set compile option for all files within the library + + :param name: |compile_option| + :param value: The value of the compile option + :param allow_empty: To disable an error when no source files were found + + :example: + + .. code-block:: python + + lib.set_compile_option("ghdl.flags", ["--no-vital-checks"]) + + + .. note:: + Only affects files added *before* the option is set. + """ + for source_file in self.get_source_files(allow_empty=allow_empty): + source_file.set_compile_option(name, value) + + def add_compile_option(self, name, value, allow_empty=False): + """ + Add compile option to all files within the library + + :param name: |compile_option| + :param value: The value of the compile option + :param allow_empty: To disable an error when no source files were found + + .. note:: + Only affects files added *before* the option is set. + """ + for source_file in self.get_source_files(allow_empty=allow_empty): + source_file.add_compile_option(name, value) + + def get_source_file(self, file_name): + """ + Get a source file within this library + + :param file_name: The name of the file as a relative or absolute path + + :returns: A :class:`.SourceFile` object + """ + return self._parent.get_source_file(file_name, self._library_name) + + def get_source_files(self, pattern="*", allow_empty=False): + """ + Get a list of source files within this libary + + :param pattern: A wildcard pattern matching either an absolute or relative path + :param allow_empty: To disable an error if no files matched the pattern + :returns: A :class:`.SourceFileList` object + """ + return self._parent.get_source_files(pattern, self._library_name, allow_empty) + + def add_source_files(self, # pylint: disable=too-many-arguments + pattern, preprocessors=None, include_dirs=None, defines=None, allow_empty=False, + vhdl_standard=None, no_parse=False, file_type=None): + """ + Add source files matching wildcard pattern to library + + :param pattern: A wildcard pattern match the files to add + :param include_dirs: A list of include directories + :param defines: A dictionary containing Verilog defines to be set + :param allow_empty: To disable an error if no files matched the pattern + :param vhdl_standard: The VHDL standard used to compile these files, if None library default is used + :param no_parse: Do not parse file(s) for dependency or test scanning purposes + :param file_type: The type of the file; ``"vhdl"``, ``"verilog"`` or ``"systemverilog"``. + Auto-detected by default when set to ``None``. + :returns: A list of files (:class:`.SourceFileList`) which were added + + :example: + + .. code-block:: python + + library.add_source_files("*.vhd") + + """ + if is_string_not_iterable(pattern): + patterns = [pattern] + else: + patterns = pattern + + file_names = [] + for pattern_instance in patterns: + new_file_names = glob(pattern_instance) + check_not_empty(new_file_names, allow_empty, "Pattern %r did not match any file" % pattern_instance) + file_names += new_file_names + + return SourceFileList(source_files=[ + self.add_source_file(file_name, preprocessors, include_dirs, defines, vhdl_standard, + no_parse=no_parse, file_type=file_type) + for file_name in file_names]) + + def add_source_file(self, # pylint: disable=too-many-arguments + file_name, preprocessors=None, include_dirs=None, defines=None, + vhdl_standard=None, no_parse=False, file_type=None): + """ + Add source file to library + + :param file_name: The name of the file + :param include_dirs: A list of include directories + :param defines: A dictionary containing Verilog defines to be set + :param vhdl_standard: The VHDL standard used to compile this file, if None library default is used + :param no_parse: Do not parse file for dependency or test scanning purposes + :param file_type: The type of the file; ``"vhdl"``, ``"verilog"`` or ``"systemverilog"``. + Auto-detected by default when set to ``None``. + :returns: The :class:`.SourceFile` which was added + + :example: + + .. code-block:: python + + library.add_source_file("file.vhd") + + """ + file_name = abspath(file_name) + + if file_type is None: + file_type = file_type_of(file_name) + elif file_type not in FILE_TYPES: + raise ValueError("file_type %r not in %r" % (file_type, FILE_TYPES)) + + if file_type in VERILOG_FILE_TYPES: + include_dirs = include_dirs if include_dirs is not None else [] + include_dirs = add_verilog_include_dir(include_dirs) + + new_file_name = self._parent._preprocess( # pylint: disable=protected-access + self._library_name, file_name, preprocessors) + + source_file = self._project.add_source_file(new_file_name, + self._library_name, + file_type=file_type, + include_dirs=include_dirs, + defines=defines, + vhdl_standard=vhdl_standard, + no_parse=no_parse) + # To get correct tb_path generic + source_file.original_name = file_name + + self._test_bench_list.add_from_source_file(source_file) + + return SourceFile(source_file, + self._project, + self._parent) + + def package(self, name): + """ + Get a package within the library + """ + library = self._project.get_library(self._library_name) + design_unit = library.primary_design_units.get(name) + + if design_unit is None: + raise KeyError(name) + if design_unit.unit_type != 'package': + raise KeyError(name) + + return PackageFacade(self._parent, self._library_name, name, design_unit) + + def entity(self, name): + """ + Get an entity within the library + + :param name: The name of the entity + :returns: A :class:`.TestBench` object + :raises: KeyError + """ + name = name.lower() + library = self._project.get_library(self._library_name) + if not library.has_entity(name): + raise KeyError(name) + + return self.test_bench(name) + + def module(self, name): + """ + Get a module within the library + + :param name: The name of the module + :returns: A :class:`.TestBench` object + :raises: KeyError + """ + library = self._project.get_library(self._library_name) + if name not in library.modules: + raise KeyError(name) + + return self.test_bench(name) + + def test_bench(self, name): + """ + Get a test bench within this library + + :param name: The name of the test bench + :returns: A :class:`.TestBench` object + :raises: KeyError + """ + name = name.lower() + + return TestBench(self._test_bench_list.get_test_bench(self._library_name, name), self) + + def get_test_benches(self, pattern="*", allow_empty=False): + """ + Get a list of test benches + + :param pattern: A wildcard pattern matching the test_bench name + :param allow_empty: To disable an error when no test benches were found + :returns: A list of :class:`.TestBench` objects + """ + results = [] + for test_bench in self._test_bench_list.get_test_benches_in_library(self._library_name): + if not fnmatch(abspath(test_bench.name), pattern): + continue + + results.append(TestBench(test_bench, self)) + + return check_not_empty(results, allow_empty, + "No test benches found within library %s" % self._library_name) + + +class TestBench(object): + """ + User interface of a test bench. + A test bench consists of one or more :class:`.Test` cases. Setting options for a test + bench will apply that option all test cases belonging to that test bench. + """ + def __init__(self, test_bench, library): + self._test_bench = test_bench + self._library = library + + @property + def name(self): + """ + :returns: The entity or module name of the test bench + """ + return self._test_bench.name + + @property + def library(self): + """ + :returns: The library that contains this test bench + """ + return self._library + + def set_attribute(self, name, value): + """ + Set a value of attribute within all |configurations| of this test bench or test cases within it + + :param name: The name of the attribute + :param value: The value of the atrribute + + :example: + + .. code-block:: python + + test_bench.set_attribute(".foo", "bar") + + """ + self._test_bench.set_attribute(name, value) + + def set_generic(self, name, value): + """ + Set a value of generic within all |configurations| of this test bench or test cases within it + + :param name: The name of the generic + :param value: The value of the generic + + :example: + + .. code-block:: python + + test_bench.set_generic("data_width", 16) + + """ + self._test_bench.set_generic(name.lower(), value) + + def set_parameter(self, name, value): + """ + Set a value of parameter within all |configurations| of this test bench or test cases within it + + :param name: The name of the parameter + :param value: The value of the parameter + + :example: + + .. code-block:: python + + test_bench.set_parameter("data_width", 16) + + """ + self._test_bench.set_generic(name, value) + + def set_sim_option(self, name, value, overwrite=True): + """ + Set simulation option within all |configurations| of this test bench or test cases within it + + :param name: |simulation_options| + :param value: The value of the simulation option + :param overwrite: To overwrite the option or append to the existing value + + :example: + + .. code-block:: python + + test_bench.set_sim_option("ghdl.flags", ["--no-vital-checks"]) + + """ + self._test_bench.set_sim_option(name, value, overwrite) + + def set_pre_config(self, value): + """ + Set :ref:`pre_config ` function of all + |configurations| of this test bench or test cases within it + + :param value: The pre_config function + """ + self._test_bench.set_pre_config(value) + + def set_post_check(self, value): + """ + Set :ref:`post_check ` function of all + |configurations| of this test bench or test cases within it + + :param value: The post_check function + """ + self._test_bench.set_post_check(value) + + def add_config(self, # pylint: disable=too-many-arguments + name, generics=None, parameters=None, pre_config=None, post_check=None, sim_options=None, + attributes=None): + """ + Add a configuration of this test bench or to all test cases within it by copying the default configuration. + + Multiple configuration may be added one after another. + If no |configurations| are added the default configuration is used. + + :param name: The name of the configuration. Will be added as a suffix on the test name + :param generics: A `dict` containing the generics to be set in addition to the default configuration + :param parameters: A `dict` containing the parameters to be set in addition to the default configuration + :param pre_config: A :ref:`callback function ` to be called before test execution. + :param post_check: A :ref:`callback function ` to be called after test execution. + :param sim_options: A `dict` containing the sim_options to be set in addition to the default configuration + :param attributes: A `dict` containing the attributes to be set in addition to the default configuration + + :example: + + Given a test bench that by default gives rise to the test + ``lib.test_bench`` and the following ``add_config`` calls: + + .. code-block:: python + + for data_width in range(14, 15+1): + for sign in [False, True]: + test_bench.add_config( + name="data_width=%s,sign=%s" % (data_width, sign), + generics=dict(data_width=data_width, sign=sign)) + + The following tests will be created: + + * ``lib.test_bench.data_width=14,sign=False`` + + * ``lib.test_bench.data_width=14,sign=True`` + + * ``lib.test_bench.data_width=15,sign=False`` + + * ``lib.test_bench.data_width=15,sign=True`` + + """ + generics = {} if generics is None else generics + generics = lower_generics(generics) + parameters = {} if parameters is None else parameters + generics.update(parameters) + attributes = {} if attributes is None else attributes + self._test_bench.add_config(name=name, + generics=generics, + pre_config=pre_config, + post_check=post_check, + sim_options=sim_options, + attributes=attributes) + + def test(self, name): + """ + Get a test within this test bench + + :param name: The name of the test + :returns: A :class:`.Test` object + """ + return Test(self._test_bench.get_test_case(name)) + + def get_tests(self, pattern="*"): + """ + Get a list of tests + + :param pattern: A wildcard pattern matching the test name + :returns: A list of :class:`.Test` objects + """ + results = [] + for test_case in self._test_bench.tests: + if not fnmatch(test_case.name, pattern): + continue + + results.append(Test(test_case)) + return results + + def scan_tests_from_file(self, file_name): + """ + Scan tests from another file than the one containg the test + bench. Useful for when the top level test bench does not + contain the tests. + + Such a structure is not the preferred way of doing things in + VUnit but this method exists to accommodate legacy needs. + + :param file_name: The name of another file to scan for tests + + .. warning:: + The nested module containing the tests needs to be given + the ``runner_cfg`` parameter or generic by the + instantiating top level test bench. The nested module + should not call its parameter or generic `runner_cfg` but + rather `nested_runner_cfg` to avoid the VUnit test scanner + detecting and running it as a test bench. In SystemVerilog + the ``NESTED_TEST_SUITE`` macro should be used instead of + the ``TEST_SUITE`` macro. + """ + self._test_bench.scan_tests_from_file(file_name) + + +class PackageFacade(object): + """ + User interface of a Package + """ + def __init__(self, parent, library_name, package_name, design_unit): + self._parent = parent + self._library_name = library_name + self._package_name = package_name + self._design_unit = design_unit + + def generate_codecs(self, codec_package_name=None, used_packages=None, output_file_name=None): + """ + Generates codecs for the datatypes in this Package + """ + if codec_package_name is None: + codec_package_name = self._package_name + '_codecs' + + if output_file_name is None: + codecs_path = join(self._parent.codecs_path, self._library_name) + file_extension = splitext(self._design_unit.source_file.name)[1] + output_file_name = join(codecs_path, codec_package_name + file_extension) + + codec_generator.generate_codecs(self._design_unit, + codec_package_name, + used_packages, + output_file_name) + + return self._parent.add_source_files(output_file_name, self._library_name) + + +class Test(object): + """ + User interface of a single test case + + """ + def __init__(self, test_case): + self._test_case = test_case + + @property + def name(self): + """ + :returns: the entity or module name of the test bench + """ + return self._test_case.name + + def add_config(self, # pylint: disable=too-many-arguments + name, generics=None, parameters=None, pre_config=None, post_check=None, sim_options=None, + attributes=None): + """ + Add a configuration to this test copying the default configuration. + + Multiple configuration may be added one after another. + If no |configurations| are added the default configuration is used. + + :param name: The name of the configuration. Will be added as a suffix on the test name + :param generics: A `dict` containing the generics to be set in addition to the default configuration. + :param parameters: A `dict` containing the parameters to be set in addition to the default configuration. + :param pre_config: A :ref:`callback function ` to be called before test execution. + :param post_check: A :ref:`callback function ` to be called after test execution. + :param sim_options: A `dict` containing the sim_options to be set in addition to the default configuration. + :param attributes: A `dict` containing the attributes to be set in addition to the default configuration. + + :example: + + Given the ``lib.test_bench.test`` test and the following ``add_config`` calls: + + .. code-block:: python + + for data_width in range(14, 15+1): + for sign in [False, True]: + test.add_config( + name="data_width=%s,sign=%s" % (data_width, sign), + generics=dict(data_width=data_width, sign=sign)) + + The following tests will be created: + + * ``lib.test_bench.data_width=14,sign=False.test`` + + * ``lib.test_bench.data_width=14,sign=True.test`` + + * ``lib.test_bench.data_width=15,sign=False.test`` + + * ``lib.test_bench.data_width=15,sign=True.test`` + + """ + generics = {} if generics is None else generics + generics = lower_generics(generics) + parameters = {} if parameters is None else parameters + generics.update(parameters) + attributes = {} if attributes is None else attributes + self._test_case.add_config(name=name, + generics=generics, + pre_config=pre_config, + post_check=post_check, + sim_options=sim_options, + attributes=attributes) + + def set_attribute(self, name, value): + """ + Set a value of attribute within all |configurations| of this test + + :param name: The name of the attribute + :param value: The value of the attribute + + :example: + + .. code-block:: python + + test.set_attribute(".foo", "bar") + + """ + self._test_case.set_attribute(name, value) + + def set_generic(self, name, value): + """ + Set a value of generic within all |configurations| of this test + + :param name: The name of the generic + :param value: The value of the generic + + :example: + + .. code-block:: python + + test.set_generic("data_width", 16) + + """ + self._test_case.set_generic(name.lower(), value) + + def set_parameter(self, name, value): + """ + Set a value of parameter within all |configurations| of this test + + :param name: The name of the parameter + :param value: The value of the parameter + + :example: + + .. code-block:: python + + test.set_parameter("data_width", 16) + + """ + self._test_case.set_generic(name, value) + + def set_sim_option(self, name, value, overwrite=True): + """ + Set simulation option within all |configurations| of this test + + :param name: |simulation_options| + :param value: The value of the simulation option + :param overwrite: To overwrite the option or append to the existing value + + :example: + + .. code-block:: python + + test.set_sim_option("ghdl.flags", ["--no-vital-checks"]) + + """ + self._test_case.set_sim_option(name, value, overwrite) + + def set_pre_config(self, value): + """ + Set :ref:`pre_config ` function of all |configurations| of this test + + :param value: The pre_config function + """ + self._test_case.set_pre_config(value) + + def set_post_check(self, value): + """ + Set :ref:`post_check ` function of all |configurations| of this test + + :param value: The post_check function + """ + self._test_case.set_post_check(value) + + +class SourceFileList(list): + """ + A list of :class:`.SourceFile` + """ + + def __init__(self, source_files): + list.__init__(self, source_files) + + def set_compile_option(self, name, value): + """ + Set compile option for all files in the list + + :param name: |compile_option| + :param value: The value of the compile option + + :example: + + .. code-block:: python + + files.set_compile_option("ghdl.flags", ["--no-vital-checks"]) + """ + for source_file in self: + source_file.set_compile_option(name, value) + + def add_compile_option(self, name, value): + """ + Add compile option to all files in the list + + :param name: |compile_option| + :param value: The value of the compile option + """ + for source_file in self: + source_file.add_compile_option(name, value) + + def add_dependency_on(self, source_file): + """ + Add manual dependency of these files on other file(s) + + :param source_file: The file(s) which this file depends on + + :example: + + .. code-block:: python + + other_file = lib.get_source_file("other_file.vhd") + files.add_dependency_on(other_file) + """ + for my_source_file in self: + my_source_file.add_dependency_on(source_file) + + +class SourceFile(object): + """ + A single file + """ + def __init__(self, source_file, project, ui): + self._source_file = source_file + self._project = project + self._ui = ui + + @property + def name(self): + """ + The name of the SourceFile + """ + return ostools.simplify_path(self._source_file.name) + + @property + def vhdl_standard(self): + """ + The VHDL standard applicable to the file, + None if not a VHDL file + """ + if self._source_file.file_type == "vhdl": + return self._source_file.get_vhdl_standard() + + return None + + @property + def library(self): + """ + The library of the source file + """ + return self._ui.library(self._source_file.library.name) + + def set_compile_option(self, name, value): + """ + Set compile option for this file + + :param name: |compile_option| + :param value: The value of the compile option + + :example: + + .. code-block:: python + + my_file.set_compile_option("ghdl.flags", ["--no-vital-checks"]) + """ + self._source_file.set_compile_option(name, value) + + def add_compile_option(self, name, value): + """ + Add compile option to this file + + :param name: |compile_option| + :param value: The value of the compile option + """ + self._source_file.add_compile_option(name, value) + + def get_compile_option(self, name): + """ + Return compile option of this file + + :param name: |compile_option| + """ + return self._source_file.get_compile_option(name) + + def add_dependency_on(self, source_file): + """ + Add manual dependency of this file other file(s) + + :param source_file: The file(s) which this file depends on + + :example: + + .. code-block:: python + + other_files = lib.get_source_files("*.vhd") + my_file.add_dependency_on(other_files) + """ + if isinstance(source_file, SourceFile): + private_source_file = source_file._source_file # pylint: disable=protected-access + self._project.add_manual_dependency(self._source_file, + depends_on=private_source_file) + elif hasattr(source_file, "__iter__"): + for element in source_file: + self.add_dependency_on(element) + else: + raise ValueError(source_file) + + +class Results(object): + """ + Gives access to results after running tests. + """ + + def __init__(self, simulator_if): + self._simulator_if = simulator_if + + def merge_coverage(self, file_name, args=None): + """ + Create a merged coverage report from the individual coverage files + + :param file_name: The resulting coverage file name. + :param args: The tool arguments for the merge command. Should be a list of strings. + """ + + self._simulator_if.merge_coverage(file_name=file_name, args=args) + + +def select_vhdl_standard(vhdl_standard=None): + """ + Select VHDL standard either from class initialization or according to environment variable VUNIT_VHDL_STANDARD + """ + if vhdl_standard is not None: + check_vhdl_standard(vhdl_standard, from_str="From class initialization") + else: + vhdl_standard = os.environ.get('VUNIT_VHDL_STANDARD', '2008') + check_vhdl_standard(vhdl_standard, from_str="VUNIT_VHDL_STANDARD environment variable") + + return vhdl_standard + + +def lower_generics(generics): + """ + Convert all generics names to lower case to match internal representation. + @TODO Maybe warn in case of conflict. VHDL forbids this though so the user will notice anyway. + """ + return dict((name.lower(), value) for name, value in generics.items()) + + +def check_not_empty(lst, allow_empty, error_msg): + """ + Raise ValueError if the list is empty unless allow_empty is True + Returns the list + """ + if (not allow_empty) and (not lst): + raise ValueError(error_msg + + ". Use allow_empty=True to avoid exception.") + return lst diff --git a/vunit/verilog.py b/vunit/verilog.py index 49caad79a..56e769059 100644 --- a/vunit/verilog.py +++ b/vunit/verilog.py @@ -1,23 +1,23 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -The main public Python interface of VUnit-Verilog. -""" - -from vunit.ui import VUnit as VUnitVHDL - - -class VUnit(VUnitVHDL): - """ - VUnit Verilog interface - """ - - def add_builtins(self): # pylint: disable=arguments-differ - """ - Add vunit Verilog builtin libraries - """ - self._builtins.add_verilog_builtins() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +The main public Python interface of VUnit-Verilog. +""" + +from vunit.ui import VUnit as VUnitVHDL + + +class VUnit(VUnitVHDL): + """ + VUnit Verilog interface + """ + + def add_builtins(self): # pylint: disable=arguments-differ + """ + Add vunit Verilog builtin libraries + """ + self._builtins.add_verilog_builtins() diff --git a/vunit/verilog/vunit_pkg.sv b/vunit/verilog/vunit_pkg.sv index dc5abfe9a..5f6760fe2 100644 --- a/vunit/verilog/vunit_pkg.sv +++ b/vunit/verilog/vunit_pkg.sv @@ -1,234 +1,234 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -package vunit_pkg; - -class test_runner; - typedef enum {idle, - init, - test_suite_setup, - test_case_setup, - test_case, - test_case_cleanup, - test_suite_cleanup} - phase_t; - - phase_t phase = idle; - string test_cases_found[$]; - string test_cases_to_run[$]; - string output_path; - int test_idx = 0; - int exit_without_errors = 0; - int trace_fd; - - function automatic string search_replace(string original, string old, string replacement); - // First find the index of the old string - int start_index = 0; - int original_index = 0; - int replace_index = 0; - bit found = 0; - - while(1) begin - if (original[original_index] == old[replace_index]) begin - if (replace_index == 0) begin - start_index = original_index; - end - replace_index++; - original_index++; - if (replace_index == old.len()) begin - found = 1; - break; - end - end else if (replace_index != 0) begin - replace_index = 0; - original_index = start_index + 1; - end else begin - original_index++; - end - if (original_index == original.len()) begin - // Not found - break; - end - end - - if (!found) return original; - - return { - original.substr(0, start_index-1), - replacement, - original.substr(start_index+old.len(), original.len()-1) - }; - - endfunction - - function int setup(string runner_cfg); - // Ugly hack pending actual dictionary parsing - string prefix; - int index; - - prefix = "enabled_test_cases : "; - index = -1; - for (int i=0; i= (3, 4) - - -if not version_is_ok(): - print("Your Python version (%i.%i) is too old for VUnit. " - "Please consider upgrading." % (sys.version_info[0], - sys.version_info[1])) - print("VUnit supports versions:") - print(" - Python 2.7") - print(" - Python 3.4 or higher") - exit(1) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Check that the Python version used is supported by VUnit +""" + +from __future__ import print_function +import sys + + +def version_is_ok(): + """ + Returns true if version is 2.7 or higher or equal to 3.4 + """ + version = (sys.version_info[0], + sys.version_info[1]) + return version == (2, 7) or version >= (3, 4) + + +if not version_is_ok(): + print("Your Python version (%i.%i) is too old for VUnit. " + "Please consider upgrading." % (sys.version_info[0], + sys.version_info[1])) + print("VUnit supports versions:") + print(" - Python 2.7") + print(" - Python 3.4 or higher") + exit(1) diff --git a/vunit/vhdl/array/run.py b/vunit/vhdl/array/run.py index 14c1a8f82..d524a9ec2 100644 --- a/vunit/vhdl/array/run.py +++ b/vunit/vhdl/array/run.py @@ -1,17 +1,17 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname, basename -from vunit import VUnit -from glob import glob - -root = dirname(__file__) - -ui = VUnit.from_argv() -ui.add_array_util() -lib = ui.library("vunit_lib") -lib.add_source_files(join(root, "test", "*.vhd")) -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname, basename +from vunit import VUnit +from glob import glob + +root = dirname(__file__) + +ui = VUnit.from_argv() +ui.add_array_util() +lib = ui.library("vunit_lib") +lib.add_source_files(join(root, "test", "*.vhd")) +ui.main() diff --git a/vunit/vhdl/array/src/array_pkg.vhd b/vunit/vhdl/array/src/array_pkg.vhd index 30e3587d8..b4feceddb 100644 --- a/vunit/vhdl/array/src/array_pkg.vhd +++ b/vunit/vhdl/array/src/array_pkg.vhd @@ -1,252 +1,252 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- @TODO > 32-bit ieee signed/unsigned - -use std.textio.all; -use work.integer_array_pkg.all; - -package array_pkg is - - type array_t is protected - procedure init(arr : integer_array_t); - - procedure init(length : integer := 0; - bit_width : natural := 32; - is_signed : boolean := true); - - procedure init_2d(width : integer := 0; - height : integer := 0; - bit_width : natural := 32; - is_signed : boolean := true); - - procedure init_3d(width : integer := 0; - height : integer := 0; - depth : integer := 0; - bit_width : natural := 32; - is_signed : boolean := true); - - procedure copy_from(input : inout array_t); - - procedure load_csv(file_name : string; - bit_width : natural := 32; - is_signed : boolean := true); - - procedure load_raw(file_name : string; - bit_width : natural := 32; - is_signed : boolean := true); - procedure clear; - - impure function height return integer; - impure function width return integer; - impure function depth return integer; - impure function length return integer; - impure function bit_width return integer; - impure function is_signed return boolean; - impure function lower_limit return integer; - impure function upper_limit return integer; - - impure function get(idx : integer) return integer; - impure function get(x,y : integer) return integer; - impure function get(x,y,z : integer) return integer; - procedure set(idx : integer; value : integer); - procedure set(x,y : integer; value : integer); - procedure set(x,y,z : integer; value : integer); - - -- Return interal integer_array_t object - impure function get_integer_array return integer_array_t; - - procedure append(value : integer); - procedure reshape(length : integer); - procedure reshape(width, height : integer); - procedure reshape(width, height, depth : integer); - procedure save_csv(file_name : string); - procedure save_raw(file_name : string); - end protected; -end package; - -package body array_pkg is - type array_t is protected body - variable my_arr : integer_array_t := null_integer_array; - - impure function length return integer is - begin - return my_arr.length; - end function; - - impure function width return integer is - begin - return my_arr.width; - end function; - - impure function height return integer is - begin - return my_arr.height; - end function; - - impure function depth return integer is - begin - return my_arr.depth; - end function; - - impure function bit_width return integer is - begin - return my_arr.bit_width; - end function; - - impure function is_signed return boolean is - begin - return my_arr.is_signed; - end function; - - impure function bytes_per_word return integer is - begin - return (my_arr.bit_width + 7)/8; - end function; - - impure function lower_limit return integer is - begin - return my_arr.lower_limit; - end function; - - impure function upper_limit return integer is - begin - return my_arr.upper_limit; - end function; - - procedure reshape(length : integer) is - begin - reshape(my_arr, length); - end procedure; - - procedure reshape(width, height : integer) is - begin - reshape(my_arr, width, height); - end procedure; - - procedure reshape(width, height, depth : integer) is - begin - reshape(my_arr, width, height, depth); - end procedure; - - impure function get_integer_array return integer_array_t is - begin - return my_arr; - end; - - procedure append(value : integer) is - begin - append(my_arr, value); - end procedure; - - impure function get(idx : integer) return integer is - begin - return get(my_arr, idx); - end function; - - impure function get(x, y : integer) return integer is - begin - return get(my_arr, x, y); - end function; - - impure function get(x,y,z : integer) return integer is - begin - return get(my_arr, x, y, z); - end function; - - procedure set(idx : integer; value : integer) is - begin - set(my_arr, idx, value); - end procedure; - - procedure set(x,y : integer; value : integer) is - begin - set(my_arr, x, y, value); - end procedure; - - procedure set(x,y,z : integer; value : integer) is - begin - set(my_arr, x, y, z, value); - end procedure; - - procedure init(arr : integer_array_t) is - begin - my_arr := arr; - end procedure; - - procedure init(length : integer := 0; - bit_width : natural := 32; - is_signed : boolean := true) is - begin - init_3d(width => length, - height => 1, - depth => 1, - bit_width => bit_width, - is_signed => is_signed); - end procedure; - - procedure init_2d(width : integer := 0; - height : integer := 0; - bit_width : natural := 32; - is_signed : boolean := true) is - begin - init_3d(width => width, - height => height, - depth => 1, - bit_width => bit_width, - is_signed => is_signed); - end procedure; - - procedure init_3d(width : integer := 0; - height : integer := 0; - depth : integer := 0; - bit_width : natural := 32; - is_signed : boolean := true) is - begin - deallocate(my_arr); - my_arr := new_3d(width, height, depth, bit_width, is_signed); - end procedure; - - procedure copy_from(input : inout array_t) is - begin - init_3d(input.width, input.height, input.depth, input.bit_width, input.is_signed); - for i in 0 to input.length-1 loop - set(i, input.get(i)); - end loop; - end procedure; - - procedure clear is - begin - deallocate(my_arr); - end procedure; - - procedure save_csv(file_name : string) is - begin - save_csv(my_arr, file_name); - end procedure; - - procedure load_csv(file_name : string; - bit_width : natural := 32; - is_signed : boolean := true) is - begin - deallocate(my_arr); - my_arr := load_csv(file_name, bit_width, is_signed); - end procedure; - - procedure save_raw(file_name : string) is - begin - save_raw(my_arr, file_name); - end procedure; - - procedure load_raw(file_name : string; - bit_width : natural := 32; - is_signed : boolean := true) is - begin - deallocate(my_arr); - my_arr := load_raw(file_name, bit_width, is_signed); - end procedure; - end protected body; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- @TODO > 32-bit ieee signed/unsigned + +use std.textio.all; +use work.integer_array_pkg.all; + +package array_pkg is + + type array_t is protected + procedure init(arr : integer_array_t); + + procedure init(length : integer := 0; + bit_width : natural := 32; + is_signed : boolean := true); + + procedure init_2d(width : integer := 0; + height : integer := 0; + bit_width : natural := 32; + is_signed : boolean := true); + + procedure init_3d(width : integer := 0; + height : integer := 0; + depth : integer := 0; + bit_width : natural := 32; + is_signed : boolean := true); + + procedure copy_from(input : inout array_t); + + procedure load_csv(file_name : string; + bit_width : natural := 32; + is_signed : boolean := true); + + procedure load_raw(file_name : string; + bit_width : natural := 32; + is_signed : boolean := true); + procedure clear; + + impure function height return integer; + impure function width return integer; + impure function depth return integer; + impure function length return integer; + impure function bit_width return integer; + impure function is_signed return boolean; + impure function lower_limit return integer; + impure function upper_limit return integer; + + impure function get(idx : integer) return integer; + impure function get(x,y : integer) return integer; + impure function get(x,y,z : integer) return integer; + procedure set(idx : integer; value : integer); + procedure set(x,y : integer; value : integer); + procedure set(x,y,z : integer; value : integer); + + -- Return interal integer_array_t object + impure function get_integer_array return integer_array_t; + + procedure append(value : integer); + procedure reshape(length : integer); + procedure reshape(width, height : integer); + procedure reshape(width, height, depth : integer); + procedure save_csv(file_name : string); + procedure save_raw(file_name : string); + end protected; +end package; + +package body array_pkg is + type array_t is protected body + variable my_arr : integer_array_t := null_integer_array; + + impure function length return integer is + begin + return my_arr.length; + end function; + + impure function width return integer is + begin + return my_arr.width; + end function; + + impure function height return integer is + begin + return my_arr.height; + end function; + + impure function depth return integer is + begin + return my_arr.depth; + end function; + + impure function bit_width return integer is + begin + return my_arr.bit_width; + end function; + + impure function is_signed return boolean is + begin + return my_arr.is_signed; + end function; + + impure function bytes_per_word return integer is + begin + return (my_arr.bit_width + 7)/8; + end function; + + impure function lower_limit return integer is + begin + return my_arr.lower_limit; + end function; + + impure function upper_limit return integer is + begin + return my_arr.upper_limit; + end function; + + procedure reshape(length : integer) is + begin + reshape(my_arr, length); + end procedure; + + procedure reshape(width, height : integer) is + begin + reshape(my_arr, width, height); + end procedure; + + procedure reshape(width, height, depth : integer) is + begin + reshape(my_arr, width, height, depth); + end procedure; + + impure function get_integer_array return integer_array_t is + begin + return my_arr; + end; + + procedure append(value : integer) is + begin + append(my_arr, value); + end procedure; + + impure function get(idx : integer) return integer is + begin + return get(my_arr, idx); + end function; + + impure function get(x, y : integer) return integer is + begin + return get(my_arr, x, y); + end function; + + impure function get(x,y,z : integer) return integer is + begin + return get(my_arr, x, y, z); + end function; + + procedure set(idx : integer; value : integer) is + begin + set(my_arr, idx, value); + end procedure; + + procedure set(x,y : integer; value : integer) is + begin + set(my_arr, x, y, value); + end procedure; + + procedure set(x,y,z : integer; value : integer) is + begin + set(my_arr, x, y, z, value); + end procedure; + + procedure init(arr : integer_array_t) is + begin + my_arr := arr; + end procedure; + + procedure init(length : integer := 0; + bit_width : natural := 32; + is_signed : boolean := true) is + begin + init_3d(width => length, + height => 1, + depth => 1, + bit_width => bit_width, + is_signed => is_signed); + end procedure; + + procedure init_2d(width : integer := 0; + height : integer := 0; + bit_width : natural := 32; + is_signed : boolean := true) is + begin + init_3d(width => width, + height => height, + depth => 1, + bit_width => bit_width, + is_signed => is_signed); + end procedure; + + procedure init_3d(width : integer := 0; + height : integer := 0; + depth : integer := 0; + bit_width : natural := 32; + is_signed : boolean := true) is + begin + deallocate(my_arr); + my_arr := new_3d(width, height, depth, bit_width, is_signed); + end procedure; + + procedure copy_from(input : inout array_t) is + begin + init_3d(input.width, input.height, input.depth, input.bit_width, input.is_signed); + for i in 0 to input.length-1 loop + set(i, input.get(i)); + end loop; + end procedure; + + procedure clear is + begin + deallocate(my_arr); + end procedure; + + procedure save_csv(file_name : string) is + begin + save_csv(my_arr, file_name); + end procedure; + + procedure load_csv(file_name : string; + bit_width : natural := 32; + is_signed : boolean := true) is + begin + deallocate(my_arr); + my_arr := load_csv(file_name, bit_width, is_signed); + end procedure; + + procedure save_raw(file_name : string) is + begin + save_raw(my_arr, file_name); + end procedure; + + procedure load_raw(file_name : string; + bit_width : natural := 32; + is_signed : boolean := true) is + begin + deallocate(my_arr); + my_arr := load_raw(file_name, bit_width, is_signed); + end procedure; + end protected body; +end package body; diff --git a/vunit/vhdl/array/test/tb_array.vhd b/vunit/vhdl/array/test/tb_array.vhd index b143ba91c..639f8e7ce 100644 --- a/vunit/vhdl/array/test/tb_array.vhd +++ b/vunit/vhdl/array/test/tb_array.vhd @@ -1,383 +1,383 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- @TODO add explicit check of csv string data - --- vunit: fail_on_warning --- vunit: run_all_in_same_sim - -use std.textio.all; - -library vunit_lib; -context vunit_lib.vunit_context; -use work.integer_array_pkg.all; -use work.array_pkg.all; - -entity tb_array is - generic ( - output_path : string; - runner_cfg : string); -end entity; - -architecture a of tb_array is -begin - - main : process - variable integer_arr : integer_array_t; - variable arr : array_t; - variable other_arr : array_t; - - impure function num_bytes(file_name : string) return integer is - type binary_file_t is file of character; - file fread : binary_file_t; - variable num_bytes : integer := 0; - variable chr : character; - begin - file_open(fread, file_name, read_mode); - while not endfile(fread) loop - num_bytes := num_bytes + 1; - read(fread, chr); - end loop; - file_close(fread); - return num_bytes; - end function; - - procedure test_save_and_load_raw(bit_width : integer; - is_signed : boolean) is - variable arr : array_t; - variable other_arr : array_t; - - impure function file_name return string is - begin - if is_signed then - return output_path & "s" & to_string(bit_width) & ".raw"; - else - return output_path & "u" & to_string(bit_width) & ".raw"; - end if; - end function; - - constant bytes_per_word : integer := (bit_width+7)/8; - - begin - arr.init(bit_width => bit_width, is_signed => is_signed); - arr.append(arr.lower_limit); - arr.append(0); - arr.append(arr.upper_limit); - arr.save_raw(file_name); - other_arr.load_raw(file_name, - bit_width => bit_width, is_signed => is_signed); - check_equal(other_arr.get(0), arr.get(0)); - check_equal(other_arr.get(1), arr.get(1)); - check_equal(other_arr.get(2), arr.get(2)); - check_equal(num_bytes(file_name), bytes_per_word * arr.length); - end procedure; - begin - test_runner_setup(runner, runner_cfg); - while test_suite loop - arr.init; - - if run("Has length") then - check_equal(arr.length, 0); - - elsif run("Has bit_width") then - check_equal(arr.bit_width, 32); - - elsif run("Has is_signed") then - check_equal(arr.is_signed, true); - - elsif run("Has init") then - arr.init(length => 10, bit_width => 16, is_signed => false); - check_equal(arr.length, 10); - check_equal(arr.bit_width, 16); - check_equal(arr.is_signed, false); - - elsif run("Has init_2d") then - arr.init_2d(width => 7, height => 13, bit_width => 28, is_signed => true); - check_equal(arr.width, 7); - check_equal(arr.height, 13); - check_equal(arr.length, 7*13); - check_equal(arr.bit_width, 28); - check_equal(arr.is_signed, true); - - elsif run("Has init_3d") then - arr.init_3d(width => 7, height => 13, depth => 5, - bit_width => 28, is_signed => true); - check_equal(arr.width, 7); - check_equal(arr.height, 13); - check_equal(arr.depth, 5); - check_equal(arr.length, 5*7*13); - check_equal(arr.bit_width, 28); - check_equal(arr.is_signed, true); - - elsif run("Has init from integer_array_t") then - integer_arr := new_3d(width => 7, height => 13, depth => 5, - bit_width => 28, is_signed => true); - set(integer_arr, 0, 77); - - arr.init(integer_arr); - check_equal(arr.get(0), 77); - check_equal(arr.width, 7); - check_equal(arr.height, 13); - check_equal(arr.depth, 5); - check_equal(arr.length, 5*7*13); - check_equal(arr.bit_width, 28); - check_equal(arr.is_signed, true); - - -- Check that it is a pointer to the same data - arr.set(0, 88); - check_equal(get(integer_arr, 0), 88); - - elsif run("Can extract integer_array_t") then - arr.init_3d(width => 7, height => 13, depth => 5, - bit_width => 28, is_signed => true); - arr.set(0, 77); - integer_arr := arr.get_integer_array; - check_equal(get(integer_arr, 0), 77); - check_equal(integer_arr.width, 7); - check_equal(integer_arr.height, 13); - check_equal(integer_arr.depth, 5); - check_equal(integer_arr.length, 5*7*13); - check_equal(integer_arr.bit_width, 28); - check_equal(integer_arr.is_signed, true); - - -- Check that it is a pointer to the same data - set(integer_arr, 0, 88); - check_equal(arr.get(0), 88); - - elsif run("Has copy_from") then - arr.init_3d(width => 7, height => 13, depth => 5, - bit_width => 28, is_signed => true); - for i in 0 to arr.length-1 loop - arr.set(idx=>i, value => i); - end loop; - - other_arr.copy_from(arr); - check_equal(arr.width, other_arr.width); - check_equal(arr.height, other_arr.height); - check_equal(arr.depth, other_arr.depth); - check_equal(arr.length, other_arr.length); - check_equal(arr.bit_width, other_arr.bit_width); - check_equal(arr.is_signed, other_arr.is_signed); - for i in 0 to other_arr.length-1 loop - check_equal(arr.get(i), other_arr.get(i)); - end loop; - - elsif run("Has set") then - arr.init(length => 1); - arr.set(0,7); - - elsif run("Has set 2d") then - arr.init_2d(width => 1, height => 2); - arr.set(x => 0, y => 0, value => 7); - arr.set(x => 0, y => 1, value => 11); - - elsif run("Test reshape") then - arr.init(length => 1); - arr.set(0, value => 100); - - arr.reshape(2); - check_equal(arr.length, 2); - check_equal(arr.get(0), 100); - arr.set(1, value => 200); - check_equal(arr.get(1), 200); - - arr.reshape(1); - check_equal(arr.length, 1); - check_equal(arr.get(0), 100); - - elsif run("Test reshape 2d") then - arr.init(length => 3); - for i in 0 to 2 loop - arr.set(i, value => 10+i); - end loop; - - arr.reshape(1, 3); - check_equal(arr.width, 1); - check_equal(arr.height, 3); - check_equal(arr.depth, 1); - for i in 0 to 2 loop - check_equal(arr.get(i), 10+i); - end loop; - - for i in 0 to 2 loop - check_equal(arr.get(0, i), 10+i); - end loop; - - arr.reshape(3, 1); - check_equal(arr.width, 3); - check_equal(arr.height, 1); - check_equal(arr.depth, 1); - for i in 0 to 2 loop - check_equal(arr.get(i), 10+i); - end loop; - for i in 0 to 2 loop - check_equal(arr.get(i, 0), 10+i); - end loop; - - arr.reshape(2, 1); - check_equal(arr.width, 2); - check_equal(arr.height, 1); - check_equal(arr.depth, 1); - check_equal(arr.get(0, 0), 10); - check_equal(arr.get(1, 0), 11); - - elsif run("Test reshape 3d") then - arr.init(length => 6); - for i in 0 to 5 loop - arr.set(i, value => 10+i); - end loop; - - arr.reshape(1, 2, 3); - check_equal(arr.width, 1); - check_equal(arr.height, 2); - check_equal(arr.depth, 3); - for i in 0 to 5 loop - check_equal(arr.get(i), 10+i); - end loop; - - for i in 0 to 5 loop - check_equal(arr.get(0, i / 3, i mod 3), 10+i); - end loop; - - - elsif run("Has get") then - arr.init(2); - arr.set(0, 7); - arr.set(1, 11); - check_equal(arr.get(0), 7); - check_equal(arr.get(1), 11); - - elsif run("Has get 2d") then - arr.init_2d(width => 2, height => 3); - for i in 0 to 5 loop - arr.set(i mod 2, i/2, 10 + i); - end loop; - - for i in 0 to 5 loop - check_equal(arr.get(i mod 2, i/2), 10 + i); - end loop; - - for i in 0 to 5 loop - check_equal(arr.get(i), 10 + i); - end loop; - - elsif run("Has set and get 2d") then - arr.init_3d(width => 2, height => 3, depth => 5); - for x in 0 to arr.width-1 loop - for y in 0 to arr.height-1 loop - for z in 0 to arr.depth-1 loop - arr.set(x,y,z, 1000*x + 100*y + z); - end loop; - end loop; - end loop; - - for x in 0 to arr.width-1 loop - for y in 0 to arr.height-1 loop - for z in 0 to arr.depth-1 loop - check_equal(arr.get(x,y,z), 1000*x + 100*y + z); - end loop; - end loop; - end loop; - - elsif run("Has append") then - arr.append(11); - check_equal(arr.length, 1); - check_equal(arr.get(0), 11); - - arr.append(7); - check_equal(arr.length, 2); - check_equal(arr.get(1), 7); - - elsif run("Clear sets length to 0") then - arr.append(10); - check_equal(arr.length, 1); - arr.clear; - check_equal(arr.length, 0); - - elsif run("Clear sets width height depth to 0") then - arr.init_3d(width => 2, height => 3, depth => 5); - check_equal(arr.width, 2); - check_equal(arr.height, 3); - check_equal(arr.depth, 5); - arr.clear; - check_equal(arr.width, 0); - check_equal(arr.height, 0); - check_equal(arr.depth, 0); - - elsif run("Can save and load csv") then - arr.append(integer'left); - arr.append(0); - arr.append(integer'right); - arr.save_csv(output_path & "can_save.csv"); - other_arr.load_csv(output_path & "can_save.csv"); - check_equal(other_arr.length, arr.length); - for idx in 0 to arr.length-1 loop - check_equal(arr.get(idx), other_arr.get(idx)); - end loop; - - elsif run("Can save and load csv 2d") then - arr.append(integer'left); - arr.append(0); - arr.append(integer'right); - arr.append(1); - arr.reshape(2, 2); - arr.save_csv(output_path & "can_save_2d.csv"); - - other_arr.load_csv(output_path & "can_save_2d.csv"); - check_equal(other_arr.length, arr.length); - check_equal(other_arr.width, arr.width); - check_equal(other_arr.height, arr.height); - check_equal(other_arr.depth, arr.depth); - - for x in 0 to arr.width-1 loop - for y in 0 to arr.height-1 loop - check_equal(arr.get(x,y), other_arr.get(x,y)); - end loop; - end loop; - - elsif run("Can save and load csv 3d") then - for i in 0 to 30 loop - arr.append(i); - end loop; - arr.reshape(2, 3, 5); - arr.save_csv(output_path & "can_save_3d.csv"); - - other_arr.load_csv(output_path & "can_save_3d.csv"); - check_equal(other_arr.length, arr.length); - check_equal(other_arr.width, arr.width*arr.depth); - check_equal(other_arr.height, arr.height); - - for idx in 0 to arr.length-1 loop - check_equal(arr.get(idx), other_arr.get(idx)); - end loop; - - elsif run("Can save and load raw") then - for bit_width in 1 to 31 loop - for is_signed in 0 to 1 loop - test_save_and_load_raw(bit_width => bit_width, is_signed => is_signed=1); - end loop; - end loop; - test_save_and_load_raw(bit_width => 32, is_signed => true); - - elsif run("Save signed and load unsigned") then - arr.init(bit_width => 14, - is_signed => true); - arr.append(-1); - arr.append(-2**13); - arr.save_raw(output_path & "s14_to_u16.csv"); - other_arr.load_raw(output_path & "s14_to_u16.csv", - bit_width => 16, - is_signed => false); - check_equal(other_arr.length, 2); - check_equal(other_arr.get(0), 2**16-1); - check_equal(other_arr.get(1), 2**16 - 2**13); - end if; - - end loop; - test_runner_cleanup(runner); - wait; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- @TODO add explicit check of csv string data + +-- vunit: fail_on_warning +-- vunit: run_all_in_same_sim + +use std.textio.all; + +library vunit_lib; +context vunit_lib.vunit_context; +use work.integer_array_pkg.all; +use work.array_pkg.all; + +entity tb_array is + generic ( + output_path : string; + runner_cfg : string); +end entity; + +architecture a of tb_array is +begin + + main : process + variable integer_arr : integer_array_t; + variable arr : array_t; + variable other_arr : array_t; + + impure function num_bytes(file_name : string) return integer is + type binary_file_t is file of character; + file fread : binary_file_t; + variable num_bytes : integer := 0; + variable chr : character; + begin + file_open(fread, file_name, read_mode); + while not endfile(fread) loop + num_bytes := num_bytes + 1; + read(fread, chr); + end loop; + file_close(fread); + return num_bytes; + end function; + + procedure test_save_and_load_raw(bit_width : integer; + is_signed : boolean) is + variable arr : array_t; + variable other_arr : array_t; + + impure function file_name return string is + begin + if is_signed then + return output_path & "s" & to_string(bit_width) & ".raw"; + else + return output_path & "u" & to_string(bit_width) & ".raw"; + end if; + end function; + + constant bytes_per_word : integer := (bit_width+7)/8; + + begin + arr.init(bit_width => bit_width, is_signed => is_signed); + arr.append(arr.lower_limit); + arr.append(0); + arr.append(arr.upper_limit); + arr.save_raw(file_name); + other_arr.load_raw(file_name, + bit_width => bit_width, is_signed => is_signed); + check_equal(other_arr.get(0), arr.get(0)); + check_equal(other_arr.get(1), arr.get(1)); + check_equal(other_arr.get(2), arr.get(2)); + check_equal(num_bytes(file_name), bytes_per_word * arr.length); + end procedure; + begin + test_runner_setup(runner, runner_cfg); + while test_suite loop + arr.init; + + if run("Has length") then + check_equal(arr.length, 0); + + elsif run("Has bit_width") then + check_equal(arr.bit_width, 32); + + elsif run("Has is_signed") then + check_equal(arr.is_signed, true); + + elsif run("Has init") then + arr.init(length => 10, bit_width => 16, is_signed => false); + check_equal(arr.length, 10); + check_equal(arr.bit_width, 16); + check_equal(arr.is_signed, false); + + elsif run("Has init_2d") then + arr.init_2d(width => 7, height => 13, bit_width => 28, is_signed => true); + check_equal(arr.width, 7); + check_equal(arr.height, 13); + check_equal(arr.length, 7*13); + check_equal(arr.bit_width, 28); + check_equal(arr.is_signed, true); + + elsif run("Has init_3d") then + arr.init_3d(width => 7, height => 13, depth => 5, + bit_width => 28, is_signed => true); + check_equal(arr.width, 7); + check_equal(arr.height, 13); + check_equal(arr.depth, 5); + check_equal(arr.length, 5*7*13); + check_equal(arr.bit_width, 28); + check_equal(arr.is_signed, true); + + elsif run("Has init from integer_array_t") then + integer_arr := new_3d(width => 7, height => 13, depth => 5, + bit_width => 28, is_signed => true); + set(integer_arr, 0, 77); + + arr.init(integer_arr); + check_equal(arr.get(0), 77); + check_equal(arr.width, 7); + check_equal(arr.height, 13); + check_equal(arr.depth, 5); + check_equal(arr.length, 5*7*13); + check_equal(arr.bit_width, 28); + check_equal(arr.is_signed, true); + + -- Check that it is a pointer to the same data + arr.set(0, 88); + check_equal(get(integer_arr, 0), 88); + + elsif run("Can extract integer_array_t") then + arr.init_3d(width => 7, height => 13, depth => 5, + bit_width => 28, is_signed => true); + arr.set(0, 77); + integer_arr := arr.get_integer_array; + check_equal(get(integer_arr, 0), 77); + check_equal(integer_arr.width, 7); + check_equal(integer_arr.height, 13); + check_equal(integer_arr.depth, 5); + check_equal(integer_arr.length, 5*7*13); + check_equal(integer_arr.bit_width, 28); + check_equal(integer_arr.is_signed, true); + + -- Check that it is a pointer to the same data + set(integer_arr, 0, 88); + check_equal(arr.get(0), 88); + + elsif run("Has copy_from") then + arr.init_3d(width => 7, height => 13, depth => 5, + bit_width => 28, is_signed => true); + for i in 0 to arr.length-1 loop + arr.set(idx=>i, value => i); + end loop; + + other_arr.copy_from(arr); + check_equal(arr.width, other_arr.width); + check_equal(arr.height, other_arr.height); + check_equal(arr.depth, other_arr.depth); + check_equal(arr.length, other_arr.length); + check_equal(arr.bit_width, other_arr.bit_width); + check_equal(arr.is_signed, other_arr.is_signed); + for i in 0 to other_arr.length-1 loop + check_equal(arr.get(i), other_arr.get(i)); + end loop; + + elsif run("Has set") then + arr.init(length => 1); + arr.set(0,7); + + elsif run("Has set 2d") then + arr.init_2d(width => 1, height => 2); + arr.set(x => 0, y => 0, value => 7); + arr.set(x => 0, y => 1, value => 11); + + elsif run("Test reshape") then + arr.init(length => 1); + arr.set(0, value => 100); + + arr.reshape(2); + check_equal(arr.length, 2); + check_equal(arr.get(0), 100); + arr.set(1, value => 200); + check_equal(arr.get(1), 200); + + arr.reshape(1); + check_equal(arr.length, 1); + check_equal(arr.get(0), 100); + + elsif run("Test reshape 2d") then + arr.init(length => 3); + for i in 0 to 2 loop + arr.set(i, value => 10+i); + end loop; + + arr.reshape(1, 3); + check_equal(arr.width, 1); + check_equal(arr.height, 3); + check_equal(arr.depth, 1); + for i in 0 to 2 loop + check_equal(arr.get(i), 10+i); + end loop; + + for i in 0 to 2 loop + check_equal(arr.get(0, i), 10+i); + end loop; + + arr.reshape(3, 1); + check_equal(arr.width, 3); + check_equal(arr.height, 1); + check_equal(arr.depth, 1); + for i in 0 to 2 loop + check_equal(arr.get(i), 10+i); + end loop; + for i in 0 to 2 loop + check_equal(arr.get(i, 0), 10+i); + end loop; + + arr.reshape(2, 1); + check_equal(arr.width, 2); + check_equal(arr.height, 1); + check_equal(arr.depth, 1); + check_equal(arr.get(0, 0), 10); + check_equal(arr.get(1, 0), 11); + + elsif run("Test reshape 3d") then + arr.init(length => 6); + for i in 0 to 5 loop + arr.set(i, value => 10+i); + end loop; + + arr.reshape(1, 2, 3); + check_equal(arr.width, 1); + check_equal(arr.height, 2); + check_equal(arr.depth, 3); + for i in 0 to 5 loop + check_equal(arr.get(i), 10+i); + end loop; + + for i in 0 to 5 loop + check_equal(arr.get(0, i / 3, i mod 3), 10+i); + end loop; + + + elsif run("Has get") then + arr.init(2); + arr.set(0, 7); + arr.set(1, 11); + check_equal(arr.get(0), 7); + check_equal(arr.get(1), 11); + + elsif run("Has get 2d") then + arr.init_2d(width => 2, height => 3); + for i in 0 to 5 loop + arr.set(i mod 2, i/2, 10 + i); + end loop; + + for i in 0 to 5 loop + check_equal(arr.get(i mod 2, i/2), 10 + i); + end loop; + + for i in 0 to 5 loop + check_equal(arr.get(i), 10 + i); + end loop; + + elsif run("Has set and get 2d") then + arr.init_3d(width => 2, height => 3, depth => 5); + for x in 0 to arr.width-1 loop + for y in 0 to arr.height-1 loop + for z in 0 to arr.depth-1 loop + arr.set(x,y,z, 1000*x + 100*y + z); + end loop; + end loop; + end loop; + + for x in 0 to arr.width-1 loop + for y in 0 to arr.height-1 loop + for z in 0 to arr.depth-1 loop + check_equal(arr.get(x,y,z), 1000*x + 100*y + z); + end loop; + end loop; + end loop; + + elsif run("Has append") then + arr.append(11); + check_equal(arr.length, 1); + check_equal(arr.get(0), 11); + + arr.append(7); + check_equal(arr.length, 2); + check_equal(arr.get(1), 7); + + elsif run("Clear sets length to 0") then + arr.append(10); + check_equal(arr.length, 1); + arr.clear; + check_equal(arr.length, 0); + + elsif run("Clear sets width height depth to 0") then + arr.init_3d(width => 2, height => 3, depth => 5); + check_equal(arr.width, 2); + check_equal(arr.height, 3); + check_equal(arr.depth, 5); + arr.clear; + check_equal(arr.width, 0); + check_equal(arr.height, 0); + check_equal(arr.depth, 0); + + elsif run("Can save and load csv") then + arr.append(integer'left); + arr.append(0); + arr.append(integer'right); + arr.save_csv(output_path & "can_save.csv"); + other_arr.load_csv(output_path & "can_save.csv"); + check_equal(other_arr.length, arr.length); + for idx in 0 to arr.length-1 loop + check_equal(arr.get(idx), other_arr.get(idx)); + end loop; + + elsif run("Can save and load csv 2d") then + arr.append(integer'left); + arr.append(0); + arr.append(integer'right); + arr.append(1); + arr.reshape(2, 2); + arr.save_csv(output_path & "can_save_2d.csv"); + + other_arr.load_csv(output_path & "can_save_2d.csv"); + check_equal(other_arr.length, arr.length); + check_equal(other_arr.width, arr.width); + check_equal(other_arr.height, arr.height); + check_equal(other_arr.depth, arr.depth); + + for x in 0 to arr.width-1 loop + for y in 0 to arr.height-1 loop + check_equal(arr.get(x,y), other_arr.get(x,y)); + end loop; + end loop; + + elsif run("Can save and load csv 3d") then + for i in 0 to 30 loop + arr.append(i); + end loop; + arr.reshape(2, 3, 5); + arr.save_csv(output_path & "can_save_3d.csv"); + + other_arr.load_csv(output_path & "can_save_3d.csv"); + check_equal(other_arr.length, arr.length); + check_equal(other_arr.width, arr.width*arr.depth); + check_equal(other_arr.height, arr.height); + + for idx in 0 to arr.length-1 loop + check_equal(arr.get(idx), other_arr.get(idx)); + end loop; + + elsif run("Can save and load raw") then + for bit_width in 1 to 31 loop + for is_signed in 0 to 1 loop + test_save_and_load_raw(bit_width => bit_width, is_signed => is_signed=1); + end loop; + end loop; + test_save_and_load_raw(bit_width => 32, is_signed => true); + + elsif run("Save signed and load unsigned") then + arr.init(bit_width => 14, + is_signed => true); + arr.append(-1); + arr.append(-2**13); + arr.save_raw(output_path & "s14_to_u16.csv"); + other_arr.load_raw(output_path & "s14_to_u16.csv", + bit_width => 16, + is_signed => false); + check_equal(other_arr.length, 2); + check_equal(other_arr.get(0), 2**16-1); + check_equal(other_arr.get(1), 2**16 - 2**13); + end if; + + end loop; + test_runner_cleanup(runner); + wait; + end process; +end architecture; diff --git a/vunit/vhdl/check/run.py b/vunit/vhdl/check/run.py index d636a1204..3b073aaf0 100644 --- a/vunit/vhdl/check/run.py +++ b/vunit/vhdl/check/run.py @@ -1,41 +1,41 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname, basename -from vunit import VUnit, ROOT -from vunit.check_preprocessor import CheckPreprocessor -from glob import glob - -import sys -sys.path.append(join(dirname(__file__), "tools")) - -import generate_check_equal -import generate_check_match - -generate_check_equal.main() -generate_check_match.main() - -ui = VUnit.from_argv() - -lib = ui.add_library('lib') -lib.add_source_files(join(ROOT, "vunit", "vhdl", "check", "test", "test_support.vhd")) -logging_tb_lib = ui.add_library('logging_tb_lib') -logging_tb_lib.add_source_files(join(ROOT, "vunit", "vhdl", "logging", "test", "test_support_pkg.vhd")) - -for file_name in glob(join(ROOT, "vunit", "vhdl", "check", "test", "tb_*.vhd")): - if ui.vhdl_standard != '2008' and file_name.endswith("2008.vhd"): - continue - - if basename(file_name).startswith("tb_check_relation"): - lib.add_source_files(file_name, preprocessors=[CheckPreprocessor()]) - else: - lib.add_source_files(file_name) - -tb_check = lib.entity("tb_check") -tb_check.add_config(generics=dict(use_check_not_check_true=True), name="using check") -tb_check.add_config(generics=dict(use_check_not_check_true=False), name="using check_true") - -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname, basename +from vunit import VUnit, ROOT +from vunit.check_preprocessor import CheckPreprocessor +from glob import glob + +import sys +sys.path.append(join(dirname(__file__), "tools")) + +import generate_check_equal +import generate_check_match + +generate_check_equal.main() +generate_check_match.main() + +ui = VUnit.from_argv() + +lib = ui.add_library('lib') +lib.add_source_files(join(ROOT, "vunit", "vhdl", "check", "test", "test_support.vhd")) +logging_tb_lib = ui.add_library('logging_tb_lib') +logging_tb_lib.add_source_files(join(ROOT, "vunit", "vhdl", "logging", "test", "test_support_pkg.vhd")) + +for file_name in glob(join(ROOT, "vunit", "vhdl", "check", "test", "tb_*.vhd")): + if ui.vhdl_standard != '2008' and file_name.endswith("2008.vhd"): + continue + + if basename(file_name).startswith("tb_check_relation"): + lib.add_source_files(file_name, preprocessors=[CheckPreprocessor()]) + else: + lib.add_source_files(file_name) + +tb_check = lib.entity("tb_check") +tb_check.add_config(generics=dict(use_check_not_check_true=True), name="using check") +tb_check.add_config(generics=dict(use_check_not_check_true=False), name="using check_true") + +ui.main() diff --git a/vunit/vhdl/check/src/check.vhd b/vunit/vhdl/check/src/check.vhd index 176d46228..e7761e3b7 100644 --- a/vunit/vhdl/check/src/check.vhd +++ b/vunit/vhdl/check/src/check.vhd @@ -1,4788 +1,4788 @@ --- The check package provides the primary checking functionality. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use std.textio.all; -use work.checker_pkg.all; -use work.string_ops.all; - -package body check_pkg is - type boolean_vector is array (natural range <>) of boolean; - - function logical_right_shift ( - constant arg : boolean_vector; - constant count : natural) - return boolean_vector is - variable ret_val : boolean_vector(0 to arg'length - 1) := (others => false); - constant temp : boolean_vector(0 to arg'length - 1) := arg; - begin - ret_val(count to ret_val'right) := temp(0 to ret_val'right - count); - - return ret_val; - end function logical_right_shift; - constant max_supported_num_of_bits_in_integer_implementation : natural := 256; - - function std_msg ( - constant check_result : string; - constant msg : string; - constant ctx : string) - return string is - constant msg_i : string(1 to msg'length) := msg; - - function replace_result_tag (msg, check_result : string) return string is - begin - if msg'length < check_result_tag'length then - return msg; - elsif msg(1 to check_result_tag'length) = check_result_tag then - return check_result & msg(check_result_tag'length + 1 to msg'right); - else - return msg; - end if; - end function replace_result_tag; - - function append_context (msg, ctx : string) return string is - begin - if msg = "" then - return ctx; - elsif ctx = "" then - return msg; - else - return msg & " - " & ctx; - end if; - end function append_context; - begin - return append_context(replace_result_tag(msg_i, check_result), ctx); - end function std_msg; - - - procedure get_checker_stat ( - variable stat : out checker_stat_t) is - begin - get_checker_stat(default_checker, stat); - end; - - impure function get_checker_stat - return checker_stat_t is - variable stat : checker_stat_t; - begin - get_checker_stat(default_checker, stat); - return stat; - end function get_checker_stat; - - procedure reset_checker_stat is - begin - reset_checker_stat(default_checker); - end reset_checker_stat; - - procedure wait_on_edge ( - signal clock : in std_logic; - signal en : in std_logic; - constant active_clock_edge : in edge_t; - constant n_edges : in positive := 1) is - begin - for i in 1 to n_edges loop - if active_clock_edge = rising_edge then - wait until rising_edge(clock) and (to_x01(en) = '1'); - elsif active_clock_edge = falling_edge then - wait until falling_edge(clock) and (to_x01(en) = '1'); - elsif active_clock_edge = both_edges then - wait until (falling_edge(clock) or rising_edge(clock)) and (to_x01(en) = '1'); - else - wait; - end if; - end loop; - end wait_on_edge; - - function start_condition ( - signal clock : std_logic; - constant active_clock_edge : edge_t; - signal start_event : std_logic; - signal en : std_logic) - return boolean is - begin - if (to_x01(start_event) = '0') or (to_x01(en) /= '1') then - return false; - elsif active_clock_edge = rising_edge then - return rising_edge(clock); - elsif active_clock_edge = falling_edge then - return falling_edge(clock); - elsif active_clock_edge = both_edges then - return falling_edge(clock) or rising_edge(clock); - else - return false; - end if; - end start_condition; - - function result (msg : string := "") return string is - begin - if msg = "" then - return check_result_tag; - elsif msg(msg'left) = '.' or msg(msg'left) = ',' or msg(msg'left) = ':' or - msg(msg'left) = ';' or msg(msg'left) = '?' or msg(msg'left) = '!' then - return check_result_tag & msg; - else - return check_result_tag & " " & msg; - end if; - end; - - function to_ordinal_number (num : natural) return string is - constant num_str : string := natural'image(num); - variable ordinal_unit : string(1 to 2); - begin - case num_str(num_str'right) is - when '1' => ordinal_unit := "st"; - when '2' => ordinal_unit := "nd"; - when '3' => ordinal_unit := "rd"; - when others => ordinal_unit := "th"; - end case; - - if num_str'length > 1 then - if num_str(num_str'right - 1) = '1' then - ordinal_unit := "th"; - end if; - end if; - - return num_str & ordinal_unit; - end function to_ordinal_number; - - ----------------------------------------------------------------------------- - -- check - ----------------------------------------------------------------------------- - procedure check( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - wait_on_edge(clock, en, active_clock_edge); - check(checker, to_x01(expr) = '1', msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if expr then - pass := true; - if is_pass_visible(checker) then - passing_check(checker, std_msg("Check passed", msg, ""), line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check(checker, std_msg("Check failed", msg, ""), level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check( - constant checker : in checker_t; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check( - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check( - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check( - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check( - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check(default_checker, clock, en, expr, msg, level, active_clock_edge, line_num, file_name); - -- pragma translate_on - end; - - impure function check( - constant checker : in checker_t; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - ----------------------------------------------------------------------------- - -- check_passed - ----------------------------------------------------------------------------- - procedure check_passed( - constant checker : in checker_t; - constant msg : in string := check_result_tag & "."; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if is_pass_visible(checker) then - passing_check(checker, std_msg("Unconditional check passed", msg, ""), line_num, file_name); - else - passing_check(checker); - end if; - -- pragma translate_on - end; - - procedure check_passed( - constant msg : in string := check_result_tag & "."; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_passed(default_checker, msg, line_num, file_name); - -- pragma translate_on - end; - - ----------------------------------------------------------------------------- - -- check_failed - ----------------------------------------------------------------------------- - procedure check_failed( - constant checker : in checker_t; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - failing_check(checker, std_msg("Unconditional check failed", msg, ""), level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_failed( - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - failing_check(default_checker, std_msg("Unconditional check failed", msg, ""), level, line_num, file_name); - -- pragma translate_on - end; - - ----------------------------------------------------------------------------- - -- check_true - ----------------------------------------------------------------------------- - procedure check_true( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - wait_on_edge(clock, en, active_clock_edge); - check_true(checker, to_x01(expr) = '1', msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_true( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if expr then - pass := true; - if is_pass_visible(checker) then - passing_check(checker, std_msg("True check passed", msg, ""), line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check(checker, std_msg("True check failed", msg, ""), level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_true( - constant checker : in checker_t; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_true(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_true( - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_true(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_true( - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_true(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_true( - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_true(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_true( - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_true(default_checker, clock, en, expr, msg, level, active_clock_edge, line_num, file_name); - -- pragma translate_on - end; - - impure function check_true( - constant checker : in checker_t; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_true(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - ----------------------------------------------------------------------------- - -- check_false - ----------------------------------------------------------------------------- - procedure check_false( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - wait_on_edge(clock, en, active_clock_edge); - check_false(checker, to_x01(expr) /= '0', msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_false( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if not expr then - pass := true; - if is_pass_visible(checker) then - passing_check(checker, std_msg("False check passed", msg, ""), line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check(checker, std_msg("False check failed", msg, ""), level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_false( - constant checker : in checker_t; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_false(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_false( - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_false(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_false( - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_false(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_false( - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_false(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_false( - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_false(default_checker, clock, en, expr, msg, level, active_clock_edge, line_num, file_name); - -- pragma translate_on - end; - - impure function check_false( - constant checker : in checker_t; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_false(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - ----------------------------------------------------------------------------- - -- check_implication - ----------------------------------------------------------------------------- - procedure check_implication( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal antecedent_expr : in std_logic; - signal consequent_expr : in std_logic; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - wait_on_edge(clock, en, active_clock_edge); - check_implication(checker, pass, to_x01(antecedent_expr) /= '0', - to_x01(consequent_expr) = '1', msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_implication( - constant checker : in checker_t; - variable pass : out boolean; - constant antecedent_expr : in boolean; - constant consequent_expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if (not antecedent_expr) or consequent_expr then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Implication check passed", msg, - "Got " & boolean'image(antecedent_expr) & " -> " & boolean'image(consequent_expr) & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check(checker, std_msg("Implication check failed", msg, ""), level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_implication( - signal clock : in std_logic; - signal en : in std_logic; - signal antecedent_expr : in std_logic; - signal consequent_expr : in std_logic; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_implication(default_checker, clock, en, antecedent_expr, consequent_expr, msg, level, active_clock_edge, line_num, file_name); - -- pragma translate_on - end; - - procedure check_implication( - constant checker : in checker_t; - constant antecedent_expr : in boolean; - constant consequent_expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_implication(checker, pass, antecedent_expr, consequent_expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_implication( - constant antecedent_expr : in boolean; - constant consequent_expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_implication(default_checker, pass, antecedent_expr, consequent_expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_implication( - variable pass : out boolean; - constant antecedent_expr : in boolean; - constant consequent_expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_implication(default_checker, pass, antecedent_expr, consequent_expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_implication( - constant antecedent_expr : in boolean; - constant consequent_expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_implication(default_checker, pass, antecedent_expr, consequent_expr, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_implication( - constant checker : in checker_t; - constant antecedent_expr : in boolean; - constant consequent_expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_implication(checker, pass, antecedent_expr, consequent_expr, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - ----------------------------------------------------------------------------- - -- check_stable - ----------------------------------------------------------------------------- - type check_stable_fsm_state_t is (idle, active_window); - - procedure run_stability_check ( - constant checker : in checker_t; - constant start_event : in std_logic; - constant end_event : in std_logic; - constant expr : in std_logic_vector; - constant msg : in string; - constant level : in log_level_t; - constant active_clock_edge : in edge_t; - constant allow_restart : in boolean; - constant line_num : in natural; - constant file_name : in string; - - variable state : inout check_stable_fsm_state_t; - variable ref : inout std_logic_vector; - variable clock_edge_counter : inout natural; - variable is_stable : inout boolean; - variable exit_stability_check : out boolean) is - - function format (expr : std_logic) return character is - begin - return std_logic'image(expr)(2); - end; - - function format (expr : std_logic_vector) return string is - begin - if expr'length = 1 then - return (1 => format(expr(expr'left))); - else - return to_nibble_string(expr) & " (" & to_integer_string(expr) & ")"; - end if; - - end; - - procedure open_window (variable open_ok : out boolean) is - begin - clock_edge_counter := 1; - ref := to_x01(expr); - open_ok := true; - if is_x(start_event) then - open_ok := false; - failing_check(checker, - std_msg("Stability check failed", msg, - "Start event is " & format(start_event) & "."), - level, line_num, file_name); - elsif is_x(expr) then - open_ok := false; - failing_check(checker, - std_msg("Stability check failed", msg, - "Got " & format(expr) & - " at 1st active and enabled clock edge."), - level, line_num, file_name); - end if; - end procedure; - - procedure close_window(cycle : natural; is_ok : boolean) is - variable close_ok : boolean := is_ok; - variable pass_msg_en : boolean; - begin - if is_x(end_event) then - close_ok := false; - failing_check(checker, - std_msg("Stability check failed", msg, - "End event is " & format(end_event) & "."), - level, line_num, file_name); - end if; - - if close_ok then - if is_pass_visible(checker) then - passing_check(checker, - std_msg("Stability check passed", msg, - "Got " & format(ref) & - " for " & positive'image(cycle) & - " active and enabled clock edges."), - line_num, file_name); - else - passing_check(checker); - end if; - end if; - end procedure close_window; - - variable open_ok : boolean; - begin - exit_stability_check := false; - case state is - - when idle => - if to_x01(start_event) /= '0' then - open_window(open_ok); - if not open_ok then - exit_stability_check := true; - return; - elsif to_x01(end_event) /= '0' then - close_window(clock_edge_counter, is_ok => true); - exit_stability_check := true; - return; - else - state := active_window; - end if; - end if; - - when active_window => - clock_edge_counter := clock_edge_counter + 1; - - if to_x01(start_event) /= '0' and allow_restart then - close_window(cycle => clock_edge_counter - 1, is_ok => true); - open_window(open_ok); - if not open_ok then - exit_stability_check := true; - return; - end if; - - elsif ref /= to_x01(expr) then - is_stable := false; - failing_check(checker, - std_msg("Stability check failed", msg, - "Got " & format(expr) & - " at " & to_ordinal_number(clock_edge_counter) & - " active and enabled clock edge. Expected " & - format(ref) & "."), level, line_num, file_name); - end if; - - if to_x01(end_event) /= '0' then - close_window(clock_edge_counter, is_ok => is_stable); - exit_stability_check := true; - return; - end if; - - end case; - - end procedure run_stability_check; - - procedure check_stable( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal start_event : in std_logic; - signal end_event : in std_logic; - signal expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant allow_restart : in boolean := false; - constant line_num : in natural := 0; - constant file_name : in string := "") is - - variable state : check_stable_fsm_state_t := idle; - variable ref : std_logic_vector(expr'range); - variable clock_edge_counter : natural; - variable is_stable : boolean := true; - variable exit_stability_check : boolean; - begin - -- pragma translate_off - stability_check : loop - wait_on_edge(clock, en, active_clock_edge); - - run_stability_check(checker, start_event, end_event, expr, msg, level, active_clock_edge, - allow_restart, line_num, file_name, state, ref, clock_edge_counter, - is_stable, exit_stability_check); - exit when exit_stability_check; - end loop; - -- pragma translate_on - end; - - procedure check_stable( - signal clock : in std_logic; - signal en : in std_logic; - signal start_event : in std_logic; - signal end_event : in std_logic; - signal expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant allow_restart : in boolean := false; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_stable(default_checker, clock, en, start_event, end_event, expr, msg, level, active_clock_edge, - allow_restart, line_num, file_name); - -- pragma translate_on - end; - - procedure check_stable( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal start_event : in std_logic; - signal end_event : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant allow_restart : in boolean := false; - constant line_num : in natural := 0; - constant file_name : in string := "") is - - variable state : check_stable_fsm_state_t := idle; - variable ref : std_logic_vector(0 to 0); - variable clock_edge_counter : natural; - variable is_stable : boolean := true; - variable exit_stability_check : boolean; - begin - -- pragma translate_off - stability_check : loop - wait_on_edge(clock, en, active_clock_edge); - - run_stability_check(checker, start_event, end_event, (0 => expr), msg, level, active_clock_edge, - allow_restart, line_num, file_name, state, ref, clock_edge_counter, - is_stable, exit_stability_check); - exit when exit_stability_check; - end loop; - -- pragma translate_on - end; - - procedure check_stable( - signal clock : in std_logic; - signal en : in std_logic; - signal start_event : in std_logic; - signal end_event : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant allow_restart : in boolean := false; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_stable(default_checker, clock, en, start_event, end_event, expr, msg, level, active_clock_edge, - allow_restart, line_num, file_name); - -- pragma translate_on - end; - - ----------------------------------------------------------------------------- - -- check_not_unknown - ----------------------------------------------------------------------------- - procedure check_not_unknown( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - wait_on_edge(clock, en, active_clock_edge); - check_not_unknown(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_not_unknown( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if not is_x(expr) then - pass := true; - if is_pass_visible(checker) then - passing_check(checker, - std_msg("Not unknown check passed", - msg, - "Got " & to_nibble_string(expr) & " (" & to_integer_string(expr) & ")" & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check(checker, - std_msg("Not unknown check failed", - msg, - "Got " & to_nibble_string(expr) & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_not_unknown( - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_not_unknown(default_checker, clock, en, expr, msg, level, active_clock_edge, line_num, file_name); - -- pragma translate_on - end; - - procedure check_not_unknown( - constant checker : in checker_t; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_not_unknown(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_not_unknown( - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_not_unknown(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_not_unknown( - variable pass : out boolean; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_not_unknown(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_not_unknown( - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_not_unknown(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_not_unknown( - constant checker : in checker_t; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_not_unknown(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_not_unknown( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - wait_on_edge(clock, en, active_clock_edge); - check_not_unknown(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_not_unknown( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if not is_x(expr) then - pass := true; - if is_pass_visible(checker) then - passing_check(checker, - std_msg("Not unknown check passed", - msg, - "Got " & std_logic'image(expr)(2) & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check(checker, - std_msg("Not unknown check failed", - msg, - "Got " & std_logic'image(expr)(2) & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_not_unknown( - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_not_unknown(default_checker, clock, en, expr, msg, level, active_clock_edge, line_num, file_name); - -- pragma translate_on - end; - - procedure check_not_unknown( - constant checker : in checker_t; - constant expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_not_unknown(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_not_unknown( - constant expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_not_unknown(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_not_unknown( - variable pass : out boolean; - constant expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_not_unknown(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_not_unknown( - constant expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_not_unknown(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_not_unknown( - constant checker : in checker_t; - constant expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_not_unknown(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - ----------------------------------------------------------------------------- - -- check_zero_one_hot - ----------------------------------------------------------------------------- - function n_hot_in_valid_range ( - constant expr : std_logic_vector; - constant lower_bound : in natural; - constant upper_bound : in natural) - return boolean is - variable n : natural := 0; - begin - if is_x(expr) then - return false; - end if; - for i in expr'range loop - if to_x01(expr(i)) = '1' then - n := n + 1; - end if; - end loop; - - return (n >= lower_bound) and (n <= upper_bound); - end function n_hot_in_valid_range; - - procedure check_zero_one_hot( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - wait_on_edge(clock, en, active_clock_edge); - check_zero_one_hot(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_zero_one_hot( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if n_hot_in_valid_range(expr, 0, 1) then - pass := true; - if is_pass_visible(checker) then - passing_check(checker, - std_msg("Zero one-hot check passed", msg, - "Got " & to_nibble_string(expr) & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check(checker, - std_msg("Zero one-hot check failed", msg, - "Got " & to_nibble_string(expr) & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_zero_one_hot( - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_zero_one_hot(default_checker, clock, en, expr, msg, level, active_clock_edge, line_num, file_name); - -- pragma translate_on - end; - - procedure check_zero_one_hot( - variable pass : out boolean; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_zero_one_hot(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_zero_one_hot( - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_zero_one_hot(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_zero_one_hot( - constant checker : in checker_t; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_zero_one_hot(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_zero_one_hot( - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_zero_one_hot(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_zero_one_hot( - constant checker : in checker_t; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_zero_one_hot(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - ----------------------------------------------------------------------------- - -- check_one_hot - ----------------------------------------------------------------------------- - procedure check_one_hot( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - wait_on_edge(clock, en, active_clock_edge); - check_one_hot(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_one_hot( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if n_hot_in_valid_range(expr, 1, 1) then - pass := true; - if is_pass_visible(checker) then - passing_check(checker, - std_msg("One-hot check passed", msg, - "Got " & to_nibble_string(expr) & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check(checker, - std_msg("One-hot check failed", msg, - "Got " & to_nibble_string(expr) & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_one_hot( - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_one_hot(default_checker, clock, en, expr, msg, level, active_clock_edge, line_num, file_name); - -- pragma translate_on - end; - - procedure check_one_hot( - variable pass : out boolean; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_one_hot(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_one_hot( - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_one_hot(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_one_hot( - constant checker : in checker_t; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_one_hot(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_one_hot( - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_one_hot(default_checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_one_hot( - constant checker : in checker_t; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_one_hot(checker, pass, expr, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - ----------------------------------------------------------------------------- - -- check_next - ----------------------------------------------------------------------------- - procedure check_next( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal start_event : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag; - constant num_cks : in natural := 1; - constant allow_overlapping : in boolean := true; - constant allow_missing_start : in boolean := true; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - - variable schedule : boolean_vector(0 to num_cks) := (others => false); - variable clock_cycles_after_start_event : natural; - - function check_is_scheduled( - constant schedule : in boolean_vector) - return boolean is - begin - return schedule(0); - end function check_is_scheduled; - - procedure schedule_check( - variable schedule : inout boolean_vector; - constant num_cks : in natural) is - begin - schedule(num_cks) := true; - end procedure schedule_check; - - procedure update_remaining_times_to_scheduled_checks( - variable schedule : inout boolean_vector; - constant num_cks : in natural) is - begin - schedule(0 to num_cks - 1) := schedule(1 to num_cks); - schedule(num_cks) := false; - end procedure update_remaining_times_to_scheduled_checks; - - function pending_check ( - constant schedule : boolean_vector) - return boolean is - constant no_pending_checks : boolean_vector(1 to schedule'right) := (others => false); - begin - return schedule(1 to schedule'right) /= no_pending_checks; - end function pending_check; - - procedure check_expr is - begin - if to_x01(expr) = '1' then - if is_pass_visible(checker) then - passing_check(checker, std_msg("Next check passed", msg, ""), line_num, file_name); - else - passing_check(checker); - end if; - else - failing_check(checker, - std_msg("Next check failed", msg, - "Got " & std_logic'image(expr)(2) & - " at the " & to_ordinal_number(num_cks) & - " active and enabled clock edge."), - level, line_num, file_name); - end if; - end procedure check_expr; - - begin - -- pragma translate_off - while true loop - wait_on_edge(clock, en, active_clock_edge); - clock_cycles_after_start_event := clock_cycles_after_start_event + 1; - - if to_x01(start_event) = '1' then - if pending_check(schedule) and not allow_overlapping then - failing_check(checker, - std_msg("Next check failed", msg, - "Got overlapping start event at the " & - to_ordinal_number(clock_cycles_after_start_event) & - " active and enabled clock edge."), - level, line_num, file_name); - else - schedule_check(schedule, num_cks); - clock_cycles_after_start_event := 0; - end if; - elsif to_x01(start_event) = 'X' then - failing_check(checker, - std_msg("Next check failed", msg, - "Start event is " & std_logic'image(start_event)(2) & "."), - level, line_num, file_name); - end if; - - if check_is_scheduled(schedule) then - check_expr; - elsif (to_x01(expr) = '1') and not allow_missing_start then - failing_check(checker, - std_msg("Next check failed", msg, - "Missing start event for true expression."), - level, line_num, file_name); - end if; - - - update_remaining_times_to_scheduled_checks(schedule, num_cks); - end loop; - -- pragma translate_on - end; - - procedure check_next( - signal clock : in std_logic; - signal en : in std_logic; - signal start_event : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag; - constant num_cks : in natural := 1; - constant allow_overlapping : in boolean := true; - constant allow_missing_start : in boolean := true; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_next(default_checker, clock, en, start_event, expr, msg, num_cks, allow_overlapping, - allow_missing_start, level, active_clock_edge, line_num, file_name); - -- pragma translate_on - end; - - ----------------------------------------------------------------------------- - -- check_sequence - ----------------------------------------------------------------------------- - procedure check_sequence( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal event_sequence : in std_logic_vector; - constant msg : in string := check_result_tag; - constant trigger_event : in trigger_event_t := penultimate; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - - variable expected_events : boolean_vector(0 to event_sequence'length - 1) := (others => false); - variable tracks : boolean_vector(0 to event_sequence'length - 1) := (others => false); - - procedure find_new_and_update_existing_tracks ( - variable tracks : inout boolean_vector; - constant event_sequence : in std_logic_vector) is - - constant seq : std_logic_vector(0 to event_sequence'length - 1) := event_sequence; - variable unknown_event_in_sequence : boolean := false; - - function active_tracks ( - constant tracks : in boolean_vector) - return boolean is - begin - for i in tracks'range loop - if tracks(i) then - return true; - end if; - end loop; - return false; - end function active_tracks; - begin - for i in tracks'reverse_range loop - if to_x01(seq(i)) = 'X' then - -- FIXME: check moved out of loop to work with GHDL 0.33. - unknown_event_in_sequence := true; - end if; - - if i = 0 then - if (trigger_event = first_no_pipe) and active_tracks(tracks) then - tracks(0) := false; - else - tracks(0) := (to_x01(seq(seq'left)) = '1'); - end if; - else - tracks(i) := (tracks(i - 1) and (to_x01(seq(i)) = '1')); - end if; - end loop; - - -- FIXME: check moved out of loop to work with GHDL 0.33. - if unknown_event_in_sequence then - failing_check(checker, - std_msg("Sequence check failed", msg, - "Got " & to_nibble_string(seq) & "."), - level, line_num, file_name); - end if; - end find_new_and_update_existing_tracks; - - procedure update_expectations_on_events_in_next_cycle ( - constant tracks : in boolean_vector; - variable expected_events : inout boolean_vector) is - begin - if trigger_event = penultimate then - expected_events(expected_events'right - 1) := tracks(tracks'right - 1); - else - expected_events(expected_events'range) := tracks(expected_events'range); - end if; - expected_events := logical_right_shift(expected_events, 1); - end procedure update_expectations_on_events_in_next_cycle; - - procedure verify_expected_events ( - constant expected_events : in boolean_vector; - constant event_sequence : in std_logic_vector) is - constant seq : std_logic_vector(0 to event_sequence'length - 1) := event_sequence; - begin - for i in 1 to seq'right loop - if expected_events(i) then - if to_x01(seq(i)) /= '1' then - failing_check(checker, - std_msg("Sequence check failed", msg, - "Missing required event at " & - to_ordinal_number(i) & - " active and enabled clock edge."), - level, line_num, file_name); - elsif i = seq'right then - if is_pass_visible(checker) then - passing_check(checker, std_msg("Sequence check passed", msg, ""), line_num, file_name); - else - passing_check(checker); - end if; - end if; - end if; - end loop; - end procedure verify_expected_events; - - variable valid_event_sequence_length : boolean; - begin - -- pragma translate_off - valid_event_sequence_length := event_sequence'length >= 2; - if not valid_event_sequence_length then - failing_check(checker, - std_msg("Sequence check failed", msg, - "Event sequence length must be at least 2. Got " & - natural'image(event_sequence'length) & "."), - level, line_num, file_name); - end if; - - wait_on_edge(clock, en, active_clock_edge); - while valid_event_sequence_length loop - find_new_and_update_existing_tracks(tracks, event_sequence); - update_expectations_on_events_in_next_cycle(tracks, expected_events); - wait_on_edge(clock, en, active_clock_edge); - verify_expected_events(expected_events, event_sequence); - end loop; - - wait; - -- pragma translate_on - end; - - procedure check_sequence( - signal clock : in std_logic; - signal en : in std_logic; - signal event_sequence : in std_logic_vector; - constant msg : in string := check_result_tag; - constant trigger_event : in trigger_event_t := penultimate; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_sequence(default_checker, clock, en, event_sequence, msg, trigger_event, level, active_clock_edge, - line_num, file_name); - -- pragma translate_on - end; - - ----------------------------------------------------------------------------- - -- check_relation - ----------------------------------------------------------------------------- - procedure check_relation( - constant checker : in checker_t; - constant expr : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_relation(checker, pass, expr, msg, level, context_msg, line_num, file_name); - -- pragma translate_on - end; - - procedure check_relation( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if expr then - pass := true; - if is_pass_visible(checker) then - passing_check(checker, std_msg("Relation check passed", msg, context_msg), line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check(checker, std_msg("Relation check failed", msg, context_msg), level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_relation( - constant expr : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_relation(default_checker, pass, expr, msg, level, context_msg, line_num, file_name); - -- pragma translate_on - end; - - procedure check_relation( - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_relation(default_checker, pass, expr, msg, level, context_msg, line_num, file_name); - -- pragma translate_on - end; - - impure function check_relation( - constant expr : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_relation(default_checker, pass, expr, msg, level, context_msg, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_relation( - constant checker : in checker_t; - constant expr : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_relation(checker, pass, expr, msg, level, context_msg, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_relation( - constant checker : in checker_t; - constant expr : in std_ulogic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_relation(checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); - -- pragma translate_on - end; - - procedure check_relation( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in std_ulogic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_relation(checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); - -- pragma translate_on - end; - - procedure check_relation( - constant expr : in std_ulogic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_relation(default_checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); - -- pragma translate_on - end; - - procedure check_relation( - variable pass : out boolean; - constant expr : in std_ulogic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_relation(default_checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); - -- pragma translate_on - end; - - impure function check_relation( - constant expr : in std_ulogic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_relation(default_checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_relation( - constant checker : in checker_t; - constant expr : in std_ulogic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_relation(checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_relation( - constant checker : in checker_t; - constant expr : in bit; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_relation(checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); - -- pragma translate_on - end; - - procedure check_relation( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in bit; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_relation(checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); - -- pragma translate_on - end; - - procedure check_relation( - constant expr : in bit; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_relation(default_checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); - -- pragma translate_on - end; - - procedure check_relation( - variable pass : out boolean; - constant expr : in bit; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_relation(default_checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); - -- pragma translate_on - end; - - impure function check_relation( - constant expr : in bit; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_relation(default_checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_relation( - constant checker : in checker_t; - constant expr : in bit; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_relation(default_checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); - -- pragma translate_on - return pass; - end; - - function "=" ( - constant left : unsigned; - constant right : std_logic_vector) - return boolean is - begin - return left = unsigned(right); - end function "="; - - function "=" ( - constant left : std_logic_vector; - constant right : unsigned) - return boolean is - begin - return unsigned(left) = right; - end function "="; - - function "=" ( - constant left : natural; - constant right : std_logic_vector) - return boolean is - begin - return left = unsigned(right); - end function "="; - - function "=" ( - constant left : std_logic_vector; - constant right : natural) - return boolean is - begin - return unsigned(left) = right; - end function "="; - - function "=" ( - constant left : boolean; - constant right : std_logic) - return boolean is - begin - return left = (right = '1'); - end function "="; - - function "=" ( - constant left : std_logic; - constant right : boolean) - return boolean is - begin - return (left = '1') = right; - end function "="; - - function to_char ( - constant bit : std_logic) - return character is - constant chars : string(1 to 9) := "UX01ZWLH-"; - begin - return chars(std_logic'pos(bit) + 1); - end function to_char; - - function to_string ( - constant data : std_logic) - return string is - variable ret_val : string(1 to 1); - begin - ret_val(1) := to_char(data); - return ret_val; - end function to_string; - - function to_string ( - constant data : boolean) - return string is - begin - if data then - return "true"; - else - return "false"; - end if; - end function to_string; - - function to_string ( - constant data : integer) - return string is - begin - return integer'image(data); - end function to_string; - - function to_string ( - constant data : string) - return string is - begin - return data; - end function to_string; - - function to_string ( - constant data : time) - return string is - begin - return time'image(data); - end function to_string; - - function max ( - constant value_1 : integer; - constant value_2 : integer) - return integer is - begin - if value_1 > value_2 then - return value_1; - else - return value_2; - end if; - end max; - - function required_num_of_unsigned_bits ( - constant value : natural) - return natural is - variable max_value : natural := 0; - variable required_length : natural := 1; - begin - for i in 0 to max_supported_num_of_bits_in_integer_implementation - 2 loop - max_value := max_value + 2 ** i; - exit when max_value >= value; - required_length := required_length + 1; - end loop; - - return required_length; - end required_num_of_unsigned_bits; - - function to_sufficient_unsigned ( - constant value : natural; - constant min_length : natural) - return unsigned is - begin - return to_unsigned(value, max(min_length, required_num_of_unsigned_bits(value))); - end to_sufficient_unsigned; - - function to_sufficient_signed ( - constant value : integer; - constant min_length : natural) - return signed is - variable min_value : integer := -1; - variable required_length : natural := 1; - begin - if value < 0 then - for i in 0 to max_supported_num_of_bits_in_integer_implementation - 1 loop - exit when min_value <= value; - min_value := min_value * 2; - required_length := required_length + 1; - end loop; - - return to_signed(value, max(min_length, required_length)); - else - return signed(to_unsigned(natural(value), max(min_length, required_num_of_unsigned_bits(natural(value)) + 1))); - end if; - end to_sufficient_signed; - - ----------------------------------------------------------------------------- - -- check_(almost)_equal for real - ----------------------------------------------------------------------------- - - procedure check_equal( - constant got : in real; - constant expected : in real; - constant msg : in string := check_result_tag; - constant max_diff : in real := 0.0; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, got, expected, msg, max_diff, level, line_num, file_name); - -- pragma translate_on - end; - - - procedure check_equal( - constant checker : in checker_t; - constant got : in real; - constant expected : in real; - constant msg : in string := check_result_tag; - constant max_diff : in real := 0.0; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if abs (got - expected) <= max_diff then - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got abs (" & real'image(got) & " - " & real'image(expected) & ") <= " & real'image(max_diff) & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got abs (" & real'image(got) & " - " & real'image(expected) & ") > " & real'image(max_diff) & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - ----------------------------------------------------------------------------- - -- check_equal - ----------------------------------------------------------------------------- - procedure check_equal( - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & - "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_equal( - constant got : in unsigned; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in unsigned; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in unsigned; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & - "Expected " & to_string(expected) & " (" & to_nibble_string(to_sufficient_unsigned(expected, got'length)) & ")" & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in unsigned; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in unsigned; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in unsigned; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_equal( - constant got : in natural; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in natural; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in natural; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_string(got) & " (" & to_nibble_string(to_sufficient_unsigned(got, expected'length)) & ")" & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_string(got) & " (" & to_nibble_string(to_sufficient_unsigned(got, expected'length)) & ")" & ". " & - "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in natural; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in natural; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in natural; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_equal( - constant got : in unsigned; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in unsigned; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in unsigned; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & - "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in unsigned; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in unsigned; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in unsigned; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_equal( - constant got : in std_logic_vector; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in std_logic_vector; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in std_logic_vector; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & - "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in std_logic_vector; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in std_logic_vector; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in std_logic_vector; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_equal( - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & - "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_equal( - constant got : in std_logic_vector; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in std_logic_vector; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in std_logic_vector; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & - "Expected " & to_string(expected) & " (" & to_nibble_string(to_sufficient_unsigned(expected, got'length)) & ")" & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in std_logic_vector; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in std_logic_vector; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in std_logic_vector; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_equal( - constant got : in natural; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in natural; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in natural; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_string(got) & " (" & to_nibble_string(to_sufficient_unsigned(got, expected'length)) & ")" & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_string(got) & " (" & to_nibble_string(to_sufficient_unsigned(got, expected'length)) & ")" & ". " & - "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in natural; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in natural; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in natural; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_equal( - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & - "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_equal( - constant got : in signed; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in signed; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in signed; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & - "Expected " & to_string(expected) & " (" & to_nibble_string(to_sufficient_signed(expected, got'length)) & ")" & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in signed; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in signed; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in signed; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_equal( - constant got : in integer; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in integer; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in integer; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_string(got) & " (" & to_nibble_string(to_sufficient_signed(got, expected'length)) & ")" & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_string(got) & " (" & to_nibble_string(to_sufficient_signed(got, expected'length)) & ")" & ". " & - "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in integer; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in integer; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in integer; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_equal( - constant got : in integer; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in integer; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in integer; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_string(got) & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_string(got) & ". " & - "Expected " & to_string(expected) & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in integer; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in integer; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in integer; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_equal( - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_string(got) & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_string(got) & ". " & - "Expected " & to_string(expected) & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_equal( - constant got : in std_logic; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in std_logic; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in std_logic; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_string(got) & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_string(got) & ". " & - "Expected " & to_string(expected) & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in std_logic; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in std_logic; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in std_logic; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_equal( - constant got : in boolean; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in boolean; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in boolean; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_string(got) & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_string(got) & ". " & - "Expected " & to_string(expected) & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in boolean; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in boolean; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in boolean; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_equal( - constant got : in boolean; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in boolean; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in boolean; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_string(got) & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_string(got) & ". " & - "Expected " & to_string(expected) & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in boolean; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in boolean; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in boolean; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_equal( - constant got : in string; - constant expected : in string; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in string; - constant expected : in string; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in string; - constant expected : in string; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_string(got) & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_string(got) & ". " & - "Expected " & to_string(expected) & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in string; - constant expected : in string; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in string; - constant expected : in string; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in string; - constant expected : in string; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_equal( - constant got : in time; - constant expected : in time; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in time; - constant expected : in time; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in time; - constant expected : in time; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & to_string(got) & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & to_string(got) & ". " & - "Expected " & to_string(expected) & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in time; - constant expected : in time; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in time; - constant expected : in time; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in time; - constant expected : in time; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - ----------------------------------------------------------------------------- - -- check_match - ----------------------------------------------------------------------------- - procedure check_match( - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_match( - variable pass : out boolean; - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_match( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if std_match(got, expected) then - pass := true; - - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Match check passed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & - "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Match check failed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & - "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_match( - constant checker : in checker_t; - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_match(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_match( - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_match( - constant checker : in checker_t; - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_match(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_match( - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_match( - variable pass : out boolean; - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_match( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if std_match(got, expected) then - pass := true; - - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Match check passed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & - "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Match check failed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & - "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_match( - constant checker : in checker_t; - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_match(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_match( - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_match( - constant checker : in checker_t; - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_match(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_match( - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_match( - variable pass : out boolean; - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_match( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if std_match(got, expected) then - pass := true; - - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Match check passed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & - "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Match check failed", msg, - "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & - "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_match( - constant checker : in checker_t; - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_match(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_match( - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_match( - constant checker : in checker_t; - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_match(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - procedure check_match( - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_match( - variable pass : out boolean; - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_match( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if std_match(got, expected) then - pass := true; - - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Match check passed", msg, - "Got " & to_string(got) & ". " & - "Expected " & to_string(expected) & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Match check failed", msg, - "Got " & to_string(got) & ". " & - "Expected " & to_string(expected) & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_match( - constant checker : in checker_t; - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_match(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_match( - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_match( - constant checker : in checker_t; - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_match(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - ----------------------------------------------------------------------------- - -end package body check_pkg; +-- The check package provides the primary checking functionality. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use std.textio.all; +use work.checker_pkg.all; +use work.string_ops.all; + +package body check_pkg is + type boolean_vector is array (natural range <>) of boolean; + + function logical_right_shift ( + constant arg : boolean_vector; + constant count : natural) + return boolean_vector is + variable ret_val : boolean_vector(0 to arg'length - 1) := (others => false); + constant temp : boolean_vector(0 to arg'length - 1) := arg; + begin + ret_val(count to ret_val'right) := temp(0 to ret_val'right - count); + + return ret_val; + end function logical_right_shift; + constant max_supported_num_of_bits_in_integer_implementation : natural := 256; + + function std_msg ( + constant check_result : string; + constant msg : string; + constant ctx : string) + return string is + constant msg_i : string(1 to msg'length) := msg; + + function replace_result_tag (msg, check_result : string) return string is + begin + if msg'length < check_result_tag'length then + return msg; + elsif msg(1 to check_result_tag'length) = check_result_tag then + return check_result & msg(check_result_tag'length + 1 to msg'right); + else + return msg; + end if; + end function replace_result_tag; + + function append_context (msg, ctx : string) return string is + begin + if msg = "" then + return ctx; + elsif ctx = "" then + return msg; + else + return msg & " - " & ctx; + end if; + end function append_context; + begin + return append_context(replace_result_tag(msg_i, check_result), ctx); + end function std_msg; + + + procedure get_checker_stat ( + variable stat : out checker_stat_t) is + begin + get_checker_stat(default_checker, stat); + end; + + impure function get_checker_stat + return checker_stat_t is + variable stat : checker_stat_t; + begin + get_checker_stat(default_checker, stat); + return stat; + end function get_checker_stat; + + procedure reset_checker_stat is + begin + reset_checker_stat(default_checker); + end reset_checker_stat; + + procedure wait_on_edge ( + signal clock : in std_logic; + signal en : in std_logic; + constant active_clock_edge : in edge_t; + constant n_edges : in positive := 1) is + begin + for i in 1 to n_edges loop + if active_clock_edge = rising_edge then + wait until rising_edge(clock) and (to_x01(en) = '1'); + elsif active_clock_edge = falling_edge then + wait until falling_edge(clock) and (to_x01(en) = '1'); + elsif active_clock_edge = both_edges then + wait until (falling_edge(clock) or rising_edge(clock)) and (to_x01(en) = '1'); + else + wait; + end if; + end loop; + end wait_on_edge; + + function start_condition ( + signal clock : std_logic; + constant active_clock_edge : edge_t; + signal start_event : std_logic; + signal en : std_logic) + return boolean is + begin + if (to_x01(start_event) = '0') or (to_x01(en) /= '1') then + return false; + elsif active_clock_edge = rising_edge then + return rising_edge(clock); + elsif active_clock_edge = falling_edge then + return falling_edge(clock); + elsif active_clock_edge = both_edges then + return falling_edge(clock) or rising_edge(clock); + else + return false; + end if; + end start_condition; + + function result (msg : string := "") return string is + begin + if msg = "" then + return check_result_tag; + elsif msg(msg'left) = '.' or msg(msg'left) = ',' or msg(msg'left) = ':' or + msg(msg'left) = ';' or msg(msg'left) = '?' or msg(msg'left) = '!' then + return check_result_tag & msg; + else + return check_result_tag & " " & msg; + end if; + end; + + function to_ordinal_number (num : natural) return string is + constant num_str : string := natural'image(num); + variable ordinal_unit : string(1 to 2); + begin + case num_str(num_str'right) is + when '1' => ordinal_unit := "st"; + when '2' => ordinal_unit := "nd"; + when '3' => ordinal_unit := "rd"; + when others => ordinal_unit := "th"; + end case; + + if num_str'length > 1 then + if num_str(num_str'right - 1) = '1' then + ordinal_unit := "th"; + end if; + end if; + + return num_str & ordinal_unit; + end function to_ordinal_number; + + ----------------------------------------------------------------------------- + -- check + ----------------------------------------------------------------------------- + procedure check( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + wait_on_edge(clock, en, active_clock_edge); + check(checker, to_x01(expr) = '1', msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if expr then + pass := true; + if is_pass_visible(checker) then + passing_check(checker, std_msg("Check passed", msg, ""), line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check(checker, std_msg("Check failed", msg, ""), level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check( + constant checker : in checker_t; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check( + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check( + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check( + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check( + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check(default_checker, clock, en, expr, msg, level, active_clock_edge, line_num, file_name); + -- pragma translate_on + end; + + impure function check( + constant checker : in checker_t; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + ----------------------------------------------------------------------------- + -- check_passed + ----------------------------------------------------------------------------- + procedure check_passed( + constant checker : in checker_t; + constant msg : in string := check_result_tag & "."; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if is_pass_visible(checker) then + passing_check(checker, std_msg("Unconditional check passed", msg, ""), line_num, file_name); + else + passing_check(checker); + end if; + -- pragma translate_on + end; + + procedure check_passed( + constant msg : in string := check_result_tag & "."; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_passed(default_checker, msg, line_num, file_name); + -- pragma translate_on + end; + + ----------------------------------------------------------------------------- + -- check_failed + ----------------------------------------------------------------------------- + procedure check_failed( + constant checker : in checker_t; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + failing_check(checker, std_msg("Unconditional check failed", msg, ""), level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_failed( + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + failing_check(default_checker, std_msg("Unconditional check failed", msg, ""), level, line_num, file_name); + -- pragma translate_on + end; + + ----------------------------------------------------------------------------- + -- check_true + ----------------------------------------------------------------------------- + procedure check_true( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + wait_on_edge(clock, en, active_clock_edge); + check_true(checker, to_x01(expr) = '1', msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_true( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if expr then + pass := true; + if is_pass_visible(checker) then + passing_check(checker, std_msg("True check passed", msg, ""), line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check(checker, std_msg("True check failed", msg, ""), level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_true( + constant checker : in checker_t; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_true(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_true( + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_true(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_true( + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_true(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_true( + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_true(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_true( + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_true(default_checker, clock, en, expr, msg, level, active_clock_edge, line_num, file_name); + -- pragma translate_on + end; + + impure function check_true( + constant checker : in checker_t; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_true(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + ----------------------------------------------------------------------------- + -- check_false + ----------------------------------------------------------------------------- + procedure check_false( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + wait_on_edge(clock, en, active_clock_edge); + check_false(checker, to_x01(expr) /= '0', msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_false( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if not expr then + pass := true; + if is_pass_visible(checker) then + passing_check(checker, std_msg("False check passed", msg, ""), line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check(checker, std_msg("False check failed", msg, ""), level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_false( + constant checker : in checker_t; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_false(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_false( + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_false(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_false( + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_false(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_false( + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_false(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_false( + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_false(default_checker, clock, en, expr, msg, level, active_clock_edge, line_num, file_name); + -- pragma translate_on + end; + + impure function check_false( + constant checker : in checker_t; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_false(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + ----------------------------------------------------------------------------- + -- check_implication + ----------------------------------------------------------------------------- + procedure check_implication( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal antecedent_expr : in std_logic; + signal consequent_expr : in std_logic; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + wait_on_edge(clock, en, active_clock_edge); + check_implication(checker, pass, to_x01(antecedent_expr) /= '0', + to_x01(consequent_expr) = '1', msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_implication( + constant checker : in checker_t; + variable pass : out boolean; + constant antecedent_expr : in boolean; + constant consequent_expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if (not antecedent_expr) or consequent_expr then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Implication check passed", msg, + "Got " & boolean'image(antecedent_expr) & " -> " & boolean'image(consequent_expr) & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check(checker, std_msg("Implication check failed", msg, ""), level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_implication( + signal clock : in std_logic; + signal en : in std_logic; + signal antecedent_expr : in std_logic; + signal consequent_expr : in std_logic; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_implication(default_checker, clock, en, antecedent_expr, consequent_expr, msg, level, active_clock_edge, line_num, file_name); + -- pragma translate_on + end; + + procedure check_implication( + constant checker : in checker_t; + constant antecedent_expr : in boolean; + constant consequent_expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_implication(checker, pass, antecedent_expr, consequent_expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_implication( + constant antecedent_expr : in boolean; + constant consequent_expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_implication(default_checker, pass, antecedent_expr, consequent_expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_implication( + variable pass : out boolean; + constant antecedent_expr : in boolean; + constant consequent_expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_implication(default_checker, pass, antecedent_expr, consequent_expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_implication( + constant antecedent_expr : in boolean; + constant consequent_expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_implication(default_checker, pass, antecedent_expr, consequent_expr, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_implication( + constant checker : in checker_t; + constant antecedent_expr : in boolean; + constant consequent_expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_implication(checker, pass, antecedent_expr, consequent_expr, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + ----------------------------------------------------------------------------- + -- check_stable + ----------------------------------------------------------------------------- + type check_stable_fsm_state_t is (idle, active_window); + + procedure run_stability_check ( + constant checker : in checker_t; + constant start_event : in std_logic; + constant end_event : in std_logic; + constant expr : in std_logic_vector; + constant msg : in string; + constant level : in log_level_t; + constant active_clock_edge : in edge_t; + constant allow_restart : in boolean; + constant line_num : in natural; + constant file_name : in string; + + variable state : inout check_stable_fsm_state_t; + variable ref : inout std_logic_vector; + variable clock_edge_counter : inout natural; + variable is_stable : inout boolean; + variable exit_stability_check : out boolean) is + + function format (expr : std_logic) return character is + begin + return std_logic'image(expr)(2); + end; + + function format (expr : std_logic_vector) return string is + begin + if expr'length = 1 then + return (1 => format(expr(expr'left))); + else + return to_nibble_string(expr) & " (" & to_integer_string(expr) & ")"; + end if; + + end; + + procedure open_window (variable open_ok : out boolean) is + begin + clock_edge_counter := 1; + ref := to_x01(expr); + open_ok := true; + if is_x(start_event) then + open_ok := false; + failing_check(checker, + std_msg("Stability check failed", msg, + "Start event is " & format(start_event) & "."), + level, line_num, file_name); + elsif is_x(expr) then + open_ok := false; + failing_check(checker, + std_msg("Stability check failed", msg, + "Got " & format(expr) & + " at 1st active and enabled clock edge."), + level, line_num, file_name); + end if; + end procedure; + + procedure close_window(cycle : natural; is_ok : boolean) is + variable close_ok : boolean := is_ok; + variable pass_msg_en : boolean; + begin + if is_x(end_event) then + close_ok := false; + failing_check(checker, + std_msg("Stability check failed", msg, + "End event is " & format(end_event) & "."), + level, line_num, file_name); + end if; + + if close_ok then + if is_pass_visible(checker) then + passing_check(checker, + std_msg("Stability check passed", msg, + "Got " & format(ref) & + " for " & positive'image(cycle) & + " active and enabled clock edges."), + line_num, file_name); + else + passing_check(checker); + end if; + end if; + end procedure close_window; + + variable open_ok : boolean; + begin + exit_stability_check := false; + case state is + + when idle => + if to_x01(start_event) /= '0' then + open_window(open_ok); + if not open_ok then + exit_stability_check := true; + return; + elsif to_x01(end_event) /= '0' then + close_window(clock_edge_counter, is_ok => true); + exit_stability_check := true; + return; + else + state := active_window; + end if; + end if; + + when active_window => + clock_edge_counter := clock_edge_counter + 1; + + if to_x01(start_event) /= '0' and allow_restart then + close_window(cycle => clock_edge_counter - 1, is_ok => true); + open_window(open_ok); + if not open_ok then + exit_stability_check := true; + return; + end if; + + elsif ref /= to_x01(expr) then + is_stable := false; + failing_check(checker, + std_msg("Stability check failed", msg, + "Got " & format(expr) & + " at " & to_ordinal_number(clock_edge_counter) & + " active and enabled clock edge. Expected " & + format(ref) & "."), level, line_num, file_name); + end if; + + if to_x01(end_event) /= '0' then + close_window(clock_edge_counter, is_ok => is_stable); + exit_stability_check := true; + return; + end if; + + end case; + + end procedure run_stability_check; + + procedure check_stable( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal start_event : in std_logic; + signal end_event : in std_logic; + signal expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant allow_restart : in boolean := false; + constant line_num : in natural := 0; + constant file_name : in string := "") is + + variable state : check_stable_fsm_state_t := idle; + variable ref : std_logic_vector(expr'range); + variable clock_edge_counter : natural; + variable is_stable : boolean := true; + variable exit_stability_check : boolean; + begin + -- pragma translate_off + stability_check : loop + wait_on_edge(clock, en, active_clock_edge); + + run_stability_check(checker, start_event, end_event, expr, msg, level, active_clock_edge, + allow_restart, line_num, file_name, state, ref, clock_edge_counter, + is_stable, exit_stability_check); + exit when exit_stability_check; + end loop; + -- pragma translate_on + end; + + procedure check_stable( + signal clock : in std_logic; + signal en : in std_logic; + signal start_event : in std_logic; + signal end_event : in std_logic; + signal expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant allow_restart : in boolean := false; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_stable(default_checker, clock, en, start_event, end_event, expr, msg, level, active_clock_edge, + allow_restart, line_num, file_name); + -- pragma translate_on + end; + + procedure check_stable( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal start_event : in std_logic; + signal end_event : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant allow_restart : in boolean := false; + constant line_num : in natural := 0; + constant file_name : in string := "") is + + variable state : check_stable_fsm_state_t := idle; + variable ref : std_logic_vector(0 to 0); + variable clock_edge_counter : natural; + variable is_stable : boolean := true; + variable exit_stability_check : boolean; + begin + -- pragma translate_off + stability_check : loop + wait_on_edge(clock, en, active_clock_edge); + + run_stability_check(checker, start_event, end_event, (0 => expr), msg, level, active_clock_edge, + allow_restart, line_num, file_name, state, ref, clock_edge_counter, + is_stable, exit_stability_check); + exit when exit_stability_check; + end loop; + -- pragma translate_on + end; + + procedure check_stable( + signal clock : in std_logic; + signal en : in std_logic; + signal start_event : in std_logic; + signal end_event : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant allow_restart : in boolean := false; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_stable(default_checker, clock, en, start_event, end_event, expr, msg, level, active_clock_edge, + allow_restart, line_num, file_name); + -- pragma translate_on + end; + + ----------------------------------------------------------------------------- + -- check_not_unknown + ----------------------------------------------------------------------------- + procedure check_not_unknown( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + wait_on_edge(clock, en, active_clock_edge); + check_not_unknown(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_not_unknown( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if not is_x(expr) then + pass := true; + if is_pass_visible(checker) then + passing_check(checker, + std_msg("Not unknown check passed", + msg, + "Got " & to_nibble_string(expr) & " (" & to_integer_string(expr) & ")" & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check(checker, + std_msg("Not unknown check failed", + msg, + "Got " & to_nibble_string(expr) & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_not_unknown( + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_not_unknown(default_checker, clock, en, expr, msg, level, active_clock_edge, line_num, file_name); + -- pragma translate_on + end; + + procedure check_not_unknown( + constant checker : in checker_t; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_not_unknown(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_not_unknown( + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_not_unknown(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_not_unknown( + variable pass : out boolean; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_not_unknown(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_not_unknown( + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_not_unknown(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_not_unknown( + constant checker : in checker_t; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_not_unknown(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_not_unknown( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + wait_on_edge(clock, en, active_clock_edge); + check_not_unknown(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_not_unknown( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if not is_x(expr) then + pass := true; + if is_pass_visible(checker) then + passing_check(checker, + std_msg("Not unknown check passed", + msg, + "Got " & std_logic'image(expr)(2) & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check(checker, + std_msg("Not unknown check failed", + msg, + "Got " & std_logic'image(expr)(2) & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_not_unknown( + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_not_unknown(default_checker, clock, en, expr, msg, level, active_clock_edge, line_num, file_name); + -- pragma translate_on + end; + + procedure check_not_unknown( + constant checker : in checker_t; + constant expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_not_unknown(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_not_unknown( + constant expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_not_unknown(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_not_unknown( + variable pass : out boolean; + constant expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_not_unknown(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_not_unknown( + constant expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_not_unknown(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_not_unknown( + constant checker : in checker_t; + constant expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_not_unknown(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + ----------------------------------------------------------------------------- + -- check_zero_one_hot + ----------------------------------------------------------------------------- + function n_hot_in_valid_range ( + constant expr : std_logic_vector; + constant lower_bound : in natural; + constant upper_bound : in natural) + return boolean is + variable n : natural := 0; + begin + if is_x(expr) then + return false; + end if; + for i in expr'range loop + if to_x01(expr(i)) = '1' then + n := n + 1; + end if; + end loop; + + return (n >= lower_bound) and (n <= upper_bound); + end function n_hot_in_valid_range; + + procedure check_zero_one_hot( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + wait_on_edge(clock, en, active_clock_edge); + check_zero_one_hot(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_zero_one_hot( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if n_hot_in_valid_range(expr, 0, 1) then + pass := true; + if is_pass_visible(checker) then + passing_check(checker, + std_msg("Zero one-hot check passed", msg, + "Got " & to_nibble_string(expr) & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check(checker, + std_msg("Zero one-hot check failed", msg, + "Got " & to_nibble_string(expr) & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_zero_one_hot( + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_zero_one_hot(default_checker, clock, en, expr, msg, level, active_clock_edge, line_num, file_name); + -- pragma translate_on + end; + + procedure check_zero_one_hot( + variable pass : out boolean; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_zero_one_hot(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_zero_one_hot( + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_zero_one_hot(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_zero_one_hot( + constant checker : in checker_t; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_zero_one_hot(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_zero_one_hot( + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_zero_one_hot(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_zero_one_hot( + constant checker : in checker_t; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_zero_one_hot(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + ----------------------------------------------------------------------------- + -- check_one_hot + ----------------------------------------------------------------------------- + procedure check_one_hot( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + wait_on_edge(clock, en, active_clock_edge); + check_one_hot(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_one_hot( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if n_hot_in_valid_range(expr, 1, 1) then + pass := true; + if is_pass_visible(checker) then + passing_check(checker, + std_msg("One-hot check passed", msg, + "Got " & to_nibble_string(expr) & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check(checker, + std_msg("One-hot check failed", msg, + "Got " & to_nibble_string(expr) & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_one_hot( + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_one_hot(default_checker, clock, en, expr, msg, level, active_clock_edge, line_num, file_name); + -- pragma translate_on + end; + + procedure check_one_hot( + variable pass : out boolean; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_one_hot(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_one_hot( + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_one_hot(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_one_hot( + constant checker : in checker_t; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_one_hot(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_one_hot( + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_one_hot(default_checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_one_hot( + constant checker : in checker_t; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_one_hot(checker, pass, expr, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + ----------------------------------------------------------------------------- + -- check_next + ----------------------------------------------------------------------------- + procedure check_next( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal start_event : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag; + constant num_cks : in natural := 1; + constant allow_overlapping : in boolean := true; + constant allow_missing_start : in boolean := true; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + + variable schedule : boolean_vector(0 to num_cks) := (others => false); + variable clock_cycles_after_start_event : natural; + + function check_is_scheduled( + constant schedule : in boolean_vector) + return boolean is + begin + return schedule(0); + end function check_is_scheduled; + + procedure schedule_check( + variable schedule : inout boolean_vector; + constant num_cks : in natural) is + begin + schedule(num_cks) := true; + end procedure schedule_check; + + procedure update_remaining_times_to_scheduled_checks( + variable schedule : inout boolean_vector; + constant num_cks : in natural) is + begin + schedule(0 to num_cks - 1) := schedule(1 to num_cks); + schedule(num_cks) := false; + end procedure update_remaining_times_to_scheduled_checks; + + function pending_check ( + constant schedule : boolean_vector) + return boolean is + constant no_pending_checks : boolean_vector(1 to schedule'right) := (others => false); + begin + return schedule(1 to schedule'right) /= no_pending_checks; + end function pending_check; + + procedure check_expr is + begin + if to_x01(expr) = '1' then + if is_pass_visible(checker) then + passing_check(checker, std_msg("Next check passed", msg, ""), line_num, file_name); + else + passing_check(checker); + end if; + else + failing_check(checker, + std_msg("Next check failed", msg, + "Got " & std_logic'image(expr)(2) & + " at the " & to_ordinal_number(num_cks) & + " active and enabled clock edge."), + level, line_num, file_name); + end if; + end procedure check_expr; + + begin + -- pragma translate_off + while true loop + wait_on_edge(clock, en, active_clock_edge); + clock_cycles_after_start_event := clock_cycles_after_start_event + 1; + + if to_x01(start_event) = '1' then + if pending_check(schedule) and not allow_overlapping then + failing_check(checker, + std_msg("Next check failed", msg, + "Got overlapping start event at the " & + to_ordinal_number(clock_cycles_after_start_event) & + " active and enabled clock edge."), + level, line_num, file_name); + else + schedule_check(schedule, num_cks); + clock_cycles_after_start_event := 0; + end if; + elsif to_x01(start_event) = 'X' then + failing_check(checker, + std_msg("Next check failed", msg, + "Start event is " & std_logic'image(start_event)(2) & "."), + level, line_num, file_name); + end if; + + if check_is_scheduled(schedule) then + check_expr; + elsif (to_x01(expr) = '1') and not allow_missing_start then + failing_check(checker, + std_msg("Next check failed", msg, + "Missing start event for true expression."), + level, line_num, file_name); + end if; + + + update_remaining_times_to_scheduled_checks(schedule, num_cks); + end loop; + -- pragma translate_on + end; + + procedure check_next( + signal clock : in std_logic; + signal en : in std_logic; + signal start_event : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag; + constant num_cks : in natural := 1; + constant allow_overlapping : in boolean := true; + constant allow_missing_start : in boolean := true; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_next(default_checker, clock, en, start_event, expr, msg, num_cks, allow_overlapping, + allow_missing_start, level, active_clock_edge, line_num, file_name); + -- pragma translate_on + end; + + ----------------------------------------------------------------------------- + -- check_sequence + ----------------------------------------------------------------------------- + procedure check_sequence( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal event_sequence : in std_logic_vector; + constant msg : in string := check_result_tag; + constant trigger_event : in trigger_event_t := penultimate; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + + variable expected_events : boolean_vector(0 to event_sequence'length - 1) := (others => false); + variable tracks : boolean_vector(0 to event_sequence'length - 1) := (others => false); + + procedure find_new_and_update_existing_tracks ( + variable tracks : inout boolean_vector; + constant event_sequence : in std_logic_vector) is + + constant seq : std_logic_vector(0 to event_sequence'length - 1) := event_sequence; + variable unknown_event_in_sequence : boolean := false; + + function active_tracks ( + constant tracks : in boolean_vector) + return boolean is + begin + for i in tracks'range loop + if tracks(i) then + return true; + end if; + end loop; + return false; + end function active_tracks; + begin + for i in tracks'reverse_range loop + if to_x01(seq(i)) = 'X' then + -- FIXME: check moved out of loop to work with GHDL 0.33. + unknown_event_in_sequence := true; + end if; + + if i = 0 then + if (trigger_event = first_no_pipe) and active_tracks(tracks) then + tracks(0) := false; + else + tracks(0) := (to_x01(seq(seq'left)) = '1'); + end if; + else + tracks(i) := (tracks(i - 1) and (to_x01(seq(i)) = '1')); + end if; + end loop; + + -- FIXME: check moved out of loop to work with GHDL 0.33. + if unknown_event_in_sequence then + failing_check(checker, + std_msg("Sequence check failed", msg, + "Got " & to_nibble_string(seq) & "."), + level, line_num, file_name); + end if; + end find_new_and_update_existing_tracks; + + procedure update_expectations_on_events_in_next_cycle ( + constant tracks : in boolean_vector; + variable expected_events : inout boolean_vector) is + begin + if trigger_event = penultimate then + expected_events(expected_events'right - 1) := tracks(tracks'right - 1); + else + expected_events(expected_events'range) := tracks(expected_events'range); + end if; + expected_events := logical_right_shift(expected_events, 1); + end procedure update_expectations_on_events_in_next_cycle; + + procedure verify_expected_events ( + constant expected_events : in boolean_vector; + constant event_sequence : in std_logic_vector) is + constant seq : std_logic_vector(0 to event_sequence'length - 1) := event_sequence; + begin + for i in 1 to seq'right loop + if expected_events(i) then + if to_x01(seq(i)) /= '1' then + failing_check(checker, + std_msg("Sequence check failed", msg, + "Missing required event at " & + to_ordinal_number(i) & + " active and enabled clock edge."), + level, line_num, file_name); + elsif i = seq'right then + if is_pass_visible(checker) then + passing_check(checker, std_msg("Sequence check passed", msg, ""), line_num, file_name); + else + passing_check(checker); + end if; + end if; + end if; + end loop; + end procedure verify_expected_events; + + variable valid_event_sequence_length : boolean; + begin + -- pragma translate_off + valid_event_sequence_length := event_sequence'length >= 2; + if not valid_event_sequence_length then + failing_check(checker, + std_msg("Sequence check failed", msg, + "Event sequence length must be at least 2. Got " & + natural'image(event_sequence'length) & "."), + level, line_num, file_name); + end if; + + wait_on_edge(clock, en, active_clock_edge); + while valid_event_sequence_length loop + find_new_and_update_existing_tracks(tracks, event_sequence); + update_expectations_on_events_in_next_cycle(tracks, expected_events); + wait_on_edge(clock, en, active_clock_edge); + verify_expected_events(expected_events, event_sequence); + end loop; + + wait; + -- pragma translate_on + end; + + procedure check_sequence( + signal clock : in std_logic; + signal en : in std_logic; + signal event_sequence : in std_logic_vector; + constant msg : in string := check_result_tag; + constant trigger_event : in trigger_event_t := penultimate; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_sequence(default_checker, clock, en, event_sequence, msg, trigger_event, level, active_clock_edge, + line_num, file_name); + -- pragma translate_on + end; + + ----------------------------------------------------------------------------- + -- check_relation + ----------------------------------------------------------------------------- + procedure check_relation( + constant checker : in checker_t; + constant expr : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_relation(checker, pass, expr, msg, level, context_msg, line_num, file_name); + -- pragma translate_on + end; + + procedure check_relation( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if expr then + pass := true; + if is_pass_visible(checker) then + passing_check(checker, std_msg("Relation check passed", msg, context_msg), line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check(checker, std_msg("Relation check failed", msg, context_msg), level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_relation( + constant expr : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_relation(default_checker, pass, expr, msg, level, context_msg, line_num, file_name); + -- pragma translate_on + end; + + procedure check_relation( + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_relation(default_checker, pass, expr, msg, level, context_msg, line_num, file_name); + -- pragma translate_on + end; + + impure function check_relation( + constant expr : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_relation(default_checker, pass, expr, msg, level, context_msg, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_relation( + constant checker : in checker_t; + constant expr : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_relation(checker, pass, expr, msg, level, context_msg, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_relation( + constant checker : in checker_t; + constant expr : in std_ulogic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_relation(checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); + -- pragma translate_on + end; + + procedure check_relation( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in std_ulogic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_relation(checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); + -- pragma translate_on + end; + + procedure check_relation( + constant expr : in std_ulogic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_relation(default_checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); + -- pragma translate_on + end; + + procedure check_relation( + variable pass : out boolean; + constant expr : in std_ulogic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_relation(default_checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); + -- pragma translate_on + end; + + impure function check_relation( + constant expr : in std_ulogic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_relation(default_checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_relation( + constant checker : in checker_t; + constant expr : in std_ulogic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_relation(checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_relation( + constant checker : in checker_t; + constant expr : in bit; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_relation(checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); + -- pragma translate_on + end; + + procedure check_relation( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in bit; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_relation(checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); + -- pragma translate_on + end; + + procedure check_relation( + constant expr : in bit; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_relation(default_checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); + -- pragma translate_on + end; + + procedure check_relation( + variable pass : out boolean; + constant expr : in bit; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_relation(default_checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); + -- pragma translate_on + end; + + impure function check_relation( + constant expr : in bit; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_relation(default_checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_relation( + constant checker : in checker_t; + constant expr : in bit; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_relation(default_checker, pass, (expr = '1'), msg, level, context_msg, line_num, file_name); + -- pragma translate_on + return pass; + end; + + function "=" ( + constant left : unsigned; + constant right : std_logic_vector) + return boolean is + begin + return left = unsigned(right); + end function "="; + + function "=" ( + constant left : std_logic_vector; + constant right : unsigned) + return boolean is + begin + return unsigned(left) = right; + end function "="; + + function "=" ( + constant left : natural; + constant right : std_logic_vector) + return boolean is + begin + return left = unsigned(right); + end function "="; + + function "=" ( + constant left : std_logic_vector; + constant right : natural) + return boolean is + begin + return unsigned(left) = right; + end function "="; + + function "=" ( + constant left : boolean; + constant right : std_logic) + return boolean is + begin + return left = (right = '1'); + end function "="; + + function "=" ( + constant left : std_logic; + constant right : boolean) + return boolean is + begin + return (left = '1') = right; + end function "="; + + function to_char ( + constant bit : std_logic) + return character is + constant chars : string(1 to 9) := "UX01ZWLH-"; + begin + return chars(std_logic'pos(bit) + 1); + end function to_char; + + function to_string ( + constant data : std_logic) + return string is + variable ret_val : string(1 to 1); + begin + ret_val(1) := to_char(data); + return ret_val; + end function to_string; + + function to_string ( + constant data : boolean) + return string is + begin + if data then + return "true"; + else + return "false"; + end if; + end function to_string; + + function to_string ( + constant data : integer) + return string is + begin + return integer'image(data); + end function to_string; + + function to_string ( + constant data : string) + return string is + begin + return data; + end function to_string; + + function to_string ( + constant data : time) + return string is + begin + return time'image(data); + end function to_string; + + function max ( + constant value_1 : integer; + constant value_2 : integer) + return integer is + begin + if value_1 > value_2 then + return value_1; + else + return value_2; + end if; + end max; + + function required_num_of_unsigned_bits ( + constant value : natural) + return natural is + variable max_value : natural := 0; + variable required_length : natural := 1; + begin + for i in 0 to max_supported_num_of_bits_in_integer_implementation - 2 loop + max_value := max_value + 2 ** i; + exit when max_value >= value; + required_length := required_length + 1; + end loop; + + return required_length; + end required_num_of_unsigned_bits; + + function to_sufficient_unsigned ( + constant value : natural; + constant min_length : natural) + return unsigned is + begin + return to_unsigned(value, max(min_length, required_num_of_unsigned_bits(value))); + end to_sufficient_unsigned; + + function to_sufficient_signed ( + constant value : integer; + constant min_length : natural) + return signed is + variable min_value : integer := -1; + variable required_length : natural := 1; + begin + if value < 0 then + for i in 0 to max_supported_num_of_bits_in_integer_implementation - 1 loop + exit when min_value <= value; + min_value := min_value * 2; + required_length := required_length + 1; + end loop; + + return to_signed(value, max(min_length, required_length)); + else + return signed(to_unsigned(natural(value), max(min_length, required_num_of_unsigned_bits(natural(value)) + 1))); + end if; + end to_sufficient_signed; + + ----------------------------------------------------------------------------- + -- check_(almost)_equal for real + ----------------------------------------------------------------------------- + + procedure check_equal( + constant got : in real; + constant expected : in real; + constant msg : in string := check_result_tag; + constant max_diff : in real := 0.0; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, got, expected, msg, max_diff, level, line_num, file_name); + -- pragma translate_on + end; + + + procedure check_equal( + constant checker : in checker_t; + constant got : in real; + constant expected : in real; + constant msg : in string := check_result_tag; + constant max_diff : in real := 0.0; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if abs (got - expected) <= max_diff then + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got abs (" & real'image(got) & " - " & real'image(expected) & ") <= " & real'image(max_diff) & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got abs (" & real'image(got) & " - " & real'image(expected) & ") > " & real'image(max_diff) & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + ----------------------------------------------------------------------------- + -- check_equal + ----------------------------------------------------------------------------- + procedure check_equal( + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & + "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_equal( + constant got : in unsigned; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in unsigned; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in unsigned; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & + "Expected " & to_string(expected) & " (" & to_nibble_string(to_sufficient_unsigned(expected, got'length)) & ")" & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in unsigned; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in unsigned; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in unsigned; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_equal( + constant got : in natural; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in natural; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in natural; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_string(got) & " (" & to_nibble_string(to_sufficient_unsigned(got, expected'length)) & ")" & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_string(got) & " (" & to_nibble_string(to_sufficient_unsigned(got, expected'length)) & ")" & ". " & + "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in natural; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in natural; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in natural; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_equal( + constant got : in unsigned; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in unsigned; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in unsigned; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & + "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in unsigned; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in unsigned; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in unsigned; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_equal( + constant got : in std_logic_vector; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in std_logic_vector; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in std_logic_vector; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & + "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in std_logic_vector; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in std_logic_vector; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in std_logic_vector; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_equal( + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & + "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_equal( + constant got : in std_logic_vector; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in std_logic_vector; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in std_logic_vector; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & + "Expected " & to_string(expected) & " (" & to_nibble_string(to_sufficient_unsigned(expected, got'length)) & ")" & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in std_logic_vector; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in std_logic_vector; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in std_logic_vector; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_equal( + constant got : in natural; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in natural; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in natural; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_string(got) & " (" & to_nibble_string(to_sufficient_unsigned(got, expected'length)) & ")" & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_string(got) & " (" & to_nibble_string(to_sufficient_unsigned(got, expected'length)) & ")" & ". " & + "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in natural; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in natural; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in natural; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_equal( + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & + "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_equal( + constant got : in signed; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in signed; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in signed; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & + "Expected " & to_string(expected) & " (" & to_nibble_string(to_sufficient_signed(expected, got'length)) & ")" & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in signed; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in signed; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in signed; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_equal( + constant got : in integer; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in integer; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in integer; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_string(got) & " (" & to_nibble_string(to_sufficient_signed(got, expected'length)) & ")" & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_string(got) & " (" & to_nibble_string(to_sufficient_signed(got, expected'length)) & ")" & ". " & + "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in integer; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in integer; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in integer; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_equal( + constant got : in integer; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in integer; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in integer; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_string(got) & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_string(got) & ". " & + "Expected " & to_string(expected) & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in integer; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in integer; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in integer; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_equal( + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_string(got) & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_string(got) & ". " & + "Expected " & to_string(expected) & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_equal( + constant got : in std_logic; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in std_logic; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in std_logic; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_string(got) & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_string(got) & ". " & + "Expected " & to_string(expected) & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in std_logic; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in std_logic; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in std_logic; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_equal( + constant got : in boolean; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in boolean; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in boolean; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_string(got) & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_string(got) & ". " & + "Expected " & to_string(expected) & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in boolean; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in boolean; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in boolean; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_equal( + constant got : in boolean; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in boolean; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in boolean; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_string(got) & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_string(got) & ". " & + "Expected " & to_string(expected) & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in boolean; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in boolean; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in boolean; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_equal( + constant got : in string; + constant expected : in string; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in string; + constant expected : in string; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in string; + constant expected : in string; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_string(got) & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_string(got) & ". " & + "Expected " & to_string(expected) & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in string; + constant expected : in string; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in string; + constant expected : in string; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in string; + constant expected : in string; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_equal( + constant got : in time; + constant expected : in time; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in time; + constant expected : in time; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in time; + constant expected : in time; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & to_string(got) & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & to_string(got) & ". " & + "Expected " & to_string(expected) & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in time; + constant expected : in time; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in time; + constant expected : in time; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in time; + constant expected : in time; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + ----------------------------------------------------------------------------- + -- check_match + ----------------------------------------------------------------------------- + procedure check_match( + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_match( + variable pass : out boolean; + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_match( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if std_match(got, expected) then + pass := true; + + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Match check passed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & + "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Match check failed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & + "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_match( + constant checker : in checker_t; + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_match(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_match( + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_match( + constant checker : in checker_t; + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_match(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_match( + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_match( + variable pass : out boolean; + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_match( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if std_match(got, expected) then + pass := true; + + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Match check passed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & + "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Match check failed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & + "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_match( + constant checker : in checker_t; + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_match(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_match( + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_match( + constant checker : in checker_t; + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_match(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_match( + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_match( + variable pass : out boolean; + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_match( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if std_match(got, expected) then + pass := true; + + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Match check passed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & + "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Match check failed", msg, + "Got " & to_nibble_string(got) & " (" & to_integer_string(got) & ")" & ". " & + "Expected " & to_nibble_string(expected) & " (" & to_integer_string(expected) & ")" & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_match( + constant checker : in checker_t; + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_match(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_match( + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_match( + constant checker : in checker_t; + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_match(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + procedure check_match( + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_match( + variable pass : out boolean; + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_match( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if std_match(got, expected) then + pass := true; + + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Match check passed", msg, + "Got " & to_string(got) & ". " & + "Expected " & to_string(expected) & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Match check failed", msg, + "Got " & to_string(got) & ". " & + "Expected " & to_string(expected) & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_match( + constant checker : in checker_t; + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_match(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_match( + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_match( + constant checker : in checker_t; + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_match(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + ----------------------------------------------------------------------------- + +end package body check_pkg; diff --git a/vunit/vhdl/check/src/check_api.vhd b/vunit/vhdl/check/src/check_api.vhd index 0ab5ca1dc..92c938d2a 100644 --- a/vunit/vhdl/check/src/check_api.vhd +++ b/vunit/vhdl/check/src/check_api.vhd @@ -1,2178 +1,2178 @@ --- This file contains the API for the check package. The API is --- common to all implementations of the check functionality (VHDL 2002+ and VHDL 1993) --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use std.textio.all; -use work.checker_pkg.all; -use work.logger_pkg.all; -use work.log_levels_pkg.all; - -package check_pkg is - - constant default_checker : checker_t := new_checker("check"); - constant check_logger : logger_t := get_logger(default_checker); - - signal check_enabled : std_logic := '1'; - - procedure get_checker_stat (variable stat : out checker_stat_t); - impure function get_checker_stat return checker_stat_t; - procedure reset_checker_stat; - - constant check_result_tag : string := "<+/->"; - function result (msg : string := "") return string; - - type edge_t is (rising_edge, falling_edge, both_edges); - type trigger_event_t is (first_pipe, first_no_pipe, penultimate); - - ----------------------------------------------------------------------------- - -- check - ----------------------------------------------------------------------------- - procedure check( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check( - constant checker : in checker_t; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check( - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check( - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check( - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check( - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check( - constant checker : in checker_t; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - ----------------------------------------------------------------------------- - -- check_passed - ----------------------------------------------------------------------------- - procedure check_passed( - constant checker : in checker_t; - constant msg : in string := check_result_tag & "."; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_passed( - constant msg : in string := check_result_tag & "."; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - ----------------------------------------------------------------------------- - -- check_failed - ----------------------------------------------------------------------------- - procedure check_failed( - constant checker : in checker_t; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_failed( - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - ----------------------------------------------------------------------------- - -- check_true - ----------------------------------------------------------------------------- - procedure check_true( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_true( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_true( - constant checker : in checker_t; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_true( - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_true( - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_true( - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_true( - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_true( - constant checker : in checker_t; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - ----------------------------------------------------------------------------- - -- check_false - ----------------------------------------------------------------------------- - procedure check_false( - constant checker : in checker_t; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_false( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_false( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_false( - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_false( - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_false( - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_false( - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_false( - constant checker : in checker_t; - constant expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - ----------------------------------------------------------------------------- - -- check_implication - ----------------------------------------------------------------------------- - procedure check_implication( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal antecedent_expr : in std_logic; - signal consequent_expr : in std_logic; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_implication( - signal clock : in std_logic; - signal en : in std_logic; - signal antecedent_expr : in std_logic; - signal consequent_expr : in std_logic; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_implication( - constant checker : in checker_t; - constant antecedent_expr : in boolean; - constant consequent_expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_implication( - constant checker : in checker_t; - variable pass : out boolean; - constant antecedent_expr : in boolean; - constant consequent_expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_implication( - constant antecedent_expr : in boolean; - constant consequent_expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_implication( - variable pass : out boolean; - constant antecedent_expr : in boolean; - constant consequent_expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_implication( - constant antecedent_expr : in boolean; - constant consequent_expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_implication( - constant checker : in checker_t; - constant antecedent_expr : in boolean; - constant consequent_expr : in boolean; - constant msg : in string := check_result_tag & "."; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - ----------------------------------------------------------------------------- - -- check_stable - ----------------------------------------------------------------------------- - procedure check_stable( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal start_event : in std_logic; - signal end_event : in std_logic; - signal expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant allow_restart : in boolean := false; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_stable( - signal clock : in std_logic; - signal en : in std_logic; - signal start_event : in std_logic; - signal end_event : in std_logic; - signal expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant allow_restart : in boolean := false; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_stable( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal start_event : in std_logic; - signal end_event : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant allow_restart : in boolean := false; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_stable( - signal clock : in std_logic; - signal en : in std_logic; - signal start_event : in std_logic; - signal end_event : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant allow_restart : in boolean := false; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - ------------------------------------------------------------------------------- - -- check_not_unknown - ------------------------------------------------------------------------------- - procedure check_not_unknown( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_not_unknown( - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_not_unknown( - constant checker : in checker_t; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_not_unknown( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_not_unknown( - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_not_unknown( - variable pass : out boolean; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_not_unknown( - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_not_unknown( - constant checker : in checker_t; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_not_unknown( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_not_unknown( - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_not_unknown( - constant checker : in checker_t; - constant expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_not_unknown( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_not_unknown( - constant expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_not_unknown( - variable pass : out boolean; - constant expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_not_unknown( - constant expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_not_unknown( - constant checker : in checker_t; - constant expr : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - ----------------------------------------------------------------------------- - -- check_zero_one_hot - ----------------------------------------------------------------------------- - procedure check_zero_one_hot( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_zero_one_hot( - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_zero_one_hot( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_zero_one_hot( - variable pass : out boolean; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_zero_one_hot( - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_zero_one_hot( - constant checker : in checker_t; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_zero_one_hot( - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_zero_one_hot( - constant checker : in checker_t; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - ----------------------------------------------------------------------------- - -- check_one_hot - ----------------------------------------------------------------------------- - procedure check_one_hot( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_one_hot( - signal clock : in std_logic; - signal en : in std_logic; - signal expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_one_hot( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_one_hot( - variable pass : out boolean; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_one_hot( - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_one_hot( - constant checker : in checker_t; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_one_hot( - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_one_hot( - constant checker : in checker_t; - constant expr : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - ----------------------------------------------------------------------------- - -- check_next - ----------------------------------------------------------------------------- - procedure check_next( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal start_event : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag; - constant num_cks : in natural := 1; - constant allow_overlapping : in boolean := true; - constant allow_missing_start : in boolean := true; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_next( - signal clock : in std_logic; - signal en : in std_logic; - signal start_event : in std_logic; - signal expr : in std_logic; - constant msg : in string := check_result_tag; - constant num_cks : in natural := 1; - constant allow_overlapping : in boolean := true; - constant allow_missing_start : in boolean := true; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - ----------------------------------------------------------------------------- - -- check_sequence - ----------------------------------------------------------------------------- - procedure check_sequence( - constant checker : in checker_t; - signal clock : in std_logic; - signal en : in std_logic; - signal event_sequence : in std_logic_vector; - constant msg : in string := check_result_tag; - constant trigger_event : in trigger_event_t := penultimate; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_sequence( - signal clock : in std_logic; - signal en : in std_logic; - signal event_sequence : in std_logic_vector; - constant msg : in string := check_result_tag; - constant trigger_event : in trigger_event_t := penultimate; - constant level : in log_level_t := null_log_level; - constant active_clock_edge : in edge_t := rising_edge; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - ----------------------------------------------------------------------------- - -- check_relation - ----------------------------------------------------------------------------- - procedure check_relation( - constant checker : in checker_t; - constant expr : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_relation( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_relation( - constant expr : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_relation( - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_relation( - constant expr : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_relation( - constant checker : in checker_t; - constant expr : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_relation( - constant checker : in checker_t; - constant expr : in std_ulogic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_relation( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in std_ulogic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_relation( - constant expr : in std_ulogic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_relation( - variable pass : out boolean; - constant expr : in std_ulogic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_relation( - constant expr : in std_ulogic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_relation( - constant checker : in checker_t; - constant expr : in std_ulogic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_relation( - constant checker : in checker_t; - constant expr : in bit; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_relation( - constant checker : in checker_t; - variable pass : out boolean; - constant expr : in bit; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_relation( - constant expr : in bit; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_relation( - variable pass : out boolean; - constant expr : in bit; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_relation( - constant expr : in bit; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_relation( - constant checker : in checker_t; - constant expr : in bit; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant context_msg : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - ----------------------------------------------------------------------------- - -- check_(almost)_equal for real - ----------------------------------------------------------------------------- - - procedure check_equal( - constant got : in real; - constant expected : in real; - constant msg : in string := check_result_tag; - constant max_diff : in real := 0.0; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in real; - constant expected : in real; - constant msg : in string := check_result_tag; - constant max_diff : in real := 0.0; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - ----------------------------------------------------------------------------- - -- check_equal - ----------------------------------------------------------------------------- - procedure check_equal( - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_equal( - constant got : in unsigned; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in unsigned; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in unsigned; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in unsigned; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in unsigned; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in unsigned; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_equal( - constant got : in natural; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in natural; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in natural; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in natural; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in natural; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in natural; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_equal( - constant got : in unsigned; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in unsigned; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in unsigned; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in unsigned; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in unsigned; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in unsigned; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_equal( - constant got : in std_logic_vector; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in std_logic_vector; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in std_logic_vector; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in std_logic_vector; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in std_logic_vector; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in std_logic_vector; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_equal( - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_equal( - constant got : in std_logic_vector; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in std_logic_vector; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in std_logic_vector; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in std_logic_vector; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in std_logic_vector; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in std_logic_vector; - constant expected : in natural; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_equal( - constant got : in natural; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in natural; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in natural; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in natural; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in natural; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in natural; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_equal( - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_equal( - constant got : in signed; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in signed; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in signed; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in signed; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in signed; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in signed; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_equal( - constant got : in integer; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in integer; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in integer; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in integer; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in integer; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in integer; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_equal( - constant got : in integer; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in integer; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in integer; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in integer; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in integer; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in integer; - constant expected : in integer; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_equal( - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_equal( - constant got : in std_logic; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in std_logic; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in std_logic; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in std_logic; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in std_logic; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in std_logic; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_equal( - constant got : in boolean; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in boolean; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in boolean; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in boolean; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in boolean; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in boolean; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_equal( - constant got : in boolean; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in boolean; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in boolean; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in boolean; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in boolean; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in boolean; - constant expected : in boolean; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_equal( - constant got : in string; - constant expected : in string; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in string; - constant expected : in string; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in string; - constant expected : in string; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in string; - constant expected : in string; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in string; - constant expected : in string; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in string; - constant expected : in string; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_equal( - constant got : in time; - constant expected : in time; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in time; - constant expected : in time; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in time; - constant expected : in time; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in time; - constant expected : in time; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in time; - constant expected : in time; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in time; - constant expected : in time; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - ----------------------------------------------------------------------------- - -- check_match - ----------------------------------------------------------------------------- - procedure check_match( - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_match( - variable pass : out boolean; - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_match( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_match( - constant checker : in checker_t; - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_match( - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_match( - constant checker : in checker_t; - constant got : in unsigned; - constant expected : in unsigned; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_match( - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_match( - variable pass : out boolean; - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_match( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_match( - constant checker : in checker_t; - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_match( - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_match( - constant checker : in checker_t; - constant got : in std_logic_vector; - constant expected : in std_logic_vector; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_match( - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_match( - variable pass : out boolean; - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_match( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_match( - constant checker : in checker_t; - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_match( - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_match( - constant checker : in checker_t; - constant got : in signed; - constant expected : in signed; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - procedure check_match( - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_match( - variable pass : out boolean; - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_match( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_match( - constant checker : in checker_t; - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_match( - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_match( - constant checker : in checker_t; - constant got : in std_logic; - constant expected : in std_logic; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - ----------------------------------------------------------------------------- - -end package; +-- This file contains the API for the check package. The API is +-- common to all implementations of the check functionality (VHDL 2002+ and VHDL 1993) +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use std.textio.all; +use work.checker_pkg.all; +use work.logger_pkg.all; +use work.log_levels_pkg.all; + +package check_pkg is + + constant default_checker : checker_t := new_checker("check"); + constant check_logger : logger_t := get_logger(default_checker); + + signal check_enabled : std_logic := '1'; + + procedure get_checker_stat (variable stat : out checker_stat_t); + impure function get_checker_stat return checker_stat_t; + procedure reset_checker_stat; + + constant check_result_tag : string := "<+/->"; + function result (msg : string := "") return string; + + type edge_t is (rising_edge, falling_edge, both_edges); + type trigger_event_t is (first_pipe, first_no_pipe, penultimate); + + ----------------------------------------------------------------------------- + -- check + ----------------------------------------------------------------------------- + procedure check( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check( + constant checker : in checker_t; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check( + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check( + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check( + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check( + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check( + constant checker : in checker_t; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + ----------------------------------------------------------------------------- + -- check_passed + ----------------------------------------------------------------------------- + procedure check_passed( + constant checker : in checker_t; + constant msg : in string := check_result_tag & "."; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_passed( + constant msg : in string := check_result_tag & "."; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + ----------------------------------------------------------------------------- + -- check_failed + ----------------------------------------------------------------------------- + procedure check_failed( + constant checker : in checker_t; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_failed( + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + ----------------------------------------------------------------------------- + -- check_true + ----------------------------------------------------------------------------- + procedure check_true( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_true( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_true( + constant checker : in checker_t; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_true( + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_true( + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_true( + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_true( + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_true( + constant checker : in checker_t; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + ----------------------------------------------------------------------------- + -- check_false + ----------------------------------------------------------------------------- + procedure check_false( + constant checker : in checker_t; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_false( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_false( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_false( + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_false( + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_false( + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_false( + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_false( + constant checker : in checker_t; + constant expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + ----------------------------------------------------------------------------- + -- check_implication + ----------------------------------------------------------------------------- + procedure check_implication( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal antecedent_expr : in std_logic; + signal consequent_expr : in std_logic; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_implication( + signal clock : in std_logic; + signal en : in std_logic; + signal antecedent_expr : in std_logic; + signal consequent_expr : in std_logic; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_implication( + constant checker : in checker_t; + constant antecedent_expr : in boolean; + constant consequent_expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_implication( + constant checker : in checker_t; + variable pass : out boolean; + constant antecedent_expr : in boolean; + constant consequent_expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_implication( + constant antecedent_expr : in boolean; + constant consequent_expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_implication( + variable pass : out boolean; + constant antecedent_expr : in boolean; + constant consequent_expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_implication( + constant antecedent_expr : in boolean; + constant consequent_expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_implication( + constant checker : in checker_t; + constant antecedent_expr : in boolean; + constant consequent_expr : in boolean; + constant msg : in string := check_result_tag & "."; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + ----------------------------------------------------------------------------- + -- check_stable + ----------------------------------------------------------------------------- + procedure check_stable( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal start_event : in std_logic; + signal end_event : in std_logic; + signal expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant allow_restart : in boolean := false; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_stable( + signal clock : in std_logic; + signal en : in std_logic; + signal start_event : in std_logic; + signal end_event : in std_logic; + signal expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant allow_restart : in boolean := false; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_stable( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal start_event : in std_logic; + signal end_event : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant allow_restart : in boolean := false; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_stable( + signal clock : in std_logic; + signal en : in std_logic; + signal start_event : in std_logic; + signal end_event : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant allow_restart : in boolean := false; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + ------------------------------------------------------------------------------- + -- check_not_unknown + ------------------------------------------------------------------------------- + procedure check_not_unknown( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_not_unknown( + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_not_unknown( + constant checker : in checker_t; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_not_unknown( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_not_unknown( + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_not_unknown( + variable pass : out boolean; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_not_unknown( + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_not_unknown( + constant checker : in checker_t; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_not_unknown( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_not_unknown( + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_not_unknown( + constant checker : in checker_t; + constant expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_not_unknown( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_not_unknown( + constant expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_not_unknown( + variable pass : out boolean; + constant expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_not_unknown( + constant expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_not_unknown( + constant checker : in checker_t; + constant expr : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + ----------------------------------------------------------------------------- + -- check_zero_one_hot + ----------------------------------------------------------------------------- + procedure check_zero_one_hot( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_zero_one_hot( + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_zero_one_hot( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_zero_one_hot( + variable pass : out boolean; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_zero_one_hot( + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_zero_one_hot( + constant checker : in checker_t; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_zero_one_hot( + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_zero_one_hot( + constant checker : in checker_t; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + ----------------------------------------------------------------------------- + -- check_one_hot + ----------------------------------------------------------------------------- + procedure check_one_hot( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_one_hot( + signal clock : in std_logic; + signal en : in std_logic; + signal expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_one_hot( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_one_hot( + variable pass : out boolean; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_one_hot( + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_one_hot( + constant checker : in checker_t; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_one_hot( + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_one_hot( + constant checker : in checker_t; + constant expr : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + ----------------------------------------------------------------------------- + -- check_next + ----------------------------------------------------------------------------- + procedure check_next( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal start_event : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag; + constant num_cks : in natural := 1; + constant allow_overlapping : in boolean := true; + constant allow_missing_start : in boolean := true; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_next( + signal clock : in std_logic; + signal en : in std_logic; + signal start_event : in std_logic; + signal expr : in std_logic; + constant msg : in string := check_result_tag; + constant num_cks : in natural := 1; + constant allow_overlapping : in boolean := true; + constant allow_missing_start : in boolean := true; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + ----------------------------------------------------------------------------- + -- check_sequence + ----------------------------------------------------------------------------- + procedure check_sequence( + constant checker : in checker_t; + signal clock : in std_logic; + signal en : in std_logic; + signal event_sequence : in std_logic_vector; + constant msg : in string := check_result_tag; + constant trigger_event : in trigger_event_t := penultimate; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_sequence( + signal clock : in std_logic; + signal en : in std_logic; + signal event_sequence : in std_logic_vector; + constant msg : in string := check_result_tag; + constant trigger_event : in trigger_event_t := penultimate; + constant level : in log_level_t := null_log_level; + constant active_clock_edge : in edge_t := rising_edge; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + ----------------------------------------------------------------------------- + -- check_relation + ----------------------------------------------------------------------------- + procedure check_relation( + constant checker : in checker_t; + constant expr : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_relation( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_relation( + constant expr : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_relation( + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_relation( + constant expr : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_relation( + constant checker : in checker_t; + constant expr : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_relation( + constant checker : in checker_t; + constant expr : in std_ulogic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_relation( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in std_ulogic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_relation( + constant expr : in std_ulogic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_relation( + variable pass : out boolean; + constant expr : in std_ulogic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_relation( + constant expr : in std_ulogic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_relation( + constant checker : in checker_t; + constant expr : in std_ulogic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_relation( + constant checker : in checker_t; + constant expr : in bit; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_relation( + constant checker : in checker_t; + variable pass : out boolean; + constant expr : in bit; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_relation( + constant expr : in bit; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_relation( + variable pass : out boolean; + constant expr : in bit; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_relation( + constant expr : in bit; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_relation( + constant checker : in checker_t; + constant expr : in bit; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant context_msg : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + ----------------------------------------------------------------------------- + -- check_(almost)_equal for real + ----------------------------------------------------------------------------- + + procedure check_equal( + constant got : in real; + constant expected : in real; + constant msg : in string := check_result_tag; + constant max_diff : in real := 0.0; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in real; + constant expected : in real; + constant msg : in string := check_result_tag; + constant max_diff : in real := 0.0; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + ----------------------------------------------------------------------------- + -- check_equal + ----------------------------------------------------------------------------- + procedure check_equal( + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_equal( + constant got : in unsigned; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in unsigned; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in unsigned; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in unsigned; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in unsigned; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in unsigned; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_equal( + constant got : in natural; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in natural; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in natural; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in natural; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in natural; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in natural; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_equal( + constant got : in unsigned; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in unsigned; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in unsigned; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in unsigned; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in unsigned; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in unsigned; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_equal( + constant got : in std_logic_vector; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in std_logic_vector; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in std_logic_vector; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in std_logic_vector; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in std_logic_vector; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in std_logic_vector; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_equal( + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_equal( + constant got : in std_logic_vector; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in std_logic_vector; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in std_logic_vector; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in std_logic_vector; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in std_logic_vector; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in std_logic_vector; + constant expected : in natural; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_equal( + constant got : in natural; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in natural; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in natural; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in natural; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in natural; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in natural; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_equal( + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_equal( + constant got : in signed; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in signed; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in signed; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in signed; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in signed; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in signed; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_equal( + constant got : in integer; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in integer; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in integer; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in integer; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in integer; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in integer; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_equal( + constant got : in integer; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in integer; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in integer; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in integer; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in integer; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in integer; + constant expected : in integer; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_equal( + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_equal( + constant got : in std_logic; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in std_logic; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in std_logic; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in std_logic; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in std_logic; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in std_logic; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_equal( + constant got : in boolean; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in boolean; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in boolean; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in boolean; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in boolean; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in boolean; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_equal( + constant got : in boolean; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in boolean; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in boolean; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in boolean; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in boolean; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in boolean; + constant expected : in boolean; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_equal( + constant got : in string; + constant expected : in string; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in string; + constant expected : in string; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in string; + constant expected : in string; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in string; + constant expected : in string; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in string; + constant expected : in string; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in string; + constant expected : in string; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_equal( + constant got : in time; + constant expected : in time; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in time; + constant expected : in time; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in time; + constant expected : in time; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in time; + constant expected : in time; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in time; + constant expected : in time; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in time; + constant expected : in time; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + ----------------------------------------------------------------------------- + -- check_match + ----------------------------------------------------------------------------- + procedure check_match( + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_match( + variable pass : out boolean; + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_match( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_match( + constant checker : in checker_t; + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_match( + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_match( + constant checker : in checker_t; + constant got : in unsigned; + constant expected : in unsigned; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_match( + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_match( + variable pass : out boolean; + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_match( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_match( + constant checker : in checker_t; + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_match( + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_match( + constant checker : in checker_t; + constant got : in std_logic_vector; + constant expected : in std_logic_vector; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_match( + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_match( + variable pass : out boolean; + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_match( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_match( + constant checker : in checker_t; + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_match( + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_match( + constant checker : in checker_t; + constant got : in signed; + constant expected : in signed; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + procedure check_match( + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_match( + variable pass : out boolean; + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_match( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_match( + constant checker : in checker_t; + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_match( + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_match( + constant checker : in checker_t; + constant got : in std_logic; + constant expected : in std_logic; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + ----------------------------------------------------------------------------- + +end package; diff --git a/vunit/vhdl/check/src/check_deprecated_pkg.vhd b/vunit/vhdl/check/src/check_deprecated_pkg.vhd index e4fe92a57..a14336c48 100644 --- a/vunit/vhdl/check/src/check_deprecated_pkg.vhd +++ b/vunit/vhdl/check/src/check_deprecated_pkg.vhd @@ -1,204 +1,204 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.log_levels_pkg.all; -use work.logger_pkg.all; -use work.log_handler_pkg.all; -use work.log_deprecated_pkg.all; -use work.checker_pkg.all; -use work.check_pkg.all; -use work.integer_vector_ptr_pkg.all; -use work.core_pkg.core_failure; - -use std.textio.all; - -package check_deprecated_pkg is - -- Deprecated interfaces to better support legacy testbenches. - - -- Calls to checker_init will be mapped to contemporary functionality using best effort: - -- - -- * default_src sets the name of an uninitialized checker. Empty string names are not supported - -- and will be replaced with "anonymous". - -- - -- - -- Changing checker - -- name of an already initialized logger is not allowed. In this case the - -- empty string is the only valid value. - -- * Changing the separator and append parameters to non-default values is not - -- supported - -- * The logger is configured with private display and file log handlers independent - -- of the predefined handlers used by default_logger - procedure checker_init ( - variable checker : inout checker_t; - constant default_level : in log_level_t := error; - constant default_src : in string := ""; - constant file_name : in string := "error.csv"; - constant display_format : in deprecated_log_format_t := level; - constant file_format : in deprecated_log_format_t := off; - constant stop_level : in log_level_t := failure; - constant separator : in character := ','; - constant append : in boolean := false); - - -- This checker_init is used to reinitialize the default_checker - procedure checker_init ( - constant default_level : in log_level_t := error; - constant default_src : in string := ""; - constant file_name : in string := "error.csv"; - constant display_format : in deprecated_log_format_t := level; - constant file_format : in deprecated_log_format_t := off; - constant stop_level : in log_level_t := failure; - constant separator : in character := ','; - constant append : in boolean := false); - - -- Enabling pass messages is done by setting the log level to pass unless - -- the current log level already allows pass messages. Disabling pass messages - -- will set log level to debug unless the current log level already suppresses - -- pass messages. - procedure enable_pass_msg(checker : checker_t; handler : log_handler_t); - procedure disable_pass_msg(checker : checker_t; handler : log_handler_t); - procedure enable_pass_msg(checker : checker_t); - procedure disable_pass_msg(checker : checker_t); - - procedure enable_pass_msg(handler : log_handler_t); - procedure disable_pass_msg(handler : log_handler_t); - procedure enable_pass_msg; - procedure disable_pass_msg; - - -- These subprograms can be replaced by calls to get_checker_stat and check for n_failed > 0 - procedure checker_found_errors ( - variable result : out boolean); - procedure checker_found_errors ( - constant checker : in checker_t; - variable result : out boolean); - impure function checker_found_errors - return boolean; - - -- Deprecated constant with _c suffix. Use without suffix instead - constant check_result_tag_c : string := check_result_tag; - -end package check_deprecated_pkg; - -package body check_deprecated_pkg is - constant anonymous_counter : integer_vector_ptr_t := new_integer_vector_ptr(1); - - procedure checker_init ( - variable checker : inout checker_t; - constant default_level : in log_level_t := error; - constant default_src : in string := ""; - constant file_name : in string := "error.csv"; - constant display_format : in deprecated_log_format_t := level; - constant file_format : in deprecated_log_format_t := off; - constant stop_level : in log_level_t := failure; - constant separator : in character := ','; - constant append : in boolean := false) is - variable name : line; - variable logger : logger_t := null_logger; - begin - warning("Using deprecated procedure checker_init. Using best effort mapping to contemporary functionality"); - - if checker = null_checker then - if default_src = "" then - write(name, "anonymous" & integer'image(get(anonymous_counter, 0))); - warning("Empty string checker names not supported. Using """ & name.all & """"); - set(anonymous_counter, 0, get(anonymous_counter, 0) + 1); - else - write(name, default_src); - end if; - logger_init(logger, name.all, file_name, display_format, file_format, stop_level, separator, append); - checker := new_checker(logger, default_level); - deallocate(name); - else - logger := get_logger(checker); - logger_init(logger, default_src, file_name, display_format, file_format, stop_level, separator, append); - set_default_log_level(checker, default_level); - end if; - end; - - procedure checker_init ( - constant default_level : in log_level_t := error; - constant default_src : in string := ""; - constant file_name : in string := "error.csv"; - constant display_format : in deprecated_log_format_t := level; - constant file_format : in deprecated_log_format_t := off; - constant stop_level : in log_level_t := failure; - constant separator : in character := ','; - constant append : in boolean := false) is - variable checker : checker_t := default_checker; - begin - checker_init(checker, default_level, default_src, file_name, display_format, - file_format, stop_level, separator, append); - end; - - procedure enable_pass_msg(checker : checker_t; handler : log_handler_t) is - begin - show(get_logger(checker), handler, pass); - end; - - procedure disable_pass_msg(checker : checker_t; handler : log_handler_t) is - begin - hide(get_logger(checker), handler, pass); - end; - - procedure enable_pass_msg(checker : checker_t) is - constant handlers : log_handler_vec_t := get_log_handlers(get_logger(checker)); - begin - for h in handlers'range loop - enable_pass_msg(checker, handlers(h)); - end loop; - end; - - procedure disable_pass_msg(checker : checker_t) is - constant handlers : log_handler_vec_t := get_log_handlers(get_logger(checker)); - begin - for h in handlers'range loop - disable_pass_msg(checker, handlers(h)); - end loop; - end; - - procedure enable_pass_msg(handler : log_handler_t) is - begin - enable_pass_msg(default_checker, handler); - end; - - procedure disable_pass_msg(handler : log_handler_t) is - begin - disable_pass_msg(default_checker, handler); - end; - - procedure enable_pass_msg is - begin - enable_pass_msg(default_checker); - end; - - procedure disable_pass_msg is - begin - disable_pass_msg(default_checker); - end; - - procedure checker_found_errors ( - variable result : out boolean) is - begin - checker_found_errors(default_checker, result); - end; - - procedure checker_found_errors ( - constant checker :in checker_t; - variable result : out boolean) is - variable stat : checker_stat_t; - begin - warning("Using deprecated checker_found_errors. Use get_checker_stat instead."); - stat := get_checker_stat(checker); - result := stat.n_failed > 0; - end; - - impure function checker_found_errors return boolean is - variable result : boolean; - begin - checker_found_errors(default_checker, result); - return result; - end; - -end package body check_deprecated_pkg; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.log_levels_pkg.all; +use work.logger_pkg.all; +use work.log_handler_pkg.all; +use work.log_deprecated_pkg.all; +use work.checker_pkg.all; +use work.check_pkg.all; +use work.integer_vector_ptr_pkg.all; +use work.core_pkg.core_failure; + +use std.textio.all; + +package check_deprecated_pkg is + -- Deprecated interfaces to better support legacy testbenches. + + -- Calls to checker_init will be mapped to contemporary functionality using best effort: + -- + -- * default_src sets the name of an uninitialized checker. Empty string names are not supported + -- and will be replaced with "anonymous". + -- + -- + -- Changing checker + -- name of an already initialized logger is not allowed. In this case the + -- empty string is the only valid value. + -- * Changing the separator and append parameters to non-default values is not + -- supported + -- * The logger is configured with private display and file log handlers independent + -- of the predefined handlers used by default_logger + procedure checker_init ( + variable checker : inout checker_t; + constant default_level : in log_level_t := error; + constant default_src : in string := ""; + constant file_name : in string := "error.csv"; + constant display_format : in deprecated_log_format_t := level; + constant file_format : in deprecated_log_format_t := off; + constant stop_level : in log_level_t := failure; + constant separator : in character := ','; + constant append : in boolean := false); + + -- This checker_init is used to reinitialize the default_checker + procedure checker_init ( + constant default_level : in log_level_t := error; + constant default_src : in string := ""; + constant file_name : in string := "error.csv"; + constant display_format : in deprecated_log_format_t := level; + constant file_format : in deprecated_log_format_t := off; + constant stop_level : in log_level_t := failure; + constant separator : in character := ','; + constant append : in boolean := false); + + -- Enabling pass messages is done by setting the log level to pass unless + -- the current log level already allows pass messages. Disabling pass messages + -- will set log level to debug unless the current log level already suppresses + -- pass messages. + procedure enable_pass_msg(checker : checker_t; handler : log_handler_t); + procedure disable_pass_msg(checker : checker_t; handler : log_handler_t); + procedure enable_pass_msg(checker : checker_t); + procedure disable_pass_msg(checker : checker_t); + + procedure enable_pass_msg(handler : log_handler_t); + procedure disable_pass_msg(handler : log_handler_t); + procedure enable_pass_msg; + procedure disable_pass_msg; + + -- These subprograms can be replaced by calls to get_checker_stat and check for n_failed > 0 + procedure checker_found_errors ( + variable result : out boolean); + procedure checker_found_errors ( + constant checker : in checker_t; + variable result : out boolean); + impure function checker_found_errors + return boolean; + + -- Deprecated constant with _c suffix. Use without suffix instead + constant check_result_tag_c : string := check_result_tag; + +end package check_deprecated_pkg; + +package body check_deprecated_pkg is + constant anonymous_counter : integer_vector_ptr_t := new_integer_vector_ptr(1); + + procedure checker_init ( + variable checker : inout checker_t; + constant default_level : in log_level_t := error; + constant default_src : in string := ""; + constant file_name : in string := "error.csv"; + constant display_format : in deprecated_log_format_t := level; + constant file_format : in deprecated_log_format_t := off; + constant stop_level : in log_level_t := failure; + constant separator : in character := ','; + constant append : in boolean := false) is + variable name : line; + variable logger : logger_t := null_logger; + begin + warning("Using deprecated procedure checker_init. Using best effort mapping to contemporary functionality"); + + if checker = null_checker then + if default_src = "" then + write(name, "anonymous" & integer'image(get(anonymous_counter, 0))); + warning("Empty string checker names not supported. Using """ & name.all & """"); + set(anonymous_counter, 0, get(anonymous_counter, 0) + 1); + else + write(name, default_src); + end if; + logger_init(logger, name.all, file_name, display_format, file_format, stop_level, separator, append); + checker := new_checker(logger, default_level); + deallocate(name); + else + logger := get_logger(checker); + logger_init(logger, default_src, file_name, display_format, file_format, stop_level, separator, append); + set_default_log_level(checker, default_level); + end if; + end; + + procedure checker_init ( + constant default_level : in log_level_t := error; + constant default_src : in string := ""; + constant file_name : in string := "error.csv"; + constant display_format : in deprecated_log_format_t := level; + constant file_format : in deprecated_log_format_t := off; + constant stop_level : in log_level_t := failure; + constant separator : in character := ','; + constant append : in boolean := false) is + variable checker : checker_t := default_checker; + begin + checker_init(checker, default_level, default_src, file_name, display_format, + file_format, stop_level, separator, append); + end; + + procedure enable_pass_msg(checker : checker_t; handler : log_handler_t) is + begin + show(get_logger(checker), handler, pass); + end; + + procedure disable_pass_msg(checker : checker_t; handler : log_handler_t) is + begin + hide(get_logger(checker), handler, pass); + end; + + procedure enable_pass_msg(checker : checker_t) is + constant handlers : log_handler_vec_t := get_log_handlers(get_logger(checker)); + begin + for h in handlers'range loop + enable_pass_msg(checker, handlers(h)); + end loop; + end; + + procedure disable_pass_msg(checker : checker_t) is + constant handlers : log_handler_vec_t := get_log_handlers(get_logger(checker)); + begin + for h in handlers'range loop + disable_pass_msg(checker, handlers(h)); + end loop; + end; + + procedure enable_pass_msg(handler : log_handler_t) is + begin + enable_pass_msg(default_checker, handler); + end; + + procedure disable_pass_msg(handler : log_handler_t) is + begin + disable_pass_msg(default_checker, handler); + end; + + procedure enable_pass_msg is + begin + enable_pass_msg(default_checker); + end; + + procedure disable_pass_msg is + begin + disable_pass_msg(default_checker); + end; + + procedure checker_found_errors ( + variable result : out boolean) is + begin + checker_found_errors(default_checker, result); + end; + + procedure checker_found_errors ( + constant checker :in checker_t; + variable result : out boolean) is + variable stat : checker_stat_t; + begin + warning("Using deprecated checker_found_errors. Use get_checker_stat instead."); + stat := get_checker_stat(checker); + result := stat.n_failed > 0; + end; + + impure function checker_found_errors return boolean is + variable result : boolean; + begin + checker_found_errors(default_checker, result); + return result; + end; + +end package body check_deprecated_pkg; diff --git a/vunit/vhdl/check/src/checker_pkg-body.vhd b/vunit/vhdl/check/src/checker_pkg-body.vhd index 8b6c77675..3dd4b3cde 100644 --- a/vunit/vhdl/check/src/checker_pkg-body.vhd +++ b/vunit/vhdl/check/src/checker_pkg-body.vhd @@ -1,161 +1,161 @@ --- This package provides fundamental types used by the check package. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - - -package body checker_pkg is - - constant logger_idx : natural := 0; - constant default_log_level_idx : natural := 1; - constant stat_checks_idx : natural := 2; - constant stat_failed_idx : natural := 3; - constant stat_passed_idx : natural := 4; - constant checker_length : natural := stat_passed_idx + 1; - - impure function new_checker(logger_name : string; - default_log_level : log_level_t := error) return checker_t is - begin - return new_checker(get_logger(logger_name), default_log_level); - end; - - impure function new_checker(logger : logger_t; - default_log_level : log_level_t := error) return checker_t is - variable checker : checker_t; - variable id : natural; - begin - checker := (p_data => new_integer_vector_ptr(checker_length)); - set(checker.p_data, logger_idx, to_integer(logger.p_data)); - set(checker.p_data, default_log_level_idx, log_level_t'pos(default_log_level)); - reset_checker_stat(checker); - return checker; - end; - - impure function get_logger(checker : checker_t) return logger_t is - begin - return (p_data => to_integer_vector_ptr(get(checker.p_data, logger_idx))); - end; - - impure function get_default_log_level(checker : checker_t) return log_level_t is - begin - return log_level_t'val(get(checker.p_data, default_log_level_idx)); - end; - - procedure set_default_log_level(checker : checker_t; default_log_level : log_level_t) is - begin - set(checker.p_data, default_log_level_idx, log_level_t'pos(default_log_level)); - end; - - procedure reset_checker_stat(checker : checker_t) is - begin - set(checker.p_data, stat_checks_idx, 0); - set(checker.p_data, stat_failed_idx, 0); - set(checker.p_data, stat_passed_idx, 0); - end; - - impure function get_checker_stat(checker : checker_t) return checker_stat_t is - begin - return (n_checks => get(checker.p_data, stat_checks_idx), - n_failed => get(checker.p_data, stat_failed_idx), - n_passed => get(checker.p_data, stat_passed_idx)); - - end; - - procedure get_checker_stat(checker : checker_t; - variable stat : out checker_stat_t) is - begin - stat := get_checker_stat(checker); - end; - - impure function is_pass_visible(checker : checker_t) return boolean is - begin - return is_visible(get_logger(checker), pass); - end; - - procedure passing_check(checker : checker_t) is - constant logger : logger_t := get_logger(checker); - begin - set(checker.p_data, stat_checks_idx, get(checker.p_data, stat_checks_idx) + 1); - set(checker.p_data, stat_passed_idx, get(checker.p_data, stat_passed_idx) + 1); - - log(logger, "", pass); -- invisible log - end; - - procedure passing_check( - checker : checker_t; - msg : string; - line_num : natural := 0; - file_name : string := "") is - constant logger : logger_t := get_logger(checker); - begin - -- pragma translate_off - set(checker.p_data, stat_checks_idx, get(checker.p_data, stat_checks_idx) + 1); - set(checker.p_data, stat_passed_idx, get(checker.p_data, stat_passed_idx) + 1); - - if is_visible(logger, pass) then - log(logger, msg, pass, line_num, file_name); - else - log(logger, "", pass); -- invisible log - end if; - - -- pragma translate_on - end; - - procedure failing_check( - checker : checker_t; - msg : string; - level : log_level_t := null_log_level; - line_num : natural := 0; - file_name : string := "") is - begin - -- pragma translate_off - set(checker.p_data, stat_checks_idx, get(checker.p_data, stat_checks_idx) + 1); - set(checker.p_data, stat_failed_idx, get(checker.p_data, stat_failed_idx) + 1); - - if level = null_log_level then - log(get_logger(checker), msg, get_default_log_level(checker), line_num, file_name); - else - log(get_logger(checker), msg, level, line_num, file_name); - end if; - -- pragma translate_on - end; - - function "+" ( - stat1 : checker_stat_t; - stat2 : checker_stat_t) - return checker_stat_t is - variable sum : checker_stat_t; - begin - sum.n_checks := stat1.n_checks + stat2.n_checks; - sum.n_passed := stat1.n_passed + stat2.n_passed; - sum.n_failed := stat1.n_failed + stat2.n_failed; - - return sum; - end function "+"; - - function "-" ( - stat1 : checker_stat_t; - stat2 : checker_stat_t) - return checker_stat_t is - variable diff : checker_stat_t; - begin - diff.n_checks := stat1.n_checks - stat2.n_checks; - diff.n_passed := stat1.n_passed - stat2.n_passed; - diff.n_failed := stat1.n_failed - stat2.n_failed; - - return diff; - end function "-"; - - function to_string(stat : checker_stat_t) return string is - begin - return ("checker_stat'("& - "n_checks => " & integer'image(stat.n_checks) & ", " & - "n_failed => " & integer'image(stat.n_failed) & ", " & - "n_passed => " & integer'image(stat.n_passed) & - ")"); - end function; - -end package body; +-- This package provides fundamental types used by the check package. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + + +package body checker_pkg is + + constant logger_idx : natural := 0; + constant default_log_level_idx : natural := 1; + constant stat_checks_idx : natural := 2; + constant stat_failed_idx : natural := 3; + constant stat_passed_idx : natural := 4; + constant checker_length : natural := stat_passed_idx + 1; + + impure function new_checker(logger_name : string; + default_log_level : log_level_t := error) return checker_t is + begin + return new_checker(get_logger(logger_name), default_log_level); + end; + + impure function new_checker(logger : logger_t; + default_log_level : log_level_t := error) return checker_t is + variable checker : checker_t; + variable id : natural; + begin + checker := (p_data => new_integer_vector_ptr(checker_length)); + set(checker.p_data, logger_idx, to_integer(logger.p_data)); + set(checker.p_data, default_log_level_idx, log_level_t'pos(default_log_level)); + reset_checker_stat(checker); + return checker; + end; + + impure function get_logger(checker : checker_t) return logger_t is + begin + return (p_data => to_integer_vector_ptr(get(checker.p_data, logger_idx))); + end; + + impure function get_default_log_level(checker : checker_t) return log_level_t is + begin + return log_level_t'val(get(checker.p_data, default_log_level_idx)); + end; + + procedure set_default_log_level(checker : checker_t; default_log_level : log_level_t) is + begin + set(checker.p_data, default_log_level_idx, log_level_t'pos(default_log_level)); + end; + + procedure reset_checker_stat(checker : checker_t) is + begin + set(checker.p_data, stat_checks_idx, 0); + set(checker.p_data, stat_failed_idx, 0); + set(checker.p_data, stat_passed_idx, 0); + end; + + impure function get_checker_stat(checker : checker_t) return checker_stat_t is + begin + return (n_checks => get(checker.p_data, stat_checks_idx), + n_failed => get(checker.p_data, stat_failed_idx), + n_passed => get(checker.p_data, stat_passed_idx)); + + end; + + procedure get_checker_stat(checker : checker_t; + variable stat : out checker_stat_t) is + begin + stat := get_checker_stat(checker); + end; + + impure function is_pass_visible(checker : checker_t) return boolean is + begin + return is_visible(get_logger(checker), pass); + end; + + procedure passing_check(checker : checker_t) is + constant logger : logger_t := get_logger(checker); + begin + set(checker.p_data, stat_checks_idx, get(checker.p_data, stat_checks_idx) + 1); + set(checker.p_data, stat_passed_idx, get(checker.p_data, stat_passed_idx) + 1); + + log(logger, "", pass); -- invisible log + end; + + procedure passing_check( + checker : checker_t; + msg : string; + line_num : natural := 0; + file_name : string := "") is + constant logger : logger_t := get_logger(checker); + begin + -- pragma translate_off + set(checker.p_data, stat_checks_idx, get(checker.p_data, stat_checks_idx) + 1); + set(checker.p_data, stat_passed_idx, get(checker.p_data, stat_passed_idx) + 1); + + if is_visible(logger, pass) then + log(logger, msg, pass, line_num, file_name); + else + log(logger, "", pass); -- invisible log + end if; + + -- pragma translate_on + end; + + procedure failing_check( + checker : checker_t; + msg : string; + level : log_level_t := null_log_level; + line_num : natural := 0; + file_name : string := "") is + begin + -- pragma translate_off + set(checker.p_data, stat_checks_idx, get(checker.p_data, stat_checks_idx) + 1); + set(checker.p_data, stat_failed_idx, get(checker.p_data, stat_failed_idx) + 1); + + if level = null_log_level then + log(get_logger(checker), msg, get_default_log_level(checker), line_num, file_name); + else + log(get_logger(checker), msg, level, line_num, file_name); + end if; + -- pragma translate_on + end; + + function "+" ( + stat1 : checker_stat_t; + stat2 : checker_stat_t) + return checker_stat_t is + variable sum : checker_stat_t; + begin + sum.n_checks := stat1.n_checks + stat2.n_checks; + sum.n_passed := stat1.n_passed + stat2.n_passed; + sum.n_failed := stat1.n_failed + stat2.n_failed; + + return sum; + end function "+"; + + function "-" ( + stat1 : checker_stat_t; + stat2 : checker_stat_t) + return checker_stat_t is + variable diff : checker_stat_t; + begin + diff.n_checks := stat1.n_checks - stat2.n_checks; + diff.n_passed := stat1.n_passed - stat2.n_passed; + diff.n_failed := stat1.n_failed - stat2.n_failed; + + return diff; + end function "-"; + + function to_string(stat : checker_stat_t) return string is + begin + return ("checker_stat'("& + "n_checks => " & integer'image(stat.n_checks) & ", " & + "n_failed => " & integer'image(stat.n_failed) & ", " & + "n_passed => " & integer'image(stat.n_passed) & + ")"); + end function; + +end package body; diff --git a/vunit/vhdl/check/src/checker_pkg.vhd b/vunit/vhdl/check/src/checker_pkg.vhd index fd9a89647..3815c5848 100644 --- a/vunit/vhdl/check/src/checker_pkg.vhd +++ b/vunit/vhdl/check/src/checker_pkg.vhd @@ -1,68 +1,68 @@ --- This package provides fundamental types used by the check package. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.log_levels_pkg.all; -use work.logger_pkg.all; -use work.integer_vector_ptr_pkg.all; - -package checker_pkg is - type checker_t is record - p_data : integer_vector_ptr_t; - end record; - constant null_checker : checker_t := (p_data => null_ptr); - - impure function new_checker(logger_name : string; - default_log_level : log_level_t := error) return checker_t; - impure function new_checker(logger : logger_t; - default_log_level : log_level_t := error) return checker_t; - - impure function get_logger(checker : checker_t) return logger_t; - impure function get_default_log_level(checker : checker_t) return log_level_t; - procedure set_default_log_level(checker : checker_t; default_log_level : log_level_t); - - impure function is_pass_visible(checker : checker_t) return boolean; - - procedure passing_check(checker : checker_t); - - procedure passing_check( - checker : checker_t; - msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure failing_check( - checker : checker_t; - msg : string; - level : log_level_t := null_log_level; - line_num : natural := 0; - file_name : string := ""); - - type checker_stat_t is record - n_checks : natural; - n_failed : natural; - n_passed : natural; - end record; - - function "+" ( - stat1 : checker_stat_t; - stat2 : checker_stat_t) - return checker_stat_t; - - function "-" ( - stat1 : checker_stat_t; - stat2 : checker_stat_t) - return checker_stat_t; - - function to_string(stat : checker_stat_t) return string; - - impure function get_checker_stat(checker : checker_t) return checker_stat_t; - procedure reset_checker_stat(checker : checker_t); - procedure get_checker_stat(checker : checker_t; - variable stat : out checker_stat_t); - -end package; +-- This package provides fundamental types used by the check package. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.log_levels_pkg.all; +use work.logger_pkg.all; +use work.integer_vector_ptr_pkg.all; + +package checker_pkg is + type checker_t is record + p_data : integer_vector_ptr_t; + end record; + constant null_checker : checker_t := (p_data => null_ptr); + + impure function new_checker(logger_name : string; + default_log_level : log_level_t := error) return checker_t; + impure function new_checker(logger : logger_t; + default_log_level : log_level_t := error) return checker_t; + + impure function get_logger(checker : checker_t) return logger_t; + impure function get_default_log_level(checker : checker_t) return log_level_t; + procedure set_default_log_level(checker : checker_t; default_log_level : log_level_t); + + impure function is_pass_visible(checker : checker_t) return boolean; + + procedure passing_check(checker : checker_t); + + procedure passing_check( + checker : checker_t; + msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure failing_check( + checker : checker_t; + msg : string; + level : log_level_t := null_log_level; + line_num : natural := 0; + file_name : string := ""); + + type checker_stat_t is record + n_checks : natural; + n_failed : natural; + n_passed : natural; + end record; + + function "+" ( + stat1 : checker_stat_t; + stat2 : checker_stat_t) + return checker_stat_t; + + function "-" ( + stat1 : checker_stat_t; + stat2 : checker_stat_t) + return checker_stat_t; + + function to_string(stat : checker_stat_t) return string; + + impure function get_checker_stat(checker : checker_t) return checker_stat_t; + procedure reset_checker_stat(checker : checker_t); + procedure get_checker_stat(checker : checker_t; + variable stat : out checker_stat_t); + +end package; diff --git a/vunit/vhdl/check/test/tb_check.vhd b/vunit/vhdl/check/test/tb_check.vhd index ca60d0a53..befcbd352 100644 --- a/vunit/vhdl/check/test/tb_check.vhd +++ b/vunit/vhdl/check/test/tb_check.vhd @@ -1,315 +1,315 @@ --- This test suite verifies the check checker. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library ieee; -use ieee.std_logic_1164.all; -library vunit_lib; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use vunit_lib.runner_pkg.all; -use work.test_support.all; -use ieee.numeric_std.all; -entity tb_check is - generic ( - use_check_not_check_true : boolean := true; - runner_cfg : string); -end entity tb_check; - -architecture test_fixture of tb_check is - signal clk : std_logic := '0'; - signal check_in_1, check_in_2, check_in_3, check_in_4 : std_logic := '1'; - signal check_en_1, check_en_2, check_en_3, check_en_4 : std_logic := '1'; - - constant check_checker : checker_t := new_checker("checker"); - constant check_checker2 : checker_t := new_checker("checker2"); - constant check_checker3 : checker_t := new_checker("checker3", default_log_level => info); - constant check_checker4 : checker_t := new_checker("checker4"); - -begin - clock: process is - begin - while get_phase(runner_state) < test_runner_exit loop - clk <= '1', '0' after 5 ns; - wait for 10 ns; - end loop; - wait; - end process clock; - - concurrent_checks: if use_check_not_check_true generate - check_1 : check(clk, check_en_1, check_in_1); - check_2 : check(check_checker2, clk, check_en_2, check_in_2, active_clock_edge => falling_edge); - check_3 : check(check_checker3, clk, check_en_3, check_in_3); - check_4 : check(check_checker4, clk, check_en_4, check_in_4); - end generate concurrent_checks; - - concurrent_true_checks: if not use_check_not_check_true generate - check_1 : check_true(clk, check_en_1, check_in_1); - check_2 : check_true(check_checker2, clk, check_en_2, check_in_2, active_clock_edge => falling_edge); - check_3 : check_true(check_checker3, clk, check_en_3, check_in_3); - check_4 : check_true(check_checker4, clk, check_en_4, check_in_4); - end generate concurrent_true_checks; - - check_runner : process - variable passed : boolean; - variable stat : checker_stat_t; - constant default_level : log_level_t := error; - - function prefix return string is - begin - if use_check_not_check_true then - return "Check "; - else - return "True check "; - end if; - end function prefix; - - procedure test_concurrent_check ( - signal clk : in std_logic; - signal check_input : out std_logic; - checker : checker_t; - constant level : in log_level_t := error; - constant active_rising_clock_edge : in boolean := true) is - begin - -- Verify that one log is generated on false and that that log is - -- generated on the correct clock edge. No log on true. - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - get_checker_stat(checker, stat); - apply_sequence("0", clk, check_input, active_rising_clock_edge); - wait until clock_edge(clk, not active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 0); - mock(get_logger(checker)); - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - check_only_log(get_logger(checker), prefix & "failed.", level); - unmock(get_logger(checker)); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 1); - apply_sequence("1", clk, check_input, active_rising_clock_edge); - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 1); - verify_failed_checks(checker, stat, 1); - reset_checker_stat(checker); - end procedure test_concurrent_check; - - procedure internal_check( - checker : checker_t; - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := result(".")) is - begin - if use_check_not_check_true then - check(checker, pass, expr, msg); - else - check_true(checker, pass, expr, msg); - end if; - end; - - procedure internal_check( - checker : checker_t; - constant expr : in boolean; - constant msg : in string := result(".")) is - begin - if use_check_not_check_true then - check(checker, expr, msg); - else - check_true(checker, expr, msg); - end if; - end; - - procedure internal_check( - constant expr : in boolean; - constant msg : in string := result(".")) is - begin - if use_check_not_check_true then - check(expr, msg); - else - check_true(expr, msg); - end if; - end; - - procedure internal_check( - variable pass : out boolean; - constant expr : in boolean; - constant msg : in string := result(".")) is - begin - if use_check_not_check_true then - check(pass, expr, msg); - else - check_true(pass, expr, msg); - end if; - end; - - impure function internal_check( - constant expr : in boolean; - constant msg : in string := result(".")) - return boolean is - begin - if use_check_not_check_true then - return check(expr, msg); - else - return check_true(expr, msg); - end if; - end; - - impure function internal_check(checker : checker_t; expr : boolean; msg : string := result(".")) - return boolean is - begin - if use_check_not_check_true then - return check(checker, expr, msg); - else - return check_true(checker, expr, msg); - end if; - end; - - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test should pass on true inputs to sequential checks") then - get_checker_stat(stat); - internal_check(true); - internal_check(passed, true); - assert_true(passed, "Should return pass = true on passing check"); - passed := internal_check(true); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(stat, 3); - - get_checker_stat(check_checker, stat); - internal_check(check_checker, true); - internal_check(check_checker, passed, true); - assert_true(passed, "Should return pass = true on passing check"); - passed := internal_check(check_checker, true); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(check_checker, stat, 3); - - elsif run("Test pass message") then - mock(check_logger); - internal_check(true); - check_only_log(check_logger, prefix & "passed.", pass); - - internal_check(true, ""); - check_only_log(check_logger, "", pass); - - internal_check(true, "Checking my data."); - check_only_log(check_logger, "Checking my data.", pass); - - internal_check(true, result("for my data.")); - check_only_log(check_logger, prefix & "passed for my data.", pass); - unmock(check_logger); - - elsif run("Test should fail on false inputs to sequential checks") then - get_checker_stat(stat); - mock(check_logger); - internal_check(false); - check_only_log(check_logger, prefix & "failed.", default_level); - - internal_check(false, ""); - check_only_log(check_logger, "", default_level); - - internal_check(passed, false, "Checking my data."); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Checking my data.", default_level); - - passed := internal_check(false, result("for my data.")); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, prefix & "failed for my data.", default_level); - unmock(check_logger); - verify_failed_checks(stat, 4); - reset_checker_stat; - - get_checker_stat(check_checker, stat); - mock(get_logger(check_checker)); - internal_check(check_checker, false); - check_only_log(get_logger(check_checker), prefix & "failed.", default_level); - - internal_check(check_checker, passed, false, result("for my data.")); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(check_checker), prefix & "failed for my data.", default_level); - - passed := internal_check(check_checker, false, result("for my data.")); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(check_checker), prefix & "failed for my data.", default_level); - unmock(get_logger(check_checker)); - verify_failed_checks(check_checker, stat, 3); - reset_checker_stat(check_checker); - - elsif run("Test should be possible to use concurrently") then - test_concurrent_check(clk, check_in_1, default_checker); - - elsif run("Test should be possible to use concurrently with negative active clock edge") then - test_concurrent_check(clk, check_in_2, check_checker2, error, false); - - elsif run("Test should be possible to use concurrently with custom checker") then - test_concurrent_check(clk, check_in_3, check_checker3, info); - - elsif run("Test should pass on weak high but fail on other meta values") then - - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(check_checker4, stat); - apply_sequence("1H1", clk, check_in_4); - wait until rising_edge(clk); - wait for 1 ns; - mock(get_logger(check_checker4)); - verify_passed_checks(check_checker4, stat, 3); - get_checker_stat(check_checker4, stat); - apply_sequence("1UXZWL-1", clk, check_in_4); - wait until rising_edge(clk); - wait for 1 ns; - check_log(get_logger(check_checker4), prefix & "passed.", pass); - check_log(get_logger(check_checker4), prefix & "failed.", default_level); - check_log(get_logger(check_checker4), prefix & "failed.", default_level); - check_log(get_logger(check_checker4), prefix & "failed.", default_level); - check_log(get_logger(check_checker4), prefix & "failed.", default_level); - check_log(get_logger(check_checker4), prefix & "failed.", default_level); - check_log(get_logger(check_checker4), prefix & "failed.", default_level); - check_log(get_logger(check_checker4), prefix & "passed.", pass); - unmock(get_logger(check_checker4)); - verify_passed_checks(check_checker4, stat, 2); - verify_failed_checks(check_checker4, stat, 6); - reset_checker_stat(check_checker4); - - elsif run("Test should pass on logic low inputs when not enabled") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(stat); - check_en_1 <= '0'; - apply_sequence("01", clk, check_in_1); - check_en_1 <= '1'; - wait until rising_edge(clk); - check_en_1 <= 'L'; - apply_sequence("01", clk, check_in_1); - check_en_1 <= 'H'; - wait until rising_edge(clk); - check_en_1 <= 'X'; - apply_sequence("01", clk, check_in_1); - check_en_1 <= '1'; - wait until rising_edge(clk); - wait for 1 ns; - verify_passed_checks(stat, 3); - verify_failed_checks(stat, 0); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 2 us); - -end test_fixture; +-- This test suite verifies the check checker. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library ieee; +use ieee.std_logic_1164.all; +library vunit_lib; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use vunit_lib.runner_pkg.all; +use work.test_support.all; +use ieee.numeric_std.all; +entity tb_check is + generic ( + use_check_not_check_true : boolean := true; + runner_cfg : string); +end entity tb_check; + +architecture test_fixture of tb_check is + signal clk : std_logic := '0'; + signal check_in_1, check_in_2, check_in_3, check_in_4 : std_logic := '1'; + signal check_en_1, check_en_2, check_en_3, check_en_4 : std_logic := '1'; + + constant check_checker : checker_t := new_checker("checker"); + constant check_checker2 : checker_t := new_checker("checker2"); + constant check_checker3 : checker_t := new_checker("checker3", default_log_level => info); + constant check_checker4 : checker_t := new_checker("checker4"); + +begin + clock: process is + begin + while get_phase(runner_state) < test_runner_exit loop + clk <= '1', '0' after 5 ns; + wait for 10 ns; + end loop; + wait; + end process clock; + + concurrent_checks: if use_check_not_check_true generate + check_1 : check(clk, check_en_1, check_in_1); + check_2 : check(check_checker2, clk, check_en_2, check_in_2, active_clock_edge => falling_edge); + check_3 : check(check_checker3, clk, check_en_3, check_in_3); + check_4 : check(check_checker4, clk, check_en_4, check_in_4); + end generate concurrent_checks; + + concurrent_true_checks: if not use_check_not_check_true generate + check_1 : check_true(clk, check_en_1, check_in_1); + check_2 : check_true(check_checker2, clk, check_en_2, check_in_2, active_clock_edge => falling_edge); + check_3 : check_true(check_checker3, clk, check_en_3, check_in_3); + check_4 : check_true(check_checker4, clk, check_en_4, check_in_4); + end generate concurrent_true_checks; + + check_runner : process + variable passed : boolean; + variable stat : checker_stat_t; + constant default_level : log_level_t := error; + + function prefix return string is + begin + if use_check_not_check_true then + return "Check "; + else + return "True check "; + end if; + end function prefix; + + procedure test_concurrent_check ( + signal clk : in std_logic; + signal check_input : out std_logic; + checker : checker_t; + constant level : in log_level_t := error; + constant active_rising_clock_edge : in boolean := true) is + begin + -- Verify that one log is generated on false and that that log is + -- generated on the correct clock edge. No log on true. + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + get_checker_stat(checker, stat); + apply_sequence("0", clk, check_input, active_rising_clock_edge); + wait until clock_edge(clk, not active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 0); + mock(get_logger(checker)); + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + check_only_log(get_logger(checker), prefix & "failed.", level); + unmock(get_logger(checker)); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 1); + apply_sequence("1", clk, check_input, active_rising_clock_edge); + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 1); + verify_failed_checks(checker, stat, 1); + reset_checker_stat(checker); + end procedure test_concurrent_check; + + procedure internal_check( + checker : checker_t; + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := result(".")) is + begin + if use_check_not_check_true then + check(checker, pass, expr, msg); + else + check_true(checker, pass, expr, msg); + end if; + end; + + procedure internal_check( + checker : checker_t; + constant expr : in boolean; + constant msg : in string := result(".")) is + begin + if use_check_not_check_true then + check(checker, expr, msg); + else + check_true(checker, expr, msg); + end if; + end; + + procedure internal_check( + constant expr : in boolean; + constant msg : in string := result(".")) is + begin + if use_check_not_check_true then + check(expr, msg); + else + check_true(expr, msg); + end if; + end; + + procedure internal_check( + variable pass : out boolean; + constant expr : in boolean; + constant msg : in string := result(".")) is + begin + if use_check_not_check_true then + check(pass, expr, msg); + else + check_true(pass, expr, msg); + end if; + end; + + impure function internal_check( + constant expr : in boolean; + constant msg : in string := result(".")) + return boolean is + begin + if use_check_not_check_true then + return check(expr, msg); + else + return check_true(expr, msg); + end if; + end; + + impure function internal_check(checker : checker_t; expr : boolean; msg : string := result(".")) + return boolean is + begin + if use_check_not_check_true then + return check(checker, expr, msg); + else + return check_true(checker, expr, msg); + end if; + end; + + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test should pass on true inputs to sequential checks") then + get_checker_stat(stat); + internal_check(true); + internal_check(passed, true); + assert_true(passed, "Should return pass = true on passing check"); + passed := internal_check(true); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(stat, 3); + + get_checker_stat(check_checker, stat); + internal_check(check_checker, true); + internal_check(check_checker, passed, true); + assert_true(passed, "Should return pass = true on passing check"); + passed := internal_check(check_checker, true); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(check_checker, stat, 3); + + elsif run("Test pass message") then + mock(check_logger); + internal_check(true); + check_only_log(check_logger, prefix & "passed.", pass); + + internal_check(true, ""); + check_only_log(check_logger, "", pass); + + internal_check(true, "Checking my data."); + check_only_log(check_logger, "Checking my data.", pass); + + internal_check(true, result("for my data.")); + check_only_log(check_logger, prefix & "passed for my data.", pass); + unmock(check_logger); + + elsif run("Test should fail on false inputs to sequential checks") then + get_checker_stat(stat); + mock(check_logger); + internal_check(false); + check_only_log(check_logger, prefix & "failed.", default_level); + + internal_check(false, ""); + check_only_log(check_logger, "", default_level); + + internal_check(passed, false, "Checking my data."); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Checking my data.", default_level); + + passed := internal_check(false, result("for my data.")); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, prefix & "failed for my data.", default_level); + unmock(check_logger); + verify_failed_checks(stat, 4); + reset_checker_stat; + + get_checker_stat(check_checker, stat); + mock(get_logger(check_checker)); + internal_check(check_checker, false); + check_only_log(get_logger(check_checker), prefix & "failed.", default_level); + + internal_check(check_checker, passed, false, result("for my data.")); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(check_checker), prefix & "failed for my data.", default_level); + + passed := internal_check(check_checker, false, result("for my data.")); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(check_checker), prefix & "failed for my data.", default_level); + unmock(get_logger(check_checker)); + verify_failed_checks(check_checker, stat, 3); + reset_checker_stat(check_checker); + + elsif run("Test should be possible to use concurrently") then + test_concurrent_check(clk, check_in_1, default_checker); + + elsif run("Test should be possible to use concurrently with negative active clock edge") then + test_concurrent_check(clk, check_in_2, check_checker2, error, false); + + elsif run("Test should be possible to use concurrently with custom checker") then + test_concurrent_check(clk, check_in_3, check_checker3, info); + + elsif run("Test should pass on weak high but fail on other meta values") then + + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(check_checker4, stat); + apply_sequence("1H1", clk, check_in_4); + wait until rising_edge(clk); + wait for 1 ns; + mock(get_logger(check_checker4)); + verify_passed_checks(check_checker4, stat, 3); + get_checker_stat(check_checker4, stat); + apply_sequence("1UXZWL-1", clk, check_in_4); + wait until rising_edge(clk); + wait for 1 ns; + check_log(get_logger(check_checker4), prefix & "passed.", pass); + check_log(get_logger(check_checker4), prefix & "failed.", default_level); + check_log(get_logger(check_checker4), prefix & "failed.", default_level); + check_log(get_logger(check_checker4), prefix & "failed.", default_level); + check_log(get_logger(check_checker4), prefix & "failed.", default_level); + check_log(get_logger(check_checker4), prefix & "failed.", default_level); + check_log(get_logger(check_checker4), prefix & "failed.", default_level); + check_log(get_logger(check_checker4), prefix & "passed.", pass); + unmock(get_logger(check_checker4)); + verify_passed_checks(check_checker4, stat, 2); + verify_failed_checks(check_checker4, stat, 6); + reset_checker_stat(check_checker4); + + elsif run("Test should pass on logic low inputs when not enabled") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(stat); + check_en_1 <= '0'; + apply_sequence("01", clk, check_in_1); + check_en_1 <= '1'; + wait until rising_edge(clk); + check_en_1 <= 'L'; + apply_sequence("01", clk, check_in_1); + check_en_1 <= 'H'; + wait until rising_edge(clk); + check_en_1 <= 'X'; + apply_sequence("01", clk, check_in_1); + check_en_1 <= '1'; + wait until rising_edge(clk); + wait for 1 ns; + verify_passed_checks(stat, 3); + verify_failed_checks(stat, 0); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 2 us); + +end test_fixture; diff --git a/vunit/vhdl/check/test/tb_check_equal_real.vhd b/vunit/vhdl/check/test/tb_check_equal_real.vhd index 7d12f0809..73229e340 100644 --- a/vunit/vhdl/check/test/tb_check_equal_real.vhd +++ b/vunit/vhdl/check/test/tb_check_equal_real.vhd @@ -1,70 +1,70 @@ --- This test suite verifies the check_equal checker. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -library vunit_lib; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use work.test_support.all; - -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; - -entity tb_check_equal_real is - generic ( - runner_cfg : string); -end entity; - -architecture tb of tb_check_equal_real is -begin - main : process - variable my_checker : checker_t := new_checker("my_checker"); - variable my_logger : logger_t := get_logger(my_checker); - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test default checker") then - mock(check_logger); - check_equal(0.0, 0.0); - check_only_log(check_logger, "Equality check passed - Got abs (0.0 - 0.0) <= 0.0.", pass); - - check_equal(0.0, 1.0, max_diff => 1.0); - check_only_log(check_logger, "Equality check passed - Got abs (0.0 - 1.0) <= 1.0.", pass); - - check_equal(0.0, 1.0); - check_only_log(check_logger, "Equality check failed - Got abs (0.0 - 1.0) > 0.0.", error); - - check_equal(0.0, 1.0, max_diff => 0.9); - check_only_log(check_logger, "Equality check failed - Got abs (0.0 - 1.0) > " & real'image(0.9) & ".", error); - unmock(check_logger); - - elsif run("Test custom checker") then - mock(my_logger); - check_equal(my_checker, 0.0, 0.0); - check_only_log(my_logger, "Equality check passed - Got abs (0.0 - 0.0) <= 0.0.", pass); - - check_equal(my_checker, 0.0, 1.0, max_diff => 1.0); - check_only_log(my_logger, "Equality check passed - Got abs (0.0 - 1.0) <= 1.0.", pass); - - check_equal(my_checker, 0.0, 1.0); - check_only_log(my_logger, "Equality check failed - Got abs (0.0 - 1.0) > 0.0.", error); - - check_equal(my_checker, 0.0, 1.0, max_diff => 0.9); - check_only_log(my_logger, "Equality check failed - Got abs (0.0 - 1.0) > " & real'image(0.9) & ".", error); - unmock(my_logger); - end if; - end loop; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This test suite verifies the check_equal checker. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +library vunit_lib; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use work.test_support.all; + +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; + +entity tb_check_equal_real is + generic ( + runner_cfg : string); +end entity; + +architecture tb of tb_check_equal_real is +begin + main : process + variable my_checker : checker_t := new_checker("my_checker"); + variable my_logger : logger_t := get_logger(my_checker); + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test default checker") then + mock(check_logger); + check_equal(0.0, 0.0); + check_only_log(check_logger, "Equality check passed - Got abs (0.0 - 0.0) <= 0.0.", pass); + + check_equal(0.0, 1.0, max_diff => 1.0); + check_only_log(check_logger, "Equality check passed - Got abs (0.0 - 1.0) <= 1.0.", pass); + + check_equal(0.0, 1.0); + check_only_log(check_logger, "Equality check failed - Got abs (0.0 - 1.0) > 0.0.", error); + + check_equal(0.0, 1.0, max_diff => 0.9); + check_only_log(check_logger, "Equality check failed - Got abs (0.0 - 1.0) > " & real'image(0.9) & ".", error); + unmock(check_logger); + + elsif run("Test custom checker") then + mock(my_logger); + check_equal(my_checker, 0.0, 0.0); + check_only_log(my_logger, "Equality check passed - Got abs (0.0 - 0.0) <= 0.0.", pass); + + check_equal(my_checker, 0.0, 1.0, max_diff => 1.0); + check_only_log(my_logger, "Equality check passed - Got abs (0.0 - 1.0) <= 1.0.", pass); + + check_equal(my_checker, 0.0, 1.0); + check_only_log(my_logger, "Equality check failed - Got abs (0.0 - 1.0) > 0.0.", error); + + check_equal(my_checker, 0.0, 1.0, max_diff => 0.9); + check_only_log(my_logger, "Equality check failed - Got abs (0.0 - 1.0) > " & real'image(0.9) & ".", error); + unmock(my_logger); + end if; + end loop; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/vhdl/check/test/tb_check_failed.vhd b/vunit/vhdl/check/test/tb_check_failed.vhd index 06cdd39f6..ea216f8f6 100644 --- a/vunit/vhdl/check/test/tb_check_failed.vhd +++ b/vunit/vhdl/check/test/tb_check_failed.vhd @@ -1,77 +1,77 @@ --- This test suite verifies the check checker. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library ieee; -use ieee.std_logic_1164.all; -library vunit_lib; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use work.test_support.all; -use ieee.numeric_std.all; - -entity tb_check_failed is - generic ( - runner_cfg : string); -end entity tb_check_failed; - -architecture test_fixture of tb_check_failed is -begin - test_runner : process - variable my_checker : checker_t := new_checker("my_checker"); - variable my_logger : logger_t := get_logger(my_checker); - variable stat : checker_stat_t; - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test that default checker check_failed always fails") then - get_checker_stat(stat); - - mock(check_logger); - check_failed; - check_only_log(check_logger, "Unconditional check failed.", error); - - check_failed(""); - check_only_log(check_logger, "", error); - - check_failed("Checking my data."); - check_only_log(check_logger, "Checking my data.", error); - - check_failed(result("for my data.")); - check_only_log(check_logger, "Unconditional check failed for my data.", error); - unmock(check_logger); - - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 4); - reset_checker_stat; - - elsif run("Test that custom checker check_failed always fails") then - get_checker_stat(my_checker, stat); - - mock(my_logger); - check_failed(my_checker); - check_only_log(my_logger, "Unconditional check failed.", error); - unmock(my_logger); - - verify_passed_checks(my_checker, stat, 0); - verify_failed_checks(my_checker, stat, 1); - reset_checker_stat(my_checker); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - -end test_fixture; +-- This test suite verifies the check checker. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library ieee; +use ieee.std_logic_1164.all; +library vunit_lib; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use work.test_support.all; +use ieee.numeric_std.all; + +entity tb_check_failed is + generic ( + runner_cfg : string); +end entity tb_check_failed; + +architecture test_fixture of tb_check_failed is +begin + test_runner : process + variable my_checker : checker_t := new_checker("my_checker"); + variable my_logger : logger_t := get_logger(my_checker); + variable stat : checker_stat_t; + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test that default checker check_failed always fails") then + get_checker_stat(stat); + + mock(check_logger); + check_failed; + check_only_log(check_logger, "Unconditional check failed.", error); + + check_failed(""); + check_only_log(check_logger, "", error); + + check_failed("Checking my data."); + check_only_log(check_logger, "Checking my data.", error); + + check_failed(result("for my data.")); + check_only_log(check_logger, "Unconditional check failed for my data.", error); + unmock(check_logger); + + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 4); + reset_checker_stat; + + elsif run("Test that custom checker check_failed always fails") then + get_checker_stat(my_checker, stat); + + mock(my_logger); + check_failed(my_checker); + check_only_log(my_logger, "Unconditional check failed.", error); + unmock(my_logger); + + verify_passed_checks(my_checker, stat, 0); + verify_failed_checks(my_checker, stat, 1); + reset_checker_stat(my_checker); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + +end test_fixture; diff --git a/vunit/vhdl/check/test/tb_check_false.vhd b/vunit/vhdl/check/test/tb_check_false.vhd index 08066dc33..be16340ab 100644 --- a/vunit/vhdl/check/test/tb_check_false.vhd +++ b/vunit/vhdl/check/test/tb_check_false.vhd @@ -1,225 +1,225 @@ --- This test suite verifies the check_false checker. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library ieee; -use ieee.std_logic_1164.all; -library vunit_lib; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use vunit_lib.runner_pkg.all; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use work.test_support.all; - -entity tb_check_false is - generic ( - runner_cfg : string); -end entity tb_check_false; - -architecture test_fixture of tb_check_false is - signal clk : std_logic := '0'; - signal check_false_in_1, check_false_in_2, check_false_in_3, check_false_in_4 : std_logic := '0'; - signal check_false_en_1, check_false_en_2, check_false_en_3, check_false_en_4 : std_logic := '1'; - - constant check_false_checker : checker_t := new_checker("checker1"); - constant check_false_checker2 : checker_t := new_checker("checker2"); - constant check_false_checker3 : checker_t := new_checker("checker3", default_log_level => info); - constant check_false_checker4 : checker_t := new_checker("checker4"); - - constant default_level : log_level_t := error; -begin - clock: process is - begin - while get_phase(runner_state) < test_runner_exit loop - clk <= '1', '0' after 5 ns; - wait for 10 ns; - end loop; - wait; - end process clock; - - check_false_1 : check_false(clk, check_false_en_1, check_false_in_1); - check_false_2 : check_false(check_false_checker2, clk, check_false_en_2, check_false_in_2, active_clock_edge => falling_edge); - check_false_3 : check_false(check_false_checker3, clk, check_false_en_3, check_false_in_3); - check_false_4 : check_false(check_false_checker4, clk, check_false_en_4, check_false_in_4); - - check_false_runner : process - variable passed : boolean; - variable stat : checker_stat_t; - - procedure test_concurrent_check ( - signal clk : in std_logic; - signal check_input : out std_logic; - checker : checker_t ; - constant level : in log_level_t := error; - constant active_rising_clock_edge : in boolean := true) is - begin - -- Verify that one log is generated on high and that that log is - -- generated on the correct clock edge. No log on low. - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - get_checker_stat(checker, stat); - apply_sequence("1", clk, check_input, active_rising_clock_edge); - wait until clock_edge(clk, not active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 0); - mock(get_logger(checker)); - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - check_only_log(get_logger(checker), "False check failed.", level); - unmock(get_logger(checker)); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 1); - apply_sequence("0", clk, check_input, active_rising_clock_edge); - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 1); - verify_failed_checks(checker, stat, 1); - reset_checker_stat(checker); - end procedure test_concurrent_check; - - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test should fail on true and logic 1 inputs to sequential checks") then - get_checker_stat(stat); - mock(check_logger); - check_false(true); - check_only_log(check_logger, "False check failed.", default_level); - - check_false(true, ""); - check_only_log(check_logger, "", default_level); - - check_false(passed, true, "Checking my data.", default_level); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Checking my data.", default_level); - - passed := check_false(true, result("for my data."), default_level); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "False check failed for my data.", default_level); - unmock(check_logger); - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 4); - reset_checker_stat; - - get_checker_stat(check_false_checker, stat); - mock(get_logger(check_false_checker)); - check_false(check_false_checker, true); - check_only_log(get_logger(check_false_checker), "False check failed.", default_level); - - check_false(check_false_checker, passed, true, result("for my data.")); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(check_false_checker), "False check failed for my data.", default_level); - - passed := check_false(check_false_checker, true, result("for my data."), default_level); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(check_false_checker), "False check failed for my data.", default_level); - unmock(get_logger(check_false_checker)); - verify_passed_checks(check_false_checker,stat, 0); - verify_failed_checks(check_false_checker,stat, 3); - reset_checker_stat(check_false_checker); - - elsif run("Test should pass on false and logic 0 inputs to sequential checks") then - get_checker_stat(stat); - check_false(false); - check_false(passed, false); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_false(false); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(stat, 3); - - get_checker_stat(check_false_checker, stat); - check_false(check_false_checker, false); - check_false(check_false_checker, passed, false); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_false(check_false_checker, false); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(stat, 3); - verify_passed_checks(check_false_checker, stat, 3); - - elsif run("Test pass message") then - mock(check_logger); - check_false(false); - check_only_log(check_logger, "False check passed.", pass); - - check_false(false, ""); - check_only_log(check_logger, "", pass); - - check_false(false, "Checking my data."); - check_only_log(check_logger, "Checking my data.", pass); - - check_false(false, result("for my data.")); - check_only_log(check_logger, "False check passed for my data.", pass); - unmock(check_logger); - - elsif run("Test should be possible to use concurrently") then - test_concurrent_check(clk, check_false_in_1, default_checker); - elsif run("Test should be possible to use concurrently with negative active clock edge") then - test_concurrent_check(clk, check_false_in_2, check_false_checker2, error, false); - elsif run("Test should be possible to use concurrently with custom checker") then - test_concurrent_check(clk, check_false_in_3, check_false_checker3, info); - elsif run("Test should pass on weak low but fail on other meta values") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(check_false_checker4, stat); - apply_sequence("0L0", clk, check_false_in_4); - wait until rising_edge(clk); - wait for 1 ns; - mock(get_logger(check_false_checker4)); - verify_passed_checks(check_false_checker4, stat, 3); - verify_failed_checks(check_false_checker4, stat, 0); - apply_sequence("0UXZWH-0", clk, check_false_in_4); - wait until rising_edge(clk); - wait for 1 ns; - check_log(get_logger(check_false_checker4), "False check passed.", pass); - check_log(get_logger(check_false_checker4), "False check failed.", default_level); - check_log(get_logger(check_false_checker4), "False check failed.", default_level); - check_log(get_logger(check_false_checker4), "False check failed.", default_level); - check_log(get_logger(check_false_checker4), "False check failed.", default_level); - check_log(get_logger(check_false_checker4), "False check failed.", default_level); - check_log(get_logger(check_false_checker4), "False check failed.", default_level); - check_log(get_logger(check_false_checker4), "False check passed.", pass); - unmock(get_logger(check_false_checker4)); - verify_passed_checks(check_false_checker4, stat, 5); - verify_failed_checks(check_false_checker4, stat, 6); - reset_checker_stat(check_false_checker4); - - elsif run("Test should pass on logic high inputs when not enabled") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(stat); - check_false_en_1 <= '0'; - apply_sequence("10", clk, check_false_in_1); - check_false_en_1 <= '1'; - wait until rising_edge(clk); - check_false_en_1 <= 'L'; - apply_sequence("10", clk, check_false_in_1); - check_false_en_1 <= 'H'; - wait until rising_edge(clk); - check_false_en_1 <= 'X'; - apply_sequence("10", clk, check_false_in_1); - check_false_en_1 <= '1'; - wait until rising_edge(clk); - wait for 1 ns; - verify_passed_checks(stat, 3); - verify_failed_checks(stat, 0); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 2 us); - -end test_fixture; +-- This test suite verifies the check_false checker. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library ieee; +use ieee.std_logic_1164.all; +library vunit_lib; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use vunit_lib.runner_pkg.all; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use work.test_support.all; + +entity tb_check_false is + generic ( + runner_cfg : string); +end entity tb_check_false; + +architecture test_fixture of tb_check_false is + signal clk : std_logic := '0'; + signal check_false_in_1, check_false_in_2, check_false_in_3, check_false_in_4 : std_logic := '0'; + signal check_false_en_1, check_false_en_2, check_false_en_3, check_false_en_4 : std_logic := '1'; + + constant check_false_checker : checker_t := new_checker("checker1"); + constant check_false_checker2 : checker_t := new_checker("checker2"); + constant check_false_checker3 : checker_t := new_checker("checker3", default_log_level => info); + constant check_false_checker4 : checker_t := new_checker("checker4"); + + constant default_level : log_level_t := error; +begin + clock: process is + begin + while get_phase(runner_state) < test_runner_exit loop + clk <= '1', '0' after 5 ns; + wait for 10 ns; + end loop; + wait; + end process clock; + + check_false_1 : check_false(clk, check_false_en_1, check_false_in_1); + check_false_2 : check_false(check_false_checker2, clk, check_false_en_2, check_false_in_2, active_clock_edge => falling_edge); + check_false_3 : check_false(check_false_checker3, clk, check_false_en_3, check_false_in_3); + check_false_4 : check_false(check_false_checker4, clk, check_false_en_4, check_false_in_4); + + check_false_runner : process + variable passed : boolean; + variable stat : checker_stat_t; + + procedure test_concurrent_check ( + signal clk : in std_logic; + signal check_input : out std_logic; + checker : checker_t ; + constant level : in log_level_t := error; + constant active_rising_clock_edge : in boolean := true) is + begin + -- Verify that one log is generated on high and that that log is + -- generated on the correct clock edge. No log on low. + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + get_checker_stat(checker, stat); + apply_sequence("1", clk, check_input, active_rising_clock_edge); + wait until clock_edge(clk, not active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 0); + mock(get_logger(checker)); + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + check_only_log(get_logger(checker), "False check failed.", level); + unmock(get_logger(checker)); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 1); + apply_sequence("0", clk, check_input, active_rising_clock_edge); + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 1); + verify_failed_checks(checker, stat, 1); + reset_checker_stat(checker); + end procedure test_concurrent_check; + + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test should fail on true and logic 1 inputs to sequential checks") then + get_checker_stat(stat); + mock(check_logger); + check_false(true); + check_only_log(check_logger, "False check failed.", default_level); + + check_false(true, ""); + check_only_log(check_logger, "", default_level); + + check_false(passed, true, "Checking my data.", default_level); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Checking my data.", default_level); + + passed := check_false(true, result("for my data."), default_level); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "False check failed for my data.", default_level); + unmock(check_logger); + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 4); + reset_checker_stat; + + get_checker_stat(check_false_checker, stat); + mock(get_logger(check_false_checker)); + check_false(check_false_checker, true); + check_only_log(get_logger(check_false_checker), "False check failed.", default_level); + + check_false(check_false_checker, passed, true, result("for my data.")); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(check_false_checker), "False check failed for my data.", default_level); + + passed := check_false(check_false_checker, true, result("for my data."), default_level); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(check_false_checker), "False check failed for my data.", default_level); + unmock(get_logger(check_false_checker)); + verify_passed_checks(check_false_checker,stat, 0); + verify_failed_checks(check_false_checker,stat, 3); + reset_checker_stat(check_false_checker); + + elsif run("Test should pass on false and logic 0 inputs to sequential checks") then + get_checker_stat(stat); + check_false(false); + check_false(passed, false); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_false(false); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(stat, 3); + + get_checker_stat(check_false_checker, stat); + check_false(check_false_checker, false); + check_false(check_false_checker, passed, false); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_false(check_false_checker, false); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(stat, 3); + verify_passed_checks(check_false_checker, stat, 3); + + elsif run("Test pass message") then + mock(check_logger); + check_false(false); + check_only_log(check_logger, "False check passed.", pass); + + check_false(false, ""); + check_only_log(check_logger, "", pass); + + check_false(false, "Checking my data."); + check_only_log(check_logger, "Checking my data.", pass); + + check_false(false, result("for my data.")); + check_only_log(check_logger, "False check passed for my data.", pass); + unmock(check_logger); + + elsif run("Test should be possible to use concurrently") then + test_concurrent_check(clk, check_false_in_1, default_checker); + elsif run("Test should be possible to use concurrently with negative active clock edge") then + test_concurrent_check(clk, check_false_in_2, check_false_checker2, error, false); + elsif run("Test should be possible to use concurrently with custom checker") then + test_concurrent_check(clk, check_false_in_3, check_false_checker3, info); + elsif run("Test should pass on weak low but fail on other meta values") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(check_false_checker4, stat); + apply_sequence("0L0", clk, check_false_in_4); + wait until rising_edge(clk); + wait for 1 ns; + mock(get_logger(check_false_checker4)); + verify_passed_checks(check_false_checker4, stat, 3); + verify_failed_checks(check_false_checker4, stat, 0); + apply_sequence("0UXZWH-0", clk, check_false_in_4); + wait until rising_edge(clk); + wait for 1 ns; + check_log(get_logger(check_false_checker4), "False check passed.", pass); + check_log(get_logger(check_false_checker4), "False check failed.", default_level); + check_log(get_logger(check_false_checker4), "False check failed.", default_level); + check_log(get_logger(check_false_checker4), "False check failed.", default_level); + check_log(get_logger(check_false_checker4), "False check failed.", default_level); + check_log(get_logger(check_false_checker4), "False check failed.", default_level); + check_log(get_logger(check_false_checker4), "False check failed.", default_level); + check_log(get_logger(check_false_checker4), "False check passed.", pass); + unmock(get_logger(check_false_checker4)); + verify_passed_checks(check_false_checker4, stat, 5); + verify_failed_checks(check_false_checker4, stat, 6); + reset_checker_stat(check_false_checker4); + + elsif run("Test should pass on logic high inputs when not enabled") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(stat); + check_false_en_1 <= '0'; + apply_sequence("10", clk, check_false_in_1); + check_false_en_1 <= '1'; + wait until rising_edge(clk); + check_false_en_1 <= 'L'; + apply_sequence("10", clk, check_false_in_1); + check_false_en_1 <= 'H'; + wait until rising_edge(clk); + check_false_en_1 <= 'X'; + apply_sequence("10", clk, check_false_in_1); + check_false_en_1 <= '1'; + wait until rising_edge(clk); + wait for 1 ns; + verify_passed_checks(stat, 3); + verify_failed_checks(stat, 0); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 2 us); + +end test_fixture; diff --git a/vunit/vhdl/check/test/tb_check_implication.vhd b/vunit/vhdl/check/test/tb_check_implication.vhd index d907a5de3..d605fa178 100644 --- a/vunit/vhdl/check/test/tb_check_implication.vhd +++ b/vunit/vhdl/check/test/tb_check_implication.vhd @@ -1,244 +1,244 @@ --- This test suite verifies the check_implication checker. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library ieee; -use ieee.std_logic_1164.all; -library vunit_lib; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use vunit_lib.runner_pkg.all; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use work.test_support.all; - -entity tb_check_implication is - generic ( - runner_cfg : string); -end entity tb_check_implication; - -architecture test_fixture of tb_check_implication is - signal clk : std_logic := '0'; - - signal check_implication_in_1, - check_implication_in_2, - check_implication_in_3, - check_implication_in_4 : std_logic_vector(1 to 2) := "00"; - alias antecedent_1 : std_logic is check_implication_in_1(1); - alias consequent_1 : std_logic is check_implication_in_1(2); - alias antecedent_2 : std_logic is check_implication_in_2(1); - alias consequent_2 : std_logic is check_implication_in_2(2); - alias antecedent_3 : std_logic is check_implication_in_3(1); - alias consequent_3 : std_logic is check_implication_in_3(2); - alias antecedent_4 : std_logic is check_implication_in_4(1); - alias consequent_4 : std_logic is check_implication_in_4(2); - signal check_implication_en_1, check_implication_en_2, check_implication_en_3, check_implication_en_4 : std_logic := '1'; - - constant my_checker : checker_t := new_checker("my_checker1"); - constant my_checker2 : checker_t := new_checker("my_checker2"); - constant my_checker3 : checker_t := new_checker("my_checker3", default_log_level => info); - constant my_checker4 : checker_t := new_checker("my_checker4"); - -begin - clock: process is - begin - while get_phase(runner_state) < test_runner_exit loop - clk <= '1', '0' after 5 ns; - wait for 10 ns; - end loop; - wait; - end process clock; - - check_implication_1 : check_implication(clk, check_implication_en_1, antecedent_1, consequent_1); - check_implication_2 : check_implication(my_checker2, clk, check_implication_en_2, antecedent_2, consequent_2, active_clock_edge => falling_edge); - check_implication_3 : check_implication(my_checker3, clk, check_implication_en_3, antecedent_3, consequent_3); - check_implication_4 : check_implication(my_checker4, clk, check_implication_en_4, - antecedent_4, consequent_4, result("between x and y.")); - - check_implication_runner : process - variable passed : boolean; - type boolean_vector is array (natural range <>) of boolean; - constant test_antecedents : boolean_vector(1 to 4) := (false, false, true, true); - constant test_consequents : boolean_vector(1 to 4) := (false, true, false, true); - constant test_implication_expected_result : boolean_vector(1 to 4) := (true, true, false, true); - variable stat : checker_stat_t; - constant default_level : log_level_t := error; - - procedure test_concurrent_check ( - signal clk : in std_logic; - signal check_input : out std_logic_vector; - checker : checker_t; - constant level : in log_level_t := error; - constant active_rising_clock_edge : in boolean := true) is - begin - -- Verify all combinations of antecedent/consequent inputs - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - get_checker_stat(checker, stat); - apply_sequence("000110", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 2); - verify_failed_checks(checker, stat, 0); - mock(get_logger(checker)); - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - check_only_log(get_logger(checker), "Implication check failed.", level); - unmock(get_logger(checker)); - verify_passed_checks(checker, stat, 2); - verify_failed_checks(checker, stat, 1); - apply_sequence("11", clk, check_input, active_rising_clock_edge); - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 3); - verify_failed_checks(checker, stat, 1); - reset_checker_stat(checker); - end procedure test_concurrent_check; - - procedure verify_result ( - constant iteration : in natural; - checker : checker_t; - variable stat : inout checker_stat_t) is - begin - if test_implication_expected_result(iteration) then - verify_passed_checks(checker, stat, 1); - verify_failed_checks(checker, stat, 0); - check_only_log(get_logger(checker), - "Implication check passed. - Got " & - boolean'image(test_antecedents(iteration)) &" -> " & boolean'image(test_consequents(iteration)) & - ".", pass); - else - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 1); - check_only_log(get_logger(checker), "Implication check failed.", default_level); - end if; - reset_checker_stat(checker); - - end verify_result; - - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test sequential checkers should fail on true implies false but pass on other inputs") then - for i in test_antecedents'range loop - mock(check_logger); - get_checker_stat(stat); - check_implication(test_antecedents(i), test_consequents(i)); - verify_result(i, default_checker, stat); - unmock(check_logger); - - mock(get_logger(my_checker)); - get_checker_stat(my_checker, stat); - check_implication(my_checker, test_antecedents(i), test_consequents(i)); - verify_result(i, my_checker, stat); - unmock(get_logger(my_checker)); - - mock(check_logger); - get_checker_stat(stat); - check_implication(passed, test_antecedents(i), test_consequents(i)); - verify_result(i, default_checker, stat); - unmock(check_logger); - - mock(check_logger); - get_checker_stat(stat); - passed := check_implication(test_antecedents(i), test_consequents(i)); - verify_result(i, default_checker, stat); - unmock(check_logger); - - mock(get_logger(my_checker)); - get_checker_stat(my_checker, stat); - check_implication(my_checker, passed, test_antecedents(i), test_consequents(i)); - verify_result(i, my_checker, stat); - unmock(get_logger(my_checker)); - - mock(get_logger(my_checker)); - get_checker_stat(my_checker, stat); - passed := check_implication(my_checker, test_antecedents(i), test_consequents(i)); - verify_result(i, my_checker, stat); - unmock(get_logger(my_checker)); - end loop; - - elsif run("Test should be possible to use concurrently") then - test_concurrent_check(clk, check_implication_in_1, default_checker); - - elsif run("Test should be possible to use concurrently with negative active clock edge") then - test_concurrent_check(clk, check_implication_in_2, my_checker2, error, false); - - elsif run("Test should be possible to use concurrently with custom checker") then - test_concurrent_check(clk, check_implication_in_3, my_checker3, info); - - elsif run("Test should handle weak known meta values as known values and others as unknowns") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(my_checker4, stat); - apply_sequence("000XLXH1HH00", clk, check_implication_in_4); - wait for 1 ns; - verify_passed_checks(my_checker4, stat, 5); - verify_failed_checks(my_checker4, stat, 0); - mock(get_logger(my_checker4)); - apply_sequence("00HL00", clk, check_implication_in_4); - wait until rising_edge(clk); - wait for 1 ns; - check_log(get_logger(my_checker4), "Implication check passed between x and y. - Got false -> false.", pass); - check_log(get_logger(my_checker4), "Implication check failed between x and y.", default_level); - check_log(get_logger(my_checker4), "Implication check passed between x and y. - Got false -> false.", pass); - check_no_log; - verify_passed_checks(my_checker4, stat, 7); - verify_failed_checks(my_checker4, stat, 1); - apply_sequence("00H000", clk, check_implication_in_4); - wait until rising_edge(clk); - wait for 1 ns; - check_log(get_logger(my_checker4), "Implication check passed between x and y. - Got false -> false.", pass); - check_log(get_logger(my_checker4), "Implication check failed between x and y.", default_level); - check_log(get_logger(my_checker4), "Implication check passed between x and y. - Got false -> false.", pass); - check_no_log; - verify_passed_checks(my_checker4, stat, 9); - verify_failed_checks(my_checker4, stat, 2); - apply_sequence("00HX00", clk, check_implication_in_4); - wait until rising_edge(clk); - wait for 1 ns; - check_log(get_logger(my_checker4), "Implication check passed between x and y. - Got false -> false.", pass); - check_log(get_logger(my_checker4), "Implication check failed between x and y.", default_level); - check_log(get_logger(my_checker4), "Implication check passed between x and y. - Got false -> false.", pass); - unmock(get_logger(my_checker4)); - verify_passed_checks(my_checker4, stat, 11); - verify_failed_checks(my_checker4, stat, 3); - reset_checker_stat(my_checker4); - - elsif run("Test should pass on true implies false when not enabled") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(stat); - check_implication_en_1 <= '0'; - apply_sequence("1000", clk, check_implication_in_1); - check_implication_en_1 <= '1'; - wait until rising_edge(clk); - check_implication_en_1 <= 'L'; - apply_sequence("1000", clk, check_implication_in_1); - check_implication_en_1 <= 'H'; - wait until rising_edge(clk); - check_implication_en_1 <= 'X'; - apply_sequence("1000", clk, check_implication_in_1); - check_implication_en_1 <= '1'; - wait until rising_edge(clk); - wait for 1 ns; - verify_passed_checks(stat, 3); - verify_failed_checks(stat, 0); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 2 us); - -end test_fixture; +-- This test suite verifies the check_implication checker. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library ieee; +use ieee.std_logic_1164.all; +library vunit_lib; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use vunit_lib.runner_pkg.all; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use work.test_support.all; + +entity tb_check_implication is + generic ( + runner_cfg : string); +end entity tb_check_implication; + +architecture test_fixture of tb_check_implication is + signal clk : std_logic := '0'; + + signal check_implication_in_1, + check_implication_in_2, + check_implication_in_3, + check_implication_in_4 : std_logic_vector(1 to 2) := "00"; + alias antecedent_1 : std_logic is check_implication_in_1(1); + alias consequent_1 : std_logic is check_implication_in_1(2); + alias antecedent_2 : std_logic is check_implication_in_2(1); + alias consequent_2 : std_logic is check_implication_in_2(2); + alias antecedent_3 : std_logic is check_implication_in_3(1); + alias consequent_3 : std_logic is check_implication_in_3(2); + alias antecedent_4 : std_logic is check_implication_in_4(1); + alias consequent_4 : std_logic is check_implication_in_4(2); + signal check_implication_en_1, check_implication_en_2, check_implication_en_3, check_implication_en_4 : std_logic := '1'; + + constant my_checker : checker_t := new_checker("my_checker1"); + constant my_checker2 : checker_t := new_checker("my_checker2"); + constant my_checker3 : checker_t := new_checker("my_checker3", default_log_level => info); + constant my_checker4 : checker_t := new_checker("my_checker4"); + +begin + clock: process is + begin + while get_phase(runner_state) < test_runner_exit loop + clk <= '1', '0' after 5 ns; + wait for 10 ns; + end loop; + wait; + end process clock; + + check_implication_1 : check_implication(clk, check_implication_en_1, antecedent_1, consequent_1); + check_implication_2 : check_implication(my_checker2, clk, check_implication_en_2, antecedent_2, consequent_2, active_clock_edge => falling_edge); + check_implication_3 : check_implication(my_checker3, clk, check_implication_en_3, antecedent_3, consequent_3); + check_implication_4 : check_implication(my_checker4, clk, check_implication_en_4, + antecedent_4, consequent_4, result("between x and y.")); + + check_implication_runner : process + variable passed : boolean; + type boolean_vector is array (natural range <>) of boolean; + constant test_antecedents : boolean_vector(1 to 4) := (false, false, true, true); + constant test_consequents : boolean_vector(1 to 4) := (false, true, false, true); + constant test_implication_expected_result : boolean_vector(1 to 4) := (true, true, false, true); + variable stat : checker_stat_t; + constant default_level : log_level_t := error; + + procedure test_concurrent_check ( + signal clk : in std_logic; + signal check_input : out std_logic_vector; + checker : checker_t; + constant level : in log_level_t := error; + constant active_rising_clock_edge : in boolean := true) is + begin + -- Verify all combinations of antecedent/consequent inputs + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + get_checker_stat(checker, stat); + apply_sequence("000110", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 2); + verify_failed_checks(checker, stat, 0); + mock(get_logger(checker)); + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + check_only_log(get_logger(checker), "Implication check failed.", level); + unmock(get_logger(checker)); + verify_passed_checks(checker, stat, 2); + verify_failed_checks(checker, stat, 1); + apply_sequence("11", clk, check_input, active_rising_clock_edge); + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 3); + verify_failed_checks(checker, stat, 1); + reset_checker_stat(checker); + end procedure test_concurrent_check; + + procedure verify_result ( + constant iteration : in natural; + checker : checker_t; + variable stat : inout checker_stat_t) is + begin + if test_implication_expected_result(iteration) then + verify_passed_checks(checker, stat, 1); + verify_failed_checks(checker, stat, 0); + check_only_log(get_logger(checker), + "Implication check passed. - Got " & + boolean'image(test_antecedents(iteration)) &" -> " & boolean'image(test_consequents(iteration)) & + ".", pass); + else + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 1); + check_only_log(get_logger(checker), "Implication check failed.", default_level); + end if; + reset_checker_stat(checker); + + end verify_result; + + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test sequential checkers should fail on true implies false but pass on other inputs") then + for i in test_antecedents'range loop + mock(check_logger); + get_checker_stat(stat); + check_implication(test_antecedents(i), test_consequents(i)); + verify_result(i, default_checker, stat); + unmock(check_logger); + + mock(get_logger(my_checker)); + get_checker_stat(my_checker, stat); + check_implication(my_checker, test_antecedents(i), test_consequents(i)); + verify_result(i, my_checker, stat); + unmock(get_logger(my_checker)); + + mock(check_logger); + get_checker_stat(stat); + check_implication(passed, test_antecedents(i), test_consequents(i)); + verify_result(i, default_checker, stat); + unmock(check_logger); + + mock(check_logger); + get_checker_stat(stat); + passed := check_implication(test_antecedents(i), test_consequents(i)); + verify_result(i, default_checker, stat); + unmock(check_logger); + + mock(get_logger(my_checker)); + get_checker_stat(my_checker, stat); + check_implication(my_checker, passed, test_antecedents(i), test_consequents(i)); + verify_result(i, my_checker, stat); + unmock(get_logger(my_checker)); + + mock(get_logger(my_checker)); + get_checker_stat(my_checker, stat); + passed := check_implication(my_checker, test_antecedents(i), test_consequents(i)); + verify_result(i, my_checker, stat); + unmock(get_logger(my_checker)); + end loop; + + elsif run("Test should be possible to use concurrently") then + test_concurrent_check(clk, check_implication_in_1, default_checker); + + elsif run("Test should be possible to use concurrently with negative active clock edge") then + test_concurrent_check(clk, check_implication_in_2, my_checker2, error, false); + + elsif run("Test should be possible to use concurrently with custom checker") then + test_concurrent_check(clk, check_implication_in_3, my_checker3, info); + + elsif run("Test should handle weak known meta values as known values and others as unknowns") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(my_checker4, stat); + apply_sequence("000XLXH1HH00", clk, check_implication_in_4); + wait for 1 ns; + verify_passed_checks(my_checker4, stat, 5); + verify_failed_checks(my_checker4, stat, 0); + mock(get_logger(my_checker4)); + apply_sequence("00HL00", clk, check_implication_in_4); + wait until rising_edge(clk); + wait for 1 ns; + check_log(get_logger(my_checker4), "Implication check passed between x and y. - Got false -> false.", pass); + check_log(get_logger(my_checker4), "Implication check failed between x and y.", default_level); + check_log(get_logger(my_checker4), "Implication check passed between x and y. - Got false -> false.", pass); + check_no_log; + verify_passed_checks(my_checker4, stat, 7); + verify_failed_checks(my_checker4, stat, 1); + apply_sequence("00H000", clk, check_implication_in_4); + wait until rising_edge(clk); + wait for 1 ns; + check_log(get_logger(my_checker4), "Implication check passed between x and y. - Got false -> false.", pass); + check_log(get_logger(my_checker4), "Implication check failed between x and y.", default_level); + check_log(get_logger(my_checker4), "Implication check passed between x and y. - Got false -> false.", pass); + check_no_log; + verify_passed_checks(my_checker4, stat, 9); + verify_failed_checks(my_checker4, stat, 2); + apply_sequence("00HX00", clk, check_implication_in_4); + wait until rising_edge(clk); + wait for 1 ns; + check_log(get_logger(my_checker4), "Implication check passed between x and y. - Got false -> false.", pass); + check_log(get_logger(my_checker4), "Implication check failed between x and y.", default_level); + check_log(get_logger(my_checker4), "Implication check passed between x and y. - Got false -> false.", pass); + unmock(get_logger(my_checker4)); + verify_passed_checks(my_checker4, stat, 11); + verify_failed_checks(my_checker4, stat, 3); + reset_checker_stat(my_checker4); + + elsif run("Test should pass on true implies false when not enabled") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(stat); + check_implication_en_1 <= '0'; + apply_sequence("1000", clk, check_implication_in_1); + check_implication_en_1 <= '1'; + wait until rising_edge(clk); + check_implication_en_1 <= 'L'; + apply_sequence("1000", clk, check_implication_in_1); + check_implication_en_1 <= 'H'; + wait until rising_edge(clk); + check_implication_en_1 <= 'X'; + apply_sequence("1000", clk, check_implication_in_1); + check_implication_en_1 <= '1'; + wait until rising_edge(clk); + wait for 1 ns; + verify_passed_checks(stat, 3); + verify_failed_checks(stat, 0); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 2 us); + +end test_fixture; diff --git a/vunit/vhdl/check/test/tb_check_next.vhd b/vunit/vhdl/check/test/tb_check_next.vhd index 8aa99101a..8b019167e 100644 --- a/vunit/vhdl/check/test/tb_check_next.vhd +++ b/vunit/vhdl/check/test/tb_check_next.vhd @@ -1,307 +1,307 @@ --- This test suite verifies the check_next checker. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library ieee; -use ieee.std_logic_1164.all; -library vunit_lib; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use vunit_lib.runner_pkg.all; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use work.test_support.all; - -entity tb_check_next is - generic ( - runner_cfg : string); -end entity tb_check_next; - -architecture test_fixture of tb_check_next is - signal clk : std_logic := '0'; - - signal check_next_in_1, check_next_in_2, check_next_in_3, check_next_in_4, - check_next_in_5, check_next_in_6, check_next_in_7, check_next_in_8 : std_logic_vector(1 to 3) := "001"; - alias check_next_start_event_1 : std_logic is check_next_in_1(1); - alias check_next_expr_1 : std_logic is check_next_in_1(2); - alias check_next_en_1 : std_logic is check_next_in_1(3); - alias check_next_start_event_2 : std_logic is check_next_in_2(1); - alias check_next_expr_2 : std_logic is check_next_in_2(2); - alias check_next_en_2 : std_logic is check_next_in_2(3); - alias check_next_start_event_3 : std_logic is check_next_in_3(1); - alias check_next_expr_3 : std_logic is check_next_in_3(2); - alias check_next_en_3 : std_logic is check_next_in_3(3); - alias check_next_start_event_4 : std_logic is check_next_in_4(1); - alias check_next_expr_4 : std_logic is check_next_in_4(2); - alias check_next_en_4 : std_logic is check_next_in_4(3); - alias check_next_start_event_5 : std_logic is check_next_in_5(1); - alias check_next_expr_5 : std_logic is check_next_in_5(2); - alias check_next_en_5 : std_logic is check_next_in_5(3); - alias check_next_start_event_6 : std_logic is check_next_in_6(1); - alias check_next_expr_6 : std_logic is check_next_in_6(2); - alias check_next_en_6 : std_logic is check_next_in_6(3); - alias check_next_start_event_7 : std_logic is check_next_in_7(1); - alias check_next_expr_7 : std_logic is check_next_in_7(2); - alias check_next_en_7 : std_logic is check_next_in_7(3); - alias check_next_start_event_8 : std_logic is check_next_in_8(1); - alias check_next_expr_8 : std_logic is check_next_in_8(2); - alias check_next_en_8 : std_logic is check_next_in_8(3); - - constant my_checker2 : checker_t := new_checker("my_checker2"); - constant my_checker3 : checker_t := new_checker("my_checker3", default_log_level => info); - constant my_checker4 : checker_t := new_checker("my_checker4"); - constant my_checker5 : checker_t := new_checker("my_checker5"); -begin - clock: process is - begin - while get_phase(runner_state) < test_runner_exit loop - clk <= '1', '0' after 5 ns; - wait for 10 ns; - end loop; - wait; - end process clock; - - check_next_1 : check_next(clk, - check_next_en_1, - check_next_start_event_1, - check_next_expr_1, - num_cks => 4); - - check_next_2 : check_next(my_checker2, - clk, - check_next_en_2, - check_next_start_event_2, - check_next_expr_2, - active_clock_edge => falling_edge, - num_cks => 4); - check_next_3 : check_next(my_checker3, - clk, - check_next_en_3, - check_next_start_event_3, - check_next_expr_3, - num_cks => 4); - check_next_4 : check_next(my_checker4, - clk, - check_next_en_4, - check_next_start_event_4, - check_next_expr_4, - result("for my data"), - num_cks => 4, - allow_overlapping => false); - check_next_5 : check_next(my_checker5, - clk, - check_next_en_5, - check_next_start_event_5, - check_next_expr_5, - "Checking my data", - num_cks => 4, - allow_missing_start => false); - check_next_6 : check_next(clk, - check_next_en_6, - check_next_start_event_6, - check_next_expr_6, - result("for my data"), - num_cks => 1); - - check_next_7 : check_next(clk, - check_next_en_7, - check_next_start_event_7, - check_next_expr_7, - num_cks => 0); - - check_next_8 : check_next(clk, - check_next_en_8, - check_next_start_event_8, - check_next_expr_8, - result("for my data"), - num_cks => 0, - allow_missing_start => false); - - check_next_runner : process - variable stat : checker_stat_t; - constant default_level : log_level_t := error; - - procedure test_concurrent_check ( - signal clk : in std_logic; - signal check_input : out std_logic_vector; - checker : checker_t; - constant level : in log_level_t := error; - constant active_rising_clock_edge : in boolean := true) is - - begin - if enabled("Test should pass when expr is true num_cks enabled cycles after start_event") then - get_checker_stat(checker, stat); - apply_sequence("001;101;001;001;000;001;011;001", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 1); - - elsif enabled("Test should fail when expr is false num_cks enabled cycles after start_event") then - mock(check_logger); - apply_sequence("001;111;011;011;010;011;001;011;001", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(check_logger, "Next check failed - Got 0 at the 4th active and enabled clock edge.", level); - unmock(check_logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 1); - reset_checker_stat; - - elsif enabled("Test should handle a mix of passing and failing overlapping checks when allowed") then - get_checker_stat(checker, stat); - apply_sequence("001;101;001;101;101;000;011;001", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 1); - verify_failed_checks(checker, stat, 0); - apply_sequence("001;011;001", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 2); - verify_failed_checks(checker, stat, 0); - apply_sequence("001;001", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(check_logger, "Next check failed - Got 0 at the 4th active and enabled clock edge.", level); - verify_passed_checks(checker, stat, 2); - verify_failed_checks(checker, stat, 1); - reset_checker_stat(checker); - - elsif enabled("Test should pass a true expr without start_event if missing start is allowed") then - get_checker_stat(checker, stat); - apply_sequence("001;001;001;011;001", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 0); - end if; - - end procedure test_concurrent_check; - - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test should pass when expr is true num_cks enabled cycles after start_event") or - run("Test should fail when expr is false num_cks enabled cycles after start_event") or - run("Test should handle a mix of passing and failing overlapping checks when allowed") or - run("Test should pass a true expr without start_event if missing start is allowed") then - - test_concurrent_check(clk, check_next_in_1, default_checker); - test_concurrent_check(clk, check_next_in_2, my_checker2, error, false); - test_concurrent_check(clk, check_next_in_3, my_checker3, level => info); - elsif run("Test should pass when expr is true num_cks=0 enabled cycles after start_event") then - get_checker_stat(stat); - apply_sequence("001;111;001;001;100;001;111;111;001", clk, check_next_in_7); - wait for 1 ns; - verify_passed_checks(stat, 3); - verify_failed_checks(stat, 0); - elsif run("Test should fail when expr is false num_cks=0 enabled cycles after start_event") then - get_checker_stat(stat); - mock(check_logger); - apply_sequence("001;101;001", clk, check_next_in_7); - wait for 1 ns; - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 1); - check_only_log(check_logger, "Next check failed - Got 0 at the 0th active and enabled clock edge.", default_level); - unmock(check_logger); - elsif run("Test should pass a true expr without start_event if missing start is allowed and num_cks=0") then - get_checker_stat(stat); - apply_sequence("001;011;001", clk, check_next_in_7); - wait for 1 ns; - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 0); - elsif run("Test should fail when an overlapping check is initiated when not allowed") then - get_checker_stat(my_checker4, stat); - mock(get_logger(my_checker4)); - apply_sequence("001;101;001;101;001;011;001;011;001", clk, check_next_in_4); - wait until rising_edge(clk); - wait for 1 ns; - verify_passed_checks(my_checker4, stat, 1); - verify_failed_checks(my_checker4, stat, 1); - check_log(get_logger(my_checker4), "Next check failed for my data - Got overlapping start event at the 2nd active and enabled clock edge.", default_level); - check_log(get_logger(my_checker4), "Next check passed for my data", pass); - unmock(get_logger(my_checker4)); - apply_sequence("001;101;001;001;001;111;001;001;001;011;001", clk, check_next_in_4); - wait until rising_edge(clk); - wait for 1 ns; - verify_passed_checks(my_checker4, stat, 3); - verify_failed_checks(my_checker4, stat, 1); - reset_checker_stat(my_checker4); - elsif run("Test should fail a true expr without start event if missing start is not allowed") then - get_checker_stat(my_checker5, stat); - mock(get_logger(my_checker5)); - apply_sequence("001;001;001;011;001", clk, check_next_in_5); - wait until rising_edge(clk); - wait for 1 ns; - verify_passed_checks(my_checker5, stat, 0); - verify_failed_checks(my_checker5, stat, 1); - reset_checker_stat(my_checker5); - check_only_log(get_logger(my_checker5), "Checking my data - Missing start event for true expression.", default_level); - unmock(get_logger(my_checker5)); - elsif run("Test should fail a true expr without start event if missing start is not allowed and num_cks=0") then - get_checker_stat(stat); - mock(check_logger); - apply_sequence("001;011;001", clk, check_next_in_8); - wait for 1 ns; - verify_passed_checks(stat, 0); - check_only_log(check_logger, "Next check failed for my data - Missing start event for true expression.", default_level); - unmock(check_logger); - elsif run("Test should handle meta values") then - get_checker_stat(my_checker5, stat); - apply_sequence("00H;10H;00H;00H;00L;00H;01H;00H;00H", clk, check_next_in_5); - wait until rising_edge(clk); - wait for 1 ns; - verify_passed_checks(my_checker5, stat, 1); - verify_failed_checks(my_checker5, stat, 0); - - get_checker_stat(my_checker5, stat); - apply_sequence("0LH;1LH;0LH;0LH;0LL;0LH;0HH;0LH;0LH", clk, check_next_in_5); - wait for 1 ns; - verify_passed_checks(my_checker5, stat, 1); - verify_failed_checks(my_checker5, stat, 0); - - get_checker_stat(my_checker5, stat); - apply_sequence("LLH;HLH;LLH;LLH;LLL;LLH;LHH;LLH;LLH", clk, check_next_in_5); - wait for 1 ns; - verify_passed_checks(my_checker5, stat, 1); - verify_failed_checks(my_checker5, stat, 0); - - get_checker_stat(my_checker5, stat); - mock(get_logger(my_checker5)); - apply_sequence("XLH;HLH;LLH;LLH;LXX;LLH;LHH;LLH;LLH", clk, check_next_in_5); - wait for 1 ns; - verify_passed_checks(my_checker5, stat, 1); - verify_failed_checks(my_checker5, stat, 1); - reset_checker_stat(my_checker5); - check_log(get_logger(my_checker5), "Checking my data - Start event is X.", default_level); - check_log(get_logger(my_checker5), "Checking my data", pass); - unmock(get_logger(my_checker5)); - - elsif run("Test pass message and that internal checks don't count") then - get_checker_stat(stat); - mock(check_logger); - apply_sequence("001;101;0U1;011", clk, check_next_in_6); - wait for 1 ns; - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 1); - check_only_log(check_logger, "Next check failed for my data - Got U at the 1st active and enabled clock edge.", default_level); - apply_sequence("011;101;011;001", clk, check_next_in_6); - wait for 1 ns; - check_only_log(check_logger, "Next check passed for my data", pass); - unmock(check_logger); - verify_passed_checks(stat, 1); - verify_failed_checks(stat, 1); - reset_checker_stat; - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 2 us); - -end test_fixture; +-- This test suite verifies the check_next checker. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library ieee; +use ieee.std_logic_1164.all; +library vunit_lib; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use vunit_lib.runner_pkg.all; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use work.test_support.all; + +entity tb_check_next is + generic ( + runner_cfg : string); +end entity tb_check_next; + +architecture test_fixture of tb_check_next is + signal clk : std_logic := '0'; + + signal check_next_in_1, check_next_in_2, check_next_in_3, check_next_in_4, + check_next_in_5, check_next_in_6, check_next_in_7, check_next_in_8 : std_logic_vector(1 to 3) := "001"; + alias check_next_start_event_1 : std_logic is check_next_in_1(1); + alias check_next_expr_1 : std_logic is check_next_in_1(2); + alias check_next_en_1 : std_logic is check_next_in_1(3); + alias check_next_start_event_2 : std_logic is check_next_in_2(1); + alias check_next_expr_2 : std_logic is check_next_in_2(2); + alias check_next_en_2 : std_logic is check_next_in_2(3); + alias check_next_start_event_3 : std_logic is check_next_in_3(1); + alias check_next_expr_3 : std_logic is check_next_in_3(2); + alias check_next_en_3 : std_logic is check_next_in_3(3); + alias check_next_start_event_4 : std_logic is check_next_in_4(1); + alias check_next_expr_4 : std_logic is check_next_in_4(2); + alias check_next_en_4 : std_logic is check_next_in_4(3); + alias check_next_start_event_5 : std_logic is check_next_in_5(1); + alias check_next_expr_5 : std_logic is check_next_in_5(2); + alias check_next_en_5 : std_logic is check_next_in_5(3); + alias check_next_start_event_6 : std_logic is check_next_in_6(1); + alias check_next_expr_6 : std_logic is check_next_in_6(2); + alias check_next_en_6 : std_logic is check_next_in_6(3); + alias check_next_start_event_7 : std_logic is check_next_in_7(1); + alias check_next_expr_7 : std_logic is check_next_in_7(2); + alias check_next_en_7 : std_logic is check_next_in_7(3); + alias check_next_start_event_8 : std_logic is check_next_in_8(1); + alias check_next_expr_8 : std_logic is check_next_in_8(2); + alias check_next_en_8 : std_logic is check_next_in_8(3); + + constant my_checker2 : checker_t := new_checker("my_checker2"); + constant my_checker3 : checker_t := new_checker("my_checker3", default_log_level => info); + constant my_checker4 : checker_t := new_checker("my_checker4"); + constant my_checker5 : checker_t := new_checker("my_checker5"); +begin + clock: process is + begin + while get_phase(runner_state) < test_runner_exit loop + clk <= '1', '0' after 5 ns; + wait for 10 ns; + end loop; + wait; + end process clock; + + check_next_1 : check_next(clk, + check_next_en_1, + check_next_start_event_1, + check_next_expr_1, + num_cks => 4); + + check_next_2 : check_next(my_checker2, + clk, + check_next_en_2, + check_next_start_event_2, + check_next_expr_2, + active_clock_edge => falling_edge, + num_cks => 4); + check_next_3 : check_next(my_checker3, + clk, + check_next_en_3, + check_next_start_event_3, + check_next_expr_3, + num_cks => 4); + check_next_4 : check_next(my_checker4, + clk, + check_next_en_4, + check_next_start_event_4, + check_next_expr_4, + result("for my data"), + num_cks => 4, + allow_overlapping => false); + check_next_5 : check_next(my_checker5, + clk, + check_next_en_5, + check_next_start_event_5, + check_next_expr_5, + "Checking my data", + num_cks => 4, + allow_missing_start => false); + check_next_6 : check_next(clk, + check_next_en_6, + check_next_start_event_6, + check_next_expr_6, + result("for my data"), + num_cks => 1); + + check_next_7 : check_next(clk, + check_next_en_7, + check_next_start_event_7, + check_next_expr_7, + num_cks => 0); + + check_next_8 : check_next(clk, + check_next_en_8, + check_next_start_event_8, + check_next_expr_8, + result("for my data"), + num_cks => 0, + allow_missing_start => false); + + check_next_runner : process + variable stat : checker_stat_t; + constant default_level : log_level_t := error; + + procedure test_concurrent_check ( + signal clk : in std_logic; + signal check_input : out std_logic_vector; + checker : checker_t; + constant level : in log_level_t := error; + constant active_rising_clock_edge : in boolean := true) is + + begin + if enabled("Test should pass when expr is true num_cks enabled cycles after start_event") then + get_checker_stat(checker, stat); + apply_sequence("001;101;001;001;000;001;011;001", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 1); + + elsif enabled("Test should fail when expr is false num_cks enabled cycles after start_event") then + mock(check_logger); + apply_sequence("001;111;011;011;010;011;001;011;001", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(check_logger, "Next check failed - Got 0 at the 4th active and enabled clock edge.", level); + unmock(check_logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 1); + reset_checker_stat; + + elsif enabled("Test should handle a mix of passing and failing overlapping checks when allowed") then + get_checker_stat(checker, stat); + apply_sequence("001;101;001;101;101;000;011;001", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 1); + verify_failed_checks(checker, stat, 0); + apply_sequence("001;011;001", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 2); + verify_failed_checks(checker, stat, 0); + apply_sequence("001;001", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(check_logger, "Next check failed - Got 0 at the 4th active and enabled clock edge.", level); + verify_passed_checks(checker, stat, 2); + verify_failed_checks(checker, stat, 1); + reset_checker_stat(checker); + + elsif enabled("Test should pass a true expr without start_event if missing start is allowed") then + get_checker_stat(checker, stat); + apply_sequence("001;001;001;011;001", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 0); + end if; + + end procedure test_concurrent_check; + + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test should pass when expr is true num_cks enabled cycles after start_event") or + run("Test should fail when expr is false num_cks enabled cycles after start_event") or + run("Test should handle a mix of passing and failing overlapping checks when allowed") or + run("Test should pass a true expr without start_event if missing start is allowed") then + + test_concurrent_check(clk, check_next_in_1, default_checker); + test_concurrent_check(clk, check_next_in_2, my_checker2, error, false); + test_concurrent_check(clk, check_next_in_3, my_checker3, level => info); + elsif run("Test should pass when expr is true num_cks=0 enabled cycles after start_event") then + get_checker_stat(stat); + apply_sequence("001;111;001;001;100;001;111;111;001", clk, check_next_in_7); + wait for 1 ns; + verify_passed_checks(stat, 3); + verify_failed_checks(stat, 0); + elsif run("Test should fail when expr is false num_cks=0 enabled cycles after start_event") then + get_checker_stat(stat); + mock(check_logger); + apply_sequence("001;101;001", clk, check_next_in_7); + wait for 1 ns; + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 1); + check_only_log(check_logger, "Next check failed - Got 0 at the 0th active and enabled clock edge.", default_level); + unmock(check_logger); + elsif run("Test should pass a true expr without start_event if missing start is allowed and num_cks=0") then + get_checker_stat(stat); + apply_sequence("001;011;001", clk, check_next_in_7); + wait for 1 ns; + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 0); + elsif run("Test should fail when an overlapping check is initiated when not allowed") then + get_checker_stat(my_checker4, stat); + mock(get_logger(my_checker4)); + apply_sequence("001;101;001;101;001;011;001;011;001", clk, check_next_in_4); + wait until rising_edge(clk); + wait for 1 ns; + verify_passed_checks(my_checker4, stat, 1); + verify_failed_checks(my_checker4, stat, 1); + check_log(get_logger(my_checker4), "Next check failed for my data - Got overlapping start event at the 2nd active and enabled clock edge.", default_level); + check_log(get_logger(my_checker4), "Next check passed for my data", pass); + unmock(get_logger(my_checker4)); + apply_sequence("001;101;001;001;001;111;001;001;001;011;001", clk, check_next_in_4); + wait until rising_edge(clk); + wait for 1 ns; + verify_passed_checks(my_checker4, stat, 3); + verify_failed_checks(my_checker4, stat, 1); + reset_checker_stat(my_checker4); + elsif run("Test should fail a true expr without start event if missing start is not allowed") then + get_checker_stat(my_checker5, stat); + mock(get_logger(my_checker5)); + apply_sequence("001;001;001;011;001", clk, check_next_in_5); + wait until rising_edge(clk); + wait for 1 ns; + verify_passed_checks(my_checker5, stat, 0); + verify_failed_checks(my_checker5, stat, 1); + reset_checker_stat(my_checker5); + check_only_log(get_logger(my_checker5), "Checking my data - Missing start event for true expression.", default_level); + unmock(get_logger(my_checker5)); + elsif run("Test should fail a true expr without start event if missing start is not allowed and num_cks=0") then + get_checker_stat(stat); + mock(check_logger); + apply_sequence("001;011;001", clk, check_next_in_8); + wait for 1 ns; + verify_passed_checks(stat, 0); + check_only_log(check_logger, "Next check failed for my data - Missing start event for true expression.", default_level); + unmock(check_logger); + elsif run("Test should handle meta values") then + get_checker_stat(my_checker5, stat); + apply_sequence("00H;10H;00H;00H;00L;00H;01H;00H;00H", clk, check_next_in_5); + wait until rising_edge(clk); + wait for 1 ns; + verify_passed_checks(my_checker5, stat, 1); + verify_failed_checks(my_checker5, stat, 0); + + get_checker_stat(my_checker5, stat); + apply_sequence("0LH;1LH;0LH;0LH;0LL;0LH;0HH;0LH;0LH", clk, check_next_in_5); + wait for 1 ns; + verify_passed_checks(my_checker5, stat, 1); + verify_failed_checks(my_checker5, stat, 0); + + get_checker_stat(my_checker5, stat); + apply_sequence("LLH;HLH;LLH;LLH;LLL;LLH;LHH;LLH;LLH", clk, check_next_in_5); + wait for 1 ns; + verify_passed_checks(my_checker5, stat, 1); + verify_failed_checks(my_checker5, stat, 0); + + get_checker_stat(my_checker5, stat); + mock(get_logger(my_checker5)); + apply_sequence("XLH;HLH;LLH;LLH;LXX;LLH;LHH;LLH;LLH", clk, check_next_in_5); + wait for 1 ns; + verify_passed_checks(my_checker5, stat, 1); + verify_failed_checks(my_checker5, stat, 1); + reset_checker_stat(my_checker5); + check_log(get_logger(my_checker5), "Checking my data - Start event is X.", default_level); + check_log(get_logger(my_checker5), "Checking my data", pass); + unmock(get_logger(my_checker5)); + + elsif run("Test pass message and that internal checks don't count") then + get_checker_stat(stat); + mock(check_logger); + apply_sequence("001;101;0U1;011", clk, check_next_in_6); + wait for 1 ns; + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 1); + check_only_log(check_logger, "Next check failed for my data - Got U at the 1st active and enabled clock edge.", default_level); + apply_sequence("011;101;011;001", clk, check_next_in_6); + wait for 1 ns; + check_only_log(check_logger, "Next check passed for my data", pass); + unmock(check_logger); + verify_passed_checks(stat, 1); + verify_failed_checks(stat, 1); + reset_checker_stat; + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 2 us); + +end test_fixture; diff --git a/vunit/vhdl/check/test/tb_check_not_unknown.vhd b/vunit/vhdl/check/test/tb_check_not_unknown.vhd index 8187a6686..c14f1e184 100644 --- a/vunit/vhdl/check/test/tb_check_not_unknown.vhd +++ b/vunit/vhdl/check/test/tb_check_not_unknown.vhd @@ -1,282 +1,282 @@ --- This test suite verifies the check_not_unknown checker. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library ieee; -use ieee.std_logic_1164.all; -library vunit_lib; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use vunit_lib.runner_pkg.all; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use vunit_lib.string_ops.all; -use work.test_support.all; - -entity tb_check_not_unknown is - generic ( - runner_cfg : string); -end entity tb_check_not_unknown; - -architecture test_fixture of tb_check_not_unknown is - signal clk : std_logic := '0'; - signal check_not_unknown_in_1, check_not_unknown_in_2, check_not_unknown_in_3 : std_logic_vector(8 downto 0) := (others => '0'); - signal check_not_unknown_in_4, check_not_unknown_in_5, check_not_unknown_in_6 : std_logic_vector(1 downto 0) := (others => '0'); - alias expr_1 : std_logic_vector(7 downto 0) is check_not_unknown_in_1(8 downto 1); - alias expr_2 : std_logic_vector(7 downto 0) is check_not_unknown_in_2(8 downto 1); - alias expr_3 : std_logic_vector(7 downto 0) is check_not_unknown_in_3(8 downto 1); - alias expr_4 : std_logic is check_not_unknown_in_4(1); - alias expr_5 : std_logic is check_not_unknown_in_5(1); - alias expr_6 : std_logic is check_not_unknown_in_6(1); - alias en_1 : std_logic is check_not_unknown_in_1(0); - alias en_2 : std_logic is check_not_unknown_in_2(0); - alias en_3 : std_logic is check_not_unknown_in_3(0); - alias en_4 : std_logic is check_not_unknown_in_4(0); - alias en_5 : std_logic is check_not_unknown_in_5(0); - alias en_6 : std_logic is check_not_unknown_in_6(0); - - constant my_checker : checker_t := new_checker("my_checker1"); - constant my_checker2 : checker_t := new_checker("my_checker2"); - constant my_checker3 : checker_t := new_checker("my_checker3", default_log_level => info); - constant my_checker4 : checker_t := new_checker("my_checker4"); - constant my_checker5 : checker_t := new_checker("my_checker5"); - constant my_checker6 : checker_t := new_checker("my_checker6", default_log_level => info); - - -begin - clock: process is - begin - while get_phase(runner_state) < test_runner_exit loop - clk <= '1', '0' after 5 ns; - wait for 10 ns; - end loop; - wait; - end process clock; - - check_not_unknown_1 : check_not_unknown(clk, en_1, expr_1); - check_not_unknown_2 : check_not_unknown(my_checker2, clk, en_2, expr_2, active_clock_edge => falling_edge); - check_not_unknown_3 : check_not_unknown(my_checker3, clk, en_3, expr_3); - check_not_unknown_4 : check_not_unknown(clk, en_4, expr_4); - check_not_unknown_5 : check_not_unknown(my_checker5, clk, en_5, expr_5, active_clock_edge => falling_edge); - check_not_unknown_6 : check_not_unknown(my_checker6, clk, en_6, expr_6); - - check_not_unknown_runner : process - variable passed : boolean; - variable stat : checker_stat_t; - variable test_expr : std_logic_vector(7 downto 0); - constant metadata : std_logic_vector(1 to 5) := "UXZW-"; - constant not_unknowns : string(1 to 4) := "01LH"; - constant reversed_and_offset_expr : std_logic_vector(23 downto 16) := "10100101"; - constant default_level : log_level_t := error; - - procedure test_concurrent_check ( - signal clk : in std_logic; - signal check_input : out std_logic_vector; - checker : checker_t ; - constant level : in log_level_t := error; - constant active_rising_clock_edge : in boolean := true) is - variable test_expr : string(1 to check_input'length - 1); - variable test_expr_slv : std_logic_vector(0 to test_expr'length - 1); - begin - -- Forced and weak zeros and ones should pass regardless of en - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - get_checker_stat(checker, stat); - for i in 1 to 4 loop - test_expr := (others => not_unknowns(i)); - apply_sequence(test_expr & "0" & test_expr & "1", clk, check_input, active_rising_clock_edge); - wait until clock_edge(clk, active_rising_clock_edge); - end loop; - wait for 1 ns; - verify_passed_checks(checker, stat, 4); - verify_failed_checks(checker, stat, 0); - - -- Values other than strong/weak zeros/ones should fail when en is high, - -- pass otherwise - for i in metadata'range loop - get_checker_stat(checker, stat); - test_expr_slv := (others => metadata(i)); - test_expr := (others => std_logic'image(metadata(i))(2)); - apply_sequence(test_expr & "0" & test_expr & "L", clk, check_input, active_rising_clock_edge); - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 0); - - mock(get_logger(checker)); - apply_sequence(test_expr & "1" & test_expr & "H", clk, check_input, active_rising_clock_edge); - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - - for k in 0 to 1 loop - check_log(get_logger(checker), - "Not unknown check failed - Got " & to_nibble_string(test_expr_slv) & ".", - level); - end loop; - unmock(get_logger(checker)); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 2); - reset_checker_stat(checker); - end loop; - - test_expr := (others => '0'); - apply_sequence(test_expr & "0", clk, check_input, active_rising_clock_edge); - end procedure test_concurrent_check; - - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test should pass on zeros and ones") then - get_checker_stat(stat); - check_not_unknown('0'); - check_not_unknown('1'); - check_not_unknown("10100101"); - check_not_unknown(passed, "10100101"); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_not_unknown('0'); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_not_unknown('1'); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_not_unknown("10100101"); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(stat, 7); - - get_checker_stat(my_checker3, stat); - check_not_unknown(my_checker3, '0'); - check_not_unknown(my_checker3, '1'); - check_not_unknown(my_checker3, "10100101"); - check_not_unknown(my_checker3, passed, "10100101"); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_not_unknown(my_checker3, '0'); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_not_unknown(my_checker3, '1'); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_not_unknown(my_checker3, "10100101"); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(my_checker3, stat, 7); - - elsif run("Test pass message") then - mock(check_logger); - check_not_unknown('0'); - check_only_log(check_logger, "Not unknown check passed - Got 0.", pass); - - check_not_unknown("00110"); - check_only_log(check_logger, "Not unknown check passed - Got 0_0110 (6).", pass); - - check_not_unknown('0', ""); - check_only_log(check_logger, "Got 0.", pass); - - check_not_unknown("00110", ""); - check_only_log(check_logger, "Got 0_0110 (6).", pass); - - check_not_unknown('0', "Checking my data"); - check_only_log(check_logger, "Checking my data - Got 0.", pass); - - check_not_unknown("00110", "Checking my data"); - check_only_log(check_logger, "Checking my data - Got 0_0110 (6).", pass); - - check_not_unknown('0', result("for my data")); - check_only_log(check_logger, "Not unknown check passed for my data - Got 0.", pass); - - check_not_unknown("00110", result("for my data")); - check_only_log(check_logger, "Not unknown check passed for my data - Got 0_0110 (6).", pass); - unmock(check_logger); - - elsif run("Test should fail on all std logic values except zero and one") then - for i in metadata'range loop - get_checker_stat(stat); - test_expr := (others => metadata(i)); - mock(check_logger); - check_not_unknown(metadata(i)); - check_only_log(check_logger, "Not unknown check failed - Got " & std_logic'image(metadata(i))(2) & ".", default_level); - - check_not_unknown(test_expr); - check_only_log(check_logger, "Not unknown check failed - Got " & to_nibble_string(test_expr) & ".", default_level); - - check_not_unknown(passed, metadata(i)); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Not unknown check failed - Got " & std_logic'image(metadata(i))(2) & ".", default_level); - - check_not_unknown(passed, test_expr); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Not unknown check failed - Got " & to_nibble_string(test_expr) & ".", default_level); - - passed := check_not_unknown(metadata(i)); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Not unknown check failed - Got " & std_logic'image(metadata(i))(2) & ".", default_level); - - passed := check_not_unknown(test_expr); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Not unknown check failed - Got " & to_nibble_string(test_expr) & ".", default_level); - unmock(check_logger); - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 6); - reset_checker_stat; - - get_checker_stat(my_checker3, stat); - mock(get_logger(my_checker3)); - check_not_unknown(my_checker3, metadata(i)); - check_only_log(get_logger(my_checker3), "Not unknown check failed - Got " & std_logic'image(metadata(i))(2) & ".", - info); - check_not_unknown(my_checker3, test_expr); - check_only_log(get_logger(my_checker3), "Not unknown check failed - Got " & to_nibble_string(test_expr) & ".", - info); - check_not_unknown(my_checker3, passed, metadata(i)); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "Not unknown check failed - Got " & std_logic'image(metadata(i))(2) & ".", - info); - check_not_unknown(my_checker3, passed, test_expr); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "Not unknown check failed - Got " & to_nibble_string(test_expr) & ".", - info); - - passed := check_not_unknown(my_checker3, metadata(i)); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), - "Not unknown check failed - Got " & std_logic'image(metadata(i))(2) & ".", info); - - passed := check_not_unknown(my_checker3, test_expr); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), - "Not unknown check failed - Got " & to_nibble_string(test_expr) & ".", info); - unmock(get_logger(my_checker3)); - verify_passed_checks(my_checker3, stat, 0); - verify_failed_checks(my_checker3, stat, 6); - reset_checker_stat(my_checker3); - end loop; - - elsif run("Test should be possible to use concurrently") then - test_concurrent_check(clk, check_not_unknown_in_1, default_checker); - test_concurrent_check(clk, check_not_unknown_in_4, default_checker); - - elsif run("Test should be possible to use concurrently with negative active clock edge") then - test_concurrent_check(clk, check_not_unknown_in_2, my_checker2, error, false); - test_concurrent_check(clk, check_not_unknown_in_5, my_checker5, error, false); - - elsif run("Test should be possible to use concurrently with custom checker") then - test_concurrent_check(clk, check_not_unknown_in_3, my_checker3, info); - test_concurrent_check(clk, check_not_unknown_in_6, my_checker6, info); - - elsif run("Test should handle reversed and or offset expressions") then - get_checker_stat(stat); - check_not_unknown(reversed_and_offset_expr); - verify_passed_checks(stat, 1); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 2 us); - -end test_fixture; +-- This test suite verifies the check_not_unknown checker. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library ieee; +use ieee.std_logic_1164.all; +library vunit_lib; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use vunit_lib.runner_pkg.all; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.string_ops.all; +use work.test_support.all; + +entity tb_check_not_unknown is + generic ( + runner_cfg : string); +end entity tb_check_not_unknown; + +architecture test_fixture of tb_check_not_unknown is + signal clk : std_logic := '0'; + signal check_not_unknown_in_1, check_not_unknown_in_2, check_not_unknown_in_3 : std_logic_vector(8 downto 0) := (others => '0'); + signal check_not_unknown_in_4, check_not_unknown_in_5, check_not_unknown_in_6 : std_logic_vector(1 downto 0) := (others => '0'); + alias expr_1 : std_logic_vector(7 downto 0) is check_not_unknown_in_1(8 downto 1); + alias expr_2 : std_logic_vector(7 downto 0) is check_not_unknown_in_2(8 downto 1); + alias expr_3 : std_logic_vector(7 downto 0) is check_not_unknown_in_3(8 downto 1); + alias expr_4 : std_logic is check_not_unknown_in_4(1); + alias expr_5 : std_logic is check_not_unknown_in_5(1); + alias expr_6 : std_logic is check_not_unknown_in_6(1); + alias en_1 : std_logic is check_not_unknown_in_1(0); + alias en_2 : std_logic is check_not_unknown_in_2(0); + alias en_3 : std_logic is check_not_unknown_in_3(0); + alias en_4 : std_logic is check_not_unknown_in_4(0); + alias en_5 : std_logic is check_not_unknown_in_5(0); + alias en_6 : std_logic is check_not_unknown_in_6(0); + + constant my_checker : checker_t := new_checker("my_checker1"); + constant my_checker2 : checker_t := new_checker("my_checker2"); + constant my_checker3 : checker_t := new_checker("my_checker3", default_log_level => info); + constant my_checker4 : checker_t := new_checker("my_checker4"); + constant my_checker5 : checker_t := new_checker("my_checker5"); + constant my_checker6 : checker_t := new_checker("my_checker6", default_log_level => info); + + +begin + clock: process is + begin + while get_phase(runner_state) < test_runner_exit loop + clk <= '1', '0' after 5 ns; + wait for 10 ns; + end loop; + wait; + end process clock; + + check_not_unknown_1 : check_not_unknown(clk, en_1, expr_1); + check_not_unknown_2 : check_not_unknown(my_checker2, clk, en_2, expr_2, active_clock_edge => falling_edge); + check_not_unknown_3 : check_not_unknown(my_checker3, clk, en_3, expr_3); + check_not_unknown_4 : check_not_unknown(clk, en_4, expr_4); + check_not_unknown_5 : check_not_unknown(my_checker5, clk, en_5, expr_5, active_clock_edge => falling_edge); + check_not_unknown_6 : check_not_unknown(my_checker6, clk, en_6, expr_6); + + check_not_unknown_runner : process + variable passed : boolean; + variable stat : checker_stat_t; + variable test_expr : std_logic_vector(7 downto 0); + constant metadata : std_logic_vector(1 to 5) := "UXZW-"; + constant not_unknowns : string(1 to 4) := "01LH"; + constant reversed_and_offset_expr : std_logic_vector(23 downto 16) := "10100101"; + constant default_level : log_level_t := error; + + procedure test_concurrent_check ( + signal clk : in std_logic; + signal check_input : out std_logic_vector; + checker : checker_t ; + constant level : in log_level_t := error; + constant active_rising_clock_edge : in boolean := true) is + variable test_expr : string(1 to check_input'length - 1); + variable test_expr_slv : std_logic_vector(0 to test_expr'length - 1); + begin + -- Forced and weak zeros and ones should pass regardless of en + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + get_checker_stat(checker, stat); + for i in 1 to 4 loop + test_expr := (others => not_unknowns(i)); + apply_sequence(test_expr & "0" & test_expr & "1", clk, check_input, active_rising_clock_edge); + wait until clock_edge(clk, active_rising_clock_edge); + end loop; + wait for 1 ns; + verify_passed_checks(checker, stat, 4); + verify_failed_checks(checker, stat, 0); + + -- Values other than strong/weak zeros/ones should fail when en is high, + -- pass otherwise + for i in metadata'range loop + get_checker_stat(checker, stat); + test_expr_slv := (others => metadata(i)); + test_expr := (others => std_logic'image(metadata(i))(2)); + apply_sequence(test_expr & "0" & test_expr & "L", clk, check_input, active_rising_clock_edge); + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 0); + + mock(get_logger(checker)); + apply_sequence(test_expr & "1" & test_expr & "H", clk, check_input, active_rising_clock_edge); + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + + for k in 0 to 1 loop + check_log(get_logger(checker), + "Not unknown check failed - Got " & to_nibble_string(test_expr_slv) & ".", + level); + end loop; + unmock(get_logger(checker)); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 2); + reset_checker_stat(checker); + end loop; + + test_expr := (others => '0'); + apply_sequence(test_expr & "0", clk, check_input, active_rising_clock_edge); + end procedure test_concurrent_check; + + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test should pass on zeros and ones") then + get_checker_stat(stat); + check_not_unknown('0'); + check_not_unknown('1'); + check_not_unknown("10100101"); + check_not_unknown(passed, "10100101"); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_not_unknown('0'); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_not_unknown('1'); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_not_unknown("10100101"); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(stat, 7); + + get_checker_stat(my_checker3, stat); + check_not_unknown(my_checker3, '0'); + check_not_unknown(my_checker3, '1'); + check_not_unknown(my_checker3, "10100101"); + check_not_unknown(my_checker3, passed, "10100101"); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_not_unknown(my_checker3, '0'); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_not_unknown(my_checker3, '1'); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_not_unknown(my_checker3, "10100101"); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(my_checker3, stat, 7); + + elsif run("Test pass message") then + mock(check_logger); + check_not_unknown('0'); + check_only_log(check_logger, "Not unknown check passed - Got 0.", pass); + + check_not_unknown("00110"); + check_only_log(check_logger, "Not unknown check passed - Got 0_0110 (6).", pass); + + check_not_unknown('0', ""); + check_only_log(check_logger, "Got 0.", pass); + + check_not_unknown("00110", ""); + check_only_log(check_logger, "Got 0_0110 (6).", pass); + + check_not_unknown('0', "Checking my data"); + check_only_log(check_logger, "Checking my data - Got 0.", pass); + + check_not_unknown("00110", "Checking my data"); + check_only_log(check_logger, "Checking my data - Got 0_0110 (6).", pass); + + check_not_unknown('0', result("for my data")); + check_only_log(check_logger, "Not unknown check passed for my data - Got 0.", pass); + + check_not_unknown("00110", result("for my data")); + check_only_log(check_logger, "Not unknown check passed for my data - Got 0_0110 (6).", pass); + unmock(check_logger); + + elsif run("Test should fail on all std logic values except zero and one") then + for i in metadata'range loop + get_checker_stat(stat); + test_expr := (others => metadata(i)); + mock(check_logger); + check_not_unknown(metadata(i)); + check_only_log(check_logger, "Not unknown check failed - Got " & std_logic'image(metadata(i))(2) & ".", default_level); + + check_not_unknown(test_expr); + check_only_log(check_logger, "Not unknown check failed - Got " & to_nibble_string(test_expr) & ".", default_level); + + check_not_unknown(passed, metadata(i)); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Not unknown check failed - Got " & std_logic'image(metadata(i))(2) & ".", default_level); + + check_not_unknown(passed, test_expr); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Not unknown check failed - Got " & to_nibble_string(test_expr) & ".", default_level); + + passed := check_not_unknown(metadata(i)); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Not unknown check failed - Got " & std_logic'image(metadata(i))(2) & ".", default_level); + + passed := check_not_unknown(test_expr); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Not unknown check failed - Got " & to_nibble_string(test_expr) & ".", default_level); + unmock(check_logger); + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 6); + reset_checker_stat; + + get_checker_stat(my_checker3, stat); + mock(get_logger(my_checker3)); + check_not_unknown(my_checker3, metadata(i)); + check_only_log(get_logger(my_checker3), "Not unknown check failed - Got " & std_logic'image(metadata(i))(2) & ".", + info); + check_not_unknown(my_checker3, test_expr); + check_only_log(get_logger(my_checker3), "Not unknown check failed - Got " & to_nibble_string(test_expr) & ".", + info); + check_not_unknown(my_checker3, passed, metadata(i)); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "Not unknown check failed - Got " & std_logic'image(metadata(i))(2) & ".", + info); + check_not_unknown(my_checker3, passed, test_expr); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "Not unknown check failed - Got " & to_nibble_string(test_expr) & ".", + info); + + passed := check_not_unknown(my_checker3, metadata(i)); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), + "Not unknown check failed - Got " & std_logic'image(metadata(i))(2) & ".", info); + + passed := check_not_unknown(my_checker3, test_expr); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), + "Not unknown check failed - Got " & to_nibble_string(test_expr) & ".", info); + unmock(get_logger(my_checker3)); + verify_passed_checks(my_checker3, stat, 0); + verify_failed_checks(my_checker3, stat, 6); + reset_checker_stat(my_checker3); + end loop; + + elsif run("Test should be possible to use concurrently") then + test_concurrent_check(clk, check_not_unknown_in_1, default_checker); + test_concurrent_check(clk, check_not_unknown_in_4, default_checker); + + elsif run("Test should be possible to use concurrently with negative active clock edge") then + test_concurrent_check(clk, check_not_unknown_in_2, my_checker2, error, false); + test_concurrent_check(clk, check_not_unknown_in_5, my_checker5, error, false); + + elsif run("Test should be possible to use concurrently with custom checker") then + test_concurrent_check(clk, check_not_unknown_in_3, my_checker3, info); + test_concurrent_check(clk, check_not_unknown_in_6, my_checker6, info); + + elsif run("Test should handle reversed and or offset expressions") then + get_checker_stat(stat); + check_not_unknown(reversed_and_offset_expr); + verify_passed_checks(stat, 1); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 2 us); + +end test_fixture; diff --git a/vunit/vhdl/check/test/tb_check_one_hot.vhd b/vunit/vhdl/check/test/tb_check_one_hot.vhd index a2eb81d94..38f1b83ae 100644 --- a/vunit/vhdl/check/test/tb_check_one_hot.vhd +++ b/vunit/vhdl/check/test/tb_check_one_hot.vhd @@ -1,324 +1,324 @@ --- This test suite verifies the check_one_hot checker. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library ieee; -use ieee.std_logic_1164.all; -library vunit_lib; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use vunit_lib.runner_pkg.all; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use work.test_support.all; - -entity tb_check_one_hot is - generic ( - runner_cfg : string); -end entity tb_check_one_hot; - -architecture test_fixture of tb_check_one_hot is - signal clk : std_logic := '0'; - - signal check_one_hot_in_1, check_one_hot_in_2, check_one_hot_in_3 : std_logic_vector(3 downto 0) := "0001"; - signal check_one_hot_en_1, check_one_hot_en_2, check_one_hot_en_3 : std_logic := '1'; - - constant my_checker2 : checker_t := new_checker("my_checker2"); - constant my_checker3 : checker_t := new_checker("my_checker3", default_log_level => info); - -begin - clock: process is - begin - while get_phase(runner_state) < test_runner_exit loop - clk <= '1', '0' after 5 ns; - wait for 10 ns; - end loop; - wait; - end process clock; - - check_one_hot_1 : check_one_hot(clk, check_one_hot_en_1, check_one_hot_in_1); - check_one_hot_2 : check_one_hot(my_checker2, clk, check_one_hot_en_2, check_one_hot_in_2, active_clock_edge => falling_edge); - check_one_hot_3 : check_one_hot(my_checker3, clk, check_one_hot_en_3, check_one_hot_in_3); - - check_one_hot_runner : process - variable passed : boolean; - variable stat : checker_stat_t; - constant reversed_and_offset_expr : std_logic_vector(23 downto 20) := "1000"; - constant default_level : log_level_t := error; - - procedure test_concurrent_check ( - signal clk : in std_logic; - signal check_input : out std_logic_vector; - checker : checker_t ; - constant level : in log_level_t := error; - constant active_rising_clock_edge : in boolean := true) is - begin - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - get_checker_stat(checker, stat); - apply_sequence("1000;HL00", clk, check_input, active_rising_clock_edge); - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 2); - verify_failed_checks(checker, stat, 0); - mock(get_logger(checker)); - apply_sequence("1001;0000;00LL;100H;000X", clk, check_input, active_rising_clock_edge); - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - check_log(get_logger(checker), "One-hot check failed - Got 1001.", level); - check_log(get_logger(checker), "One-hot check failed - Got 0000.", level); - check_log(get_logger(checker), "One-hot check failed - Got 00LL.", level); - check_log(get_logger(checker), "One-hot check failed - Got 100H.", level); - check_log(get_logger(checker), "One-hot check failed - Got 000X.", level); - unmock(get_logger(checker)); - verify_passed_checks(checker, stat, 2); - verify_failed_checks(checker, stat, 5); - reset_checker_stat(checker); - apply_sequence("1000", clk, check_input, active_rising_clock_edge); - end procedure test_concurrent_check; - - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test should pass on one high bit") then - get_checker_stat(stat); - check_one_hot("1000"); - check_one_hot("HL00"); - verify_passed_checks(stat, 2); - - get_checker_stat(my_checker3, stat); - check_one_hot(my_checker3, "1000"); - check_one_hot(my_checker3, "HL00"); - verify_passed_checks(my_checker3, stat, 2); - - get_checker_stat(stat); - check_one_hot(passed, "1000"); - assert_true(passed, "Should return pass = true on passing check"); - check_one_hot(passed, "HL00"); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(stat, 2); - - get_checker_stat(stat); - passed := check_one_hot("1000"); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_one_hot("HL00"); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(stat, 2); - - get_checker_stat(my_checker3, stat); - check_one_hot(my_checker3, passed, "1000"); - assert_true(passed, "Should return pass = true on passing check"); - check_one_hot(my_checker3, passed, "HL00"); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(my_checker3, stat, 2); - - get_checker_stat(my_checker3, stat); - passed := check_one_hot(my_checker3, "1000"); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_one_hot(my_checker3, "HL00"); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(my_checker3, stat, 2); - - elsif run("Test pass message") then - mock(check_logger); - check_one_hot("10000"); - check_only_log(check_logger, "One-hot check passed - Got 1_0000.", pass); - - check_one_hot("10000", ""); - check_only_log(check_logger, "Got 1_0000.", pass); - - check_one_hot("10000", "Checking my data"); - check_only_log(check_logger, "Checking my data - Got 1_0000.", pass); - - check_one_hot("10000", result("for my data")); - check_only_log(check_logger, "One-hot check passed for my data - Got 1_0000.", pass); - unmock(check_logger); - - elsif run("Test should fail on zero or more than one high bit") then - get_checker_stat(stat); - mock(check_logger); - check_one_hot("00000"); - check_only_log(check_logger, "One-hot check failed - Got 0_0000.", default_level); - - check_one_hot("0L00L"); - check_only_log(check_logger, "One-hot check failed - Got 0_L00L.", default_level); - - check_one_hot("01001"); - check_only_log(check_logger, "One-hot check failed - Got 0_1001.", default_level); - - check_one_hot("0100H"); - check_only_log(check_logger, "One-hot check failed - Got 0_100H.", default_level); - - check_one_hot(passed, "01001"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "One-hot check failed - Got 0_1001.", default_level); - - check_one_hot(passed, "0100H"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "One-hot check failed - Got 0_100H.", default_level); - - passed := check_one_hot("01001"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "One-hot check failed - Got 0_1001.", default_level); - - passed := check_one_hot("0100H"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "One-hot check failed - Got 0_100H.", default_level); - - check_one_hot(passed, "00000"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "One-hot check failed - Got 0_0000.", default_level); - - check_one_hot(passed, "0L00L"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "One-hot check failed - Got 0_L00L.", default_level); - - passed := check_one_hot("00000"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "One-hot check failed - Got 0_0000.", default_level); - - passed := check_one_hot("0L00L"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "One-hot check failed - Got 0_L00L.", default_level); - unmock(check_logger); - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 12); - reset_checker_stat; - - get_checker_stat(my_checker3, stat); - mock(get_logger(my_checker3)); - check_one_hot(my_checker3, "00000"); - check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_0000.", info); - - check_one_hot(my_checker3, "0L00L"); - check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_L00L.", info); - - check_one_hot(my_checker3, "01001"); - check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_1001.", info); - - check_one_hot(my_checker3, "0100H"); - check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_100H.", info); - - check_one_hot(my_checker3, passed, "01001"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_1001.", info); - - check_one_hot(my_checker3, passed, "0100H"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_100H.", info); - - passed := check_one_hot(my_checker3, "01001"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_1001.", info); - - passed := check_one_hot(my_checker3, "0100H"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_100H.", info); - - check_one_hot(my_checker3, passed, "00000"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_0000.", info); - - check_one_hot(my_checker3, passed, "0L00L"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_L00L.", info); - - passed := check_one_hot(my_checker3, "00000"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_0000.", info); - - passed := check_one_hot(my_checker3, "0L00L"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_L00L.", info); - unmock(get_logger(my_checker3)); - verify_passed_checks(my_checker3, stat, 0); - verify_failed_checks(my_checker3, stat, 12); - reset_checker_stat(my_checker3); - - elsif run("Test should fail on unknowns") then - get_checker_stat(stat); - mock(check_logger); - check_one_hot("0000X"); - check_only_log(check_logger, "One-hot check failed - Got 0_000X.", default_level); - - check_one_hot(passed, "0000X"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "One-hot check failed - Got 0_000X.", default_level); - - passed := check_one_hot("0000X"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "One-hot check failed - Got 0_000X.", default_level); - unmock(check_logger); - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 3); - reset_checker_stat; - - get_checker_stat(my_checker3, stat); - mock(get_logger(my_checker3)); - check_one_hot(my_checker3, "0000X"); - check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_000X.", info); - - check_one_hot(my_checker3, passed, "0000X"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_000X.", info); - - passed := check_one_hot(my_checker3, "0000X"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_000X.", info); - unmock(get_logger(my_checker3)); - verify_passed_checks(my_checker3, stat, 0); - verify_failed_checks(my_checker3, stat, 3); - reset_checker_stat(my_checker3); - - elsif run("Test should be possible to use concurrently") then - test_concurrent_check(clk, check_one_hot_in_1, default_checker); - - elsif run("Test should be possible to use concurrently with negative active clock edge") then - test_concurrent_check(clk, check_one_hot_in_2, my_checker2, error, false); - - elsif run("Test should be possible to use concurrently with custom checker") then - test_concurrent_check(clk, check_one_hot_in_3, my_checker3, info); - - elsif run("Test should pass on unknowns when not enabled") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(stat); - apply_sequence("0001;1001", clk, check_one_hot_in_1); - check_one_hot_en_1 <= '0'; - apply_sequence("1001;100H;0001", clk, check_one_hot_in_1); - check_one_hot_en_1 <= '1'; - apply_sequence("0001;100H", clk, check_one_hot_in_1); - check_one_hot_en_1 <= 'L'; - apply_sequence("1001;100H;0001", clk, check_one_hot_in_1); - check_one_hot_en_1 <= 'H'; - apply_sequence("0001;100H", clk, check_one_hot_in_1); - check_one_hot_en_1 <= 'X'; - apply_sequence("1001;100H;0001", clk, check_one_hot_in_1); - check_one_hot_en_1 <= '1'; - wait for 1 ns; - verify_passed_checks(stat, 3); - verify_failed_checks(stat, 0); - - elsif run("Test should handle reversed and or offset expressions") then - get_checker_stat(stat); - check_zero_one_hot(reversed_and_offset_expr); - verify_passed_checks(stat, 1); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 2 us); - -end test_fixture; +-- This test suite verifies the check_one_hot checker. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library ieee; +use ieee.std_logic_1164.all; +library vunit_lib; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use vunit_lib.runner_pkg.all; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use work.test_support.all; + +entity tb_check_one_hot is + generic ( + runner_cfg : string); +end entity tb_check_one_hot; + +architecture test_fixture of tb_check_one_hot is + signal clk : std_logic := '0'; + + signal check_one_hot_in_1, check_one_hot_in_2, check_one_hot_in_3 : std_logic_vector(3 downto 0) := "0001"; + signal check_one_hot_en_1, check_one_hot_en_2, check_one_hot_en_3 : std_logic := '1'; + + constant my_checker2 : checker_t := new_checker("my_checker2"); + constant my_checker3 : checker_t := new_checker("my_checker3", default_log_level => info); + +begin + clock: process is + begin + while get_phase(runner_state) < test_runner_exit loop + clk <= '1', '0' after 5 ns; + wait for 10 ns; + end loop; + wait; + end process clock; + + check_one_hot_1 : check_one_hot(clk, check_one_hot_en_1, check_one_hot_in_1); + check_one_hot_2 : check_one_hot(my_checker2, clk, check_one_hot_en_2, check_one_hot_in_2, active_clock_edge => falling_edge); + check_one_hot_3 : check_one_hot(my_checker3, clk, check_one_hot_en_3, check_one_hot_in_3); + + check_one_hot_runner : process + variable passed : boolean; + variable stat : checker_stat_t; + constant reversed_and_offset_expr : std_logic_vector(23 downto 20) := "1000"; + constant default_level : log_level_t := error; + + procedure test_concurrent_check ( + signal clk : in std_logic; + signal check_input : out std_logic_vector; + checker : checker_t ; + constant level : in log_level_t := error; + constant active_rising_clock_edge : in boolean := true) is + begin + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + get_checker_stat(checker, stat); + apply_sequence("1000;HL00", clk, check_input, active_rising_clock_edge); + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 2); + verify_failed_checks(checker, stat, 0); + mock(get_logger(checker)); + apply_sequence("1001;0000;00LL;100H;000X", clk, check_input, active_rising_clock_edge); + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + check_log(get_logger(checker), "One-hot check failed - Got 1001.", level); + check_log(get_logger(checker), "One-hot check failed - Got 0000.", level); + check_log(get_logger(checker), "One-hot check failed - Got 00LL.", level); + check_log(get_logger(checker), "One-hot check failed - Got 100H.", level); + check_log(get_logger(checker), "One-hot check failed - Got 000X.", level); + unmock(get_logger(checker)); + verify_passed_checks(checker, stat, 2); + verify_failed_checks(checker, stat, 5); + reset_checker_stat(checker); + apply_sequence("1000", clk, check_input, active_rising_clock_edge); + end procedure test_concurrent_check; + + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test should pass on one high bit") then + get_checker_stat(stat); + check_one_hot("1000"); + check_one_hot("HL00"); + verify_passed_checks(stat, 2); + + get_checker_stat(my_checker3, stat); + check_one_hot(my_checker3, "1000"); + check_one_hot(my_checker3, "HL00"); + verify_passed_checks(my_checker3, stat, 2); + + get_checker_stat(stat); + check_one_hot(passed, "1000"); + assert_true(passed, "Should return pass = true on passing check"); + check_one_hot(passed, "HL00"); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(stat, 2); + + get_checker_stat(stat); + passed := check_one_hot("1000"); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_one_hot("HL00"); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(stat, 2); + + get_checker_stat(my_checker3, stat); + check_one_hot(my_checker3, passed, "1000"); + assert_true(passed, "Should return pass = true on passing check"); + check_one_hot(my_checker3, passed, "HL00"); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(my_checker3, stat, 2); + + get_checker_stat(my_checker3, stat); + passed := check_one_hot(my_checker3, "1000"); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_one_hot(my_checker3, "HL00"); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(my_checker3, stat, 2); + + elsif run("Test pass message") then + mock(check_logger); + check_one_hot("10000"); + check_only_log(check_logger, "One-hot check passed - Got 1_0000.", pass); + + check_one_hot("10000", ""); + check_only_log(check_logger, "Got 1_0000.", pass); + + check_one_hot("10000", "Checking my data"); + check_only_log(check_logger, "Checking my data - Got 1_0000.", pass); + + check_one_hot("10000", result("for my data")); + check_only_log(check_logger, "One-hot check passed for my data - Got 1_0000.", pass); + unmock(check_logger); + + elsif run("Test should fail on zero or more than one high bit") then + get_checker_stat(stat); + mock(check_logger); + check_one_hot("00000"); + check_only_log(check_logger, "One-hot check failed - Got 0_0000.", default_level); + + check_one_hot("0L00L"); + check_only_log(check_logger, "One-hot check failed - Got 0_L00L.", default_level); + + check_one_hot("01001"); + check_only_log(check_logger, "One-hot check failed - Got 0_1001.", default_level); + + check_one_hot("0100H"); + check_only_log(check_logger, "One-hot check failed - Got 0_100H.", default_level); + + check_one_hot(passed, "01001"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "One-hot check failed - Got 0_1001.", default_level); + + check_one_hot(passed, "0100H"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "One-hot check failed - Got 0_100H.", default_level); + + passed := check_one_hot("01001"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "One-hot check failed - Got 0_1001.", default_level); + + passed := check_one_hot("0100H"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "One-hot check failed - Got 0_100H.", default_level); + + check_one_hot(passed, "00000"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "One-hot check failed - Got 0_0000.", default_level); + + check_one_hot(passed, "0L00L"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "One-hot check failed - Got 0_L00L.", default_level); + + passed := check_one_hot("00000"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "One-hot check failed - Got 0_0000.", default_level); + + passed := check_one_hot("0L00L"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "One-hot check failed - Got 0_L00L.", default_level); + unmock(check_logger); + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 12); + reset_checker_stat; + + get_checker_stat(my_checker3, stat); + mock(get_logger(my_checker3)); + check_one_hot(my_checker3, "00000"); + check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_0000.", info); + + check_one_hot(my_checker3, "0L00L"); + check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_L00L.", info); + + check_one_hot(my_checker3, "01001"); + check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_1001.", info); + + check_one_hot(my_checker3, "0100H"); + check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_100H.", info); + + check_one_hot(my_checker3, passed, "01001"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_1001.", info); + + check_one_hot(my_checker3, passed, "0100H"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_100H.", info); + + passed := check_one_hot(my_checker3, "01001"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_1001.", info); + + passed := check_one_hot(my_checker3, "0100H"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_100H.", info); + + check_one_hot(my_checker3, passed, "00000"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_0000.", info); + + check_one_hot(my_checker3, passed, "0L00L"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_L00L.", info); + + passed := check_one_hot(my_checker3, "00000"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_0000.", info); + + passed := check_one_hot(my_checker3, "0L00L"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_L00L.", info); + unmock(get_logger(my_checker3)); + verify_passed_checks(my_checker3, stat, 0); + verify_failed_checks(my_checker3, stat, 12); + reset_checker_stat(my_checker3); + + elsif run("Test should fail on unknowns") then + get_checker_stat(stat); + mock(check_logger); + check_one_hot("0000X"); + check_only_log(check_logger, "One-hot check failed - Got 0_000X.", default_level); + + check_one_hot(passed, "0000X"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "One-hot check failed - Got 0_000X.", default_level); + + passed := check_one_hot("0000X"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "One-hot check failed - Got 0_000X.", default_level); + unmock(check_logger); + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 3); + reset_checker_stat; + + get_checker_stat(my_checker3, stat); + mock(get_logger(my_checker3)); + check_one_hot(my_checker3, "0000X"); + check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_000X.", info); + + check_one_hot(my_checker3, passed, "0000X"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_000X.", info); + + passed := check_one_hot(my_checker3, "0000X"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "One-hot check failed - Got 0_000X.", info); + unmock(get_logger(my_checker3)); + verify_passed_checks(my_checker3, stat, 0); + verify_failed_checks(my_checker3, stat, 3); + reset_checker_stat(my_checker3); + + elsif run("Test should be possible to use concurrently") then + test_concurrent_check(clk, check_one_hot_in_1, default_checker); + + elsif run("Test should be possible to use concurrently with negative active clock edge") then + test_concurrent_check(clk, check_one_hot_in_2, my_checker2, error, false); + + elsif run("Test should be possible to use concurrently with custom checker") then + test_concurrent_check(clk, check_one_hot_in_3, my_checker3, info); + + elsif run("Test should pass on unknowns when not enabled") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(stat); + apply_sequence("0001;1001", clk, check_one_hot_in_1); + check_one_hot_en_1 <= '0'; + apply_sequence("1001;100H;0001", clk, check_one_hot_in_1); + check_one_hot_en_1 <= '1'; + apply_sequence("0001;100H", clk, check_one_hot_in_1); + check_one_hot_en_1 <= 'L'; + apply_sequence("1001;100H;0001", clk, check_one_hot_in_1); + check_one_hot_en_1 <= 'H'; + apply_sequence("0001;100H", clk, check_one_hot_in_1); + check_one_hot_en_1 <= 'X'; + apply_sequence("1001;100H;0001", clk, check_one_hot_in_1); + check_one_hot_en_1 <= '1'; + wait for 1 ns; + verify_passed_checks(stat, 3); + verify_failed_checks(stat, 0); + + elsif run("Test should handle reversed and or offset expressions") then + get_checker_stat(stat); + check_zero_one_hot(reversed_and_offset_expr); + verify_passed_checks(stat, 1); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 2 us); + +end test_fixture; diff --git a/vunit/vhdl/check/test/tb_check_passed.vhd b/vunit/vhdl/check/test/tb_check_passed.vhd index 39ac28109..c1cdc4548 100644 --- a/vunit/vhdl/check/test/tb_check_passed.vhd +++ b/vunit/vhdl/check/test/tb_check_passed.vhd @@ -1,70 +1,70 @@ --- This test suite verifies the check checker. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library ieee; -use ieee.std_logic_1164.all; -library vunit_lib; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use work.test_support.all; -use ieee.numeric_std.all; - -entity tb_check_passed is - generic ( - runner_cfg : string); -end entity tb_check_passed; - -architecture test_fixture of tb_check_passed is -begin - test_runner : process - variable my_checker : checker_t := new_checker("my_checker"); - variable stat : checker_stat_t; - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test that default checker check_passed always passes") then - get_checker_stat(stat); - check_passed; - verify_passed_checks(stat, 1); - verify_failed_checks(stat, 0); - - elsif run("Test that custom checker check_passed always passes") then - get_checker_stat(my_checker, stat); - check_passed(my_checker); - verify_passed_checks(my_checker, stat, 1); - verify_failed_checks(my_checker, stat, 0); - - elsif run("Test pass message") then - mock(check_logger); - check_passed; - check_only_log(check_logger, "Unconditional check passed.", pass); - - check_passed(""); - check_only_log(check_logger, "", pass); - - check_passed("Checking my data."); - check_only_log(check_logger, "Checking my data.", pass); - - check_passed(result("for my data.")); - check_only_log(check_logger, "Unconditional check passed for my data.", pass); - unmock(check_logger); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - -end test_fixture; +-- This test suite verifies the check checker. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library ieee; +use ieee.std_logic_1164.all; +library vunit_lib; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use work.test_support.all; +use ieee.numeric_std.all; + +entity tb_check_passed is + generic ( + runner_cfg : string); +end entity tb_check_passed; + +architecture test_fixture of tb_check_passed is +begin + test_runner : process + variable my_checker : checker_t := new_checker("my_checker"); + variable stat : checker_stat_t; + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test that default checker check_passed always passes") then + get_checker_stat(stat); + check_passed; + verify_passed_checks(stat, 1); + verify_failed_checks(stat, 0); + + elsif run("Test that custom checker check_passed always passes") then + get_checker_stat(my_checker, stat); + check_passed(my_checker); + verify_passed_checks(my_checker, stat, 1); + verify_failed_checks(my_checker, stat, 0); + + elsif run("Test pass message") then + mock(check_logger); + check_passed; + check_only_log(check_logger, "Unconditional check passed.", pass); + + check_passed(""); + check_only_log(check_logger, "", pass); + + check_passed("Checking my data."); + check_only_log(check_logger, "Checking my data.", pass); + + check_passed(result("for my data.")); + check_only_log(check_logger, "Unconditional check passed for my data.", pass); + unmock(check_logger); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + +end test_fixture; diff --git a/vunit/vhdl/check/test/tb_check_relation.vhd b/vunit/vhdl/check/test/tb_check_relation.vhd index 1a51eeafc..dcffd8325 100644 --- a/vunit/vhdl/check/test/tb_check_relation.vhd +++ b/vunit/vhdl/check/test/tb_check_relation.vhd @@ -1,215 +1,215 @@ --- This test suite verifies the check_relation checker. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library ieee; -use ieee.std_logic_1164.all; -library vunit_lib; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use work.test_support.all; - -entity tb_check_relation is - generic ( - runner_cfg : string); -end entity; - -architecture test_fixture of tb_check_relation is -begin - - test_runner : process - type cash_t is record - dollars : natural; - cents : natural range 0 to 99; - end record cash_t; - - function ">" ( - constant l : cash_t; - constant r : cash_t) - return boolean is - begin - return ((l.dollars > r.dollars) or - ((l.dollars = r.dollars) and (l.cents >= r.cents))); - end function; - - function to_string ( - constant value : cash_t) - return string is - begin - return "$" & natural'image(value.dollars) & "." & natural'image(value.cents); - end function to_string; - - function len ( - constant s : string) - return integer is - begin - return s'length; - end function len; - - function to_string ( - constant value : integer) - return string is - begin - return integer'image(value); - end function to_string; - - variable passed : boolean; - variable stat : checker_stat_t; - variable my_checker : checker_t := new_checker("my_checker"); - variable my_logger : logger_t := get_logger(my_checker); - constant cash : cash_t := (99,95); - constant default_level : log_level_t := error; - variable data : natural; - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test that a true relation does not generate an error") then - data := 5; - get_checker_stat(stat); - check_relation(data > 3); - check_relation(passed, data > 3); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_relation(data > 3); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(stat, 3); - - get_checker_stat(my_checker, stat); - check_relation(my_checker, data > 3); - check_relation(my_checker, passed, data > 3); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_relation(my_checker, data > 3); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(my_checker, stat, 3); - - elsif run("Test pass message") then - data := 5; - mock(check_logger); - check_relation(data > 3); - check_only_log(check_logger, "Relation check passed - Expected data > 3. Left is 5. Right is 3.", pass); - - check_relation(data > 3, ""); - check_only_log(check_logger, "Expected data > 3. Left is 5. Right is 3.", pass); - - check_relation(data > 3, "Checking my data"); - check_only_log(check_logger, "Checking my data - Expected data > 3. Left is 5. Right is 3.", pass); - - check_relation(data > 3, result("for my data")); - check_only_log(check_logger, "Relation check passed for my data - Expected data > 3. Left is 5. Right is 3.", pass); - unmock(check_logger); - - elsif run("Test that all pre VHDL 2008 relational operators are supported") then - get_checker_stat(stat); - data := 5; - mock(check_logger); - check_relation(data = 3); - check_only_log(check_logger, "Relation check failed - Expected data = 3. Left is 5. Right is 3.", default_level); - - check_relation(data /= 5); - check_only_log(check_logger, "Relation check failed - Expected data /= 5. Left is 5. Right is 5.", default_level); - - check_relation(data < 5); - check_only_log(check_logger, "Relation check failed - Expected data < 5. Left is 5. Right is 5.", default_level); - - check_relation(data <= 4); - check_only_log(check_logger, "Relation check failed - Expected data <= 4. Left is 5. Right is 4.", default_level); - - check_relation(data > 5); - check_only_log(check_logger, "Relation check failed - Expected data > 5. Left is 5. Right is 5.", default_level); - - check_relation(data >= 6); - check_only_log(check_logger, "Relation check failed - Expected data >= 6. Left is 5. Right is 6.", default_level); - unmock(check_logger); - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 6); - reset_checker_stat; - - elsif run("Test that a generated message can contain string containing operands") then - get_checker_stat(stat); - mock(check_logger); - check_relation(len("foo") = 4); - check_only_log(check_logger, "Relation check failed - Expected len(""foo"") = 4. Left is 3. Right is 4.", default_level); - - check_relation(passed, len("foo") = 4); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Relation check failed - Expected len(""foo"") = 4. Left is 3. Right is 4.", default_level); - - passed := check_relation(len("foo") = 4); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Relation check failed - Expected len(""foo"") = 4. Left is 3. Right is 4.", default_level); - unmock(check_logger); - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 3); - reset_checker_stat; - - get_checker_stat(my_checker, stat); - mock(my_logger); - check_relation(my_checker, len("foo") = 4); - check_only_log(my_logger, "Relation check failed - Expected len(""foo"") = 4. Left is 3. Right is 4.", default_level); - - check_relation(my_checker, passed, len("foo") = 4); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(my_logger, "Relation check failed - Expected len(""foo"") = 4. Left is 3. Right is 4.", default_level); - - passed := check_relation(my_checker, len("foo") = 4); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(my_logger, "Relation check failed - Expected len(""foo"") = 4. Left is 3. Right is 4.", default_level); - unmock(my_logger); - verify_passed_checks(my_checker, stat, 0); - verify_failed_checks(my_checker, stat, 3); - reset_checker_stat(my_checker); - - elsif run("Test that custom types can be used") then - get_checker_stat(stat); - mock(check_logger); - check_relation(cash > cash_t'((100,0))); - check_only_log(check_logger, "Relation check failed - Expected cash > cash_t'((100,0)). Left is $99.95. Right is $100.0.", default_level); - - check_relation(passed, cash > cash_t'((100,0))); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Relation check failed - Expected cash > cash_t'((100,0)). Left is $99.95. Right is $100.0.", default_level); - - passed := check_relation(cash > cash_t'((100,0))); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Relation check failed - Expected cash > cash_t'((100,0)). Left is $99.95. Right is $100.0.", default_level); - unmock(check_logger); - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 3); - reset_checker_stat; - - get_checker_stat(my_checker, stat); - mock(my_logger); - check_relation(my_checker, cash > cash_t'((100,0))); - check_only_log(my_logger, "Relation check failed - Expected cash > cash_t'((100,0)). Left is $99.95. Right is $100.0.", default_level); - - check_relation(my_checker, passed, cash > cash_t'((100,0))); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(my_logger, "Relation check failed - Expected cash > cash_t'((100,0)). Left is $99.95. Right is $100.0.", default_level); - - passed := check_relation(my_checker, cash > cash_t'((100,0))); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(my_logger, "Relation check failed - Expected cash > cash_t'((100,0)). Left is $99.95. Right is $100.0.", default_level); - unmock(my_logger); - verify_passed_checks(my_checker, stat, 0); - verify_failed_checks(my_checker, stat, 3); - reset_checker_stat(my_checker); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 1 us); - -end test_fixture; +-- This test suite verifies the check_relation checker. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library ieee; +use ieee.std_logic_1164.all; +library vunit_lib; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use work.test_support.all; + +entity tb_check_relation is + generic ( + runner_cfg : string); +end entity; + +architecture test_fixture of tb_check_relation is +begin + + test_runner : process + type cash_t is record + dollars : natural; + cents : natural range 0 to 99; + end record cash_t; + + function ">" ( + constant l : cash_t; + constant r : cash_t) + return boolean is + begin + return ((l.dollars > r.dollars) or + ((l.dollars = r.dollars) and (l.cents >= r.cents))); + end function; + + function to_string ( + constant value : cash_t) + return string is + begin + return "$" & natural'image(value.dollars) & "." & natural'image(value.cents); + end function to_string; + + function len ( + constant s : string) + return integer is + begin + return s'length; + end function len; + + function to_string ( + constant value : integer) + return string is + begin + return integer'image(value); + end function to_string; + + variable passed : boolean; + variable stat : checker_stat_t; + variable my_checker : checker_t := new_checker("my_checker"); + variable my_logger : logger_t := get_logger(my_checker); + constant cash : cash_t := (99,95); + constant default_level : log_level_t := error; + variable data : natural; + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test that a true relation does not generate an error") then + data := 5; + get_checker_stat(stat); + check_relation(data > 3); + check_relation(passed, data > 3); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_relation(data > 3); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(stat, 3); + + get_checker_stat(my_checker, stat); + check_relation(my_checker, data > 3); + check_relation(my_checker, passed, data > 3); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_relation(my_checker, data > 3); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(my_checker, stat, 3); + + elsif run("Test pass message") then + data := 5; + mock(check_logger); + check_relation(data > 3); + check_only_log(check_logger, "Relation check passed - Expected data > 3. Left is 5. Right is 3.", pass); + + check_relation(data > 3, ""); + check_only_log(check_logger, "Expected data > 3. Left is 5. Right is 3.", pass); + + check_relation(data > 3, "Checking my data"); + check_only_log(check_logger, "Checking my data - Expected data > 3. Left is 5. Right is 3.", pass); + + check_relation(data > 3, result("for my data")); + check_only_log(check_logger, "Relation check passed for my data - Expected data > 3. Left is 5. Right is 3.", pass); + unmock(check_logger); + + elsif run("Test that all pre VHDL 2008 relational operators are supported") then + get_checker_stat(stat); + data := 5; + mock(check_logger); + check_relation(data = 3); + check_only_log(check_logger, "Relation check failed - Expected data = 3. Left is 5. Right is 3.", default_level); + + check_relation(data /= 5); + check_only_log(check_logger, "Relation check failed - Expected data /= 5. Left is 5. Right is 5.", default_level); + + check_relation(data < 5); + check_only_log(check_logger, "Relation check failed - Expected data < 5. Left is 5. Right is 5.", default_level); + + check_relation(data <= 4); + check_only_log(check_logger, "Relation check failed - Expected data <= 4. Left is 5. Right is 4.", default_level); + + check_relation(data > 5); + check_only_log(check_logger, "Relation check failed - Expected data > 5. Left is 5. Right is 5.", default_level); + + check_relation(data >= 6); + check_only_log(check_logger, "Relation check failed - Expected data >= 6. Left is 5. Right is 6.", default_level); + unmock(check_logger); + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 6); + reset_checker_stat; + + elsif run("Test that a generated message can contain string containing operands") then + get_checker_stat(stat); + mock(check_logger); + check_relation(len("foo") = 4); + check_only_log(check_logger, "Relation check failed - Expected len(""foo"") = 4. Left is 3. Right is 4.", default_level); + + check_relation(passed, len("foo") = 4); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Relation check failed - Expected len(""foo"") = 4. Left is 3. Right is 4.", default_level); + + passed := check_relation(len("foo") = 4); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Relation check failed - Expected len(""foo"") = 4. Left is 3. Right is 4.", default_level); + unmock(check_logger); + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 3); + reset_checker_stat; + + get_checker_stat(my_checker, stat); + mock(my_logger); + check_relation(my_checker, len("foo") = 4); + check_only_log(my_logger, "Relation check failed - Expected len(""foo"") = 4. Left is 3. Right is 4.", default_level); + + check_relation(my_checker, passed, len("foo") = 4); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(my_logger, "Relation check failed - Expected len(""foo"") = 4. Left is 3. Right is 4.", default_level); + + passed := check_relation(my_checker, len("foo") = 4); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(my_logger, "Relation check failed - Expected len(""foo"") = 4. Left is 3. Right is 4.", default_level); + unmock(my_logger); + verify_passed_checks(my_checker, stat, 0); + verify_failed_checks(my_checker, stat, 3); + reset_checker_stat(my_checker); + + elsif run("Test that custom types can be used") then + get_checker_stat(stat); + mock(check_logger); + check_relation(cash > cash_t'((100,0))); + check_only_log(check_logger, "Relation check failed - Expected cash > cash_t'((100,0)). Left is $99.95. Right is $100.0.", default_level); + + check_relation(passed, cash > cash_t'((100,0))); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Relation check failed - Expected cash > cash_t'((100,0)). Left is $99.95. Right is $100.0.", default_level); + + passed := check_relation(cash > cash_t'((100,0))); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Relation check failed - Expected cash > cash_t'((100,0)). Left is $99.95. Right is $100.0.", default_level); + unmock(check_logger); + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 3); + reset_checker_stat; + + get_checker_stat(my_checker, stat); + mock(my_logger); + check_relation(my_checker, cash > cash_t'((100,0))); + check_only_log(my_logger, "Relation check failed - Expected cash > cash_t'((100,0)). Left is $99.95. Right is $100.0.", default_level); + + check_relation(my_checker, passed, cash > cash_t'((100,0))); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(my_logger, "Relation check failed - Expected cash > cash_t'((100,0)). Left is $99.95. Right is $100.0.", default_level); + + passed := check_relation(my_checker, cash > cash_t'((100,0))); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(my_logger, "Relation check failed - Expected cash > cash_t'((100,0)). Left is $99.95. Right is $100.0.", default_level); + unmock(my_logger); + verify_passed_checks(my_checker, stat, 0); + verify_failed_checks(my_checker, stat, 3); + reset_checker_stat(my_checker); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 1 us); + +end test_fixture; diff --git a/vunit/vhdl/check/test/tb_check_relation_2008.vhd b/vunit/vhdl/check/test/tb_check_relation_2008.vhd index 15e3711e6..792a8c39c 100644 --- a/vunit/vhdl/check/test/tb_check_relation_2008.vhd +++ b/vunit/vhdl/check/test/tb_check_relation_2008.vhd @@ -1,77 +1,77 @@ --- This test suite verifies the check_relation checker. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library ieee; -use ieee.std_logic_1164.all; -library vunit_lib; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use work.test_support.all; - -entity tb_check_relation_2008 is - generic ( - runner_cfg : string); -end entity; - -architecture test_fixture of tb_check_relation_2008 is - signal sl_0 : std_logic := '0'; - signal sl_1 : std_logic := '1'; - signal bit_0 : bit := '0'; - signal bit_1 : bit := '1'; -begin - - test_runner : process - constant default_level : log_level_t := error; - variable stat : checker_stat_t; - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test that VHDL 2008 matching relational operators are supported") then - get_checker_stat(stat); - mock(check_logger); - check_relation(sl_1 ?= sl_0); - check_only_log(check_logger, "Relation check failed - Expected sl_1 ?= sl_0. Left is 1. Right is 0.", default_level); - - check_relation(bit_1 ?= bit_0); - check_only_log(check_logger, "Relation check failed - Expected bit_1 ?= bit_0. Left is 1. Right is 0.", default_level); - - check_relation(sl_1 ?/= sl_1); - check_only_log(check_logger, "Relation check failed - Expected sl_1 ?/= sl_1. Left is 1. Right is 1.", default_level); - - check_relation(sl_1 ?< sl_0); - check_only_log(check_logger, "Relation check failed - Expected sl_1 ?< sl_0. Left is 1. Right is 0.", default_level); - - check_relation(sl_1 ?<= sl_0); - check_only_log(check_logger, "Relation check failed - Expected sl_1 ?<= sl_0. Left is 1. Right is 0.", default_level); - - check_relation(sl_0 ?> sl_1); - check_only_log(check_logger, "Relation check failed - Expected sl_0 ?> sl_1. Left is 0. Right is 1.", default_level); - - check_relation(sl_0 ?>= sl_1); - check_only_log(check_logger, "Relation check failed - Expected sl_0 ?>= sl_1. Left is 0. Right is 1.", default_level); - unmock(check_logger); - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 7); - reset_checker_stat; - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 1 us); - -end test_fixture; +-- This test suite verifies the check_relation checker. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library ieee; +use ieee.std_logic_1164.all; +library vunit_lib; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use work.test_support.all; + +entity tb_check_relation_2008 is + generic ( + runner_cfg : string); +end entity; + +architecture test_fixture of tb_check_relation_2008 is + signal sl_0 : std_logic := '0'; + signal sl_1 : std_logic := '1'; + signal bit_0 : bit := '0'; + signal bit_1 : bit := '1'; +begin + + test_runner : process + constant default_level : log_level_t := error; + variable stat : checker_stat_t; + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test that VHDL 2008 matching relational operators are supported") then + get_checker_stat(stat); + mock(check_logger); + check_relation(sl_1 ?= sl_0); + check_only_log(check_logger, "Relation check failed - Expected sl_1 ?= sl_0. Left is 1. Right is 0.", default_level); + + check_relation(bit_1 ?= bit_0); + check_only_log(check_logger, "Relation check failed - Expected bit_1 ?= bit_0. Left is 1. Right is 0.", default_level); + + check_relation(sl_1 ?/= sl_1); + check_only_log(check_logger, "Relation check failed - Expected sl_1 ?/= sl_1. Left is 1. Right is 1.", default_level); + + check_relation(sl_1 ?< sl_0); + check_only_log(check_logger, "Relation check failed - Expected sl_1 ?< sl_0. Left is 1. Right is 0.", default_level); + + check_relation(sl_1 ?<= sl_0); + check_only_log(check_logger, "Relation check failed - Expected sl_1 ?<= sl_0. Left is 1. Right is 0.", default_level); + + check_relation(sl_0 ?> sl_1); + check_only_log(check_logger, "Relation check failed - Expected sl_0 ?> sl_1. Left is 0. Right is 1.", default_level); + + check_relation(sl_0 ?>= sl_1); + check_only_log(check_logger, "Relation check failed - Expected sl_0 ?>= sl_1. Left is 0. Right is 1.", default_level); + unmock(check_logger); + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 7); + reset_checker_stat; + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 1 us); + +end test_fixture; diff --git a/vunit/vhdl/check/test/tb_check_sequence.vhd b/vunit/vhdl/check/test/tb_check_sequence.vhd index 12290fd5f..08f7d46f5 100644 --- a/vunit/vhdl/check/test/tb_check_sequence.vhd +++ b/vunit/vhdl/check/test/tb_check_sequence.vhd @@ -1,281 +1,281 @@ --- This test suite verifies the check_sequence checker. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library ieee; -use ieee.std_logic_1164.all; -library vunit_lib; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use vunit_lib.runner_pkg.all; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use vunit_lib.string_ops.all; -use work.test_support.all; - -entity tb_check_sequence is - generic ( - runner_cfg : string); -end entity tb_check_sequence; - -architecture test_fixture of tb_check_sequence is - signal clk : std_logic := '0'; - - type slv_vector is array (natural range <>) of std_logic_vector(1 to 5); - constant n_checks : positive := 5; - signal inp : slv_vector(1 to n_checks) := (others => "00000"); - signal en : std_logic := '0'; - signal event_sequence : std_logic_vector(23 downto 20) := "0000"; - - signal start_check_sequence_4 : boolean := false; - constant my_checker_2 : checker_t := new_checker("my_checker2"); - constant my_checker_3 : checker_t := new_checker("my_checker3"); - constant my_checker_4 : checker_t := new_checker("my_checker4"); - constant my_checker_5 : checker_t := new_checker("my_checker5"); -begin - clock: process is - begin - while get_phase(runner_state) < test_runner_exit loop - clk <= '1', '0' after 5 ns; - wait for 10 ns; - end loop; - wait; - end process clock; - - check_sequence_1 : check_sequence(clk, - inp(1)(5), - inp(1)(1 to 4), - trigger_event => first_pipe); - - check_sequence_2 : check_sequence(my_checker_2, - clk, - inp(2)(5), - inp(2)(1 to 4), - trigger_event => penultimate); - - check_sequence_3 : check_sequence(my_checker_3, - clk, - inp(3)(5), - inp(3)(1 to 4), - trigger_event => first_no_pipe); - - check_sequence_4 : process - begin - wait until start_check_sequence_4; - check_sequence(my_checker_4, clk, inp(4)(5), inp(4)(1 to 1), result("for my data")); - wait; - end process; - - check_sequence_5 : check_sequence(my_checker_5, - clk, - inp(5)(5), - inp(5)(1 to 4), - result("for my data"), - trigger_event => first_pipe); - - check_sequence_6 : check_sequence(clk, - en, - event_sequence, - trigger_event => first_no_pipe); - - check_sequence_runner : process - variable stat : checker_stat_t; - constant default_level : log_level_t := error; - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test should fail on sequences shorter than two events") then - get_checker_stat(my_checker_4, stat); - mock(get_logger(my_checker_4)); - start_check_sequence_4 <= true; - wait for 1 ns; - check_only_log(get_logger(my_checker_4), - "Sequence check failed for my data - Event sequence length must be at least 2. Got 1.", - default_level); - unmock(get_logger(my_checker_4)); - verify_passed_checks(my_checker_4, stat, 0); - verify_failed_checks(my_checker_4, stat, 1); - reset_checker_stat(my_checker_4); - - elsif run("Test should pass a penultimate triggered pipelined and sequentially asserted event sequence") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(my_checker_2, stat); - apply_sequence("0000.1;1000.1;0100.1;1010.1;0101.1;0010.1", clk, inp(2)); - wait for 1 ns; - verify_passed_checks(my_checker_2,stat, 1); - verify_failed_checks(my_checker_2,stat, 0); - apply_sequence("0010.1;0001.1;0000.1", clk, inp(2)); - wait for 1 ns; - verify_passed_checks(my_checker_2,stat, 2); - verify_failed_checks(my_checker_2,stat, 0); - apply_sequence("0000.1;1000.1;0100.1;0000.1;0001.1;0000.0", clk, inp(2)); - wait for 1 ns; - verify_passed_checks(my_checker_2,stat, 2); - verify_failed_checks(my_checker_2,stat, 0); - - elsif run("Test should fail a penultimate triggered but interrupted event sequence") then - get_checker_stat(my_checker_2, stat); - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(my_checker_2, stat); - apply_sequence("0000.1;1000.1;0100.1;1010.1;0101.1;0010.1", clk, inp(2)); - wait for 1 ns; - verify_passed_checks(my_checker_2, stat, 1); - verify_failed_checks(my_checker_2, stat, 0); - mock(get_logger(my_checker_2)); - apply_sequence("0010.1;0000.1;0000.0", clk, inp(2)); - wait for 1 ns; - verify_passed_checks(my_checker_2, stat, 1); - verify_failed_checks(my_checker_2, stat, 1); - check_only_log(get_logger(my_checker_2), - "Sequence check failed - Missing required event at 3rd active and enabled clock edge.", - default_level); - unmock(get_logger(my_checker_2)); - reset_checker_stat(my_checker_2); - - elsif run("Test should pass a first triggered pipelined and sequentially asserted event sequence when pipelining is supported") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(stat); - apply_sequence("0000.1;1000.1;0100.1;1010.1;0101.1;0010.1", clk, inp(1)); - wait for 1 ns; - verify_passed_checks(stat, 1); - verify_failed_checks(stat, 0); - apply_sequence("0010.1;0001.1;0000.0", clk, inp(1)); - wait for 1 ns; - verify_passed_checks(stat, 2); - verify_failed_checks(stat, 0); - - elsif run("Test should fail a first triggered but interrupted event sequence") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(stat); - apply_sequence("0000.1;1000.1;0100.1;1010.1;0101.1;0000.1", clk, inp(1)); - wait for 1 ns; - verify_passed_checks(stat, 1); - verify_failed_checks(stat, 0); - mock(check_logger); - apply_sequence("0000.1;0000.0", clk, inp(1)); - wait for 1 ns; - verify_passed_checks(stat, 1); - verify_failed_checks(stat, 1); - check_only_log(check_logger, - "Sequence check failed - Missing required event at 2nd active and enabled clock edge.", - default_level); - unmock(check_logger); - reset_checker_stat; - - elsif run("Test should ignore a first triggered and simulataneously initiated event sequence when pipelining is not supported") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(my_checker_3, stat); - apply_sequence("0000.1;1000.1;0100.1;1010.1;0101.1;0010.1", clk, inp(3)); - wait for 1 ns; - verify_passed_checks(my_checker_3, stat, 1); - verify_failed_checks(my_checker_3, stat, 0); - apply_sequence("0010.1;0001.1;0000.0", clk, inp(3)); - wait for 1 ns; - verify_passed_checks(my_checker_3, stat, 1); - verify_failed_checks(my_checker_3, stat, 0); - - elsif run("Test should fail on unknowns in event sequence") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(my_checker_2, stat); - mock(get_logger(my_checker_2)); - apply_sequence("0000.1;1000.1;0100.1;10X0.1;0101.1;0010.1", clk, inp(2)); - wait for 1 ns; - verify_passed_checks(my_checker_2, stat, 0); - verify_failed_checks(my_checker_2, stat, 1); - check_only_log(get_logger(my_checker_2), "Sequence check failed - Got 10X0.", default_level); - unmock(get_logger(my_checker_2)); - apply_sequence("0010.1;0001.1;0000.0", clk, inp(2)); - wait for 1 ns; - verify_passed_checks(my_checker_2, stat, 1); - verify_failed_checks(my_checker_2, stat, 1); - reset_checker_stat(my_checker_2); - get_checker_stat(stat); - mock(check_logger); - apply_sequence("0000.1;10X0.1;0100.1", clk, inp(1)); - wait for 1 ns; - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 1); - check_only_log(check_logger, "Sequence check failed - Got 10X0.", default_level); - apply_sequence("0100.1;10X0.1;0101.1;0010.1", clk, inp(1)); - wait for 1 ns; - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 3); - check_log(check_logger, "Sequence check failed - Missing required event at 2nd active and enabled clock edge.", default_level); - check_only_log(check_logger, "Sequence check failed - Got 10X0.", default_level); - apply_sequence("0010.1;0001.1;0000.0", clk, inp(1)); - wait for 1 ns; - verify_passed_checks(stat, 1); - verify_failed_checks(stat, 3); - check_only_log(check_logger, "Sequence check passed", pass); - - apply_sequence("0000.1;1000.1;1100.1;0X10.1;0011.1;0001.1;0000.1", clk, inp(1)); - verify_passed_checks(stat, 2); - verify_failed_checks(stat, 5); - check_log(check_logger, "Sequence check failed - Missing required event at 1st active and enabled clock edge.", default_level); - check_log(check_logger, "Sequence check failed - Got 0X10.", default_level); - check_only_log(check_logger, "Sequence check passed", pass); - unmock(check_logger); - reset_checker_stat; - - elsif run("Test should support weak high and low meta values") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(my_checker_2, stat); - apply_sequence("0000.1;HL00.H;LH00.1;0010.1;000H.1;0000.0", clk, inp(2)); - wait for 1 ns; - verify_passed_checks(my_checker_2, stat, 1); - verify_failed_checks(my_checker_2, stat, 0); - - elsif run("Test should handle reversed and or offset expressions") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(stat); - en <= '1'; - event_sequence <= "1000"; - wait until rising_edge(clk); - event_sequence <= "0100"; - wait until rising_edge(clk); - event_sequence <= "0010"; - wait until rising_edge(clk); - event_sequence <= "0001"; - wait until rising_edge(clk); - event_sequence <= "0000"; - en <= '0'; - wait for 1 ns; - verify_passed_checks(stat, 1); - verify_failed_checks(stat, 0); - - elsif run("Test pass message") then - get_checker_stat(my_checker_5, stat); - mock(get_logger(my_checker_5)); - apply_sequence("0000.1;1000.1;0100.1;0010.1;0001.1;0000.1", clk, inp(5)); - wait for 1 ns; - check_only_log(get_logger(my_checker_5), "Sequence check passed for my data", pass); - unmock(get_logger(my_checker_5)); - verify_passed_checks(my_checker_5, stat, 1); - verify_failed_checks(my_checker_5, stat, 0); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 2 us); - -end test_fixture; +-- This test suite verifies the check_sequence checker. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library ieee; +use ieee.std_logic_1164.all; +library vunit_lib; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use vunit_lib.runner_pkg.all; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.string_ops.all; +use work.test_support.all; + +entity tb_check_sequence is + generic ( + runner_cfg : string); +end entity tb_check_sequence; + +architecture test_fixture of tb_check_sequence is + signal clk : std_logic := '0'; + + type slv_vector is array (natural range <>) of std_logic_vector(1 to 5); + constant n_checks : positive := 5; + signal inp : slv_vector(1 to n_checks) := (others => "00000"); + signal en : std_logic := '0'; + signal event_sequence : std_logic_vector(23 downto 20) := "0000"; + + signal start_check_sequence_4 : boolean := false; + constant my_checker_2 : checker_t := new_checker("my_checker2"); + constant my_checker_3 : checker_t := new_checker("my_checker3"); + constant my_checker_4 : checker_t := new_checker("my_checker4"); + constant my_checker_5 : checker_t := new_checker("my_checker5"); +begin + clock: process is + begin + while get_phase(runner_state) < test_runner_exit loop + clk <= '1', '0' after 5 ns; + wait for 10 ns; + end loop; + wait; + end process clock; + + check_sequence_1 : check_sequence(clk, + inp(1)(5), + inp(1)(1 to 4), + trigger_event => first_pipe); + + check_sequence_2 : check_sequence(my_checker_2, + clk, + inp(2)(5), + inp(2)(1 to 4), + trigger_event => penultimate); + + check_sequence_3 : check_sequence(my_checker_3, + clk, + inp(3)(5), + inp(3)(1 to 4), + trigger_event => first_no_pipe); + + check_sequence_4 : process + begin + wait until start_check_sequence_4; + check_sequence(my_checker_4, clk, inp(4)(5), inp(4)(1 to 1), result("for my data")); + wait; + end process; + + check_sequence_5 : check_sequence(my_checker_5, + clk, + inp(5)(5), + inp(5)(1 to 4), + result("for my data"), + trigger_event => first_pipe); + + check_sequence_6 : check_sequence(clk, + en, + event_sequence, + trigger_event => first_no_pipe); + + check_sequence_runner : process + variable stat : checker_stat_t; + constant default_level : log_level_t := error; + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test should fail on sequences shorter than two events") then + get_checker_stat(my_checker_4, stat); + mock(get_logger(my_checker_4)); + start_check_sequence_4 <= true; + wait for 1 ns; + check_only_log(get_logger(my_checker_4), + "Sequence check failed for my data - Event sequence length must be at least 2. Got 1.", + default_level); + unmock(get_logger(my_checker_4)); + verify_passed_checks(my_checker_4, stat, 0); + verify_failed_checks(my_checker_4, stat, 1); + reset_checker_stat(my_checker_4); + + elsif run("Test should pass a penultimate triggered pipelined and sequentially asserted event sequence") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(my_checker_2, stat); + apply_sequence("0000.1;1000.1;0100.1;1010.1;0101.1;0010.1", clk, inp(2)); + wait for 1 ns; + verify_passed_checks(my_checker_2,stat, 1); + verify_failed_checks(my_checker_2,stat, 0); + apply_sequence("0010.1;0001.1;0000.1", clk, inp(2)); + wait for 1 ns; + verify_passed_checks(my_checker_2,stat, 2); + verify_failed_checks(my_checker_2,stat, 0); + apply_sequence("0000.1;1000.1;0100.1;0000.1;0001.1;0000.0", clk, inp(2)); + wait for 1 ns; + verify_passed_checks(my_checker_2,stat, 2); + verify_failed_checks(my_checker_2,stat, 0); + + elsif run("Test should fail a penultimate triggered but interrupted event sequence") then + get_checker_stat(my_checker_2, stat); + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(my_checker_2, stat); + apply_sequence("0000.1;1000.1;0100.1;1010.1;0101.1;0010.1", clk, inp(2)); + wait for 1 ns; + verify_passed_checks(my_checker_2, stat, 1); + verify_failed_checks(my_checker_2, stat, 0); + mock(get_logger(my_checker_2)); + apply_sequence("0010.1;0000.1;0000.0", clk, inp(2)); + wait for 1 ns; + verify_passed_checks(my_checker_2, stat, 1); + verify_failed_checks(my_checker_2, stat, 1); + check_only_log(get_logger(my_checker_2), + "Sequence check failed - Missing required event at 3rd active and enabled clock edge.", + default_level); + unmock(get_logger(my_checker_2)); + reset_checker_stat(my_checker_2); + + elsif run("Test should pass a first triggered pipelined and sequentially asserted event sequence when pipelining is supported") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(stat); + apply_sequence("0000.1;1000.1;0100.1;1010.1;0101.1;0010.1", clk, inp(1)); + wait for 1 ns; + verify_passed_checks(stat, 1); + verify_failed_checks(stat, 0); + apply_sequence("0010.1;0001.1;0000.0", clk, inp(1)); + wait for 1 ns; + verify_passed_checks(stat, 2); + verify_failed_checks(stat, 0); + + elsif run("Test should fail a first triggered but interrupted event sequence") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(stat); + apply_sequence("0000.1;1000.1;0100.1;1010.1;0101.1;0000.1", clk, inp(1)); + wait for 1 ns; + verify_passed_checks(stat, 1); + verify_failed_checks(stat, 0); + mock(check_logger); + apply_sequence("0000.1;0000.0", clk, inp(1)); + wait for 1 ns; + verify_passed_checks(stat, 1); + verify_failed_checks(stat, 1); + check_only_log(check_logger, + "Sequence check failed - Missing required event at 2nd active and enabled clock edge.", + default_level); + unmock(check_logger); + reset_checker_stat; + + elsif run("Test should ignore a first triggered and simulataneously initiated event sequence when pipelining is not supported") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(my_checker_3, stat); + apply_sequence("0000.1;1000.1;0100.1;1010.1;0101.1;0010.1", clk, inp(3)); + wait for 1 ns; + verify_passed_checks(my_checker_3, stat, 1); + verify_failed_checks(my_checker_3, stat, 0); + apply_sequence("0010.1;0001.1;0000.0", clk, inp(3)); + wait for 1 ns; + verify_passed_checks(my_checker_3, stat, 1); + verify_failed_checks(my_checker_3, stat, 0); + + elsif run("Test should fail on unknowns in event sequence") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(my_checker_2, stat); + mock(get_logger(my_checker_2)); + apply_sequence("0000.1;1000.1;0100.1;10X0.1;0101.1;0010.1", clk, inp(2)); + wait for 1 ns; + verify_passed_checks(my_checker_2, stat, 0); + verify_failed_checks(my_checker_2, stat, 1); + check_only_log(get_logger(my_checker_2), "Sequence check failed - Got 10X0.", default_level); + unmock(get_logger(my_checker_2)); + apply_sequence("0010.1;0001.1;0000.0", clk, inp(2)); + wait for 1 ns; + verify_passed_checks(my_checker_2, stat, 1); + verify_failed_checks(my_checker_2, stat, 1); + reset_checker_stat(my_checker_2); + get_checker_stat(stat); + mock(check_logger); + apply_sequence("0000.1;10X0.1;0100.1", clk, inp(1)); + wait for 1 ns; + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 1); + check_only_log(check_logger, "Sequence check failed - Got 10X0.", default_level); + apply_sequence("0100.1;10X0.1;0101.1;0010.1", clk, inp(1)); + wait for 1 ns; + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 3); + check_log(check_logger, "Sequence check failed - Missing required event at 2nd active and enabled clock edge.", default_level); + check_only_log(check_logger, "Sequence check failed - Got 10X0.", default_level); + apply_sequence("0010.1;0001.1;0000.0", clk, inp(1)); + wait for 1 ns; + verify_passed_checks(stat, 1); + verify_failed_checks(stat, 3); + check_only_log(check_logger, "Sequence check passed", pass); + + apply_sequence("0000.1;1000.1;1100.1;0X10.1;0011.1;0001.1;0000.1", clk, inp(1)); + verify_passed_checks(stat, 2); + verify_failed_checks(stat, 5); + check_log(check_logger, "Sequence check failed - Missing required event at 1st active and enabled clock edge.", default_level); + check_log(check_logger, "Sequence check failed - Got 0X10.", default_level); + check_only_log(check_logger, "Sequence check passed", pass); + unmock(check_logger); + reset_checker_stat; + + elsif run("Test should support weak high and low meta values") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(my_checker_2, stat); + apply_sequence("0000.1;HL00.H;LH00.1;0010.1;000H.1;0000.0", clk, inp(2)); + wait for 1 ns; + verify_passed_checks(my_checker_2, stat, 1); + verify_failed_checks(my_checker_2, stat, 0); + + elsif run("Test should handle reversed and or offset expressions") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(stat); + en <= '1'; + event_sequence <= "1000"; + wait until rising_edge(clk); + event_sequence <= "0100"; + wait until rising_edge(clk); + event_sequence <= "0010"; + wait until rising_edge(clk); + event_sequence <= "0001"; + wait until rising_edge(clk); + event_sequence <= "0000"; + en <= '0'; + wait for 1 ns; + verify_passed_checks(stat, 1); + verify_failed_checks(stat, 0); + + elsif run("Test pass message") then + get_checker_stat(my_checker_5, stat); + mock(get_logger(my_checker_5)); + apply_sequence("0000.1;1000.1;0100.1;0010.1;0001.1;0000.1", clk, inp(5)); + wait for 1 ns; + check_only_log(get_logger(my_checker_5), "Sequence check passed for my data", pass); + unmock(get_logger(my_checker_5)); + verify_passed_checks(my_checker_5, stat, 1); + verify_failed_checks(my_checker_5, stat, 0); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 2 us); + +end test_fixture; diff --git a/vunit/vhdl/check/test/tb_check_stable.vhd b/vunit/vhdl/check/test/tb_check_stable.vhd index f01c3e4f4..59768d843 100644 --- a/vunit/vhdl/check/test/tb_check_stable.vhd +++ b/vunit/vhdl/check/test/tb_check_stable.vhd @@ -1,659 +1,659 @@ --- This test suite verifies the check_stable checker. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library ieee; -use ieee.std_logic_1164.all; -library vunit_lib; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use vunit_lib.runner_pkg.all; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use work.test_support.all; - -entity tb_check_stable is - generic ( - runner_cfg : string); -end entity tb_check_stable; - -architecture test_fixture of tb_check_stable is - signal clk : std_logic := '0'; - - signal check_stable_in_1, check_stable_in_2, check_stable_in_3, - check_stable_in_8, check_stable_in_10 : std_logic_vector(1 to 5) := "00000"; - alias check_stable_start_event_1 : std_logic is check_stable_in_1(1); - alias check_stable_end_event_1 : std_logic is check_stable_in_1(2); - alias check_stable_expr_1 : std_logic_vector(2 downto 0) is check_stable_in_1(3 to 5); - alias check_stable_start_event_2 : std_logic is check_stable_in_2(1); - alias check_stable_end_event_2 : std_logic is check_stable_in_2(2); - alias check_stable_expr_2 : std_logic_vector(2 downto 0) is check_stable_in_2(3 to 5); - alias check_stable_start_event_3 : std_logic is check_stable_in_3(1); - alias check_stable_end_event_3 : std_logic is check_stable_in_3(2); - alias check_stable_expr_3 : std_logic_vector(2 downto 0) is check_stable_in_3(3 to 5); - alias check_stable_start_event_8 : std_logic is check_stable_in_8(1); - alias check_stable_end_event_8 : std_logic is check_stable_in_8(2); - alias check_stable_expr_8 : std_logic_vector(2 downto 0) is check_stable_in_8(3 to 5); - alias check_stable_start_event_10 : std_logic is check_stable_in_10(1); - alias check_stable_end_event_10 : std_logic is check_stable_in_10(2); - alias check_stable_expr_10 : std_logic_vector(2 downto 0) is check_stable_in_10(3 to 5); - - signal check_stable_start_event_4 : std_logic := '0'; - signal check_stable_end_event_4 : std_logic := '0'; - signal check_stable_expr_4 : std_logic_vector(7 to 9) := "000"; - - signal check_stable_in_5, check_stable_in_6, check_stable_in_7, check_stable_in_9, - check_stable_in_11 : std_logic_vector(1 to 3) := "000"; - alias check_stable_start_event_5 : std_logic is check_stable_in_5(1); - alias check_stable_end_event_5 : std_logic is check_stable_in_5(2); - alias check_stable_expr_5 : std_logic is check_stable_in_5(3); - alias check_stable_start_event_6 : std_logic is check_stable_in_6(1); - alias check_stable_end_event_6 : std_logic is check_stable_in_6(2); - alias check_stable_expr_6 : std_logic is check_stable_in_6(3); - alias check_stable_start_event_7 : std_logic is check_stable_in_7(1); - alias check_stable_end_event_7 : std_logic is check_stable_in_7(2); - alias check_stable_expr_7 : std_logic is check_stable_in_7(3); - alias check_stable_start_event_9 : std_logic is check_stable_in_9(1); - alias check_stable_end_event_9 : std_logic is check_stable_in_9(2); - alias check_stable_expr_9 : std_logic is check_stable_in_9(3); - alias check_stable_start_event_11 : std_logic is check_stable_in_11(1); - alias check_stable_end_event_11 : std_logic is check_stable_in_11(2); - alias check_stable_expr_11 : std_logic is check_stable_in_11(3); - - signal check_stable_en_1, check_stable_en_2, check_stable_en_3, check_stable_en_4 : std_logic := '1'; - signal check_stable_en_5, check_stable_en_6, check_stable_en_7, check_stable_en_8 : std_logic := '1'; - signal check_stable_en_9, check_stable_en_10, check_stable_en_11 : std_logic := '1'; - - signal en, start_event, end_event, expr : std_logic := '1'; - - constant my_checker2 : checker_t := new_checker("my_checker2"); - constant my_checker3 : checker_t := new_checker("my_checker3", default_log_level => info); - constant my_checker6 : checker_t := new_checker("my_checker6"); - constant my_checker7 : checker_t := new_checker("my_checker7", default_log_level => info); - constant my_checker10 : checker_t := new_checker("my_checker10"); - constant my_checker11 : checker_t := new_checker("my_checker11"); -begin - clock : process is - begin - while get_phase(runner_state) < test_runner_exit loop - clk <= '1', '0' after 5 ns; - wait for 10 ns; - end loop; - wait; - end process clock; - - check_stable_1 : check_stable(clk, - check_stable_en_1, - check_stable_start_event_1, - check_stable_end_event_1, - check_stable_expr_1); - check_stable_2 : check_stable(my_checker2, - clk, - check_stable_en_2, - check_stable_start_event_2, - check_stable_end_event_2, - check_stable_expr_2, - active_clock_edge => falling_edge); - check_stable_3 : check_stable(my_checker3, - clk, - check_stable_en_3, - check_stable_start_event_3, - check_stable_end_event_3, - check_stable_expr_3); - - check_stable_4 : check_stable(clk, - check_stable_en_4, - check_stable_start_event_4, - check_stable_end_event_4, - check_stable_expr_4); - - check_stable_5 : check_stable(clk, - check_stable_en_5, - check_stable_start_event_5, - check_stable_end_event_5, - check_stable_expr_5); - check_stable_6 : check_stable(my_checker6, - clk, - check_stable_en_6, - check_stable_start_event_6, - check_stable_end_event_6, - check_stable_expr_6, - active_clock_edge => falling_edge); - check_stable_7 : check_stable(my_checker7, - clk, - check_stable_en_7, - check_stable_start_event_7, - check_stable_end_event_7, - check_stable_expr_7); - check_stable_8 : check_stable(clk, - check_stable_en_8, - check_stable_start_event_8, - check_stable_end_event_8, - check_stable_expr_8, - "Checking stability", - allow_restart => true); - check_stable_9 : check_stable(clk, - check_stable_en_9, - check_stable_start_event_9, - check_stable_end_event_9, - check_stable_expr_9, - result("for my data"), - allow_restart => true); - check_stable_10 : check_stable(my_checker10, - clk, - check_stable_en_10, - check_stable_start_event_10, - check_stable_end_event_10, - check_stable_expr_10, - allow_restart => true); - check_stable_11 : check_stable(my_checker11, - clk, - check_stable_en_11, - check_stable_start_event_11, - check_stable_end_event_11, - check_stable_expr_11, - allow_restart => true); - - check_stable_runner : process - variable stat : checker_stat_t; - constant default_level : log_level_t := error; - - procedure test_concurrent_std_logic_vector_check ( - signal clk : in std_logic; - signal check_input : out std_logic_vector(1 to 5); - checker : checker_t; - constant level : in log_level_t := error; - constant active_rising_clock_edge : in boolean := true) is - constant logger : logger_t := get_logger(checker); - begin - if running_test_case = "Test concurrent checker should pass stable window" then - get_checker_stat(checker, stat); - apply_sequence("00.101;10.101;00.101;01.101;00.101", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 1); - - elsif running_test_case = "Test concurrent checker should pass window with varying drive strength" then - get_checker_stat(checker, stat); - apply_sequence("00.101;10.101;00.1LH;01.101;00.101", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 1); - - elsif running_test_case = "Test concurrent checker should handle weak start and end events" then - get_checker_stat(checker, stat); - mock(logger); - apply_sequence("00.101;HL.101;LL.111;LH.111", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got 111 (7) at 2nd active and enabled clock edge. Expected 101 (5).", - level); - apply_sequence("LH.111;00.111", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got 111 (7) at 3rd active and enabled clock edge. Expected 101 (5).", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 2); - reset_checker_stat(checker); - - elsif running_test_case = "Test concurrent checker should fail unstable window" then - get_checker_stat(checker, stat); - mock(logger); - apply_sequence("00.101;10.101;00.111;00.111", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got 111 (7) at 2nd active and enabled clock edge. Expected 101 (5).", - level); - apply_sequence("01.111;00.111", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got 111 (7) at 3rd active and enabled clock edge. Expected 101 (5).", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 2); - reset_checker_stat(checker); - - elsif running_test_case = "Test concurrent checker should fail window with weak changes to opposite level" then - get_checker_stat(checker, stat); - mock(logger); - apply_sequence("00.101;10.101;00.101;00.L01;01.1H1", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got L01 (1) at 3rd active and enabled clock edge. Expected 101 (5).", - level); - apply_sequence("01.1H1;00.111", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got 1H1 (7) at 4th active and enabled clock edge. Expected 101 (5).", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 2); - reset_checker_stat(checker); - - elsif running_test_case = "Test concurrent checker should fail on unknown start event" then - get_checker_stat(checker, stat); - mock(logger); - apply_sequence("00.101;X0.101;00.101;01.101;00.101", clk, check_input, active_rising_clock_edge); - check_only_log(logger, - "Stability check failed - Start event is X.", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 1); - reset_checker_stat(checker); - - elsif running_test_case = "Test concurrent checker should fail on unknown end event in active window" then - get_checker_stat(checker, stat); - mock(logger); - apply_sequence("00.101;0X.101;10.101;0X.101;00.101", clk, check_input, active_rising_clock_edge); - check_only_log(logger, - "Stability check failed - End event is X.", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 1); - reset_checker_stat(checker); - - elsif running_test_case = "Test concurrent checker should fail on stable unknown window" then - get_checker_stat(checker, stat); - mock(logger); - apply_sequence("00.101;10.10X;00.10X;01.10X;00.101", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got 10X (NaN) at 1st active and enabled clock edge.", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 1); - reset_checker_stat(checker); - elsif running_test_case = "Test concurrent checker should fail on unknown in window" then - get_checker_stat(checker, stat); - mock(logger); - apply_sequence("00.101;10.101;00.10X;01.101;01.101;00.101", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got 10X (NaN) at 2nd active and enabled clock edge. Expected 101 (5).", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 1); - reset_checker_stat(checker); - - elsif running_test_case = "Test concurrent checker should handle back to back windows" then - get_checker_stat(checker, stat); - mock(logger); - apply_sequence("00.101;10.101;01.111;10.010", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got 111 (7) at 2nd active and enabled clock edge. Expected 101 (5).", - level); - apply_sequence("10.010;01.101;00.101", clk, check_input, active_rising_clock_edge); - check_only_log(logger, - "Stability check failed - Got 101 (5) at 2nd active and enabled clock edge. Expected 010 (2).", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 2); - reset_checker_stat(checker); - - elsif running_test_case = "Test concurrent checker should ignore second of two overlapping windows" then - get_checker_stat(checker, stat); - mock(logger); - apply_sequence("00.101;10.101;10.111;01.111", clk, check_input, - active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got 111 (7) at 2nd active and enabled clock edge. Expected 101 (5).", - level); - apply_sequence("01.111;00.111;00.101;01.101;00.101", clk, check_input, - active_rising_clock_edge); - check_only_log(logger, - "Stability check failed - Got 111 (7) at 3rd active and enabled clock edge. Expected 101 (5).", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 2); - reset_checker_stat(checker); - - elsif running_test_case = "Test concurrent checker should handle one cycle windows" then - get_checker_stat(checker, stat); - apply_sequence("00.101;11.101;10.111;01.111;00.111", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 2); - end if; - end procedure test_concurrent_std_logic_vector_check; - - procedure test_concurrent_std_logic_check ( - signal clk : in std_logic; - signal check_input : out std_logic_vector(1 to 3); - checker : checker_t; - constant level : in log_level_t := error; - constant active_rising_clock_edge : in boolean := true) is - constant logger : logger_t := get_logger(checker); - begin - if running_test_case = "Test concurrent checker should pass stable window" then - get_checker_stat(checker, stat); - apply_sequence("00.1;10.1;00.1;01.1;00.1", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 1); - - elsif running_test_case = "Test concurrent checker should pass window with varying drive strength" then - get_checker_stat(checker, stat); - apply_sequence("00.1;10.1;00.H;01.1;00.1", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 1); - - elsif running_test_case = "Test concurrent checker should handle weak start and end events" then - get_checker_stat(checker, stat); - mock(logger); - apply_sequence("00.0;HL.0;LL.1;LH.1", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got 1 at 2nd active and enabled clock edge. Expected 0.", - level); - apply_sequence("LH.1;00.1", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got 1 at 3rd active and enabled clock edge. Expected 0.", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 2); - reset_checker_stat(checker); - - elsif running_test_case = "Test concurrent checker should fail unstable window" then - get_checker_stat(checker, stat); - mock(logger); - apply_sequence("00.0;10.0;00.1;01.1", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got 1 at 2nd active and enabled clock edge. Expected 0.", - level); - apply_sequence("01.1;00.1", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got 1 at 3rd active and enabled clock edge. Expected 0.", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 2); - reset_checker_stat(checker); - - elsif running_test_case = "Test concurrent checker should fail window with weak changes to opposite level" then - get_checker_stat(checker, stat); - mock(logger); - apply_sequence("00.1;10.1;00.1;00.1;00.L;01.1;00.1", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got L at 4th active and enabled clock edge. Expected 1.", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 1); - reset_checker_stat(checker); - - elsif running_test_case = "Test concurrent checker should fail on unknown start event" then - get_checker_stat(checker, stat); - mock(logger); - apply_sequence("00.0;X0.0;00.0;01.0;00.0", clk, check_input, active_rising_clock_edge); - check_only_log(logger, - "Stability check failed - Start event is X.", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 1); - reset_checker_stat(checker); - - elsif running_test_case = "Test concurrent checker should fail on unknown end event in active window" then - get_checker_stat(checker, stat); - mock(logger); - apply_sequence("00.1;0X.1;10.1;0X.1;00.1", clk, check_input, active_rising_clock_edge); - check_only_log(logger, - "Stability check failed - End event is X.", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 1); - reset_checker_stat(checker); - - elsif running_test_case = "Test concurrent checker should fail on stable unknown window" then - get_checker_stat(checker, stat); - mock(logger); - apply_sequence("00.1;10.X;00.X;01.X;00.1", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got X at 1st active and enabled clock edge.", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 1); - reset_checker_stat(checker); - elsif running_test_case = "Test concurrent checker should fail on unknown in window" then - mock(logger); - apply_sequence("00.1;10.1;00.X;01.1;01.1;00.1", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got X at 2nd active and enabled clock edge. Expected 1.", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 1); - reset_checker_stat(checker); - - elsif running_test_case = "Test concurrent checker should handle back to back windows" then - get_checker_stat(checker, stat); - mock(logger); - apply_sequence("00.0;10.0;01.1;10.1", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got 1 at 2nd active and enabled clock edge. Expected 0.", - level); - apply_sequence("10.1;01.0;00.0", clk, check_input, active_rising_clock_edge); - check_only_log(logger, - "Stability check failed - Got 0 at 2nd active and enabled clock edge. Expected 1.", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 2); - reset_checker_stat(checker); - - elsif running_test_case = "Test concurrent checker should ignore second of two overlapping windows" then - get_checker_stat(checker, stat); - mock(logger); - apply_sequence("00.0;10.0;10.1;01.1", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - check_only_log(logger, - "Stability check failed - Got 1 at 2nd active and enabled clock edge. Expected 0.", - level); - apply_sequence("01.1;00.1;00.0;01.0;00.0", clk, check_input, active_rising_clock_edge); - check_only_log(logger, - "Stability check failed - Got 1 at 3rd active and enabled clock edge. Expected 0.", - level); - unmock(logger); - verify_passed_checks(checker, stat, 0); - verify_failed_checks(checker, stat, 2); - reset_checker_stat(checker); - - elsif running_test_case = "Test concurrent checker should handle one cycle windows" then - get_checker_stat(checker, stat); - apply_sequence("00.0;11.0;10.1;01.1;00.1", clk, check_input, active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 2); - end if; - end procedure test_concurrent_std_logic_check; - - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test concurrent checker should pass stable window") or - run("Test concurrent checker should pass window with varying drive strength") or - run("Test concurrent checker should handle weak start and end events") or - run("Test concurrent checker should fail unstable window") or - run("Test concurrent checker should fail window with weak changes to opposite level") or - run("Test concurrent checker should fail on unknown start event") or - run("Test concurrent checker should fail on unknown end event in active window") or - run("Test concurrent checker should fail on stable unknown window") or - run("Test concurrent checker should fail on unknown in window") or - run("Test concurrent checker should handle back to back windows") or - run("Test concurrent checker should ignore second of two overlapping windows") or - run("Test concurrent checker should handle one cycle windows") then - - test_concurrent_std_logic_vector_check(clk, check_stable_in_1, default_checker); - test_concurrent_std_logic_vector_check(clk, check_stable_in_2, my_checker2, error, false); - test_concurrent_std_logic_vector_check(clk, check_stable_in_3, my_checker3, info); - test_concurrent_std_logic_check(clk, check_stable_in_5, default_checker); - test_concurrent_std_logic_check(clk, check_stable_in_6, my_checker6, error, false); - test_concurrent_std_logic_check(clk, check_stable_in_7, my_checker7, info); - - elsif run("Test concurrent checker with std_logic_vector input should pass unstable window if not enabled") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(stat); - apply_sequence("00.101;10.101;00.111", clk, check_stable_in_1); - check_stable_en_1 <= '0'; - apply_sequence("00.111;01.101", clk, check_stable_in_1); - check_stable_en_1 <= '1'; - apply_sequence("01.101;00.101", clk, check_stable_in_1); - apply_sequence("00.101;10.101;00.111", clk, check_stable_in_1); - check_stable_en_1 <= 'L'; - apply_sequence("00.111;01.101", clk, check_stable_in_1); - check_stable_en_1 <= 'H'; - apply_sequence("01.101;00.101", clk, check_stable_in_1); - apply_sequence("00.101;10.101;00.111", clk, check_stable_in_1); - check_stable_en_1 <= 'X'; - apply_sequence("00.111;01.101", clk, check_stable_in_1); - check_stable_en_1 <= '1'; - apply_sequence("01.101;00.101", clk, check_stable_in_1); - wait until rising_edge(clk); - wait for 1 ns; - verify_passed_checks(stat, 3); - verify_failed_checks(stat, 0); - - elsif run("Test concurrent checker with std_logic input should pass unstable window if not enabled") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(stat); - apply_sequence("00.0;10.0;00.1", clk, check_stable_in_5); - check_stable_en_5 <= '0'; - apply_sequence("00.1;01.0", clk, check_stable_in_5); - check_stable_en_5 <= '1'; - apply_sequence("01.0;00.0", clk, check_stable_in_5); - apply_sequence("00.0;10.0;00.1", clk, check_stable_in_5); - check_stable_en_5 <= 'L'; - apply_sequence("00.1;01.0", clk, check_stable_in_5); - check_stable_en_5 <= 'H'; - apply_sequence("01.0;00.0", clk, check_stable_in_5); - apply_sequence("00.0;10.0;00.1", clk, check_stable_in_5); - check_stable_en_5 <= 'X'; - apply_sequence("00.1;01.0", clk, check_stable_in_5); - check_stable_en_5 <= '1'; - apply_sequence("01.0;00.0", clk, check_stable_in_5); - wait until rising_edge(clk); - wait for 1 ns; - verify_passed_checks(stat, 3); - verify_failed_checks(stat, 0); - - elsif run("Test should handle reversed and or offset expressions") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(stat); - check_stable_start_event_4 <= '1'; - check_stable_expr_4 <= "101"; - wait until rising_edge(clk); - check_stable_start_event_4 <= '0'; - wait until rising_edge(clk); - check_stable_end_event_4 <= '1'; - wait until rising_edge(clk); - check_stable_end_event_4 <= '0'; - wait for 1 ns; - verify_passed_checks(stat, 1); - - elsif run("Test pass message and that internal checks don't count for std_logic_vector") then - get_checker_stat(stat); - mock(check_logger); - apply_sequence("00.101;10.101;00.111;01.101;00.101;00.101", clk, check_stable_in_8); - check_only_log(check_logger, - "Checking stability - Got 111 (7) at 2nd active and enabled clock edge. Expected 101 (5).", - default_level); - apply_sequence("00.101;10.101;00.101;01.101;00.101", clk, check_stable_in_8); - wait for 1 ns; - check_only_log(check_logger, - "Checking stability - Got 101 (5) for 3 active and enabled clock edges.", - pass); - apply_sequence("00.101;10.101;00.101;10.111;00.111", clk, check_stable_in_8); - wait for 1 ns; - check_only_log(check_logger, - "Checking stability - Got 101 (5) for 2 active and enabled clock edges.", - pass); - unmock(check_logger); verify_passed_checks(stat, 2); - verify_failed_checks(stat, 1); - reset_checker_stat; - elsif run("Test pass message and that internal checks don't count for std_logic") then - get_checker_stat(stat); - mock(check_logger); - apply_sequence("00.1;10.1;00.0;01.1;00.1;00.1", clk, check_stable_in_9); - check_only_log(check_logger, - "Stability check failed for my data - Got 0 at 2nd active and enabled clock edge. Expected 1.", - default_level); - apply_sequence("00.1;10.1;00.1;01.1;00.1", clk, check_stable_in_9); - wait for 1 ns; - check_only_log(check_logger, - "Stability check passed for my data - Got 1 for 3 active and enabled clock edges.", - pass); - apply_sequence("00.1;10.1;00.1;10.0;00.0", clk, check_stable_in_9); - wait for 1 ns; - check_only_log(check_logger, - "Stability check passed for my data - Got 1 for 2 active and enabled clock edges.", - pass); - unmock(check_logger); - verify_passed_checks(stat, 2); - verify_failed_checks(stat, 1); - elsif run("Test that a new start event restarts a std_logic_vector window when allowed") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(my_checker10, stat); - apply_sequence("00.101;10.101;00.101;10.110;10.111", clk, check_stable_in_10); - apply_sequence("10.111;00.111;H0.101;00.101;01.101;00.110", clk, check_stable_in_10); - wait until rising_edge(clk); - wait for 1 ns; - verify_passed_checks(my_checker10, stat, 4); - verify_failed_checks(my_checker10, stat, 0); - - elsif run("Test that a new start event restarts a std_logic window when allowed") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(my_checker11, stat); - apply_sequence("00.1;10.1;00.1;10.0;10.1", clk, check_stable_in_11); - apply_sequence("10.1;00.1;H0.0;00.0;01.0;00.1", clk, check_stable_in_11); - wait until rising_edge(clk); - wait for 1 ns; - verify_passed_checks(my_checker11, stat, 4); - verify_failed_checks(my_checker11, stat, 0); - - elsif run("Test that check_stable can be called sequentially") then - get_checker_stat(stat); - check_stable(clk, en, start_event, end_event, expr); - verify_passed_checks(stat, 1); - verify_failed_checks(stat, 0); - reset_checker_stat; - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 4 us); - -end test_fixture; +-- This test suite verifies the check_stable checker. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library ieee; +use ieee.std_logic_1164.all; +library vunit_lib; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use vunit_lib.runner_pkg.all; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use work.test_support.all; + +entity tb_check_stable is + generic ( + runner_cfg : string); +end entity tb_check_stable; + +architecture test_fixture of tb_check_stable is + signal clk : std_logic := '0'; + + signal check_stable_in_1, check_stable_in_2, check_stable_in_3, + check_stable_in_8, check_stable_in_10 : std_logic_vector(1 to 5) := "00000"; + alias check_stable_start_event_1 : std_logic is check_stable_in_1(1); + alias check_stable_end_event_1 : std_logic is check_stable_in_1(2); + alias check_stable_expr_1 : std_logic_vector(2 downto 0) is check_stable_in_1(3 to 5); + alias check_stable_start_event_2 : std_logic is check_stable_in_2(1); + alias check_stable_end_event_2 : std_logic is check_stable_in_2(2); + alias check_stable_expr_2 : std_logic_vector(2 downto 0) is check_stable_in_2(3 to 5); + alias check_stable_start_event_3 : std_logic is check_stable_in_3(1); + alias check_stable_end_event_3 : std_logic is check_stable_in_3(2); + alias check_stable_expr_3 : std_logic_vector(2 downto 0) is check_stable_in_3(3 to 5); + alias check_stable_start_event_8 : std_logic is check_stable_in_8(1); + alias check_stable_end_event_8 : std_logic is check_stable_in_8(2); + alias check_stable_expr_8 : std_logic_vector(2 downto 0) is check_stable_in_8(3 to 5); + alias check_stable_start_event_10 : std_logic is check_stable_in_10(1); + alias check_stable_end_event_10 : std_logic is check_stable_in_10(2); + alias check_stable_expr_10 : std_logic_vector(2 downto 0) is check_stable_in_10(3 to 5); + + signal check_stable_start_event_4 : std_logic := '0'; + signal check_stable_end_event_4 : std_logic := '0'; + signal check_stable_expr_4 : std_logic_vector(7 to 9) := "000"; + + signal check_stable_in_5, check_stable_in_6, check_stable_in_7, check_stable_in_9, + check_stable_in_11 : std_logic_vector(1 to 3) := "000"; + alias check_stable_start_event_5 : std_logic is check_stable_in_5(1); + alias check_stable_end_event_5 : std_logic is check_stable_in_5(2); + alias check_stable_expr_5 : std_logic is check_stable_in_5(3); + alias check_stable_start_event_6 : std_logic is check_stable_in_6(1); + alias check_stable_end_event_6 : std_logic is check_stable_in_6(2); + alias check_stable_expr_6 : std_logic is check_stable_in_6(3); + alias check_stable_start_event_7 : std_logic is check_stable_in_7(1); + alias check_stable_end_event_7 : std_logic is check_stable_in_7(2); + alias check_stable_expr_7 : std_logic is check_stable_in_7(3); + alias check_stable_start_event_9 : std_logic is check_stable_in_9(1); + alias check_stable_end_event_9 : std_logic is check_stable_in_9(2); + alias check_stable_expr_9 : std_logic is check_stable_in_9(3); + alias check_stable_start_event_11 : std_logic is check_stable_in_11(1); + alias check_stable_end_event_11 : std_logic is check_stable_in_11(2); + alias check_stable_expr_11 : std_logic is check_stable_in_11(3); + + signal check_stable_en_1, check_stable_en_2, check_stable_en_3, check_stable_en_4 : std_logic := '1'; + signal check_stable_en_5, check_stable_en_6, check_stable_en_7, check_stable_en_8 : std_logic := '1'; + signal check_stable_en_9, check_stable_en_10, check_stable_en_11 : std_logic := '1'; + + signal en, start_event, end_event, expr : std_logic := '1'; + + constant my_checker2 : checker_t := new_checker("my_checker2"); + constant my_checker3 : checker_t := new_checker("my_checker3", default_log_level => info); + constant my_checker6 : checker_t := new_checker("my_checker6"); + constant my_checker7 : checker_t := new_checker("my_checker7", default_log_level => info); + constant my_checker10 : checker_t := new_checker("my_checker10"); + constant my_checker11 : checker_t := new_checker("my_checker11"); +begin + clock : process is + begin + while get_phase(runner_state) < test_runner_exit loop + clk <= '1', '0' after 5 ns; + wait for 10 ns; + end loop; + wait; + end process clock; + + check_stable_1 : check_stable(clk, + check_stable_en_1, + check_stable_start_event_1, + check_stable_end_event_1, + check_stable_expr_1); + check_stable_2 : check_stable(my_checker2, + clk, + check_stable_en_2, + check_stable_start_event_2, + check_stable_end_event_2, + check_stable_expr_2, + active_clock_edge => falling_edge); + check_stable_3 : check_stable(my_checker3, + clk, + check_stable_en_3, + check_stable_start_event_3, + check_stable_end_event_3, + check_stable_expr_3); + + check_stable_4 : check_stable(clk, + check_stable_en_4, + check_stable_start_event_4, + check_stable_end_event_4, + check_stable_expr_4); + + check_stable_5 : check_stable(clk, + check_stable_en_5, + check_stable_start_event_5, + check_stable_end_event_5, + check_stable_expr_5); + check_stable_6 : check_stable(my_checker6, + clk, + check_stable_en_6, + check_stable_start_event_6, + check_stable_end_event_6, + check_stable_expr_6, + active_clock_edge => falling_edge); + check_stable_7 : check_stable(my_checker7, + clk, + check_stable_en_7, + check_stable_start_event_7, + check_stable_end_event_7, + check_stable_expr_7); + check_stable_8 : check_stable(clk, + check_stable_en_8, + check_stable_start_event_8, + check_stable_end_event_8, + check_stable_expr_8, + "Checking stability", + allow_restart => true); + check_stable_9 : check_stable(clk, + check_stable_en_9, + check_stable_start_event_9, + check_stable_end_event_9, + check_stable_expr_9, + result("for my data"), + allow_restart => true); + check_stable_10 : check_stable(my_checker10, + clk, + check_stable_en_10, + check_stable_start_event_10, + check_stable_end_event_10, + check_stable_expr_10, + allow_restart => true); + check_stable_11 : check_stable(my_checker11, + clk, + check_stable_en_11, + check_stable_start_event_11, + check_stable_end_event_11, + check_stable_expr_11, + allow_restart => true); + + check_stable_runner : process + variable stat : checker_stat_t; + constant default_level : log_level_t := error; + + procedure test_concurrent_std_logic_vector_check ( + signal clk : in std_logic; + signal check_input : out std_logic_vector(1 to 5); + checker : checker_t; + constant level : in log_level_t := error; + constant active_rising_clock_edge : in boolean := true) is + constant logger : logger_t := get_logger(checker); + begin + if running_test_case = "Test concurrent checker should pass stable window" then + get_checker_stat(checker, stat); + apply_sequence("00.101;10.101;00.101;01.101;00.101", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 1); + + elsif running_test_case = "Test concurrent checker should pass window with varying drive strength" then + get_checker_stat(checker, stat); + apply_sequence("00.101;10.101;00.1LH;01.101;00.101", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 1); + + elsif running_test_case = "Test concurrent checker should handle weak start and end events" then + get_checker_stat(checker, stat); + mock(logger); + apply_sequence("00.101;HL.101;LL.111;LH.111", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got 111 (7) at 2nd active and enabled clock edge. Expected 101 (5).", + level); + apply_sequence("LH.111;00.111", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got 111 (7) at 3rd active and enabled clock edge. Expected 101 (5).", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 2); + reset_checker_stat(checker); + + elsif running_test_case = "Test concurrent checker should fail unstable window" then + get_checker_stat(checker, stat); + mock(logger); + apply_sequence("00.101;10.101;00.111;00.111", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got 111 (7) at 2nd active and enabled clock edge. Expected 101 (5).", + level); + apply_sequence("01.111;00.111", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got 111 (7) at 3rd active and enabled clock edge. Expected 101 (5).", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 2); + reset_checker_stat(checker); + + elsif running_test_case = "Test concurrent checker should fail window with weak changes to opposite level" then + get_checker_stat(checker, stat); + mock(logger); + apply_sequence("00.101;10.101;00.101;00.L01;01.1H1", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got L01 (1) at 3rd active and enabled clock edge. Expected 101 (5).", + level); + apply_sequence("01.1H1;00.111", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got 1H1 (7) at 4th active and enabled clock edge. Expected 101 (5).", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 2); + reset_checker_stat(checker); + + elsif running_test_case = "Test concurrent checker should fail on unknown start event" then + get_checker_stat(checker, stat); + mock(logger); + apply_sequence("00.101;X0.101;00.101;01.101;00.101", clk, check_input, active_rising_clock_edge); + check_only_log(logger, + "Stability check failed - Start event is X.", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 1); + reset_checker_stat(checker); + + elsif running_test_case = "Test concurrent checker should fail on unknown end event in active window" then + get_checker_stat(checker, stat); + mock(logger); + apply_sequence("00.101;0X.101;10.101;0X.101;00.101", clk, check_input, active_rising_clock_edge); + check_only_log(logger, + "Stability check failed - End event is X.", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 1); + reset_checker_stat(checker); + + elsif running_test_case = "Test concurrent checker should fail on stable unknown window" then + get_checker_stat(checker, stat); + mock(logger); + apply_sequence("00.101;10.10X;00.10X;01.10X;00.101", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got 10X (NaN) at 1st active and enabled clock edge.", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 1); + reset_checker_stat(checker); + elsif running_test_case = "Test concurrent checker should fail on unknown in window" then + get_checker_stat(checker, stat); + mock(logger); + apply_sequence("00.101;10.101;00.10X;01.101;01.101;00.101", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got 10X (NaN) at 2nd active and enabled clock edge. Expected 101 (5).", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 1); + reset_checker_stat(checker); + + elsif running_test_case = "Test concurrent checker should handle back to back windows" then + get_checker_stat(checker, stat); + mock(logger); + apply_sequence("00.101;10.101;01.111;10.010", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got 111 (7) at 2nd active and enabled clock edge. Expected 101 (5).", + level); + apply_sequence("10.010;01.101;00.101", clk, check_input, active_rising_clock_edge); + check_only_log(logger, + "Stability check failed - Got 101 (5) at 2nd active and enabled clock edge. Expected 010 (2).", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 2); + reset_checker_stat(checker); + + elsif running_test_case = "Test concurrent checker should ignore second of two overlapping windows" then + get_checker_stat(checker, stat); + mock(logger); + apply_sequence("00.101;10.101;10.111;01.111", clk, check_input, + active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got 111 (7) at 2nd active and enabled clock edge. Expected 101 (5).", + level); + apply_sequence("01.111;00.111;00.101;01.101;00.101", clk, check_input, + active_rising_clock_edge); + check_only_log(logger, + "Stability check failed - Got 111 (7) at 3rd active and enabled clock edge. Expected 101 (5).", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 2); + reset_checker_stat(checker); + + elsif running_test_case = "Test concurrent checker should handle one cycle windows" then + get_checker_stat(checker, stat); + apply_sequence("00.101;11.101;10.111;01.111;00.111", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 2); + end if; + end procedure test_concurrent_std_logic_vector_check; + + procedure test_concurrent_std_logic_check ( + signal clk : in std_logic; + signal check_input : out std_logic_vector(1 to 3); + checker : checker_t; + constant level : in log_level_t := error; + constant active_rising_clock_edge : in boolean := true) is + constant logger : logger_t := get_logger(checker); + begin + if running_test_case = "Test concurrent checker should pass stable window" then + get_checker_stat(checker, stat); + apply_sequence("00.1;10.1;00.1;01.1;00.1", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 1); + + elsif running_test_case = "Test concurrent checker should pass window with varying drive strength" then + get_checker_stat(checker, stat); + apply_sequence("00.1;10.1;00.H;01.1;00.1", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 1); + + elsif running_test_case = "Test concurrent checker should handle weak start and end events" then + get_checker_stat(checker, stat); + mock(logger); + apply_sequence("00.0;HL.0;LL.1;LH.1", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got 1 at 2nd active and enabled clock edge. Expected 0.", + level); + apply_sequence("LH.1;00.1", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got 1 at 3rd active and enabled clock edge. Expected 0.", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 2); + reset_checker_stat(checker); + + elsif running_test_case = "Test concurrent checker should fail unstable window" then + get_checker_stat(checker, stat); + mock(logger); + apply_sequence("00.0;10.0;00.1;01.1", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got 1 at 2nd active and enabled clock edge. Expected 0.", + level); + apply_sequence("01.1;00.1", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got 1 at 3rd active and enabled clock edge. Expected 0.", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 2); + reset_checker_stat(checker); + + elsif running_test_case = "Test concurrent checker should fail window with weak changes to opposite level" then + get_checker_stat(checker, stat); + mock(logger); + apply_sequence("00.1;10.1;00.1;00.1;00.L;01.1;00.1", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got L at 4th active and enabled clock edge. Expected 1.", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 1); + reset_checker_stat(checker); + + elsif running_test_case = "Test concurrent checker should fail on unknown start event" then + get_checker_stat(checker, stat); + mock(logger); + apply_sequence("00.0;X0.0;00.0;01.0;00.0", clk, check_input, active_rising_clock_edge); + check_only_log(logger, + "Stability check failed - Start event is X.", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 1); + reset_checker_stat(checker); + + elsif running_test_case = "Test concurrent checker should fail on unknown end event in active window" then + get_checker_stat(checker, stat); + mock(logger); + apply_sequence("00.1;0X.1;10.1;0X.1;00.1", clk, check_input, active_rising_clock_edge); + check_only_log(logger, + "Stability check failed - End event is X.", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 1); + reset_checker_stat(checker); + + elsif running_test_case = "Test concurrent checker should fail on stable unknown window" then + get_checker_stat(checker, stat); + mock(logger); + apply_sequence("00.1;10.X;00.X;01.X;00.1", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got X at 1st active and enabled clock edge.", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 1); + reset_checker_stat(checker); + elsif running_test_case = "Test concurrent checker should fail on unknown in window" then + mock(logger); + apply_sequence("00.1;10.1;00.X;01.1;01.1;00.1", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got X at 2nd active and enabled clock edge. Expected 1.", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 1); + reset_checker_stat(checker); + + elsif running_test_case = "Test concurrent checker should handle back to back windows" then + get_checker_stat(checker, stat); + mock(logger); + apply_sequence("00.0;10.0;01.1;10.1", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got 1 at 2nd active and enabled clock edge. Expected 0.", + level); + apply_sequence("10.1;01.0;00.0", clk, check_input, active_rising_clock_edge); + check_only_log(logger, + "Stability check failed - Got 0 at 2nd active and enabled clock edge. Expected 1.", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 2); + reset_checker_stat(checker); + + elsif running_test_case = "Test concurrent checker should ignore second of two overlapping windows" then + get_checker_stat(checker, stat); + mock(logger); + apply_sequence("00.0;10.0;10.1;01.1", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + check_only_log(logger, + "Stability check failed - Got 1 at 2nd active and enabled clock edge. Expected 0.", + level); + apply_sequence("01.1;00.1;00.0;01.0;00.0", clk, check_input, active_rising_clock_edge); + check_only_log(logger, + "Stability check failed - Got 1 at 3rd active and enabled clock edge. Expected 0.", + level); + unmock(logger); + verify_passed_checks(checker, stat, 0); + verify_failed_checks(checker, stat, 2); + reset_checker_stat(checker); + + elsif running_test_case = "Test concurrent checker should handle one cycle windows" then + get_checker_stat(checker, stat); + apply_sequence("00.0;11.0;10.1;01.1;00.1", clk, check_input, active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 2); + end if; + end procedure test_concurrent_std_logic_check; + + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test concurrent checker should pass stable window") or + run("Test concurrent checker should pass window with varying drive strength") or + run("Test concurrent checker should handle weak start and end events") or + run("Test concurrent checker should fail unstable window") or + run("Test concurrent checker should fail window with weak changes to opposite level") or + run("Test concurrent checker should fail on unknown start event") or + run("Test concurrent checker should fail on unknown end event in active window") or + run("Test concurrent checker should fail on stable unknown window") or + run("Test concurrent checker should fail on unknown in window") or + run("Test concurrent checker should handle back to back windows") or + run("Test concurrent checker should ignore second of two overlapping windows") or + run("Test concurrent checker should handle one cycle windows") then + + test_concurrent_std_logic_vector_check(clk, check_stable_in_1, default_checker); + test_concurrent_std_logic_vector_check(clk, check_stable_in_2, my_checker2, error, false); + test_concurrent_std_logic_vector_check(clk, check_stable_in_3, my_checker3, info); + test_concurrent_std_logic_check(clk, check_stable_in_5, default_checker); + test_concurrent_std_logic_check(clk, check_stable_in_6, my_checker6, error, false); + test_concurrent_std_logic_check(clk, check_stable_in_7, my_checker7, info); + + elsif run("Test concurrent checker with std_logic_vector input should pass unstable window if not enabled") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(stat); + apply_sequence("00.101;10.101;00.111", clk, check_stable_in_1); + check_stable_en_1 <= '0'; + apply_sequence("00.111;01.101", clk, check_stable_in_1); + check_stable_en_1 <= '1'; + apply_sequence("01.101;00.101", clk, check_stable_in_1); + apply_sequence("00.101;10.101;00.111", clk, check_stable_in_1); + check_stable_en_1 <= 'L'; + apply_sequence("00.111;01.101", clk, check_stable_in_1); + check_stable_en_1 <= 'H'; + apply_sequence("01.101;00.101", clk, check_stable_in_1); + apply_sequence("00.101;10.101;00.111", clk, check_stable_in_1); + check_stable_en_1 <= 'X'; + apply_sequence("00.111;01.101", clk, check_stable_in_1); + check_stable_en_1 <= '1'; + apply_sequence("01.101;00.101", clk, check_stable_in_1); + wait until rising_edge(clk); + wait for 1 ns; + verify_passed_checks(stat, 3); + verify_failed_checks(stat, 0); + + elsif run("Test concurrent checker with std_logic input should pass unstable window if not enabled") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(stat); + apply_sequence("00.0;10.0;00.1", clk, check_stable_in_5); + check_stable_en_5 <= '0'; + apply_sequence("00.1;01.0", clk, check_stable_in_5); + check_stable_en_5 <= '1'; + apply_sequence("01.0;00.0", clk, check_stable_in_5); + apply_sequence("00.0;10.0;00.1", clk, check_stable_in_5); + check_stable_en_5 <= 'L'; + apply_sequence("00.1;01.0", clk, check_stable_in_5); + check_stable_en_5 <= 'H'; + apply_sequence("01.0;00.0", clk, check_stable_in_5); + apply_sequence("00.0;10.0;00.1", clk, check_stable_in_5); + check_stable_en_5 <= 'X'; + apply_sequence("00.1;01.0", clk, check_stable_in_5); + check_stable_en_5 <= '1'; + apply_sequence("01.0;00.0", clk, check_stable_in_5); + wait until rising_edge(clk); + wait for 1 ns; + verify_passed_checks(stat, 3); + verify_failed_checks(stat, 0); + + elsif run("Test should handle reversed and or offset expressions") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(stat); + check_stable_start_event_4 <= '1'; + check_stable_expr_4 <= "101"; + wait until rising_edge(clk); + check_stable_start_event_4 <= '0'; + wait until rising_edge(clk); + check_stable_end_event_4 <= '1'; + wait until rising_edge(clk); + check_stable_end_event_4 <= '0'; + wait for 1 ns; + verify_passed_checks(stat, 1); + + elsif run("Test pass message and that internal checks don't count for std_logic_vector") then + get_checker_stat(stat); + mock(check_logger); + apply_sequence("00.101;10.101;00.111;01.101;00.101;00.101", clk, check_stable_in_8); + check_only_log(check_logger, + "Checking stability - Got 111 (7) at 2nd active and enabled clock edge. Expected 101 (5).", + default_level); + apply_sequence("00.101;10.101;00.101;01.101;00.101", clk, check_stable_in_8); + wait for 1 ns; + check_only_log(check_logger, + "Checking stability - Got 101 (5) for 3 active and enabled clock edges.", + pass); + apply_sequence("00.101;10.101;00.101;10.111;00.111", clk, check_stable_in_8); + wait for 1 ns; + check_only_log(check_logger, + "Checking stability - Got 101 (5) for 2 active and enabled clock edges.", + pass); + unmock(check_logger); verify_passed_checks(stat, 2); + verify_failed_checks(stat, 1); + reset_checker_stat; + elsif run("Test pass message and that internal checks don't count for std_logic") then + get_checker_stat(stat); + mock(check_logger); + apply_sequence("00.1;10.1;00.0;01.1;00.1;00.1", clk, check_stable_in_9); + check_only_log(check_logger, + "Stability check failed for my data - Got 0 at 2nd active and enabled clock edge. Expected 1.", + default_level); + apply_sequence("00.1;10.1;00.1;01.1;00.1", clk, check_stable_in_9); + wait for 1 ns; + check_only_log(check_logger, + "Stability check passed for my data - Got 1 for 3 active and enabled clock edges.", + pass); + apply_sequence("00.1;10.1;00.1;10.0;00.0", clk, check_stable_in_9); + wait for 1 ns; + check_only_log(check_logger, + "Stability check passed for my data - Got 1 for 2 active and enabled clock edges.", + pass); + unmock(check_logger); + verify_passed_checks(stat, 2); + verify_failed_checks(stat, 1); + elsif run("Test that a new start event restarts a std_logic_vector window when allowed") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(my_checker10, stat); + apply_sequence("00.101;10.101;00.101;10.110;10.111", clk, check_stable_in_10); + apply_sequence("10.111;00.111;H0.101;00.101;01.101;00.110", clk, check_stable_in_10); + wait until rising_edge(clk); + wait for 1 ns; + verify_passed_checks(my_checker10, stat, 4); + verify_failed_checks(my_checker10, stat, 0); + + elsif run("Test that a new start event restarts a std_logic window when allowed") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(my_checker11, stat); + apply_sequence("00.1;10.1;00.1;10.0;10.1", clk, check_stable_in_11); + apply_sequence("10.1;00.1;H0.0;00.0;01.0;00.1", clk, check_stable_in_11); + wait until rising_edge(clk); + wait for 1 ns; + verify_passed_checks(my_checker11, stat, 4); + verify_failed_checks(my_checker11, stat, 0); + + elsif run("Test that check_stable can be called sequentially") then + get_checker_stat(stat); + check_stable(clk, en, start_event, end_event, expr); + verify_passed_checks(stat, 1); + verify_failed_checks(stat, 0); + reset_checker_stat; + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 4 us); + +end test_fixture; diff --git a/vunit/vhdl/check/test/tb_check_zero_one_hot.vhd b/vunit/vhdl/check/test/tb_check_zero_one_hot.vhd index 612f5e4f6..9cc763235 100644 --- a/vunit/vhdl/check/test/tb_check_zero_one_hot.vhd +++ b/vunit/vhdl/check/test/tb_check_zero_one_hot.vhd @@ -1,298 +1,298 @@ --- This test suite verifies the check_zero_one_hot checker. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library ieee; -use ieee.std_logic_1164.all; -library vunit_lib; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use vunit_lib.runner_pkg.all; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use work.test_support.all; - -entity tb_check_zero_one_hot is - generic ( - runner_cfg : string); -end entity tb_check_zero_one_hot; - -architecture test_fixture of tb_check_zero_one_hot is - signal clk : std_logic := '0'; - - signal check_zero_one_hot_in_1, check_zero_one_hot_in_2, check_zero_one_hot_in_3 : std_logic_vector(3 downto 0) := (others => '0'); - signal check_zero_one_hot_en_1, check_zero_one_hot_en_2, check_zero_one_hot_en_3 : std_logic := '1'; - - constant my_checker2 : checker_t := new_checker("my_checker2"); - constant my_checker3 : checker_t := new_checker("my_checker3", default_log_level => info); - -begin - clock: process is - begin - while get_phase(runner_state) < test_runner_exit loop - clk <= '1', '0' after 5 ns; - wait for 10 ns; - end loop; - wait; - end process clock; - - check_zero_one_hot_1 : check_zero_one_hot(clk, check_zero_one_hot_en_1, check_zero_one_hot_in_1); - check_zero_one_hot_2 : check_zero_one_hot(my_checker2, clk, check_zero_one_hot_en_2, check_zero_one_hot_in_2, active_clock_edge => falling_edge); - check_zero_one_hot_3 : check_zero_one_hot(my_checker3, clk, check_zero_one_hot_en_3, check_zero_one_hot_in_3); - - check_zero_one_hot_runner : process - variable passed : boolean; - variable stat : checker_stat_t; - constant reversed_and_offset_expr : std_logic_vector(23 downto 20) := "1000"; - constant default_level : log_level_t := error; - - procedure test_concurrent_check ( - signal clk : in std_logic; - signal check_input : out std_logic_vector; - checker : checker_t; - constant level : in log_level_t := error; - constant active_rising_clock_edge : in boolean := true) is - begin - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - get_checker_stat(checker, stat); - apply_sequence("0000;LL00;1000;HL00", clk, check_input, active_rising_clock_edge); - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - verify_passed_checks(checker, stat, 4); - verify_failed_checks(checker, stat, 0); - mock(get_logger(checker)); - apply_sequence("1001;100H;000X", clk, check_input, active_rising_clock_edge); - wait until clock_edge(clk, active_rising_clock_edge); - wait for 1 ns; - check_log(get_logger(checker), "Zero one-hot check failed - Got 1001.", level); - check_log(get_logger(checker), "Zero one-hot check failed - Got 100H.", level); - check_log(get_logger(checker), "Zero one-hot check failed - Got 000X.", level); - unmock(get_logger(checker)); - verify_passed_checks(checker, stat, 4); - verify_failed_checks(checker, stat, 3); - reset_checker_stat(checker); - apply_sequence("0000", clk, check_input, active_rising_clock_edge); - end procedure test_concurrent_check; - - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test should pass on zero or one high bit") then - get_checker_stat(stat); - check_zero_one_hot("0000"); - check_zero_one_hot("LL00"); - check_zero_one_hot("1000"); - check_zero_one_hot("HL00"); - verify_passed_checks(stat, 4); - - get_checker_stat(my_checker3, stat); - check_zero_one_hot(my_checker3, "0000"); - check_zero_one_hot(my_checker3, "LL00"); - check_zero_one_hot(my_checker3, "1000"); - check_zero_one_hot(my_checker3, "HL00"); - verify_passed_checks(my_checker3, stat, 4); - - get_checker_stat(stat); - check_zero_one_hot(passed, "0000"); - assert_true(passed, "Should return pass = true on passing check"); - check_zero_one_hot(passed, "LL00"); - assert_true(passed, "Should return pass = true on passing check"); - check_zero_one_hot(passed, "1000"); - assert_true(passed, "Should return pass = true on passing check"); - check_zero_one_hot(passed, "HL00"); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(stat, 4); - - get_checker_stat(stat); - passed := check_zero_one_hot("0000"); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_zero_one_hot("LL00"); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_zero_one_hot("1000"); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_zero_one_hot("HL00"); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(stat, 4); - - get_checker_stat(my_checker3, stat); - check_zero_one_hot(my_checker3, passed, "0000"); - assert_true(passed, "Should return pass = true on passing check"); - check_zero_one_hot(my_checker3, passed, "LL00"); - assert_true(passed, "Should return pass = true on passing check"); - check_zero_one_hot(my_checker3, passed, "1000"); - assert_true(passed, "Should return pass = true on passing check"); - check_zero_one_hot(my_checker3, passed, "HL00"); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(my_checker3, stat, 4); - - get_checker_stat(my_checker3, stat); - passed := check_zero_one_hot(my_checker3, "0000"); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_zero_one_hot(my_checker3, "LL00"); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_zero_one_hot(my_checker3, "1000"); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_zero_one_hot(my_checker3, "HL00"); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(my_checker3, stat, 4); - - elsif run("Test pass message") then - mock(check_logger); - check_zero_one_hot("00000"); - check_only_log(check_logger, "Zero one-hot check passed - Got 0_0000.", pass); - - check_zero_one_hot("00000", ""); - check_only_log(check_logger, "Got 0_0000.", pass); - - check_zero_one_hot("00000", "Checking my data"); - check_only_log(check_logger, "Checking my data - Got 0_0000.", pass); - - check_zero_one_hot("00000", result("for my data")); - check_only_log(check_logger, "Zero one-hot check passed for my data - Got 0_0000.", pass); - unmock(check_logger); - - elsif run("Test should fail on more than one high bit") then - get_checker_stat(stat); - mock(check_logger); - check_zero_one_hot("01001"); - check_only_log(check_logger, "Zero one-hot check failed - Got 0_1001.", default_level); - - check_zero_one_hot("0100H"); - check_only_log(check_logger, "Zero one-hot check failed - Got 0_100H.", default_level); - - check_zero_one_hot(passed, "01001"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Zero one-hot check failed - Got 0_1001.", default_level); - - check_zero_one_hot(passed, "0100H"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Zero one-hot check failed - Got 0_100H.", default_level); - - passed := check_zero_one_hot("01001"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Zero one-hot check failed - Got 0_1001.", default_level); - - passed := check_zero_one_hot("0100H"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Zero one-hot check failed - Got 0_100H.", default_level); - unmock(check_logger); - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 6); - reset_checker_stat; - - get_checker_stat(my_checker3, stat); - mock(get_logger(my_checker3)); - check_zero_one_hot(my_checker3, "01001"); - check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_1001.", info); - - check_zero_one_hot(my_checker3, "0100H"); - check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_100H.", info); - - check_zero_one_hot(my_checker3, passed, "01001"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_1001.", info); - - check_zero_one_hot(my_checker3, passed, "0100H"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_100H.", info); - - passed := check_zero_one_hot(my_checker3, "01001"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_1001.", info); - - passed := check_zero_one_hot(my_checker3, "0100H"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_100H.", info); - unmock(get_logger(my_checker3)); - verify_passed_checks(my_checker3, stat, 0); - verify_failed_checks(my_checker3, stat, 6); - reset_checker_stat(my_checker3); - - elsif run("Test should fail on unknowns") then - get_checker_stat(stat); - mock(check_logger); - check_zero_one_hot("0000X"); - check_only_log(check_logger, "Zero one-hot check failed - Got 0_000X.", default_level); - - check_zero_one_hot(passed, "0000X"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Zero one-hot check failed - Got 0_000X.", default_level); - - passed := check_zero_one_hot("0000X"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Zero one-hot check failed - Got 0_000X.", default_level); - unmock(check_logger); - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 3); - reset_checker_stat; - - get_checker_stat(my_checker3, stat); - mock(get_logger(my_checker3)); - check_zero_one_hot(my_checker3, "0000X"); - check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_000X.", info); - - check_zero_one_hot(my_checker3, passed, "0000X"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_000X.", info); - - passed := check_zero_one_hot(my_checker3, "0000X"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_000X.", info); - unmock(get_logger(my_checker3)); - verify_passed_checks(my_checker3, stat, 0); - verify_failed_checks(my_checker3, stat, 3); - reset_checker_stat(my_checker3); - - elsif run("Test should be possible to use concurrently") then - test_concurrent_check(clk, check_zero_one_hot_in_1, default_checker); - - elsif run("Test should be possible to use concurrently with negative active clock edge") then - test_concurrent_check(clk, check_zero_one_hot_in_2, my_checker2, error, false); - - elsif run("Test should be possible to use concurrently with custom checker") then - test_concurrent_check(clk, check_zero_one_hot_in_3, my_checker3, info); - - elsif run("Test should pass on unknowns when not enabled") then - wait until rising_edge(clk); - wait for 1 ns; - get_checker_stat(stat); - apply_sequence("0000;100H", clk, check_zero_one_hot_in_1); - check_zero_one_hot_en_1 <= '0'; - apply_sequence("1001;100H;0000", clk, check_zero_one_hot_in_1); - check_zero_one_hot_en_1 <= '1'; - apply_sequence("0000;100H", clk, check_zero_one_hot_in_1); - check_zero_one_hot_en_1 <= 'L'; - apply_sequence("1001;100H;0000", clk, check_zero_one_hot_in_1); - check_zero_one_hot_en_1 <= 'H'; - apply_sequence("0000;100H", clk, check_zero_one_hot_in_1); - check_zero_one_hot_en_1 <= 'X'; - apply_sequence("1001;100H;0000", clk, check_zero_one_hot_in_1); - check_zero_one_hot_en_1 <= '1'; - wait for 1 ns; - verify_passed_checks(stat, 3); - verify_failed_checks(stat, 0); - - elsif run("Test should handle reversed and or offset expressions") then - get_checker_stat(stat); - check_zero_one_hot(reversed_and_offset_expr); - verify_passed_checks(stat, 1); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 2 us); - -end test_fixture; +-- This test suite verifies the check_zero_one_hot checker. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library ieee; +use ieee.std_logic_1164.all; +library vunit_lib; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use vunit_lib.runner_pkg.all; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use work.test_support.all; + +entity tb_check_zero_one_hot is + generic ( + runner_cfg : string); +end entity tb_check_zero_one_hot; + +architecture test_fixture of tb_check_zero_one_hot is + signal clk : std_logic := '0'; + + signal check_zero_one_hot_in_1, check_zero_one_hot_in_2, check_zero_one_hot_in_3 : std_logic_vector(3 downto 0) := (others => '0'); + signal check_zero_one_hot_en_1, check_zero_one_hot_en_2, check_zero_one_hot_en_3 : std_logic := '1'; + + constant my_checker2 : checker_t := new_checker("my_checker2"); + constant my_checker3 : checker_t := new_checker("my_checker3", default_log_level => info); + +begin + clock: process is + begin + while get_phase(runner_state) < test_runner_exit loop + clk <= '1', '0' after 5 ns; + wait for 10 ns; + end loop; + wait; + end process clock; + + check_zero_one_hot_1 : check_zero_one_hot(clk, check_zero_one_hot_en_1, check_zero_one_hot_in_1); + check_zero_one_hot_2 : check_zero_one_hot(my_checker2, clk, check_zero_one_hot_en_2, check_zero_one_hot_in_2, active_clock_edge => falling_edge); + check_zero_one_hot_3 : check_zero_one_hot(my_checker3, clk, check_zero_one_hot_en_3, check_zero_one_hot_in_3); + + check_zero_one_hot_runner : process + variable passed : boolean; + variable stat : checker_stat_t; + constant reversed_and_offset_expr : std_logic_vector(23 downto 20) := "1000"; + constant default_level : log_level_t := error; + + procedure test_concurrent_check ( + signal clk : in std_logic; + signal check_input : out std_logic_vector; + checker : checker_t; + constant level : in log_level_t := error; + constant active_rising_clock_edge : in boolean := true) is + begin + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + get_checker_stat(checker, stat); + apply_sequence("0000;LL00;1000;HL00", clk, check_input, active_rising_clock_edge); + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + verify_passed_checks(checker, stat, 4); + verify_failed_checks(checker, stat, 0); + mock(get_logger(checker)); + apply_sequence("1001;100H;000X", clk, check_input, active_rising_clock_edge); + wait until clock_edge(clk, active_rising_clock_edge); + wait for 1 ns; + check_log(get_logger(checker), "Zero one-hot check failed - Got 1001.", level); + check_log(get_logger(checker), "Zero one-hot check failed - Got 100H.", level); + check_log(get_logger(checker), "Zero one-hot check failed - Got 000X.", level); + unmock(get_logger(checker)); + verify_passed_checks(checker, stat, 4); + verify_failed_checks(checker, stat, 3); + reset_checker_stat(checker); + apply_sequence("0000", clk, check_input, active_rising_clock_edge); + end procedure test_concurrent_check; + + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test should pass on zero or one high bit") then + get_checker_stat(stat); + check_zero_one_hot("0000"); + check_zero_one_hot("LL00"); + check_zero_one_hot("1000"); + check_zero_one_hot("HL00"); + verify_passed_checks(stat, 4); + + get_checker_stat(my_checker3, stat); + check_zero_one_hot(my_checker3, "0000"); + check_zero_one_hot(my_checker3, "LL00"); + check_zero_one_hot(my_checker3, "1000"); + check_zero_one_hot(my_checker3, "HL00"); + verify_passed_checks(my_checker3, stat, 4); + + get_checker_stat(stat); + check_zero_one_hot(passed, "0000"); + assert_true(passed, "Should return pass = true on passing check"); + check_zero_one_hot(passed, "LL00"); + assert_true(passed, "Should return pass = true on passing check"); + check_zero_one_hot(passed, "1000"); + assert_true(passed, "Should return pass = true on passing check"); + check_zero_one_hot(passed, "HL00"); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(stat, 4); + + get_checker_stat(stat); + passed := check_zero_one_hot("0000"); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_zero_one_hot("LL00"); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_zero_one_hot("1000"); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_zero_one_hot("HL00"); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(stat, 4); + + get_checker_stat(my_checker3, stat); + check_zero_one_hot(my_checker3, passed, "0000"); + assert_true(passed, "Should return pass = true on passing check"); + check_zero_one_hot(my_checker3, passed, "LL00"); + assert_true(passed, "Should return pass = true on passing check"); + check_zero_one_hot(my_checker3, passed, "1000"); + assert_true(passed, "Should return pass = true on passing check"); + check_zero_one_hot(my_checker3, passed, "HL00"); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(my_checker3, stat, 4); + + get_checker_stat(my_checker3, stat); + passed := check_zero_one_hot(my_checker3, "0000"); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_zero_one_hot(my_checker3, "LL00"); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_zero_one_hot(my_checker3, "1000"); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_zero_one_hot(my_checker3, "HL00"); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(my_checker3, stat, 4); + + elsif run("Test pass message") then + mock(check_logger); + check_zero_one_hot("00000"); + check_only_log(check_logger, "Zero one-hot check passed - Got 0_0000.", pass); + + check_zero_one_hot("00000", ""); + check_only_log(check_logger, "Got 0_0000.", pass); + + check_zero_one_hot("00000", "Checking my data"); + check_only_log(check_logger, "Checking my data - Got 0_0000.", pass); + + check_zero_one_hot("00000", result("for my data")); + check_only_log(check_logger, "Zero one-hot check passed for my data - Got 0_0000.", pass); + unmock(check_logger); + + elsif run("Test should fail on more than one high bit") then + get_checker_stat(stat); + mock(check_logger); + check_zero_one_hot("01001"); + check_only_log(check_logger, "Zero one-hot check failed - Got 0_1001.", default_level); + + check_zero_one_hot("0100H"); + check_only_log(check_logger, "Zero one-hot check failed - Got 0_100H.", default_level); + + check_zero_one_hot(passed, "01001"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Zero one-hot check failed - Got 0_1001.", default_level); + + check_zero_one_hot(passed, "0100H"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Zero one-hot check failed - Got 0_100H.", default_level); + + passed := check_zero_one_hot("01001"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Zero one-hot check failed - Got 0_1001.", default_level); + + passed := check_zero_one_hot("0100H"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Zero one-hot check failed - Got 0_100H.", default_level); + unmock(check_logger); + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 6); + reset_checker_stat; + + get_checker_stat(my_checker3, stat); + mock(get_logger(my_checker3)); + check_zero_one_hot(my_checker3, "01001"); + check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_1001.", info); + + check_zero_one_hot(my_checker3, "0100H"); + check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_100H.", info); + + check_zero_one_hot(my_checker3, passed, "01001"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_1001.", info); + + check_zero_one_hot(my_checker3, passed, "0100H"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_100H.", info); + + passed := check_zero_one_hot(my_checker3, "01001"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_1001.", info); + + passed := check_zero_one_hot(my_checker3, "0100H"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_100H.", info); + unmock(get_logger(my_checker3)); + verify_passed_checks(my_checker3, stat, 0); + verify_failed_checks(my_checker3, stat, 6); + reset_checker_stat(my_checker3); + + elsif run("Test should fail on unknowns") then + get_checker_stat(stat); + mock(check_logger); + check_zero_one_hot("0000X"); + check_only_log(check_logger, "Zero one-hot check failed - Got 0_000X.", default_level); + + check_zero_one_hot(passed, "0000X"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Zero one-hot check failed - Got 0_000X.", default_level); + + passed := check_zero_one_hot("0000X"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Zero one-hot check failed - Got 0_000X.", default_level); + unmock(check_logger); + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 3); + reset_checker_stat; + + get_checker_stat(my_checker3, stat); + mock(get_logger(my_checker3)); + check_zero_one_hot(my_checker3, "0000X"); + check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_000X.", info); + + check_zero_one_hot(my_checker3, passed, "0000X"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_000X.", info); + + passed := check_zero_one_hot(my_checker3, "0000X"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(get_logger(my_checker3), "Zero one-hot check failed - Got 0_000X.", info); + unmock(get_logger(my_checker3)); + verify_passed_checks(my_checker3, stat, 0); + verify_failed_checks(my_checker3, stat, 3); + reset_checker_stat(my_checker3); + + elsif run("Test should be possible to use concurrently") then + test_concurrent_check(clk, check_zero_one_hot_in_1, default_checker); + + elsif run("Test should be possible to use concurrently with negative active clock edge") then + test_concurrent_check(clk, check_zero_one_hot_in_2, my_checker2, error, false); + + elsif run("Test should be possible to use concurrently with custom checker") then + test_concurrent_check(clk, check_zero_one_hot_in_3, my_checker3, info); + + elsif run("Test should pass on unknowns when not enabled") then + wait until rising_edge(clk); + wait for 1 ns; + get_checker_stat(stat); + apply_sequence("0000;100H", clk, check_zero_one_hot_in_1); + check_zero_one_hot_en_1 <= '0'; + apply_sequence("1001;100H;0000", clk, check_zero_one_hot_in_1); + check_zero_one_hot_en_1 <= '1'; + apply_sequence("0000;100H", clk, check_zero_one_hot_in_1); + check_zero_one_hot_en_1 <= 'L'; + apply_sequence("1001;100H;0000", clk, check_zero_one_hot_in_1); + check_zero_one_hot_en_1 <= 'H'; + apply_sequence("0000;100H", clk, check_zero_one_hot_in_1); + check_zero_one_hot_en_1 <= 'X'; + apply_sequence("1001;100H;0000", clk, check_zero_one_hot_in_1); + check_zero_one_hot_en_1 <= '1'; + wait for 1 ns; + verify_passed_checks(stat, 3); + verify_failed_checks(stat, 0); + + elsif run("Test should handle reversed and or offset expressions") then + get_checker_stat(stat); + check_zero_one_hot(reversed_and_offset_expr); + verify_passed_checks(stat, 1); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 2 us); + +end test_fixture; diff --git a/vunit/vhdl/check/test/tb_checker.vhd b/vunit/vhdl/check/test/tb_checker.vhd index 4e947fdc5..7eaa3e9f1 100644 --- a/vunit/vhdl/check/test/tb_checker.vhd +++ b/vunit/vhdl/check/test/tb_checker.vhd @@ -1,108 +1,108 @@ --- This test suite verifies basic check functionality. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -use vunit_lib.string_ops.all; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use vunit_lib.ansi_pkg.all; -use work.test_support.all; -use std.textio.all; - -use vunit_lib.logger_pkg.all; - -entity tb_checker is - generic ( - runner_cfg : string := ""; - output_path : string); -end entity; - -architecture test_fixture of tb_checker is -begin - test_runner : process - variable checker1, checker2 : checker_t; - variable stat, stat1, stat2 : checker_stat_t; - variable stat_before, stat_after : checker_stat_t; - variable my_checker : checker_t := new_checker("my_checker"); - variable my_logger : logger_t := get_logger(my_checker); - variable passed : boolean; - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test default checker") then - stat_before := get_checker_stat; - mock(check_logger); - - passing_check(default_checker, "Check true"); - check_only_log(check_logger, "Check true", pass); - - passing_check(default_checker); - check_only_log(check_logger, "", pass); - - failing_check(default_checker, "Custom error message"); - check_only_log(check_logger, "Custom error message", error); - - failing_check(default_checker, "Custom level", info); - check_only_log(check_logger, "Custom level", info); - - failing_check(default_checker, "Line and file name", info, 377, "some_file.vhd"); - check_only_log(check_logger, "Line and file name", info, - line_num => 377, file_name => "some_file.vhd"); - unmock(check_logger); - - stat_after := get_checker_stat; - assert_true(stat_after = stat_before + (5, 3, 2), "Expected 5 checks, 3 fail, and 2 pass but got " & to_string(stat_after - stat_before)); - reset_checker_stat; - - elsif run("Test custom checker") then - my_checker := new_checker("my_checker"); - my_logger := get_logger("my_checker"); - assert_true(get_full_name(get_logger(my_checker)) = get_full_name(my_logger)); - stat_before := get_checker_stat(my_checker); - - mock(my_logger); - passing_check(my_checker, "Check true"); - check_only_log(my_logger, "Check true", pass); - - failing_check(my_checker, "Custom error message"); - check_only_log(my_logger, "Custom error message", error); - - failing_check(my_checker, "Custom level", info); - check_only_log(my_logger, "Custom level", info); - - failing_check(my_checker, "Line and file name", info, 377, "some_file.vhd"); - check_only_log(my_logger, "Line and file name", info, - line_num => 377, file_name => "some_file.vhd"); - unmock(my_logger); - - stat_after := get_checker_stat(my_checker); - assert_true(stat_after = stat_before + (4, 3, 1), "Expected 4 checks, 3 fail, and 1 pass but got " & to_string(stat_after - stat_before)); - reset_checker_stat(my_checker); - - elsif run("Verify checker_stat_t functions and operators") then - assert_true(stat1 = (0, 0, 0), "Expected initial stat value = (0, 0, 0)"); - stat1 := (20, 13, 7); - stat2 := (11, 3, 8); - assert_true(stat1 + stat2 = (31, 16, 15), "Expected sum = (31, 16, 15)"); - passed := to_string(stat1) = "checker_stat'(n_checks => 20, n_failed => 13, n_passed => 7)"; - assert_true(passed, "Format error of checker_stat_t. Got:" & to_string(stat1)); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 2 us); - -end; +-- This test suite verifies basic check functionality. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +use vunit_lib.string_ops.all; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use vunit_lib.ansi_pkg.all; +use work.test_support.all; +use std.textio.all; + +use vunit_lib.logger_pkg.all; + +entity tb_checker is + generic ( + runner_cfg : string := ""; + output_path : string); +end entity; + +architecture test_fixture of tb_checker is +begin + test_runner : process + variable checker1, checker2 : checker_t; + variable stat, stat1, stat2 : checker_stat_t; + variable stat_before, stat_after : checker_stat_t; + variable my_checker : checker_t := new_checker("my_checker"); + variable my_logger : logger_t := get_logger(my_checker); + variable passed : boolean; + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test default checker") then + stat_before := get_checker_stat; + mock(check_logger); + + passing_check(default_checker, "Check true"); + check_only_log(check_logger, "Check true", pass); + + passing_check(default_checker); + check_only_log(check_logger, "", pass); + + failing_check(default_checker, "Custom error message"); + check_only_log(check_logger, "Custom error message", error); + + failing_check(default_checker, "Custom level", info); + check_only_log(check_logger, "Custom level", info); + + failing_check(default_checker, "Line and file name", info, 377, "some_file.vhd"); + check_only_log(check_logger, "Line and file name", info, + line_num => 377, file_name => "some_file.vhd"); + unmock(check_logger); + + stat_after := get_checker_stat; + assert_true(stat_after = stat_before + (5, 3, 2), "Expected 5 checks, 3 fail, and 2 pass but got " & to_string(stat_after - stat_before)); + reset_checker_stat; + + elsif run("Test custom checker") then + my_checker := new_checker("my_checker"); + my_logger := get_logger("my_checker"); + assert_true(get_full_name(get_logger(my_checker)) = get_full_name(my_logger)); + stat_before := get_checker_stat(my_checker); + + mock(my_logger); + passing_check(my_checker, "Check true"); + check_only_log(my_logger, "Check true", pass); + + failing_check(my_checker, "Custom error message"); + check_only_log(my_logger, "Custom error message", error); + + failing_check(my_checker, "Custom level", info); + check_only_log(my_logger, "Custom level", info); + + failing_check(my_checker, "Line and file name", info, 377, "some_file.vhd"); + check_only_log(my_logger, "Line and file name", info, + line_num => 377, file_name => "some_file.vhd"); + unmock(my_logger); + + stat_after := get_checker_stat(my_checker); + assert_true(stat_after = stat_before + (4, 3, 1), "Expected 4 checks, 3 fail, and 1 pass but got " & to_string(stat_after - stat_before)); + reset_checker_stat(my_checker); + + elsif run("Verify checker_stat_t functions and operators") then + assert_true(stat1 = (0, 0, 0), "Expected initial stat value = (0, 0, 0)"); + stat1 := (20, 13, 7); + stat2 := (11, 3, 8); + assert_true(stat1 + stat2 = (31, 16, 15), "Expected sum = (31, 16, 15)"); + passed := to_string(stat1) = "checker_stat'(n_checks => 20, n_failed => 13, n_passed => 7)"; + assert_true(passed, "Format error of checker_stat_t. Got:" & to_string(stat1)); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 2 us); + +end; diff --git a/vunit/vhdl/check/test/tb_deprecated.vhd b/vunit/vhdl/check/test/tb_deprecated.vhd index e5dced154..3ca8343d8 100644 --- a/vunit/vhdl/check/test/tb_deprecated.vhd +++ b/vunit/vhdl/check/test/tb_deprecated.vhd @@ -1,174 +1,174 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com -------------------------------------------------------------------------------- --- This testbench verifies deprecated interfaces -------------------------------------------------------------------------------- - -library vunit_lib; -use vunit_lib.run_pkg.all; - -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.log_handler_pkg.all; -use vunit_lib.check_pkg.all; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_deprecated_pkg.all; -use vunit_lib.log_deprecated_pkg.all; -use vunit_lib.core_pkg.all; -use vunit_lib.ansi_pkg.all; -library logging_tb_lib; -use logging_tb_lib.test_support_pkg.all; - -entity tb_deprecated is - generic ( - runner_cfg : string); -end entity; - -architecture a of tb_deprecated is -begin - test_runner : process - variable my_checker : checker_t; - variable my_checker_logger : logger_t; - - variable found_errors : boolean; - - constant deprecated_logger_init_msg : string := - "Using deprecated procedure logger_init. Using best effort mapping to contemporary functionality"; - constant deprecated_checker_init_msg : string := - "Using deprecated procedure checker_init. Using best effort mapping to contemporary functionality"; - constant empty_msg : string := - "Empty string checker names not supported. Using ""anonymous0"""; - - procedure check_warnings(is_new : boolean) is - begin - check_log(default_logger, deprecated_checker_init_msg, warning); - if is_new then - check_log(default_logger, empty_msg, warning); - end if; - check_log(default_logger, deprecated_logger_init_msg, warning); - end procedure check_warnings; - - procedure check_checker_init(checker : checker_t; name : string; is_new : boolean) is - variable logger : logger_t; - variable checker_int : checker_t := checker; - begin - check_warnings(is_new); - - assert_true(get_default_log_level(checker_int) = error); - - logger := get_logger(checker_int); - assert_equal(get_name(get_logger(checker_int)), name); - - assert_equal(num_log_handlers(logger), 2); - assert_equal(get_file_name(get_file_handler(logger)), "error.csv"); - assert_equal(get_file_name(get_display_handler(logger)), stdout_file_name); - - check_format(logger, get_display_handler(logger), level); - check_format(logger, get_file_handler(logger), off); - end; - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - mock(check_logger); - mock(default_logger); - - if run("Test initializing checker") then - checker_init(my_checker); - check_checker_init(my_checker, "anonymous0", true); - - checker_init; - check_checker_init(default_checker, "check", false); - elsif run("Test changing checker default level") then - checker_init(my_checker); - check_warnings(true); - checker_init(my_checker, default_level => failure); - check_warnings(false); - assert_true(get_default_log_level(my_checker) = failure); - - checker_init; - check_warnings(false); - checker_init(default_level => failure); - check_warnings(false); - assert_true(get_default_log_level(default_checker) = failure); - elsif run("Test changing checker name") then - mock_core_failure; - - checker_init(my_checker); - check_warnings(true); - checker_init(my_checker, default_src => "my_checker"); - check_log(default_logger, deprecated_checker_init_msg, warning); - - checker_init; - check_warnings(false); - checker_init(default_src => "my_checker"); - check_log(default_logger, deprecated_checker_init_msg, warning); - - elsif run("Test enabling and disabling of pass messages for custom checker") then - my_checker := new_checker("my_checker"); - my_checker_logger := get_logger(my_checker); - - assert_false(is_visible(my_checker_logger, display_handler, pass)); - enable_pass_msg(my_checker, display_handler); - - assert_true(is_visible(my_checker_logger, display_handler, pass)); - - disable_pass_msg(my_checker, display_handler); - assert_false(is_visible(my_checker_logger, display_handler, pass)); - - elsif run("Test enabling and disabling of pass messages for default checker") then - assert_false(is_visible(check_logger, display_handler, pass)); - enable_pass_msg(display_handler); - - assert_true(is_visible(check_logger, display_handler, pass)); - - disable_pass_msg(display_handler); - assert_false(is_visible(check_logger, display_handler, pass)); - - elsif run("Test found errors subprograms") then - my_checker := new_checker("my_checker"); - - reset_checker_stat(my_checker); - reset_checker_stat; - - assert_false(checker_found_errors); - check_only_log(default_logger, "Using deprecated checker_found_errors. Use get_checker_stat instead.", warning); - - checker_found_errors(found_errors); - assert_false(found_errors); - check_only_log(default_logger, "Using deprecated checker_found_errors. Use get_checker_stat instead.", warning); - - checker_found_errors(my_checker, found_errors); - assert_false(found_errors); - check_only_log(default_logger, "Using deprecated checker_found_errors. Use get_checker_stat instead.", warning); - - check_failed(level => warning); - assert_true(checker_found_errors); - check_log(check_logger, "Unconditional check failed.", warning); - check_only_log(default_logger, "Using deprecated checker_found_errors. Use get_checker_stat instead.", warning); - - checker_found_errors(found_errors); - assert_true(found_errors); - check_only_log(default_logger, "Using deprecated checker_found_errors. Use get_checker_stat instead.", warning); - - reset_checker_stat; - - check_failed(my_checker, level => warning); - checker_found_errors(my_checker, found_errors); - assert_true(found_errors); - check_only_log(default_logger, "Using deprecated checker_found_errors. Use get_checker_stat instead.", warning); - - reset_checker_stat(my_checker); - - end if; - - unmock(default_logger); - unmock(check_logger); - end loop; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +------------------------------------------------------------------------------- +-- This testbench verifies deprecated interfaces +------------------------------------------------------------------------------- + +library vunit_lib; +use vunit_lib.run_pkg.all; + +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.log_handler_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_deprecated_pkg.all; +use vunit_lib.log_deprecated_pkg.all; +use vunit_lib.core_pkg.all; +use vunit_lib.ansi_pkg.all; +library logging_tb_lib; +use logging_tb_lib.test_support_pkg.all; + +entity tb_deprecated is + generic ( + runner_cfg : string); +end entity; + +architecture a of tb_deprecated is +begin + test_runner : process + variable my_checker : checker_t; + variable my_checker_logger : logger_t; + + variable found_errors : boolean; + + constant deprecated_logger_init_msg : string := + "Using deprecated procedure logger_init. Using best effort mapping to contemporary functionality"; + constant deprecated_checker_init_msg : string := + "Using deprecated procedure checker_init. Using best effort mapping to contemporary functionality"; + constant empty_msg : string := + "Empty string checker names not supported. Using ""anonymous0"""; + + procedure check_warnings(is_new : boolean) is + begin + check_log(default_logger, deprecated_checker_init_msg, warning); + if is_new then + check_log(default_logger, empty_msg, warning); + end if; + check_log(default_logger, deprecated_logger_init_msg, warning); + end procedure check_warnings; + + procedure check_checker_init(checker : checker_t; name : string; is_new : boolean) is + variable logger : logger_t; + variable checker_int : checker_t := checker; + begin + check_warnings(is_new); + + assert_true(get_default_log_level(checker_int) = error); + + logger := get_logger(checker_int); + assert_equal(get_name(get_logger(checker_int)), name); + + assert_equal(num_log_handlers(logger), 2); + assert_equal(get_file_name(get_file_handler(logger)), "error.csv"); + assert_equal(get_file_name(get_display_handler(logger)), stdout_file_name); + + check_format(logger, get_display_handler(logger), level); + check_format(logger, get_file_handler(logger), off); + end; + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + mock(check_logger); + mock(default_logger); + + if run("Test initializing checker") then + checker_init(my_checker); + check_checker_init(my_checker, "anonymous0", true); + + checker_init; + check_checker_init(default_checker, "check", false); + elsif run("Test changing checker default level") then + checker_init(my_checker); + check_warnings(true); + checker_init(my_checker, default_level => failure); + check_warnings(false); + assert_true(get_default_log_level(my_checker) = failure); + + checker_init; + check_warnings(false); + checker_init(default_level => failure); + check_warnings(false); + assert_true(get_default_log_level(default_checker) = failure); + elsif run("Test changing checker name") then + mock_core_failure; + + checker_init(my_checker); + check_warnings(true); + checker_init(my_checker, default_src => "my_checker"); + check_log(default_logger, deprecated_checker_init_msg, warning); + + checker_init; + check_warnings(false); + checker_init(default_src => "my_checker"); + check_log(default_logger, deprecated_checker_init_msg, warning); + + elsif run("Test enabling and disabling of pass messages for custom checker") then + my_checker := new_checker("my_checker"); + my_checker_logger := get_logger(my_checker); + + assert_false(is_visible(my_checker_logger, display_handler, pass)); + enable_pass_msg(my_checker, display_handler); + + assert_true(is_visible(my_checker_logger, display_handler, pass)); + + disable_pass_msg(my_checker, display_handler); + assert_false(is_visible(my_checker_logger, display_handler, pass)); + + elsif run("Test enabling and disabling of pass messages for default checker") then + assert_false(is_visible(check_logger, display_handler, pass)); + enable_pass_msg(display_handler); + + assert_true(is_visible(check_logger, display_handler, pass)); + + disable_pass_msg(display_handler); + assert_false(is_visible(check_logger, display_handler, pass)); + + elsif run("Test found errors subprograms") then + my_checker := new_checker("my_checker"); + + reset_checker_stat(my_checker); + reset_checker_stat; + + assert_false(checker_found_errors); + check_only_log(default_logger, "Using deprecated checker_found_errors. Use get_checker_stat instead.", warning); + + checker_found_errors(found_errors); + assert_false(found_errors); + check_only_log(default_logger, "Using deprecated checker_found_errors. Use get_checker_stat instead.", warning); + + checker_found_errors(my_checker, found_errors); + assert_false(found_errors); + check_only_log(default_logger, "Using deprecated checker_found_errors. Use get_checker_stat instead.", warning); + + check_failed(level => warning); + assert_true(checker_found_errors); + check_log(check_logger, "Unconditional check failed.", warning); + check_only_log(default_logger, "Using deprecated checker_found_errors. Use get_checker_stat instead.", warning); + + checker_found_errors(found_errors); + assert_true(found_errors); + check_only_log(default_logger, "Using deprecated checker_found_errors. Use get_checker_stat instead.", warning); + + reset_checker_stat; + + check_failed(my_checker, level => warning); + checker_found_errors(my_checker, found_errors); + assert_true(found_errors); + check_only_log(default_logger, "Using deprecated checker_found_errors. Use get_checker_stat instead.", warning); + + reset_checker_stat(my_checker); + + end if; + + unmock(default_logger); + unmock(check_logger); + end loop; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/vhdl/check/test/tb_result.vhd b/vunit/vhdl/check/test/tb_result.vhd index 97bd9130d..a0ae7ba39 100644 --- a/vunit/vhdl/check/test/tb_result.vhd +++ b/vunit/vhdl/check/test/tb_result.vhd @@ -1,60 +1,60 @@ --- This test suite verifies basic check functionality. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library vunit_lib; -use vunit_lib.run_pkg.all; -use vunit_lib.string_ops.all; -use vunit_lib.check_pkg.all; -use vunit_lib.checker_pkg.all; -use work.test_support.all; - -entity tb_result is - generic ( - runner_cfg : string := ""); -end entity; - -architecture test_fixture of tb_result is -begin - test_runner : process - constant punctuation_marks_not_preceeded_by_space : string := ".,:;?!"; - variable stat : checker_stat_t; - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test that result returns result tag by default") then - assert_true(result = check_result_tag); - - elsif run("Test that result returns result tag on empty message") then - assert_true(result("") = check_result_tag); - - elsif run("Test that result returns result tag + message if the message starts with a punctuation mark") then - for i in punctuation_marks_not_preceeded_by_space'range loop - assert_true(result(punctuation_marks_not_preceeded_by_space(i) & "Foo") = - check_result_tag & punctuation_marks_not_preceeded_by_space(i) & "Foo"); - end loop; - - elsif run("Test that result returns result tag + space + message if the message doesn't start with a punctuation mark") then - for c in character'left to character'right loop - if find(punctuation_marks_not_preceeded_by_space, c) = 0 then - assert_true(result(c & "Foo") = - check_result_tag & " " & c & "Foo"); - end if; - end loop; - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 2 us); - -end; +-- This test suite verifies basic check functionality. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library vunit_lib; +use vunit_lib.run_pkg.all; +use vunit_lib.string_ops.all; +use vunit_lib.check_pkg.all; +use vunit_lib.checker_pkg.all; +use work.test_support.all; + +entity tb_result is + generic ( + runner_cfg : string := ""); +end entity; + +architecture test_fixture of tb_result is +begin + test_runner : process + constant punctuation_marks_not_preceeded_by_space : string := ".,:;?!"; + variable stat : checker_stat_t; + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test that result returns result tag by default") then + assert_true(result = check_result_tag); + + elsif run("Test that result returns result tag on empty message") then + assert_true(result("") = check_result_tag); + + elsif run("Test that result returns result tag + message if the message starts with a punctuation mark") then + for i in punctuation_marks_not_preceeded_by_space'range loop + assert_true(result(punctuation_marks_not_preceeded_by_space(i) & "Foo") = + check_result_tag & punctuation_marks_not_preceeded_by_space(i) & "Foo"); + end loop; + + elsif run("Test that result returns result tag + space + message if the message doesn't start with a punctuation mark") then + for c in character'left to character'right loop + if find(punctuation_marks_not_preceeded_by_space, c) = 0 then + assert_true(result(c & "Foo") = + check_result_tag & " " & c & "Foo"); + end if; + end loop; + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 2 us); + +end; diff --git a/vunit/vhdl/check/test/test_support.vhd b/vunit/vhdl/check/test/test_support.vhd index 7d4975d44..1647224e7 100644 --- a/vunit/vhdl/check/test/test_support.vhd +++ b/vunit/vhdl/check/test/test_support.vhd @@ -1,201 +1,201 @@ --- This package provides various test support for the checker test suites. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -library vunit_lib; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use std.textio.all; - -package test_support is - - procedure assert_true ( - constant expr : in boolean; - constant msg : in string := ""; - constant level : in severity_level := error); - - procedure verify_passed_checks ( - variable stat : inout checker_stat_t; - constant expected_n_passed : in integer := -1); - - procedure verify_passed_checks ( - checker : checker_t; - variable stat : inout checker_stat_t; - constant expected_n_passed : in integer := -1); - - procedure verify_failed_checks ( - variable stat : inout checker_stat_t; - constant expected_n_failed : in integer := -1); - - procedure verify_failed_checks ( - checker : checker_t; - variable stat : inout checker_stat_t; - constant expected_n_failed : in integer := -1); - - procedure apply_sequence ( - constant seq : in string; - signal clk : in std_logic; - signal data : out std_logic; - constant active_rising_clk_edge : in boolean := true); - - procedure apply_sequence ( - constant seq : in string; - signal clk : in std_logic; - signal data : out std_logic_vector; - constant active_rising_clk_edge : in boolean := true); - - function clock_edge ( - signal clk : in std_logic; - constant wait_rising_edge : in boolean := true) - return boolean; - -end package test_support; - -package body test_support is - procedure assert_true ( - constant expr : in boolean; - constant msg : in string := ""; - constant level : in severity_level := error) is - begin - if not expr then - assert false report msg severity level; - end if; - end procedure assert_true; - - procedure verify_passed_checks ( - variable stat : inout checker_stat_t; - constant expected_n_passed : in integer := -1) is - begin - verify_passed_checks(default_checker, stat, expected_n_passed); - end; - - procedure verify_passed_checks ( - checker : checker_t; - variable stat : inout checker_stat_t; - constant expected_n_passed : in integer := -1) is - variable new_stat : checker_stat_t; - begin - get_checker_stat(checker, new_stat); - if expected_n_passed = -1 then - assert_true(new_stat.n_passed > stat.n_passed, "No passed checks registered."); - else - assert_true(new_stat.n_passed = stat.n_passed + expected_n_passed, - "Not expected number of passed checks registered. Got " & integer'image(new_stat.n_passed) - & " but expected " & integer'image(stat.n_passed + expected_n_passed) & "."); - end if; - end; - - procedure verify_failed_checks ( - variable stat : inout checker_stat_t; - constant expected_n_failed : in integer := -1) is - begin - verify_failed_checks(default_checker, stat, expected_n_failed); - end; - - procedure verify_failed_checks ( - checker : checker_t; - variable stat : inout checker_stat_t; - constant expected_n_failed : in integer := -1) is - variable new_stat : checker_stat_t; - begin - get_checker_stat(checker, new_stat); - if expected_n_failed = -1 then - assert_true(new_stat.n_failed > stat.n_failed, "No failed checks registered."); - else - assert_true(new_stat.n_failed = stat.n_failed + expected_n_failed, - "Not expected number of failed checks registered. Got " & integer'image(new_stat.n_failed) - & " but expected " & integer'image(stat.n_failed + expected_n_failed) & "."); - end if; - end; - - function is_std_logic ( - constant c : character) - return boolean is - begin - for i in 0 to 8 loop - if c = std_logic'image(std_logic'val(i))(2) then - return true; - end if; - end loop; - return false; - end; - - procedure apply_sequence ( - constant seq : in string; - signal clk : in std_logic; - signal data : out std_logic; - constant active_rising_clk_edge : in boolean := true) is - begin - for i in seq'range loop - if is_std_logic(seq(i)) then - data <= std_logic'value("'" & seq(i) & "'"); - end if; - if i /= seq'right then - if active_rising_clk_edge then - wait until rising_edge(clk); - else - wait until falling_edge(clk); - end if; - end if; - end loop; - end procedure apply_sequence; - - procedure apply_sequence ( - constant seq : in string; - signal clk : in std_logic; - signal data : out std_logic_vector; - constant active_rising_clk_edge : in boolean := true) is - variable i : natural := seq'left; - variable delimiters : natural := 0; - variable j : integer := 0; - begin - while i <= seq'right loop - j := data'left; - delimiters := 0; - while (j <= data'high) and (j >= data'low) loop - if data'ascending then - if is_std_logic(seq(i + delimiters + j - data'left)) then - data(j) <= std_logic'value("'" & seq(i + delimiters + j - data'left) & "'"); - j := j + 1; - else - delimiters := delimiters + 1; - end if; - else - if is_std_logic(seq(i + delimiters - j + data'left)) then - data(j) <= std_logic'value("'" & seq(i + delimiters - j + data'left) & "'"); - j := j - 1; - else - delimiters := delimiters + 1; - end if; - end if; - end loop; - i := i + data'length + delimiters; - if i <= seq'right then - if active_rising_clk_edge then - wait until rising_edge(clk); - else - wait until falling_edge(clk); - end if; - end if; - end loop; - end procedure apply_sequence; - - function clock_edge ( - signal clk : in std_logic; - constant wait_rising_edge : in boolean := true) - return boolean is - begin - if wait_rising_edge then - return rising_edge(clk); - else - return falling_edge(clk); - end if; - end clock_edge; - -end package body test_support; +-- This package provides various test support for the checker test suites. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +library vunit_lib; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use std.textio.all; + +package test_support is + + procedure assert_true ( + constant expr : in boolean; + constant msg : in string := ""; + constant level : in severity_level := error); + + procedure verify_passed_checks ( + variable stat : inout checker_stat_t; + constant expected_n_passed : in integer := -1); + + procedure verify_passed_checks ( + checker : checker_t; + variable stat : inout checker_stat_t; + constant expected_n_passed : in integer := -1); + + procedure verify_failed_checks ( + variable stat : inout checker_stat_t; + constant expected_n_failed : in integer := -1); + + procedure verify_failed_checks ( + checker : checker_t; + variable stat : inout checker_stat_t; + constant expected_n_failed : in integer := -1); + + procedure apply_sequence ( + constant seq : in string; + signal clk : in std_logic; + signal data : out std_logic; + constant active_rising_clk_edge : in boolean := true); + + procedure apply_sequence ( + constant seq : in string; + signal clk : in std_logic; + signal data : out std_logic_vector; + constant active_rising_clk_edge : in boolean := true); + + function clock_edge ( + signal clk : in std_logic; + constant wait_rising_edge : in boolean := true) + return boolean; + +end package test_support; + +package body test_support is + procedure assert_true ( + constant expr : in boolean; + constant msg : in string := ""; + constant level : in severity_level := error) is + begin + if not expr then + assert false report msg severity level; + end if; + end procedure assert_true; + + procedure verify_passed_checks ( + variable stat : inout checker_stat_t; + constant expected_n_passed : in integer := -1) is + begin + verify_passed_checks(default_checker, stat, expected_n_passed); + end; + + procedure verify_passed_checks ( + checker : checker_t; + variable stat : inout checker_stat_t; + constant expected_n_passed : in integer := -1) is + variable new_stat : checker_stat_t; + begin + get_checker_stat(checker, new_stat); + if expected_n_passed = -1 then + assert_true(new_stat.n_passed > stat.n_passed, "No passed checks registered."); + else + assert_true(new_stat.n_passed = stat.n_passed + expected_n_passed, + "Not expected number of passed checks registered. Got " & integer'image(new_stat.n_passed) + & " but expected " & integer'image(stat.n_passed + expected_n_passed) & "."); + end if; + end; + + procedure verify_failed_checks ( + variable stat : inout checker_stat_t; + constant expected_n_failed : in integer := -1) is + begin + verify_failed_checks(default_checker, stat, expected_n_failed); + end; + + procedure verify_failed_checks ( + checker : checker_t; + variable stat : inout checker_stat_t; + constant expected_n_failed : in integer := -1) is + variable new_stat : checker_stat_t; + begin + get_checker_stat(checker, new_stat); + if expected_n_failed = -1 then + assert_true(new_stat.n_failed > stat.n_failed, "No failed checks registered."); + else + assert_true(new_stat.n_failed = stat.n_failed + expected_n_failed, + "Not expected number of failed checks registered. Got " & integer'image(new_stat.n_failed) + & " but expected " & integer'image(stat.n_failed + expected_n_failed) & "."); + end if; + end; + + function is_std_logic ( + constant c : character) + return boolean is + begin + for i in 0 to 8 loop + if c = std_logic'image(std_logic'val(i))(2) then + return true; + end if; + end loop; + return false; + end; + + procedure apply_sequence ( + constant seq : in string; + signal clk : in std_logic; + signal data : out std_logic; + constant active_rising_clk_edge : in boolean := true) is + begin + for i in seq'range loop + if is_std_logic(seq(i)) then + data <= std_logic'value("'" & seq(i) & "'"); + end if; + if i /= seq'right then + if active_rising_clk_edge then + wait until rising_edge(clk); + else + wait until falling_edge(clk); + end if; + end if; + end loop; + end procedure apply_sequence; + + procedure apply_sequence ( + constant seq : in string; + signal clk : in std_logic; + signal data : out std_logic_vector; + constant active_rising_clk_edge : in boolean := true) is + variable i : natural := seq'left; + variable delimiters : natural := 0; + variable j : integer := 0; + begin + while i <= seq'right loop + j := data'left; + delimiters := 0; + while (j <= data'high) and (j >= data'low) loop + if data'ascending then + if is_std_logic(seq(i + delimiters + j - data'left)) then + data(j) <= std_logic'value("'" & seq(i + delimiters + j - data'left) & "'"); + j := j + 1; + else + delimiters := delimiters + 1; + end if; + else + if is_std_logic(seq(i + delimiters - j + data'left)) then + data(j) <= std_logic'value("'" & seq(i + delimiters - j + data'left) & "'"); + j := j - 1; + else + delimiters := delimiters + 1; + end if; + end if; + end loop; + i := i + data'length + delimiters; + if i <= seq'right then + if active_rising_clk_edge then + wait until rising_edge(clk); + else + wait until falling_edge(clk); + end if; + end if; + end loop; + end procedure apply_sequence; + + function clock_edge ( + signal clk : in std_logic; + constant wait_rising_edge : in boolean := true) + return boolean is + begin + if wait_rising_edge then + return rising_edge(clk); + else + return falling_edge(clk); + end if; + end clock_edge; + +end package body test_support; diff --git a/vunit/vhdl/check/tools/generate_check_equal.py b/vunit/vhdl/check/tools/generate_check_equal.py index 57ce25c1f..8a9ae4f82 100644 --- a/vunit/vhdl/check/tools/generate_check_equal.py +++ b/vunit/vhdl/check/tools/generate_check_equal.py @@ -1,627 +1,627 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from string import Template - -api_template = """ procedure check_equal( - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - variable pass : out boolean; - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_equal( - constant checker : in checker_t; - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_equal( - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_equal( - constant checker : in checker_t; - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - -""" - -impl_template = """ procedure check_equal( - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - variable pass : out boolean; - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if got = expected then - pass := true; - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Equality check passed", msg, - "Got " & $got_str & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Equality check failed", msg, - "Got " & $got_str & ". " & - "Expected " & $expected_str & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_equal( - constant checker : in checker_t; - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_equal( - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_equal( - constant checker : in checker_t; - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_equal(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - -""" - -test_template = """\ - - elsif run("Test should pass on $left_type equal $right_type") then - get_checker_stat(stat); - check_equal($left_pass, $right_pass); - check_equal(passed, $left_pass, $right_pass); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_equal($left_pass, $right_pass); - assert_true(passed, "Should return pass = true on passing check"); - check_equal($left_min, $right_min); - check_equal($left_max, $right_max); - verify_passed_checks(stat, 5); - - get_checker_stat(my_checker, stat); - check_equal(my_checker, $left_pass, $right_pass); - check_equal(my_checker, passed, $left_pass, $right_pass); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_equal(my_checker, $left_pass, $right_pass); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(my_checker, stat, 3); - - elsif run("Test pass message on $left_type equal $right_type") then - mock(check_logger); - check_equal($left_pass, $right_pass); - check_only_log(check_logger, "Equality check passed - Got $left_pass_str.", pass); - - check_equal($left_pass, $right_pass, ""); - check_only_log(check_logger, "Got $left_pass_str.", pass); - - check_equal($left_pass, $right_pass, "Checking my data"); - check_only_log(check_logger, "Checking my data - Got $left_pass_str.", pass); - - check_equal($left_pass, $right_pass, result("for my data")); - check_only_log(check_logger, "Equality check passed for my data - Got $left_pass_str.", pass); - unmock(check_logger); - - elsif run("Test should fail on $left_type not equal $right_type") then - get_checker_stat(stat); - mock(check_logger); - check_equal($left_pass, $right_fail); - check_only_log(check_logger, "Equality check failed - Got $left_pass_str. Expected $fail_str.", - default_level); - - check_equal($left_pass, $right_fail, ""); - check_only_log(check_logger, "Got $left_pass_str. Expected $fail_str.", default_level); - - check_equal($left_pass, $right_fail, "Checking my data"); - check_only_log(check_logger, "Checking my data - Got $left_pass_str. Expected $fail_str.", default_level); - - check_equal($left_pass, $right_fail, result("for my data")); - check_only_log(check_logger, "Equality check failed for my data - Got $left_pass_str. Expected $fail_str.", - default_level); - - check_equal(passed, $left_pass, $right_fail); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Equality check failed - Got $left_pass_str. Expected $fail_str.", - default_level); - - passed := check_equal($left_pass, $right_fail); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Equality check failed - Got $left_pass_str. Expected $fail_str.", - default_level); - unmock(check_logger); - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 6); - reset_checker_stat; - - get_checker_stat(my_checker, stat); - mock(my_logger); - check_equal(my_checker, $left_pass, $right_fail); - check_only_log(my_logger, "Equality check failed - Got $left_pass_str. Expected $fail_str.", default_level); - - check_equal(my_checker, passed, $left_pass, $right_fail); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(my_logger, "Equality check failed - Got $left_pass_str. Expected $fail_str.", default_level); - - passed := check_equal(my_checker, $left_pass, $right_fail); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(my_logger, "Equality check failed - Got $left_pass_str. Expected $fail_str.", default_level); - - unmock(my_logger); - verify_passed_checks(my_checker, stat, 0); - verify_failed_checks(my_checker, stat, 3); - reset_checker_stat(my_checker); -""" - -combinations = [ - ('unsigned', 'unsigned', - """unsigned'(X"A5")""", """unsigned'(X"A5")""", - "to_unsigned(natural'left,31)", "to_unsigned(natural'left,31)", - "to_unsigned(natural'right,31)", "to_unsigned(natural'right,31)", - """unsigned'(X"5A")""", - "1010_0101 (165)", "1010_0101 (165)", - "0101_1010 (90)"), - ('unsigned', 'natural', - """unsigned'(X"A5")""", "natural'(165)", - "to_unsigned(natural'left,31)", "natural'left", - "to_unsigned(natural'right,31)", "natural'right", - "natural'(90)", - "1010_0101 (165)", "165 (1010_0101)", - "90 (0101_1010)"), - ('natural', 'unsigned', - "natural'(165)", """unsigned'(X"A5")""", - "natural'left", "to_unsigned(natural'left,31)", - "natural'right", "to_unsigned(natural'right,31)", - """unsigned'(X"5A")""", - "165 (1010_0101)", "1010_0101 (165)", - "0101_1010 (90)"), - ('unsigned', 'std_logic_vector', - """unsigned'(X"A5")""", """std_logic_vector'(X"A5")""", - "to_unsigned(natural'left,31)", "std_logic_vector(to_unsigned(natural'left,31))", - "to_unsigned(natural'right,31)", "std_logic_vector(to_unsigned(natural'right,31))", - """std_logic_vector'(X"5A")""", - "1010_0101 (165)", "1010_0101 (165)", - "0101_1010 (90)"), - ('std_logic_vector', 'unsigned', - """std_logic_vector'(X"A5")""", """unsigned'(X"A5")""", - "std_logic_vector(to_unsigned(natural'left,31))", "to_unsigned(natural'left,31)", - "std_logic_vector(to_unsigned(natural'right,31))", "to_unsigned(natural'right,31)", - """unsigned'(X"5A")""", - "1010_0101 (165)", "1010_0101 (165)", - "0101_1010 (90)"), - ('std_logic_vector', 'std_logic_vector', - """std_logic_vector'(X"A5")""", """std_logic_vector'(X"A5")""", - "std_logic_vector(to_unsigned(natural'left,31))", "std_logic_vector(to_unsigned(natural'left,31))", - "std_logic_vector(to_unsigned(natural'right,31))", "std_logic_vector(to_unsigned(natural'right,31))", - """std_logic_vector'(X"5A")""", - "1010_0101 (165)", "1010_0101 (165)", - "0101_1010 (90)"), - ('std_logic_vector', 'natural', - """std_logic_vector'(X"A5")""", "natural'(165)", - "std_logic_vector(to_unsigned(natural'left,31))", "natural'left", - "std_logic_vector(to_unsigned(natural'right,31))", "natural'right", - "natural'(90)", - "1010_0101 (165)", "165 (1010_0101)", - "90 (0101_1010)"), - ('natural', 'std_logic_vector', - "natural'(165)", """std_logic_vector'(X"A5")""", - "natural'left", "std_logic_vector(to_unsigned(natural'left,31))", - "natural'right", "std_logic_vector(to_unsigned(natural'right,31))", - """std_logic_vector'(X"5A")""", - "165 (1010_0101)", "1010_0101 (165)", - "0101_1010 (90)"), - ('signed', 'signed', - """signed'(X"A5")""", """signed'(X"A5")""", - "to_signed(integer'left,32)", "to_signed(integer'left,32)", - "to_signed(integer'right,32)", "to_signed(integer'right,32)", - """signed'(X"5A")""", - "1010_0101 (-91)", "1010_0101 (-91)", - "0101_1010 (90)"), - ('signed', 'integer', - """signed'(X"A5")""", "integer'(-91)", - "to_signed(integer'left,32)", "integer'left", - "to_signed(integer'right,32)", "integer'right", - "integer'(90)", - "1010_0101 (-91)", "-91 (1010_0101)", - "90 (0101_1010)"), - ('integer', 'signed', - "integer'(-91)", """signed'(X"A5")""", - "integer'left", "to_signed(integer'left,32)", - "integer'right", "to_signed(integer'right,32)", - """signed'(X"5A")""", - "-91 (1010_0101)", "1010_0101 (-91)", - "0101_1010 (90)"), - ('integer', 'integer', - "integer'(-91)", "integer'(-91)", - "integer'left", "integer'left", - "integer'right", "integer'right", - "integer'(90)", - "-91", "-91", - "90"), - ('std_logic', 'std_logic', - "'1'", "'1'", - "'0'", "'0'", - "'1'", "'1'", - "'0'", - "1", "1", - "0"), - ('std_logic', 'boolean', - "'1'", "true", - "'0'", "false", - "'1'", "true", - "false", - "1", "true", - "false"), - ('boolean', 'std_logic', - "true", "'1'", - "false", "'0'", - "true", "'1'", - "'0'", - "true", "1", - "0"), - ('boolean', 'boolean', - "true", "true", - "false", "false", - "true", "true", - "false", - "true", "true", - "false"), - ('string', 'string', - """string'("test")""", """string'("test")""", - """string'("")""", """string'("")""", - """string'("autogenerated test for type with no max value")""", - """string'("autogenerated test for type with no max value")""", - """string'("tests")""", - "test", "test", - "tests"), - ('time', 'time', - "time'(-91 ns)", "time'(-91 ns)", - "time'left", "time'left", - "time'right", "time'right", - "time'(90 ns)", - "\" & time'image(-91 ns) & \"", "\" & time'image(-91 ns) & \"", - "\" & time'image(90 ns) & \"")] - - -def generate_api(): - api = '' - for c in combinations: - t = Template(api_template) - api += t.substitute(got_type=c[0], expected_type=c[1]) - return api - - -def dual_format(base_type, got_or_expected): - if got_or_expected == 'got': - expected_or_got = 'expected' - else: - expected_or_got = 'got' - - if base_type in ['unsigned', 'signed', 'std_logic_vector']: - return ('to_nibble_string(%s) & " (" & ' % got_or_expected - + "to_integer_string(%s) & " % got_or_expected + '")"') - elif base_type == 'integer': - return ('to_string(%s) & " (" & ' % got_or_expected - + "to_nibble_string(to_sufficient_signed(%s, %s'length)) & " % (got_or_expected, expected_or_got) - + '")"') - else: - return ('to_string(%s) & " (" & ' % got_or_expected - + "to_nibble_string(to_sufficient_unsigned(%s, %s'length)) & " % (got_or_expected, expected_or_got) - + '")"') - - -def generate_impl(): - impl = '' - for c in combinations: - t = Template(impl_template) - if (c[0] in ['unsigned', 'signed', 'std_logic_vector']) or (c[1] in ['unsigned', 'signed', 'std_logic_vector']): - got_str = dual_format(c[0], 'got') - expected_str = dual_format(c[1], 'expected') - else: - got_str = 'to_string(got)' - expected_str = 'to_string(expected)' - impl += t.substitute(got_type=c[0], expected_type=c[1], got_str=got_str, expected_str=expected_str) - return impl - - -def generate_test(): - test = """\ --- This test suite verifies the check_equal checker. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -library vunit_lib; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use work.test_support.all; - -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; - -entity tb_check_equal is - generic ( - runner_cfg : string); -end entity tb_check_equal; - -architecture test_fixture of tb_check_equal is -begin - check_equal_runner : process - variable stat : checker_stat_t; - variable my_checker : checker_t := new_checker("my_checker"); - variable my_logger : logger_t := get_logger(my_checker); - variable passed : boolean; - constant default_level : log_level_t := error; - - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test should handle comparison of vectors longer than 32 bits") then - get_checker_stat(stat); - check_equal(unsigned'(X"A5A5A5A5A"), unsigned'(X"A5A5A5A5A")); - check_equal(std_logic_vector'(X"A5A5A5A5A"), unsigned'(X"A5A5A5A5A")); - check_equal(unsigned'(X"A5A5A5A5A"), std_logic_vector'(X"A5A5A5A5A")); - check_equal(std_logic_vector'(X"A5A5A5A5A"), std_logic_vector'(X"A5A5A5A5A")); - verify_passed_checks(stat, 4); - verify_failed_checks(stat, 0); - - mock(check_logger); - check_equal(unsigned'(X"A5A5A5A5A"), unsigned'(X"B5A5A5A5A")); - check_only_log(check_logger, "\ -Equality check failed - Got 1010_0101_1010_0101_1010_0101_1010_0101_1010 (44465543770). \ -Expected 1011_0101_1010_0101_1010_0101_1010_0101_1010 (48760511066).", default_level); - - check_equal(std_logic_vector'(X"A5A5A5A5A"), unsigned'(X"B5A5A5A5A")); - check_only_log(check_logger, "\ -Equality check failed - Got 1010_0101_1010_0101_1010_0101_1010_0101_1010 (44465543770). \ -Expected 1011_0101_1010_0101_1010_0101_1010_0101_1010 (48760511066).", default_level); - - check_equal(unsigned'(X"A5A5A5A5A"), std_logic_vector'(X"B5A5A5A5A")); - check_only_log(check_logger, "\ -Equality check failed - Got 1010_0101_1010_0101_1010_0101_1010_0101_1010 (44465543770). \ -Expected 1011_0101_1010_0101_1010_0101_1010_0101_1010 (48760511066).", default_level); - - check_equal(std_logic_vector'(X"A5A5A5A5A"), std_logic_vector'(X"B5A5A5A5A")); - check_only_log(check_logger, "\ -Equality check failed - Got 1010_0101_1010_0101_1010_0101_1010_0101_1010 (44465543770). \ -Expected 1011_0101_1010_0101_1010_0101_1010_0101_1010 (48760511066).", default_level); - unmock(check_logger); - verify_passed_checks(stat, 4); - verify_failed_checks(stat, 4); - reset_checker_stat; - - elsif run("Test print full integer vector when fail on comparison with to short vector") then - get_checker_stat(stat); - mock(check_logger); - check_equal(unsigned'(X"A5"), natural'(256)); - check_only_log(check_logger, "Equality check failed - Got 1010_0101 (165). Expected 256 (1_0000_0000).", - default_level); - - check_equal(natural'(256), unsigned'(X"A5")); - check_only_log(check_logger, "Equality check failed - Got 256 (1_0000_0000). Expected 1010_0101 (165).", - default_level); - - check_equal(unsigned'(X"A5"), natural'(2147483647)); - check_only_log(check_logger, "Equality check failed - Got 1010_0101 (165). \ -Expected 2147483647 (111_1111_1111_1111_1111_1111_1111_1111).", default_level); - - check_equal(signed'(X"A5"), integer'(-256)); - check_only_log(check_logger, "Equality check failed - Got 1010_0101 (-91). Expected -256 (1_0000_0000).", - default_level); - - check_equal(integer'(-256), signed'(X"A5")); - check_only_log(check_logger, "Equality check failed - Got -256 (1_0000_0000). Expected 1010_0101 (-91).", - default_level); - - check_equal(signed'(X"05"), integer'(256)); - check_only_log(check_logger, "Equality check failed - Got 0000_0101 (5). Expected 256 (01_0000_0000).", - default_level); - - check_equal(signed'(X"A5"), integer'(-2147483648)); - check_only_log(check_logger, "Equality check failed - Got 1010_0101 (-91). \ -Expected -2147483648 (1000_0000_0000_0000_0000_0000_0000_0000).", default_level); - unmock(check_logger); - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 7); - reset_checker_stat; -""" - - natural_equal_natural = [('natural', 'natural', - "natural'(165)", "natural'(165)", - "natural'left", "natural'left", - "natural'right", "natural'right", - "natural'(90)", - '165', '165', - '90')] - - for c in combinations + natural_equal_natural: - t = Template(test_template) - test += t.substitute(left_type=c[0], right_type=c[1], - left_pass=c[2], right_pass=c[3], - left_min=c[4], right_min=c[5], - left_max=c[6], right_max=c[7], - right_fail=c[8], - left_pass_str=c[9], right_pass_str=c[10], - fail_str=c[11]) - - test += """\ - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 2 us); - -end test_fixture; -""" - return test - - -def replace_region(region_name, file_name, new_contents): - result = "" - inside_region = False - - with open(file_name, "rb") as fptr: - contents = fptr.read().decode() - - previous_line = "" - found_region = False - for line in contents.splitlines(): - if inside_region: - if not found_region: - result += new_contents - found_region = True - - if line.startswith(" -------------------"): - inside_region = False - - if not inside_region: - result += line + "\n" - - if previous_line.startswith(" -- %s" % region_name) and line.startswith(" ----------"): - assert not found_region - inside_region = True - - previous_line = line - - assert found_region - - with open(file_name, "wb") as fptr: - fptr.write(result.encode()) - - -def main(): - check_api_file_name = join(dirname(__file__), "..", "src", "check_api.vhd") - replace_region("check_equal", check_api_file_name, generate_api()) - - check_file_name = join(dirname(__file__), "..", "src", "check.vhd") - replace_region("check_equal", check_file_name, generate_impl()) - - with open(join(dirname(__file__), "..", "test", "tb_check_equal.vhd"), "wb") as fptr: - fptr.write(generate_test().encode()) - - -if __name__ == "__main__": - main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from string import Template + +api_template = """ procedure check_equal( + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + variable pass : out boolean; + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_equal( + constant checker : in checker_t; + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_equal( + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_equal( + constant checker : in checker_t; + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + +""" + +impl_template = """ procedure check_equal( + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + variable pass : out boolean; + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if got = expected then + pass := true; + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Equality check passed", msg, + "Got " & $got_str & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Equality check failed", msg, + "Got " & $got_str & ". " & + "Expected " & $expected_str & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_equal( + constant checker : in checker_t; + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_equal( + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_equal( + constant checker : in checker_t; + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_equal(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + +""" + +test_template = """\ + + elsif run("Test should pass on $left_type equal $right_type") then + get_checker_stat(stat); + check_equal($left_pass, $right_pass); + check_equal(passed, $left_pass, $right_pass); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_equal($left_pass, $right_pass); + assert_true(passed, "Should return pass = true on passing check"); + check_equal($left_min, $right_min); + check_equal($left_max, $right_max); + verify_passed_checks(stat, 5); + + get_checker_stat(my_checker, stat); + check_equal(my_checker, $left_pass, $right_pass); + check_equal(my_checker, passed, $left_pass, $right_pass); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_equal(my_checker, $left_pass, $right_pass); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(my_checker, stat, 3); + + elsif run("Test pass message on $left_type equal $right_type") then + mock(check_logger); + check_equal($left_pass, $right_pass); + check_only_log(check_logger, "Equality check passed - Got $left_pass_str.", pass); + + check_equal($left_pass, $right_pass, ""); + check_only_log(check_logger, "Got $left_pass_str.", pass); + + check_equal($left_pass, $right_pass, "Checking my data"); + check_only_log(check_logger, "Checking my data - Got $left_pass_str.", pass); + + check_equal($left_pass, $right_pass, result("for my data")); + check_only_log(check_logger, "Equality check passed for my data - Got $left_pass_str.", pass); + unmock(check_logger); + + elsif run("Test should fail on $left_type not equal $right_type") then + get_checker_stat(stat); + mock(check_logger); + check_equal($left_pass, $right_fail); + check_only_log(check_logger, "Equality check failed - Got $left_pass_str. Expected $fail_str.", + default_level); + + check_equal($left_pass, $right_fail, ""); + check_only_log(check_logger, "Got $left_pass_str. Expected $fail_str.", default_level); + + check_equal($left_pass, $right_fail, "Checking my data"); + check_only_log(check_logger, "Checking my data - Got $left_pass_str. Expected $fail_str.", default_level); + + check_equal($left_pass, $right_fail, result("for my data")); + check_only_log(check_logger, "Equality check failed for my data - Got $left_pass_str. Expected $fail_str.", + default_level); + + check_equal(passed, $left_pass, $right_fail); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Equality check failed - Got $left_pass_str. Expected $fail_str.", + default_level); + + passed := check_equal($left_pass, $right_fail); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Equality check failed - Got $left_pass_str. Expected $fail_str.", + default_level); + unmock(check_logger); + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 6); + reset_checker_stat; + + get_checker_stat(my_checker, stat); + mock(my_logger); + check_equal(my_checker, $left_pass, $right_fail); + check_only_log(my_logger, "Equality check failed - Got $left_pass_str. Expected $fail_str.", default_level); + + check_equal(my_checker, passed, $left_pass, $right_fail); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(my_logger, "Equality check failed - Got $left_pass_str. Expected $fail_str.", default_level); + + passed := check_equal(my_checker, $left_pass, $right_fail); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(my_logger, "Equality check failed - Got $left_pass_str. Expected $fail_str.", default_level); + + unmock(my_logger); + verify_passed_checks(my_checker, stat, 0); + verify_failed_checks(my_checker, stat, 3); + reset_checker_stat(my_checker); +""" + +combinations = [ + ('unsigned', 'unsigned', + """unsigned'(X"A5")""", """unsigned'(X"A5")""", + "to_unsigned(natural'left,31)", "to_unsigned(natural'left,31)", + "to_unsigned(natural'right,31)", "to_unsigned(natural'right,31)", + """unsigned'(X"5A")""", + "1010_0101 (165)", "1010_0101 (165)", + "0101_1010 (90)"), + ('unsigned', 'natural', + """unsigned'(X"A5")""", "natural'(165)", + "to_unsigned(natural'left,31)", "natural'left", + "to_unsigned(natural'right,31)", "natural'right", + "natural'(90)", + "1010_0101 (165)", "165 (1010_0101)", + "90 (0101_1010)"), + ('natural', 'unsigned', + "natural'(165)", """unsigned'(X"A5")""", + "natural'left", "to_unsigned(natural'left,31)", + "natural'right", "to_unsigned(natural'right,31)", + """unsigned'(X"5A")""", + "165 (1010_0101)", "1010_0101 (165)", + "0101_1010 (90)"), + ('unsigned', 'std_logic_vector', + """unsigned'(X"A5")""", """std_logic_vector'(X"A5")""", + "to_unsigned(natural'left,31)", "std_logic_vector(to_unsigned(natural'left,31))", + "to_unsigned(natural'right,31)", "std_logic_vector(to_unsigned(natural'right,31))", + """std_logic_vector'(X"5A")""", + "1010_0101 (165)", "1010_0101 (165)", + "0101_1010 (90)"), + ('std_logic_vector', 'unsigned', + """std_logic_vector'(X"A5")""", """unsigned'(X"A5")""", + "std_logic_vector(to_unsigned(natural'left,31))", "to_unsigned(natural'left,31)", + "std_logic_vector(to_unsigned(natural'right,31))", "to_unsigned(natural'right,31)", + """unsigned'(X"5A")""", + "1010_0101 (165)", "1010_0101 (165)", + "0101_1010 (90)"), + ('std_logic_vector', 'std_logic_vector', + """std_logic_vector'(X"A5")""", """std_logic_vector'(X"A5")""", + "std_logic_vector(to_unsigned(natural'left,31))", "std_logic_vector(to_unsigned(natural'left,31))", + "std_logic_vector(to_unsigned(natural'right,31))", "std_logic_vector(to_unsigned(natural'right,31))", + """std_logic_vector'(X"5A")""", + "1010_0101 (165)", "1010_0101 (165)", + "0101_1010 (90)"), + ('std_logic_vector', 'natural', + """std_logic_vector'(X"A5")""", "natural'(165)", + "std_logic_vector(to_unsigned(natural'left,31))", "natural'left", + "std_logic_vector(to_unsigned(natural'right,31))", "natural'right", + "natural'(90)", + "1010_0101 (165)", "165 (1010_0101)", + "90 (0101_1010)"), + ('natural', 'std_logic_vector', + "natural'(165)", """std_logic_vector'(X"A5")""", + "natural'left", "std_logic_vector(to_unsigned(natural'left,31))", + "natural'right", "std_logic_vector(to_unsigned(natural'right,31))", + """std_logic_vector'(X"5A")""", + "165 (1010_0101)", "1010_0101 (165)", + "0101_1010 (90)"), + ('signed', 'signed', + """signed'(X"A5")""", """signed'(X"A5")""", + "to_signed(integer'left,32)", "to_signed(integer'left,32)", + "to_signed(integer'right,32)", "to_signed(integer'right,32)", + """signed'(X"5A")""", + "1010_0101 (-91)", "1010_0101 (-91)", + "0101_1010 (90)"), + ('signed', 'integer', + """signed'(X"A5")""", "integer'(-91)", + "to_signed(integer'left,32)", "integer'left", + "to_signed(integer'right,32)", "integer'right", + "integer'(90)", + "1010_0101 (-91)", "-91 (1010_0101)", + "90 (0101_1010)"), + ('integer', 'signed', + "integer'(-91)", """signed'(X"A5")""", + "integer'left", "to_signed(integer'left,32)", + "integer'right", "to_signed(integer'right,32)", + """signed'(X"5A")""", + "-91 (1010_0101)", "1010_0101 (-91)", + "0101_1010 (90)"), + ('integer', 'integer', + "integer'(-91)", "integer'(-91)", + "integer'left", "integer'left", + "integer'right", "integer'right", + "integer'(90)", + "-91", "-91", + "90"), + ('std_logic', 'std_logic', + "'1'", "'1'", + "'0'", "'0'", + "'1'", "'1'", + "'0'", + "1", "1", + "0"), + ('std_logic', 'boolean', + "'1'", "true", + "'0'", "false", + "'1'", "true", + "false", + "1", "true", + "false"), + ('boolean', 'std_logic', + "true", "'1'", + "false", "'0'", + "true", "'1'", + "'0'", + "true", "1", + "0"), + ('boolean', 'boolean', + "true", "true", + "false", "false", + "true", "true", + "false", + "true", "true", + "false"), + ('string', 'string', + """string'("test")""", """string'("test")""", + """string'("")""", """string'("")""", + """string'("autogenerated test for type with no max value")""", + """string'("autogenerated test for type with no max value")""", + """string'("tests")""", + "test", "test", + "tests"), + ('time', 'time', + "time'(-91 ns)", "time'(-91 ns)", + "time'left", "time'left", + "time'right", "time'right", + "time'(90 ns)", + "\" & time'image(-91 ns) & \"", "\" & time'image(-91 ns) & \"", + "\" & time'image(90 ns) & \"")] + + +def generate_api(): + api = '' + for c in combinations: + t = Template(api_template) + api += t.substitute(got_type=c[0], expected_type=c[1]) + return api + + +def dual_format(base_type, got_or_expected): + if got_or_expected == 'got': + expected_or_got = 'expected' + else: + expected_or_got = 'got' + + if base_type in ['unsigned', 'signed', 'std_logic_vector']: + return ('to_nibble_string(%s) & " (" & ' % got_or_expected + + "to_integer_string(%s) & " % got_or_expected + '")"') + elif base_type == 'integer': + return ('to_string(%s) & " (" & ' % got_or_expected + + "to_nibble_string(to_sufficient_signed(%s, %s'length)) & " % (got_or_expected, expected_or_got) + + '")"') + else: + return ('to_string(%s) & " (" & ' % got_or_expected + + "to_nibble_string(to_sufficient_unsigned(%s, %s'length)) & " % (got_or_expected, expected_or_got) + + '")"') + + +def generate_impl(): + impl = '' + for c in combinations: + t = Template(impl_template) + if (c[0] in ['unsigned', 'signed', 'std_logic_vector']) or (c[1] in ['unsigned', 'signed', 'std_logic_vector']): + got_str = dual_format(c[0], 'got') + expected_str = dual_format(c[1], 'expected') + else: + got_str = 'to_string(got)' + expected_str = 'to_string(expected)' + impl += t.substitute(got_type=c[0], expected_type=c[1], got_str=got_str, expected_str=expected_str) + return impl + + +def generate_test(): + test = """\ +-- This test suite verifies the check_equal checker. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +library vunit_lib; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use work.test_support.all; + +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; + +entity tb_check_equal is + generic ( + runner_cfg : string); +end entity tb_check_equal; + +architecture test_fixture of tb_check_equal is +begin + check_equal_runner : process + variable stat : checker_stat_t; + variable my_checker : checker_t := new_checker("my_checker"); + variable my_logger : logger_t := get_logger(my_checker); + variable passed : boolean; + constant default_level : log_level_t := error; + + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test should handle comparison of vectors longer than 32 bits") then + get_checker_stat(stat); + check_equal(unsigned'(X"A5A5A5A5A"), unsigned'(X"A5A5A5A5A")); + check_equal(std_logic_vector'(X"A5A5A5A5A"), unsigned'(X"A5A5A5A5A")); + check_equal(unsigned'(X"A5A5A5A5A"), std_logic_vector'(X"A5A5A5A5A")); + check_equal(std_logic_vector'(X"A5A5A5A5A"), std_logic_vector'(X"A5A5A5A5A")); + verify_passed_checks(stat, 4); + verify_failed_checks(stat, 0); + + mock(check_logger); + check_equal(unsigned'(X"A5A5A5A5A"), unsigned'(X"B5A5A5A5A")); + check_only_log(check_logger, "\ +Equality check failed - Got 1010_0101_1010_0101_1010_0101_1010_0101_1010 (44465543770). \ +Expected 1011_0101_1010_0101_1010_0101_1010_0101_1010 (48760511066).", default_level); + + check_equal(std_logic_vector'(X"A5A5A5A5A"), unsigned'(X"B5A5A5A5A")); + check_only_log(check_logger, "\ +Equality check failed - Got 1010_0101_1010_0101_1010_0101_1010_0101_1010 (44465543770). \ +Expected 1011_0101_1010_0101_1010_0101_1010_0101_1010 (48760511066).", default_level); + + check_equal(unsigned'(X"A5A5A5A5A"), std_logic_vector'(X"B5A5A5A5A")); + check_only_log(check_logger, "\ +Equality check failed - Got 1010_0101_1010_0101_1010_0101_1010_0101_1010 (44465543770). \ +Expected 1011_0101_1010_0101_1010_0101_1010_0101_1010 (48760511066).", default_level); + + check_equal(std_logic_vector'(X"A5A5A5A5A"), std_logic_vector'(X"B5A5A5A5A")); + check_only_log(check_logger, "\ +Equality check failed - Got 1010_0101_1010_0101_1010_0101_1010_0101_1010 (44465543770). \ +Expected 1011_0101_1010_0101_1010_0101_1010_0101_1010 (48760511066).", default_level); + unmock(check_logger); + verify_passed_checks(stat, 4); + verify_failed_checks(stat, 4); + reset_checker_stat; + + elsif run("Test print full integer vector when fail on comparison with to short vector") then + get_checker_stat(stat); + mock(check_logger); + check_equal(unsigned'(X"A5"), natural'(256)); + check_only_log(check_logger, "Equality check failed - Got 1010_0101 (165). Expected 256 (1_0000_0000).", + default_level); + + check_equal(natural'(256), unsigned'(X"A5")); + check_only_log(check_logger, "Equality check failed - Got 256 (1_0000_0000). Expected 1010_0101 (165).", + default_level); + + check_equal(unsigned'(X"A5"), natural'(2147483647)); + check_only_log(check_logger, "Equality check failed - Got 1010_0101 (165). \ +Expected 2147483647 (111_1111_1111_1111_1111_1111_1111_1111).", default_level); + + check_equal(signed'(X"A5"), integer'(-256)); + check_only_log(check_logger, "Equality check failed - Got 1010_0101 (-91). Expected -256 (1_0000_0000).", + default_level); + + check_equal(integer'(-256), signed'(X"A5")); + check_only_log(check_logger, "Equality check failed - Got -256 (1_0000_0000). Expected 1010_0101 (-91).", + default_level); + + check_equal(signed'(X"05"), integer'(256)); + check_only_log(check_logger, "Equality check failed - Got 0000_0101 (5). Expected 256 (01_0000_0000).", + default_level); + + check_equal(signed'(X"A5"), integer'(-2147483648)); + check_only_log(check_logger, "Equality check failed - Got 1010_0101 (-91). \ +Expected -2147483648 (1000_0000_0000_0000_0000_0000_0000_0000).", default_level); + unmock(check_logger); + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 7); + reset_checker_stat; +""" + + natural_equal_natural = [('natural', 'natural', + "natural'(165)", "natural'(165)", + "natural'left", "natural'left", + "natural'right", "natural'right", + "natural'(90)", + '165', '165', + '90')] + + for c in combinations + natural_equal_natural: + t = Template(test_template) + test += t.substitute(left_type=c[0], right_type=c[1], + left_pass=c[2], right_pass=c[3], + left_min=c[4], right_min=c[5], + left_max=c[6], right_max=c[7], + right_fail=c[8], + left_pass_str=c[9], right_pass_str=c[10], + fail_str=c[11]) + + test += """\ + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 2 us); + +end test_fixture; +""" + return test + + +def replace_region(region_name, file_name, new_contents): + result = "" + inside_region = False + + with open(file_name, "rb") as fptr: + contents = fptr.read().decode() + + previous_line = "" + found_region = False + for line in contents.splitlines(): + if inside_region: + if not found_region: + result += new_contents + found_region = True + + if line.startswith(" -------------------"): + inside_region = False + + if not inside_region: + result += line + "\n" + + if previous_line.startswith(" -- %s" % region_name) and line.startswith(" ----------"): + assert not found_region + inside_region = True + + previous_line = line + + assert found_region + + with open(file_name, "wb") as fptr: + fptr.write(result.encode()) + + +def main(): + check_api_file_name = join(dirname(__file__), "..", "src", "check_api.vhd") + replace_region("check_equal", check_api_file_name, generate_api()) + + check_file_name = join(dirname(__file__), "..", "src", "check.vhd") + replace_region("check_equal", check_file_name, generate_impl()) + + with open(join(dirname(__file__), "..", "test", "tb_check_equal.vhd"), "wb") as fptr: + fptr.write(generate_test().encode()) + + +if __name__ == "__main__": + main() diff --git a/vunit/vhdl/check/tools/generate_check_match.py b/vunit/vhdl/check/tools/generate_check_match.py index 5b17d7f50..37af54e2b 100644 --- a/vunit/vhdl/check/tools/generate_check_match.py +++ b/vunit/vhdl/check/tools/generate_check_match.py @@ -1,410 +1,410 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from string import Template -from generate_check_equal import replace_region - -api_template = """ procedure check_match( - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_match( - variable pass : out boolean; - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_match( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure check_match( - constant checker : in checker_t; - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - impure function check_match( - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - - impure function check_match( - constant checker : in checker_t; - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean; - -""" - -impl_template = """ procedure check_match( - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_match( - variable pass : out boolean; - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - procedure check_match( - constant checker : in checker_t; - variable pass : out boolean; - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - -- pragma translate_off - if std_match(got, expected) then - pass := true; - - if is_pass_visible(checker) then - passing_check( - checker, - std_msg( - "Match check passed", msg, - "Got " & $got_str & ". " & - "Expected " & $expected_str & "."), - line_num, file_name); - else - passing_check(checker); - end if; - else - pass := false; - failing_check( - checker, - std_msg( - "Match check failed", msg, - "Got " & $got_str & ". " & - "Expected " & $expected_str & "."), - level, line_num, file_name); - end if; - -- pragma translate_on - end; - - procedure check_match( - constant checker : in checker_t; - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") is - variable pass : boolean; - begin - -- pragma translate_off - check_match(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - end; - - impure function check_match( - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - - impure function check_match( - constant checker : in checker_t; - constant got : in $got_type; - constant expected : in $expected_type; - constant msg : in string := check_result_tag; - constant level : in log_level_t := null_log_level; - constant line_num : in natural := 0; - constant file_name : in string := "") - return boolean is - variable pass : boolean; - begin - -- pragma translate_off - check_match(checker, pass, got, expected, msg, level, line_num, file_name); - -- pragma translate_on - return pass; - end; - -""" - -test_template = """ - $first_if run("Test should pass on $left_type matching $right_type") then - get_checker_stat(stat); - check_match($left_pass, $right_pass); - check_match($left_pass_dc, $right_pass); - check_match($left_pass, $right_pass_dc); - check_match($left_pass_dc, $right_pass_dc); - check_match(passed, $left_pass, $right_pass); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_match($left_pass, $right_pass); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(stat, 6); - - get_checker_stat(my_checker, stat); - check_match(my_checker, $left_pass, $right_pass); - check_match(my_checker, passed, $left_pass, $right_pass); - assert_true(passed, "Should return pass = true on passing check"); - passed := check_match(my_checker, $left_pass, $right_pass); - assert_true(passed, "Should return pass = true on passing check"); - verify_passed_checks(my_checker,stat, 3); - - elsif run("Test pass message for $left_type matching $right_type") then - mock(check_logger); - check_match($left_pass, $right_pass); - check_only_log(check_logger, "Match check passed - Got $pass_str. Expected $pass_str.", pass); - - check_match($left_pass_dc, $right_pass, ""); - check_only_log(check_logger, "Got $pass_dc_str. Expected $pass_str.", pass); - - check_match($left_pass, $right_pass_dc, "Checking my data"); - check_only_log(check_logger, "Checking my data - Got $pass_str. Expected $pass_dc_str.", pass); - - check_match($left_pass_dc, $right_pass_dc, result("for my data")); - check_only_log(check_logger, - "Match check passed for my data - Got $pass_dc_str. Expected $pass_dc_str.", - pass); - unmock(check_logger); - - elsif run("Test should fail on $left_type not matching $right_type") then - get_checker_stat(stat); - mock(check_logger); - check_match($left_pass, $right_fail_dc); - check_only_log(check_logger, "Match check failed - Got $pass_str. Expected $fail_dc_str.", default_level); - - check_match($left_pass, $right_fail_dc, ""); - check_only_log(check_logger, "Got $pass_str. Expected $fail_dc_str.", default_level); - - check_match(passed, $left_pass, $right_fail, "Checking my data"); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Checking my data - Got $pass_str. Expected $fail_str.", default_level); - - passed := check_match($left_pass, $right_fail, result("for my data")); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(check_logger, "Match check failed for my data - Got $pass_str. Expected $fail_str.", - default_level); - unmock(check_logger); - verify_passed_checks(stat, 0); - verify_failed_checks(stat, 4); - reset_checker_stat; - - get_checker_stat(my_checker, stat); - mock(my_logger); - check_match(my_checker, $left_pass, $right_fail); - check_only_log(my_logger, "Match check failed - Got $pass_str. Expected $fail_str.", default_level); - - check_match(my_checker, passed, $left_pass, $right_fail); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(my_logger, "Match check failed - Got $pass_str. Expected $fail_str.", default_level); - - passed := check_match(my_checker, $left_pass, $right_fail, result("for my data")); - assert_true(not passed, "Should return pass = false on failing check"); - check_only_log(my_logger, "Match check failed for my data - Got $pass_str. Expected $fail_str.", - default_level); - unmock(my_logger); - verify_passed_checks(my_checker, stat, 0); - verify_failed_checks(my_checker, stat, 3); - reset_checker_stat(my_checker); -""" - -combinations = [ - ('unsigned', 'unsigned', - """unsigned'(X"A5")""", """unsigned'(X"A5")""", - """unsigned'("1010----")""", """unsigned'("1010----")""", - """unsigned'(X"5A")""", """unsigned'("0101----")""", - '1010_0101 (165)', '0101_1010 (90)', - '1010_---- (NaN)', '0101_---- (NaN)'), - ('std_logic_vector', 'std_logic_vector', - """std_logic_vector'(X"A5")""", """std_logic_vector'(X"A5")""", - """std_logic_vector'("1010----")""", """std_logic_vector'("1010----")""", - """std_logic_vector'(X"5A")""", """std_logic_vector'("0101----")""", - '1010_0101 (165)', '0101_1010 (90)', - '1010_---- (NaN)', '0101_---- (NaN)'), - ('signed', 'signed', - """signed'(X"A5")""", """signed'(X"A5")""", - """signed'("1010----")""", """signed'("1010----")""", - """signed'(X"5A")""", """signed'("0101----")""", - '1010_0101 (-91)', '0101_1010 (90)', - '1010_---- (NaN)', '0101_---- (NaN)'), - ('std_logic', 'std_logic', - "std_logic'('1')", "'1'", - "'-'", "'-'", - "'0'", "'0'", - "1", "0", "-", "0")] - - -def generate_api(): - api = '' - for c in combinations: - t = Template(api_template) - api += t.substitute(got_type=c[0], expected_type=c[1]) - return api - - -def dual_format(base_type, got_or_expected): - if got_or_expected == 'got': - expected_or_got = 'expected' - else: - expected_or_got = 'got' - - if base_type in ['unsigned', 'signed', 'std_logic_vector']: - return ('to_nibble_string(%s) & " (" & ' % got_or_expected - + "to_integer_string(%s) & " % got_or_expected + '")"') - elif base_type == 'integer': - return ('to_string(%s) & " (" & ' % got_or_expected - + "to_nibble_string(to_sufficient_signed(%s, %s'length)) & " % (got_or_expected, expected_or_got) - + '")"') - else: - return ('to_string(%s) & " (" & ' % got_or_expected - + "to_nibble_string(to_sufficient_unsigned(%s, %s'length)) & " % (got_or_expected, expected_or_got) - + '")"') - - -def generate_impl(): - impl = '' - for c in combinations: - t = Template(impl_template) - if (c[0] in ['unsigned', 'signed', 'std_logic_vector']) or (c[1] in ['unsigned', 'signed', 'std_logic_vector']): - got_str = dual_format(c[0], 'got') - expected_str = dual_format(c[1], 'expected') - else: - got_str = 'to_string(got)' - expected_str = 'to_string(expected)' - impl += t.substitute(got_type=c[0], expected_type=c[1], got_str=got_str, expected_str=expected_str) - return impl - - -def generate_test(): - test = """\ --- This test suite verifies the check_equal checker. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -library vunit_lib; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use work.test_support.all; - -entity tb_check_match is - generic ( - runner_cfg : string); -end entity tb_check_match; - -architecture test_fixture of tb_check_match is -begin - check_match_runner : process - variable stat : checker_stat_t; - variable my_checker : checker_t := new_checker("my_checker"); - constant my_logger : logger_t := get_logger(my_checker); - variable passed : boolean; - constant default_level : log_level_t := error; - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop""" - - for idx, c in enumerate(combinations): - t = Template(test_template) - test += t.substitute(first_if="if" if idx == 0 else "elsif", - left_type=c[0], right_type=c[1], - left_pass=c[2], right_pass=c[3], - left_pass_dc=c[4], right_pass_dc=c[5], - right_fail=c[6], right_fail_dc=c[7], - pass_str=c[8], fail_str=c[9], - pass_dc_str=c[10], fail_dc_str=c[11]) - - test += """ - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 2 us); - -end test_fixture; -""" - - return test - - -def main(): - check_api_file_name = join(dirname(__file__), "..", "src", "check_api.vhd") - replace_region("check_match", check_api_file_name, generate_api()) - - check_file_name = join(dirname(__file__), "..", "src", "check.vhd") - replace_region("check_match", check_file_name, generate_impl()) - - with open(join(dirname(__file__), "..", "test", "tb_check_match.vhd"), "wb") as fptr: - fptr.write(generate_test().encode()) - - -if __name__ == "__main__": - main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from string import Template +from generate_check_equal import replace_region + +api_template = """ procedure check_match( + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_match( + variable pass : out boolean; + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_match( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure check_match( + constant checker : in checker_t; + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + impure function check_match( + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + + impure function check_match( + constant checker : in checker_t; + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean; + +""" + +impl_template = """ procedure check_match( + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_match( + variable pass : out boolean; + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + procedure check_match( + constant checker : in checker_t; + variable pass : out boolean; + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + -- pragma translate_off + if std_match(got, expected) then + pass := true; + + if is_pass_visible(checker) then + passing_check( + checker, + std_msg( + "Match check passed", msg, + "Got " & $got_str & ". " & + "Expected " & $expected_str & "."), + line_num, file_name); + else + passing_check(checker); + end if; + else + pass := false; + failing_check( + checker, + std_msg( + "Match check failed", msg, + "Got " & $got_str & ". " & + "Expected " & $expected_str & "."), + level, line_num, file_name); + end if; + -- pragma translate_on + end; + + procedure check_match( + constant checker : in checker_t; + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") is + variable pass : boolean; + begin + -- pragma translate_off + check_match(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + end; + + impure function check_match( + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_match(default_checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + + impure function check_match( + constant checker : in checker_t; + constant got : in $got_type; + constant expected : in $expected_type; + constant msg : in string := check_result_tag; + constant level : in log_level_t := null_log_level; + constant line_num : in natural := 0; + constant file_name : in string := "") + return boolean is + variable pass : boolean; + begin + -- pragma translate_off + check_match(checker, pass, got, expected, msg, level, line_num, file_name); + -- pragma translate_on + return pass; + end; + +""" + +test_template = """ + $first_if run("Test should pass on $left_type matching $right_type") then + get_checker_stat(stat); + check_match($left_pass, $right_pass); + check_match($left_pass_dc, $right_pass); + check_match($left_pass, $right_pass_dc); + check_match($left_pass_dc, $right_pass_dc); + check_match(passed, $left_pass, $right_pass); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_match($left_pass, $right_pass); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(stat, 6); + + get_checker_stat(my_checker, stat); + check_match(my_checker, $left_pass, $right_pass); + check_match(my_checker, passed, $left_pass, $right_pass); + assert_true(passed, "Should return pass = true on passing check"); + passed := check_match(my_checker, $left_pass, $right_pass); + assert_true(passed, "Should return pass = true on passing check"); + verify_passed_checks(my_checker,stat, 3); + + elsif run("Test pass message for $left_type matching $right_type") then + mock(check_logger); + check_match($left_pass, $right_pass); + check_only_log(check_logger, "Match check passed - Got $pass_str. Expected $pass_str.", pass); + + check_match($left_pass_dc, $right_pass, ""); + check_only_log(check_logger, "Got $pass_dc_str. Expected $pass_str.", pass); + + check_match($left_pass, $right_pass_dc, "Checking my data"); + check_only_log(check_logger, "Checking my data - Got $pass_str. Expected $pass_dc_str.", pass); + + check_match($left_pass_dc, $right_pass_dc, result("for my data")); + check_only_log(check_logger, + "Match check passed for my data - Got $pass_dc_str. Expected $pass_dc_str.", + pass); + unmock(check_logger); + + elsif run("Test should fail on $left_type not matching $right_type") then + get_checker_stat(stat); + mock(check_logger); + check_match($left_pass, $right_fail_dc); + check_only_log(check_logger, "Match check failed - Got $pass_str. Expected $fail_dc_str.", default_level); + + check_match($left_pass, $right_fail_dc, ""); + check_only_log(check_logger, "Got $pass_str. Expected $fail_dc_str.", default_level); + + check_match(passed, $left_pass, $right_fail, "Checking my data"); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Checking my data - Got $pass_str. Expected $fail_str.", default_level); + + passed := check_match($left_pass, $right_fail, result("for my data")); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(check_logger, "Match check failed for my data - Got $pass_str. Expected $fail_str.", + default_level); + unmock(check_logger); + verify_passed_checks(stat, 0); + verify_failed_checks(stat, 4); + reset_checker_stat; + + get_checker_stat(my_checker, stat); + mock(my_logger); + check_match(my_checker, $left_pass, $right_fail); + check_only_log(my_logger, "Match check failed - Got $pass_str. Expected $fail_str.", default_level); + + check_match(my_checker, passed, $left_pass, $right_fail); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(my_logger, "Match check failed - Got $pass_str. Expected $fail_str.", default_level); + + passed := check_match(my_checker, $left_pass, $right_fail, result("for my data")); + assert_true(not passed, "Should return pass = false on failing check"); + check_only_log(my_logger, "Match check failed for my data - Got $pass_str. Expected $fail_str.", + default_level); + unmock(my_logger); + verify_passed_checks(my_checker, stat, 0); + verify_failed_checks(my_checker, stat, 3); + reset_checker_stat(my_checker); +""" + +combinations = [ + ('unsigned', 'unsigned', + """unsigned'(X"A5")""", """unsigned'(X"A5")""", + """unsigned'("1010----")""", """unsigned'("1010----")""", + """unsigned'(X"5A")""", """unsigned'("0101----")""", + '1010_0101 (165)', '0101_1010 (90)', + '1010_---- (NaN)', '0101_---- (NaN)'), + ('std_logic_vector', 'std_logic_vector', + """std_logic_vector'(X"A5")""", """std_logic_vector'(X"A5")""", + """std_logic_vector'("1010----")""", """std_logic_vector'("1010----")""", + """std_logic_vector'(X"5A")""", """std_logic_vector'("0101----")""", + '1010_0101 (165)', '0101_1010 (90)', + '1010_---- (NaN)', '0101_---- (NaN)'), + ('signed', 'signed', + """signed'(X"A5")""", """signed'(X"A5")""", + """signed'("1010----")""", """signed'("1010----")""", + """signed'(X"5A")""", """signed'("0101----")""", + '1010_0101 (-91)', '0101_1010 (90)', + '1010_---- (NaN)', '0101_---- (NaN)'), + ('std_logic', 'std_logic', + "std_logic'('1')", "'1'", + "'-'", "'-'", + "'0'", "'0'", + "1", "0", "-", "0")] + + +def generate_api(): + api = '' + for c in combinations: + t = Template(api_template) + api += t.substitute(got_type=c[0], expected_type=c[1]) + return api + + +def dual_format(base_type, got_or_expected): + if got_or_expected == 'got': + expected_or_got = 'expected' + else: + expected_or_got = 'got' + + if base_type in ['unsigned', 'signed', 'std_logic_vector']: + return ('to_nibble_string(%s) & " (" & ' % got_or_expected + + "to_integer_string(%s) & " % got_or_expected + '")"') + elif base_type == 'integer': + return ('to_string(%s) & " (" & ' % got_or_expected + + "to_nibble_string(to_sufficient_signed(%s, %s'length)) & " % (got_or_expected, expected_or_got) + + '")"') + else: + return ('to_string(%s) & " (" & ' % got_or_expected + + "to_nibble_string(to_sufficient_unsigned(%s, %s'length)) & " % (got_or_expected, expected_or_got) + + '")"') + + +def generate_impl(): + impl = '' + for c in combinations: + t = Template(impl_template) + if (c[0] in ['unsigned', 'signed', 'std_logic_vector']) or (c[1] in ['unsigned', 'signed', 'std_logic_vector']): + got_str = dual_format(c[0], 'got') + expected_str = dual_format(c[1], 'expected') + else: + got_str = 'to_string(got)' + expected_str = 'to_string(expected)' + impl += t.substitute(got_type=c[0], expected_type=c[1], got_str=got_str, expected_str=expected_str) + return impl + + +def generate_test(): + test = """\ +-- This test suite verifies the check_equal checker. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +library vunit_lib; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use work.test_support.all; + +entity tb_check_match is + generic ( + runner_cfg : string); +end entity tb_check_match; + +architecture test_fixture of tb_check_match is +begin + check_match_runner : process + variable stat : checker_stat_t; + variable my_checker : checker_t := new_checker("my_checker"); + constant my_logger : logger_t := get_logger(my_checker); + variable passed : boolean; + constant default_level : log_level_t := error; + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop""" + + for idx, c in enumerate(combinations): + t = Template(test_template) + test += t.substitute(first_if="if" if idx == 0 else "elsif", + left_type=c[0], right_type=c[1], + left_pass=c[2], right_pass=c[3], + left_pass_dc=c[4], right_pass_dc=c[5], + right_fail=c[6], right_fail_dc=c[7], + pass_str=c[8], fail_str=c[9], + pass_dc_str=c[10], fail_dc_str=c[11]) + + test += """ + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 2 us); + +end test_fixture; +""" + + return test + + +def main(): + check_api_file_name = join(dirname(__file__), "..", "src", "check_api.vhd") + replace_region("check_match", check_api_file_name, generate_api()) + + check_file_name = join(dirname(__file__), "..", "src", "check.vhd") + replace_region("check_match", check_file_name, generate_impl()) + + with open(join(dirname(__file__), "..", "test", "tb_check_match.vhd"), "wb") as fptr: + fptr.write(generate_test().encode()) + + +if __name__ == "__main__": + main() diff --git a/vunit/vhdl/com/run.py b/vunit/vhdl/com/run.py index cb32acca9..fc204cf66 100644 --- a/vunit/vhdl/com/run.py +++ b/vunit/vhdl/com/run.py @@ -1,19 +1,19 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -root = dirname(__file__) - -prj = VUnit.from_argv() -prj.add_com() -tb_com_lib = prj.add_library("tb_com_lib") -tb_com_lib.add_source_files(join(root, 'test', '*.vhd')) -pkg = tb_com_lib.package('custom_types_pkg') -pkg.generate_codecs(codec_package_name='custom_codec_pkg', used_packages=['ieee.std_logic_1164', 'constants_pkg', - 'tb_com_lib.more_constants_pkg']) -prj.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +root = dirname(__file__) + +prj = VUnit.from_argv() +prj.add_com() +tb_com_lib = prj.add_library("tb_com_lib") +tb_com_lib.add_source_files(join(root, 'test', '*.vhd')) +pkg = tb_com_lib.package('custom_types_pkg') +pkg.generate_codecs(codec_package_name='custom_codec_pkg', used_packages=['ieee.std_logic_1164', 'constants_pkg', + 'tb_com_lib.more_constants_pkg']) +prj.main() diff --git a/vunit/vhdl/com/src/com.vhd b/vunit/vhdl/com/src/com.vhd index b3f97d92b..0ca05ab2a 100644 --- a/vunit/vhdl/com/src/com.vhd +++ b/vunit/vhdl/com/src/com.vhd @@ -1,699 +1,699 @@ --- Com package provides a generic communication mechanism for testbenches --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -context work.vunit_context; - -use work.queue_pkg.all; -use work.queue_2008_pkg.all; -use work.queue_pool_pkg.all; -use work.integer_vector_ptr_pkg.all; -use work.string_ptr_pkg.all; -use work.codec_pkg.all; -use work.com_support_pkg.all; -use work.com_messenger_pkg.all; -use work.com_common_pkg.all; -use work.logger_pkg.all; - -use std.textio.all; - -package body com_pkg is - ----------------------------------------------------------------------------- - -- Handling of actors - ----------------------------------------------------------------------------- - impure function new_actor ( - name : string := ""; - inbox_size : positive := positive'high; - outbox_size : positive := positive'high - ) return actor_t is - begin - return messenger.create(name, inbox_size, outbox_size); - end; - - impure function find (name : string; enable_deferred_creation : boolean := true) return actor_t is - begin - return messenger.find(name, enable_deferred_creation); - end; - - impure function name (actor : actor_t) return string is - begin - return messenger.name(actor); - end; - - procedure destroy (actor : inout actor_t) is - begin - messenger.destroy(actor); - end; - - procedure reset_messenger is - begin - messenger.reset_messenger; - end; - - impure function num_of_actors - return natural is - begin - return messenger.num_of_actors; - end; - - impure function is_deferred(actor : actor_t) return boolean is - begin - return messenger.is_deferred(actor); - end; - - impure function num_of_deferred_creations - return natural is - begin - return messenger.num_of_deferred_creations; - end; - - impure function num_of_messages (actor : actor_t; mailbox_id : mailbox_id_t := inbox) return natural is - begin - return messenger.num_of_messages(actor, mailbox_id); - end; - - impure function mailbox_size (actor : actor_t; mailbox_id : mailbox_id_t := inbox) return natural is - begin - return messenger.mailbox_size(actor, mailbox_id); - end; - - procedure resize_mailbox (actor : actor_t; new_size : natural; mailbox_id : mailbox_id_t := inbox) is - begin - messenger.resize_mailbox(actor, new_size, mailbox_id); - end; - - ----------------------------------------------------------------------------- - -- Primary send and receive related subprograms - ----------------------------------------------------------------------------- - procedure wait_on_subscribers ( - publisher : actor_t; - subscription_traffic_types : subscription_traffic_types_t; - timeout : time) is - begin - if messenger.subscriber_inbox_is_full(publisher, subscription_traffic_types) then - wait on net until not messenger.subscriber_inbox_is_full(publisher, subscription_traffic_types) for timeout; - check(not messenger.subscriber_inbox_is_full(publisher, subscription_traffic_types), full_inbox_error); - end if; - end procedure wait_on_subscribers; - - procedure send ( - signal net : inout network_t; - constant receiver : in actor_t; - constant mailbox_id : in mailbox_id_t; - variable msg : inout msg_t; - constant timeout : in time := max_timeout) is - variable t_start : time; - begin - if not check(msg.data /= null_queue, null_message_error) then - return; - end if; - - if not check(not messenger.unknown_actor(receiver), unknown_receiver_error) then - return; - end if; - - t_start := now; - if messenger.is_full(receiver, mailbox_id) then - wait on net until not messenger.is_full(receiver, mailbox_id) for timeout; - check(not messenger.is_full(receiver, mailbox_id), full_inbox_error); - end if; - - messenger.send(receiver, mailbox_id, msg); - - if msg.sender /= null_actor then - if messenger.has_subscribers(msg.sender, outbound) then - wait_on_subscribers(msg.sender, (0 => outbound), timeout - (now - t_start)); - messenger.internal_publish(msg.sender, msg, (0 => outbound)); - end if; - end if; - - if (mailbox_id = inbox) and messenger.has_subscribers(receiver, inbound) then - wait_on_subscribers(receiver, (0 => inbound), timeout - (now - t_start)); - messenger.internal_publish(receiver, msg, (0 => inbound)); - end if; - - notify(net); - msg.data := null_queue; - end; - - procedure send ( - signal net : inout network_t; - constant receiver : in actor_t; - variable msg : inout msg_t; - constant timeout : in time := max_timeout) is - begin - send(net, receiver, inbox, msg, timeout); - end; - - procedure send ( - signal net : inout network_t; - constant receivers : in actor_vec_t; - variable msg : inout msg_t; - constant timeout : in time := max_timeout) is - variable msg_to_send : msg_t; - variable t_start : time; - begin - if receivers'length = 0 then - delete(msg); - return; - end if; - - t_start := now; - for i in receivers'range loop - if i = receivers'right then - send(net, receivers(i), msg, timeout - (now - t_start)); - else - msg_to_send := copy(msg); - send(net, receivers(i), msg_to_send, timeout - (now - t_start)); - end if; - end loop; - end; - - procedure receive ( - signal net : inout network_t; - constant receiver : in actor_t; - variable msg : inout msg_t; - constant timeout : in time := max_timeout) is - begin - receive(net, actor_vec_t'(0 => receiver), msg, timeout); - end; - - procedure receive ( - signal net : inout network_t; - constant receivers : in actor_vec_t; - variable msg : inout msg_t; - constant timeout : in time := max_timeout) is - variable status : com_status_t; - variable receiver : actor_t; - begin - delete(msg); - wait_for_message(net, receivers, status, timeout); - if not check(no_error_status(status), status) then - return; - end if; - - for i in receivers'range loop - receiver := receivers(i); - if has_message(receiver) then - get_message(net, receiver, msg); - exit; - end if; - end loop; - end; - - procedure reply ( - signal net : inout network_t; - variable request_msg : inout msg_t; - variable reply_msg : inout msg_t; - constant timeout : in time := max_timeout) is - begin - check(request_msg.id /= no_message_id, reply_missing_request_id_error); - reply_msg.request_id := request_msg.id; - reply_msg.sender := request_msg.receiver; - - if request_msg.sender /= null_actor then - send(net, request_msg.sender, inbox, reply_msg, timeout); - else - send(net, request_msg.receiver, outbox, reply_msg, timeout); - end if; - end; - - procedure receive_reply ( - signal net : inout network_t; - variable request_msg : inout msg_t; - variable reply_msg : inout msg_t; - constant timeout : in time := max_timeout) is - variable status : com_status_t; - variable source_actor : actor_t; - variable mailbox : mailbox_id_t; - variable message : message_ptr_t; - begin - delete(reply_msg); - - wait_for_reply(net, request_msg, status, timeout); - check(no_error_status(status), status); - - get_reply(net, request_msg, reply_msg); - end; - - procedure publish ( - signal net : inout network_t; - constant sender : in actor_t; - variable msg : inout msg_t; - constant timeout : in time := max_timeout) is - begin - wait_on_subscribers(sender, (published, outbound), timeout); - messenger.publish(sender, msg, (published, outbound)); - notify(net); - recycle(queue_pool, msg.data); - end; - - impure function peek_message( - actor : actor_t; - position : natural := 0; - mailbox_id : mailbox_id_t := inbox) return msg_t is - variable msg : msg_t; - begin - if position > messenger.num_of_messages(actor, mailbox_id) - 1 then - failure(com_logger, "Peeking non-existing position."); - return msg; - end if; - - msg := messenger.get_all_but_payload(actor, position, mailbox_id); - msg.data := decode(messenger.get_payload(actor, position, mailbox_id)); - - return msg; - end; - - ----------------------------------------------------------------------------- - -- Secondary send and receive related subprograms - ----------------------------------------------------------------------------- - procedure request ( - signal net : inout network_t; - constant receiver : in actor_t; - variable request_msg : inout msg_t; - variable reply_msg : inout msg_t; - constant timeout : in time := max_timeout) is - variable start : time; - begin - start := now; - send(net, receiver, request_msg, timeout); - receive_reply(net, request_msg, reply_msg, timeout - (now - start)); - end; - - procedure request ( - signal net : inout network_t; - constant receiver : in actor_t; - variable request_msg : inout msg_t; - variable positive_ack : out boolean; - constant timeout : in time := max_timeout) is - variable start : time; - begin - start := now; - send(net, receiver, request_msg, timeout); - receive_reply(net, request_msg, positive_ack, timeout - (now - start)); - end; - - procedure acknowledge ( - signal net : inout network_t; - variable request_msg : inout msg_t; - constant positive_ack : in boolean := true; - constant timeout : in time := max_timeout) is - variable reply_msg : msg_t; - begin - reply_msg := new_msg; - push_boolean(reply_msg, positive_ack); - reply(net, request_msg, reply_msg, timeout); - end; - - procedure receive_reply ( - signal net : inout network_t; - variable request_msg : inout msg_t; - variable positive_ack : out boolean; - constant timeout : in time := max_timeout) is - variable reply_msg : msg_t; - begin - receive_reply(net, request_msg, reply_msg, timeout); - positive_ack := pop_boolean(reply_msg); - delete(reply_msg); - end; - - ----------------------------------------------------------------------------- - -- Low-level subprograms primarily used for handling timeout wihout error - ----------------------------------------------------------------------------- - procedure wait_for_message ( - signal net : in network_t; - constant receiver : in actor_t; - variable status : out com_status_t; - constant timeout : in time := max_timeout) is - begin - wait_for_message(net, actor_vec_t'(0 => receiver), status, timeout); - end procedure wait_for_message; - - procedure wait_for_message ( - signal net : in network_t; - constant receivers : in actor_vec_t; - variable status : out com_status_t; - constant timeout : in time := max_timeout) is - begin - for i in receivers'range loop - if not check(not messenger.deferred(receivers(i)), deferred_receiver_error) then - status := deferred_receiver_error; - return; - end if; - end loop; - - status := ok; - if not messenger.has_messages(receivers) then - wait on net until messenger.has_messages(receivers) for timeout; - if not messenger.has_messages(receivers) then - status := work.com_types_pkg.timeout; - end if; - end if; - end procedure wait_for_message; - - impure function has_message (actor : actor_t) return boolean is - begin - return messenger.has_messages(actor); - end function has_message; - - procedure get_message ( - signal net : inout network_t; - actor : actor_t; - position : natural; - mailbox_id : mailbox_id_t; - variable msg : inout msg_t) is - variable started_with_full_mailbox : boolean; - begin - started_with_full_mailbox := messenger.is_full(actor, mailbox_id); - - msg := messenger.get_all_but_payload(actor, position, mailbox_id); - msg.data := decode(messenger.get_payload(actor, position, mailbox_id)); - messenger.delete_envelope(actor, position, mailbox_id); - - if started_with_full_mailbox then - notify(net); - end if; - end; - - procedure get_message (signal net : inout network_t; receiver : actor_t; variable msg : inout msg_t) is - begin - check(messenger.has_messages(receiver), null_message_error); - get_message(net, receiver, 0, inbox, msg); - end; - - procedure wait_for_reply_message ( - signal net : inout network_t; - constant actor : in actor_t; - constant mailbox_id : in mailbox_id_t := inbox; - constant request_id : in message_id_t; - variable status : out com_status_t; - constant timeout : in time := max_timeout) is - - begin - check(not messenger.deferred(actor), deferred_receiver_error); - - status := ok; - if messenger.find_reply_message(actor, request_id, mailbox_id) = -1 then - wait on net until messenger.find_reply_message(actor, request_id, mailbox_id) /= -1 for timeout; - if messenger.find_reply_message(actor, request_id, mailbox_id) = -1 then - status := work.com_types_pkg.timeout; - end if; - end if; - end procedure; - - procedure wait_for_reply ( - signal net : inout network_t; - variable request_msg : inout msg_t; - variable status : out com_status_t; - constant timeout : in time := max_timeout) is - variable source_actor : actor_t; - variable mailbox : mailbox_id_t; - begin - source_actor := request_msg.sender when request_msg.sender /= null_actor else request_msg.receiver; - mailbox := inbox when request_msg.sender /= null_actor else outbox; - - wait_for_reply_message(net, source_actor, mailbox, request_msg.id, status, timeout); - end; - - procedure get_reply ( - signal net : inout network_t; - variable request_msg : inout msg_t; - variable reply_msg : inout msg_t) is - variable source_actor : actor_t; - variable mailbox_id : mailbox_id_t; - variable position : integer; - begin - source_actor := request_msg.sender when request_msg.sender /= null_actor else request_msg.receiver; - mailbox_id := inbox when request_msg.sender /= null_actor else outbox; - position := messenger.find_reply_message(source_actor, request_msg.id, mailbox_id); - - check(position /= -1, null_message_error); - - get_message(net, source_actor, position, mailbox_id, reply_msg); - end; - - ----------------------------------------------------------------------------- - -- Subscriptions - ----------------------------------------------------------------------------- - procedure subscribe ( - subscriber : actor_t; - publisher : actor_t; - traffic_type : subscription_traffic_type_t := published) is - begin - messenger.subscribe(subscriber, publisher, traffic_type); - end procedure subscribe; - - procedure unsubscribe ( - subscriber : actor_t; - publisher : actor_t; - traffic_type : subscription_traffic_type_t := published) is - begin - messenger.unsubscribe(subscriber, publisher, traffic_type); - end procedure unsubscribe; - - ----------------------------------------------------------------------------- - -- Debugging - ----------------------------------------------------------------------------- - impure function to_string(msg : msg_t) return string is - begin - return messenger.to_string(msg); - end; - - impure function peek_all_messages(actor : actor_t; mailbox_id : mailbox_id_t := inbox) return msg_vec_ptr_t is - variable msg_vec_ptr : msg_vec_ptr_t; - constant n_messages : natural := messenger.num_of_messages(actor, mailbox_id); - begin - if n_messages = 0 then - return null; - end if; - - msg_vec_ptr := new msg_vec_t(0 to n_messages - 1); - for i in msg_vec_ptr'range loop - msg_vec_ptr(i) := peek_message(actor, i, mailbox_id); - end loop; - - return msg_vec_ptr; - end; - - impure function get_mailbox_state(actor : actor_t; mailbox_id : mailbox_id_t := inbox) return mailbox_state_t is - variable state : mailbox_state_t; - begin - state.id := mailbox_id; - state.size := mailbox_size(actor, mailbox_id); - state.messages := peek_all_messages(actor, mailbox_id); - - return state; - end; - - procedure deallocate(variable mailbox_state : inout mailbox_state_t) is - begin - mailbox_state.id := inbox; - mailbox_state.size := 0; - deallocate(mailbox_state.messages); - end; - - impure function get_mailbox_state_string ( - actor : actor_t; - mailbox_id : mailbox_id_t := inbox; - indent : string := "") return string is - variable messages : msg_vec_ptr_t := peek_all_messages(actor, mailbox_id); - variable l : line; - begin - write(l, indent & "Mailbox: " & mailbox_id_t'image(mailbox_id) & LF); - write(l, indent & " Size: " & to_string(mailbox_size(actor, mailbox_id)) & LF); - write(l, indent & " Messages:"); - if messages /= null then - for i in messages'range loop - write(l, LF & indent & " " & to_string(i) & ". " & messenger.to_string(messages(i))); - end loop; - end if; - - deallocate(messages); - - return l.all; - end; - - impure function get_subscriptions(subscriber : actor_t) return subscription_vec_ptr_t is - constant subscriptions : subscription_vec_t := messenger.get_subscriptions(subscriber); - begin - if subscriptions'length = 0 then - return null; - else - return new subscription_vec_t'(subscriptions); - end if; - end; - - impure function get_subscribers(publisher : actor_t) return subscription_vec_ptr_t is - constant subscriptions : subscription_vec_t := messenger.get_subscribers(publisher); - begin - if subscriptions'length = 0 then - return null; - else - return new subscription_vec_t'(subscriptions); - end if; - end; - - impure function get_actor_state(actor : actor_t) return actor_state_t is - variable state : actor_state_t; - begin - write(state.name, name(actor)); - state.is_deferred := is_deferred(actor); - state.inbox := get_mailbox_state(actor, inbox); - state.outbox := get_mailbox_state(actor, outbox); - state.subscriptions := get_subscriptions(actor); - state.subscribers := get_subscribers(actor); - - return state; - end; - - procedure deallocate(variable actor_state : inout actor_state_t) is - begin - deallocate(actor_state.name); - actor_state.is_deferred := false; - deallocate(actor_state.inbox); - deallocate(actor_state.outbox); - deallocate(actor_state.subscriptions); - deallocate(actor_state.subscribers); - end; - - impure function get_actor_state_string (actor : actor_t; indent : string := "") return string is - variable state : actor_state_t := get_actor_state(actor); - variable l : line; - variable traffic_type : subscription_traffic_type_t; - begin - write(l, indent & "Name: " & state.name.all & LF); - - write(l, indent & " Is deferred: "); - if state.is_deferred then - write(l, "yes" & LF); - else - write(l, "no" & LF); - end if; - - write(l, get_mailbox_state_string(actor, inbox, indent & " ") & LF); - write(l, get_mailbox_state_string(actor, outbox, indent & " ") & LF); - - write(l, indent & " Subscriptions:"); - if state.subscriptions /= null then - for i in state.subscriptions'range loop - traffic_type := state.subscriptions(i).traffic_type; - write(l, LF & indent & " " & subscription_traffic_type_t'image(traffic_type) & " traffic "); - if state.subscriptions(i).traffic_type = inbound then - write(l, indent & "to "); - else - write(l, indent & "from "); - end if; - write(l, name(state.subscriptions(i).publisher)); - end loop; - end if; - - write(l, LF & indent & " Subscribers:"); - if state.Subscribers /= null then - for i in state.subscribers'range loop - write(l, LF & indent & " " & name(state.subscribers(i).subscriber) & " subscribes to "); - write(l, subscription_traffic_type_t'image(state.subscribers(i).traffic_type) & " traffic"); - end loop; - end if; - - return l.all; - end; - - impure function get_messenger_state return messenger_state_t is - variable state : messenger_state_t; - constant actors : actor_vec_t := messenger.get_all_actors; - constant n_deferred : natural := messenger.num_of_deferred_creations; - constant n_active : natural := actors'length - n_deferred; - variable active_idx, deferred_idx : natural := 0; - begin - if n_active > 0 then - state.active_actors := new actor_state_vec_t(0 to n_active - 1); - end if; - - if n_deferred > 0 then - state.deferred_actors := new actor_state_vec_t(0 to n_deferred - 1); - end if; - - for i in actors'range loop - if is_deferred(actors(i)) then - state.deferred_actors(deferred_idx) := get_actor_state(actors(i)); - deferred_idx := deferred_idx + 1; - else - state.active_actors(active_idx) := get_actor_state(actors(i)); - active_idx := active_idx + 1; - end if; - end loop; - - return state; - end; - - procedure deallocate(variable messenger_state : inout messenger_state_t) is - begin - if messenger_state.active_actors /= null then - for a in messenger_state.active_actors'range loop - deallocate(messenger_state.active_actors(a)); - end loop; - deallocate(messenger_state.active_actors); - end if; - - if messenger_state.deferred_actors /= null then - for a in messenger_state.deferred_actors'range loop - deallocate(messenger_state.deferred_actors(a)); - end loop; - deallocate(messenger_state.deferred_actors); - end if; - end; - - impure function get_messenger_state_string(indent : string := "") return string is - constant actors : actor_vec_t := messenger.get_all_actors; - variable l, active_actors, deferred_actors : line; - variable first_deferred : boolean := true; - begin - for i in actors'range loop - if is_deferred(actors(i)) then - if first_deferred then - first_deferred := false; - else - write(deferred_actors, LF & LF); - end if; - write(deferred_actors, get_actor_state_string(actors(i), indent & " ")); - else - write(active_actors, get_actor_state_string(actors(i), indent & " ") & LF & LF); - end if; - end loop; - - - write(l, indent & "Active actors:" & LF); - if active_actors /= null then - write(l, active_actors.all); - end if; - - write(l, indent & "Deferred actors:"); - if deferred_actors /= null then - write(l, LF & deferred_actors.all); - end if; - - return l.all; - end; - - ----------------------------------------------------------------------------- - -- Misc - ----------------------------------------------------------------------------- - procedure allow_timeout is - begin - messenger.allow_timeout; - end; - - procedure allow_deprecated is - begin - messenger.allow_deprecated; - end; - -end package body com_pkg; +-- Com package provides a generic communication mechanism for testbenches +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +context work.vunit_context; + +use work.queue_pkg.all; +use work.queue_2008_pkg.all; +use work.queue_pool_pkg.all; +use work.integer_vector_ptr_pkg.all; +use work.string_ptr_pkg.all; +use work.codec_pkg.all; +use work.com_support_pkg.all; +use work.com_messenger_pkg.all; +use work.com_common_pkg.all; +use work.logger_pkg.all; + +use std.textio.all; + +package body com_pkg is + ----------------------------------------------------------------------------- + -- Handling of actors + ----------------------------------------------------------------------------- + impure function new_actor ( + name : string := ""; + inbox_size : positive := positive'high; + outbox_size : positive := positive'high + ) return actor_t is + begin + return messenger.create(name, inbox_size, outbox_size); + end; + + impure function find (name : string; enable_deferred_creation : boolean := true) return actor_t is + begin + return messenger.find(name, enable_deferred_creation); + end; + + impure function name (actor : actor_t) return string is + begin + return messenger.name(actor); + end; + + procedure destroy (actor : inout actor_t) is + begin + messenger.destroy(actor); + end; + + procedure reset_messenger is + begin + messenger.reset_messenger; + end; + + impure function num_of_actors + return natural is + begin + return messenger.num_of_actors; + end; + + impure function is_deferred(actor : actor_t) return boolean is + begin + return messenger.is_deferred(actor); + end; + + impure function num_of_deferred_creations + return natural is + begin + return messenger.num_of_deferred_creations; + end; + + impure function num_of_messages (actor : actor_t; mailbox_id : mailbox_id_t := inbox) return natural is + begin + return messenger.num_of_messages(actor, mailbox_id); + end; + + impure function mailbox_size (actor : actor_t; mailbox_id : mailbox_id_t := inbox) return natural is + begin + return messenger.mailbox_size(actor, mailbox_id); + end; + + procedure resize_mailbox (actor : actor_t; new_size : natural; mailbox_id : mailbox_id_t := inbox) is + begin + messenger.resize_mailbox(actor, new_size, mailbox_id); + end; + + ----------------------------------------------------------------------------- + -- Primary send and receive related subprograms + ----------------------------------------------------------------------------- + procedure wait_on_subscribers ( + publisher : actor_t; + subscription_traffic_types : subscription_traffic_types_t; + timeout : time) is + begin + if messenger.subscriber_inbox_is_full(publisher, subscription_traffic_types) then + wait on net until not messenger.subscriber_inbox_is_full(publisher, subscription_traffic_types) for timeout; + check(not messenger.subscriber_inbox_is_full(publisher, subscription_traffic_types), full_inbox_error); + end if; + end procedure wait_on_subscribers; + + procedure send ( + signal net : inout network_t; + constant receiver : in actor_t; + constant mailbox_id : in mailbox_id_t; + variable msg : inout msg_t; + constant timeout : in time := max_timeout) is + variable t_start : time; + begin + if not check(msg.data /= null_queue, null_message_error) then + return; + end if; + + if not check(not messenger.unknown_actor(receiver), unknown_receiver_error) then + return; + end if; + + t_start := now; + if messenger.is_full(receiver, mailbox_id) then + wait on net until not messenger.is_full(receiver, mailbox_id) for timeout; + check(not messenger.is_full(receiver, mailbox_id), full_inbox_error); + end if; + + messenger.send(receiver, mailbox_id, msg); + + if msg.sender /= null_actor then + if messenger.has_subscribers(msg.sender, outbound) then + wait_on_subscribers(msg.sender, (0 => outbound), timeout - (now - t_start)); + messenger.internal_publish(msg.sender, msg, (0 => outbound)); + end if; + end if; + + if (mailbox_id = inbox) and messenger.has_subscribers(receiver, inbound) then + wait_on_subscribers(receiver, (0 => inbound), timeout - (now - t_start)); + messenger.internal_publish(receiver, msg, (0 => inbound)); + end if; + + notify(net); + msg.data := null_queue; + end; + + procedure send ( + signal net : inout network_t; + constant receiver : in actor_t; + variable msg : inout msg_t; + constant timeout : in time := max_timeout) is + begin + send(net, receiver, inbox, msg, timeout); + end; + + procedure send ( + signal net : inout network_t; + constant receivers : in actor_vec_t; + variable msg : inout msg_t; + constant timeout : in time := max_timeout) is + variable msg_to_send : msg_t; + variable t_start : time; + begin + if receivers'length = 0 then + delete(msg); + return; + end if; + + t_start := now; + for i in receivers'range loop + if i = receivers'right then + send(net, receivers(i), msg, timeout - (now - t_start)); + else + msg_to_send := copy(msg); + send(net, receivers(i), msg_to_send, timeout - (now - t_start)); + end if; + end loop; + end; + + procedure receive ( + signal net : inout network_t; + constant receiver : in actor_t; + variable msg : inout msg_t; + constant timeout : in time := max_timeout) is + begin + receive(net, actor_vec_t'(0 => receiver), msg, timeout); + end; + + procedure receive ( + signal net : inout network_t; + constant receivers : in actor_vec_t; + variable msg : inout msg_t; + constant timeout : in time := max_timeout) is + variable status : com_status_t; + variable receiver : actor_t; + begin + delete(msg); + wait_for_message(net, receivers, status, timeout); + if not check(no_error_status(status), status) then + return; + end if; + + for i in receivers'range loop + receiver := receivers(i); + if has_message(receiver) then + get_message(net, receiver, msg); + exit; + end if; + end loop; + end; + + procedure reply ( + signal net : inout network_t; + variable request_msg : inout msg_t; + variable reply_msg : inout msg_t; + constant timeout : in time := max_timeout) is + begin + check(request_msg.id /= no_message_id, reply_missing_request_id_error); + reply_msg.request_id := request_msg.id; + reply_msg.sender := request_msg.receiver; + + if request_msg.sender /= null_actor then + send(net, request_msg.sender, inbox, reply_msg, timeout); + else + send(net, request_msg.receiver, outbox, reply_msg, timeout); + end if; + end; + + procedure receive_reply ( + signal net : inout network_t; + variable request_msg : inout msg_t; + variable reply_msg : inout msg_t; + constant timeout : in time := max_timeout) is + variable status : com_status_t; + variable source_actor : actor_t; + variable mailbox : mailbox_id_t; + variable message : message_ptr_t; + begin + delete(reply_msg); + + wait_for_reply(net, request_msg, status, timeout); + check(no_error_status(status), status); + + get_reply(net, request_msg, reply_msg); + end; + + procedure publish ( + signal net : inout network_t; + constant sender : in actor_t; + variable msg : inout msg_t; + constant timeout : in time := max_timeout) is + begin + wait_on_subscribers(sender, (published, outbound), timeout); + messenger.publish(sender, msg, (published, outbound)); + notify(net); + recycle(queue_pool, msg.data); + end; + + impure function peek_message( + actor : actor_t; + position : natural := 0; + mailbox_id : mailbox_id_t := inbox) return msg_t is + variable msg : msg_t; + begin + if position > messenger.num_of_messages(actor, mailbox_id) - 1 then + failure(com_logger, "Peeking non-existing position."); + return msg; + end if; + + msg := messenger.get_all_but_payload(actor, position, mailbox_id); + msg.data := decode(messenger.get_payload(actor, position, mailbox_id)); + + return msg; + end; + + ----------------------------------------------------------------------------- + -- Secondary send and receive related subprograms + ----------------------------------------------------------------------------- + procedure request ( + signal net : inout network_t; + constant receiver : in actor_t; + variable request_msg : inout msg_t; + variable reply_msg : inout msg_t; + constant timeout : in time := max_timeout) is + variable start : time; + begin + start := now; + send(net, receiver, request_msg, timeout); + receive_reply(net, request_msg, reply_msg, timeout - (now - start)); + end; + + procedure request ( + signal net : inout network_t; + constant receiver : in actor_t; + variable request_msg : inout msg_t; + variable positive_ack : out boolean; + constant timeout : in time := max_timeout) is + variable start : time; + begin + start := now; + send(net, receiver, request_msg, timeout); + receive_reply(net, request_msg, positive_ack, timeout - (now - start)); + end; + + procedure acknowledge ( + signal net : inout network_t; + variable request_msg : inout msg_t; + constant positive_ack : in boolean := true; + constant timeout : in time := max_timeout) is + variable reply_msg : msg_t; + begin + reply_msg := new_msg; + push_boolean(reply_msg, positive_ack); + reply(net, request_msg, reply_msg, timeout); + end; + + procedure receive_reply ( + signal net : inout network_t; + variable request_msg : inout msg_t; + variable positive_ack : out boolean; + constant timeout : in time := max_timeout) is + variable reply_msg : msg_t; + begin + receive_reply(net, request_msg, reply_msg, timeout); + positive_ack := pop_boolean(reply_msg); + delete(reply_msg); + end; + + ----------------------------------------------------------------------------- + -- Low-level subprograms primarily used for handling timeout wihout error + ----------------------------------------------------------------------------- + procedure wait_for_message ( + signal net : in network_t; + constant receiver : in actor_t; + variable status : out com_status_t; + constant timeout : in time := max_timeout) is + begin + wait_for_message(net, actor_vec_t'(0 => receiver), status, timeout); + end procedure wait_for_message; + + procedure wait_for_message ( + signal net : in network_t; + constant receivers : in actor_vec_t; + variable status : out com_status_t; + constant timeout : in time := max_timeout) is + begin + for i in receivers'range loop + if not check(not messenger.deferred(receivers(i)), deferred_receiver_error) then + status := deferred_receiver_error; + return; + end if; + end loop; + + status := ok; + if not messenger.has_messages(receivers) then + wait on net until messenger.has_messages(receivers) for timeout; + if not messenger.has_messages(receivers) then + status := work.com_types_pkg.timeout; + end if; + end if; + end procedure wait_for_message; + + impure function has_message (actor : actor_t) return boolean is + begin + return messenger.has_messages(actor); + end function has_message; + + procedure get_message ( + signal net : inout network_t; + actor : actor_t; + position : natural; + mailbox_id : mailbox_id_t; + variable msg : inout msg_t) is + variable started_with_full_mailbox : boolean; + begin + started_with_full_mailbox := messenger.is_full(actor, mailbox_id); + + msg := messenger.get_all_but_payload(actor, position, mailbox_id); + msg.data := decode(messenger.get_payload(actor, position, mailbox_id)); + messenger.delete_envelope(actor, position, mailbox_id); + + if started_with_full_mailbox then + notify(net); + end if; + end; + + procedure get_message (signal net : inout network_t; receiver : actor_t; variable msg : inout msg_t) is + begin + check(messenger.has_messages(receiver), null_message_error); + get_message(net, receiver, 0, inbox, msg); + end; + + procedure wait_for_reply_message ( + signal net : inout network_t; + constant actor : in actor_t; + constant mailbox_id : in mailbox_id_t := inbox; + constant request_id : in message_id_t; + variable status : out com_status_t; + constant timeout : in time := max_timeout) is + + begin + check(not messenger.deferred(actor), deferred_receiver_error); + + status := ok; + if messenger.find_reply_message(actor, request_id, mailbox_id) = -1 then + wait on net until messenger.find_reply_message(actor, request_id, mailbox_id) /= -1 for timeout; + if messenger.find_reply_message(actor, request_id, mailbox_id) = -1 then + status := work.com_types_pkg.timeout; + end if; + end if; + end procedure; + + procedure wait_for_reply ( + signal net : inout network_t; + variable request_msg : inout msg_t; + variable status : out com_status_t; + constant timeout : in time := max_timeout) is + variable source_actor : actor_t; + variable mailbox : mailbox_id_t; + begin + source_actor := request_msg.sender when request_msg.sender /= null_actor else request_msg.receiver; + mailbox := inbox when request_msg.sender /= null_actor else outbox; + + wait_for_reply_message(net, source_actor, mailbox, request_msg.id, status, timeout); + end; + + procedure get_reply ( + signal net : inout network_t; + variable request_msg : inout msg_t; + variable reply_msg : inout msg_t) is + variable source_actor : actor_t; + variable mailbox_id : mailbox_id_t; + variable position : integer; + begin + source_actor := request_msg.sender when request_msg.sender /= null_actor else request_msg.receiver; + mailbox_id := inbox when request_msg.sender /= null_actor else outbox; + position := messenger.find_reply_message(source_actor, request_msg.id, mailbox_id); + + check(position /= -1, null_message_error); + + get_message(net, source_actor, position, mailbox_id, reply_msg); + end; + + ----------------------------------------------------------------------------- + -- Subscriptions + ----------------------------------------------------------------------------- + procedure subscribe ( + subscriber : actor_t; + publisher : actor_t; + traffic_type : subscription_traffic_type_t := published) is + begin + messenger.subscribe(subscriber, publisher, traffic_type); + end procedure subscribe; + + procedure unsubscribe ( + subscriber : actor_t; + publisher : actor_t; + traffic_type : subscription_traffic_type_t := published) is + begin + messenger.unsubscribe(subscriber, publisher, traffic_type); + end procedure unsubscribe; + + ----------------------------------------------------------------------------- + -- Debugging + ----------------------------------------------------------------------------- + impure function to_string(msg : msg_t) return string is + begin + return messenger.to_string(msg); + end; + + impure function peek_all_messages(actor : actor_t; mailbox_id : mailbox_id_t := inbox) return msg_vec_ptr_t is + variable msg_vec_ptr : msg_vec_ptr_t; + constant n_messages : natural := messenger.num_of_messages(actor, mailbox_id); + begin + if n_messages = 0 then + return null; + end if; + + msg_vec_ptr := new msg_vec_t(0 to n_messages - 1); + for i in msg_vec_ptr'range loop + msg_vec_ptr(i) := peek_message(actor, i, mailbox_id); + end loop; + + return msg_vec_ptr; + end; + + impure function get_mailbox_state(actor : actor_t; mailbox_id : mailbox_id_t := inbox) return mailbox_state_t is + variable state : mailbox_state_t; + begin + state.id := mailbox_id; + state.size := mailbox_size(actor, mailbox_id); + state.messages := peek_all_messages(actor, mailbox_id); + + return state; + end; + + procedure deallocate(variable mailbox_state : inout mailbox_state_t) is + begin + mailbox_state.id := inbox; + mailbox_state.size := 0; + deallocate(mailbox_state.messages); + end; + + impure function get_mailbox_state_string ( + actor : actor_t; + mailbox_id : mailbox_id_t := inbox; + indent : string := "") return string is + variable messages : msg_vec_ptr_t := peek_all_messages(actor, mailbox_id); + variable l : line; + begin + write(l, indent & "Mailbox: " & mailbox_id_t'image(mailbox_id) & LF); + write(l, indent & " Size: " & to_string(mailbox_size(actor, mailbox_id)) & LF); + write(l, indent & " Messages:"); + if messages /= null then + for i in messages'range loop + write(l, LF & indent & " " & to_string(i) & ". " & messenger.to_string(messages(i))); + end loop; + end if; + + deallocate(messages); + + return l.all; + end; + + impure function get_subscriptions(subscriber : actor_t) return subscription_vec_ptr_t is + constant subscriptions : subscription_vec_t := messenger.get_subscriptions(subscriber); + begin + if subscriptions'length = 0 then + return null; + else + return new subscription_vec_t'(subscriptions); + end if; + end; + + impure function get_subscribers(publisher : actor_t) return subscription_vec_ptr_t is + constant subscriptions : subscription_vec_t := messenger.get_subscribers(publisher); + begin + if subscriptions'length = 0 then + return null; + else + return new subscription_vec_t'(subscriptions); + end if; + end; + + impure function get_actor_state(actor : actor_t) return actor_state_t is + variable state : actor_state_t; + begin + write(state.name, name(actor)); + state.is_deferred := is_deferred(actor); + state.inbox := get_mailbox_state(actor, inbox); + state.outbox := get_mailbox_state(actor, outbox); + state.subscriptions := get_subscriptions(actor); + state.subscribers := get_subscribers(actor); + + return state; + end; + + procedure deallocate(variable actor_state : inout actor_state_t) is + begin + deallocate(actor_state.name); + actor_state.is_deferred := false; + deallocate(actor_state.inbox); + deallocate(actor_state.outbox); + deallocate(actor_state.subscriptions); + deallocate(actor_state.subscribers); + end; + + impure function get_actor_state_string (actor : actor_t; indent : string := "") return string is + variable state : actor_state_t := get_actor_state(actor); + variable l : line; + variable traffic_type : subscription_traffic_type_t; + begin + write(l, indent & "Name: " & state.name.all & LF); + + write(l, indent & " Is deferred: "); + if state.is_deferred then + write(l, "yes" & LF); + else + write(l, "no" & LF); + end if; + + write(l, get_mailbox_state_string(actor, inbox, indent & " ") & LF); + write(l, get_mailbox_state_string(actor, outbox, indent & " ") & LF); + + write(l, indent & " Subscriptions:"); + if state.subscriptions /= null then + for i in state.subscriptions'range loop + traffic_type := state.subscriptions(i).traffic_type; + write(l, LF & indent & " " & subscription_traffic_type_t'image(traffic_type) & " traffic "); + if state.subscriptions(i).traffic_type = inbound then + write(l, indent & "to "); + else + write(l, indent & "from "); + end if; + write(l, name(state.subscriptions(i).publisher)); + end loop; + end if; + + write(l, LF & indent & " Subscribers:"); + if state.Subscribers /= null then + for i in state.subscribers'range loop + write(l, LF & indent & " " & name(state.subscribers(i).subscriber) & " subscribes to "); + write(l, subscription_traffic_type_t'image(state.subscribers(i).traffic_type) & " traffic"); + end loop; + end if; + + return l.all; + end; + + impure function get_messenger_state return messenger_state_t is + variable state : messenger_state_t; + constant actors : actor_vec_t := messenger.get_all_actors; + constant n_deferred : natural := messenger.num_of_deferred_creations; + constant n_active : natural := actors'length - n_deferred; + variable active_idx, deferred_idx : natural := 0; + begin + if n_active > 0 then + state.active_actors := new actor_state_vec_t(0 to n_active - 1); + end if; + + if n_deferred > 0 then + state.deferred_actors := new actor_state_vec_t(0 to n_deferred - 1); + end if; + + for i in actors'range loop + if is_deferred(actors(i)) then + state.deferred_actors(deferred_idx) := get_actor_state(actors(i)); + deferred_idx := deferred_idx + 1; + else + state.active_actors(active_idx) := get_actor_state(actors(i)); + active_idx := active_idx + 1; + end if; + end loop; + + return state; + end; + + procedure deallocate(variable messenger_state : inout messenger_state_t) is + begin + if messenger_state.active_actors /= null then + for a in messenger_state.active_actors'range loop + deallocate(messenger_state.active_actors(a)); + end loop; + deallocate(messenger_state.active_actors); + end if; + + if messenger_state.deferred_actors /= null then + for a in messenger_state.deferred_actors'range loop + deallocate(messenger_state.deferred_actors(a)); + end loop; + deallocate(messenger_state.deferred_actors); + end if; + end; + + impure function get_messenger_state_string(indent : string := "") return string is + constant actors : actor_vec_t := messenger.get_all_actors; + variable l, active_actors, deferred_actors : line; + variable first_deferred : boolean := true; + begin + for i in actors'range loop + if is_deferred(actors(i)) then + if first_deferred then + first_deferred := false; + else + write(deferred_actors, LF & LF); + end if; + write(deferred_actors, get_actor_state_string(actors(i), indent & " ")); + else + write(active_actors, get_actor_state_string(actors(i), indent & " ") & LF & LF); + end if; + end loop; + + + write(l, indent & "Active actors:" & LF); + if active_actors /= null then + write(l, active_actors.all); + end if; + + write(l, indent & "Deferred actors:"); + if deferred_actors /= null then + write(l, LF & deferred_actors.all); + end if; + + return l.all; + end; + + ----------------------------------------------------------------------------- + -- Misc + ----------------------------------------------------------------------------- + procedure allow_timeout is + begin + messenger.allow_timeout; + end; + + procedure allow_deprecated is + begin + messenger.allow_deprecated; + end; + +end package body com_pkg; diff --git a/vunit/vhdl/com/src/com_api.vhd b/vunit/vhdl/com/src/com_api.vhd index 532bcce59..dfc5ba26b 100644 --- a/vunit/vhdl/com/src/com_api.vhd +++ b/vunit/vhdl/com/src/com_api.vhd @@ -1,281 +1,281 @@ --- Com API package provides the common API for all --- implementations of the com functionality (VHDL 2002+ and VHDL 1993) --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -use work.com_types_pkg.all; -use work.queue_pkg.all; -use work.integer_vector_ptr_pkg.all; -use work.string_ptr_pkg.all; - -package com_pkg is - -- Global predefined network. See network_t description in com_types.vhd for - -- more information. - signal net : network_t := idle_network; - - ----------------------------------------------------------------------------- - -- Handling of actors - ----------------------------------------------------------------------------- - -- Create a new actor. Any number of unnamed actors (name = "") can be - -- created. Named actors must be unique - impure function new_actor ( - name : string := ""; - inbox_size : positive := positive'high; - outbox_size : positive := positive'high - ) return actor_t; - - -- Find named actor by name. Enable deferred creation to create a deferred - -- actor when no actor is found - impure function find (name : string; enable_deferred_creation : boolean := true) return actor_t; - - -- Name of actor - impure function name (actor : actor_t) return string; - - -- Destroy actor. Mailboxes are deallocated and dependent subscriptions are - -- removed. Returns null_actor. - procedure destroy (actor : inout actor_t); - - -- Reset communication system. All actors are destroyed. - procedure reset_messenger; - - -- Check if an actor's creation is deferred - impure function is_deferred(actor : actor_t) return boolean; - - -- Total number of actors with deferred creation - impure function num_of_deferred_creations return natural; - - -- Total number of actors - impure function num_of_actors return natural; - - -- Number of messages in actor mailbox - impure function num_of_messages (actor : actor_t; mailbox_id : mailbox_id_t := inbox) return natural; - - -- Return the maximum number of messages that can be stored in an inbox - impure function mailbox_size (actor : actor_t; mailbox_id : mailbox_id_t := inbox) return natural; - - -- Resize actor mailbox. Reducing size below the number of messages in the - -- mailbox in runtime error - procedure resize_mailbox (actor : actor_t; new_size : natural; mailbox_id : mailbox_id_t := inbox); - - ----------------------------------------------------------------------------- - -- Primary send and receive related subprograms - -- - -- All timeouts will result in a runtime error unless otherwise noted. - ----------------------------------------------------------------------------- - - -- Send message to receiver. Blocking if reciever or any subscriber inbox is - -- full. - procedure send ( - signal net : inout network_t; - constant receiver : in actor_t; - variable msg : inout msg_t; - constant timeout : in time := max_timeout); - - -- Send message to an array of receivers. Blocking if any reciever or any subscriber inbox is - -- full. - procedure send ( - signal net : inout network_t; - constant receivers : in actor_vec_t; - variable msg : inout msg_t; - constant timeout : in time := max_timeout); - - -- Receive message sent to receiver. Returns oldest message or the first - -- incoming if the inbox is empty. msg is initially deleted. - procedure receive ( - signal net : inout network_t; - constant receiver : in actor_t; - variable msg : inout msg_t; - constant timeout : in time := max_timeout); - - -- Receive message sent to any of the receivers. Returns oldest message or the first - -- incoming if the inboxes are empty. Receiver inboxes are emptied from left - -- to right. msg is initially deleted. - procedure receive ( - signal net : inout network_t; - constant receivers : in actor_vec_t; - variable msg : inout msg_t; - constant timeout : in time := max_timeout); - - -- Reply to request_msg with reply_msg. request_msg may be anonymous. Blocking if reciever - -- or any subscriber inbox is full. - procedure reply ( - signal net : inout network_t; - variable request_msg : inout msg_t; - variable reply_msg : inout msg_t; - constant timeout : in time := max_timeout); - - -- Receive a reply_msg to request_msg. request_msg may be anonymous. reply_msg is initially deleted. - procedure receive_reply ( - signal net : inout network_t; - variable request_msg : inout msg_t; - variable reply_msg : inout msg_t; - constant timeout : in time := max_timeout); - - -- Publish a message from sender to all its subscribers. Blocking if reciever or any subscriber inbox is - -- full. - procedure publish ( - signal net : inout network_t; - constant sender : in actor_t; - variable msg : inout msg_t; - constant timeout : in time := max_timeout); - - -- Peek at message in actor mailbox but don't remove it. Position 0 is the oldest message. Runtime error if - -- position doesn't exist. - impure function peek_message( - actor : actor_t; - position : natural := 0; - mailbox_id : mailbox_id_t := inbox) return msg_t; - - ----------------------------------------------------------------------------- - -- Secondary send and receive related subprograms - -- - -- All timeouts will result in a runtime error unless otherwise noted. - ----------------------------------------------------------------------------- - - -- Positive or negative acknowledge of a request_msg. Same as a reply with a - -- boolean reply message. - procedure acknowledge ( - signal net : inout network_t; - variable request_msg : inout msg_t; - constant positive_ack : in boolean := true; - constant timeout : in time := max_timeout); - - -- Receive positive or negative acknowledge for a request_msg. request_msg - -- may be anonymous. reply_msg is initially deleted. - procedure receive_reply ( - signal net : inout network_t; - variable request_msg : inout msg_t; - variable positive_ack : out boolean; - constant timeout : in time := max_timeout); - - -- This request is the same as send of request_msg to receiver followed by a - -- receive_reply of a reply_msg - procedure request ( - signal net : inout network_t; - constant receiver : in actor_t; - variable request_msg : inout msg_t; - variable reply_msg : inout msg_t; - constant timeout : in time := max_timeout); - - -- This request is the same as send of request_msg to receiver followed by a - -- receive_reply of a positive or negative acknowledge. - procedure request ( - signal net : inout network_t; - constant receiver : in actor_t; - variable request_msg : inout msg_t; - variable positive_ack : out boolean; - constant timeout : in time := max_timeout); - - ----------------------------------------------------------------------------- - -- Low-level subprograms primarily used for handling timeout wihout error - ----------------------------------------------------------------------------- - - -- Wait for message sent to receiver. status = ok if message is - -- received before the timeout, status = timeout otherwise. - procedure wait_for_message ( - signal net : in network_t; - constant receiver : in actor_t; - variable status : out com_status_t; - constant timeout : in time := max_timeout); - - -- Wait for message sent to any of the listed receivers. status = ok - -- if message is received before the timeout, status = timeout otherwise. - procedure wait_for_message ( - signal net : in network_t; - constant receivers : in actor_vec_t; - variable status : out com_status_t; - constant timeout : in time := max_timeout); - - -- Returns true if there is at least one message in the actor's inbox. - impure function has_message (actor : actor_t) return boolean; - - -- Wait for reply to request_msg. status = ok - -- if message is received before the timeout, status = timeout otherwise. - procedure wait_for_reply ( - signal net : inout network_t; - variable request_msg : inout msg_t; - variable status : out com_status_t; - constant timeout : in time := max_timeout); - - -- Get oldest message from receiver inbox. Runtime error if inbox is empty. - procedure get_message (signal net : inout network_t; receiver : actor_t; variable msg : inout msg_t); - - -- Get reply message to request_msg. Runtime error if reply message isn't available. - procedure get_reply ( - signal net : inout network_t; - variable request_msg : inout msg_t; - variable reply_msg : inout msg_t); - - ----------------------------------------------------------------------------- - -- Subscriptions - ----------------------------------------------------------------------------- - - -- Make subscriber subscribe on the specified publisher and traffic type. For - -- a description of the traffic types see com_types.vhd - procedure subscribe ( - subscriber : actor_t; - publisher : actor_t; - traffic_type : subscription_traffic_type_t := published); - - -- Remove subscription on the given publisher and traffic type. - procedure unsubscribe ( - subscriber : actor_t; - publisher : actor_t; - traffic_type : subscription_traffic_type_t := published); - - ----------------------------------------------------------------------------- - -- Debugging - ----------------------------------------------------------------------------- - - -- Return string representation of a message - impure function to_string(msg : msg_t) return string; - - -- Get current state for actor mailbox - impure function get_mailbox_state(actor : actor_t; mailbox_id : mailbox_id_t := inbox) return mailbox_state_t; - - -- Deallocate memory allocated to a mailbox state variable - procedure deallocate(variable mailbox_state : inout mailbox_state_t); - - -- Return string representation of a mailbox state - impure function get_mailbox_state_string ( - actor : actor_t; - mailbox_id : mailbox_id_t := inbox; - indent : string := "") return string; - - -- Get current state of actor - impure function get_actor_state(actor : actor_t) return actor_state_t; - - -- Deallocate memory allocated to a actor state variable - procedure deallocate(variable actor_state : inout actor_state_t); - - -- Return string representation of an actor state - impure function get_actor_state_string (actor : actor_t; indent : string := "") return string; - - -- Get current state of messenger - impure function get_messenger_state return messenger_state_t; - - -- Deallocate memory allocated to a messenger state variable - procedure deallocate(variable messenger_state : inout messenger_state_t); - - -- Return string representation of the messenger state - impure function get_messenger_state_string(indent : string := "") return string; - - ----------------------------------------------------------------------------- - -- Misc - ----------------------------------------------------------------------------- - - -- Allow deprecated APIs - procedure allow_deprecated; - - -- Allow timeout in deprecated functionality. If not allowed timeouts will - -- cause a runtime error. - procedure allow_timeout; - -end package; +-- Com API package provides the common API for all +-- implementations of the com functionality (VHDL 2002+ and VHDL 1993) +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +use work.com_types_pkg.all; +use work.queue_pkg.all; +use work.integer_vector_ptr_pkg.all; +use work.string_ptr_pkg.all; + +package com_pkg is + -- Global predefined network. See network_t description in com_types.vhd for + -- more information. + signal net : network_t := idle_network; + + ----------------------------------------------------------------------------- + -- Handling of actors + ----------------------------------------------------------------------------- + -- Create a new actor. Any number of unnamed actors (name = "") can be + -- created. Named actors must be unique + impure function new_actor ( + name : string := ""; + inbox_size : positive := positive'high; + outbox_size : positive := positive'high + ) return actor_t; + + -- Find named actor by name. Enable deferred creation to create a deferred + -- actor when no actor is found + impure function find (name : string; enable_deferred_creation : boolean := true) return actor_t; + + -- Name of actor + impure function name (actor : actor_t) return string; + + -- Destroy actor. Mailboxes are deallocated and dependent subscriptions are + -- removed. Returns null_actor. + procedure destroy (actor : inout actor_t); + + -- Reset communication system. All actors are destroyed. + procedure reset_messenger; + + -- Check if an actor's creation is deferred + impure function is_deferred(actor : actor_t) return boolean; + + -- Total number of actors with deferred creation + impure function num_of_deferred_creations return natural; + + -- Total number of actors + impure function num_of_actors return natural; + + -- Number of messages in actor mailbox + impure function num_of_messages (actor : actor_t; mailbox_id : mailbox_id_t := inbox) return natural; + + -- Return the maximum number of messages that can be stored in an inbox + impure function mailbox_size (actor : actor_t; mailbox_id : mailbox_id_t := inbox) return natural; + + -- Resize actor mailbox. Reducing size below the number of messages in the + -- mailbox in runtime error + procedure resize_mailbox (actor : actor_t; new_size : natural; mailbox_id : mailbox_id_t := inbox); + + ----------------------------------------------------------------------------- + -- Primary send and receive related subprograms + -- + -- All timeouts will result in a runtime error unless otherwise noted. + ----------------------------------------------------------------------------- + + -- Send message to receiver. Blocking if reciever or any subscriber inbox is + -- full. + procedure send ( + signal net : inout network_t; + constant receiver : in actor_t; + variable msg : inout msg_t; + constant timeout : in time := max_timeout); + + -- Send message to an array of receivers. Blocking if any reciever or any subscriber inbox is + -- full. + procedure send ( + signal net : inout network_t; + constant receivers : in actor_vec_t; + variable msg : inout msg_t; + constant timeout : in time := max_timeout); + + -- Receive message sent to receiver. Returns oldest message or the first + -- incoming if the inbox is empty. msg is initially deleted. + procedure receive ( + signal net : inout network_t; + constant receiver : in actor_t; + variable msg : inout msg_t; + constant timeout : in time := max_timeout); + + -- Receive message sent to any of the receivers. Returns oldest message or the first + -- incoming if the inboxes are empty. Receiver inboxes are emptied from left + -- to right. msg is initially deleted. + procedure receive ( + signal net : inout network_t; + constant receivers : in actor_vec_t; + variable msg : inout msg_t; + constant timeout : in time := max_timeout); + + -- Reply to request_msg with reply_msg. request_msg may be anonymous. Blocking if reciever + -- or any subscriber inbox is full. + procedure reply ( + signal net : inout network_t; + variable request_msg : inout msg_t; + variable reply_msg : inout msg_t; + constant timeout : in time := max_timeout); + + -- Receive a reply_msg to request_msg. request_msg may be anonymous. reply_msg is initially deleted. + procedure receive_reply ( + signal net : inout network_t; + variable request_msg : inout msg_t; + variable reply_msg : inout msg_t; + constant timeout : in time := max_timeout); + + -- Publish a message from sender to all its subscribers. Blocking if reciever or any subscriber inbox is + -- full. + procedure publish ( + signal net : inout network_t; + constant sender : in actor_t; + variable msg : inout msg_t; + constant timeout : in time := max_timeout); + + -- Peek at message in actor mailbox but don't remove it. Position 0 is the oldest message. Runtime error if + -- position doesn't exist. + impure function peek_message( + actor : actor_t; + position : natural := 0; + mailbox_id : mailbox_id_t := inbox) return msg_t; + + ----------------------------------------------------------------------------- + -- Secondary send and receive related subprograms + -- + -- All timeouts will result in a runtime error unless otherwise noted. + ----------------------------------------------------------------------------- + + -- Positive or negative acknowledge of a request_msg. Same as a reply with a + -- boolean reply message. + procedure acknowledge ( + signal net : inout network_t; + variable request_msg : inout msg_t; + constant positive_ack : in boolean := true; + constant timeout : in time := max_timeout); + + -- Receive positive or negative acknowledge for a request_msg. request_msg + -- may be anonymous. reply_msg is initially deleted. + procedure receive_reply ( + signal net : inout network_t; + variable request_msg : inout msg_t; + variable positive_ack : out boolean; + constant timeout : in time := max_timeout); + + -- This request is the same as send of request_msg to receiver followed by a + -- receive_reply of a reply_msg + procedure request ( + signal net : inout network_t; + constant receiver : in actor_t; + variable request_msg : inout msg_t; + variable reply_msg : inout msg_t; + constant timeout : in time := max_timeout); + + -- This request is the same as send of request_msg to receiver followed by a + -- receive_reply of a positive or negative acknowledge. + procedure request ( + signal net : inout network_t; + constant receiver : in actor_t; + variable request_msg : inout msg_t; + variable positive_ack : out boolean; + constant timeout : in time := max_timeout); + + ----------------------------------------------------------------------------- + -- Low-level subprograms primarily used for handling timeout wihout error + ----------------------------------------------------------------------------- + + -- Wait for message sent to receiver. status = ok if message is + -- received before the timeout, status = timeout otherwise. + procedure wait_for_message ( + signal net : in network_t; + constant receiver : in actor_t; + variable status : out com_status_t; + constant timeout : in time := max_timeout); + + -- Wait for message sent to any of the listed receivers. status = ok + -- if message is received before the timeout, status = timeout otherwise. + procedure wait_for_message ( + signal net : in network_t; + constant receivers : in actor_vec_t; + variable status : out com_status_t; + constant timeout : in time := max_timeout); + + -- Returns true if there is at least one message in the actor's inbox. + impure function has_message (actor : actor_t) return boolean; + + -- Wait for reply to request_msg. status = ok + -- if message is received before the timeout, status = timeout otherwise. + procedure wait_for_reply ( + signal net : inout network_t; + variable request_msg : inout msg_t; + variable status : out com_status_t; + constant timeout : in time := max_timeout); + + -- Get oldest message from receiver inbox. Runtime error if inbox is empty. + procedure get_message (signal net : inout network_t; receiver : actor_t; variable msg : inout msg_t); + + -- Get reply message to request_msg. Runtime error if reply message isn't available. + procedure get_reply ( + signal net : inout network_t; + variable request_msg : inout msg_t; + variable reply_msg : inout msg_t); + + ----------------------------------------------------------------------------- + -- Subscriptions + ----------------------------------------------------------------------------- + + -- Make subscriber subscribe on the specified publisher and traffic type. For + -- a description of the traffic types see com_types.vhd + procedure subscribe ( + subscriber : actor_t; + publisher : actor_t; + traffic_type : subscription_traffic_type_t := published); + + -- Remove subscription on the given publisher and traffic type. + procedure unsubscribe ( + subscriber : actor_t; + publisher : actor_t; + traffic_type : subscription_traffic_type_t := published); + + ----------------------------------------------------------------------------- + -- Debugging + ----------------------------------------------------------------------------- + + -- Return string representation of a message + impure function to_string(msg : msg_t) return string; + + -- Get current state for actor mailbox + impure function get_mailbox_state(actor : actor_t; mailbox_id : mailbox_id_t := inbox) return mailbox_state_t; + + -- Deallocate memory allocated to a mailbox state variable + procedure deallocate(variable mailbox_state : inout mailbox_state_t); + + -- Return string representation of a mailbox state + impure function get_mailbox_state_string ( + actor : actor_t; + mailbox_id : mailbox_id_t := inbox; + indent : string := "") return string; + + -- Get current state of actor + impure function get_actor_state(actor : actor_t) return actor_state_t; + + -- Deallocate memory allocated to a actor state variable + procedure deallocate(variable actor_state : inout actor_state_t); + + -- Return string representation of an actor state + impure function get_actor_state_string (actor : actor_t; indent : string := "") return string; + + -- Get current state of messenger + impure function get_messenger_state return messenger_state_t; + + -- Deallocate memory allocated to a messenger state variable + procedure deallocate(variable messenger_state : inout messenger_state_t); + + -- Return string representation of the messenger state + impure function get_messenger_state_string(indent : string := "") return string; + + ----------------------------------------------------------------------------- + -- Misc + ----------------------------------------------------------------------------- + + -- Allow deprecated APIs + procedure allow_deprecated; + + -- Allow timeout in deprecated functionality. If not allowed timeouts will + -- cause a runtime error. + procedure allow_timeout; + +end package; diff --git a/vunit/vhdl/com/src/com_common.vhd b/vunit/vhdl/com/src/com_common.vhd index a5ea9a409..3078f0b89 100644 --- a/vunit/vhdl/com/src/com_common.vhd +++ b/vunit/vhdl/com/src/com_common.vhd @@ -1,38 +1,38 @@ --- Com common package provides functionality shared among the other packages --- The package is private to com. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com -library ieee; -use ieee.std_logic_1164.all; - -use work.com_messenger_pkg.all; -use work.com_types_pkg.all; - -package com_common_pkg is - shared variable messenger : messenger_t; - - procedure notify (signal net : inout network_t); - - impure function no_error_status (status : com_status_t; old_api : boolean := false) return boolean; -end package com_common_pkg; - -package body com_common_pkg is - procedure notify (signal net : inout network_t) is - begin - if net /= network_event then - net <= network_event; - wait until net = network_event; - net <= idle_network; - end if; - end procedure notify; - - impure function no_error_status (status : com_status_t; old_api : boolean := false) return boolean is - begin - return (status = ok) or ((status = timeout) and messenger.timeout_is_allowed and old_api); - end; - -end package body com_common_pkg; +-- Com common package provides functionality shared among the other packages +-- The package is private to com. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +library ieee; +use ieee.std_logic_1164.all; + +use work.com_messenger_pkg.all; +use work.com_types_pkg.all; + +package com_common_pkg is + shared variable messenger : messenger_t; + + procedure notify (signal net : inout network_t); + + impure function no_error_status (status : com_status_t; old_api : boolean := false) return boolean; +end package com_common_pkg; + +package body com_common_pkg is + procedure notify (signal net : inout network_t) is + begin + if net /= network_event then + net <= network_event; + wait until net = network_event; + net <= idle_network; + end if; + end procedure notify; + + impure function no_error_status (status : com_status_t; old_api : boolean := false) return boolean is + begin + return (status = ok) or ((status = timeout) and messenger.timeout_is_allowed and old_api); + end; + +end package body com_common_pkg; diff --git a/vunit/vhdl/com/src/com_context.vhd b/vunit/vhdl/com/src/com_context.vhd index 5308142d7..6764b0f09 100644 --- a/vunit/vhdl/com/src/com_context.vhd +++ b/vunit/vhdl/com/src/com_context.vhd @@ -1,19 +1,19 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -context com_context is - library vunit_lib; - use vunit_lib.com_pkg.all; - use vunit_lib.com_types_pkg.all; - use vunit_lib.codec_pkg.all; - use vunit_lib.codec_2008_pkg.all; - use vunit_lib.com_string_pkg.all; - use vunit_lib.codec_builder_pkg.all; - use vunit_lib.codec_builder_2008_pkg.all; - use vunit_lib.com_debug_codec_builder_pkg.all; - use vunit_lib.com_deprecated_pkg.all; - use vunit_lib.com_common_pkg.all; -end context; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +context com_context is + library vunit_lib; + use vunit_lib.com_pkg.all; + use vunit_lib.com_types_pkg.all; + use vunit_lib.codec_pkg.all; + use vunit_lib.codec_2008_pkg.all; + use vunit_lib.com_string_pkg.all; + use vunit_lib.codec_builder_pkg.all; + use vunit_lib.codec_builder_2008_pkg.all; + use vunit_lib.com_debug_codec_builder_pkg.all; + use vunit_lib.com_deprecated_pkg.all; + use vunit_lib.com_common_pkg.all; +end context; diff --git a/vunit/vhdl/com/src/com_debug_codec_builder.vhd b/vunit/vhdl/com/src/com_debug_codec_builder.vhd index 66888c920..8720f8956 100644 --- a/vunit/vhdl/com/src/com_debug_codec_builder.vhd +++ b/vunit/vhdl/com/src/com_debug_codec_builder.vhd @@ -1,294 +1,294 @@ --- This package contains support functions for debug codec building --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; - -use std.textio.all; - -package com_debug_codec_builder_pkg is - ----------------------------------------------------------------------------- - -- Encoding support - ----------------------------------------------------------------------------- - procedure open_group ( - variable l : inout line); - procedure append_group ( - variable l : inout line; - constant element : in string); - procedure close_group ( - variable l : inout line; - variable code : out string; - variable length : out natural); - function create_group ( - constant num_of_elements : natural range 0 to 10; - constant element1 : string := ""; - constant element2 : string := ""; - constant element3 : string := ""; - constant element4 : string := ""; - constant element5 : string := ""; - constant element6 : string := ""; - constant element7 : string := ""; - constant element8 : string := ""; - constant element9 : string := ""; - constant element10 : string := "") - return string; - function create_array_group ( - constant arr : string; - constant range_left1 : string; - constant range_right1 : string; - constant is_ascending1 : boolean; - constant range_left2 : string := ""; - constant range_right2 : string := ""; - constant is_ascending2 : boolean := true) - return string; - function escape_special_characters ( - constant data : string) - return string; - - ----------------------------------------------------------------------------- - -- Decoding support - ----------------------------------------------------------------------------- - procedure split_group ( - constant grp : in string; - variable elements : inout lines_t; - constant max_num_of_elements : in natural; - variable length : inout natural); - function get_element ( - constant grp : in string; - constant position : in natural) - return string; - function get_first_element ( - constant grp : string) - return string; - procedure deallocate_elements ( - variable elements : inout lines_t); - function unescape_special_characters ( - constant code : string) - return string; -end package com_debug_codec_builder_pkg; - -package body com_debug_codec_builder_pkg is - ----------------------------------------------------------------------------- - -- Encoding support - ----------------------------------------------------------------------------- - procedure open_group ( - variable l : inout line) is - begin - deallocate(l); - write(l, '('); - end procedure open_group; - - procedure append_group ( - variable l : inout line; - constant element : in string) is - begin - write(l, element & ","); - end procedure append_group; - - procedure close_group ( - variable l : inout line; - variable code : out string; - variable length : out natural) is - variable final : line; - variable line_length : integer; - begin - if l.all /= "(" then - line_length := l.all'length; - write(final, l.all(1 to line_length - 1)); - deallocate(l); - else - final := l; - end if; - write(final, ')'); - length := final.all'length; - code(1 to length) := final.all; - deallocate(final); - end procedure close_group; - - function create_group ( - constant num_of_elements : natural range 0 to 10; - constant element1 : string := ""; - constant element2 : string := ""; - constant element3 : string := ""; - constant element4 : string := ""; - constant element5 : string := ""; - constant element6 : string := ""; - constant element7 : string := ""; - constant element8 : string := ""; - constant element9 : string := ""; - constant element10 : string := "") - return string is - begin - if num_of_elements = 4 then - return "(" & element1 & "," & element2 & "," & element3 & "," & element4 & ")"; - elsif num_of_elements = 3 then - return "(" & element1 & "," & element2 & "," & element3 & ")"; - elsif num_of_elements = 5 then - return "(" & element1 & "," & element2 & "," & element3 & "," & element4 & "," & element5 & ")"; - elsif num_of_elements = 2 then - return "(" & element1 & "," & element2 & ")"; - elsif num_of_elements = 7 then - return "(" & element1 & "," & element2 & "," & element3 & "," & element4 & "," & element5 & "," & element6 & "," & element7 & ")"; - elsif num_of_elements = 1 then - return "(" & element1 & ")"; - elsif num_of_elements = 6 then - return "(" & element1 & "," & element2 & "," & element3 & "," & element4 & "," & element5 & "," & element6 & ")"; - elsif num_of_elements = 8 then - return "(" & element1 & "," & element2 & "," & element3 & "," & element4 & "," & element5 & "," & element6 & "," & element7 & "," & element8 & ")"; - elsif num_of_elements = 9 then - return "(" & element1 & "," & element2 & "," & element3 & "," & element4 & "," & element5 & "," & element6 & "," & element7 & "," & element8 & "," & element9 & ")"; - elsif num_of_elements = 10 then - return "(" & element1 & "," & element2 & "," & element3 & "," & element4 & "," & element5 & "," & element6 & "," & element7 & "," & element8 & "," & element9 & "," & element10 & ")"; - else - return "()"; - end if; - end function create_group; - - function create_array_group ( - constant arr : string; - constant range_left1 : string; - constant range_right1 : string; - constant is_ascending1 : boolean; - constant range_left2 : string := ""; - constant range_right2 : string := ""; - constant is_ascending2 : boolean := true) - return string is - variable ret_val : string(1 to 18 + arr'length + range_left1'length + range_right1'length + - range_left2'length + range_right2'length); - variable l : line; - variable length : natural; - begin - open_group(l); - append_group(l, arr); - append_group(l, range_left1); - append_group(l, range_right1); - append_group(l, to_string(is_ascending1)); - if range_left2 /= "" then - append_group(l, range_left2); - append_group(l, range_right2); - append_group(l, to_string(is_ascending2)); - end if; - close_group(l, ret_val, length); - - return ret_val(1 to length); - end; - - function escape_special_characters ( - constant data : string) - return string is - begin - return replace(replace(replace(data, ')', "\rp"), '(', "\lp"), ',', "\comma"); - end function escape_special_characters; - - ----------------------------------------------------------------------------- - -- Decoding support - ----------------------------------------------------------------------------- - procedure split_group ( - constant grp : in string; - variable elements : inout lines_t; - constant max_num_of_elements : in natural; - variable length : inout natural) is - variable element_start : positive; - variable level : natural := 0; - begin - deallocate_elements(elements); - length := 0; - - if (grp = "()") or -- Empty group - (grp(grp'left) /= '(') or -- Not a valid group - (grp(grp'right) /= ')') then -- Not a valid group - return; - end if; - - elements := new line_vector(0 to max_num_of_elements - 1); - element_start := grp'left + 1; - for i in grp'left + 1 to grp'right loop - if length = max_num_of_elements then - return; - elsif grp(i) = '(' then - level := level + 1; - elsif ((grp(i) = ',') or (i = grp'right)) and (level = 0) then - if grp(i) = ',' then - write(elements.all(length), grp(element_start to i - 1)); - else - write(elements.all(length), grp(element_start to i - 1)); - end if; - - length := length + 1; - element_start := i + 1; - elsif grp(i) = ')' then - level := level - 1; - end if; - end loop; - - if level /= 0 then -- Not a valid group - deallocate_elements(elements); - length := 0; - end if; - - end procedure split_group; - - function get_element ( - constant grp : in string; - constant position : in natural) - return string is - variable elements : lines_t; - variable length : natural; - variable ret_val : string(1 to grp'length); - begin - if grp = "" then - return ""; - end if; - - if grp(grp'left) /= '(' then - if position = 0 then - return grp; - else - return ""; - end if; - end if; - - split_group(grp, elements, position + 1, length); - if length < position + 1 then - return ""; - else - length := elements.all(position).all'length; - ret_val(1 to length) := elements.all(position).all; - deallocate_elements(elements); - return ret_val(1 to length); - end if; - end function get_element; - - function get_first_element ( - constant grp : string) - return string is - begin - return get_element(grp, 0); - end function get_first_element; - - procedure deallocate_elements ( - variable elements : inout lines_t) is - begin - if elements = null then - return; - end if; - - for i in elements.all'range loop - deallocate(elements.all(i)); - end loop; - deallocate(elements); - end procedure deallocate_elements; - - function unescape_special_characters ( - constant code : string) - return string is - begin - return replace(replace(replace(code, "\rp", ')'), "\lp", '('), "\comma", ','); - end function unescape_special_characters; - -end package body com_debug_codec_builder_pkg; +-- This package contains support functions for debug codec building +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +use std.textio.all; + +package com_debug_codec_builder_pkg is + ----------------------------------------------------------------------------- + -- Encoding support + ----------------------------------------------------------------------------- + procedure open_group ( + variable l : inout line); + procedure append_group ( + variable l : inout line; + constant element : in string); + procedure close_group ( + variable l : inout line; + variable code : out string; + variable length : out natural); + function create_group ( + constant num_of_elements : natural range 0 to 10; + constant element1 : string := ""; + constant element2 : string := ""; + constant element3 : string := ""; + constant element4 : string := ""; + constant element5 : string := ""; + constant element6 : string := ""; + constant element7 : string := ""; + constant element8 : string := ""; + constant element9 : string := ""; + constant element10 : string := "") + return string; + function create_array_group ( + constant arr : string; + constant range_left1 : string; + constant range_right1 : string; + constant is_ascending1 : boolean; + constant range_left2 : string := ""; + constant range_right2 : string := ""; + constant is_ascending2 : boolean := true) + return string; + function escape_special_characters ( + constant data : string) + return string; + + ----------------------------------------------------------------------------- + -- Decoding support + ----------------------------------------------------------------------------- + procedure split_group ( + constant grp : in string; + variable elements : inout lines_t; + constant max_num_of_elements : in natural; + variable length : inout natural); + function get_element ( + constant grp : in string; + constant position : in natural) + return string; + function get_first_element ( + constant grp : string) + return string; + procedure deallocate_elements ( + variable elements : inout lines_t); + function unescape_special_characters ( + constant code : string) + return string; +end package com_debug_codec_builder_pkg; + +package body com_debug_codec_builder_pkg is + ----------------------------------------------------------------------------- + -- Encoding support + ----------------------------------------------------------------------------- + procedure open_group ( + variable l : inout line) is + begin + deallocate(l); + write(l, '('); + end procedure open_group; + + procedure append_group ( + variable l : inout line; + constant element : in string) is + begin + write(l, element & ","); + end procedure append_group; + + procedure close_group ( + variable l : inout line; + variable code : out string; + variable length : out natural) is + variable final : line; + variable line_length : integer; + begin + if l.all /= "(" then + line_length := l.all'length; + write(final, l.all(1 to line_length - 1)); + deallocate(l); + else + final := l; + end if; + write(final, ')'); + length := final.all'length; + code(1 to length) := final.all; + deallocate(final); + end procedure close_group; + + function create_group ( + constant num_of_elements : natural range 0 to 10; + constant element1 : string := ""; + constant element2 : string := ""; + constant element3 : string := ""; + constant element4 : string := ""; + constant element5 : string := ""; + constant element6 : string := ""; + constant element7 : string := ""; + constant element8 : string := ""; + constant element9 : string := ""; + constant element10 : string := "") + return string is + begin + if num_of_elements = 4 then + return "(" & element1 & "," & element2 & "," & element3 & "," & element4 & ")"; + elsif num_of_elements = 3 then + return "(" & element1 & "," & element2 & "," & element3 & ")"; + elsif num_of_elements = 5 then + return "(" & element1 & "," & element2 & "," & element3 & "," & element4 & "," & element5 & ")"; + elsif num_of_elements = 2 then + return "(" & element1 & "," & element2 & ")"; + elsif num_of_elements = 7 then + return "(" & element1 & "," & element2 & "," & element3 & "," & element4 & "," & element5 & "," & element6 & "," & element7 & ")"; + elsif num_of_elements = 1 then + return "(" & element1 & ")"; + elsif num_of_elements = 6 then + return "(" & element1 & "," & element2 & "," & element3 & "," & element4 & "," & element5 & "," & element6 & ")"; + elsif num_of_elements = 8 then + return "(" & element1 & "," & element2 & "," & element3 & "," & element4 & "," & element5 & "," & element6 & "," & element7 & "," & element8 & ")"; + elsif num_of_elements = 9 then + return "(" & element1 & "," & element2 & "," & element3 & "," & element4 & "," & element5 & "," & element6 & "," & element7 & "," & element8 & "," & element9 & ")"; + elsif num_of_elements = 10 then + return "(" & element1 & "," & element2 & "," & element3 & "," & element4 & "," & element5 & "," & element6 & "," & element7 & "," & element8 & "," & element9 & "," & element10 & ")"; + else + return "()"; + end if; + end function create_group; + + function create_array_group ( + constant arr : string; + constant range_left1 : string; + constant range_right1 : string; + constant is_ascending1 : boolean; + constant range_left2 : string := ""; + constant range_right2 : string := ""; + constant is_ascending2 : boolean := true) + return string is + variable ret_val : string(1 to 18 + arr'length + range_left1'length + range_right1'length + + range_left2'length + range_right2'length); + variable l : line; + variable length : natural; + begin + open_group(l); + append_group(l, arr); + append_group(l, range_left1); + append_group(l, range_right1); + append_group(l, to_string(is_ascending1)); + if range_left2 /= "" then + append_group(l, range_left2); + append_group(l, range_right2); + append_group(l, to_string(is_ascending2)); + end if; + close_group(l, ret_val, length); + + return ret_val(1 to length); + end; + + function escape_special_characters ( + constant data : string) + return string is + begin + return replace(replace(replace(data, ')', "\rp"), '(', "\lp"), ',', "\comma"); + end function escape_special_characters; + + ----------------------------------------------------------------------------- + -- Decoding support + ----------------------------------------------------------------------------- + procedure split_group ( + constant grp : in string; + variable elements : inout lines_t; + constant max_num_of_elements : in natural; + variable length : inout natural) is + variable element_start : positive; + variable level : natural := 0; + begin + deallocate_elements(elements); + length := 0; + + if (grp = "()") or -- Empty group + (grp(grp'left) /= '(') or -- Not a valid group + (grp(grp'right) /= ')') then -- Not a valid group + return; + end if; + + elements := new line_vector(0 to max_num_of_elements - 1); + element_start := grp'left + 1; + for i in grp'left + 1 to grp'right loop + if length = max_num_of_elements then + return; + elsif grp(i) = '(' then + level := level + 1; + elsif ((grp(i) = ',') or (i = grp'right)) and (level = 0) then + if grp(i) = ',' then + write(elements.all(length), grp(element_start to i - 1)); + else + write(elements.all(length), grp(element_start to i - 1)); + end if; + + length := length + 1; + element_start := i + 1; + elsif grp(i) = ')' then + level := level - 1; + end if; + end loop; + + if level /= 0 then -- Not a valid group + deallocate_elements(elements); + length := 0; + end if; + + end procedure split_group; + + function get_element ( + constant grp : in string; + constant position : in natural) + return string is + variable elements : lines_t; + variable length : natural; + variable ret_val : string(1 to grp'length); + begin + if grp = "" then + return ""; + end if; + + if grp(grp'left) /= '(' then + if position = 0 then + return grp; + else + return ""; + end if; + end if; + + split_group(grp, elements, position + 1, length); + if length < position + 1 then + return ""; + else + length := elements.all(position).all'length; + ret_val(1 to length) := elements.all(position).all; + deallocate_elements(elements); + return ret_val(1 to length); + end if; + end function get_element; + + function get_first_element ( + constant grp : string) + return string is + begin + return get_element(grp, 0); + end function get_first_element; + + procedure deallocate_elements ( + variable elements : inout lines_t) is + begin + if elements = null then + return; + end if; + + for i in elements.all'range loop + deallocate(elements.all(i)); + end loop; + deallocate(elements); + end procedure deallocate_elements; + + function unescape_special_characters ( + constant code : string) + return string is + begin + return replace(replace(replace(code, "\rp", ')'), "\lp", '('), "\comma", ','); + end function unescape_special_characters; + +end package body com_debug_codec_builder_pkg; diff --git a/vunit/vhdl/com/src/com_deprecated.vhd b/vunit/vhdl/com/src/com_deprecated.vhd index cb2c096b5..2829176eb 100644 --- a/vunit/vhdl/com/src/com_deprecated.vhd +++ b/vunit/vhdl/com/src/com_deprecated.vhd @@ -1,917 +1,917 @@ --- Com deprecated package provides deprecated functionality and APIs. These --- will eventually be removed --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -use work.codec_pkg.all; -use work.com_support_pkg.all; -use work.com_messenger_pkg.all; -use work.com_types_pkg.all; -use work.com_pkg.all; -use work.com_common_pkg.all; - -use std.textio.all; - -package com_deprecated_pkg is - ----------------------------------------------------------------------------- - -- Handling of actors - ----------------------------------------------------------------------------- - constant null_actor_c : actor_t := null_actor; - impure function create (name : string := ""; inbox_size : positive := positive'high) return actor_t; - procedure destroy (actor : inout actor_t; status : out com_status_t); - impure function inbox_size (actor : actor_t) return natural; - - ----------------------------------------------------------------------------- - -- Message related subprograms - ----------------------------------------------------------------------------- - constant no_message_id_c : message_id_t := no_message_id; - impure function new_message (sender : actor_t := null_actor_c) return message_ptr_t; - impure function compose ( - payload : string := ""; - sender : actor_t := null_actor_c; - request_id : message_id_t := no_message_id_c) - return message_ptr_t; - procedure copy (src : inout message_ptr_t; dst : inout message_ptr_t); - procedure delete (message : inout message_ptr_t); - - ----------------------------------------------------------------------------- - -- Primary send and receive related subprograms - ----------------------------------------------------------------------------- - constant max_timeout_c : time := max_timeout; - procedure send ( - signal net : inout network_t; - constant receiver : in actor_t; - variable message : inout message_ptr_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := true); - procedure receive ( - signal net : inout network_t; - constant receiver : in actor_t; - variable message : inout message_ptr_t; - constant timeout : in time := max_timeout_c); - procedure reply ( - signal net : inout network_t; - variable request : inout message_ptr_t; - variable message : inout message_ptr_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := true); - procedure receive_reply ( - signal net : inout network_t; - variable request : inout message_ptr_t; - variable message : inout message_ptr_t; - constant timeout : in time := max_timeout_c); - procedure send ( - signal net : inout network_t; - constant receiver : in actor_t; - variable message : inout message_ptr_t; - variable receipt : out receipt_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := true); - procedure reply ( - signal net : inout network_t; - constant sender : in actor_t; - constant receiver : in actor_t; - constant request_id : in message_id_t; - constant payload : in string := ""; - variable receipt : out receipt_t; - constant timeout : in time := max_timeout_c); - procedure reply ( - signal net : inout network_t; - constant receiver : in actor_t; - constant request_id : in message_id_t; - constant payload : in string := ""; - variable receipt : out receipt_t; - constant timeout : in time := max_timeout_c); - procedure reply ( - signal net : inout network_t; - constant receiver : in actor_t; - variable message : inout message_ptr_t; - variable receipt : out receipt_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := false); - procedure receive_reply ( - signal net : inout network_t; - constant receiver : in actor_t; - constant request_id : in message_id_t; - variable message : inout message_ptr_t; - constant timeout : in time := max_timeout_c); - procedure receive_reply ( - signal net : inout network_t; - constant receiver : in actor_t; - constant request_id : in message_id_t; - variable positive_ack : out boolean; - variable status : out com_status_t; - constant timeout : in time := max_timeout_c); - - ----------------------------------------------------------------------------- - -- Secondary send and receive related subprograms - ----------------------------------------------------------------------------- - procedure send ( - signal net : inout network_t; - constant sender : in actor_t; - constant receiver : in actor_t; - constant payload : in string := ""; - variable receipt : out receipt_t; - constant timeout : in time := max_timeout_c); - procedure send ( - signal net : inout network_t; - constant receiver : in actor_t; - constant payload : in string := ""; - variable receipt : out receipt_t; - constant timeout : in time := max_timeout_c); - procedure request ( - signal net : inout network_t; - constant sender : in actor_t; - constant receiver : in actor_t; - constant request_payload : in string := ""; - variable reply_message : inout message_ptr_t; - constant timeout : in time := max_timeout_c); - procedure request ( - signal net : inout network_t; - constant receiver : in actor_t; - variable request_message : inout message_ptr_t; - variable reply_message : inout message_ptr_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := false); - procedure request ( - signal net : inout network_t; - constant receiver : in actor_t; - variable request_message : inout message_ptr_t; - variable positive_ack : out boolean; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := false); - procedure publish ( - signal net : inout network_t; - variable message : inout message_ptr_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := false); - procedure request ( - signal net : inout network_t; - constant sender : in actor_t; - constant receiver : in actor_t; - constant request_payload : in string := ""; - variable positive_ack : out boolean; - variable status : out com_status_t; - constant timeout : in time := max_timeout_c); - procedure request ( - signal net : inout network_t; - constant receiver : in actor_t; - variable request_message : inout message_ptr_t; - variable positive_ack : out boolean; - variable status : out com_status_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := false); - procedure publish ( - signal net : inout network_t; - constant sender : in actor_t; - constant payload : in string := ""; - variable status : out com_status_t; - constant timeout : in time := max_timeout_c); - procedure publish ( - signal net : inout network_t; - variable message : inout message_ptr_t; - variable status : out com_status_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := false); - procedure acknowledge ( - signal net : inout network_t; - constant sender : in actor_t; - constant receiver : in actor_t; - constant request_id : in message_id_t; - constant positive_ack : in boolean := true; - variable receipt : out receipt_t; - constant timeout : in time := max_timeout_c); - procedure acknowledge ( - signal net : inout network_t; - constant receiver : in actor_t; - constant request_id : in message_id_t; - constant positive_ack : in boolean := true; - variable receipt : out receipt_t; - constant timeout : in time := max_timeout_c); - - ----------------------------------------------------------------------------- - -- Low-level subprograms primarily used for handling timeout wihout error - ----------------------------------------------------------------------------- - impure function has_messages (actor : actor_t) return boolean; - impure function get_message (receiver : actor_t; delete_from_inbox : boolean := true) return message_ptr_t; - - procedure wait_for_messages ( - signal net : in network_t; - constant receiver : in actor_t; - variable status : out com_status_t; - constant receive_timeout : in time := max_timeout_c); - - ----------------------------------------------------------------------------- - -- Receive related subprograms - ----------------------------------------------------------------------------- - procedure subscribe ( - constant subscriber : in actor_t; - constant publisher : in actor_t; - variable status : out com_status_t); - procedure unsubscribe ( - constant subscriber : in actor_t; - constant publisher : in actor_t; - variable status : out com_status_t); - impure function num_of_missed_messages (actor : actor_t) return natural; - -end package; - -package body com_deprecated_pkg is - procedure deprecated (msg : string) is - begin - messenger.deprecated(msg); - end; - - ----------------------------------------------------------------------------- - -- Handling of actors - ----------------------------------------------------------------------------- - impure function create (name : string := ""; inbox_size : positive := positive'high) return actor_t is - begin - deprecated("create() instead of new_actor()"); - return new_actor(name, inbox_size); - end; - - procedure destroy (actor : inout actor_t; status : out com_status_t) is - begin - deprecated("destroy() with status output"); - status := ok; - messenger.destroy(actor); - end; - - impure function inbox_size (actor : actor_t) return natural is - begin - deprecated("inbox_size() instead of mailbox_size()"); - return messenger.mailbox_size(actor, inbox); - end; - - ----------------------------------------------------------------------------- - -- Message related subprograms - ----------------------------------------------------------------------------- - impure function new_message (sender : actor_t := null_actor_c) return message_ptr_t is - variable message : message_ptr_t; - begin - deprecated("new_message()"); - message := new message_t; - message.sender := sender; - return message; - end function; - - impure function compose ( - payload : string := ""; - sender : actor_t := null_actor_c; - request_id : message_id_t := no_message_id_c) - return message_ptr_t is - variable message : message_ptr_t; - begin - deprecated("compose()"); - message := new message_t; - message.sender := sender; - message.request_id := request_id; - write(message.payload, payload); - return message; - end function compose; - - procedure copy (src : inout message_ptr_t; dst : inout message_ptr_t) is - begin - deprecated("copy() based on message_ptr_t"); - dst := new message_t; - dst.id := src.id; - dst.status := src.status; - dst.receiver := src.receiver; - dst.sender := src.sender; - dst.request_id := src.request_id; - write(dst.payload, src.payload.all); - end procedure copy; - - procedure delete (message : inout message_ptr_t) is - begin - deprecated("delete() based on message_ptr_t"); - if message /= null then - deallocate(message.payload); - deallocate(message); - end if; - end procedure delete; - - ----------------------------------------------------------------------------- - -- Primary send and receive related subprograms - ----------------------------------------------------------------------------- - procedure send ( - signal net : inout network_t; - constant receiver : in actor_t; - constant mailbox_id : in mailbox_id_t; - variable message : inout message_ptr_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := true) is - variable receipt : receipt_t; - begin - deprecated("send() based on message_ptr_t"); - check(message /= null, null_message_error); - check(not messenger.unknown_actor(receiver), unknown_receiver_error); - - if messenger.is_full(receiver, mailbox_id) then - wait on net until not messenger.is_full(receiver, mailbox_id) for timeout; - check(not messenger.is_full(receiver, mailbox_id), full_inbox_error); - end if; - - messenger.send(message.sender, receiver, mailbox_id, message.request_id, message.payload.all, receipt); - message.id := receipt.id; - message.receiver := receiver; - notify(net); - - if not keep_message then - delete(message); - end if; - end; - - procedure send ( - signal net : inout network_t; - constant receiver : in actor_t; - variable message : inout message_ptr_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := true) is - begin - send(net, receiver, inbox, message, timeout, keep_message); - end; - - procedure receive ( - signal net : inout network_t; - constant receiver : in actor_t; - variable message : inout message_ptr_t; - constant timeout : in time := max_timeout_c) is - variable status : com_status_t; - variable started_with_full_inbox : boolean; - begin - deprecated("receive() based on message_ptr_t"); - delete(message); - wait_for_message(net, receiver, status, timeout); - - if not check(no_error_status(status, true), status) then - return; - end if; - - if status = ok then - started_with_full_inbox := messenger.is_full(receiver, inbox); - message := get_message(receiver); - if started_with_full_inbox then - notify(net); - end if; - else - message := new message_t; - message.receiver := receiver; - message.status := status; - end if; - end; - - procedure reply ( - signal net : inout network_t; - variable request : inout message_ptr_t; - variable message : inout message_ptr_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := true) is - begin - deprecated("reply() based on message_ptr_t"); - check(request.id /= no_message_id_c, reply_missing_request_id_error); - message.request_id := request.id; - message.sender := request.receiver; - - if request.sender /= null_actor_c then - send(net, request.sender, inbox, message, timeout, keep_message); - else - send(net, request.receiver, outbox, message, timeout, keep_message); - end if; - end; - - procedure wait_for_reply_stash_message ( - signal net : inout network_t; - constant receiver : in actor_t; - constant mailbox_id : in mailbox_id_t := inbox; - constant request_id : in message_id_t; - variable status : out com_status_t; - constant timeout : in time := max_timeout_c) is - variable started_with_full_inbox : boolean := false; - begin - check(not messenger.deferred(receiver), deferred_receiver_error); - - status := ok; - if mailbox_id = inbox then - started_with_full_inbox := messenger.is_full(receiver, inbox); - end if; - - if messenger.has_reply_stash_message(receiver, request_id) then - return; - elsif messenger.find_and_stash_reply_message(receiver, request_id, mailbox_id) then - if started_with_full_inbox then - notify(net); - end if; - return; - else - wait on net until messenger.find_and_stash_reply_message(receiver, request_id, mailbox_id) for timeout; - if not messenger.has_reply_stash_message(receiver, request_id) then - status := work.com_types_pkg.timeout; - elsif started_with_full_inbox then - notify(net); - end if; - end if; - end procedure wait_for_reply_stash_message; - - impure function get_reply_stash_message ( - receiver : actor_t; - clear_reply_stash : boolean := true) - return message_ptr_t is - variable message : message_ptr_t; - begin - check(messenger.has_reply_stash_message(receiver), null_message_error); - - message := new message_t; - message.status := ok; - message.id := messenger.get_reply_stash_message_id(receiver); - message.request_id := messenger.get_reply_stash_message_request_id(receiver); - message.sender := messenger.get_reply_stash_message_sender(receiver); - message.receiver := messenger.get_reply_stash_message_receiver(receiver); - write(message.payload, messenger.get_reply_stash_message_payload(receiver)); - if clear_reply_stash then - messenger.clear_reply_stash(receiver); - end if; - - return message; - end function get_reply_stash_message; - - procedure receive_reply ( - signal net : inout network_t; - variable request : inout message_ptr_t; - variable message : inout message_ptr_t; - constant timeout : in time := max_timeout_c) is - variable status : com_status_t; - variable source_actor : actor_t; - variable mailbox : mailbox_id_t; - begin - deprecated("receive_reply() based on message_ptr_t"); - delete(message); - - source_actor := request.sender when request.sender /= null_actor_c else request.receiver; - mailbox := inbox when request.sender /= null_actor_c else outbox; - - wait_for_reply_stash_message(net, source_actor, mailbox, request.id, status, timeout); - check(no_error_status(status, true), status); - if status = ok then - message := get_reply_stash_message(source_actor); - else - message := new message_t; - message.receiver := request.sender; - message.status := status; - end if; - end; - - -- - procedure publish ( - signal net : inout network_t; - constant sender : in actor_t; - variable message : inout message_ptr_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := true) is - begin - deprecated("publish() based on message_ptr_t"); - check(message /= null, null_message_error); - message.sender := sender; - message.receiver := null_actor_c; - - if messenger.subscriber_inbox_is_full(message.sender, (published, outbound)) then - wait on net until not messenger.subscriber_inbox_is_full(sender, (published, outbound)) for timeout; - check(not messenger.subscriber_inbox_is_full(message.sender, (published, outbound)), full_inbox_error); - end if; - - messenger.publish(message.sender, message.payload.all); - notify(net); - - if not keep_message then - delete(message); - end if; - end; - procedure send ( - signal net : inout network_t; - constant receiver : in actor_t; - variable message : inout message_ptr_t; - variable receipt : out receipt_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := true) is - begin - deprecated("send() with message and receipt. Use send() without receipt and look at message.id instead"); - check(message /= null, null_message_error); - check(not messenger.unknown_actor(receiver), unknown_receiver_error); - - if messenger.is_full(receiver, inbox) then - wait on net until not messenger.is_full(receiver, inbox) for timeout; - check(not messenger.is_full(receiver, inbox), full_inbox_error); - end if; - - messenger.send(message.sender, receiver, inbox, message.request_id, message.payload.all, receipt); - message.id := receipt.id; - message.receiver := receiver; - notify(net); - - if not keep_message then - delete(message); - end if; - end; - - -- - procedure receive_reply ( - signal net : inout network_t; - constant receiver : in actor_t; - constant receipt : in receipt_t; - variable positive_ack : out boolean; - variable status : out com_status_t; - constant timeout : in time := max_timeout_c) is - variable message : message_ptr_t; - begin - deprecated("receive_reply() with status output. Use without or wait_for_reply() if accepting timeout"); - wait_for_reply_stash_message(net, receiver, inbox, receipt.id, status, timeout); - check(no_error_status(status, true), status); - if status = ok then - message := get_reply_stash_message(receiver); - status := message.status; - positive_ack := decode(message.payload.all); - delete(message); - else - positive_ack := false; - end if; - end; - - -- - procedure receive_reply ( - signal net : inout network_t; - variable request : inout message_ptr_t; - variable positive_ack : out boolean; - variable status : out com_status_t; - constant timeout : in time := max_timeout_c) is - constant receipt : receipt_t := (status => ok, id => request.id); - begin - receive_reply(net, request.sender, receipt, positive_ack, status, timeout); - end; - - procedure receive_reply ( - signal net : inout network_t; - constant receiver : in actor_t; - constant request_id : in message_id_t; - variable message : inout message_ptr_t; - constant timeout : in time := max_timeout_c) is - variable status : com_status_t; - begin - deprecated("receive_reply() with request ID input. Use send receipt or message input instead"); - delete(message); - wait_for_reply_stash_message(net, receiver, inbox, request_id, status, timeout); - if status = ok then - message := get_reply_stash_message(receiver); - else - message := new message_t; - message.status := status; - end if; - end; - - procedure receive_reply ( - signal net : inout network_t; - constant receiver : in actor_t; - constant request_id : in message_id_t; - variable positive_ack : out boolean; - variable status : out com_status_t; - constant timeout : in time := max_timeout_c) is - variable message : message_ptr_t; - begin - deprecated("receive_reply() with request ID input. Use send receipt or message input instead"); - receive_reply(net, receiver, request_id, message, timeout); - if message.status = ok then - positive_ack := decode(message.payload.all); - else - positive_ack := false; - end if; - - status := message.status; - end; - - procedure reply ( - signal net : inout network_t; - constant sender : in actor_t; - constant receiver : in actor_t; - constant request_id : in message_id_t; - constant payload : in string := ""; - variable receipt : out receipt_t; - constant timeout : in time := max_timeout_c) is - variable message : message_ptr_t; - begin - deprecated("reply() with sender (already known by requestor)"); - deprecated("reply() with receipt. A reply being a request for which a reply is expected is not common "); - message := compose(payload, sender, request_id); - reply(net, receiver, message, receipt, timeout); - end; - - procedure reply ( - signal net : inout network_t; - constant receiver : in actor_t; - variable message : inout message_ptr_t; - variable receipt : out receipt_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := false) is - begin - deprecated("reply() with receipt. A reply being a request for which a reply is expected is not common "); - check(message.request_id /= no_message_id_c, reply_missing_request_id_error); - - send(net, receiver, message, receipt, timeout, keep_message); - end; - - procedure reply ( - signal net : inout network_t; - constant receiver : in actor_t; - constant request_id : in message_id_t; - constant payload : in string := ""; - variable receipt : out receipt_t; - constant timeout : in time := max_timeout_c) is - variable message : message_ptr_t; - begin - deprecated("reply() with receipt. A reply being a request for which a reply is expected is not common "); - message := compose(payload, request_id => request_id); - reply(net, receiver, message, receipt, timeout); - end; - - ----------------------------------------------------------------------------- - -- Secondary send and receive related subprograms - ----------------------------------------------------------------------------- - procedure send ( - signal net : inout network_t; - constant sender : in actor_t; - constant receiver : in actor_t; - constant payload : in string := ""; - variable receipt : out receipt_t; - constant timeout : in time := max_timeout_c) is - variable message : message_ptr_t; - begin - deprecated("send() with string payload"); - message := compose(payload, sender); - send(net, receiver, message, timeout, keep_message => true); - receipt := (status => ok, id => message.id); - delete(message); - end; - procedure send ( - signal net : inout network_t; - constant receiver : in actor_t; - constant payload : in string := ""; - variable receipt : out receipt_t; - constant timeout : in time := max_timeout_c) is - variable message : message_ptr_t; - begin - deprecated("send() with string payload"); - message := compose(payload); - send(net, receiver, message, timeout, keep_message => true); - receipt := (status => ok, id => message.id); - delete(message); - end; - - procedure request ( - signal net : inout network_t; - constant sender : in actor_t; - constant receiver : in actor_t; - constant request_payload : in string := ""; - variable reply_message : inout message_ptr_t; - constant timeout : in time := max_timeout_c) is - variable request_message : message_ptr_t; - begin - deprecated("request() with string payload"); - request_message := compose(request_payload, sender); - request(net, receiver, request_message, reply_message, timeout); - end; - - procedure request ( - signal net : inout network_t; - constant receiver : in actor_t; - variable request_message : inout message_ptr_t; - variable reply_message : inout message_ptr_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := false) is - variable start : time; - begin - deprecated("request() based on message_ptr_t"); - start := now; - send(net, receiver, request_message, timeout, keep_message => true); - receive_reply(net, request_message, reply_message, timeout - (now - start)); - if not keep_message then - delete(request_message); - end if; - end; - - procedure request ( - signal net : inout network_t; - constant sender : in actor_t; - constant receiver : in actor_t; - constant request_payload : in string := ""; - variable positive_ack : out boolean; - variable status : out com_status_t; - constant timeout : in time := max_timeout_c) is - variable request_message : message_ptr_t; - begin - deprecated("request() with status. use request() without status or send + wait_for_reply for polling requests"); - request_message := compose(request_payload, sender); - request(net, receiver, request_message, positive_ack, status, timeout); - end; - - procedure request ( - signal net : inout network_t; - constant receiver : in actor_t; - variable request_message : inout message_ptr_t; - variable positive_ack : out boolean; - variable status : out com_status_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := false) is - variable start : time; - begin - deprecated("request() with status. use request() without status or send + wait_for_reply for polling requests"); - start := now; - send(net, receiver, request_message, timeout, keep_message => true); - receive_reply(net, request_message, positive_ack, status, timeout - (now - start)); - if not keep_message then - delete(request_message); - end if; - end; - - -- - procedure receive_reply ( - signal net : inout network_t; - variable request : inout message_ptr_t; - variable positive_ack : out boolean; - constant timeout : in time := max_timeout_c) is - variable message : message_ptr_t; - begin - receive_reply(net, request, message, timeout); - positive_ack := decode(message.payload.all); - delete(message); - end; - - procedure request ( - signal net : inout network_t; - constant receiver : in actor_t; - variable request_message : inout message_ptr_t; - variable positive_ack : out boolean; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := false) is - variable start : time; - begin - deprecated("request() based on message_ptr_t"); - start := now; - send(net, receiver, request_message, timeout, keep_message => true); - receive_reply(net, request_message, positive_ack, timeout - (now - start)); - if not keep_message then - delete(request_message); - end if; - end; - - procedure publish ( - signal net : inout network_t; - variable message : inout message_ptr_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := false) is - begin - publish(net, message.sender, message, timeout, keep_message); - end; - - procedure acknowledge ( - signal net : inout network_t; - constant sender : in actor_t; - constant receiver : in actor_t; - constant request_id : in message_id_t; - constant positive_ack : in boolean := true; - variable receipt : out receipt_t; - constant timeout : in time := max_timeout_c) is - variable message : message_ptr_t; - begin - deprecated("acknowledge() with sender (already known by requestor)"); - deprecated("acknowledge() with receipt. An acknowledge being a request for which a reply is expected is not common "); - message := compose(encode(positive_ack), sender, request_id); - send(net, receiver, message, receipt, timeout); - end; - - procedure acknowledge ( - signal net : inout network_t; - constant receiver : in actor_t; - constant request_id : in message_id_t; - constant positive_ack : in boolean := true; - variable receipt : out receipt_t; - constant timeout : in time := max_timeout_c) is - begin - deprecated("acknowledge() with receipt. An acknowledge being a request for which a reply is expected is not common "); - acknowledge(net, null_actor_c, receiver, request_id, positive_ack, receipt, timeout); - end; - - - ----------------------------------------------------------------------------- - -- Low-level subprograms primarily used for handling timeout wihout error - ----------------------------------------------------------------------------- - impure function has_messages (actor : actor_t) return boolean is - begin - deprecated("has_messages() with old naming. Use has_message() instead"); - return has_message(actor); - end function has_messages; - - impure function get_message (receiver : actor_t; delete_from_inbox : boolean := true) return message_ptr_t is - variable message : message_ptr_t; - begin - deprecated("get_message() based on message_ptr_t"); - check(messenger.has_messages(receiver), null_message_error); - - message := new message_t; - message.status := ok; - message.id := messenger.get_id(receiver); - message.request_id := messenger.get_request_id(receiver); - message.sender := messenger.get_sender(receiver); - message.receiver := receiver; - write(message.payload, messenger.get_payload(receiver)); - if delete_from_inbox then - messenger.delete_envelope(receiver); - end if; - - return message; - end function get_message; - - procedure wait_for_messages ( - signal net : in network_t; - constant receiver : in actor_t; - variable status : out com_status_t; - constant receive_timeout : in time := max_timeout_c) is - begin - deprecated("wait_for_messages() with old naming. Use wait_for_message() instead"); - wait_for_message(net, receiver, status, receive_timeout); - end procedure wait_for_messages; - - - procedure publish ( - signal net : inout network_t; - constant sender : in actor_t; - constant payload : in string := ""; - variable status : out com_status_t; - constant timeout : in time := max_timeout_c) is - variable message : message_ptr_t; - begin - deprecated("publish() with status output"); - status := ok; - message := compose(payload, sender); - publish(net, message, status, timeout); - end; - - procedure publish ( - signal net : inout network_t; - variable message : inout message_ptr_t; - variable status : out com_status_t; - constant timeout : in time := max_timeout_c; - constant keep_message : in boolean := false) is - begin - deprecated("publish() with status output"); - check(message /= null, null_message_error); - - status := ok; - if messenger.subscriber_inbox_is_full(message.sender, (published, outbound)) then - wait on net until not messenger.subscriber_inbox_is_full(message.sender, (published, outbound)) for timeout; - check(not messenger.subscriber_inbox_is_full(message.sender, (published, outbound)), full_inbox_error); - end if; - - messenger.publish(message.sender, message.payload.all); - notify(net); - - if not keep_message then - delete(message); - end if; - end; - - ----------------------------------------------------------------------------- - -- Receive related subprograms - ----------------------------------------------------------------------------- - procedure subscribe ( - constant subscriber : in actor_t; - constant publisher : in actor_t; - variable status : out com_status_t) is - begin - deprecated("subscribe() with status output"); - status := ok; - messenger.subscribe(subscriber, publisher); - end procedure subscribe; - - procedure unsubscribe ( - constant subscriber : in actor_t; - constant publisher : in actor_t; - variable status : out com_status_t) is - begin - deprecated("subscribe() with status output"); - status := ok; - messenger.unsubscribe(subscriber, publisher); - end procedure unsubscribe; - - impure function num_of_missed_messages (actor : actor_t) return natural is - begin - deprecated("num_of_missed_messages(). Missed messages not allowed."); - return 0; - end num_of_missed_messages; - -end package body com_deprecated_pkg; +-- Com deprecated package provides deprecated functionality and APIs. These +-- will eventually be removed +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +use work.codec_pkg.all; +use work.com_support_pkg.all; +use work.com_messenger_pkg.all; +use work.com_types_pkg.all; +use work.com_pkg.all; +use work.com_common_pkg.all; + +use std.textio.all; + +package com_deprecated_pkg is + ----------------------------------------------------------------------------- + -- Handling of actors + ----------------------------------------------------------------------------- + constant null_actor_c : actor_t := null_actor; + impure function create (name : string := ""; inbox_size : positive := positive'high) return actor_t; + procedure destroy (actor : inout actor_t; status : out com_status_t); + impure function inbox_size (actor : actor_t) return natural; + + ----------------------------------------------------------------------------- + -- Message related subprograms + ----------------------------------------------------------------------------- + constant no_message_id_c : message_id_t := no_message_id; + impure function new_message (sender : actor_t := null_actor_c) return message_ptr_t; + impure function compose ( + payload : string := ""; + sender : actor_t := null_actor_c; + request_id : message_id_t := no_message_id_c) + return message_ptr_t; + procedure copy (src : inout message_ptr_t; dst : inout message_ptr_t); + procedure delete (message : inout message_ptr_t); + + ----------------------------------------------------------------------------- + -- Primary send and receive related subprograms + ----------------------------------------------------------------------------- + constant max_timeout_c : time := max_timeout; + procedure send ( + signal net : inout network_t; + constant receiver : in actor_t; + variable message : inout message_ptr_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := true); + procedure receive ( + signal net : inout network_t; + constant receiver : in actor_t; + variable message : inout message_ptr_t; + constant timeout : in time := max_timeout_c); + procedure reply ( + signal net : inout network_t; + variable request : inout message_ptr_t; + variable message : inout message_ptr_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := true); + procedure receive_reply ( + signal net : inout network_t; + variable request : inout message_ptr_t; + variable message : inout message_ptr_t; + constant timeout : in time := max_timeout_c); + procedure send ( + signal net : inout network_t; + constant receiver : in actor_t; + variable message : inout message_ptr_t; + variable receipt : out receipt_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := true); + procedure reply ( + signal net : inout network_t; + constant sender : in actor_t; + constant receiver : in actor_t; + constant request_id : in message_id_t; + constant payload : in string := ""; + variable receipt : out receipt_t; + constant timeout : in time := max_timeout_c); + procedure reply ( + signal net : inout network_t; + constant receiver : in actor_t; + constant request_id : in message_id_t; + constant payload : in string := ""; + variable receipt : out receipt_t; + constant timeout : in time := max_timeout_c); + procedure reply ( + signal net : inout network_t; + constant receiver : in actor_t; + variable message : inout message_ptr_t; + variable receipt : out receipt_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := false); + procedure receive_reply ( + signal net : inout network_t; + constant receiver : in actor_t; + constant request_id : in message_id_t; + variable message : inout message_ptr_t; + constant timeout : in time := max_timeout_c); + procedure receive_reply ( + signal net : inout network_t; + constant receiver : in actor_t; + constant request_id : in message_id_t; + variable positive_ack : out boolean; + variable status : out com_status_t; + constant timeout : in time := max_timeout_c); + + ----------------------------------------------------------------------------- + -- Secondary send and receive related subprograms + ----------------------------------------------------------------------------- + procedure send ( + signal net : inout network_t; + constant sender : in actor_t; + constant receiver : in actor_t; + constant payload : in string := ""; + variable receipt : out receipt_t; + constant timeout : in time := max_timeout_c); + procedure send ( + signal net : inout network_t; + constant receiver : in actor_t; + constant payload : in string := ""; + variable receipt : out receipt_t; + constant timeout : in time := max_timeout_c); + procedure request ( + signal net : inout network_t; + constant sender : in actor_t; + constant receiver : in actor_t; + constant request_payload : in string := ""; + variable reply_message : inout message_ptr_t; + constant timeout : in time := max_timeout_c); + procedure request ( + signal net : inout network_t; + constant receiver : in actor_t; + variable request_message : inout message_ptr_t; + variable reply_message : inout message_ptr_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := false); + procedure request ( + signal net : inout network_t; + constant receiver : in actor_t; + variable request_message : inout message_ptr_t; + variable positive_ack : out boolean; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := false); + procedure publish ( + signal net : inout network_t; + variable message : inout message_ptr_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := false); + procedure request ( + signal net : inout network_t; + constant sender : in actor_t; + constant receiver : in actor_t; + constant request_payload : in string := ""; + variable positive_ack : out boolean; + variable status : out com_status_t; + constant timeout : in time := max_timeout_c); + procedure request ( + signal net : inout network_t; + constant receiver : in actor_t; + variable request_message : inout message_ptr_t; + variable positive_ack : out boolean; + variable status : out com_status_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := false); + procedure publish ( + signal net : inout network_t; + constant sender : in actor_t; + constant payload : in string := ""; + variable status : out com_status_t; + constant timeout : in time := max_timeout_c); + procedure publish ( + signal net : inout network_t; + variable message : inout message_ptr_t; + variable status : out com_status_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := false); + procedure acknowledge ( + signal net : inout network_t; + constant sender : in actor_t; + constant receiver : in actor_t; + constant request_id : in message_id_t; + constant positive_ack : in boolean := true; + variable receipt : out receipt_t; + constant timeout : in time := max_timeout_c); + procedure acknowledge ( + signal net : inout network_t; + constant receiver : in actor_t; + constant request_id : in message_id_t; + constant positive_ack : in boolean := true; + variable receipt : out receipt_t; + constant timeout : in time := max_timeout_c); + + ----------------------------------------------------------------------------- + -- Low-level subprograms primarily used for handling timeout wihout error + ----------------------------------------------------------------------------- + impure function has_messages (actor : actor_t) return boolean; + impure function get_message (receiver : actor_t; delete_from_inbox : boolean := true) return message_ptr_t; + + procedure wait_for_messages ( + signal net : in network_t; + constant receiver : in actor_t; + variable status : out com_status_t; + constant receive_timeout : in time := max_timeout_c); + + ----------------------------------------------------------------------------- + -- Receive related subprograms + ----------------------------------------------------------------------------- + procedure subscribe ( + constant subscriber : in actor_t; + constant publisher : in actor_t; + variable status : out com_status_t); + procedure unsubscribe ( + constant subscriber : in actor_t; + constant publisher : in actor_t; + variable status : out com_status_t); + impure function num_of_missed_messages (actor : actor_t) return natural; + +end package; + +package body com_deprecated_pkg is + procedure deprecated (msg : string) is + begin + messenger.deprecated(msg); + end; + + ----------------------------------------------------------------------------- + -- Handling of actors + ----------------------------------------------------------------------------- + impure function create (name : string := ""; inbox_size : positive := positive'high) return actor_t is + begin + deprecated("create() instead of new_actor()"); + return new_actor(name, inbox_size); + end; + + procedure destroy (actor : inout actor_t; status : out com_status_t) is + begin + deprecated("destroy() with status output"); + status := ok; + messenger.destroy(actor); + end; + + impure function inbox_size (actor : actor_t) return natural is + begin + deprecated("inbox_size() instead of mailbox_size()"); + return messenger.mailbox_size(actor, inbox); + end; + + ----------------------------------------------------------------------------- + -- Message related subprograms + ----------------------------------------------------------------------------- + impure function new_message (sender : actor_t := null_actor_c) return message_ptr_t is + variable message : message_ptr_t; + begin + deprecated("new_message()"); + message := new message_t; + message.sender := sender; + return message; + end function; + + impure function compose ( + payload : string := ""; + sender : actor_t := null_actor_c; + request_id : message_id_t := no_message_id_c) + return message_ptr_t is + variable message : message_ptr_t; + begin + deprecated("compose()"); + message := new message_t; + message.sender := sender; + message.request_id := request_id; + write(message.payload, payload); + return message; + end function compose; + + procedure copy (src : inout message_ptr_t; dst : inout message_ptr_t) is + begin + deprecated("copy() based on message_ptr_t"); + dst := new message_t; + dst.id := src.id; + dst.status := src.status; + dst.receiver := src.receiver; + dst.sender := src.sender; + dst.request_id := src.request_id; + write(dst.payload, src.payload.all); + end procedure copy; + + procedure delete (message : inout message_ptr_t) is + begin + deprecated("delete() based on message_ptr_t"); + if message /= null then + deallocate(message.payload); + deallocate(message); + end if; + end procedure delete; + + ----------------------------------------------------------------------------- + -- Primary send and receive related subprograms + ----------------------------------------------------------------------------- + procedure send ( + signal net : inout network_t; + constant receiver : in actor_t; + constant mailbox_id : in mailbox_id_t; + variable message : inout message_ptr_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := true) is + variable receipt : receipt_t; + begin + deprecated("send() based on message_ptr_t"); + check(message /= null, null_message_error); + check(not messenger.unknown_actor(receiver), unknown_receiver_error); + + if messenger.is_full(receiver, mailbox_id) then + wait on net until not messenger.is_full(receiver, mailbox_id) for timeout; + check(not messenger.is_full(receiver, mailbox_id), full_inbox_error); + end if; + + messenger.send(message.sender, receiver, mailbox_id, message.request_id, message.payload.all, receipt); + message.id := receipt.id; + message.receiver := receiver; + notify(net); + + if not keep_message then + delete(message); + end if; + end; + + procedure send ( + signal net : inout network_t; + constant receiver : in actor_t; + variable message : inout message_ptr_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := true) is + begin + send(net, receiver, inbox, message, timeout, keep_message); + end; + + procedure receive ( + signal net : inout network_t; + constant receiver : in actor_t; + variable message : inout message_ptr_t; + constant timeout : in time := max_timeout_c) is + variable status : com_status_t; + variable started_with_full_inbox : boolean; + begin + deprecated("receive() based on message_ptr_t"); + delete(message); + wait_for_message(net, receiver, status, timeout); + + if not check(no_error_status(status, true), status) then + return; + end if; + + if status = ok then + started_with_full_inbox := messenger.is_full(receiver, inbox); + message := get_message(receiver); + if started_with_full_inbox then + notify(net); + end if; + else + message := new message_t; + message.receiver := receiver; + message.status := status; + end if; + end; + + procedure reply ( + signal net : inout network_t; + variable request : inout message_ptr_t; + variable message : inout message_ptr_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := true) is + begin + deprecated("reply() based on message_ptr_t"); + check(request.id /= no_message_id_c, reply_missing_request_id_error); + message.request_id := request.id; + message.sender := request.receiver; + + if request.sender /= null_actor_c then + send(net, request.sender, inbox, message, timeout, keep_message); + else + send(net, request.receiver, outbox, message, timeout, keep_message); + end if; + end; + + procedure wait_for_reply_stash_message ( + signal net : inout network_t; + constant receiver : in actor_t; + constant mailbox_id : in mailbox_id_t := inbox; + constant request_id : in message_id_t; + variable status : out com_status_t; + constant timeout : in time := max_timeout_c) is + variable started_with_full_inbox : boolean := false; + begin + check(not messenger.deferred(receiver), deferred_receiver_error); + + status := ok; + if mailbox_id = inbox then + started_with_full_inbox := messenger.is_full(receiver, inbox); + end if; + + if messenger.has_reply_stash_message(receiver, request_id) then + return; + elsif messenger.find_and_stash_reply_message(receiver, request_id, mailbox_id) then + if started_with_full_inbox then + notify(net); + end if; + return; + else + wait on net until messenger.find_and_stash_reply_message(receiver, request_id, mailbox_id) for timeout; + if not messenger.has_reply_stash_message(receiver, request_id) then + status := work.com_types_pkg.timeout; + elsif started_with_full_inbox then + notify(net); + end if; + end if; + end procedure wait_for_reply_stash_message; + + impure function get_reply_stash_message ( + receiver : actor_t; + clear_reply_stash : boolean := true) + return message_ptr_t is + variable message : message_ptr_t; + begin + check(messenger.has_reply_stash_message(receiver), null_message_error); + + message := new message_t; + message.status := ok; + message.id := messenger.get_reply_stash_message_id(receiver); + message.request_id := messenger.get_reply_stash_message_request_id(receiver); + message.sender := messenger.get_reply_stash_message_sender(receiver); + message.receiver := messenger.get_reply_stash_message_receiver(receiver); + write(message.payload, messenger.get_reply_stash_message_payload(receiver)); + if clear_reply_stash then + messenger.clear_reply_stash(receiver); + end if; + + return message; + end function get_reply_stash_message; + + procedure receive_reply ( + signal net : inout network_t; + variable request : inout message_ptr_t; + variable message : inout message_ptr_t; + constant timeout : in time := max_timeout_c) is + variable status : com_status_t; + variable source_actor : actor_t; + variable mailbox : mailbox_id_t; + begin + deprecated("receive_reply() based on message_ptr_t"); + delete(message); + + source_actor := request.sender when request.sender /= null_actor_c else request.receiver; + mailbox := inbox when request.sender /= null_actor_c else outbox; + + wait_for_reply_stash_message(net, source_actor, mailbox, request.id, status, timeout); + check(no_error_status(status, true), status); + if status = ok then + message := get_reply_stash_message(source_actor); + else + message := new message_t; + message.receiver := request.sender; + message.status := status; + end if; + end; + + -- + procedure publish ( + signal net : inout network_t; + constant sender : in actor_t; + variable message : inout message_ptr_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := true) is + begin + deprecated("publish() based on message_ptr_t"); + check(message /= null, null_message_error); + message.sender := sender; + message.receiver := null_actor_c; + + if messenger.subscriber_inbox_is_full(message.sender, (published, outbound)) then + wait on net until not messenger.subscriber_inbox_is_full(sender, (published, outbound)) for timeout; + check(not messenger.subscriber_inbox_is_full(message.sender, (published, outbound)), full_inbox_error); + end if; + + messenger.publish(message.sender, message.payload.all); + notify(net); + + if not keep_message then + delete(message); + end if; + end; + procedure send ( + signal net : inout network_t; + constant receiver : in actor_t; + variable message : inout message_ptr_t; + variable receipt : out receipt_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := true) is + begin + deprecated("send() with message and receipt. Use send() without receipt and look at message.id instead"); + check(message /= null, null_message_error); + check(not messenger.unknown_actor(receiver), unknown_receiver_error); + + if messenger.is_full(receiver, inbox) then + wait on net until not messenger.is_full(receiver, inbox) for timeout; + check(not messenger.is_full(receiver, inbox), full_inbox_error); + end if; + + messenger.send(message.sender, receiver, inbox, message.request_id, message.payload.all, receipt); + message.id := receipt.id; + message.receiver := receiver; + notify(net); + + if not keep_message then + delete(message); + end if; + end; + + -- + procedure receive_reply ( + signal net : inout network_t; + constant receiver : in actor_t; + constant receipt : in receipt_t; + variable positive_ack : out boolean; + variable status : out com_status_t; + constant timeout : in time := max_timeout_c) is + variable message : message_ptr_t; + begin + deprecated("receive_reply() with status output. Use without or wait_for_reply() if accepting timeout"); + wait_for_reply_stash_message(net, receiver, inbox, receipt.id, status, timeout); + check(no_error_status(status, true), status); + if status = ok then + message := get_reply_stash_message(receiver); + status := message.status; + positive_ack := decode(message.payload.all); + delete(message); + else + positive_ack := false; + end if; + end; + + -- + procedure receive_reply ( + signal net : inout network_t; + variable request : inout message_ptr_t; + variable positive_ack : out boolean; + variable status : out com_status_t; + constant timeout : in time := max_timeout_c) is + constant receipt : receipt_t := (status => ok, id => request.id); + begin + receive_reply(net, request.sender, receipt, positive_ack, status, timeout); + end; + + procedure receive_reply ( + signal net : inout network_t; + constant receiver : in actor_t; + constant request_id : in message_id_t; + variable message : inout message_ptr_t; + constant timeout : in time := max_timeout_c) is + variable status : com_status_t; + begin + deprecated("receive_reply() with request ID input. Use send receipt or message input instead"); + delete(message); + wait_for_reply_stash_message(net, receiver, inbox, request_id, status, timeout); + if status = ok then + message := get_reply_stash_message(receiver); + else + message := new message_t; + message.status := status; + end if; + end; + + procedure receive_reply ( + signal net : inout network_t; + constant receiver : in actor_t; + constant request_id : in message_id_t; + variable positive_ack : out boolean; + variable status : out com_status_t; + constant timeout : in time := max_timeout_c) is + variable message : message_ptr_t; + begin + deprecated("receive_reply() with request ID input. Use send receipt or message input instead"); + receive_reply(net, receiver, request_id, message, timeout); + if message.status = ok then + positive_ack := decode(message.payload.all); + else + positive_ack := false; + end if; + + status := message.status; + end; + + procedure reply ( + signal net : inout network_t; + constant sender : in actor_t; + constant receiver : in actor_t; + constant request_id : in message_id_t; + constant payload : in string := ""; + variable receipt : out receipt_t; + constant timeout : in time := max_timeout_c) is + variable message : message_ptr_t; + begin + deprecated("reply() with sender (already known by requestor)"); + deprecated("reply() with receipt. A reply being a request for which a reply is expected is not common "); + message := compose(payload, sender, request_id); + reply(net, receiver, message, receipt, timeout); + end; + + procedure reply ( + signal net : inout network_t; + constant receiver : in actor_t; + variable message : inout message_ptr_t; + variable receipt : out receipt_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := false) is + begin + deprecated("reply() with receipt. A reply being a request for which a reply is expected is not common "); + check(message.request_id /= no_message_id_c, reply_missing_request_id_error); + + send(net, receiver, message, receipt, timeout, keep_message); + end; + + procedure reply ( + signal net : inout network_t; + constant receiver : in actor_t; + constant request_id : in message_id_t; + constant payload : in string := ""; + variable receipt : out receipt_t; + constant timeout : in time := max_timeout_c) is + variable message : message_ptr_t; + begin + deprecated("reply() with receipt. A reply being a request for which a reply is expected is not common "); + message := compose(payload, request_id => request_id); + reply(net, receiver, message, receipt, timeout); + end; + + ----------------------------------------------------------------------------- + -- Secondary send and receive related subprograms + ----------------------------------------------------------------------------- + procedure send ( + signal net : inout network_t; + constant sender : in actor_t; + constant receiver : in actor_t; + constant payload : in string := ""; + variable receipt : out receipt_t; + constant timeout : in time := max_timeout_c) is + variable message : message_ptr_t; + begin + deprecated("send() with string payload"); + message := compose(payload, sender); + send(net, receiver, message, timeout, keep_message => true); + receipt := (status => ok, id => message.id); + delete(message); + end; + procedure send ( + signal net : inout network_t; + constant receiver : in actor_t; + constant payload : in string := ""; + variable receipt : out receipt_t; + constant timeout : in time := max_timeout_c) is + variable message : message_ptr_t; + begin + deprecated("send() with string payload"); + message := compose(payload); + send(net, receiver, message, timeout, keep_message => true); + receipt := (status => ok, id => message.id); + delete(message); + end; + + procedure request ( + signal net : inout network_t; + constant sender : in actor_t; + constant receiver : in actor_t; + constant request_payload : in string := ""; + variable reply_message : inout message_ptr_t; + constant timeout : in time := max_timeout_c) is + variable request_message : message_ptr_t; + begin + deprecated("request() with string payload"); + request_message := compose(request_payload, sender); + request(net, receiver, request_message, reply_message, timeout); + end; + + procedure request ( + signal net : inout network_t; + constant receiver : in actor_t; + variable request_message : inout message_ptr_t; + variable reply_message : inout message_ptr_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := false) is + variable start : time; + begin + deprecated("request() based on message_ptr_t"); + start := now; + send(net, receiver, request_message, timeout, keep_message => true); + receive_reply(net, request_message, reply_message, timeout - (now - start)); + if not keep_message then + delete(request_message); + end if; + end; + + procedure request ( + signal net : inout network_t; + constant sender : in actor_t; + constant receiver : in actor_t; + constant request_payload : in string := ""; + variable positive_ack : out boolean; + variable status : out com_status_t; + constant timeout : in time := max_timeout_c) is + variable request_message : message_ptr_t; + begin + deprecated("request() with status. use request() without status or send + wait_for_reply for polling requests"); + request_message := compose(request_payload, sender); + request(net, receiver, request_message, positive_ack, status, timeout); + end; + + procedure request ( + signal net : inout network_t; + constant receiver : in actor_t; + variable request_message : inout message_ptr_t; + variable positive_ack : out boolean; + variable status : out com_status_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := false) is + variable start : time; + begin + deprecated("request() with status. use request() without status or send + wait_for_reply for polling requests"); + start := now; + send(net, receiver, request_message, timeout, keep_message => true); + receive_reply(net, request_message, positive_ack, status, timeout - (now - start)); + if not keep_message then + delete(request_message); + end if; + end; + + -- + procedure receive_reply ( + signal net : inout network_t; + variable request : inout message_ptr_t; + variable positive_ack : out boolean; + constant timeout : in time := max_timeout_c) is + variable message : message_ptr_t; + begin + receive_reply(net, request, message, timeout); + positive_ack := decode(message.payload.all); + delete(message); + end; + + procedure request ( + signal net : inout network_t; + constant receiver : in actor_t; + variable request_message : inout message_ptr_t; + variable positive_ack : out boolean; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := false) is + variable start : time; + begin + deprecated("request() based on message_ptr_t"); + start := now; + send(net, receiver, request_message, timeout, keep_message => true); + receive_reply(net, request_message, positive_ack, timeout - (now - start)); + if not keep_message then + delete(request_message); + end if; + end; + + procedure publish ( + signal net : inout network_t; + variable message : inout message_ptr_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := false) is + begin + publish(net, message.sender, message, timeout, keep_message); + end; + + procedure acknowledge ( + signal net : inout network_t; + constant sender : in actor_t; + constant receiver : in actor_t; + constant request_id : in message_id_t; + constant positive_ack : in boolean := true; + variable receipt : out receipt_t; + constant timeout : in time := max_timeout_c) is + variable message : message_ptr_t; + begin + deprecated("acknowledge() with sender (already known by requestor)"); + deprecated("acknowledge() with receipt. An acknowledge being a request for which a reply is expected is not common "); + message := compose(encode(positive_ack), sender, request_id); + send(net, receiver, message, receipt, timeout); + end; + + procedure acknowledge ( + signal net : inout network_t; + constant receiver : in actor_t; + constant request_id : in message_id_t; + constant positive_ack : in boolean := true; + variable receipt : out receipt_t; + constant timeout : in time := max_timeout_c) is + begin + deprecated("acknowledge() with receipt. An acknowledge being a request for which a reply is expected is not common "); + acknowledge(net, null_actor_c, receiver, request_id, positive_ack, receipt, timeout); + end; + + + ----------------------------------------------------------------------------- + -- Low-level subprograms primarily used for handling timeout wihout error + ----------------------------------------------------------------------------- + impure function has_messages (actor : actor_t) return boolean is + begin + deprecated("has_messages() with old naming. Use has_message() instead"); + return has_message(actor); + end function has_messages; + + impure function get_message (receiver : actor_t; delete_from_inbox : boolean := true) return message_ptr_t is + variable message : message_ptr_t; + begin + deprecated("get_message() based on message_ptr_t"); + check(messenger.has_messages(receiver), null_message_error); + + message := new message_t; + message.status := ok; + message.id := messenger.get_id(receiver); + message.request_id := messenger.get_request_id(receiver); + message.sender := messenger.get_sender(receiver); + message.receiver := receiver; + write(message.payload, messenger.get_payload(receiver)); + if delete_from_inbox then + messenger.delete_envelope(receiver); + end if; + + return message; + end function get_message; + + procedure wait_for_messages ( + signal net : in network_t; + constant receiver : in actor_t; + variable status : out com_status_t; + constant receive_timeout : in time := max_timeout_c) is + begin + deprecated("wait_for_messages() with old naming. Use wait_for_message() instead"); + wait_for_message(net, receiver, status, receive_timeout); + end procedure wait_for_messages; + + + procedure publish ( + signal net : inout network_t; + constant sender : in actor_t; + constant payload : in string := ""; + variable status : out com_status_t; + constant timeout : in time := max_timeout_c) is + variable message : message_ptr_t; + begin + deprecated("publish() with status output"); + status := ok; + message := compose(payload, sender); + publish(net, message, status, timeout); + end; + + procedure publish ( + signal net : inout network_t; + variable message : inout message_ptr_t; + variable status : out com_status_t; + constant timeout : in time := max_timeout_c; + constant keep_message : in boolean := false) is + begin + deprecated("publish() with status output"); + check(message /= null, null_message_error); + + status := ok; + if messenger.subscriber_inbox_is_full(message.sender, (published, outbound)) then + wait on net until not messenger.subscriber_inbox_is_full(message.sender, (published, outbound)) for timeout; + check(not messenger.subscriber_inbox_is_full(message.sender, (published, outbound)), full_inbox_error); + end if; + + messenger.publish(message.sender, message.payload.all); + notify(net); + + if not keep_message then + delete(message); + end if; + end; + + ----------------------------------------------------------------------------- + -- Receive related subprograms + ----------------------------------------------------------------------------- + procedure subscribe ( + constant subscriber : in actor_t; + constant publisher : in actor_t; + variable status : out com_status_t) is + begin + deprecated("subscribe() with status output"); + status := ok; + messenger.subscribe(subscriber, publisher); + end procedure subscribe; + + procedure unsubscribe ( + constant subscriber : in actor_t; + constant publisher : in actor_t; + variable status : out com_status_t) is + begin + deprecated("subscribe() with status output"); + status := ok; + messenger.unsubscribe(subscriber, publisher); + end procedure unsubscribe; + + impure function num_of_missed_messages (actor : actor_t) return natural is + begin + deprecated("num_of_missed_messages(). Missed messages not allowed."); + return 0; + end num_of_missed_messages; + +end package body com_deprecated_pkg; diff --git a/vunit/vhdl/com/src/com_messenger.vhd b/vunit/vhdl/com/src/com_messenger.vhd index 7d00b4a0c..90cf58333 100644 --- a/vunit/vhdl/com/src/com_messenger.vhd +++ b/vunit/vhdl/com/src/com_messenger.vhd @@ -1,1212 +1,1212 @@ --- This file defines the com messenger which is responsible for housing the --- messages in the system. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.com_types_pkg.all; -use work.com_support_pkg.all; -use work.queue_pkg.all; -use work.queue_pool_pkg.all; -use work.string_ptr_pkg.all; -use work.codec_pkg.all; -use work.logger_pkg.all; -use work.log_levels_pkg.all; - -use std.textio.all; - -package com_messenger_pkg is - type subscription_traffic_types_t is array (natural range <>) of subscription_traffic_type_t; - - type messenger_t is protected - ----------------------------------------------------------------------------- - -- Handling of actors - ----------------------------------------------------------------------------- - impure function create ( - name : string := ""; - inbox_size : positive := positive'high; - outbox_size : positive := positive'high - ) return actor_t; - impure function find (name : string; enable_deferred_creation : boolean := true) return actor_t; - impure function name (actor : actor_t) return string; - - procedure destroy (actor : inout actor_t); - procedure reset_messenger; - - impure function num_of_actors return natural; - impure function get_all_actors return actor_vec_t; - impure function is_deferred(actor : actor_t) return boolean; - impure function num_of_deferred_creations return natural; - impure function unknown_actor (actor : actor_t) return boolean; - impure function deferred (actor : actor_t) return boolean; - impure function is_full (actor : actor_t; mailbox_id : mailbox_id_t) return boolean; - impure function num_of_messages (actor : actor_t; mailbox_id : mailbox_id_t) return natural; - impure function mailbox_size (actor : actor_t; mailbox_id : mailbox_id_t) return natural; - procedure resize_mailbox (actor : actor_t; new_size : natural; mailbox_id : mailbox_id_t); - impure function subscriber_inbox_is_full ( - publisher : actor_t; - subscription_traffic_types : subscription_traffic_types_t) return boolean; - impure function has_subscribers ( - actor : actor_t; - subscription_traffic_type : subscription_traffic_type_t := published) return boolean; - - ----------------------------------------------------------------------------- - -- Send related subprograms - ----------------------------------------------------------------------------- - procedure send ( - constant sender : in actor_t; - constant receiver : in actor_t; - constant mailbox_id : in mailbox_id_t; - constant request_id : in message_id_t; - constant payload : in string; - variable receipt : out receipt_t); - procedure send ( - constant receiver : in actor_t; - constant mailbox_id : in mailbox_id_t; - variable msg : inout msg_t); - procedure publish (sender : actor_t; payload : string); - procedure publish ( - constant sender : in actor_t; - variable msg : inout msg_t; - constant subscriber_traffic_types : in subscription_traffic_types_t); - procedure internal_publish ( - constant sender : in actor_t; - variable msg : inout msg_t; - constant subscriber_traffic_types : in subscription_traffic_types_t); - - ----------------------------------------------------------------------------- - -- Receive related subprograms - ----------------------------------------------------------------------------- - impure function has_messages (actor : actor_t) return boolean; - impure function has_messages (actor_vec : actor_vec_t) return boolean; - impure function get_payload ( - actor : actor_t; - position : natural := 0; - mailbox_id : mailbox_id_t := inbox) return string; - impure function get_sender ( - actor : actor_t; - position : natural := 0; - mailbox_id : mailbox_id_t := inbox) return actor_t; - impure function get_receiver ( - actor : actor_t; - position : natural := 0; - mailbox_id : mailbox_id_t := inbox) return actor_t; - impure function get_id ( - actor : actor_t; - position : natural := 0; - mailbox_id : mailbox_id_t := inbox) return message_id_t; - impure function get_request_id ( - actor : actor_t; - position : natural := 0; - mailbox_id : mailbox_id_t := inbox) return message_id_t; - impure function get_all_but_payload ( - actor : actor_t; - position : natural := 0; - mailbox_id : mailbox_id_t := inbox) return msg_t; - - procedure delete_envelope ( - actor : actor_t; - position : natural := 0; - mailbox_id : mailbox_id_t := inbox); - - impure function has_reply_stash_message ( - actor : actor_t; - request_id : message_id_t := no_message_id) - return boolean; -- - impure function get_reply_stash_message_payload (actor : actor_t) return string; - impure function get_reply_stash_message_sender (actor : actor_t) return actor_t; - impure function get_reply_stash_message_receiver (actor : actor_t) return actor_t; - impure function get_reply_stash_message_id (actor : actor_t) return message_id_t; - impure function get_reply_stash_message_request_id (actor : actor_t) return message_id_t; - impure function find_reply_message ( - actor : actor_t; - request_id : message_id_t; - mailbox_id : mailbox_id_t := inbox) - return integer; - impure function find_and_stash_reply_message ( - actor : actor_t; - request_id : message_id_t; - mailbox_id : mailbox_id_t := inbox) - return boolean; - procedure clear_reply_stash (actor : actor_t); - - procedure subscribe ( - subscriber : actor_t; - publisher : actor_t; - traffic_type : subscription_traffic_type_t := published); - procedure unsubscribe ( - subscriber : actor_t; - publisher : actor_t; - traffic_type : subscription_traffic_type_t := published); - - --------------------------------------------------------------------------- - -- Debugging - --------------------------------------------------------------------------- - impure function to_string(msg : msg_t) return string; - impure function get_subscriptions(subscriber : actor_t) return subscription_vec_t; - impure function get_subscribers(publisher : actor_t) return subscription_vec_t; - - --------------------------------------------------------------------------- - -- Misc - --------------------------------------------------------------------------- - procedure allow_timeout; - impure function timeout_is_allowed return boolean; - procedure allow_deprecated; - procedure deprecated (msg : string); - - end protected; -end package com_messenger_pkg; - -package body com_messenger_pkg is - type envelope_t; - type envelope_ptr_t is access envelope_t; - - type envelope_t is record - message : message_t; - next_envelope : envelope_ptr_t; - end record envelope_t; - type envelope_ptr_array is array (positive range <>) of envelope_ptr_t; - - type mailbox_t is record - num_of_messages : natural; - size : natural; - first_envelope : envelope_ptr_t; - last_envelope : envelope_ptr_t; - end record mailbox_t; - type mailbox_ptr_t is access mailbox_t; - - impure function create(size : natural := natural'high) - return mailbox_ptr_t is - begin - return new mailbox_t'(0, size, null, null); - end function create; - - -- TODO: subscriber can be simplified to a pointer to an array. - type subscriber_item_t; - type subscriber_item_ptr_t is access subscriber_item_t; - - type subscriber_item_t is record - actor : actor_t; - next_item : subscriber_item_ptr_t; - end record subscriber_item_t; - - type subscribers_t is array (subscription_traffic_type_t range published to inbound) of subscriber_item_ptr_t; - - type actor_item_t is record - actor : actor_t; - name : line; - deferred_creation : boolean; - inbox : mailbox_ptr_t; - outbox : mailbox_ptr_t; - reply_stash : envelope_ptr_t; - subscribers : subscribers_t; - end record actor_item_t; - - type actor_item_array_t is array (natural range <>) of actor_item_t; - type actor_item_array_ptr_t is access actor_item_array_t; - - type messenger_t is protected - body - variable null_actor_item : actor_item_t := ( - actor => null_actor, - name => null, - deferred_creation => false, - inbox => create(0), - outbox => create(0), - reply_stash => null, - subscribers => (null, null, null)); -- - variable envelope_recycle_bin : envelope_ptr_array(1 to 1000); - variable n_recycled_envelopes : natural := 0; - variable null_message : message_t := (no_message_id, null_msg_type, ok, null_actor, - null_actor, no_message_id, null); - variable next_message_id : message_id_t := no_message_id + 1; - variable timeout_allowed : boolean := false; - variable deprecated_allowed : boolean := false; - - ----------------------------------------------------------------------------- - -- Handling of actors - ----------------------------------------------------------------------------- - impure function new_envelope return envelope_ptr_t is - begin - if n_recycled_envelopes > 0 then - n_recycled_envelopes := n_recycled_envelopes - 1; - envelope_recycle_bin(n_recycled_envelopes + 1).message := null_message; - envelope_recycle_bin(n_recycled_envelopes + 1).next_envelope := null; - return envelope_recycle_bin(n_recycled_envelopes + 1); - else - return new envelope_t; - end if; - end new_envelope; - - procedure deallocate_envelope (ptr : inout envelope_ptr_t) is - begin - if (n_recycled_envelopes < envelope_recycle_bin'length) and (ptr /= null) then - n_recycled_envelopes := n_recycled_envelopes + 1; - envelope_recycle_bin(n_recycled_envelopes) := ptr; - ptr := null; - else - deallocate(ptr); - end if; - end deallocate_envelope; - - impure function init_actors return actor_item_array_ptr_t is - variable ret_val : actor_item_array_ptr_t; - begin - ret_val := new actor_item_array_t(0 to 0); - ret_val(0) := null_actor_item; - - return ret_val; - end function init_actors; - - variable actors : actor_item_array_ptr_t := init_actors; - - impure function find_actor (name : string) return actor_t is - variable ret_val : actor_t; - begin - for i in actors'reverse_range loop - ret_val := actors(i).actor; - if actors(i).name /= null then - exit when actors(i).name.all = name; - end if; - end loop; - - return ret_val; - end; - - impure function create_actor ( - name : string := ""; - deferred_creation : in boolean := false; - inbox_size : in natural := natural'high; - outbox_size : in natural := natural'high) - return actor_t is - variable old_actors : actor_item_array_ptr_t; - begin - old_actors := actors; - actors := new actor_item_array_t(0 to actors'length); - actors(0) := null_actor_item; - for i in old_actors'range loop - actors(i) := old_actors(i); - end loop; - deallocate(old_actors); - actors(actors'length - 1) := ((id => actors'length - 1), new string'(name), - deferred_creation, create(inbox_size), create(outbox_size), null, (null, null, null)); - - return actors(actors'length - 1).actor; - end function; - - impure function find (name : string; enable_deferred_creation : boolean := true) return actor_t is - constant actor : actor_t := find_actor(name); - begin - if name = "" then - return null_actor; - elsif (actor = null_actor) and enable_deferred_creation then - return create_actor(name, true, 1); - else - return actor; - end if; - end; - - impure function name (actor : actor_t) return string is - begin - if actors(actor.id).name /= null then - return actors(actor.id).name.all; - else - return ""; - end if; - - end; - - - impure function create ( - name : string := ""; - inbox_size : positive := positive'high; - outbox_size : positive := positive'high - ) return actor_t is - variable actor : actor_t := find_actor(name); - begin - if (actor = null_actor) or (name = "") then - actor := create_actor(name, false, inbox_size, outbox_size); - elsif actors(actor.id).deferred_creation then - actors(actor.id).deferred_creation := false; - actors(actor.id).inbox.size := inbox_size; - actors(actor.id).outbox.size := outbox_size; - else - check_failed(duplicate_actor_name_error); - end if; - - return actor; - end; - - impure function is_subscriber ( - subscriber : actor_t; - publisher : actor_t; - traffic_type : subscription_traffic_type_t) return boolean is - variable item : subscriber_item_ptr_t := actors(publisher.id).subscribers(traffic_type); - begin - while item /= null loop - if item.actor = subscriber then - return true; - end if; - item := item.next_item; - end loop; - - return false; - end; - - procedure remove_subscriber (subscriber : actor_t; publisher : actor_t; traffic_type : subscription_traffic_type_t) is - variable item, previous_item : subscriber_item_ptr_t; - begin - item := actors(publisher.id).subscribers(traffic_type); - previous_item := null; - while item /= null loop - if item.actor = subscriber then - if previous_item = null then - actors(publisher.id).subscribers(traffic_type) := item.next_item; - else - previous_item.next_item := item.next_item; - end if; - deallocate(item); - return; - end if; - previous_item := item; - item := item.next_item; - end loop; - - check_failed(not_a_subscriber_error); - end; - - procedure destroy (actor : inout actor_t) is - variable envelope : envelope_ptr_t; - variable item : subscriber_item_ptr_t; - begin - check(not unknown_actor(actor), unknown_actor_error); - - while actors(actor.id).inbox.first_envelope /= null loop - envelope := actors(actor.id).inbox.first_envelope; - actors(actor.id).inbox.first_envelope := envelope.next_envelope; - deallocate(envelope.message.payload); - deallocate_envelope(envelope); - end loop; - - for t in subscription_traffic_type_t'left to subscription_traffic_type_t'right loop - while actors(actor.id).subscribers(t) /= null loop - item := actors(actor.id).subscribers(t); - actors(actor.id).subscribers(t) := item.next_item; - deallocate(item); - end loop; - end loop; - - for i in actors'range loop - for t in subscription_traffic_type_t'left to subscription_traffic_type_t'right loop - if is_subscriber(actor, actors(i).actor, t) then - remove_subscriber(actor, actors(i).actor, t); - end if; - end loop; - end loop; - - deallocate(actors(actor.id).name); - deallocate(actors(actor.id).inbox); - deallocate(actors(actor.id).outbox); - actors(actor.id) := null_actor_item; - actor := null_actor; - end; - - procedure reset_messenger is - begin - for i in actors'range loop - if actors(i).actor /= null_actor then - destroy(actors(i).actor); - end if; - end loop; - deallocate(actors); - actors := init_actors; - next_message_id := no_message_id + 1; - end; - - impure function num_of_actors return natural is - variable n_actors : natural := 0; - begin - for i in actors'range loop - if actors(i).actor /= null_actor then - n_actors := n_actors + 1; - end if; - end loop; - - return n_actors; - end; - - impure function get_all_actors return actor_vec_t is - constant n_actors : natural := num_of_actors; - variable result : actor_vec_t(0 to n_actors - 1); - variable idx : natural := 0; - begin - for i in actors'range loop - if actors(i).actor /= null_actor then - result(idx) := actors(i).actor; - idx := idx + 1; - end if; - end loop; - - return result; - end; - - - impure function is_deferred(actor : actor_t) return boolean is - begin - return actors(actor.id).deferred_creation; - end; - - impure function num_of_deferred_creations return natural is - variable n_deferred_actors : natural := 0; - begin - for i in actors'range loop - if actors(i).deferred_creation then - n_deferred_actors := n_deferred_actors + 1; - end if; - end loop; - - return n_deferred_actors; - end; - - impure function unknown_actor (actor : actor_t) return boolean is - begin - if (actor.id = 0) or (actor.id > actors'length - 1) then - return true; - elsif actors(actor.id).actor = null_actor then - return true; - end if; - - return false; - end function unknown_actor; - - impure function deferred (actor : actor_t) return boolean is - begin - return actors(actor.id).deferred_creation; - end function deferred; - - impure function is_full (actor : actor_t; mailbox_id : mailbox_id_t) return boolean is - begin - if mailbox_id = inbox then - return actors(actor.id).inbox.num_of_messages >= actors(actor.id).inbox.size; - else - return actors(actor.id).outbox.num_of_messages >= actors(actor.id).outbox.size; - end if; - end function; - - impure function num_of_messages (actor : actor_t; mailbox_id : mailbox_id_t) return natural is - begin - if mailbox_id = inbox then - return actors(actor.id).inbox.num_of_messages; - else - return actors(actor.id).outbox.num_of_messages; - end if; - end function; - - procedure resize_mailbox (actor : actor_t; new_size : natural; mailbox_id : mailbox_id_t) is - begin - if mailbox_id = inbox then - check(num_of_messages(actor, inbox) <= new_size, insufficient_size_error); - actors(actor.id).inbox.size := new_size; - else - check(num_of_messages(actor, outbox) <= new_size, insufficient_size_error); - actors(actor.id).outbox.size := new_size; - end if; - end; - - impure function subscriber_inbox_is_full ( - publisher : actor_t; - subscription_traffic_types : subscription_traffic_types_t) return boolean is - variable result : boolean_vector(subscription_traffic_types'range) := (others => false); - procedure has_full_inboxes ( - variable subscribers : in subscriber_item_ptr_t; - variable result : out boolean) is - variable item : subscriber_item_ptr_t := subscribers; - begin - result := false; - while item /= null loop - result := is_full(item.actor, inbox); - exit when result; - has_full_inboxes(actors(item.actor.id).subscribers(inbound), result); - exit when result; - item := item.next_item; - end loop; - end; - begin - for t in subscription_traffic_types'range loop - has_full_inboxes(actors(publisher.id).subscribers(subscription_traffic_types(t)), result(t)); - end loop; - - return or result; - end function; - - impure function has_subscribers ( - actor : actor_t; - subscription_traffic_type : subscription_traffic_type_t := published) return boolean is - begin - return actors(actor.id).subscribers(subscription_traffic_type) /= null; - end; - - impure function mailbox_size (actor : actor_t; mailbox_id : mailbox_id_t) return natural is - begin - if mailbox_id = inbox then - return actors(actor.id).inbox.size; - else - return actors(actor.id).outbox.size; - end if; - end function; - - ----------------------------------------------------------------------------- - -- Send related subprograms - ----------------------------------------------------------------------------- - procedure send ( - constant sender : in actor_t; - constant receiver : in actor_t; - constant mailbox_id : in mailbox_id_t; - constant request_id : in message_id_t; - constant payload : in string; - variable receipt : out receipt_t) is - variable envelope : envelope_ptr_t; - variable mailbox : mailbox_ptr_t; - begin - check(not is_full(receiver, mailbox_id), full_inbox_error); - - receipt.status := ok; - receipt.id := next_message_id; - envelope := new_envelope; - envelope.message.sender := sender; - envelope.message.receiver := receiver; - envelope.message.id := next_message_id; - envelope.message.request_id := request_id; - write(envelope.message.payload, payload); - next_message_id := next_message_id + 1; - - mailbox := actors(receiver.id).inbox when mailbox_id = inbox else actors(receiver.id).outbox; - mailbox.num_of_messages := mailbox.num_of_messages + 1; - if mailbox.last_envelope /= null then - mailbox.last_envelope.next_envelope := envelope; - else - mailbox.first_envelope := envelope; - end if; - mailbox.last_envelope := envelope; - end; - - procedure publish (sender : actor_t; payload : string) is - variable receipt : receipt_t; - variable subscriber_item : subscriber_item_ptr_t; - begin - check(not unknown_actor(sender), unknown_publisher_error); - - subscriber_item := actors(sender.id).subscribers(published); - while subscriber_item /= null loop - send(sender, subscriber_item.actor, inbox, no_message_id, payload, receipt); - subscriber_item := subscriber_item.next_item; - end loop; - end; - - impure function to_string(msg : msg_t) return string is - function id_to_string(id : message_id_t) return string is - begin - if id = no_message_id then - return "-"; - else - return to_string(id); - end if; - end function; - - impure function actor_to_string(actor : actor_t) return string is - begin - if actor = null_actor then - return "-"; - else - return name(actor); - end if; - end function; - - impure function msg_type_to_string (msg_type : msg_type_t) return string is - begin - if msg_type = null_msg_type then - return "-"; - else - return name(msg_type); - end if; - end; - - begin - return id_to_string(msg.id) & ":" & id_to_string(msg.request_id) & " " & - actor_to_string(msg.sender) & " -> " & actor_to_string(msg.receiver) & - " (" & msg_type_to_string(msg.msg_type) & ")"; - end; - - procedure put_message ( - receiver : actor_t; - msg : msg_t; - mailbox_id : mailbox_id_t; - copy_msg : boolean) is - variable envelope : envelope_ptr_t; - variable data : msg_data_t := msg.data; - variable mailbox : mailbox_ptr_t; - - begin - if copy_msg then - data := new_queue(queue_pool); - for i in 0 to length(msg.data) - 1 loop - unsafe_push(data, get(msg.data.data, 1+i)); - end loop; - end if; - - if is_visible(com_logger, trace) then - trace(com_logger, "[" & to_string(msg) & "] => " & name(receiver) & " " & mailbox_id_t'image(mailbox_id)); - end if; - - envelope := new_envelope; - envelope.message.id := msg.id; - envelope.message.msg_type := msg.msg_type; - envelope.message.sender := msg.sender; - envelope.message.receiver := msg.receiver; - envelope.message.request_id := msg.request_id; - write(envelope.message.payload, encode(data)); - - mailbox := actors(receiver.id).inbox when mailbox_id = inbox else actors(receiver.id).outbox; - mailbox.num_of_messages := mailbox.num_of_messages + 1; - if mailbox.last_envelope /= null then - mailbox.last_envelope.next_envelope := envelope; - else - mailbox.first_envelope := envelope; - end if; - mailbox.last_envelope := envelope; - end procedure; - - procedure put_subscriber_messages ( - variable subscribers : inout subscriber_item_ptr_t; - variable msg : inout msg_t; - constant set_msg_receiver : in boolean) is - variable subscriber_item : subscriber_item_ptr_t := subscribers; - begin - while subscriber_item /= null loop - if set_msg_receiver then - msg.receiver := subscriber_item.actor; - end if; - put_message(subscriber_item.actor, msg, inbox, true); - internal_publish(subscriber_item.actor, msg, (0 => inbound)); - subscriber_item := subscriber_item.next_item; - end loop; - end; - - procedure publish ( - constant sender : in actor_t; - variable msg : inout msg_t; - constant subscriber_traffic_types : in subscription_traffic_types_t) is - begin - check(not unknown_actor(sender), unknown_publisher_error); - check(msg.data /= null_queue, null_message_error); - - msg.id := next_message_id; - next_message_id := next_message_id + 1; - msg.status := ok; - msg.sender := sender; - - for t in subscriber_traffic_types'range loop - put_subscriber_messages(actors(sender.id).subscribers(subscriber_traffic_types(t)), - msg, set_msg_receiver => true); - end loop; - - msg.receiver := null_actor; - end; - - procedure internal_publish ( - constant sender : in actor_t; - variable msg : inout msg_t; - constant subscriber_traffic_types : in subscription_traffic_types_t) is - begin - for t in subscriber_traffic_types'range loop - put_subscriber_messages(actors(sender.id).subscribers(subscriber_traffic_types(t)), - msg, set_msg_receiver => false); - end loop; - end; - - procedure send ( - constant receiver : in actor_t; - constant mailbox_id : in mailbox_id_t; - variable msg : inout msg_t) is - begin - msg.id := next_message_id; - next_message_id := next_message_id + 1; - msg.status := ok; - if mailbox_id = inbox then - msg.receiver := receiver; - else - msg.sender := receiver; - msg.receiver := null_actor; - end if; - - - put_message(receiver, msg, mailbox_id, false); - end; - - ----------------------------------------------------------------------------- - -- Receive related subprograms - ----------------------------------------------------------------------------- - impure function has_messages (actor : actor_t) return boolean is - begin - return actors(actor.id).inbox.first_envelope /= null; - end function has_messages; - - impure function has_messages (actor_vec : actor_vec_t) return boolean is - begin - for i in actor_vec'range loop - if has_messages(actor_vec(i)) then - return true; - end if; - end loop; - return false; - end function has_messages; - - procedure get_envelope( - actor : actor_t; - position : natural; - mailbox_id : mailbox_id_t; - variable mailbox : inout mailbox_ptr_t; - variable envelope : inout envelope_ptr_t; - variable previous_envelope : inout envelope_ptr_t) is - begin - mailbox := actors(actor.id).inbox when mailbox_id = inbox else actors(actor.id).outbox; - envelope := mailbox.first_envelope; - previous_envelope := null; - - for i in 1 to position loop - exit when envelope = null; - previous_envelope := envelope; - envelope := envelope.next_envelope; - end loop; - end; - - impure function get_payload ( - actor : actor_t; - position : natural := 0; - mailbox_id : mailbox_id_t := inbox) return string is - variable envelope, previous_envelope : envelope_ptr_t; - variable mailbox : mailbox_ptr_t; - begin - get_envelope(actor, position, mailbox_id, mailbox, envelope, previous_envelope); - if envelope /= null then - return envelope.message.payload.all; - else - return ""; - end if; - end; - - impure function get_sender ( - actor : actor_t; - position : natural := 0; - mailbox_id : mailbox_id_t := inbox) return actor_t is - variable envelope, previous_envelope : envelope_ptr_t; - variable mailbox : mailbox_ptr_t; - begin - get_envelope(actor, position, mailbox_id, mailbox, envelope, previous_envelope); - if envelope /= null then - return envelope.message.sender; - else - return null_actor; - end if; - end; - - impure function get_receiver ( - actor : actor_t; - position : natural := 0; - mailbox_id : mailbox_id_t := inbox) return actor_t is - variable envelope, previous_envelope : envelope_ptr_t; - variable mailbox : mailbox_ptr_t; - begin - get_envelope(actor, position, mailbox_id, mailbox, envelope, previous_envelope); - if envelope /= null then - return envelope.message.receiver; - else - return null_actor; - end if; - end; - - impure function get_id ( - actor : actor_t; - position : natural := 0; - mailbox_id : mailbox_id_t := inbox) return message_id_t is - variable envelope, previous_envelope : envelope_ptr_t; - variable mailbox : mailbox_ptr_t; - begin - get_envelope(actor, position, mailbox_id, mailbox, envelope, previous_envelope); - if envelope /= null then - return envelope.message.id; - else - return no_message_id; - end if; - end; - - impure function get_request_id ( - actor : actor_t; - position : natural := 0; - mailbox_id : mailbox_id_t := inbox) return message_id_t is - variable envelope, previous_envelope : envelope_ptr_t; - variable mailbox : mailbox_ptr_t; - begin - get_envelope(actor, position, mailbox_id, mailbox, envelope, previous_envelope); - if envelope /= null then - return envelope.message.request_id; - else - return no_message_id; - end if; - end; - - impure function get_all_but_payload ( - actor : actor_t; - position : natural := 0; - mailbox_id : mailbox_id_t := inbox) return msg_t is - variable envelope, previous_envelope : envelope_ptr_t; - variable mailbox : mailbox_ptr_t; - variable msg : msg_t; - begin - get_envelope(actor, position, mailbox_id, mailbox, envelope, previous_envelope); - if envelope /= null then - msg.id := envelope.message.id; - msg.msg_type := envelope.message.msg_type; - msg.status := envelope.message.status; - msg.sender := envelope.message.sender; - msg.receiver := envelope.message.receiver; - msg.request_id := envelope.message.request_id; - msg.data := null_queue; - else - msg := null_msg; - end if; - - return msg; - end; - - procedure delete_envelope ( - actor : actor_t; - position : natural := 0; - mailbox_id : mailbox_id_t := inbox) is - variable envelope, previous_envelope : envelope_ptr_t; - variable mailbox : mailbox_ptr_t; - variable msg : msg_t; - begin - get_envelope(actor, position, mailbox_id, mailbox, envelope, previous_envelope); - - if envelope /= null then - if is_visible(com_logger, trace) then - msg.id := envelope.message.id; - msg.msg_type := envelope.message.msg_type; - msg.sender := envelope.message.sender; - msg.receiver := envelope.message.receiver; - msg.request_id := envelope.message.request_id; - trace(com_logger, name(actor) & " " & mailbox_id_t'image(mailbox_id) & " => [" & to_string(msg) & "]"); - end if; - - deallocate(envelope.message.payload); - - if previous_envelope /= null then - previous_envelope.next_envelope := envelope.next_envelope; - else - mailbox.first_envelope := envelope.next_envelope; - end if; - - if envelope.next_envelope = null then - mailbox.last_envelope := previous_envelope; - end if; - - deallocate_envelope(envelope); - mailbox.num_of_messages := mailbox.num_of_messages - 1; - end if; - end; - - impure function has_reply_stash_message ( - actor : actor_t; - request_id : message_id_t := no_message_id) - return boolean is - begin - if request_id = no_message_id then - return actors(actor.id).reply_stash /= null; - elsif actors(actor.id).reply_stash /= null then - return actors(actor.id).reply_stash.message.request_id = request_id; - else - return false; - end if; - end function has_reply_stash_message; - - impure function get_reply_stash_message_payload (actor : actor_t) return string is - variable envelope : envelope_ptr_t := actors(actor.id).reply_stash; - begin - if envelope /= null then - return envelope.message.payload.all; - else - return ""; - end if; - end; - - impure function get_reply_stash_message_sender (actor : actor_t) return actor_t is - variable envelope : envelope_ptr_t := actors(actor.id).reply_stash; - begin - if envelope /= null then - return envelope.message.sender; - else - return null_actor; - end if; - end; - - impure function get_reply_stash_message_receiver (actor : actor_t) return actor_t is - variable envelope : envelope_ptr_t := actors(actor.id).reply_stash; - begin - if envelope /= null then - return envelope.message.receiver; - else - return null_actor; - end if; - end; - - impure function get_reply_stash_message_id (actor : actor_t) return message_id_t is - variable envelope : envelope_ptr_t := actors(actor.id).reply_stash; - begin - if envelope /= null then - return envelope.message.id; - else - return no_message_id; - end if; - end; - - impure function get_reply_stash_message_request_id (actor : actor_t) return message_id_t is - variable envelope : envelope_ptr_t := actors(actor.id).reply_stash; - begin - if envelope /= null then - return envelope.message.request_id; - else - return no_message_id; - end if; - end; - - procedure find_reply_message ( - actor : actor_t; - request_id : message_id_t; - mailbox_id : mailbox_id_t; - variable mailbox : inout mailbox_ptr_t; - variable envelope : inout envelope_ptr_t; - variable previous_envelope : out envelope_ptr_t; - variable position : out natural) is - begin - mailbox := actors(actor.id).inbox when mailbox_id = inbox else actors(actor.id).outbox; - envelope := mailbox.first_envelope; - previous_envelope := null; - position := 0; - - while envelope /= null loop - if envelope.message.request_id = request_id then - return; - end if; - previous_envelope := envelope; - envelope := envelope.next_envelope; - position := position + 1; - end loop; - end; - - impure function find_reply_message ( - actor : actor_t; - request_id : message_id_t; - mailbox_id : mailbox_id_t := inbox) - return integer is - variable envelope : envelope_ptr_t; - variable previous_envelope : envelope_ptr_t := null; - variable mailbox : mailbox_ptr_t; - variable position : natural; - begin - find_reply_message(actor, request_id, mailbox_id, mailbox, envelope, previous_envelope, position); - - if envelope /= null then - return position; - else - return -1; - end if; - end; - - impure function find_and_stash_reply_message ( - actor : actor_t; - request_id : message_id_t; - mailbox_id : mailbox_id_t := inbox) - return boolean is - variable envelope : envelope_ptr_t; - variable previous_envelope : envelope_ptr_t := null; - variable mailbox : mailbox_ptr_t; - variable position : natural; - begin - find_reply_message(actor, request_id, mailbox_id, mailbox, envelope, previous_envelope, position); - - if envelope /= null then - actors(actor.id).reply_stash := envelope; - - if previous_envelope /= null then - previous_envelope.next_envelope := envelope.next_envelope; - else - mailbox.first_envelope := envelope.next_envelope; - end if; - - if mailbox.first_envelope = null then - mailbox.last_envelope := null; - end if; - - mailbox.num_of_messages := mailbox.num_of_messages - 1; - - return true; - end if; - - return false; - end function find_and_stash_reply_message; - - procedure clear_reply_stash (actor : actor_t) is - begin - deallocate(actors(actor.id).reply_stash.message.payload); - deallocate(actors(actor.id).reply_stash); - end procedure clear_reply_stash; - - procedure subscribe ( - subscriber : actor_t; - publisher : actor_t; - traffic_type : subscription_traffic_type_t := published) is - variable new_subscriber : subscriber_item_ptr_t; - begin - check(not unknown_actor(subscriber), unknown_subscriber_error); - check(not unknown_actor(publisher), unknown_publisher_error); - check(not is_subscriber(subscriber, publisher, traffic_type), already_a_subscriber_error); - if traffic_type = published then - check(not is_subscriber(subscriber, publisher, outbound), already_a_subscriber_error); - elsif traffic_type = outbound then - check(not is_subscriber(subscriber, publisher, published), already_a_subscriber_error); - end if; - - if traffic_type = published then - new_subscriber := new subscriber_item_t'(subscriber, actors(publisher.id).subscribers(published)); - actors(publisher.id).subscribers(published) := new_subscriber; - elsif traffic_type = outbound then - new_subscriber := new subscriber_item_t'(subscriber, actors(publisher.id).subscribers(outbound)); - actors(publisher.id).subscribers(outbound) := new_subscriber; - else - new_subscriber := new subscriber_item_t'(subscriber, actors(publisher.id).subscribers(inbound)); - actors(publisher.id).subscribers(inbound) := new_subscriber; - end if; - end procedure subscribe; - - procedure unsubscribe ( - subscriber : actor_t; - publisher : actor_t; - traffic_type : subscription_traffic_type_t := published) is - begin - check(not unknown_actor(subscriber), unknown_subscriber_error); - check(not unknown_actor(publisher), unknown_publisher_error); - check(is_subscriber(subscriber, publisher, traffic_type), not_a_subscriber_error); - - remove_subscriber(subscriber, publisher, traffic_type); - end procedure unsubscribe; - - --------------------------------------------------------------------------- - -- Debugging - --------------------------------------------------------------------------- - impure function get_subscriptions(subscriber : actor_t) return subscription_vec_t is - impure function num_of_subscriptions return natural is - variable n_subscriptions : natural := 0; - variable item : subscriber_item_ptr_t; - begin - for a in actors'range loop - for t in subscription_traffic_type_t'left to subscription_traffic_type_t'right loop - item := actors(a).subscribers(t); - while item /= null loop - if item.actor = subscriber then - n_subscriptions := n_subscriptions + 1; - end if; - item := item.next_item; - end loop; - end loop; - end loop; - - return n_subscriptions; - end; - - constant n_subscriptions : natural := num_of_subscriptions; - variable subscriptions : subscription_vec_t(0 to n_subscriptions - 1); - variable item : subscriber_item_ptr_t; - variable idx : natural := 0; - begin - for a in actors'range loop - for t in subscription_traffic_type_t'left to subscription_traffic_type_t'right loop - item := actors(a).subscribers(t); - while item /= null loop - if item.actor = subscriber then - subscriptions(idx).subscriber := subscriber; - subscriptions(idx).publisher := actors(a).actor; - subscriptions(idx).traffic_type := t; - idx := idx + 1; - end if; - item := item.next_item; - end loop; - end loop; - end loop; - - return subscriptions; - end; - - impure function get_subscribers(publisher : actor_t) return subscription_vec_t is - impure function num_of_subscriptions return natural is - variable n_subscriptions : natural := 0; - variable item : subscriber_item_ptr_t; - begin - for t in subscription_traffic_type_t'left to subscription_traffic_type_t'right loop - item := actors(publisher.id).subscribers(t); - while item /= null loop - n_subscriptions := n_subscriptions + 1; - item := item.next_item; - end loop; - end loop; - - return n_subscriptions; - end; - constant n_subscriptions : natural := num_of_subscriptions; - variable subscriptions : subscription_vec_t(0 to n_subscriptions - 1); - variable item : subscriber_item_ptr_t; - variable idx : natural := 0; - begin - for t in subscription_traffic_type_t'left to subscription_traffic_type_t'right loop - item := actors(publisher.id).subscribers(t); - while item /= null loop - subscriptions(idx).subscriber := item.actor; - subscriptions(idx).publisher := publisher; - subscriptions(idx).traffic_type := t; - idx := idx + 1; - item := item.next_item; - end loop; - end loop; - - return subscriptions; - end; - - - ----------------------------------------------------------------------------- - -- Misc - ----------------------------------------------------------------------------- - procedure allow_timeout is - begin - timeout_allowed := true; - end procedure allow_timeout; - - impure function timeout_is_allowed return boolean is - begin - return timeout_allowed; - end function timeout_is_allowed; - - procedure allow_deprecated is - begin - deprecated_allowed := true; - end procedure allow_deprecated; - - procedure deprecated (msg : string) is - begin - check(deprecated_allowed, deprecated_interface_error, msg); - end; -end protected body; - -end package body com_messenger_pkg; +-- This file defines the com messenger which is responsible for housing the +-- messages in the system. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.com_types_pkg.all; +use work.com_support_pkg.all; +use work.queue_pkg.all; +use work.queue_pool_pkg.all; +use work.string_ptr_pkg.all; +use work.codec_pkg.all; +use work.logger_pkg.all; +use work.log_levels_pkg.all; + +use std.textio.all; + +package com_messenger_pkg is + type subscription_traffic_types_t is array (natural range <>) of subscription_traffic_type_t; + + type messenger_t is protected + ----------------------------------------------------------------------------- + -- Handling of actors + ----------------------------------------------------------------------------- + impure function create ( + name : string := ""; + inbox_size : positive := positive'high; + outbox_size : positive := positive'high + ) return actor_t; + impure function find (name : string; enable_deferred_creation : boolean := true) return actor_t; + impure function name (actor : actor_t) return string; + + procedure destroy (actor : inout actor_t); + procedure reset_messenger; + + impure function num_of_actors return natural; + impure function get_all_actors return actor_vec_t; + impure function is_deferred(actor : actor_t) return boolean; + impure function num_of_deferred_creations return natural; + impure function unknown_actor (actor : actor_t) return boolean; + impure function deferred (actor : actor_t) return boolean; + impure function is_full (actor : actor_t; mailbox_id : mailbox_id_t) return boolean; + impure function num_of_messages (actor : actor_t; mailbox_id : mailbox_id_t) return natural; + impure function mailbox_size (actor : actor_t; mailbox_id : mailbox_id_t) return natural; + procedure resize_mailbox (actor : actor_t; new_size : natural; mailbox_id : mailbox_id_t); + impure function subscriber_inbox_is_full ( + publisher : actor_t; + subscription_traffic_types : subscription_traffic_types_t) return boolean; + impure function has_subscribers ( + actor : actor_t; + subscription_traffic_type : subscription_traffic_type_t := published) return boolean; + + ----------------------------------------------------------------------------- + -- Send related subprograms + ----------------------------------------------------------------------------- + procedure send ( + constant sender : in actor_t; + constant receiver : in actor_t; + constant mailbox_id : in mailbox_id_t; + constant request_id : in message_id_t; + constant payload : in string; + variable receipt : out receipt_t); + procedure send ( + constant receiver : in actor_t; + constant mailbox_id : in mailbox_id_t; + variable msg : inout msg_t); + procedure publish (sender : actor_t; payload : string); + procedure publish ( + constant sender : in actor_t; + variable msg : inout msg_t; + constant subscriber_traffic_types : in subscription_traffic_types_t); + procedure internal_publish ( + constant sender : in actor_t; + variable msg : inout msg_t; + constant subscriber_traffic_types : in subscription_traffic_types_t); + + ----------------------------------------------------------------------------- + -- Receive related subprograms + ----------------------------------------------------------------------------- + impure function has_messages (actor : actor_t) return boolean; + impure function has_messages (actor_vec : actor_vec_t) return boolean; + impure function get_payload ( + actor : actor_t; + position : natural := 0; + mailbox_id : mailbox_id_t := inbox) return string; + impure function get_sender ( + actor : actor_t; + position : natural := 0; + mailbox_id : mailbox_id_t := inbox) return actor_t; + impure function get_receiver ( + actor : actor_t; + position : natural := 0; + mailbox_id : mailbox_id_t := inbox) return actor_t; + impure function get_id ( + actor : actor_t; + position : natural := 0; + mailbox_id : mailbox_id_t := inbox) return message_id_t; + impure function get_request_id ( + actor : actor_t; + position : natural := 0; + mailbox_id : mailbox_id_t := inbox) return message_id_t; + impure function get_all_but_payload ( + actor : actor_t; + position : natural := 0; + mailbox_id : mailbox_id_t := inbox) return msg_t; + + procedure delete_envelope ( + actor : actor_t; + position : natural := 0; + mailbox_id : mailbox_id_t := inbox); + + impure function has_reply_stash_message ( + actor : actor_t; + request_id : message_id_t := no_message_id) + return boolean; -- + impure function get_reply_stash_message_payload (actor : actor_t) return string; + impure function get_reply_stash_message_sender (actor : actor_t) return actor_t; + impure function get_reply_stash_message_receiver (actor : actor_t) return actor_t; + impure function get_reply_stash_message_id (actor : actor_t) return message_id_t; + impure function get_reply_stash_message_request_id (actor : actor_t) return message_id_t; + impure function find_reply_message ( + actor : actor_t; + request_id : message_id_t; + mailbox_id : mailbox_id_t := inbox) + return integer; + impure function find_and_stash_reply_message ( + actor : actor_t; + request_id : message_id_t; + mailbox_id : mailbox_id_t := inbox) + return boolean; + procedure clear_reply_stash (actor : actor_t); + + procedure subscribe ( + subscriber : actor_t; + publisher : actor_t; + traffic_type : subscription_traffic_type_t := published); + procedure unsubscribe ( + subscriber : actor_t; + publisher : actor_t; + traffic_type : subscription_traffic_type_t := published); + + --------------------------------------------------------------------------- + -- Debugging + --------------------------------------------------------------------------- + impure function to_string(msg : msg_t) return string; + impure function get_subscriptions(subscriber : actor_t) return subscription_vec_t; + impure function get_subscribers(publisher : actor_t) return subscription_vec_t; + + --------------------------------------------------------------------------- + -- Misc + --------------------------------------------------------------------------- + procedure allow_timeout; + impure function timeout_is_allowed return boolean; + procedure allow_deprecated; + procedure deprecated (msg : string); + + end protected; +end package com_messenger_pkg; + +package body com_messenger_pkg is + type envelope_t; + type envelope_ptr_t is access envelope_t; + + type envelope_t is record + message : message_t; + next_envelope : envelope_ptr_t; + end record envelope_t; + type envelope_ptr_array is array (positive range <>) of envelope_ptr_t; + + type mailbox_t is record + num_of_messages : natural; + size : natural; + first_envelope : envelope_ptr_t; + last_envelope : envelope_ptr_t; + end record mailbox_t; + type mailbox_ptr_t is access mailbox_t; + + impure function create(size : natural := natural'high) + return mailbox_ptr_t is + begin + return new mailbox_t'(0, size, null, null); + end function create; + + -- TODO: subscriber can be simplified to a pointer to an array. + type subscriber_item_t; + type subscriber_item_ptr_t is access subscriber_item_t; + + type subscriber_item_t is record + actor : actor_t; + next_item : subscriber_item_ptr_t; + end record subscriber_item_t; + + type subscribers_t is array (subscription_traffic_type_t range published to inbound) of subscriber_item_ptr_t; + + type actor_item_t is record + actor : actor_t; + name : line; + deferred_creation : boolean; + inbox : mailbox_ptr_t; + outbox : mailbox_ptr_t; + reply_stash : envelope_ptr_t; + subscribers : subscribers_t; + end record actor_item_t; + + type actor_item_array_t is array (natural range <>) of actor_item_t; + type actor_item_array_ptr_t is access actor_item_array_t; + + type messenger_t is protected + body + variable null_actor_item : actor_item_t := ( + actor => null_actor, + name => null, + deferred_creation => false, + inbox => create(0), + outbox => create(0), + reply_stash => null, + subscribers => (null, null, null)); -- + variable envelope_recycle_bin : envelope_ptr_array(1 to 1000); + variable n_recycled_envelopes : natural := 0; + variable null_message : message_t := (no_message_id, null_msg_type, ok, null_actor, + null_actor, no_message_id, null); + variable next_message_id : message_id_t := no_message_id + 1; + variable timeout_allowed : boolean := false; + variable deprecated_allowed : boolean := false; + + ----------------------------------------------------------------------------- + -- Handling of actors + ----------------------------------------------------------------------------- + impure function new_envelope return envelope_ptr_t is + begin + if n_recycled_envelopes > 0 then + n_recycled_envelopes := n_recycled_envelopes - 1; + envelope_recycle_bin(n_recycled_envelopes + 1).message := null_message; + envelope_recycle_bin(n_recycled_envelopes + 1).next_envelope := null; + return envelope_recycle_bin(n_recycled_envelopes + 1); + else + return new envelope_t; + end if; + end new_envelope; + + procedure deallocate_envelope (ptr : inout envelope_ptr_t) is + begin + if (n_recycled_envelopes < envelope_recycle_bin'length) and (ptr /= null) then + n_recycled_envelopes := n_recycled_envelopes + 1; + envelope_recycle_bin(n_recycled_envelopes) := ptr; + ptr := null; + else + deallocate(ptr); + end if; + end deallocate_envelope; + + impure function init_actors return actor_item_array_ptr_t is + variable ret_val : actor_item_array_ptr_t; + begin + ret_val := new actor_item_array_t(0 to 0); + ret_val(0) := null_actor_item; + + return ret_val; + end function init_actors; + + variable actors : actor_item_array_ptr_t := init_actors; + + impure function find_actor (name : string) return actor_t is + variable ret_val : actor_t; + begin + for i in actors'reverse_range loop + ret_val := actors(i).actor; + if actors(i).name /= null then + exit when actors(i).name.all = name; + end if; + end loop; + + return ret_val; + end; + + impure function create_actor ( + name : string := ""; + deferred_creation : in boolean := false; + inbox_size : in natural := natural'high; + outbox_size : in natural := natural'high) + return actor_t is + variable old_actors : actor_item_array_ptr_t; + begin + old_actors := actors; + actors := new actor_item_array_t(0 to actors'length); + actors(0) := null_actor_item; + for i in old_actors'range loop + actors(i) := old_actors(i); + end loop; + deallocate(old_actors); + actors(actors'length - 1) := ((id => actors'length - 1), new string'(name), + deferred_creation, create(inbox_size), create(outbox_size), null, (null, null, null)); + + return actors(actors'length - 1).actor; + end function; + + impure function find (name : string; enable_deferred_creation : boolean := true) return actor_t is + constant actor : actor_t := find_actor(name); + begin + if name = "" then + return null_actor; + elsif (actor = null_actor) and enable_deferred_creation then + return create_actor(name, true, 1); + else + return actor; + end if; + end; + + impure function name (actor : actor_t) return string is + begin + if actors(actor.id).name /= null then + return actors(actor.id).name.all; + else + return ""; + end if; + + end; + + + impure function create ( + name : string := ""; + inbox_size : positive := positive'high; + outbox_size : positive := positive'high + ) return actor_t is + variable actor : actor_t := find_actor(name); + begin + if (actor = null_actor) or (name = "") then + actor := create_actor(name, false, inbox_size, outbox_size); + elsif actors(actor.id).deferred_creation then + actors(actor.id).deferred_creation := false; + actors(actor.id).inbox.size := inbox_size; + actors(actor.id).outbox.size := outbox_size; + else + check_failed(duplicate_actor_name_error); + end if; + + return actor; + end; + + impure function is_subscriber ( + subscriber : actor_t; + publisher : actor_t; + traffic_type : subscription_traffic_type_t) return boolean is + variable item : subscriber_item_ptr_t := actors(publisher.id).subscribers(traffic_type); + begin + while item /= null loop + if item.actor = subscriber then + return true; + end if; + item := item.next_item; + end loop; + + return false; + end; + + procedure remove_subscriber (subscriber : actor_t; publisher : actor_t; traffic_type : subscription_traffic_type_t) is + variable item, previous_item : subscriber_item_ptr_t; + begin + item := actors(publisher.id).subscribers(traffic_type); + previous_item := null; + while item /= null loop + if item.actor = subscriber then + if previous_item = null then + actors(publisher.id).subscribers(traffic_type) := item.next_item; + else + previous_item.next_item := item.next_item; + end if; + deallocate(item); + return; + end if; + previous_item := item; + item := item.next_item; + end loop; + + check_failed(not_a_subscriber_error); + end; + + procedure destroy (actor : inout actor_t) is + variable envelope : envelope_ptr_t; + variable item : subscriber_item_ptr_t; + begin + check(not unknown_actor(actor), unknown_actor_error); + + while actors(actor.id).inbox.first_envelope /= null loop + envelope := actors(actor.id).inbox.first_envelope; + actors(actor.id).inbox.first_envelope := envelope.next_envelope; + deallocate(envelope.message.payload); + deallocate_envelope(envelope); + end loop; + + for t in subscription_traffic_type_t'left to subscription_traffic_type_t'right loop + while actors(actor.id).subscribers(t) /= null loop + item := actors(actor.id).subscribers(t); + actors(actor.id).subscribers(t) := item.next_item; + deallocate(item); + end loop; + end loop; + + for i in actors'range loop + for t in subscription_traffic_type_t'left to subscription_traffic_type_t'right loop + if is_subscriber(actor, actors(i).actor, t) then + remove_subscriber(actor, actors(i).actor, t); + end if; + end loop; + end loop; + + deallocate(actors(actor.id).name); + deallocate(actors(actor.id).inbox); + deallocate(actors(actor.id).outbox); + actors(actor.id) := null_actor_item; + actor := null_actor; + end; + + procedure reset_messenger is + begin + for i in actors'range loop + if actors(i).actor /= null_actor then + destroy(actors(i).actor); + end if; + end loop; + deallocate(actors); + actors := init_actors; + next_message_id := no_message_id + 1; + end; + + impure function num_of_actors return natural is + variable n_actors : natural := 0; + begin + for i in actors'range loop + if actors(i).actor /= null_actor then + n_actors := n_actors + 1; + end if; + end loop; + + return n_actors; + end; + + impure function get_all_actors return actor_vec_t is + constant n_actors : natural := num_of_actors; + variable result : actor_vec_t(0 to n_actors - 1); + variable idx : natural := 0; + begin + for i in actors'range loop + if actors(i).actor /= null_actor then + result(idx) := actors(i).actor; + idx := idx + 1; + end if; + end loop; + + return result; + end; + + + impure function is_deferred(actor : actor_t) return boolean is + begin + return actors(actor.id).deferred_creation; + end; + + impure function num_of_deferred_creations return natural is + variable n_deferred_actors : natural := 0; + begin + for i in actors'range loop + if actors(i).deferred_creation then + n_deferred_actors := n_deferred_actors + 1; + end if; + end loop; + + return n_deferred_actors; + end; + + impure function unknown_actor (actor : actor_t) return boolean is + begin + if (actor.id = 0) or (actor.id > actors'length - 1) then + return true; + elsif actors(actor.id).actor = null_actor then + return true; + end if; + + return false; + end function unknown_actor; + + impure function deferred (actor : actor_t) return boolean is + begin + return actors(actor.id).deferred_creation; + end function deferred; + + impure function is_full (actor : actor_t; mailbox_id : mailbox_id_t) return boolean is + begin + if mailbox_id = inbox then + return actors(actor.id).inbox.num_of_messages >= actors(actor.id).inbox.size; + else + return actors(actor.id).outbox.num_of_messages >= actors(actor.id).outbox.size; + end if; + end function; + + impure function num_of_messages (actor : actor_t; mailbox_id : mailbox_id_t) return natural is + begin + if mailbox_id = inbox then + return actors(actor.id).inbox.num_of_messages; + else + return actors(actor.id).outbox.num_of_messages; + end if; + end function; + + procedure resize_mailbox (actor : actor_t; new_size : natural; mailbox_id : mailbox_id_t) is + begin + if mailbox_id = inbox then + check(num_of_messages(actor, inbox) <= new_size, insufficient_size_error); + actors(actor.id).inbox.size := new_size; + else + check(num_of_messages(actor, outbox) <= new_size, insufficient_size_error); + actors(actor.id).outbox.size := new_size; + end if; + end; + + impure function subscriber_inbox_is_full ( + publisher : actor_t; + subscription_traffic_types : subscription_traffic_types_t) return boolean is + variable result : boolean_vector(subscription_traffic_types'range) := (others => false); + procedure has_full_inboxes ( + variable subscribers : in subscriber_item_ptr_t; + variable result : out boolean) is + variable item : subscriber_item_ptr_t := subscribers; + begin + result := false; + while item /= null loop + result := is_full(item.actor, inbox); + exit when result; + has_full_inboxes(actors(item.actor.id).subscribers(inbound), result); + exit when result; + item := item.next_item; + end loop; + end; + begin + for t in subscription_traffic_types'range loop + has_full_inboxes(actors(publisher.id).subscribers(subscription_traffic_types(t)), result(t)); + end loop; + + return or result; + end function; + + impure function has_subscribers ( + actor : actor_t; + subscription_traffic_type : subscription_traffic_type_t := published) return boolean is + begin + return actors(actor.id).subscribers(subscription_traffic_type) /= null; + end; + + impure function mailbox_size (actor : actor_t; mailbox_id : mailbox_id_t) return natural is + begin + if mailbox_id = inbox then + return actors(actor.id).inbox.size; + else + return actors(actor.id).outbox.size; + end if; + end function; + + ----------------------------------------------------------------------------- + -- Send related subprograms + ----------------------------------------------------------------------------- + procedure send ( + constant sender : in actor_t; + constant receiver : in actor_t; + constant mailbox_id : in mailbox_id_t; + constant request_id : in message_id_t; + constant payload : in string; + variable receipt : out receipt_t) is + variable envelope : envelope_ptr_t; + variable mailbox : mailbox_ptr_t; + begin + check(not is_full(receiver, mailbox_id), full_inbox_error); + + receipt.status := ok; + receipt.id := next_message_id; + envelope := new_envelope; + envelope.message.sender := sender; + envelope.message.receiver := receiver; + envelope.message.id := next_message_id; + envelope.message.request_id := request_id; + write(envelope.message.payload, payload); + next_message_id := next_message_id + 1; + + mailbox := actors(receiver.id).inbox when mailbox_id = inbox else actors(receiver.id).outbox; + mailbox.num_of_messages := mailbox.num_of_messages + 1; + if mailbox.last_envelope /= null then + mailbox.last_envelope.next_envelope := envelope; + else + mailbox.first_envelope := envelope; + end if; + mailbox.last_envelope := envelope; + end; + + procedure publish (sender : actor_t; payload : string) is + variable receipt : receipt_t; + variable subscriber_item : subscriber_item_ptr_t; + begin + check(not unknown_actor(sender), unknown_publisher_error); + + subscriber_item := actors(sender.id).subscribers(published); + while subscriber_item /= null loop + send(sender, subscriber_item.actor, inbox, no_message_id, payload, receipt); + subscriber_item := subscriber_item.next_item; + end loop; + end; + + impure function to_string(msg : msg_t) return string is + function id_to_string(id : message_id_t) return string is + begin + if id = no_message_id then + return "-"; + else + return to_string(id); + end if; + end function; + + impure function actor_to_string(actor : actor_t) return string is + begin + if actor = null_actor then + return "-"; + else + return name(actor); + end if; + end function; + + impure function msg_type_to_string (msg_type : msg_type_t) return string is + begin + if msg_type = null_msg_type then + return "-"; + else + return name(msg_type); + end if; + end; + + begin + return id_to_string(msg.id) & ":" & id_to_string(msg.request_id) & " " & + actor_to_string(msg.sender) & " -> " & actor_to_string(msg.receiver) & + " (" & msg_type_to_string(msg.msg_type) & ")"; + end; + + procedure put_message ( + receiver : actor_t; + msg : msg_t; + mailbox_id : mailbox_id_t; + copy_msg : boolean) is + variable envelope : envelope_ptr_t; + variable data : msg_data_t := msg.data; + variable mailbox : mailbox_ptr_t; + + begin + if copy_msg then + data := new_queue(queue_pool); + for i in 0 to length(msg.data) - 1 loop + unsafe_push(data, get(msg.data.data, 1+i)); + end loop; + end if; + + if is_visible(com_logger, trace) then + trace(com_logger, "[" & to_string(msg) & "] => " & name(receiver) & " " & mailbox_id_t'image(mailbox_id)); + end if; + + envelope := new_envelope; + envelope.message.id := msg.id; + envelope.message.msg_type := msg.msg_type; + envelope.message.sender := msg.sender; + envelope.message.receiver := msg.receiver; + envelope.message.request_id := msg.request_id; + write(envelope.message.payload, encode(data)); + + mailbox := actors(receiver.id).inbox when mailbox_id = inbox else actors(receiver.id).outbox; + mailbox.num_of_messages := mailbox.num_of_messages + 1; + if mailbox.last_envelope /= null then + mailbox.last_envelope.next_envelope := envelope; + else + mailbox.first_envelope := envelope; + end if; + mailbox.last_envelope := envelope; + end procedure; + + procedure put_subscriber_messages ( + variable subscribers : inout subscriber_item_ptr_t; + variable msg : inout msg_t; + constant set_msg_receiver : in boolean) is + variable subscriber_item : subscriber_item_ptr_t := subscribers; + begin + while subscriber_item /= null loop + if set_msg_receiver then + msg.receiver := subscriber_item.actor; + end if; + put_message(subscriber_item.actor, msg, inbox, true); + internal_publish(subscriber_item.actor, msg, (0 => inbound)); + subscriber_item := subscriber_item.next_item; + end loop; + end; + + procedure publish ( + constant sender : in actor_t; + variable msg : inout msg_t; + constant subscriber_traffic_types : in subscription_traffic_types_t) is + begin + check(not unknown_actor(sender), unknown_publisher_error); + check(msg.data /= null_queue, null_message_error); + + msg.id := next_message_id; + next_message_id := next_message_id + 1; + msg.status := ok; + msg.sender := sender; + + for t in subscriber_traffic_types'range loop + put_subscriber_messages(actors(sender.id).subscribers(subscriber_traffic_types(t)), + msg, set_msg_receiver => true); + end loop; + + msg.receiver := null_actor; + end; + + procedure internal_publish ( + constant sender : in actor_t; + variable msg : inout msg_t; + constant subscriber_traffic_types : in subscription_traffic_types_t) is + begin + for t in subscriber_traffic_types'range loop + put_subscriber_messages(actors(sender.id).subscribers(subscriber_traffic_types(t)), + msg, set_msg_receiver => false); + end loop; + end; + + procedure send ( + constant receiver : in actor_t; + constant mailbox_id : in mailbox_id_t; + variable msg : inout msg_t) is + begin + msg.id := next_message_id; + next_message_id := next_message_id + 1; + msg.status := ok; + if mailbox_id = inbox then + msg.receiver := receiver; + else + msg.sender := receiver; + msg.receiver := null_actor; + end if; + + + put_message(receiver, msg, mailbox_id, false); + end; + + ----------------------------------------------------------------------------- + -- Receive related subprograms + ----------------------------------------------------------------------------- + impure function has_messages (actor : actor_t) return boolean is + begin + return actors(actor.id).inbox.first_envelope /= null; + end function has_messages; + + impure function has_messages (actor_vec : actor_vec_t) return boolean is + begin + for i in actor_vec'range loop + if has_messages(actor_vec(i)) then + return true; + end if; + end loop; + return false; + end function has_messages; + + procedure get_envelope( + actor : actor_t; + position : natural; + mailbox_id : mailbox_id_t; + variable mailbox : inout mailbox_ptr_t; + variable envelope : inout envelope_ptr_t; + variable previous_envelope : inout envelope_ptr_t) is + begin + mailbox := actors(actor.id).inbox when mailbox_id = inbox else actors(actor.id).outbox; + envelope := mailbox.first_envelope; + previous_envelope := null; + + for i in 1 to position loop + exit when envelope = null; + previous_envelope := envelope; + envelope := envelope.next_envelope; + end loop; + end; + + impure function get_payload ( + actor : actor_t; + position : natural := 0; + mailbox_id : mailbox_id_t := inbox) return string is + variable envelope, previous_envelope : envelope_ptr_t; + variable mailbox : mailbox_ptr_t; + begin + get_envelope(actor, position, mailbox_id, mailbox, envelope, previous_envelope); + if envelope /= null then + return envelope.message.payload.all; + else + return ""; + end if; + end; + + impure function get_sender ( + actor : actor_t; + position : natural := 0; + mailbox_id : mailbox_id_t := inbox) return actor_t is + variable envelope, previous_envelope : envelope_ptr_t; + variable mailbox : mailbox_ptr_t; + begin + get_envelope(actor, position, mailbox_id, mailbox, envelope, previous_envelope); + if envelope /= null then + return envelope.message.sender; + else + return null_actor; + end if; + end; + + impure function get_receiver ( + actor : actor_t; + position : natural := 0; + mailbox_id : mailbox_id_t := inbox) return actor_t is + variable envelope, previous_envelope : envelope_ptr_t; + variable mailbox : mailbox_ptr_t; + begin + get_envelope(actor, position, mailbox_id, mailbox, envelope, previous_envelope); + if envelope /= null then + return envelope.message.receiver; + else + return null_actor; + end if; + end; + + impure function get_id ( + actor : actor_t; + position : natural := 0; + mailbox_id : mailbox_id_t := inbox) return message_id_t is + variable envelope, previous_envelope : envelope_ptr_t; + variable mailbox : mailbox_ptr_t; + begin + get_envelope(actor, position, mailbox_id, mailbox, envelope, previous_envelope); + if envelope /= null then + return envelope.message.id; + else + return no_message_id; + end if; + end; + + impure function get_request_id ( + actor : actor_t; + position : natural := 0; + mailbox_id : mailbox_id_t := inbox) return message_id_t is + variable envelope, previous_envelope : envelope_ptr_t; + variable mailbox : mailbox_ptr_t; + begin + get_envelope(actor, position, mailbox_id, mailbox, envelope, previous_envelope); + if envelope /= null then + return envelope.message.request_id; + else + return no_message_id; + end if; + end; + + impure function get_all_but_payload ( + actor : actor_t; + position : natural := 0; + mailbox_id : mailbox_id_t := inbox) return msg_t is + variable envelope, previous_envelope : envelope_ptr_t; + variable mailbox : mailbox_ptr_t; + variable msg : msg_t; + begin + get_envelope(actor, position, mailbox_id, mailbox, envelope, previous_envelope); + if envelope /= null then + msg.id := envelope.message.id; + msg.msg_type := envelope.message.msg_type; + msg.status := envelope.message.status; + msg.sender := envelope.message.sender; + msg.receiver := envelope.message.receiver; + msg.request_id := envelope.message.request_id; + msg.data := null_queue; + else + msg := null_msg; + end if; + + return msg; + end; + + procedure delete_envelope ( + actor : actor_t; + position : natural := 0; + mailbox_id : mailbox_id_t := inbox) is + variable envelope, previous_envelope : envelope_ptr_t; + variable mailbox : mailbox_ptr_t; + variable msg : msg_t; + begin + get_envelope(actor, position, mailbox_id, mailbox, envelope, previous_envelope); + + if envelope /= null then + if is_visible(com_logger, trace) then + msg.id := envelope.message.id; + msg.msg_type := envelope.message.msg_type; + msg.sender := envelope.message.sender; + msg.receiver := envelope.message.receiver; + msg.request_id := envelope.message.request_id; + trace(com_logger, name(actor) & " " & mailbox_id_t'image(mailbox_id) & " => [" & to_string(msg) & "]"); + end if; + + deallocate(envelope.message.payload); + + if previous_envelope /= null then + previous_envelope.next_envelope := envelope.next_envelope; + else + mailbox.first_envelope := envelope.next_envelope; + end if; + + if envelope.next_envelope = null then + mailbox.last_envelope := previous_envelope; + end if; + + deallocate_envelope(envelope); + mailbox.num_of_messages := mailbox.num_of_messages - 1; + end if; + end; + + impure function has_reply_stash_message ( + actor : actor_t; + request_id : message_id_t := no_message_id) + return boolean is + begin + if request_id = no_message_id then + return actors(actor.id).reply_stash /= null; + elsif actors(actor.id).reply_stash /= null then + return actors(actor.id).reply_stash.message.request_id = request_id; + else + return false; + end if; + end function has_reply_stash_message; + + impure function get_reply_stash_message_payload (actor : actor_t) return string is + variable envelope : envelope_ptr_t := actors(actor.id).reply_stash; + begin + if envelope /= null then + return envelope.message.payload.all; + else + return ""; + end if; + end; + + impure function get_reply_stash_message_sender (actor : actor_t) return actor_t is + variable envelope : envelope_ptr_t := actors(actor.id).reply_stash; + begin + if envelope /= null then + return envelope.message.sender; + else + return null_actor; + end if; + end; + + impure function get_reply_stash_message_receiver (actor : actor_t) return actor_t is + variable envelope : envelope_ptr_t := actors(actor.id).reply_stash; + begin + if envelope /= null then + return envelope.message.receiver; + else + return null_actor; + end if; + end; + + impure function get_reply_stash_message_id (actor : actor_t) return message_id_t is + variable envelope : envelope_ptr_t := actors(actor.id).reply_stash; + begin + if envelope /= null then + return envelope.message.id; + else + return no_message_id; + end if; + end; + + impure function get_reply_stash_message_request_id (actor : actor_t) return message_id_t is + variable envelope : envelope_ptr_t := actors(actor.id).reply_stash; + begin + if envelope /= null then + return envelope.message.request_id; + else + return no_message_id; + end if; + end; + + procedure find_reply_message ( + actor : actor_t; + request_id : message_id_t; + mailbox_id : mailbox_id_t; + variable mailbox : inout mailbox_ptr_t; + variable envelope : inout envelope_ptr_t; + variable previous_envelope : out envelope_ptr_t; + variable position : out natural) is + begin + mailbox := actors(actor.id).inbox when mailbox_id = inbox else actors(actor.id).outbox; + envelope := mailbox.first_envelope; + previous_envelope := null; + position := 0; + + while envelope /= null loop + if envelope.message.request_id = request_id then + return; + end if; + previous_envelope := envelope; + envelope := envelope.next_envelope; + position := position + 1; + end loop; + end; + + impure function find_reply_message ( + actor : actor_t; + request_id : message_id_t; + mailbox_id : mailbox_id_t := inbox) + return integer is + variable envelope : envelope_ptr_t; + variable previous_envelope : envelope_ptr_t := null; + variable mailbox : mailbox_ptr_t; + variable position : natural; + begin + find_reply_message(actor, request_id, mailbox_id, mailbox, envelope, previous_envelope, position); + + if envelope /= null then + return position; + else + return -1; + end if; + end; + + impure function find_and_stash_reply_message ( + actor : actor_t; + request_id : message_id_t; + mailbox_id : mailbox_id_t := inbox) + return boolean is + variable envelope : envelope_ptr_t; + variable previous_envelope : envelope_ptr_t := null; + variable mailbox : mailbox_ptr_t; + variable position : natural; + begin + find_reply_message(actor, request_id, mailbox_id, mailbox, envelope, previous_envelope, position); + + if envelope /= null then + actors(actor.id).reply_stash := envelope; + + if previous_envelope /= null then + previous_envelope.next_envelope := envelope.next_envelope; + else + mailbox.first_envelope := envelope.next_envelope; + end if; + + if mailbox.first_envelope = null then + mailbox.last_envelope := null; + end if; + + mailbox.num_of_messages := mailbox.num_of_messages - 1; + + return true; + end if; + + return false; + end function find_and_stash_reply_message; + + procedure clear_reply_stash (actor : actor_t) is + begin + deallocate(actors(actor.id).reply_stash.message.payload); + deallocate(actors(actor.id).reply_stash); + end procedure clear_reply_stash; + + procedure subscribe ( + subscriber : actor_t; + publisher : actor_t; + traffic_type : subscription_traffic_type_t := published) is + variable new_subscriber : subscriber_item_ptr_t; + begin + check(not unknown_actor(subscriber), unknown_subscriber_error); + check(not unknown_actor(publisher), unknown_publisher_error); + check(not is_subscriber(subscriber, publisher, traffic_type), already_a_subscriber_error); + if traffic_type = published then + check(not is_subscriber(subscriber, publisher, outbound), already_a_subscriber_error); + elsif traffic_type = outbound then + check(not is_subscriber(subscriber, publisher, published), already_a_subscriber_error); + end if; + + if traffic_type = published then + new_subscriber := new subscriber_item_t'(subscriber, actors(publisher.id).subscribers(published)); + actors(publisher.id).subscribers(published) := new_subscriber; + elsif traffic_type = outbound then + new_subscriber := new subscriber_item_t'(subscriber, actors(publisher.id).subscribers(outbound)); + actors(publisher.id).subscribers(outbound) := new_subscriber; + else + new_subscriber := new subscriber_item_t'(subscriber, actors(publisher.id).subscribers(inbound)); + actors(publisher.id).subscribers(inbound) := new_subscriber; + end if; + end procedure subscribe; + + procedure unsubscribe ( + subscriber : actor_t; + publisher : actor_t; + traffic_type : subscription_traffic_type_t := published) is + begin + check(not unknown_actor(subscriber), unknown_subscriber_error); + check(not unknown_actor(publisher), unknown_publisher_error); + check(is_subscriber(subscriber, publisher, traffic_type), not_a_subscriber_error); + + remove_subscriber(subscriber, publisher, traffic_type); + end procedure unsubscribe; + + --------------------------------------------------------------------------- + -- Debugging + --------------------------------------------------------------------------- + impure function get_subscriptions(subscriber : actor_t) return subscription_vec_t is + impure function num_of_subscriptions return natural is + variable n_subscriptions : natural := 0; + variable item : subscriber_item_ptr_t; + begin + for a in actors'range loop + for t in subscription_traffic_type_t'left to subscription_traffic_type_t'right loop + item := actors(a).subscribers(t); + while item /= null loop + if item.actor = subscriber then + n_subscriptions := n_subscriptions + 1; + end if; + item := item.next_item; + end loop; + end loop; + end loop; + + return n_subscriptions; + end; + + constant n_subscriptions : natural := num_of_subscriptions; + variable subscriptions : subscription_vec_t(0 to n_subscriptions - 1); + variable item : subscriber_item_ptr_t; + variable idx : natural := 0; + begin + for a in actors'range loop + for t in subscription_traffic_type_t'left to subscription_traffic_type_t'right loop + item := actors(a).subscribers(t); + while item /= null loop + if item.actor = subscriber then + subscriptions(idx).subscriber := subscriber; + subscriptions(idx).publisher := actors(a).actor; + subscriptions(idx).traffic_type := t; + idx := idx + 1; + end if; + item := item.next_item; + end loop; + end loop; + end loop; + + return subscriptions; + end; + + impure function get_subscribers(publisher : actor_t) return subscription_vec_t is + impure function num_of_subscriptions return natural is + variable n_subscriptions : natural := 0; + variable item : subscriber_item_ptr_t; + begin + for t in subscription_traffic_type_t'left to subscription_traffic_type_t'right loop + item := actors(publisher.id).subscribers(t); + while item /= null loop + n_subscriptions := n_subscriptions + 1; + item := item.next_item; + end loop; + end loop; + + return n_subscriptions; + end; + constant n_subscriptions : natural := num_of_subscriptions; + variable subscriptions : subscription_vec_t(0 to n_subscriptions - 1); + variable item : subscriber_item_ptr_t; + variable idx : natural := 0; + begin + for t in subscription_traffic_type_t'left to subscription_traffic_type_t'right loop + item := actors(publisher.id).subscribers(t); + while item /= null loop + subscriptions(idx).subscriber := item.actor; + subscriptions(idx).publisher := publisher; + subscriptions(idx).traffic_type := t; + idx := idx + 1; + item := item.next_item; + end loop; + end loop; + + return subscriptions; + end; + + + ----------------------------------------------------------------------------- + -- Misc + ----------------------------------------------------------------------------- + procedure allow_timeout is + begin + timeout_allowed := true; + end procedure allow_timeout; + + impure function timeout_is_allowed return boolean is + begin + return timeout_allowed; + end function timeout_is_allowed; + + procedure allow_deprecated is + begin + deprecated_allowed := true; + end procedure allow_deprecated; + + procedure deprecated (msg : string) is + begin + check(deprecated_allowed, deprecated_interface_error, msg); + end; +end protected body; + +end package body com_messenger_pkg; diff --git a/vunit/vhdl/com/src/com_string.vhd b/vunit/vhdl/com/src/com_string.vhd index 0c6ba291e..9c53821c7 100644 --- a/vunit/vhdl/com/src/com_string.vhd +++ b/vunit/vhdl/com/src/com_string.vhd @@ -1,307 +1,307 @@ --- The com string package provides to_string functions for data types supported --- by the com codec package that don't have a standard to_string function defined. --- These functions are also used as the encode function when the debug codecs --- are used. In some cases the standard to_string function isn't used by the --- debug encoder. For example, arrays like std_ulogic_vector are encoded along with --- their ranges such that this property is unchanged by the transfer. In these --- cases there is a to_detailed_string available in this package. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com -library ieee; -use ieee.std_logic_1164.all; -use ieee.math_complex.all; -use ieee.numeric_bit.all; -use ieee.numeric_std.all; -use ieee.fixed_pkg.all; -use ieee.float_pkg.all; - -use std.textio.all; - -use work.com_debug_codec_builder_pkg.all; -use work.queue_pkg.all; -use work.integer_vector_ptr_pkg.all; -use work.string_ptr_pkg.all; - -package com_string_pkg is - function to_detailed_string ( - constant data : real) - return string; - function to_detailed_string ( - constant data : string) - return string; - alias to_string is to_detailed_string[string return string]; - function to_detailed_string ( - constant data : boolean_vector) - return string; - alias to_string is to_detailed_string[boolean_vector return string]; - function to_detailed_string ( - constant data : bit_vector) - return string; - function to_detailed_string ( - constant data : integer_vector) - return string; - alias to_string is to_detailed_string[integer_vector return string]; - function to_detailed_string ( - constant data : real_vector) - return string; - alias to_string is to_detailed_string[real_vector return string]; - function to_detailed_string ( - constant data : time_vector) - return string; - alias to_string is to_detailed_string[time_vector return string]; - function to_detailed_string ( - constant data : std_ulogic_vector) - return string; - function to_string ( - constant data : complex) - return string; - function to_string ( - constant data : complex_polar) - return string; - function to_string ( - constant data : integer_vector_ptr_t) - return string; - function to_string ( - constant data : queue_t) - return string; - function to_detailed_string ( - constant data : ieee.numeric_bit.unsigned) - return string; - function to_detailed_string ( - constant data : ieee.numeric_bit.signed) - return string; - function to_detailed_string ( - constant data : ieee.numeric_std.unsigned) - return string; - function to_detailed_string ( - constant data : ieee.numeric_std.signed) - return string; - function to_detailed_string ( - constant data : ufixed) - return string; - function to_detailed_string ( - constant data : sfixed) - return string; - function to_detailed_string ( - constant data : float) - return string; -end package com_string_pkg; - -package body com_string_pkg is - function to_detailed_string ( - constant data : real) - return string is - constant f64 : float64 := (others => '0'); - begin - return to_string(to_float(data, f64)); - end; - - function to_detailed_string ( - constant data : string) - return string is - begin - -- Modelsim sets data'right to 0 which is out of the positive index range used by - -- strings. This becomes a problem in the decoder which tries to maintain range - if (data'left = 1) and (data'right = 0) then - return create_array_group("", "2", "1", true); - else - return create_array_group(escape_special_characters(data), to_string(data'left), to_string(data'right), data'ascending); - end if; - end; - - function to_detailed_string ( - constant data : boolean_vector) - return string is - variable element : string(1 to 2 + data'length * 6); - variable l : line; - variable length : natural; - begin - open_group(l); - for i in data'range loop - append_group(l, to_string(data(i))); - end loop; - close_group(l, element, length); - - return create_array_group(element(1 to length), to_string(data'left), to_string(data'right), data'ascending); - end; - - function to_detailed_string ( - constant data : bit_vector) - return string is - begin - if (data'left = 0) and (data'right = -1) then - return create_array_group(to_string(data), "1", "0", true); - else - return create_array_group(to_string(data), to_string(data'left), to_string(data'right), data'ascending); - end if; - end; - - function to_detailed_string ( - constant data : integer_vector) - return string is - variable element : string(1 to 2 + data'length * 12); - variable l : line; - variable length : natural; - begin - open_group(l); - for i in data'range loop - append_group(l, to_string(data(i))); - end loop; - close_group(l, element, length); - - return create_array_group(element(1 to length), to_string(data'left), to_string(data'right), data'ascending); - end; - - function to_detailed_string ( - constant data : real_vector) - return string is - variable element : string(1 to 2 + data'length * 67); - variable l : line; - variable length : natural; - begin - open_group(l); - for i in data'range loop - append_group(l, to_detailed_string(data(i))); - end loop; - close_group(l, element, length); - - return create_array_group(element(1 to length), to_string(data'left), to_string(data'right), data'ascending); - end; - - function to_detailed_string ( - constant data : time_vector) - return string is - variable element : string(1 to 2 + data'length * 67); - variable l : line; - variable length : natural; - begin - open_group(l); - for i in data'range loop - append_group(l, to_string(data(i))); - end loop; - close_group(l, element, length); - - return create_array_group(element(1 to length), to_string(data'left), to_string(data'right), data'ascending); - end; - - function to_detailed_string ( - constant data : std_ulogic_vector) - return string is - begin - if (data'left = 0) and (data'right = -1) then - return create_array_group(to_string(data), "1", "0", true); - else - return create_array_group(to_string(data), to_string(data'left), to_string(data'right), data'ascending); - end if; - end; - - function to_string ( - constant data : complex) - return string is - begin - return create_group(2, to_detailed_string(data.re), to_detailed_string(data.im)); - end; - - function to_string ( - constant data : complex_polar) - return string is - begin - return create_group(2, to_detailed_string(data.mag), to_detailed_string(data.arg)); - end; - - function to_string ( - constant data : integer_vector_ptr_t) - return string is - begin - return create_group(1, to_string(data.ref)); - end; - - function to_string ( - constant data : queue_t) - return string is - begin - return create_group(2, to_string(data.p_meta), to_string(to_integer(data.data))); - end; - - function to_detailed_string ( - constant data : ieee.numeric_bit.unsigned) - return string is - begin - if (data'left = 0) and (data'right = -1) then - return create_array_group(to_string(data), "1", "0", true); - else - return create_array_group(to_string(data), to_string(data'left), to_string(data'right), data'ascending); - end if; - end; - - function to_detailed_string ( - constant data : ieee.numeric_bit.signed) - return string is - begin - if (data'left = 0) and (data'right = -1) then - return create_array_group(to_string(data), "1", "0", true); - else - return create_array_group(to_string(data), to_string(data'left), to_string(data'right), data'ascending); - end if; - end; - - function to_detailed_string ( - constant data : ieee.numeric_std.unsigned) - return string is - begin - if (data'left = 0) and (data'right = -1) then - return create_array_group(to_string(data), "1", "0", true); - else - return create_array_group(to_string(data), to_string(data'left), to_string(data'right), data'ascending); - end if; - end; - - function to_detailed_string ( - constant data : ieee.numeric_std.signed) - return string is - begin - if (data'left = 0) and (data'right = -1) then - return create_array_group(to_string(data), "1", "0", true); - else - return create_array_group(to_string(data), to_string(data'left), to_string(data'right), data'ascending); - end if; - end; - - function to_detailed_string ( - constant data : ufixed) - return string is - variable unsigned_data : ieee.numeric_std.unsigned(data'length - 1 downto 0); - begin - for i in unsigned_data'range loop - unsigned_data(i) := data(i + data'low); - end loop; - return create_array_group(to_string(unsigned_data), to_string(data'left), to_string(data'right), false); - end; - - function to_detailed_string ( - constant data : sfixed) - return string is - variable unsigned_data : ieee.numeric_std.unsigned(data'length - 1 downto 0); - begin - for i in unsigned_data'range loop - unsigned_data(i) := data(i + data'low); - end loop; - return create_array_group(to_string(unsigned_data), to_string(data'left), to_string(data'right), false); - end; - - function to_detailed_string ( - constant data : float) - return string is - variable unsigned_data : ieee.numeric_std.unsigned(data'length - 1 downto 0); - begin - for i in unsigned_data'range loop - unsigned_data(i) := data(i + data'low); - end loop; - return create_array_group(to_string(unsigned_data), to_string(data'left), to_string(data'right), false); - end; - -end package body com_string_pkg; +-- The com string package provides to_string functions for data types supported +-- by the com codec package that don't have a standard to_string function defined. +-- These functions are also used as the encode function when the debug codecs +-- are used. In some cases the standard to_string function isn't used by the +-- debug encoder. For example, arrays like std_ulogic_vector are encoded along with +-- their ranges such that this property is unchanged by the transfer. In these +-- cases there is a to_detailed_string available in this package. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +library ieee; +use ieee.std_logic_1164.all; +use ieee.math_complex.all; +use ieee.numeric_bit.all; +use ieee.numeric_std.all; +use ieee.fixed_pkg.all; +use ieee.float_pkg.all; + +use std.textio.all; + +use work.com_debug_codec_builder_pkg.all; +use work.queue_pkg.all; +use work.integer_vector_ptr_pkg.all; +use work.string_ptr_pkg.all; + +package com_string_pkg is + function to_detailed_string ( + constant data : real) + return string; + function to_detailed_string ( + constant data : string) + return string; + alias to_string is to_detailed_string[string return string]; + function to_detailed_string ( + constant data : boolean_vector) + return string; + alias to_string is to_detailed_string[boolean_vector return string]; + function to_detailed_string ( + constant data : bit_vector) + return string; + function to_detailed_string ( + constant data : integer_vector) + return string; + alias to_string is to_detailed_string[integer_vector return string]; + function to_detailed_string ( + constant data : real_vector) + return string; + alias to_string is to_detailed_string[real_vector return string]; + function to_detailed_string ( + constant data : time_vector) + return string; + alias to_string is to_detailed_string[time_vector return string]; + function to_detailed_string ( + constant data : std_ulogic_vector) + return string; + function to_string ( + constant data : complex) + return string; + function to_string ( + constant data : complex_polar) + return string; + function to_string ( + constant data : integer_vector_ptr_t) + return string; + function to_string ( + constant data : queue_t) + return string; + function to_detailed_string ( + constant data : ieee.numeric_bit.unsigned) + return string; + function to_detailed_string ( + constant data : ieee.numeric_bit.signed) + return string; + function to_detailed_string ( + constant data : ieee.numeric_std.unsigned) + return string; + function to_detailed_string ( + constant data : ieee.numeric_std.signed) + return string; + function to_detailed_string ( + constant data : ufixed) + return string; + function to_detailed_string ( + constant data : sfixed) + return string; + function to_detailed_string ( + constant data : float) + return string; +end package com_string_pkg; + +package body com_string_pkg is + function to_detailed_string ( + constant data : real) + return string is + constant f64 : float64 := (others => '0'); + begin + return to_string(to_float(data, f64)); + end; + + function to_detailed_string ( + constant data : string) + return string is + begin + -- Modelsim sets data'right to 0 which is out of the positive index range used by + -- strings. This becomes a problem in the decoder which tries to maintain range + if (data'left = 1) and (data'right = 0) then + return create_array_group("", "2", "1", true); + else + return create_array_group(escape_special_characters(data), to_string(data'left), to_string(data'right), data'ascending); + end if; + end; + + function to_detailed_string ( + constant data : boolean_vector) + return string is + variable element : string(1 to 2 + data'length * 6); + variable l : line; + variable length : natural; + begin + open_group(l); + for i in data'range loop + append_group(l, to_string(data(i))); + end loop; + close_group(l, element, length); + + return create_array_group(element(1 to length), to_string(data'left), to_string(data'right), data'ascending); + end; + + function to_detailed_string ( + constant data : bit_vector) + return string is + begin + if (data'left = 0) and (data'right = -1) then + return create_array_group(to_string(data), "1", "0", true); + else + return create_array_group(to_string(data), to_string(data'left), to_string(data'right), data'ascending); + end if; + end; + + function to_detailed_string ( + constant data : integer_vector) + return string is + variable element : string(1 to 2 + data'length * 12); + variable l : line; + variable length : natural; + begin + open_group(l); + for i in data'range loop + append_group(l, to_string(data(i))); + end loop; + close_group(l, element, length); + + return create_array_group(element(1 to length), to_string(data'left), to_string(data'right), data'ascending); + end; + + function to_detailed_string ( + constant data : real_vector) + return string is + variable element : string(1 to 2 + data'length * 67); + variable l : line; + variable length : natural; + begin + open_group(l); + for i in data'range loop + append_group(l, to_detailed_string(data(i))); + end loop; + close_group(l, element, length); + + return create_array_group(element(1 to length), to_string(data'left), to_string(data'right), data'ascending); + end; + + function to_detailed_string ( + constant data : time_vector) + return string is + variable element : string(1 to 2 + data'length * 67); + variable l : line; + variable length : natural; + begin + open_group(l); + for i in data'range loop + append_group(l, to_string(data(i))); + end loop; + close_group(l, element, length); + + return create_array_group(element(1 to length), to_string(data'left), to_string(data'right), data'ascending); + end; + + function to_detailed_string ( + constant data : std_ulogic_vector) + return string is + begin + if (data'left = 0) and (data'right = -1) then + return create_array_group(to_string(data), "1", "0", true); + else + return create_array_group(to_string(data), to_string(data'left), to_string(data'right), data'ascending); + end if; + end; + + function to_string ( + constant data : complex) + return string is + begin + return create_group(2, to_detailed_string(data.re), to_detailed_string(data.im)); + end; + + function to_string ( + constant data : complex_polar) + return string is + begin + return create_group(2, to_detailed_string(data.mag), to_detailed_string(data.arg)); + end; + + function to_string ( + constant data : integer_vector_ptr_t) + return string is + begin + return create_group(1, to_string(data.ref)); + end; + + function to_string ( + constant data : queue_t) + return string is + begin + return create_group(2, to_string(data.p_meta), to_string(to_integer(data.data))); + end; + + function to_detailed_string ( + constant data : ieee.numeric_bit.unsigned) + return string is + begin + if (data'left = 0) and (data'right = -1) then + return create_array_group(to_string(data), "1", "0", true); + else + return create_array_group(to_string(data), to_string(data'left), to_string(data'right), data'ascending); + end if; + end; + + function to_detailed_string ( + constant data : ieee.numeric_bit.signed) + return string is + begin + if (data'left = 0) and (data'right = -1) then + return create_array_group(to_string(data), "1", "0", true); + else + return create_array_group(to_string(data), to_string(data'left), to_string(data'right), data'ascending); + end if; + end; + + function to_detailed_string ( + constant data : ieee.numeric_std.unsigned) + return string is + begin + if (data'left = 0) and (data'right = -1) then + return create_array_group(to_string(data), "1", "0", true); + else + return create_array_group(to_string(data), to_string(data'left), to_string(data'right), data'ascending); + end if; + end; + + function to_detailed_string ( + constant data : ieee.numeric_std.signed) + return string is + begin + if (data'left = 0) and (data'right = -1) then + return create_array_group(to_string(data), "1", "0", true); + else + return create_array_group(to_string(data), to_string(data'left), to_string(data'right), data'ascending); + end if; + end; + + function to_detailed_string ( + constant data : ufixed) + return string is + variable unsigned_data : ieee.numeric_std.unsigned(data'length - 1 downto 0); + begin + for i in unsigned_data'range loop + unsigned_data(i) := data(i + data'low); + end loop; + return create_array_group(to_string(unsigned_data), to_string(data'left), to_string(data'right), false); + end; + + function to_detailed_string ( + constant data : sfixed) + return string is + variable unsigned_data : ieee.numeric_std.unsigned(data'length - 1 downto 0); + begin + for i in unsigned_data'range loop + unsigned_data(i) := data(i + data'low); + end loop; + return create_array_group(to_string(unsigned_data), to_string(data'left), to_string(data'right), false); + end; + + function to_detailed_string ( + constant data : float) + return string is + variable unsigned_data : ieee.numeric_std.unsigned(data'length - 1 downto 0); + begin + for i in unsigned_data'range loop + unsigned_data(i) := data(i + data'low); + end loop; + return create_array_group(to_string(unsigned_data), to_string(data'left), to_string(data'right), false); + end; + +end package body com_string_pkg; diff --git a/vunit/vhdl/com/src/com_support.vhd b/vunit/vhdl/com/src/com_support.vhd index 0a22ad116..c39369128 100644 --- a/vunit/vhdl/com/src/com_support.vhd +++ b/vunit/vhdl/com/src/com_support.vhd @@ -1,71 +1,71 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -context work.vunit_context; -use work.com_types_pkg.all; - -package com_support_pkg is - - procedure check ( - expr : boolean; - err : com_status_t; - msg : string := ""; - line_num : natural := 0; - file_name : string := ""); - procedure check_failed ( - err : com_error_t; - msg : string := ""; - line_num : natural := 0; - file_name : string := ""); - impure function check ( - expr : boolean; - err : com_status_t; - msg : string := ""; - line_num : natural := 0; - file_name : string := "") return boolean; -end package com_support_pkg; - -package body com_support_pkg is - - procedure check_failed ( - err : com_error_t; - msg : string := ""; - line_num : natural := 0; - file_name : string := "") is - constant err_msg : string := replace(com_error_t'image(err), '_', ' '); - alias err_msg_aligned : string(1 to err_msg'length) is err_msg; - constant err_msg_capitalized : string := upper(err_msg_aligned) & "."; - begin - if msg /= "" then - failure(com_logger, err_msg_capitalized & " " & msg, line_num => line_num, file_name => file_name); - else - failure(com_logger, err_msg_capitalized, line_num => line_num, file_name => file_name); - end if; - end; - - procedure check ( - expr : boolean; - err : com_status_t; - msg : string := ""; - line_num : natural := 0; - file_name : string := "") is - begin - if not expr then - check_failed(err, msg, line_num => line_num, file_name => file_name); - end if; - end; - - impure function check ( - expr : boolean; - err : com_status_t; - msg : string := ""; - line_num : natural := 0; - file_name : string := "") return boolean is - begin - check(expr, err, msg, line_num => line_num, file_name => file_name); - return expr; - end; -end package body com_support_pkg; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +context work.vunit_context; +use work.com_types_pkg.all; + +package com_support_pkg is + + procedure check ( + expr : boolean; + err : com_status_t; + msg : string := ""; + line_num : natural := 0; + file_name : string := ""); + procedure check_failed ( + err : com_error_t; + msg : string := ""; + line_num : natural := 0; + file_name : string := ""); + impure function check ( + expr : boolean; + err : com_status_t; + msg : string := ""; + line_num : natural := 0; + file_name : string := "") return boolean; +end package com_support_pkg; + +package body com_support_pkg is + + procedure check_failed ( + err : com_error_t; + msg : string := ""; + line_num : natural := 0; + file_name : string := "") is + constant err_msg : string := replace(com_error_t'image(err), '_', ' '); + alias err_msg_aligned : string(1 to err_msg'length) is err_msg; + constant err_msg_capitalized : string := upper(err_msg_aligned) & "."; + begin + if msg /= "" then + failure(com_logger, err_msg_capitalized & " " & msg, line_num => line_num, file_name => file_name); + else + failure(com_logger, err_msg_capitalized, line_num => line_num, file_name => file_name); + end if; + end; + + procedure check ( + expr : boolean; + err : com_status_t; + msg : string := ""; + line_num : natural := 0; + file_name : string := "") is + begin + if not expr then + check_failed(err, msg, line_num => line_num, file_name => file_name); + end if; + end; + + impure function check ( + expr : boolean; + err : com_status_t; + msg : string := ""; + line_num : natural := 0; + file_name : string := "") return boolean is + begin + check(expr, err, msg, line_num => line_num, file_name => file_name); + return expr; + end; +end package body com_support_pkg; diff --git a/vunit/vhdl/com/src/com_types.vhd b/vunit/vhdl/com/src/com_types.vhd index 0b24228af..42946fa6c 100644 --- a/vunit/vhdl/com/src/com_types.vhd +++ b/vunit/vhdl/com/src/com_types.vhd @@ -1,857 +1,857 @@ --- Common com types. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.math_complex.all; -use ieee.numeric_bit.all; -use ieee.numeric_std.all; -use ieee.fixed_pkg.all; -use ieee.float_pkg.all; - -use std.textio.all; - -use work.integer_vector_ptr_pkg.all; -use work.integer_array_pkg.all; -use work.string_ptr_pkg.all; -use work.logger_pkg.all; -use work.queue_pkg.all; -use work.queue_2008_pkg.all; -use work.queue_pool_pkg.all; - -package com_types_pkg is - - -- These status types are mostly internal to com and will cause runtime - -- errors. Only ok and timeout will ever be returned to the user - type com_status_t is (ok, - timeout, - null_message_error, - unknown_actor_error, - unknown_receiver_error, - unknown_subscriber_error, - unknown_publisher_error, - deferred_receiver_error, - already_a_subscriber_error, - not_a_subscriber_error, - full_inbox_error, - reply_missing_request_id_error, - unknown_request_id_error, - deprecated_interface_error, - insufficient_size_error, - duplicate_actor_name_error); - - subtype com_error_t is com_status_t range timeout to duplicate_actor_name_error; - - -- All fields of the actor type are private - type actor_t is record - id : natural; - end record actor_t; - type actor_vec_t is array (integer range <>) of actor_t; - constant null_actor : actor_t := (id => 0); - - -- Mailboxes owned by an actor - type mailbox_id_t is (inbox, outbox); - - -- A message type (of type msg_type_t) can be used identify the type of a message - -- (of type msg_t) such that it can be parsed correctly. - type msg_type_t is record - p_code : integer; - end record; - constant null_msg_type : msg_type_t := (p_code => -1); - - - -- Storage for all registered message types - type msg_types_t is record - p_name_ptrs : integer_vector_ptr_t; - end record; - - constant p_msg_types : msg_types_t := ( - p_name_ptrs => new_integer_vector_ptr); - - - -- Every message has a unique ID unless its a message from an inbound or - -- outbound traffic subscription. These messages will have the same ID as - -- the original message - subtype message_id_t is natural; - constant no_message_id : message_id_t := 0; - - -- Deprecated message type - type message_t is record - id : message_id_t; - msg_type : msg_type_t; - status : com_status_t; - sender : actor_t; - receiver : actor_t; - request_id : message_id_t; - payload : line; - end record message_t; - type message_ptr_t is access message_t; - - -- Message type. All fields of the record are private and should not be - -- referenced directly by the user. - subtype msg_data_t is queue_t; - type msg_t is record - id : message_id_t; - msg_type : msg_type_t; - status : com_status_t; - sender : actor_t; - receiver : actor_t; - - -- ID for the request message if this is a reply - request_id : message_id_t; - - data : msg_data_t; - end record msg_t; - type msg_vec_t is array (natural range <>) of msg_t; - type msg_vec_ptr_t is access msg_vec_t; - - constant null_msg : msg_t := ( - id => no_message_id, - msg_type => null_msg_type, - status => null_message_error, - sender => null_actor, - receiver => null_actor, - request_id => no_message_id, - data => null_queue); - - -- A subscriber can subscribe on three different types of traffic: - -- - -- published - Messages published by publisher - -- outbound - All non-anonymous outbound messages from publisher - -- inbound - All inbound messages to publisher. Replies anonymous requests are excluded. - type subscription_traffic_type_t is (published, outbound, inbound); - - type subscription_t is record - subscriber : actor_t; - publisher : actor_t; - traffic_type : subscription_traffic_type_t; - end record subscription_t; - type subscription_vec_t is array (natural range <>) of subscription_t; - type subscription_vec_ptr_t is access subscription_vec_t; - - -- Deprecated - type receipt_t is record - status : com_status_t; - id : message_id_t; - end record receipt_t; - - -- An event type representing the network over which actors communicate. An event in - -- the network notifies connected actors which can determine the cause of the - -- event by consulting the com messenger (com_messenger.vhd). Actors can be - -- connected to different networks but there's only one global messenger. - subtype network_t is std_logic; - constant network_event : std_logic := '1'; - constant idle_network : std_logic := 'Z'; - - -- Default value for timeout parameters. ModelSim can't handle time'high - constant max_timeout : time := 1 hr; - - -- Captures the state of a mailbox - type mailbox_state_t is record - id : mailbox_id_t; - size : natural; - messages : msg_vec_ptr_t; - end record mailbox_state_t; - - -- Captures the state of an actor - type actor_state_t is record - name : line; - is_deferred : boolean; - inbox : mailbox_state_t; - outbox : mailbox_state_t; - subscriptions : subscription_vec_ptr_t; - subscribers : subscription_vec_ptr_t; - end record actor_state_t; - type actor_state_vec_t is array (natural range <>) of actor_state_t; - type actor_state_vec_ptr_t is access actor_state_vec_t; - - -- Captures the state of the messenger - type messenger_state_t is record - active_actors : actor_state_vec_ptr_t; - deferred_actors : actor_state_vec_ptr_t; - end record messenger_state_t; - - constant com_logger : logger_t := get_logger("vunit_lib:com"); - constant queue_pool : queue_pool_t := new_queue_pool; - - ----------------------------------------------------------------------------- - -- Handling of message types - ----------------------------------------------------------------------------- - impure function new_msg_type(name : string) return msg_type_t; - impure function name(msg_type : msg_type_t) return string; - - procedure unexpected_msg_type(msg_type : msg_type_t; - logger : logger_t := com_logger); - - procedure push_msg_type(msg : msg_t; msg_type : msg_type_t; logger : logger_t := com_logger); - alias push is push_msg_type [msg_t, msg_type_t, logger_t]; - - impure function pop_msg_type(msg : msg_t; - logger : logger_t := com_logger) return msg_type_t; - alias pop is pop_msg_type [msg_t, logger_t return msg_type_t]; - - procedure handle_message(variable msg_type : inout msg_type_t); - impure function is_already_handled(msg_type : msg_type_t) return boolean; - - ----------------------------------------------------------------------------- - -- Message related subprograms - ----------------------------------------------------------------------------- - - -- Create a new empty message. The message has an optional type and can anonymous - -- or signed with the sending actor - impure function new_msg( - msg_type : msg_type_t := null_msg_type; - sender : actor_t := null_actor) return msg_t; - - impure function copy(msg : msg_t) return msg_t; - - -- Delete message. Memory allocated by the message is deallocated. - procedure delete(msg : inout msg_t); - - -- Return sending actor of message if defined, null_actor otherwise - function sender(msg : msg_t) return actor_t; - - -- Return sending actor of message if defined, null_actor otherwise - function receiver(msg : msg_t) return actor_t; - - -- Return message type of message without consuming it as pop_msg_type would - function message_type(msg : msg_t) return msg_type_t; - - -- Check if message is empty - impure function is_empty(msg : msg_t) return boolean; - - -- Push message into a queue. - -- The message is set to null to avoid duplicate ownership - procedure push(queue : queue_t; variable value : inout msg_t); - - -- Pop a message from a queue. - impure function pop(queue : queue_t) return msg_t; - - ----------------------------------------------------------------------------- - -- Subprograms for pushing/popping data to/from a message. Data is popped - -- from a message in the same order they were pushed (FIFO) - ----------------------------------------------------------------------------- - procedure push(msg : msg_t; value : integer); - impure function pop(msg : msg_t) return integer; - alias push_integer is push[msg_t, integer]; - alias pop_integer is pop[msg_t return integer]; - - procedure push(msg : msg_t; value : character); - impure function pop(msg : msg_t) return character; - alias push_character is push[msg_t, character]; - alias pop_character is pop[msg_t return character]; - - procedure push(msg : msg_t; value : boolean); - impure function pop(msg : msg_t) return boolean; - alias push_boolean is push[msg_t, boolean]; - alias pop_boolean is pop[msg_t return boolean]; - - procedure push(msg : msg_t; value : real); - impure function pop(msg : msg_t) return real; - alias push_real is push[msg_t, real]; - alias pop_real is pop[msg_t return real]; - - procedure push(msg : msg_t; value : bit); - impure function pop(msg : msg_t) return bit; - alias push_bit is push[msg_t, bit]; - alias pop_bit is pop[msg_t return bit]; - - procedure push(msg : msg_t; value : std_ulogic); - impure function pop(msg : msg_t) return std_ulogic; - alias push_std_ulogic is push[msg_t, std_ulogic]; - alias pop_std_ulogic is pop[msg_t return std_ulogic]; - - procedure push(msg : msg_t; value : severity_level); - impure function pop(msg : msg_t) return severity_level; - alias push_severity_level is push[msg_t, severity_level]; - alias pop_severity_level is pop[msg_t return severity_level]; - - procedure push(msg : msg_t; value : file_open_status); - impure function pop(msg : msg_t) return file_open_status; - alias push_file_open_status is push[msg_t, file_open_status]; - alias pop_file_open_status is pop[msg_t return file_open_status]; - - procedure push(msg : msg_t; value : file_open_kind); - impure function pop(msg : msg_t) return file_open_kind; - alias push_file_open_kind is push[msg_t, file_open_kind]; - alias pop_file_open_kind is pop[msg_t return file_open_kind]; - - procedure push(msg : msg_t; value : bit_vector); - impure function pop(msg : msg_t) return bit_vector; - alias push_bit_vector is push[msg_t, bit_vector]; - alias pop_bit_vector is pop[msg_t return bit_vector]; - - procedure push(msg : msg_t; value : std_ulogic_vector); - impure function pop(msg : msg_t) return std_ulogic_vector; - alias push_std_ulogic_vector is push[msg_t, std_ulogic_vector]; - alias pop_std_ulogic_vector is pop[msg_t return std_ulogic_vector]; - - procedure push(msg : msg_t; value : complex); - impure function pop(msg : msg_t) return complex; - alias push_complex is push[msg_t, complex]; - alias pop_complex is pop[msg_t return complex]; - - procedure push(msg : msg_t; value : complex_polar); - impure function pop(msg : msg_t) return complex_polar; - alias push_complex_polar is push[msg_t, complex_polar]; - alias pop_complex_polar is pop[msg_t return complex_polar]; - - procedure push(msg : msg_t; value : ieee.numeric_bit.unsigned); - impure function pop(msg : msg_t) return ieee.numeric_bit.unsigned; - alias push_numeric_bit_unsigned is push[msg_t, ieee.numeric_bit.unsigned]; - alias pop_numeric_bit_unsigned is pop[msg_t return ieee.numeric_bit.unsigned]; - - procedure push(msg : msg_t; value : ieee.numeric_bit.signed); - impure function pop(msg : msg_t) return ieee.numeric_bit.signed; - alias push_numeric_bit_signed is push[msg_t, ieee.numeric_bit.signed]; - alias pop_numeric_bit_signed is pop[msg_t return ieee.numeric_bit.signed]; - - procedure push(msg : msg_t; value : ieee.numeric_std.unsigned); - impure function pop(msg : msg_t) return ieee.numeric_std.unsigned; - alias push_numeric_std_unsigned is push[msg_t, ieee.numeric_std.unsigned]; - alias pop_numeric_std_unsigned is pop[msg_t return ieee.numeric_std.unsigned]; - - procedure push(msg : msg_t; value : ieee.numeric_std.signed); - impure function pop(msg : msg_t) return ieee.numeric_std.signed; - alias push_numeric_std_signed is push[msg_t, ieee.numeric_std.signed]; - alias pop_numeric_std_signed is pop[msg_t return ieee.numeric_std.signed]; - - procedure push(msg : msg_t; value : string); - impure function pop(msg : msg_t) return string; - alias push_string is push[msg_t, string]; - alias pop_string is pop[msg_t return string]; - - procedure push(msg : msg_t; value : time); - impure function pop(msg : msg_t) return time; - alias push_time is push[msg_t, time]; - alias pop_time is pop[msg_t return time]; - - -- The value is set to null to avoid duplicate ownership - procedure push(msg : msg_t; variable value : inout integer_vector_ptr_t); - impure function pop(msg : msg_t) return integer_vector_ptr_t; - alias push_integer_vector_ptr_ref is push[msg_t, integer_vector_ptr_t]; - alias pop_integer_vector_ptr_ref is pop[msg_t return integer_vector_ptr_t]; - - -- The value is set to null to avoid duplicate ownership - procedure push(msg : msg_t; variable value : inout string_ptr_t); - impure function pop(msg : msg_t) return string_ptr_t; - alias push_string_ptr_ref is push[msg_t, string_ptr_t]; - alias pop_string_ptr_ref is pop[msg_t return string_ptr_t]; - - -- The value is set to null to avoid duplicate ownership - procedure push(msg : msg_t; variable value : inout queue_t); - impure function pop(msg : msg_t) return queue_t; - alias push_queue_ref is push[msg_t, queue_t]; - alias pop_queue_ref is pop[msg_t return queue_t]; - - procedure push(msg : msg_t; value : boolean_vector); - impure function pop(msg : msg_t) return boolean_vector; - alias push_boolean_vector is push[msg_t, boolean_vector]; - alias pop_boolean_vector is pop[msg_t return boolean_vector]; - - procedure push(msg : msg_t; value : integer_vector); - impure function pop(msg : msg_t) return integer_vector; - alias push_integer_vector is push[msg_t, integer_vector]; - alias pop_integer_vector is pop[msg_t return integer_vector]; - - procedure push(msg : msg_t; value : real_vector); - impure function pop(msg : msg_t) return real_vector; - alias push_real_vector is push[msg_t, real_vector]; - alias pop_real_vector is pop[msg_t return real_vector]; - - procedure push(msg : msg_t; value : time_vector); - impure function pop(msg : msg_t) return time_vector; - alias push_time_vector is push[msg_t, time_vector]; - alias pop_time_vector is pop[msg_t return time_vector]; - - procedure push(msg : msg_t; value : ufixed); - impure function pop(msg : msg_t) return ufixed; - alias push_ufixed is push[msg_t, ufixed]; - alias pop_ufixed is pop[msg_t return ufixed]; - - procedure push(msg : msg_t; value : sfixed); - impure function pop(msg : msg_t) return sfixed; - alias push_sfixed is push[msg_t, sfixed]; - alias pop_sfixed is pop[msg_t return sfixed]; - - procedure push(msg : msg_t; value : float); - impure function pop(msg : msg_t) return float; - alias push_float is push[msg_t, float]; - alias pop_float is pop[msg_t return float]; - - procedure push(msg : msg_t; variable value : inout msg_t); - impure function pop(msg : msg_t) return msg_t; - alias push_msg_t is push[msg_t, msg_t]; - alias pop_msg_t is pop[msg_t return msg_t]; - - procedure push_ref(constant msg : msg_t; value : inout integer_array_t); - impure function pop_ref(msg : msg_t) return integer_array_t; - alias push_integer_array_t_ref is push_ref[msg_t, integer_array_t]; - alias pop_integer_array_t_ref is pop_ref[msg_t return integer_array_t]; - -end package; - -package body com_types_pkg is - - ----------------------------------------------------------------------------- - -- Handling of message types - ----------------------------------------------------------------------------- - - impure function new_msg_type(name : string) return msg_type_t is - constant code : integer := length(p_msg_types.p_name_ptrs); - begin - resize(p_msg_types.p_name_ptrs, code + 1); - set(p_msg_types.p_name_ptrs, code, to_integer(new_string_ptr(name))); - return (p_code => code); - end function; - - impure function name(msg_type : msg_type_t) return string is - begin - return to_string(to_string_ptr(get(p_msg_types.p_name_ptrs, msg_type.p_code))); - end; - - constant message_handled : msg_type_t := new_msg_type("message handled"); - - impure function is_valid(code : integer) return boolean is - begin - return 0 <= code and code < length(p_msg_types.p_name_ptrs); - end; - - procedure handle_message(variable msg_type : inout msg_type_t) is - begin - msg_type := message_handled; - end; - - impure function is_already_handled(msg_type : msg_type_t) return boolean is - begin - return msg_type = message_handled; - end; - - procedure unexpected_msg_type(msg_type : msg_type_t; - logger : logger_t := com_logger) is - constant code : integer := msg_type.p_code; - begin - if is_already_handled(msg_type) then - null; - elsif is_valid(code) then - failure(logger, "Got unexpected message " & to_string(to_string_ptr(get(p_msg_types.p_name_ptrs, code)))); - else - failure(logger, "Got invalid message with code " & to_string(code)); - end if; - end procedure; - - procedure push_msg_type(msg : msg_t; msg_type : msg_type_t; logger : logger_t := com_logger) is - begin - push(msg, msg_type.p_code); - end; - - impure function pop_msg_type(msg : msg_t; logger : logger_t := com_logger) return msg_type_t is - constant code : integer := pop(msg); - begin - if not is_valid(code) then - failure(logger, "Got invalid message with code " & to_string(code)); - end if; - return (p_code => code); - end; - - ----------------------------------------------------------------------------- - -- Message related subprograms - ----------------------------------------------------------------------------- - impure function new_msg( - msg_type : msg_type_t := null_msg_type; - sender : actor_t := null_actor) return msg_t is - variable msg : msg_t; - begin - msg.sender := sender; - msg.data := new_queue(queue_pool); - msg.msg_type := msg_type; - return msg; - end; - - procedure delete(msg : inout msg_t) is - begin - recycle(queue_pool, msg.data); - msg := null_msg; - end procedure delete; - - impure function copy(msg : msg_t) return msg_t is - variable result : msg_t := msg; - begin - result.data := new_queue(queue_pool); - for i in 0 to length(msg.data) - 1 loop - unsafe_push(result.data, get(msg.data.data, 1 + i)); - end loop; - - return result; - end; - - function sender(msg : msg_t) return actor_t is - begin - return msg.sender; - end; - - function receiver(msg : msg_t) return actor_t is - begin - return msg.receiver; - end; - - function message_type(msg : msg_t) return msg_type_t is - begin - return msg.msg_type; - end; - - impure function is_empty(msg : msg_t) return boolean is - begin - if msg.data = null_queue then - return true; - end if; - - return length(msg.data) = 0; - end; - - procedure push(queue : queue_t; variable value : inout msg_t) is - begin - push(queue, value.id); - push(queue, value.msg_type.p_code); - push(queue, com_status_t'pos(value.status)); - push(queue, value.sender.id); - push(queue, value.receiver.id); - push(queue, value.request_id); - push_queue_ref(queue, value.data); - value := null_msg; - end; - - impure function pop(queue : queue_t) return msg_t is - variable ret_val : msg_t; - begin - ret_val.id := pop(queue); - ret_val.msg_type := (p_code => pop(queue)); - ret_val.status := com_status_t'val(integer'(pop(queue))); - ret_val.sender.id := pop(queue); - ret_val.receiver.id := pop(queue); - ret_val.request_id := pop(queue); - ret_val.data := pop_queue_ref(queue); - - return ret_val; - end; - - ----------------------------------------------------------------------------- - -- Subprograms for pushing/popping data to/from a message. Data is popped - -- from a message in the same order they were pushed (FIFO) - ----------------------------------------------------------------------------- - procedure push(msg : msg_t; value : integer) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return integer is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : character) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return character is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : boolean) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return boolean is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : real) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return real is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : bit) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return bit is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : std_ulogic) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return std_ulogic is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : severity_level) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return severity_level is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : file_open_status) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return file_open_status is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : file_open_kind) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return file_open_kind is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : bit_vector) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return bit_vector is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : std_ulogic_vector) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return std_ulogic_vector is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : complex) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return complex is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : complex_polar) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return complex_polar is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : ieee.numeric_bit.unsigned) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return ieee.numeric_bit.unsigned is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : ieee.numeric_bit.signed) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return ieee.numeric_bit.signed is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : ieee.numeric_std.unsigned) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return ieee.numeric_std.unsigned is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : ieee.numeric_std.signed) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return ieee.numeric_std.signed is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : string) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return string is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : time) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return time is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; variable value : inout integer_vector_ptr_t) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return integer_vector_ptr_t is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; variable value : inout string_ptr_t) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return string_ptr_t is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; variable value : inout queue_t) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return queue_t is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : boolean_vector) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return boolean_vector is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : integer_vector) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return integer_vector is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : real_vector) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return real_vector is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : time_vector) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return time_vector is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : ufixed) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return ufixed is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : sfixed) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return sfixed is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; value : float) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return float is - begin - return pop(msg.data); - end; - - procedure push(msg : msg_t; variable value : inout msg_t) is - begin - push(msg.data, value); - end; - - impure function pop(msg : msg_t) return msg_t is - begin - return pop(msg.data); - end; - - procedure push_ref(constant msg : msg_t; value : inout integer_array_t) is - begin - push_ref(msg.data, value); - end; - - impure function pop_ref(msg : msg_t) return integer_array_t is - begin - return pop_ref(msg.data); - end; - -end package body com_types_pkg; +-- Common com types. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.math_complex.all; +use ieee.numeric_bit.all; +use ieee.numeric_std.all; +use ieee.fixed_pkg.all; +use ieee.float_pkg.all; + +use std.textio.all; + +use work.integer_vector_ptr_pkg.all; +use work.integer_array_pkg.all; +use work.string_ptr_pkg.all; +use work.logger_pkg.all; +use work.queue_pkg.all; +use work.queue_2008_pkg.all; +use work.queue_pool_pkg.all; + +package com_types_pkg is + + -- These status types are mostly internal to com and will cause runtime + -- errors. Only ok and timeout will ever be returned to the user + type com_status_t is (ok, + timeout, + null_message_error, + unknown_actor_error, + unknown_receiver_error, + unknown_subscriber_error, + unknown_publisher_error, + deferred_receiver_error, + already_a_subscriber_error, + not_a_subscriber_error, + full_inbox_error, + reply_missing_request_id_error, + unknown_request_id_error, + deprecated_interface_error, + insufficient_size_error, + duplicate_actor_name_error); + + subtype com_error_t is com_status_t range timeout to duplicate_actor_name_error; + + -- All fields of the actor type are private + type actor_t is record + id : natural; + end record actor_t; + type actor_vec_t is array (integer range <>) of actor_t; + constant null_actor : actor_t := (id => 0); + + -- Mailboxes owned by an actor + type mailbox_id_t is (inbox, outbox); + + -- A message type (of type msg_type_t) can be used identify the type of a message + -- (of type msg_t) such that it can be parsed correctly. + type msg_type_t is record + p_code : integer; + end record; + constant null_msg_type : msg_type_t := (p_code => -1); + + + -- Storage for all registered message types + type msg_types_t is record + p_name_ptrs : integer_vector_ptr_t; + end record; + + constant p_msg_types : msg_types_t := ( + p_name_ptrs => new_integer_vector_ptr); + + + -- Every message has a unique ID unless its a message from an inbound or + -- outbound traffic subscription. These messages will have the same ID as + -- the original message + subtype message_id_t is natural; + constant no_message_id : message_id_t := 0; + + -- Deprecated message type + type message_t is record + id : message_id_t; + msg_type : msg_type_t; + status : com_status_t; + sender : actor_t; + receiver : actor_t; + request_id : message_id_t; + payload : line; + end record message_t; + type message_ptr_t is access message_t; + + -- Message type. All fields of the record are private and should not be + -- referenced directly by the user. + subtype msg_data_t is queue_t; + type msg_t is record + id : message_id_t; + msg_type : msg_type_t; + status : com_status_t; + sender : actor_t; + receiver : actor_t; + + -- ID for the request message if this is a reply + request_id : message_id_t; + + data : msg_data_t; + end record msg_t; + type msg_vec_t is array (natural range <>) of msg_t; + type msg_vec_ptr_t is access msg_vec_t; + + constant null_msg : msg_t := ( + id => no_message_id, + msg_type => null_msg_type, + status => null_message_error, + sender => null_actor, + receiver => null_actor, + request_id => no_message_id, + data => null_queue); + + -- A subscriber can subscribe on three different types of traffic: + -- + -- published - Messages published by publisher + -- outbound - All non-anonymous outbound messages from publisher + -- inbound - All inbound messages to publisher. Replies anonymous requests are excluded. + type subscription_traffic_type_t is (published, outbound, inbound); + + type subscription_t is record + subscriber : actor_t; + publisher : actor_t; + traffic_type : subscription_traffic_type_t; + end record subscription_t; + type subscription_vec_t is array (natural range <>) of subscription_t; + type subscription_vec_ptr_t is access subscription_vec_t; + + -- Deprecated + type receipt_t is record + status : com_status_t; + id : message_id_t; + end record receipt_t; + + -- An event type representing the network over which actors communicate. An event in + -- the network notifies connected actors which can determine the cause of the + -- event by consulting the com messenger (com_messenger.vhd). Actors can be + -- connected to different networks but there's only one global messenger. + subtype network_t is std_logic; + constant network_event : std_logic := '1'; + constant idle_network : std_logic := 'Z'; + + -- Default value for timeout parameters. ModelSim can't handle time'high + constant max_timeout : time := 1 hr; + + -- Captures the state of a mailbox + type mailbox_state_t is record + id : mailbox_id_t; + size : natural; + messages : msg_vec_ptr_t; + end record mailbox_state_t; + + -- Captures the state of an actor + type actor_state_t is record + name : line; + is_deferred : boolean; + inbox : mailbox_state_t; + outbox : mailbox_state_t; + subscriptions : subscription_vec_ptr_t; + subscribers : subscription_vec_ptr_t; + end record actor_state_t; + type actor_state_vec_t is array (natural range <>) of actor_state_t; + type actor_state_vec_ptr_t is access actor_state_vec_t; + + -- Captures the state of the messenger + type messenger_state_t is record + active_actors : actor_state_vec_ptr_t; + deferred_actors : actor_state_vec_ptr_t; + end record messenger_state_t; + + constant com_logger : logger_t := get_logger("vunit_lib:com"); + constant queue_pool : queue_pool_t := new_queue_pool; + + ----------------------------------------------------------------------------- + -- Handling of message types + ----------------------------------------------------------------------------- + impure function new_msg_type(name : string) return msg_type_t; + impure function name(msg_type : msg_type_t) return string; + + procedure unexpected_msg_type(msg_type : msg_type_t; + logger : logger_t := com_logger); + + procedure push_msg_type(msg : msg_t; msg_type : msg_type_t; logger : logger_t := com_logger); + alias push is push_msg_type [msg_t, msg_type_t, logger_t]; + + impure function pop_msg_type(msg : msg_t; + logger : logger_t := com_logger) return msg_type_t; + alias pop is pop_msg_type [msg_t, logger_t return msg_type_t]; + + procedure handle_message(variable msg_type : inout msg_type_t); + impure function is_already_handled(msg_type : msg_type_t) return boolean; + + ----------------------------------------------------------------------------- + -- Message related subprograms + ----------------------------------------------------------------------------- + + -- Create a new empty message. The message has an optional type and can anonymous + -- or signed with the sending actor + impure function new_msg( + msg_type : msg_type_t := null_msg_type; + sender : actor_t := null_actor) return msg_t; + + impure function copy(msg : msg_t) return msg_t; + + -- Delete message. Memory allocated by the message is deallocated. + procedure delete(msg : inout msg_t); + + -- Return sending actor of message if defined, null_actor otherwise + function sender(msg : msg_t) return actor_t; + + -- Return sending actor of message if defined, null_actor otherwise + function receiver(msg : msg_t) return actor_t; + + -- Return message type of message without consuming it as pop_msg_type would + function message_type(msg : msg_t) return msg_type_t; + + -- Check if message is empty + impure function is_empty(msg : msg_t) return boolean; + + -- Push message into a queue. + -- The message is set to null to avoid duplicate ownership + procedure push(queue : queue_t; variable value : inout msg_t); + + -- Pop a message from a queue. + impure function pop(queue : queue_t) return msg_t; + + ----------------------------------------------------------------------------- + -- Subprograms for pushing/popping data to/from a message. Data is popped + -- from a message in the same order they were pushed (FIFO) + ----------------------------------------------------------------------------- + procedure push(msg : msg_t; value : integer); + impure function pop(msg : msg_t) return integer; + alias push_integer is push[msg_t, integer]; + alias pop_integer is pop[msg_t return integer]; + + procedure push(msg : msg_t; value : character); + impure function pop(msg : msg_t) return character; + alias push_character is push[msg_t, character]; + alias pop_character is pop[msg_t return character]; + + procedure push(msg : msg_t; value : boolean); + impure function pop(msg : msg_t) return boolean; + alias push_boolean is push[msg_t, boolean]; + alias pop_boolean is pop[msg_t return boolean]; + + procedure push(msg : msg_t; value : real); + impure function pop(msg : msg_t) return real; + alias push_real is push[msg_t, real]; + alias pop_real is pop[msg_t return real]; + + procedure push(msg : msg_t; value : bit); + impure function pop(msg : msg_t) return bit; + alias push_bit is push[msg_t, bit]; + alias pop_bit is pop[msg_t return bit]; + + procedure push(msg : msg_t; value : std_ulogic); + impure function pop(msg : msg_t) return std_ulogic; + alias push_std_ulogic is push[msg_t, std_ulogic]; + alias pop_std_ulogic is pop[msg_t return std_ulogic]; + + procedure push(msg : msg_t; value : severity_level); + impure function pop(msg : msg_t) return severity_level; + alias push_severity_level is push[msg_t, severity_level]; + alias pop_severity_level is pop[msg_t return severity_level]; + + procedure push(msg : msg_t; value : file_open_status); + impure function pop(msg : msg_t) return file_open_status; + alias push_file_open_status is push[msg_t, file_open_status]; + alias pop_file_open_status is pop[msg_t return file_open_status]; + + procedure push(msg : msg_t; value : file_open_kind); + impure function pop(msg : msg_t) return file_open_kind; + alias push_file_open_kind is push[msg_t, file_open_kind]; + alias pop_file_open_kind is pop[msg_t return file_open_kind]; + + procedure push(msg : msg_t; value : bit_vector); + impure function pop(msg : msg_t) return bit_vector; + alias push_bit_vector is push[msg_t, bit_vector]; + alias pop_bit_vector is pop[msg_t return bit_vector]; + + procedure push(msg : msg_t; value : std_ulogic_vector); + impure function pop(msg : msg_t) return std_ulogic_vector; + alias push_std_ulogic_vector is push[msg_t, std_ulogic_vector]; + alias pop_std_ulogic_vector is pop[msg_t return std_ulogic_vector]; + + procedure push(msg : msg_t; value : complex); + impure function pop(msg : msg_t) return complex; + alias push_complex is push[msg_t, complex]; + alias pop_complex is pop[msg_t return complex]; + + procedure push(msg : msg_t; value : complex_polar); + impure function pop(msg : msg_t) return complex_polar; + alias push_complex_polar is push[msg_t, complex_polar]; + alias pop_complex_polar is pop[msg_t return complex_polar]; + + procedure push(msg : msg_t; value : ieee.numeric_bit.unsigned); + impure function pop(msg : msg_t) return ieee.numeric_bit.unsigned; + alias push_numeric_bit_unsigned is push[msg_t, ieee.numeric_bit.unsigned]; + alias pop_numeric_bit_unsigned is pop[msg_t return ieee.numeric_bit.unsigned]; + + procedure push(msg : msg_t; value : ieee.numeric_bit.signed); + impure function pop(msg : msg_t) return ieee.numeric_bit.signed; + alias push_numeric_bit_signed is push[msg_t, ieee.numeric_bit.signed]; + alias pop_numeric_bit_signed is pop[msg_t return ieee.numeric_bit.signed]; + + procedure push(msg : msg_t; value : ieee.numeric_std.unsigned); + impure function pop(msg : msg_t) return ieee.numeric_std.unsigned; + alias push_numeric_std_unsigned is push[msg_t, ieee.numeric_std.unsigned]; + alias pop_numeric_std_unsigned is pop[msg_t return ieee.numeric_std.unsigned]; + + procedure push(msg : msg_t; value : ieee.numeric_std.signed); + impure function pop(msg : msg_t) return ieee.numeric_std.signed; + alias push_numeric_std_signed is push[msg_t, ieee.numeric_std.signed]; + alias pop_numeric_std_signed is pop[msg_t return ieee.numeric_std.signed]; + + procedure push(msg : msg_t; value : string); + impure function pop(msg : msg_t) return string; + alias push_string is push[msg_t, string]; + alias pop_string is pop[msg_t return string]; + + procedure push(msg : msg_t; value : time); + impure function pop(msg : msg_t) return time; + alias push_time is push[msg_t, time]; + alias pop_time is pop[msg_t return time]; + + -- The value is set to null to avoid duplicate ownership + procedure push(msg : msg_t; variable value : inout integer_vector_ptr_t); + impure function pop(msg : msg_t) return integer_vector_ptr_t; + alias push_integer_vector_ptr_ref is push[msg_t, integer_vector_ptr_t]; + alias pop_integer_vector_ptr_ref is pop[msg_t return integer_vector_ptr_t]; + + -- The value is set to null to avoid duplicate ownership + procedure push(msg : msg_t; variable value : inout string_ptr_t); + impure function pop(msg : msg_t) return string_ptr_t; + alias push_string_ptr_ref is push[msg_t, string_ptr_t]; + alias pop_string_ptr_ref is pop[msg_t return string_ptr_t]; + + -- The value is set to null to avoid duplicate ownership + procedure push(msg : msg_t; variable value : inout queue_t); + impure function pop(msg : msg_t) return queue_t; + alias push_queue_ref is push[msg_t, queue_t]; + alias pop_queue_ref is pop[msg_t return queue_t]; + + procedure push(msg : msg_t; value : boolean_vector); + impure function pop(msg : msg_t) return boolean_vector; + alias push_boolean_vector is push[msg_t, boolean_vector]; + alias pop_boolean_vector is pop[msg_t return boolean_vector]; + + procedure push(msg : msg_t; value : integer_vector); + impure function pop(msg : msg_t) return integer_vector; + alias push_integer_vector is push[msg_t, integer_vector]; + alias pop_integer_vector is pop[msg_t return integer_vector]; + + procedure push(msg : msg_t; value : real_vector); + impure function pop(msg : msg_t) return real_vector; + alias push_real_vector is push[msg_t, real_vector]; + alias pop_real_vector is pop[msg_t return real_vector]; + + procedure push(msg : msg_t; value : time_vector); + impure function pop(msg : msg_t) return time_vector; + alias push_time_vector is push[msg_t, time_vector]; + alias pop_time_vector is pop[msg_t return time_vector]; + + procedure push(msg : msg_t; value : ufixed); + impure function pop(msg : msg_t) return ufixed; + alias push_ufixed is push[msg_t, ufixed]; + alias pop_ufixed is pop[msg_t return ufixed]; + + procedure push(msg : msg_t; value : sfixed); + impure function pop(msg : msg_t) return sfixed; + alias push_sfixed is push[msg_t, sfixed]; + alias pop_sfixed is pop[msg_t return sfixed]; + + procedure push(msg : msg_t; value : float); + impure function pop(msg : msg_t) return float; + alias push_float is push[msg_t, float]; + alias pop_float is pop[msg_t return float]; + + procedure push(msg : msg_t; variable value : inout msg_t); + impure function pop(msg : msg_t) return msg_t; + alias push_msg_t is push[msg_t, msg_t]; + alias pop_msg_t is pop[msg_t return msg_t]; + + procedure push_ref(constant msg : msg_t; value : inout integer_array_t); + impure function pop_ref(msg : msg_t) return integer_array_t; + alias push_integer_array_t_ref is push_ref[msg_t, integer_array_t]; + alias pop_integer_array_t_ref is pop_ref[msg_t return integer_array_t]; + +end package; + +package body com_types_pkg is + + ----------------------------------------------------------------------------- + -- Handling of message types + ----------------------------------------------------------------------------- + + impure function new_msg_type(name : string) return msg_type_t is + constant code : integer := length(p_msg_types.p_name_ptrs); + begin + resize(p_msg_types.p_name_ptrs, code + 1); + set(p_msg_types.p_name_ptrs, code, to_integer(new_string_ptr(name))); + return (p_code => code); + end function; + + impure function name(msg_type : msg_type_t) return string is + begin + return to_string(to_string_ptr(get(p_msg_types.p_name_ptrs, msg_type.p_code))); + end; + + constant message_handled : msg_type_t := new_msg_type("message handled"); + + impure function is_valid(code : integer) return boolean is + begin + return 0 <= code and code < length(p_msg_types.p_name_ptrs); + end; + + procedure handle_message(variable msg_type : inout msg_type_t) is + begin + msg_type := message_handled; + end; + + impure function is_already_handled(msg_type : msg_type_t) return boolean is + begin + return msg_type = message_handled; + end; + + procedure unexpected_msg_type(msg_type : msg_type_t; + logger : logger_t := com_logger) is + constant code : integer := msg_type.p_code; + begin + if is_already_handled(msg_type) then + null; + elsif is_valid(code) then + failure(logger, "Got unexpected message " & to_string(to_string_ptr(get(p_msg_types.p_name_ptrs, code)))); + else + failure(logger, "Got invalid message with code " & to_string(code)); + end if; + end procedure; + + procedure push_msg_type(msg : msg_t; msg_type : msg_type_t; logger : logger_t := com_logger) is + begin + push(msg, msg_type.p_code); + end; + + impure function pop_msg_type(msg : msg_t; logger : logger_t := com_logger) return msg_type_t is + constant code : integer := pop(msg); + begin + if not is_valid(code) then + failure(logger, "Got invalid message with code " & to_string(code)); + end if; + return (p_code => code); + end; + + ----------------------------------------------------------------------------- + -- Message related subprograms + ----------------------------------------------------------------------------- + impure function new_msg( + msg_type : msg_type_t := null_msg_type; + sender : actor_t := null_actor) return msg_t is + variable msg : msg_t; + begin + msg.sender := sender; + msg.data := new_queue(queue_pool); + msg.msg_type := msg_type; + return msg; + end; + + procedure delete(msg : inout msg_t) is + begin + recycle(queue_pool, msg.data); + msg := null_msg; + end procedure delete; + + impure function copy(msg : msg_t) return msg_t is + variable result : msg_t := msg; + begin + result.data := new_queue(queue_pool); + for i in 0 to length(msg.data) - 1 loop + unsafe_push(result.data, get(msg.data.data, 1 + i)); + end loop; + + return result; + end; + + function sender(msg : msg_t) return actor_t is + begin + return msg.sender; + end; + + function receiver(msg : msg_t) return actor_t is + begin + return msg.receiver; + end; + + function message_type(msg : msg_t) return msg_type_t is + begin + return msg.msg_type; + end; + + impure function is_empty(msg : msg_t) return boolean is + begin + if msg.data = null_queue then + return true; + end if; + + return length(msg.data) = 0; + end; + + procedure push(queue : queue_t; variable value : inout msg_t) is + begin + push(queue, value.id); + push(queue, value.msg_type.p_code); + push(queue, com_status_t'pos(value.status)); + push(queue, value.sender.id); + push(queue, value.receiver.id); + push(queue, value.request_id); + push_queue_ref(queue, value.data); + value := null_msg; + end; + + impure function pop(queue : queue_t) return msg_t is + variable ret_val : msg_t; + begin + ret_val.id := pop(queue); + ret_val.msg_type := (p_code => pop(queue)); + ret_val.status := com_status_t'val(integer'(pop(queue))); + ret_val.sender.id := pop(queue); + ret_val.receiver.id := pop(queue); + ret_val.request_id := pop(queue); + ret_val.data := pop_queue_ref(queue); + + return ret_val; + end; + + ----------------------------------------------------------------------------- + -- Subprograms for pushing/popping data to/from a message. Data is popped + -- from a message in the same order they were pushed (FIFO) + ----------------------------------------------------------------------------- + procedure push(msg : msg_t; value : integer) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return integer is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : character) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return character is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : boolean) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return boolean is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : real) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return real is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : bit) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return bit is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : std_ulogic) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return std_ulogic is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : severity_level) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return severity_level is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : file_open_status) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return file_open_status is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : file_open_kind) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return file_open_kind is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : bit_vector) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return bit_vector is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : std_ulogic_vector) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return std_ulogic_vector is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : complex) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return complex is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : complex_polar) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return complex_polar is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : ieee.numeric_bit.unsigned) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return ieee.numeric_bit.unsigned is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : ieee.numeric_bit.signed) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return ieee.numeric_bit.signed is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : ieee.numeric_std.unsigned) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return ieee.numeric_std.unsigned is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : ieee.numeric_std.signed) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return ieee.numeric_std.signed is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : string) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return string is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : time) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return time is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; variable value : inout integer_vector_ptr_t) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return integer_vector_ptr_t is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; variable value : inout string_ptr_t) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return string_ptr_t is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; variable value : inout queue_t) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return queue_t is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : boolean_vector) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return boolean_vector is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : integer_vector) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return integer_vector is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : real_vector) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return real_vector is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : time_vector) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return time_vector is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : ufixed) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return ufixed is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : sfixed) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return sfixed is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; value : float) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return float is + begin + return pop(msg.data); + end; + + procedure push(msg : msg_t; variable value : inout msg_t) is + begin + push(msg.data, value); + end; + + impure function pop(msg : msg_t) return msg_t is + begin + return pop(msg.data); + end; + + procedure push_ref(constant msg : msg_t; value : inout integer_array_t) is + begin + push_ref(msg.data, value); + end; + + impure function pop_ref(msg : msg_t) return integer_array_t is + begin + return pop_ref(msg.data); + end; + +end package body com_types_pkg; diff --git a/vunit/vhdl/com/test/constants.vhd b/vunit/vhdl/com/test/constants.vhd index 0d0803e39..f812700db 100644 --- a/vunit/vhdl/com/test/constants.vhd +++ b/vunit/vhdl/com/test/constants.vhd @@ -1,11 +1,11 @@ --- Test suite for com codec package --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -package constants_pkg is - constant byte_msb : natural := 7; -end package constants_pkg; +-- Test suite for com codec package +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +package constants_pkg is + constant byte_msb : natural := 7; +end package constants_pkg; diff --git a/vunit/vhdl/com/test/custom_types.vhd b/vunit/vhdl/com/test/custom_types.vhd index de10f6e7d..a00c0a415 100644 --- a/vunit/vhdl/com/test/custom_types.vhd +++ b/vunit/vhdl/com/test/custom_types.vhd @@ -1,112 +1,112 @@ --- Test suite for com codec package --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; -context vunit_lib.com_context; - -library ieee; -use ieee.std_logic_1164.all; -use ieee.math_complex.all; -use ieee.numeric_bit.all; -use ieee.numeric_std.all; -use ieee.fixed_pkg.all; -use ieee.float_pkg.all; - -use std.textio.all; - -use work.constants_pkg.all; -use work.more_constants_pkg.all; - -package custom_types_pkg is - type enum1_t is (red, green, blue); - -- Commented type should be ignored, if not this will cause a compile error --- type enum1_t is (foo,bar); - - type record1_t is record - a : natural; - b, c, d : integer; - end record record1_t; - type record2_msg_type_t is (command); - type record2_t is record - msg_type : record2_msg_type_t; - a : natural; - b, c, d : integer; - e : std_logic; - end record record2_t; - type record3_t is record - char : character; - end record record3_t; - type record4_t is record - my_integer : integer; - my_real : real; - my_time : time; - my_boolean : boolean; - my_bit : bit; - my_std_ulogic : std_ulogic; - my_severity_level : severity_level; - my_file_open_status : file_open_status; - my_file_open_kind : file_open_kind; - my_integer2 : integer; - end record record4_t; - type record5_t is record - my_character : character; - my_string : string(1 to 3); - my_boolean_vector : boolean_vector(1 to 3); - my_bit_vector : bit_vector(1 to 3); - my_integer_vector : integer_vector(1 to 3); - my_real_vector : real_vector(1 to 3); - my_time_vector : time_vector(1 to 3); - my_std_ulogic_vector : std_ulogic_vector(1 to 3); - my_complex : complex; - my_character2 : character; - end record record5_t; - type record6_t is record - my_complex_polar : complex_polar; - my_bit_unsigned : ieee.numeric_bit.unsigned(1 to 3); - my_bit_signed : ieee.numeric_bit.signed(1 to 3); - my_std_unsigned : ieee.numeric_std.unsigned(1 to 3); - my_std_signed : ieee.numeric_std.signed(1 to 3); - my_ufixed : ufixed(2 downto -2); - my_sfixed : sfixed(2 downto -2); - my_float : float64; - my_complex_polar2 : complex_polar; - end record record6_t; - type record7_t is record - r4 : record4_t; - r5 : record5_t; - r6 : record6_t; - end record record7_t; - type record8_msg_type_t is (read, write); - type record8_t is record - msg_type : record8_msg_type_t; - addr : natural; - data : natural; - end record record8_t; type int_2d_t is array (integer range <>, integer range <>) of integer; - type record9_msg_type_t is (foo, bar); - type record9_t is record - msg_type : record9_msg_type_t; - slv : std_logic_vector(byte_msb downto byte_lsb); - str : string(1 to 3); - int_2d : int_2d_t(1 to 2, 4 downto -1); - end record record9_t; - - type array1_t is array (-2 to 2) of natural; - type array2_t is array (2 downto -2) of natural; - type array3_t is array (-2 to 2, -1 to 1) of natural; - type array4_t is array (positive range <>) of natural; - type array5_t is array (integer range <>, integer range <>) of natural; - type fruit_t is (apple, banana, melon, kiwi, orange, papaya); - type array6_t is array (fruit_t range <>) of natural; --- type--array7_t is array (integer range <>, fruit_t range <>) of natural; - type array8_t is array (2 * (3 - 4) to 2, -1 to -1 + 2) of natural; - type array9_t is array (array1_t'range) of natural; - type array10_t is array (array1_t'range, -1 to 1) of natural; - - -end package custom_types_pkg; +-- Test suite for com codec package +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; + +library ieee; +use ieee.std_logic_1164.all; +use ieee.math_complex.all; +use ieee.numeric_bit.all; +use ieee.numeric_std.all; +use ieee.fixed_pkg.all; +use ieee.float_pkg.all; + +use std.textio.all; + +use work.constants_pkg.all; +use work.more_constants_pkg.all; + +package custom_types_pkg is + type enum1_t is (red, green, blue); + -- Commented type should be ignored, if not this will cause a compile error +-- type enum1_t is (foo,bar); + + type record1_t is record + a : natural; + b, c, d : integer; + end record record1_t; + type record2_msg_type_t is (command); + type record2_t is record + msg_type : record2_msg_type_t; + a : natural; + b, c, d : integer; + e : std_logic; + end record record2_t; + type record3_t is record + char : character; + end record record3_t; + type record4_t is record + my_integer : integer; + my_real : real; + my_time : time; + my_boolean : boolean; + my_bit : bit; + my_std_ulogic : std_ulogic; + my_severity_level : severity_level; + my_file_open_status : file_open_status; + my_file_open_kind : file_open_kind; + my_integer2 : integer; + end record record4_t; + type record5_t is record + my_character : character; + my_string : string(1 to 3); + my_boolean_vector : boolean_vector(1 to 3); + my_bit_vector : bit_vector(1 to 3); + my_integer_vector : integer_vector(1 to 3); + my_real_vector : real_vector(1 to 3); + my_time_vector : time_vector(1 to 3); + my_std_ulogic_vector : std_ulogic_vector(1 to 3); + my_complex : complex; + my_character2 : character; + end record record5_t; + type record6_t is record + my_complex_polar : complex_polar; + my_bit_unsigned : ieee.numeric_bit.unsigned(1 to 3); + my_bit_signed : ieee.numeric_bit.signed(1 to 3); + my_std_unsigned : ieee.numeric_std.unsigned(1 to 3); + my_std_signed : ieee.numeric_std.signed(1 to 3); + my_ufixed : ufixed(2 downto -2); + my_sfixed : sfixed(2 downto -2); + my_float : float64; + my_complex_polar2 : complex_polar; + end record record6_t; + type record7_t is record + r4 : record4_t; + r5 : record5_t; + r6 : record6_t; + end record record7_t; + type record8_msg_type_t is (read, write); + type record8_t is record + msg_type : record8_msg_type_t; + addr : natural; + data : natural; + end record record8_t; type int_2d_t is array (integer range <>, integer range <>) of integer; + type record9_msg_type_t is (foo, bar); + type record9_t is record + msg_type : record9_msg_type_t; + slv : std_logic_vector(byte_msb downto byte_lsb); + str : string(1 to 3); + int_2d : int_2d_t(1 to 2, 4 downto -1); + end record record9_t; + + type array1_t is array (-2 to 2) of natural; + type array2_t is array (2 downto -2) of natural; + type array3_t is array (-2 to 2, -1 to 1) of natural; + type array4_t is array (positive range <>) of natural; + type array5_t is array (integer range <>, integer range <>) of natural; + type fruit_t is (apple, banana, melon, kiwi, orange, papaya); + type array6_t is array (fruit_t range <>) of natural; +-- type--array7_t is array (integer range <>, fruit_t range <>) of natural; + type array8_t is array (2 * (3 - 4) to 2, -1 to -1 + 2) of natural; + type array9_t is array (array1_t'range) of natural; + type array10_t is array (array1_t'range, -1 to 1) of natural; + + +end package custom_types_pkg; diff --git a/vunit/vhdl/com/test/more_constants.vhd b/vunit/vhdl/com/test/more_constants.vhd index 3b65976b1..6948acf59 100644 --- a/vunit/vhdl/com/test/more_constants.vhd +++ b/vunit/vhdl/com/test/more_constants.vhd @@ -1,11 +1,11 @@ --- Test suite for com codec package --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -package more_constants_pkg is - constant byte_lsb : natural := 0; -end package more_constants_pkg; +-- Test suite for com codec package +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +package more_constants_pkg is + constant byte_lsb : natural := 0; +end package more_constants_pkg; diff --git a/vunit/vhdl/com/test/tb_com.vhd b/vunit/vhdl/com/test/tb_com.vhd index 502ffa0b9..cc2951def 100644 --- a/vunit/vhdl/com/test/tb_com.vhd +++ b/vunit/vhdl/com/test/tb_com.vhd @@ -1,1470 +1,1470 @@ --- Test suite for com package --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; -context vunit_lib.com_context; -use vunit_lib.queue_pkg.all; - -library ieee; -use ieee.std_logic_1164.all; - -use std.textio.all; - -entity tb_com is - generic( - runner_cfg : string); -end entity tb_com; - -architecture test_fixture of tb_com is - signal hello_world_received, start_receiver, start_server : boolean := false; - signal start_server2, start_server3, start_server4, start_server5 : boolean := false; - signal start_server6, start_subscribers, start_publishers : boolean := false; - signal hello_subscriber_received : std_logic_vector(1 to 2) := "ZZ"; - signal start_limited_inbox, limited_inbox_actor_done : boolean := false; - signal start_limited_inbox_subscriber : boolean := false; - - constant com_logger : logger_t := get_logger("vunit_lib:com"); -begin - test_runner : process - variable self, actor, actor2, actor3, actor4 : actor_t; - variable actor5, my_receiver, my_sender, server, publisher : actor_t; - variable publisher2, subscriber, subscriber2, subscriber3 : actor_t; - variable actor_vec : actor_vec_t(0 to 2); - variable status : com_status_t; - variable n_actors : natural; - variable t_start, t_stop : time; - variable ack : boolean; - variable msg, msg2, msg3, msg4 : msg_t; - variable request_msg, request_msg2, request_msg3, reply_msg : msg_t; - variable peeked_msg1, peeked_msg2 : msg_t; - variable msg_vec_ptr : msg_vec_ptr_t; - variable deprecated_message : message_ptr_t; - variable subscription_vec_ptr : subscription_vec_ptr_t; - variable actor_state : actor_state_t; - variable mailbox_state : mailbox_state_t; - variable l : line; - variable messenger_state : messenger_state_t; - variable null_mailbox_state : mailbox_state_t := (inbox, 0, null); - variable null_actor_state : actor_state_t := (null, - false, - null_mailbox_state, - null_mailbox_state, - null, null - ); - variable null_messenger_state : messenger_state_t := (null, null); - - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - reset_messenger; - self := new_actor("test runner"); - - -- Create - if run("Test that named actors can be created") then - n_actors := num_of_actors; - actor := new_actor("actor"); - check(actor /= null_actor, "Failed to create named actor"); - check_equal(name(actor), "actor"); - check_equal(num_of_actors, n_actors + 1, "Expected one extra actor"); - check(new_actor("other actor").id /= new_actor("another actor").id, "Failed to create unique actors"); - check_equal(num_of_actors, n_actors + 3, "Expected two extra actors"); - elsif run("Test that no name actors can be created") then - actor := new_actor; - check(actor /= null_actor, "Failed to create no name actor"); - check_equal(name(actor), ""); - elsif run("Test that the null actor has no name") then - check_equal(name(null_actor), ""); - elsif run("Test that two actors of the same name cannot be created") then - actor := new_actor("actor2"); - mock(com_logger); - actor := new_actor("actor2"); - check_only_log(com_logger, "DUPLICATE ACTOR NAME ERROR.", failure); - unmock(com_logger); - elsif run("Test that multiple no-name actors can be created") then - n_actors := num_of_actors; - actor := new_actor; - actor2 := new_actor; - check(actor.id /= actor2.id, "The two actors must have different identities"); - check_equal(num_of_actors, n_actors + 2); - check_equal(num_of_deferred_creations, 0); - - -- Find - elsif run("Test that a created actor can be found") then - actor := new_actor("actor to be found"); - check(find("actor to be found", false) /= null_actor, "Failed to find created actor"); - check_equal(num_of_deferred_creations, 0, "Expected no deferred creations"); - check_false(is_deferred(actor)); - elsif run("Test that an actor not created is found and its creation is deferred") then - check_equal(num_of_deferred_creations, 0, "Expected no deferred creations"); - actor := find("actor with deferred creation"); - check(actor /= null_actor, "Failed to find actor with deferred creation"); - check_equal(num_of_deferred_creations, 1, "Expected one deferred creations"); - check(is_deferred(actor)); - elsif run("Test that deferred creation can be suppressed when an actor is not found") then - actor := new_actor("actor"); - actor2 := find("actor with deferred creation", false); - check(actor2 = null_actor, "Didn't expect to find any actor"); - check_equal(num_of_deferred_creations, 0, "Expected no deferred creations"); - elsif run("Test that a created actor get the correct mailbox size") then - actor := new_actor("actor with max inbox"); - check_equal(mailbox_size(actor), positive'high, result("for inbox size")); - check_equal(mailbox_size(actor, outbox), positive'high, result("for outbox size")); - - actor2 := new_actor("actor with bounded inbox", 23, 17); - check_equal(mailbox_size(actor2), 23, result("for inbox size")); - check_equal(mailbox_size(actor2, outbox), 17, result("for outbox size")); - - check_equal(mailbox_size(null_actor), 0, result("for inbox size")); - check_equal(mailbox_size(null_actor, outbox), 0, result("for outbox size")); - - check_equal(mailbox_size(find("actor to be created")), 1, result("for inbox size")); - check_equal(mailbox_size(find("actor to be created"), outbox), positive'high, result("for outbox size")); - - check_equal(mailbox_size(new_actor("actor to be created", 42, 99)), 42, result("for inbox size")); - check_equal(mailbox_size(find("actor to be created"), outbox), 99, result("for outbox size")); - - elsif run("Test that mailboxes can be resize") then - actor := new_actor("actor with max inbox"); - - resize_mailbox(actor, 17); - check_equal(mailbox_size(actor), 17, result("for inbox size")); - check_equal(mailbox_size(actor, outbox), positive'high, result("for outbox size")); - - resize_mailbox(actor, 23, outbox); - check_equal(mailbox_size(actor), 17, result("for inbox size")); - check_equal(mailbox_size(actor, outbox), 23, result("for outbox size")); - - for i in 1 to 10 loop - msg := new_msg; - send(net, actor, msg); - end loop; - - for i in 1 to 2 loop - receive(net, actor, request_msg); - reply_msg := new_msg; - reply(net, request_msg, reply_msg); - end loop; - - resize_mailbox(actor, 8); - mock(com_logger); - resize_mailbox(actor, 7); - check_only_log(com_logger, "INSUFFICIENT SIZE ERROR.", failure); - unmock(com_logger); - - resize_mailbox(actor, 2, outbox); - mock(com_logger); - resize_mailbox(actor, 1, outbox); - check_only_log(com_logger, "INSUFFICIENT SIZE ERROR.", failure); - unmock(com_logger); - - elsif run("Test that no-name actors can't be found") then - actor := new_actor; - actor2 := new_actor; - check(find("") = null_actor, "Must not find a no-name actor"); - check_equal(num_of_deferred_creations, 0); - - -- Destroy - elsif run("Test that a created actor can be destroyed") then - actor := new_actor("actor to destroy"); - actor2 := new_actor("actor to keep"); - n_actors := num_of_actors; - destroy(actor); - check(num_of_actors = n_actors - 1, "Expected one less actor"); - check(actor = null_actor, "Destroyed actor should be nullified"); - check(find("actor to destroy", false) = null_actor, "A destroyed actor should not be found"); - check(find("actor to keep", false) /= null_actor, - "Actors other than the one destroyed must not be affected"); - elsif run("Test that a non-existing actor cannot be destroyed") then - actor := null_actor; - mock(com_logger); - destroy(actor); - check_only_log(com_logger, "UNKNOWN ACTOR ERROR.", failure); - unmock(com_logger); - elsif run("Test that all actors can be destroyed") then - reset_messenger; - actor := new_actor("actor to destroy"); - actor2 := new_actor("actor to destroy 2"); - check(num_of_actors = 2, "Expected two actors"); - reset_messenger; - check(num_of_actors = 0, "Failed to destroy all actors"); - - -- Copy and delete message - elsif run("Test that a message can be deleted") then - my_sender := new_actor("my sender"); - my_receiver := new_actor("my receiver"); - - msg := new_msg; - msg.id := 17; - msg.status := timeout; - msg.sender := my_sender; - msg.receiver := my_receiver; - msg.request_id := 21; - push_string(msg, "hello"); - - delete(msg); - - check_equal(msg.id, no_message_id); - check(msg.status = null_message_error); - check(msg.sender = null_actor); - check(msg.receiver = null_actor); - check_equal(msg.request_id, no_message_id); - check(msg.data = null_queue); - - elsif run("Test that a message can be copied") then - my_sender := new_actor("my sender"); - my_receiver := new_actor("my receiver"); - - msg := new_msg; - msg.id := 17; - msg.status := timeout; - msg.sender := my_sender; - msg.receiver := my_receiver; - msg.request_id := 21; - push_string(msg, "hello"); - - msg2 := copy(msg); - push_string(msg, "world"); - delete(msg); - msg := new_msg; - push_string(msg, "peanuts"); - - check_equal(msg2.id, 17); - check(msg2.status = timeout); - check(msg2.sender = my_sender); - check(msg2.receiver = my_receiver); - check_equal(msg2.request_id, 21); - check_equal(pop_string(msg2), "hello"); - - -- to_string - elsif run("Test string representation of message") then - my_sender := new_actor("my sender"); - my_receiver := new_actor("my receiver"); - - msg := new_msg; - check_equal(to_string(msg), "-:- - -> - (-)"); - msg := new_msg(new_msg_type("msg type")); - check_equal(to_string(msg), "-:- - -> - (msg type)"); - msg.id := 1; - check_equal(to_string(msg), "1:- - -> - (msg type)"); - msg.sender := my_sender; - check_equal(to_string(msg), "1:- my sender -> - (msg type)"); - msg.receiver := my_receiver; - check_equal(to_string(msg), "1:- my sender -> my receiver (msg type)"); - msg.request_id := 7; - check_equal(to_string(msg), "1:7 my sender -> my receiver (msg type)"); - - -- Send and receive - elsif run("Test that data ownership is lost at send") then - msg := new_msg; - push_string(msg, "hello"); - send(net, self, msg); - check(msg.data = null_queue); - elsif run("Test that an actor can send a message to another actor") then - start_receiver <= true; - wait for 1 ns; - my_receiver := find("my_receiver"); - msg := new_msg(sender => self); - push_string(msg, "hello world"); - send(net, my_receiver, msg); - check(msg.sender = self); - check(msg.receiver = my_receiver); - wait until hello_world_received for 1 ns; - check(hello_world_received, "Expected ""hello world"" to be received at the server"); - elsif run("Test that an actor can send a reply to a message from an a priori unknown actor") then - start_server <= true; - wait for 1 ns; - server := find("server"); - request_msg := new_msg(sender => self); - push_string(request_msg, "request"); - send(net, server, request_msg); - receive(net, self, reply_msg); - check(reply_msg.status = ok, "Expected no receive problems"); - check_equal(pop_string(reply_msg), "request acknowledge"); - elsif run("Test that an actor can send a message to itself") then - msg := new_msg; - push_string(msg, "hello"); - send(net, self, msg); - receive(net, self, msg2); - check(msg2.status = ok, "Expected no receive problems"); - check_equal(pop_string(msg2), "hello"); - elsif run("Test that no-name actors can communicate") then - actor := new_actor; - msg := new_msg; - push_string(msg, "hello"); - send(net, actor, msg); - receive(net, actor, msg2); - check_equal(pop_string(msg2), "hello"); - elsif run("Test that an actor can poll for incoming messages") then - wait_for_message(net, self, status, 0 ns); - check(status = timeout, "Expected timeout"); - msg := new_msg(sender => self); - push_string(msg, "hello again"); - send(net, self, msg); - wait_for_message(net, self, status, 0 ns); - check(status = ok, "Expected ok status"); - get_message(net, self, msg2); - check(msg2.status = ok, "Expected no problems with receive"); - check_equal(pop_string(msg2), "hello again"); - check(msg2.sender = self, "Expected message from myself"); - elsif run("Test that sending to a non-existing actor results in an error") then - msg := new_msg; - push_string(msg, "hello"); - mock(com_logger); - send(net, null_actor, msg); - check_only_log(com_logger, "UNKNOWN RECEIVER ERROR.", failure); - unmock(com_logger); - elsif run("Test that an actor can send to an actor with deferred creation") then - actor := find("deferred actor"); - msg := new_msg; - push_string(msg, "hello actor to be created"); - send(net, actor, msg); - actor := new_actor("deferred actor"); - receive(net, actor, msg2); - check(msg2.status = ok, "Expected no problems with receive"); - check_equal(pop_string(msg2), "hello actor to be created"); - elsif run("Test that receiving from an actor with deferred creation results in an error") then - actor := find("deferred actor"); - mock(com_logger); - receive(net, actor, msg); - check_log(com_logger, "DEFERRED RECEIVER ERROR.", failure); - check_only_log(com_logger, "DEFERRED RECEIVER ERROR.", failure); - unmock(com_logger); - elsif run("Test that empty messages can be sent") then - msg := new_msg; - send(net, self, msg); - receive(net, self, msg2); - check(msg2.status = ok, "Expected no problems with receive"); - check_equal(length(msg2.data), 0); - elsif run("Test that each sent message gets an increasing message number") then - msg := new_msg; - send(net, self, msg); - check(msg.id = 1, "Expected first receipt id to be 1"); - msg := new_msg; - send(net, self, msg); - check(msg.id = 2, "Expected first receipt id to be 2"); - receive(net, self, msg2); - check(msg2.id = 1, "Expected first message id to be 1"); - receive(net, self, msg2); - check(msg2.id = 2, "Expected first message id to be 2"); - elsif run("Test that each published message gets an increasing message number") then - for i in actor_vec'range loop - actor_vec(i) := new_actor; - subscribe(actor_vec(i), self); - end loop; - - for j in 1 to 3 loop - msg := new_msg; - publish(net, self, msg); - for i in actor_vec'range loop - receive(net, actor_vec(i), msg); - check_equal(msg.id, j); - end loop; - end loop; - elsif run("Test that a limited-inbox receiver can receive as expected without blocking") then - start_limited_inbox <= true; - actor := find("limited inbox"); - t_start := now; - msg := new_msg; - push_string(msg, "First message"); - send(net, actor, msg); - t_stop := now; - check_equal(t_stop - t_start, 0 ns, "Expected no blocking on first message"); - t_start := now; - msg := new_msg; - push_string(msg, "Second message"); - send(net, actor, msg, 0 ns); - t_stop := now; - check_equal(t_stop - t_start, 0 ns, "Expected no blocking on second message"); - t_start := now; - msg := new_msg; - push_string(msg, "Third message"); - send(net, actor, msg, 11 ns); - t_stop := now; - check_equal(t_stop - t_start, 10 ns, "Expected a 10 ns blocking period on third message"); - - wait until limited_inbox_actor_done; - elsif run("Test that sending to a limited-inbox receiver times out as expected") then - start_limited_inbox <= true; - actor := find("limited inbox"); - msg := new_msg; - push_string(msg, "First message"); - send(net, actor, msg); - msg := new_msg; - push_string(msg, "Second message"); - send(net, actor, msg, 0 ns); - msg := new_msg; - push_string(msg, "Third message"); - mock(com_logger); - send(net, actor, msg, 9 ns); - check_log(com_logger, "FULL INBOX ERROR.", failure); - check_only_log(com_logger, "[3:- - -> limited inbox (-)] => limited inbox inbox", trace); - unmock(com_logger); - elsif run("Test that messages can be awaited from several actors") then - actor := new_actor; - actor2 := new_actor; - msg := new_msg; - push_string(msg, "To actor"); - send(net, actor, msg); - wait_for_message(net, actor_vec_t'(actor, actor2), status); - check(status = ok, "Expected ok status"); - check_true(has_message(actor)); - check_false(has_message(actor2)); - get_message(net, actor, msg); - check_equal(pop_string(msg), "To actor"); - msg := new_msg; - push_string(msg, "To actor2"); - send(net, actor2, msg); - wait_for_message(net, actor_vec_t'(actor, actor2), status); - check(status = ok, "Expected ok status"); - check_true(has_message(actor2)); - check_false(has_message(actor)); - get_message(net, actor2, msg); - check_equal(pop_string(msg), "To actor2"); - elsif run("Test sending to several actors") then - actor_vec := (new_actor, new_actor, new_actor); - for n in 0 to 2 loop - msg := new_msg; - push_string(msg, "hello"); - send(net, actor_vec(1 to n), msg); - check(msg.data = null_queue); - for i in 1 to n loop - receive(net, actor_vec(i), msg, 0 ns); - check_equal(pop_string(msg), "hello"); - end loop; - end loop; - elsif run("Test sending to several actors with timeout") then - actor_vec := (new_actor(inbox_size => 1), new_actor(inbox_size => 1), new_actor(inbox_size => 1)); - msg := new_msg; - push_string(msg, "hello"); - send(net, actor_vec, msg); - mock(com_logger); - msg := new_msg; - push_string(msg, "hello"); - t_start := now; - send(net, actor_vec, msg, 10 ns); - check_equal(now - t_start, 10 ns); - check_log(com_logger, "FULL INBOX ERROR.", failure); - check_log(com_logger, "[4:- - -> (-)] => inbox", trace); - check_log(com_logger, "FULL INBOX ERROR.", failure); - check_log(com_logger, "[5:- - -> (-)] => inbox", trace); - check_log(com_logger, "FULL INBOX ERROR.", failure); - check_log(com_logger, "[6:- - -> (-)] => inbox", trace); - unmock(com_logger); - elsif run("Test receiving from several actors") then - for i in 0 to 2 loop - actor_vec(i) := new_actor; - subscribe(actor_vec(i), find("publisher " & to_string(i))); - end loop; - start_publishers <= true; - receive(net, actor_vec(0 to 0), msg); - check_equal(name(msg.sender), pop_string(msg)); - for i in 1 to 2 loop - receive(net, actor_vec(1 to 2), msg); - check_equal(name(msg.sender), pop_string(msg)); - end loop; - elsif run("Test that the sender and the receiver of a message can be retrieved") then - actor := new_actor; - actor2 := new_actor; - msg := new_msg(sender => actor2); - push_string(msg, "To actor"); - send(net, actor, msg); - receive(net, actor, msg); - check(sender(msg) = actor2); - check(receiver(msg) = actor); - msg := new_msg; - push_string(msg, "To actor"); - send(net, actor, msg); - receive(net, actor, msg); - check(sender(msg) = null_actor); - check(receiver(msg) = actor); - elsif run("Test that get_message will wake up sender blocking on full inbox") then - actor := new_actor("actor", 1); - start_server6 <= true; - wait for 1 ns; - server := find("server6"); - - request_msg := new_msg(sender => actor); - push_string(request_msg, "request1"); - send(net, server, request_msg); - - request_msg2 := new_msg(sender => actor); - push_string(request_msg2, "request2"); - send(net, server, request_msg2); - - wait_for_message(net, actor, status); - get_message(net, actor, reply_msg); - check_equal(pop_string(reply_msg), "reply to request1"); - - wait_for_message(net, actor, status); - get_message(net, actor, reply_msg); - check_equal(pop_string(reply_msg), "reply to request2"); - - elsif run("Test that a message can be forwarded") then - actor := new_actor("actor"); - actor2 := new_actor("actor2"); - actor3 := new_actor("actor3"); - msg := new_msg(sender => actor); - push_string(msg, "request"); - send(net, actor2, msg); - receive(net, actor2, msg2); - msg3 := new_msg; - push_msg_t(msg3, msg2); - send(net, actor3, msg3); - receive(net, actor3, msg3); - msg2 := new_msg; - push_string(msg2, "reply"); - msg4 := pop_msg_t(msg3); - reply(net, msg4, msg2); - receive_reply(net, msg, msg3); - check_equal(pop_string(msg3), "reply"); - check(sender(msg3) = actor2); - check(receiver(msg3) = actor); - - -- Publish, subscribe, and unsubscribe - elsif run("Test that an actor can publish messages to multiple subscribers") then - publisher := new_actor("publisher"); - start_subscribers <= true; - wait for 1 ns; - msg := new_msg; - push_string(msg, "hello subscriber"); - publish(net, publisher, msg); - check(msg.sender = publisher); - check(msg.receiver = null_actor); - wait until hello_subscriber_received = "11" for 1 ns; - check(hello_subscriber_received = "11", "Expected ""hello subscribers"" to be received at the subscribers"); - elsif run("Test that subscribers receive messages sent on outbound subscription") then - my_sender := new_actor; - my_receiver := new_actor; - subscribe(self, my_sender, outbound); - - msg := new_msg(sender => my_sender); - push_string(msg, "hello"); - send(net, my_receiver, msg); - - receive(net, my_receiver, msg2, 0 ns); - check(sender(msg2) = my_sender); - check(receiver(msg2) = my_receiver); - check_equal(pop_string(msg2), "hello"); - - receive(net, self, msg2, 0 ns); - check(sender(msg2) = my_sender); - check(receiver(msg2) = my_receiver); - check_equal(pop_string(msg2), "hello"); - - msg := new_msg; - push_string(msg, "hello2"); - publish(net, my_sender, msg); - - receive(net, self, msg2, 0 ns); - check(sender(msg2) = my_sender); - check(receiver(msg2) = self); - check_equal(pop_string(msg2), "hello2"); - - elsif run("Test that subscribers don't receive duplicate message") then - publisher := new_actor("publisher"); - subscribe(self, publisher); - - msg := new_msg(sender => publisher); - push_string(msg, "hello"); - send(net, self, msg); - - receive(net, self, msg2, 0 ns); - check(msg2.receiver = self); - wait_for_message(net, self, status, 0 ns); - check(status = timeout, "Expected only one message"); - elsif run("Test that actors don't get send messages on a publish subscription") then - publisher := new_actor("publisher"); - subscribe(self, publisher); - - msg := new_msg(sender => publisher); - push_string(msg, "hello"); - send(net, publisher, msg); - - wait_for_message(net, self, status, 0 ns); - check(status = timeout, "Expected no message"); - elsif run("Test that actors can subscribe to inbound traffic") then - my_receiver := new_actor; - subscribe(self, my_receiver, inbound); - - msg := new_msg; - push_string(msg, "hello"); - send(net, my_receiver, msg); - - receive(net, my_receiver, msg2, 0 ns); - check(sender(msg2) = null_actor); - check(receiver(msg2) = my_receiver); - check_equal(pop_string(msg2), "hello"); - - receive(net, self, msg2, 0 ns); - check(sender(msg2) = null_actor); - check(receiver(msg2) = my_receiver); - check_equal(pop_string(msg2), "hello"); - - msg := new_msg; - push_string(msg, "publication"); - publish(net, my_receiver, msg); - - wait_for_message(net, self, status, 0 ns); - check(status = timeout, "Expected no message"); - - actor := new_actor("actor"); - msg := new_msg(sender => my_receiver); - push_string(msg, "hello"); - - send(net, actor, msg); - wait_for_message(net, self, status, 0 ns); - check(status = timeout, "Expected no message"); - elsif run("Test request/reply with actor having inbound subscribers") then - subscriber := new_actor("subscriber"); - start_server5 <= true; - wait for 1 ns; - server := find("server5"); - subscribe(subscriber, server, inbound); - - request_msg := new_msg(sender => self); - push_string(request_msg, "request"); - send(net, server, request_msg); - - receive_reply(net, request_msg, reply_msg, 100 ns); - check_equal(pop_string(reply_msg), "reply"); - - receive(net, subscriber, reply_msg, 0 ns); - check_equal(pop_string(reply_msg), "request"); - elsif run("Test chained subscribers") then - my_sender := new_actor; - my_receiver := new_actor; - subscriber := new_actor; - subscribe(self, my_sender, outbound); - subscribe(subscriber, self, inbound); - - msg := new_msg(sender => my_sender); - push_string(msg, "hello"); - send(net, my_receiver, msg); - - receive(net, my_receiver, msg2, 0 ns); - check(sender(msg2) = my_sender); - check(receiver(msg2) = my_receiver); - check_equal(pop_string(msg2), "hello"); - - receive(net, self, msg2, 0 ns); - check(sender(msg2) = my_sender); - check(receiver(msg2) = my_receiver); - check_equal(pop_string(msg2), "hello"); - - receive(net, subscriber, msg2, 0 ns); - check(sender(msg2) = my_sender); - check(receiver(msg2) = my_receiver); - check_equal(pop_string(msg2), "hello"); - - msg := new_msg; - push_string(msg, "hello2"); - publish(net, my_sender, msg); - - receive(net, self, msg2, 0 ns); - check(sender(msg2) = my_sender); - check(receiver(msg2) = self); - check_equal(pop_string(msg2), "hello2"); - - receive(net, subscriber, msg2, 0 ns); - check(sender(msg2) = my_sender); - check(receiver(msg2) = self, "Got: " & name(receiver(msg2))); - check_equal(pop_string(msg2), "hello2"); - - elsif run("Test that a subscriber can unsubscribe") then - subscribe(self, self, published); - subscribe(self, self, inbound); - unsubscribe(self, self, inbound); - msg := new_msg; - push_string(msg, "hello subscriber"); - publish(net, self, msg); - receive(net, self, msg, 0 ns); - check_equal(pop_string(msg), "hello subscriber"); - unsubscribe(self, self, published); - msg := new_msg; - push_string(msg, "hello subscriber"); - publish(net, self, msg); - wait_for_message(net, self, status, 0 ns); - check(status = timeout, "Expected no message"); - elsif run("Test that a destroyed subscriber is not addressed by the publisher") then - subscriber := new_actor("subscriber"); - subscribe(subscriber, self); - msg := new_msg; - push_string(msg, "hello subscriber"); - publish(net, self, msg); - receive(net, subscriber, msg, 0 ns); - check_equal(pop_string(msg), "hello subscriber"); - destroy(subscriber); - push_string(msg, "hello subscriber"); - publish(net, self, msg); - elsif run("Test that an actor can only subscribe once to the same publisher") then - subscribe(self, self); - mock(com_logger); - subscribe(self, self); - check_only_log(com_logger, "ALREADY A SUBSCRIBER ERROR.", failure); - unmock(com_logger); - elsif run("Test that publishing to subscribers with full inboxes results is an error") then - start_limited_inbox_subscriber <= true; - wait for 1 ns; - msg := new_msg; - push_string(msg, "hello subscriber"); - publish(net, self, msg); - msg := new_msg; - push_string(msg, "hello subscriber"); - mock(com_logger); - publish(net, self, msg, 8 ns); - check_log(com_logger, "FULL INBOX ERROR.", failure); - check_only_log(com_logger, - "[2:- test runner -> limited inbox subscriber (-)] => limited inbox subscriber inbox", - trace); - unmock(com_logger); - elsif run("Test that publishing to subscribers with full inboxes results passes if waiting") then - start_limited_inbox_subscriber <= true; - wait for 1 ns; - msg := new_msg; - push_string(msg, "hello subscriber"); - publish(net, self, msg); - msg := new_msg; - push_string(msg, "hello subscriber"); - publish(net, self, msg, 11 ns); - - -- Request, (receive_)reply and acknowledge - elsif run("Test that a client can wait for an out-of-order request reply") then - start_server2 <= true; - server := find("server2"); - - request_msg := new_msg(sender => self); - push_string(request_msg, "request1"); - send(net, server, request_msg); - - request_msg2 := new_msg(sender => self); - push_string(request_msg2, "request2"); - send(net, server, request_msg2); - - receive_reply(net, request_msg, ack); - check_false(ack, "Expected negative acknowledgement"); - - request_msg3 := new_msg(sender => self); - push_string(request_msg3, "request3"); - send(net, server, request_msg3); - - receive_reply(net, request_msg3, ack); - check(ack, "Expected positive acknowledgement"); - - receive_reply(net, request_msg2, reply_msg); - check(reply_msg.sender = server); - check(reply_msg.receiver = self); - check_equal(pop_string(reply_msg), "reply2"); - check_equal(reply_msg.request_id, request_msg2.id); - - elsif run("Test that a synchronous request can be made") then - start_server3 <= true; - server := find("server3"); - - request_msg := new_msg(sender => self); - push_string(request_msg, "request1"); - request(net, server, request_msg, reply_msg); - check_equal(pop_string(reply_msg), "reply1"); - - request_msg := new_msg(sender => self); - push_string(request_msg, "request2"); - request(net, server, request_msg, ack); - check(ack, "Expected positive acknowledgement"); - - request_msg := new_msg(sender => self); - push_string(request_msg, "request3"); - request(net, server, request_msg, ack); - check_false(ack, "Expected negative acknowledgement"); - elsif run("Test that waiting and getting a reply with timeout works") then - start_server4 <= true; - server := find("server4"); - - t_start := now; - request_msg := new_msg(sender => self); - push_string(request_msg, "request1"); - send(net, server, request_msg); - wait_for_reply(net, request_msg, status, 2 ns); - check(status = timeout, "Expected timeout"); - check_equal(now - t_start, 2 ns); - - t_start := now; - request_msg := new_msg; - push_string(request_msg, "request2"); - send(net, server, request_msg); - wait_for_reply(net, request_msg, status, 2 ns); - check(status = timeout, "Expected timeout"); - check_equal(now - t_start, 2 ns); - - request_msg := new_msg(sender => self); - push_string(request_msg, "request3"); - send(net, server, request_msg); - wait_for_reply(net, request_msg, status); - get_reply(net, request_msg, reply_msg); - check_equal(pop_string(reply_msg), "reply3"); - - t_start := now; - request_msg := new_msg; - push_string(request_msg, "request4"); - send(net, server, request_msg); - wait_for_reply(net, request_msg, status); - get_reply(net, request_msg, reply_msg); - check_equal(pop_string(reply_msg), "reply4"); - elsif run("Test waiting and getting a reply out-of-order") then - start_server2 <= true; - server := find("server2"); - - request_msg := new_msg(sender => self); - push_string(request_msg, "request1"); - send(net, server, request_msg); - - request_msg2 := new_msg(sender => self); - push_string(request_msg2, "request2"); - send(net, server, request_msg2); - - wait_for_reply(net, request_msg, status); - check(status = ok); - - get_reply(net, request_msg, reply_msg); - check_false(pop_boolean(reply_msg)); - - request_msg3 := new_msg(sender => self); - push_string(request_msg3, "request3"); - send(net, server, request_msg3); - - wait_for_reply(net, request_msg3, status); - check(status = ok); - - get_reply(net, request_msg3, reply_msg); - check(pop_boolean(reply_msg)); - - wait_for_reply(net, request_msg2, status); - check(status = ok); - - get_reply(net, request_msg2, reply_msg); - check_equal(pop_string(reply_msg), "reply2"); - - elsif run("Test that an anonymous request can be made") then - start_server5 <= true; - server := find("server5"); - - request_msg := new_msg; - push_string(request_msg, "request"); - send(net, server, request_msg); - wait for 10 ns; - receive_reply(net, request_msg, reply_msg); - check(reply_msg.sender = server); - check(reply_msg.receiver = null_actor); - check_equal(pop_string(reply_msg), "reply"); - check_equal(reply_msg.request_id, request_msg.id); - - request_msg := new_msg; - push_string(request_msg, "request2"); - send(net, server, request_msg); - receive_reply(net, request_msg, reply_msg); - check_equal(pop_string(reply_msg), "reply2"); - - request_msg := new_msg; - push_string(request_msg, "request3"); - send(net, server, request_msg); - receive_reply(net, request_msg, reply_msg); - check_equal(pop_string(reply_msg), "reply3"); - - elsif run("Test that get_reply will wake up sender blocking on full inbox") then - actor := new_actor("actor", 1); - start_server6 <= true; - wait for 1 ns; - server := find("server6"); - - request_msg := new_msg(sender => actor); - push_string(request_msg, "request1"); - send(net, server, request_msg); - - request_msg2 := new_msg(sender => actor); - push_string(request_msg2, "request2"); - send(net, server, request_msg2); - - receive_reply(net, request_msg, reply_msg); - check_equal(pop_string(reply_msg), "reply to request1"); - - receive_reply(net, request_msg2, reply_msg); - check_equal(pop_string(reply_msg), "reply to request2"); - - -- Timeout - elsif run("Test that timeout on receive leads to an error") then - mock(com_logger); - receive(net, self, msg, 1 ns); - check_only_log(com_logger, "TIMEOUT.", failure); - unmock(com_logger); - - -- Debugging - elsif run("Test getting the number of messages in a mailbox") then - check_equal(num_of_messages(self), 0); - msg := new_msg; - send(net, self, msg); - check_equal(num_of_messages(self), 1); - msg := new_msg; - send(net, self, msg); - check_equal(num_of_messages(self), 2); - receive(net, self, msg); - check_equal(num_of_messages(self), 1); - receive(net, self, msg); - check_equal(num_of_messages(self), 0); - - check_equal(num_of_messages(self, outbox), 0); - msg := new_msg; - send(net, self, msg); - receive(net, self, request_msg); - reply_msg := new_msg; - reply(net, request_msg, reply_msg); - check_equal(num_of_messages(self, outbox), 1); - msg2 := new_msg; - send(net, self, msg2); - receive(net, self, request_msg); - reply_msg := new_msg; - reply(net, request_msg, reply_msg); - check_equal(num_of_messages(self, outbox), 2); - receive_reply(net, msg, reply_msg); - check_equal(num_of_messages(self, outbox), 1); - receive_reply(net, msg2, reply_msg); - check_equal(num_of_messages(self, outbox), 0); - - elsif run("Test peeking at messages in a mailbox") then - actor := new_actor; - mock(com_logger); - peeked_msg1 := peek_message(actor); - check_only_log(com_logger, "Peeking non-existing position.", failure); - unmock(com_logger); - - msg := new_msg; - send(net, actor, msg); - msg := new_msg; - send(net, actor, msg); - peeked_msg1 := peek_message(actor); - peeked_msg2 := peek_message(actor, 1); - receive(net, actor, msg); - check(peeked_msg1 = msg); - receive(net, actor, msg); - check(peeked_msg2 = msg); - - msg := new_msg; - send(net, actor, msg); - msg2 := new_msg; - send(net, actor, msg2); - - mock(com_logger); - peeked_msg1 := peek_message(actor, 0, outbox); - check_only_log(com_logger, "Peeking non-existing position.", failure); - unmock(com_logger); - - receive(net, actor, request_msg); - reply_msg := new_msg; - reply(net, request_msg, reply_msg); - receive(net, actor, request_msg); - reply_msg := new_msg; - reply(net, request_msg, reply_msg); - - peeked_msg1 := peek_message(actor, 0, outbox); - peeked_msg2 := peek_message(actor, 1, outbox); - - receive_reply(net, msg, reply_msg); - check(peeked_msg1 = reply_msg); - receive_reply(net, msg2, reply_msg); - check(peeked_msg2 = reply_msg); - - elsif run("Test getting and deallocating mailbox state") then - actor := new_actor("actor", 17, 23); - msg := new_msg; - send(net, actor, msg); - mailbox_state := get_mailbox_state(actor); - check(mailbox_state.id = inbox); - check(mailbox_state.size = 17); - receive(net, actor, request_msg); - check(mailbox_state.messages(0) = request_msg); - - reply_msg := new_msg; - reply(net, request_msg, reply_msg); - mailbox_state := get_mailbox_state(actor, outbox); - check(mailbox_state.id = outbox); - check(mailbox_state.size = 23); - receive_reply(net, msg, reply_msg); - check(mailbox_state.messages(0) = reply_msg); - - deallocate(mailbox_state); - check(mailbox_state = null_mailbox_state); - - elsif run("Test making a string of mailbox state") then - actor := new_actor("actor", 17); - check_equal(get_mailbox_state_string(actor, inbox), - "Mailbox: inbox" & LF & " Size: 17" & LF & " Messages:"); - - msg := new_msg(sender => self); - send(net, actor, msg); - msg := new_msg; - send(net, actor, msg); - mailbox_state := get_mailbox_state(actor); - write(l, get_mailbox_state_string(actor, inbox, " ")); - receive(net, actor, request_msg); - info(l.all); - check_equal( - l.all, - " Mailbox: inbox" & LF & " Size: 17" & LF & " Messages:" & LF & " 0. " & -- - to_string(mailbox_state.messages(0)) & LF & " 1. " & to_string(mailbox_state.messages(1)) - ); - - elsif run("Test getting and deallocating actor state") then - actor := find("my actor"); - actor_state := get_actor_state(actor); - check_equal(actor_state.name.all, "my actor"); - check(actor_state.is_deferred); - deallocate(actor_state); - check(actor_state = null_actor_state); - - actor := new_actor("my actor", 17, 21); - actor_state := get_actor_state(actor); - check_false(actor_state.is_deferred); - - msg := new_msg; - send(net, actor, msg); - actor_state := get_actor_state(actor); - receive(net, actor, request_msg); - check(actor_state.inbox.id = inbox); - check(actor_state.inbox.size = 17); - check(actor_state.inbox.messages(0) = request_msg); - - reply_msg := new_msg; - reply(net, request_msg, reply_msg); - actor_state := get_actor_state(actor); - receive_reply(net, msg, reply_msg); - check(actor_state.outbox.id = outbox); - check(actor_state.outbox.size = 21); - check(actor_state.outbox.messages(0) = reply_msg); - deallocate(actor_state); - check(actor_state = null_actor_state); - - actor2 := new_actor; - subscribe(actor, actor2, inbound); - subscribe(self, actor); - actor_state := get_actor_state(actor); - check(actor_state.subscriptions(0) = subscription_t'(subscriber => actor, - publisher => actor2, - traffic_type => inbound - ) - ); - check(actor_state.subscribers(0) = subscription_t'(subscriber => self, - publisher => actor, - traffic_type => published - ) - ); - deallocate(actor_state); - check(actor_state = null_actor_state); - - elsif run("Test making a string of actor state") then - actor := find("my actor"); - check_equal(get_actor_state_string(actor), - "Name: my actor" & LF & " Is deferred: yes" & LF & -- - get_mailbox_state_string(actor, inbox, " ") & LF & -- - get_mailbox_state_string(actor, outbox, " ") & LF & -- - " Subscriptions:" & LF & " Subscribers:"); - - actor := new_actor("my actor"); - msg := new_msg; - send(net, actor, msg); - - check_equal(get_actor_state_string(actor), - "Name: my actor" & LF & " Is deferred: no" & LF & -- - get_mailbox_state_string(actor, inbox, " ") & LF & -- - get_mailbox_state_string(actor, outbox, " ") & LF & -- - " Subscriptions:" & LF & " Subscribers:"); - - receive(net, actor, request_msg); - reply_msg := new_msg; - reply(net, request_msg, reply_msg); - - check_equal(get_actor_state_string(actor), - "Name: my actor" & LF & " Is deferred: no" & LF & -- - get_mailbox_state_string(actor, inbox, " ") & LF & -- - get_mailbox_state_string(actor, outbox, " ") & LF & -- - " Subscriptions:" & LF & " Subscribers:"); - - actor2 := new_actor("actor 2"); - subscribe(actor, actor2); - subscribe(actor, actor2, inbound); - subscribe(self, actor, inbound); - subscribe(self, actor, outbound); - - info(get_actor_state_string(actor)); - check_equal(get_actor_state_string(actor), - "Name: my actor" & LF & " Is deferred: no" & LF & -- - get_mailbox_state_string(actor, inbox, " ") & LF & -- - get_mailbox_state_string(actor, outbox, " ") & LF & -- - " Subscriptions:" & LF & " published traffic from actor 2" & LF & -- - " inbound traffic to actor 2" & LF & " Subscribers:" & LF & -- - " test runner subscribes to outbound traffic" & LF & -- - " test runner subscribes to inbound traffic"); - - elsif run("Test getting messenger and deallocating state") then - reset_messenger; - messenger_state := get_messenger_state; - check(messenger_state = null_messenger_state); - deallocate(messenger_state); - check(messenger_state = null_messenger_state); - - actor := new_actor("actor"); - messenger_state := get_messenger_state; - check_equal(messenger_state.active_actors'length, 1); - check(messenger_state.deferred_actors = null); - actor_state := get_actor_state(actor); - check_equal(messenger_state.active_actors(0).name.all, actor_state.name.all); - check_equal(messenger_state.active_actors(0).is_deferred, actor_state.is_deferred); - check(messenger_state.active_actors(0).inbox = actor_state.inbox); - check(messenger_state.active_actors(0).outbox = actor_state.outbox); - check(messenger_state.active_actors(0).subscriptions = actor_state.subscriptions); - check(messenger_state.active_actors(0).subscribers = actor_state.subscribers); - deallocate(messenger_state); - check(messenger_state = null_messenger_state); - - actor2 := new_actor("actor 2"); - messenger_state := get_messenger_state; - check_equal(messenger_state.active_actors'length, 2); - check(messenger_state.deferred_actors = null); - actor_state := get_actor_state(actor2); - check_equal(messenger_state.active_actors(1).name.all, actor_state.name.all); - check_equal(messenger_state.active_actors(1).is_deferred, actor_state.is_deferred); - check(messenger_state.active_actors(1).inbox = actor_state.inbox); - check(messenger_state.active_actors(1).outbox = actor_state.outbox); - check(messenger_state.active_actors(1).subscriptions = actor_state.subscriptions); - check(messenger_state.active_actors(1).subscribers = actor_state.subscribers); - deallocate(messenger_state); - check(messenger_state = null_messenger_state); - - destroy(actor); - - messenger_state := get_messenger_state; - check_equal(messenger_state.active_actors'length, 1); - check(messenger_state.deferred_actors = null); - actor_state := get_actor_state(actor2); - check_equal(messenger_state.active_actors(0).name.all, actor_state.name.all); - check_equal(messenger_state.active_actors(0).is_deferred, actor_state.is_deferred); - check(messenger_state.active_actors(0).inbox = actor_state.inbox); - check(messenger_state.active_actors(0).outbox = actor_state.outbox); - check(messenger_state.active_actors(0).subscriptions = actor_state.subscriptions); - check(messenger_state.active_actors(0).subscribers = actor_state.subscribers); - deallocate(messenger_state); - check(messenger_state = null_messenger_state); - - actor3 := find("actor 3"); - messenger_state := get_messenger_state; - check_equal(messenger_state.active_actors'length, 1); - check_equal(messenger_state.deferred_actors'length, 1); - actor_state := get_actor_state(actor2); - check_equal(messenger_state.active_actors(0).name.all, actor_state.name.all); - check_equal(messenger_state.active_actors(0).is_deferred, actor_state.is_deferred); - check(messenger_state.active_actors(0).inbox = actor_state.inbox); - check(messenger_state.active_actors(0).outbox = actor_state.outbox); - check(messenger_state.active_actors(0).subscriptions = actor_state.subscriptions); - check(messenger_state.active_actors(0).subscribers = actor_state.subscribers); - actor_state := get_actor_state(actor3); - check_equal(messenger_state.deferred_actors(0).name.all, actor_state.name.all); - check_equal(messenger_state.deferred_actors(0).is_deferred, actor_state.is_deferred); - check(messenger_state.deferred_actors(0).inbox = actor_state.inbox); - check(messenger_state.deferred_actors(0).outbox = actor_state.outbox); - check(messenger_state.deferred_actors(0).subscriptions = actor_state.subscriptions); - check(messenger_state.deferred_actors(0).subscribers = actor_state.subscribers); - deallocate(messenger_state); - check(messenger_state = null_messenger_state); - - destroy(actor2); - - messenger_state := get_messenger_state; - check(messenger_state.active_actors = null); - check_equal(messenger_state.deferred_actors'length, 1); - actor_state := get_actor_state(actor3); - check_equal(messenger_state.deferred_actors(0).name.all, actor_state.name.all); - check_equal(messenger_state.deferred_actors(0).is_deferred, actor_state.is_deferred); - check(messenger_state.deferred_actors(0).inbox = actor_state.inbox); - check(messenger_state.deferred_actors(0).outbox = actor_state.outbox); - check(messenger_state.deferred_actors(0).subscriptions = actor_state.subscriptions); - check(messenger_state.deferred_actors(0).subscribers = actor_state.subscribers); - deallocate(messenger_state); - check(messenger_state = null_messenger_state); - - elsif run("Test making a string of messenger state") then - reset_messenger; - check_equal(get_messenger_state_string, - "Active actors:" & LF & "Deferred actors:"); - - actor := new_actor("actor"); - check_equal(get_messenger_state_string, - "Active actors:" & LF & get_actor_state_string(actor, " ") & LF & LF & "Deferred actors:"); - - actor2 := new_actor("actor 2"); - check_equal(get_messenger_state_string, - "Active actors:" & LF & get_actor_state_string(actor, " ") & LF & LF & -- - get_actor_state_string(actor2, " ") & LF & LF & "Deferred actors:"); - - actor3 := find("actor 3"); - actor4 := find("actor 4"); - actor5 := new_actor("actor 5"); - check_equal(get_messenger_state_string(" "), - " Active actors:" & LF & get_actor_state_string(actor, " ") & LF & LF & -- - get_actor_state_string(actor2, " ") & LF & LF & get_actor_state_string(actor5, " ") & -- - LF & LF & " Deferred actors:" & LF & get_actor_state_string(actor3, " ") & LF & -- - LF & get_actor_state_string(actor4, " ")); - - destroy(actor); - destroy(actor2); - destroy(actor5); - check_equal(get_messenger_state_string, - "Active actors:" & LF & "Deferred actors:" & LF & get_actor_state_string(actor3, " ") & -- - LF & LF & get_actor_state_string(actor4, " ")); - - elsif run("Test trace log") then - mock(com_logger); - my_sender := new_actor("sender"); - my_receiver := new_actor("receiver"); - - msg := new_msg(new_msg_type("msg type"), sender => my_sender); - send(net, my_receiver, msg); - check_only_log(com_logger, "[1:- sender -> receiver (msg type)] => receiver inbox", trace); - receive(net, my_receiver, msg); - check_only_log(com_logger, "receiver inbox => [1:- sender -> receiver (msg type)]", trace); - - request_msg := new_msg; - send(net, my_receiver, request_msg); - check_only_log(com_logger, "[2:- - -> receiver (-)] => receiver inbox", trace); - receive(net, my_receiver, reply_msg); - check_only_log(com_logger, "receiver inbox => [2:- - -> receiver (-)]", trace); - - reply(net, request_msg, reply_msg); - check_only_log(com_logger, "[3:2 receiver -> - (-)] => receiver outbox", trace); - receive_reply(net, request_msg, reply_msg); - check_only_log(com_logger, "receiver outbox => [3:2 receiver -> - (-)]", trace); - - unmock(com_logger); - - -- Deprecated APIs - elsif run("Test that use of deprecated API leads to an error") then - mock(com_logger); - deprecated_message := compose("hello world"); - check_only_log(com_logger, "DEPRECATED INTERFACE ERROR. compose()", failure); - unmock(com_logger); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 100 ms); - - my_receiver : process is - variable self : actor_t; - variable msg : msg_t; - begin - wait until start_receiver; - self := new_actor("my_receiver"); - receive(net, self, msg); - check(msg.sender = find("test runner")); - check(msg.receiver = self); - hello_world_received <= check_equal(pop_string(msg), "hello world"); - wait; - end process; - - server : process is - variable self : actor_t; - variable request_msg, reply_msg : msg_t; - begin - wait until start_server; - self := new_actor("server"); - receive(net, self, request_msg); - if check_equal(pop_string(request_msg), "request") then - reply_msg := new_msg; - push_string(reply_msg, "request acknowledge"); - send(net, request_msg.sender, reply_msg); - end if; - wait; - end process server; - - subscribers : for i in 1 to 2 generate - process is - variable self, publisher : actor_t; - variable msg : msg_t; - begin - wait until start_subscribers; - self := new_actor("subscriber " & integer'image(i)); - publisher := find("publisher"); - subscribe(self, publisher); - receive(net, self, msg); - check(sender(msg) = find("publisher")); - check(receiver(msg) = self); - if check_equal(pop_string(msg), "hello subscriber") then - hello_subscriber_received(i) <= '1'; - hello_subscriber_received(3 - i) <= 'Z'; - end if; - wait; - end process; - end generate subscribers; - - server2 : process is - variable self : actor_t; - variable request_msg1, request_msg2, request_msg3 : msg_t; - variable reply_msg : msg_t; - begin - wait until start_server2; - self := new_actor("server2"); - - receive(net, self, request_msg1); - check_equal(pop_string(request_msg1), "request1"); - - receive(net, self, request_msg2); - check_equal(pop_string(request_msg2), "request2"); - - reply_msg := new_msg; - push_string(reply_msg, "reply2"); - reply(net, request_msg2, reply_msg); - check(reply_msg.sender = self); - check(reply_msg.receiver = find("test runner")); - - acknowledge(net, request_msg1, false); - - receive(net, self, request_msg3); - check_equal(pop_string(request_msg3), "request3"); - - acknowledge(net, request_msg3, true); - wait; - end process server2; - - server3 : process is - variable self : actor_t; - variable request_msg, reply_msg : msg_t; - begin - wait until start_server3; - self := new_actor("server3"); - - receive(net, self, request_msg); - check_equal(pop_string(request_msg), "request1"); - reply_msg := new_msg; - push_string(reply_msg, "reply1"); - reply(net, request_msg, reply_msg); - - receive(net, self, request_msg); - check_equal(pop_string(request_msg), "request2"); - acknowledge(net, request_msg, true); - - receive(net, self, request_msg); - check_equal(pop_string(request_msg), "request3"); - acknowledge(net, request_msg, false); - - wait; - end process server3; - - server4 : process is - variable self : actor_t; - variable request_msg, reply_msg : msg_t; - begin - wait until start_server4; - self := new_actor("server4", 1); - - receive(net, self, request_msg); - receive(net, self, request_msg); - receive(net, self, request_msg); - reply_msg := new_msg; - push_string(reply_msg, "reply3"); - reply(net, request_msg, reply_msg); - receive(net, self, request_msg); - reply_msg := new_msg; - push_string(reply_msg, "reply4"); - reply(net, request_msg, reply_msg); - wait; - end process server4; - - server5 : process is - variable self : actor_t; - variable request_msg, reply_msg : msg_t; - begin - wait until start_server5; - self := new_actor("server5"); - - receive(net, self, request_msg); - check_equal(pop_string(request_msg), "request"); - reply_msg := new_msg; - push_string(reply_msg, "reply"); - reply(net, request_msg, reply_msg); - - receive(net, self, request_msg); - check_equal(pop_string(request_msg), "request2"); - reply_msg := new_msg; - push_string(reply_msg, "reply2"); - wait for 10 ns; - reply(net, request_msg, reply_msg); - - receive(net, self, request_msg); - check_equal(pop_string(request_msg), "request3"); - reply_msg := new_msg; - push_string(reply_msg, "reply3"); - reply(net, request_msg, reply_msg); - - wait; - end process server5; - - server6 : process is - variable self : actor_t; - variable request_msg, reply_msg : msg_t; - begin - wait until start_server6; - self := new_actor("server6"); - loop - receive(net, self, request_msg); - reply_msg := new_msg; - push_string(reply_msg, "reply to " & pop_string(request_msg)); - reply(net, request_msg, reply_msg); - end loop; - end process server6; - - limited_inbox_actor : process is - variable self : actor_t; - variable msg : msg_t; - begin - wait until start_limited_inbox; - self := new_actor("limited inbox", 2); - wait for 10 ns; - receive(net, self, msg); - receive(net, self, msg); - receive(net, self, msg); - limited_inbox_actor_done <= true; - wait; - end process limited_inbox_actor; - - limited_inbox_subscriber : process is - variable self : actor_t; - variable msg : msg_t; - begin - wait until start_limited_inbox_subscriber; - self := new_actor("limited inbox subscriber", 1); - subscribe(self, find("test runner")); - wait for 10 ns; - receive(net, self, msg); - wait; - end process limited_inbox_subscriber; - - publishers : for i in 0 to 2 generate - process is - variable self : actor_t; - variable msg : msg_t; - begin - wait until start_publishers; - self := new_actor("publisher " & integer'image(i)); - msg := new_msg; - push_string(msg, name(self)); - publish(net, self, msg); - wait; - end process; - end generate publishers; - -end test_fixture; +-- Test suite for com package +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +use vunit_lib.queue_pkg.all; + +library ieee; +use ieee.std_logic_1164.all; + +use std.textio.all; + +entity tb_com is + generic( + runner_cfg : string); +end entity tb_com; + +architecture test_fixture of tb_com is + signal hello_world_received, start_receiver, start_server : boolean := false; + signal start_server2, start_server3, start_server4, start_server5 : boolean := false; + signal start_server6, start_subscribers, start_publishers : boolean := false; + signal hello_subscriber_received : std_logic_vector(1 to 2) := "ZZ"; + signal start_limited_inbox, limited_inbox_actor_done : boolean := false; + signal start_limited_inbox_subscriber : boolean := false; + + constant com_logger : logger_t := get_logger("vunit_lib:com"); +begin + test_runner : process + variable self, actor, actor2, actor3, actor4 : actor_t; + variable actor5, my_receiver, my_sender, server, publisher : actor_t; + variable publisher2, subscriber, subscriber2, subscriber3 : actor_t; + variable actor_vec : actor_vec_t(0 to 2); + variable status : com_status_t; + variable n_actors : natural; + variable t_start, t_stop : time; + variable ack : boolean; + variable msg, msg2, msg3, msg4 : msg_t; + variable request_msg, request_msg2, request_msg3, reply_msg : msg_t; + variable peeked_msg1, peeked_msg2 : msg_t; + variable msg_vec_ptr : msg_vec_ptr_t; + variable deprecated_message : message_ptr_t; + variable subscription_vec_ptr : subscription_vec_ptr_t; + variable actor_state : actor_state_t; + variable mailbox_state : mailbox_state_t; + variable l : line; + variable messenger_state : messenger_state_t; + variable null_mailbox_state : mailbox_state_t := (inbox, 0, null); + variable null_actor_state : actor_state_t := (null, + false, + null_mailbox_state, + null_mailbox_state, + null, null + ); + variable null_messenger_state : messenger_state_t := (null, null); + + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + reset_messenger; + self := new_actor("test runner"); + + -- Create + if run("Test that named actors can be created") then + n_actors := num_of_actors; + actor := new_actor("actor"); + check(actor /= null_actor, "Failed to create named actor"); + check_equal(name(actor), "actor"); + check_equal(num_of_actors, n_actors + 1, "Expected one extra actor"); + check(new_actor("other actor").id /= new_actor("another actor").id, "Failed to create unique actors"); + check_equal(num_of_actors, n_actors + 3, "Expected two extra actors"); + elsif run("Test that no name actors can be created") then + actor := new_actor; + check(actor /= null_actor, "Failed to create no name actor"); + check_equal(name(actor), ""); + elsif run("Test that the null actor has no name") then + check_equal(name(null_actor), ""); + elsif run("Test that two actors of the same name cannot be created") then + actor := new_actor("actor2"); + mock(com_logger); + actor := new_actor("actor2"); + check_only_log(com_logger, "DUPLICATE ACTOR NAME ERROR.", failure); + unmock(com_logger); + elsif run("Test that multiple no-name actors can be created") then + n_actors := num_of_actors; + actor := new_actor; + actor2 := new_actor; + check(actor.id /= actor2.id, "The two actors must have different identities"); + check_equal(num_of_actors, n_actors + 2); + check_equal(num_of_deferred_creations, 0); + + -- Find + elsif run("Test that a created actor can be found") then + actor := new_actor("actor to be found"); + check(find("actor to be found", false) /= null_actor, "Failed to find created actor"); + check_equal(num_of_deferred_creations, 0, "Expected no deferred creations"); + check_false(is_deferred(actor)); + elsif run("Test that an actor not created is found and its creation is deferred") then + check_equal(num_of_deferred_creations, 0, "Expected no deferred creations"); + actor := find("actor with deferred creation"); + check(actor /= null_actor, "Failed to find actor with deferred creation"); + check_equal(num_of_deferred_creations, 1, "Expected one deferred creations"); + check(is_deferred(actor)); + elsif run("Test that deferred creation can be suppressed when an actor is not found") then + actor := new_actor("actor"); + actor2 := find("actor with deferred creation", false); + check(actor2 = null_actor, "Didn't expect to find any actor"); + check_equal(num_of_deferred_creations, 0, "Expected no deferred creations"); + elsif run("Test that a created actor get the correct mailbox size") then + actor := new_actor("actor with max inbox"); + check_equal(mailbox_size(actor), positive'high, result("for inbox size")); + check_equal(mailbox_size(actor, outbox), positive'high, result("for outbox size")); + + actor2 := new_actor("actor with bounded inbox", 23, 17); + check_equal(mailbox_size(actor2), 23, result("for inbox size")); + check_equal(mailbox_size(actor2, outbox), 17, result("for outbox size")); + + check_equal(mailbox_size(null_actor), 0, result("for inbox size")); + check_equal(mailbox_size(null_actor, outbox), 0, result("for outbox size")); + + check_equal(mailbox_size(find("actor to be created")), 1, result("for inbox size")); + check_equal(mailbox_size(find("actor to be created"), outbox), positive'high, result("for outbox size")); + + check_equal(mailbox_size(new_actor("actor to be created", 42, 99)), 42, result("for inbox size")); + check_equal(mailbox_size(find("actor to be created"), outbox), 99, result("for outbox size")); + + elsif run("Test that mailboxes can be resize") then + actor := new_actor("actor with max inbox"); + + resize_mailbox(actor, 17); + check_equal(mailbox_size(actor), 17, result("for inbox size")); + check_equal(mailbox_size(actor, outbox), positive'high, result("for outbox size")); + + resize_mailbox(actor, 23, outbox); + check_equal(mailbox_size(actor), 17, result("for inbox size")); + check_equal(mailbox_size(actor, outbox), 23, result("for outbox size")); + + for i in 1 to 10 loop + msg := new_msg; + send(net, actor, msg); + end loop; + + for i in 1 to 2 loop + receive(net, actor, request_msg); + reply_msg := new_msg; + reply(net, request_msg, reply_msg); + end loop; + + resize_mailbox(actor, 8); + mock(com_logger); + resize_mailbox(actor, 7); + check_only_log(com_logger, "INSUFFICIENT SIZE ERROR.", failure); + unmock(com_logger); + + resize_mailbox(actor, 2, outbox); + mock(com_logger); + resize_mailbox(actor, 1, outbox); + check_only_log(com_logger, "INSUFFICIENT SIZE ERROR.", failure); + unmock(com_logger); + + elsif run("Test that no-name actors can't be found") then + actor := new_actor; + actor2 := new_actor; + check(find("") = null_actor, "Must not find a no-name actor"); + check_equal(num_of_deferred_creations, 0); + + -- Destroy + elsif run("Test that a created actor can be destroyed") then + actor := new_actor("actor to destroy"); + actor2 := new_actor("actor to keep"); + n_actors := num_of_actors; + destroy(actor); + check(num_of_actors = n_actors - 1, "Expected one less actor"); + check(actor = null_actor, "Destroyed actor should be nullified"); + check(find("actor to destroy", false) = null_actor, "A destroyed actor should not be found"); + check(find("actor to keep", false) /= null_actor, + "Actors other than the one destroyed must not be affected"); + elsif run("Test that a non-existing actor cannot be destroyed") then + actor := null_actor; + mock(com_logger); + destroy(actor); + check_only_log(com_logger, "UNKNOWN ACTOR ERROR.", failure); + unmock(com_logger); + elsif run("Test that all actors can be destroyed") then + reset_messenger; + actor := new_actor("actor to destroy"); + actor2 := new_actor("actor to destroy 2"); + check(num_of_actors = 2, "Expected two actors"); + reset_messenger; + check(num_of_actors = 0, "Failed to destroy all actors"); + + -- Copy and delete message + elsif run("Test that a message can be deleted") then + my_sender := new_actor("my sender"); + my_receiver := new_actor("my receiver"); + + msg := new_msg; + msg.id := 17; + msg.status := timeout; + msg.sender := my_sender; + msg.receiver := my_receiver; + msg.request_id := 21; + push_string(msg, "hello"); + + delete(msg); + + check_equal(msg.id, no_message_id); + check(msg.status = null_message_error); + check(msg.sender = null_actor); + check(msg.receiver = null_actor); + check_equal(msg.request_id, no_message_id); + check(msg.data = null_queue); + + elsif run("Test that a message can be copied") then + my_sender := new_actor("my sender"); + my_receiver := new_actor("my receiver"); + + msg := new_msg; + msg.id := 17; + msg.status := timeout; + msg.sender := my_sender; + msg.receiver := my_receiver; + msg.request_id := 21; + push_string(msg, "hello"); + + msg2 := copy(msg); + push_string(msg, "world"); + delete(msg); + msg := new_msg; + push_string(msg, "peanuts"); + + check_equal(msg2.id, 17); + check(msg2.status = timeout); + check(msg2.sender = my_sender); + check(msg2.receiver = my_receiver); + check_equal(msg2.request_id, 21); + check_equal(pop_string(msg2), "hello"); + + -- to_string + elsif run("Test string representation of message") then + my_sender := new_actor("my sender"); + my_receiver := new_actor("my receiver"); + + msg := new_msg; + check_equal(to_string(msg), "-:- - -> - (-)"); + msg := new_msg(new_msg_type("msg type")); + check_equal(to_string(msg), "-:- - -> - (msg type)"); + msg.id := 1; + check_equal(to_string(msg), "1:- - -> - (msg type)"); + msg.sender := my_sender; + check_equal(to_string(msg), "1:- my sender -> - (msg type)"); + msg.receiver := my_receiver; + check_equal(to_string(msg), "1:- my sender -> my receiver (msg type)"); + msg.request_id := 7; + check_equal(to_string(msg), "1:7 my sender -> my receiver (msg type)"); + + -- Send and receive + elsif run("Test that data ownership is lost at send") then + msg := new_msg; + push_string(msg, "hello"); + send(net, self, msg); + check(msg.data = null_queue); + elsif run("Test that an actor can send a message to another actor") then + start_receiver <= true; + wait for 1 ns; + my_receiver := find("my_receiver"); + msg := new_msg(sender => self); + push_string(msg, "hello world"); + send(net, my_receiver, msg); + check(msg.sender = self); + check(msg.receiver = my_receiver); + wait until hello_world_received for 1 ns; + check(hello_world_received, "Expected ""hello world"" to be received at the server"); + elsif run("Test that an actor can send a reply to a message from an a priori unknown actor") then + start_server <= true; + wait for 1 ns; + server := find("server"); + request_msg := new_msg(sender => self); + push_string(request_msg, "request"); + send(net, server, request_msg); + receive(net, self, reply_msg); + check(reply_msg.status = ok, "Expected no receive problems"); + check_equal(pop_string(reply_msg), "request acknowledge"); + elsif run("Test that an actor can send a message to itself") then + msg := new_msg; + push_string(msg, "hello"); + send(net, self, msg); + receive(net, self, msg2); + check(msg2.status = ok, "Expected no receive problems"); + check_equal(pop_string(msg2), "hello"); + elsif run("Test that no-name actors can communicate") then + actor := new_actor; + msg := new_msg; + push_string(msg, "hello"); + send(net, actor, msg); + receive(net, actor, msg2); + check_equal(pop_string(msg2), "hello"); + elsif run("Test that an actor can poll for incoming messages") then + wait_for_message(net, self, status, 0 ns); + check(status = timeout, "Expected timeout"); + msg := new_msg(sender => self); + push_string(msg, "hello again"); + send(net, self, msg); + wait_for_message(net, self, status, 0 ns); + check(status = ok, "Expected ok status"); + get_message(net, self, msg2); + check(msg2.status = ok, "Expected no problems with receive"); + check_equal(pop_string(msg2), "hello again"); + check(msg2.sender = self, "Expected message from myself"); + elsif run("Test that sending to a non-existing actor results in an error") then + msg := new_msg; + push_string(msg, "hello"); + mock(com_logger); + send(net, null_actor, msg); + check_only_log(com_logger, "UNKNOWN RECEIVER ERROR.", failure); + unmock(com_logger); + elsif run("Test that an actor can send to an actor with deferred creation") then + actor := find("deferred actor"); + msg := new_msg; + push_string(msg, "hello actor to be created"); + send(net, actor, msg); + actor := new_actor("deferred actor"); + receive(net, actor, msg2); + check(msg2.status = ok, "Expected no problems with receive"); + check_equal(pop_string(msg2), "hello actor to be created"); + elsif run("Test that receiving from an actor with deferred creation results in an error") then + actor := find("deferred actor"); + mock(com_logger); + receive(net, actor, msg); + check_log(com_logger, "DEFERRED RECEIVER ERROR.", failure); + check_only_log(com_logger, "DEFERRED RECEIVER ERROR.", failure); + unmock(com_logger); + elsif run("Test that empty messages can be sent") then + msg := new_msg; + send(net, self, msg); + receive(net, self, msg2); + check(msg2.status = ok, "Expected no problems with receive"); + check_equal(length(msg2.data), 0); + elsif run("Test that each sent message gets an increasing message number") then + msg := new_msg; + send(net, self, msg); + check(msg.id = 1, "Expected first receipt id to be 1"); + msg := new_msg; + send(net, self, msg); + check(msg.id = 2, "Expected first receipt id to be 2"); + receive(net, self, msg2); + check(msg2.id = 1, "Expected first message id to be 1"); + receive(net, self, msg2); + check(msg2.id = 2, "Expected first message id to be 2"); + elsif run("Test that each published message gets an increasing message number") then + for i in actor_vec'range loop + actor_vec(i) := new_actor; + subscribe(actor_vec(i), self); + end loop; + + for j in 1 to 3 loop + msg := new_msg; + publish(net, self, msg); + for i in actor_vec'range loop + receive(net, actor_vec(i), msg); + check_equal(msg.id, j); + end loop; + end loop; + elsif run("Test that a limited-inbox receiver can receive as expected without blocking") then + start_limited_inbox <= true; + actor := find("limited inbox"); + t_start := now; + msg := new_msg; + push_string(msg, "First message"); + send(net, actor, msg); + t_stop := now; + check_equal(t_stop - t_start, 0 ns, "Expected no blocking on first message"); + t_start := now; + msg := new_msg; + push_string(msg, "Second message"); + send(net, actor, msg, 0 ns); + t_stop := now; + check_equal(t_stop - t_start, 0 ns, "Expected no blocking on second message"); + t_start := now; + msg := new_msg; + push_string(msg, "Third message"); + send(net, actor, msg, 11 ns); + t_stop := now; + check_equal(t_stop - t_start, 10 ns, "Expected a 10 ns blocking period on third message"); + + wait until limited_inbox_actor_done; + elsif run("Test that sending to a limited-inbox receiver times out as expected") then + start_limited_inbox <= true; + actor := find("limited inbox"); + msg := new_msg; + push_string(msg, "First message"); + send(net, actor, msg); + msg := new_msg; + push_string(msg, "Second message"); + send(net, actor, msg, 0 ns); + msg := new_msg; + push_string(msg, "Third message"); + mock(com_logger); + send(net, actor, msg, 9 ns); + check_log(com_logger, "FULL INBOX ERROR.", failure); + check_only_log(com_logger, "[3:- - -> limited inbox (-)] => limited inbox inbox", trace); + unmock(com_logger); + elsif run("Test that messages can be awaited from several actors") then + actor := new_actor; + actor2 := new_actor; + msg := new_msg; + push_string(msg, "To actor"); + send(net, actor, msg); + wait_for_message(net, actor_vec_t'(actor, actor2), status); + check(status = ok, "Expected ok status"); + check_true(has_message(actor)); + check_false(has_message(actor2)); + get_message(net, actor, msg); + check_equal(pop_string(msg), "To actor"); + msg := new_msg; + push_string(msg, "To actor2"); + send(net, actor2, msg); + wait_for_message(net, actor_vec_t'(actor, actor2), status); + check(status = ok, "Expected ok status"); + check_true(has_message(actor2)); + check_false(has_message(actor)); + get_message(net, actor2, msg); + check_equal(pop_string(msg), "To actor2"); + elsif run("Test sending to several actors") then + actor_vec := (new_actor, new_actor, new_actor); + for n in 0 to 2 loop + msg := new_msg; + push_string(msg, "hello"); + send(net, actor_vec(1 to n), msg); + check(msg.data = null_queue); + for i in 1 to n loop + receive(net, actor_vec(i), msg, 0 ns); + check_equal(pop_string(msg), "hello"); + end loop; + end loop; + elsif run("Test sending to several actors with timeout") then + actor_vec := (new_actor(inbox_size => 1), new_actor(inbox_size => 1), new_actor(inbox_size => 1)); + msg := new_msg; + push_string(msg, "hello"); + send(net, actor_vec, msg); + mock(com_logger); + msg := new_msg; + push_string(msg, "hello"); + t_start := now; + send(net, actor_vec, msg, 10 ns); + check_equal(now - t_start, 10 ns); + check_log(com_logger, "FULL INBOX ERROR.", failure); + check_log(com_logger, "[4:- - -> (-)] => inbox", trace); + check_log(com_logger, "FULL INBOX ERROR.", failure); + check_log(com_logger, "[5:- - -> (-)] => inbox", trace); + check_log(com_logger, "FULL INBOX ERROR.", failure); + check_log(com_logger, "[6:- - -> (-)] => inbox", trace); + unmock(com_logger); + elsif run("Test receiving from several actors") then + for i in 0 to 2 loop + actor_vec(i) := new_actor; + subscribe(actor_vec(i), find("publisher " & to_string(i))); + end loop; + start_publishers <= true; + receive(net, actor_vec(0 to 0), msg); + check_equal(name(msg.sender), pop_string(msg)); + for i in 1 to 2 loop + receive(net, actor_vec(1 to 2), msg); + check_equal(name(msg.sender), pop_string(msg)); + end loop; + elsif run("Test that the sender and the receiver of a message can be retrieved") then + actor := new_actor; + actor2 := new_actor; + msg := new_msg(sender => actor2); + push_string(msg, "To actor"); + send(net, actor, msg); + receive(net, actor, msg); + check(sender(msg) = actor2); + check(receiver(msg) = actor); + msg := new_msg; + push_string(msg, "To actor"); + send(net, actor, msg); + receive(net, actor, msg); + check(sender(msg) = null_actor); + check(receiver(msg) = actor); + elsif run("Test that get_message will wake up sender blocking on full inbox") then + actor := new_actor("actor", 1); + start_server6 <= true; + wait for 1 ns; + server := find("server6"); + + request_msg := new_msg(sender => actor); + push_string(request_msg, "request1"); + send(net, server, request_msg); + + request_msg2 := new_msg(sender => actor); + push_string(request_msg2, "request2"); + send(net, server, request_msg2); + + wait_for_message(net, actor, status); + get_message(net, actor, reply_msg); + check_equal(pop_string(reply_msg), "reply to request1"); + + wait_for_message(net, actor, status); + get_message(net, actor, reply_msg); + check_equal(pop_string(reply_msg), "reply to request2"); + + elsif run("Test that a message can be forwarded") then + actor := new_actor("actor"); + actor2 := new_actor("actor2"); + actor3 := new_actor("actor3"); + msg := new_msg(sender => actor); + push_string(msg, "request"); + send(net, actor2, msg); + receive(net, actor2, msg2); + msg3 := new_msg; + push_msg_t(msg3, msg2); + send(net, actor3, msg3); + receive(net, actor3, msg3); + msg2 := new_msg; + push_string(msg2, "reply"); + msg4 := pop_msg_t(msg3); + reply(net, msg4, msg2); + receive_reply(net, msg, msg3); + check_equal(pop_string(msg3), "reply"); + check(sender(msg3) = actor2); + check(receiver(msg3) = actor); + + -- Publish, subscribe, and unsubscribe + elsif run("Test that an actor can publish messages to multiple subscribers") then + publisher := new_actor("publisher"); + start_subscribers <= true; + wait for 1 ns; + msg := new_msg; + push_string(msg, "hello subscriber"); + publish(net, publisher, msg); + check(msg.sender = publisher); + check(msg.receiver = null_actor); + wait until hello_subscriber_received = "11" for 1 ns; + check(hello_subscriber_received = "11", "Expected ""hello subscribers"" to be received at the subscribers"); + elsif run("Test that subscribers receive messages sent on outbound subscription") then + my_sender := new_actor; + my_receiver := new_actor; + subscribe(self, my_sender, outbound); + + msg := new_msg(sender => my_sender); + push_string(msg, "hello"); + send(net, my_receiver, msg); + + receive(net, my_receiver, msg2, 0 ns); + check(sender(msg2) = my_sender); + check(receiver(msg2) = my_receiver); + check_equal(pop_string(msg2), "hello"); + + receive(net, self, msg2, 0 ns); + check(sender(msg2) = my_sender); + check(receiver(msg2) = my_receiver); + check_equal(pop_string(msg2), "hello"); + + msg := new_msg; + push_string(msg, "hello2"); + publish(net, my_sender, msg); + + receive(net, self, msg2, 0 ns); + check(sender(msg2) = my_sender); + check(receiver(msg2) = self); + check_equal(pop_string(msg2), "hello2"); + + elsif run("Test that subscribers don't receive duplicate message") then + publisher := new_actor("publisher"); + subscribe(self, publisher); + + msg := new_msg(sender => publisher); + push_string(msg, "hello"); + send(net, self, msg); + + receive(net, self, msg2, 0 ns); + check(msg2.receiver = self); + wait_for_message(net, self, status, 0 ns); + check(status = timeout, "Expected only one message"); + elsif run("Test that actors don't get send messages on a publish subscription") then + publisher := new_actor("publisher"); + subscribe(self, publisher); + + msg := new_msg(sender => publisher); + push_string(msg, "hello"); + send(net, publisher, msg); + + wait_for_message(net, self, status, 0 ns); + check(status = timeout, "Expected no message"); + elsif run("Test that actors can subscribe to inbound traffic") then + my_receiver := new_actor; + subscribe(self, my_receiver, inbound); + + msg := new_msg; + push_string(msg, "hello"); + send(net, my_receiver, msg); + + receive(net, my_receiver, msg2, 0 ns); + check(sender(msg2) = null_actor); + check(receiver(msg2) = my_receiver); + check_equal(pop_string(msg2), "hello"); + + receive(net, self, msg2, 0 ns); + check(sender(msg2) = null_actor); + check(receiver(msg2) = my_receiver); + check_equal(pop_string(msg2), "hello"); + + msg := new_msg; + push_string(msg, "publication"); + publish(net, my_receiver, msg); + + wait_for_message(net, self, status, 0 ns); + check(status = timeout, "Expected no message"); + + actor := new_actor("actor"); + msg := new_msg(sender => my_receiver); + push_string(msg, "hello"); + + send(net, actor, msg); + wait_for_message(net, self, status, 0 ns); + check(status = timeout, "Expected no message"); + elsif run("Test request/reply with actor having inbound subscribers") then + subscriber := new_actor("subscriber"); + start_server5 <= true; + wait for 1 ns; + server := find("server5"); + subscribe(subscriber, server, inbound); + + request_msg := new_msg(sender => self); + push_string(request_msg, "request"); + send(net, server, request_msg); + + receive_reply(net, request_msg, reply_msg, 100 ns); + check_equal(pop_string(reply_msg), "reply"); + + receive(net, subscriber, reply_msg, 0 ns); + check_equal(pop_string(reply_msg), "request"); + elsif run("Test chained subscribers") then + my_sender := new_actor; + my_receiver := new_actor; + subscriber := new_actor; + subscribe(self, my_sender, outbound); + subscribe(subscriber, self, inbound); + + msg := new_msg(sender => my_sender); + push_string(msg, "hello"); + send(net, my_receiver, msg); + + receive(net, my_receiver, msg2, 0 ns); + check(sender(msg2) = my_sender); + check(receiver(msg2) = my_receiver); + check_equal(pop_string(msg2), "hello"); + + receive(net, self, msg2, 0 ns); + check(sender(msg2) = my_sender); + check(receiver(msg2) = my_receiver); + check_equal(pop_string(msg2), "hello"); + + receive(net, subscriber, msg2, 0 ns); + check(sender(msg2) = my_sender); + check(receiver(msg2) = my_receiver); + check_equal(pop_string(msg2), "hello"); + + msg := new_msg; + push_string(msg, "hello2"); + publish(net, my_sender, msg); + + receive(net, self, msg2, 0 ns); + check(sender(msg2) = my_sender); + check(receiver(msg2) = self); + check_equal(pop_string(msg2), "hello2"); + + receive(net, subscriber, msg2, 0 ns); + check(sender(msg2) = my_sender); + check(receiver(msg2) = self, "Got: " & name(receiver(msg2))); + check_equal(pop_string(msg2), "hello2"); + + elsif run("Test that a subscriber can unsubscribe") then + subscribe(self, self, published); + subscribe(self, self, inbound); + unsubscribe(self, self, inbound); + msg := new_msg; + push_string(msg, "hello subscriber"); + publish(net, self, msg); + receive(net, self, msg, 0 ns); + check_equal(pop_string(msg), "hello subscriber"); + unsubscribe(self, self, published); + msg := new_msg; + push_string(msg, "hello subscriber"); + publish(net, self, msg); + wait_for_message(net, self, status, 0 ns); + check(status = timeout, "Expected no message"); + elsif run("Test that a destroyed subscriber is not addressed by the publisher") then + subscriber := new_actor("subscriber"); + subscribe(subscriber, self); + msg := new_msg; + push_string(msg, "hello subscriber"); + publish(net, self, msg); + receive(net, subscriber, msg, 0 ns); + check_equal(pop_string(msg), "hello subscriber"); + destroy(subscriber); + push_string(msg, "hello subscriber"); + publish(net, self, msg); + elsif run("Test that an actor can only subscribe once to the same publisher") then + subscribe(self, self); + mock(com_logger); + subscribe(self, self); + check_only_log(com_logger, "ALREADY A SUBSCRIBER ERROR.", failure); + unmock(com_logger); + elsif run("Test that publishing to subscribers with full inboxes results is an error") then + start_limited_inbox_subscriber <= true; + wait for 1 ns; + msg := new_msg; + push_string(msg, "hello subscriber"); + publish(net, self, msg); + msg := new_msg; + push_string(msg, "hello subscriber"); + mock(com_logger); + publish(net, self, msg, 8 ns); + check_log(com_logger, "FULL INBOX ERROR.", failure); + check_only_log(com_logger, + "[2:- test runner -> limited inbox subscriber (-)] => limited inbox subscriber inbox", + trace); + unmock(com_logger); + elsif run("Test that publishing to subscribers with full inboxes results passes if waiting") then + start_limited_inbox_subscriber <= true; + wait for 1 ns; + msg := new_msg; + push_string(msg, "hello subscriber"); + publish(net, self, msg); + msg := new_msg; + push_string(msg, "hello subscriber"); + publish(net, self, msg, 11 ns); + + -- Request, (receive_)reply and acknowledge + elsif run("Test that a client can wait for an out-of-order request reply") then + start_server2 <= true; + server := find("server2"); + + request_msg := new_msg(sender => self); + push_string(request_msg, "request1"); + send(net, server, request_msg); + + request_msg2 := new_msg(sender => self); + push_string(request_msg2, "request2"); + send(net, server, request_msg2); + + receive_reply(net, request_msg, ack); + check_false(ack, "Expected negative acknowledgement"); + + request_msg3 := new_msg(sender => self); + push_string(request_msg3, "request3"); + send(net, server, request_msg3); + + receive_reply(net, request_msg3, ack); + check(ack, "Expected positive acknowledgement"); + + receive_reply(net, request_msg2, reply_msg); + check(reply_msg.sender = server); + check(reply_msg.receiver = self); + check_equal(pop_string(reply_msg), "reply2"); + check_equal(reply_msg.request_id, request_msg2.id); + + elsif run("Test that a synchronous request can be made") then + start_server3 <= true; + server := find("server3"); + + request_msg := new_msg(sender => self); + push_string(request_msg, "request1"); + request(net, server, request_msg, reply_msg); + check_equal(pop_string(reply_msg), "reply1"); + + request_msg := new_msg(sender => self); + push_string(request_msg, "request2"); + request(net, server, request_msg, ack); + check(ack, "Expected positive acknowledgement"); + + request_msg := new_msg(sender => self); + push_string(request_msg, "request3"); + request(net, server, request_msg, ack); + check_false(ack, "Expected negative acknowledgement"); + elsif run("Test that waiting and getting a reply with timeout works") then + start_server4 <= true; + server := find("server4"); + + t_start := now; + request_msg := new_msg(sender => self); + push_string(request_msg, "request1"); + send(net, server, request_msg); + wait_for_reply(net, request_msg, status, 2 ns); + check(status = timeout, "Expected timeout"); + check_equal(now - t_start, 2 ns); + + t_start := now; + request_msg := new_msg; + push_string(request_msg, "request2"); + send(net, server, request_msg); + wait_for_reply(net, request_msg, status, 2 ns); + check(status = timeout, "Expected timeout"); + check_equal(now - t_start, 2 ns); + + request_msg := new_msg(sender => self); + push_string(request_msg, "request3"); + send(net, server, request_msg); + wait_for_reply(net, request_msg, status); + get_reply(net, request_msg, reply_msg); + check_equal(pop_string(reply_msg), "reply3"); + + t_start := now; + request_msg := new_msg; + push_string(request_msg, "request4"); + send(net, server, request_msg); + wait_for_reply(net, request_msg, status); + get_reply(net, request_msg, reply_msg); + check_equal(pop_string(reply_msg), "reply4"); + elsif run("Test waiting and getting a reply out-of-order") then + start_server2 <= true; + server := find("server2"); + + request_msg := new_msg(sender => self); + push_string(request_msg, "request1"); + send(net, server, request_msg); + + request_msg2 := new_msg(sender => self); + push_string(request_msg2, "request2"); + send(net, server, request_msg2); + + wait_for_reply(net, request_msg, status); + check(status = ok); + + get_reply(net, request_msg, reply_msg); + check_false(pop_boolean(reply_msg)); + + request_msg3 := new_msg(sender => self); + push_string(request_msg3, "request3"); + send(net, server, request_msg3); + + wait_for_reply(net, request_msg3, status); + check(status = ok); + + get_reply(net, request_msg3, reply_msg); + check(pop_boolean(reply_msg)); + + wait_for_reply(net, request_msg2, status); + check(status = ok); + + get_reply(net, request_msg2, reply_msg); + check_equal(pop_string(reply_msg), "reply2"); + + elsif run("Test that an anonymous request can be made") then + start_server5 <= true; + server := find("server5"); + + request_msg := new_msg; + push_string(request_msg, "request"); + send(net, server, request_msg); + wait for 10 ns; + receive_reply(net, request_msg, reply_msg); + check(reply_msg.sender = server); + check(reply_msg.receiver = null_actor); + check_equal(pop_string(reply_msg), "reply"); + check_equal(reply_msg.request_id, request_msg.id); + + request_msg := new_msg; + push_string(request_msg, "request2"); + send(net, server, request_msg); + receive_reply(net, request_msg, reply_msg); + check_equal(pop_string(reply_msg), "reply2"); + + request_msg := new_msg; + push_string(request_msg, "request3"); + send(net, server, request_msg); + receive_reply(net, request_msg, reply_msg); + check_equal(pop_string(reply_msg), "reply3"); + + elsif run("Test that get_reply will wake up sender blocking on full inbox") then + actor := new_actor("actor", 1); + start_server6 <= true; + wait for 1 ns; + server := find("server6"); + + request_msg := new_msg(sender => actor); + push_string(request_msg, "request1"); + send(net, server, request_msg); + + request_msg2 := new_msg(sender => actor); + push_string(request_msg2, "request2"); + send(net, server, request_msg2); + + receive_reply(net, request_msg, reply_msg); + check_equal(pop_string(reply_msg), "reply to request1"); + + receive_reply(net, request_msg2, reply_msg); + check_equal(pop_string(reply_msg), "reply to request2"); + + -- Timeout + elsif run("Test that timeout on receive leads to an error") then + mock(com_logger); + receive(net, self, msg, 1 ns); + check_only_log(com_logger, "TIMEOUT.", failure); + unmock(com_logger); + + -- Debugging + elsif run("Test getting the number of messages in a mailbox") then + check_equal(num_of_messages(self), 0); + msg := new_msg; + send(net, self, msg); + check_equal(num_of_messages(self), 1); + msg := new_msg; + send(net, self, msg); + check_equal(num_of_messages(self), 2); + receive(net, self, msg); + check_equal(num_of_messages(self), 1); + receive(net, self, msg); + check_equal(num_of_messages(self), 0); + + check_equal(num_of_messages(self, outbox), 0); + msg := new_msg; + send(net, self, msg); + receive(net, self, request_msg); + reply_msg := new_msg; + reply(net, request_msg, reply_msg); + check_equal(num_of_messages(self, outbox), 1); + msg2 := new_msg; + send(net, self, msg2); + receive(net, self, request_msg); + reply_msg := new_msg; + reply(net, request_msg, reply_msg); + check_equal(num_of_messages(self, outbox), 2); + receive_reply(net, msg, reply_msg); + check_equal(num_of_messages(self, outbox), 1); + receive_reply(net, msg2, reply_msg); + check_equal(num_of_messages(self, outbox), 0); + + elsif run("Test peeking at messages in a mailbox") then + actor := new_actor; + mock(com_logger); + peeked_msg1 := peek_message(actor); + check_only_log(com_logger, "Peeking non-existing position.", failure); + unmock(com_logger); + + msg := new_msg; + send(net, actor, msg); + msg := new_msg; + send(net, actor, msg); + peeked_msg1 := peek_message(actor); + peeked_msg2 := peek_message(actor, 1); + receive(net, actor, msg); + check(peeked_msg1 = msg); + receive(net, actor, msg); + check(peeked_msg2 = msg); + + msg := new_msg; + send(net, actor, msg); + msg2 := new_msg; + send(net, actor, msg2); + + mock(com_logger); + peeked_msg1 := peek_message(actor, 0, outbox); + check_only_log(com_logger, "Peeking non-existing position.", failure); + unmock(com_logger); + + receive(net, actor, request_msg); + reply_msg := new_msg; + reply(net, request_msg, reply_msg); + receive(net, actor, request_msg); + reply_msg := new_msg; + reply(net, request_msg, reply_msg); + + peeked_msg1 := peek_message(actor, 0, outbox); + peeked_msg2 := peek_message(actor, 1, outbox); + + receive_reply(net, msg, reply_msg); + check(peeked_msg1 = reply_msg); + receive_reply(net, msg2, reply_msg); + check(peeked_msg2 = reply_msg); + + elsif run("Test getting and deallocating mailbox state") then + actor := new_actor("actor", 17, 23); + msg := new_msg; + send(net, actor, msg); + mailbox_state := get_mailbox_state(actor); + check(mailbox_state.id = inbox); + check(mailbox_state.size = 17); + receive(net, actor, request_msg); + check(mailbox_state.messages(0) = request_msg); + + reply_msg := new_msg; + reply(net, request_msg, reply_msg); + mailbox_state := get_mailbox_state(actor, outbox); + check(mailbox_state.id = outbox); + check(mailbox_state.size = 23); + receive_reply(net, msg, reply_msg); + check(mailbox_state.messages(0) = reply_msg); + + deallocate(mailbox_state); + check(mailbox_state = null_mailbox_state); + + elsif run("Test making a string of mailbox state") then + actor := new_actor("actor", 17); + check_equal(get_mailbox_state_string(actor, inbox), + "Mailbox: inbox" & LF & " Size: 17" & LF & " Messages:"); + + msg := new_msg(sender => self); + send(net, actor, msg); + msg := new_msg; + send(net, actor, msg); + mailbox_state := get_mailbox_state(actor); + write(l, get_mailbox_state_string(actor, inbox, " ")); + receive(net, actor, request_msg); + info(l.all); + check_equal( + l.all, + " Mailbox: inbox" & LF & " Size: 17" & LF & " Messages:" & LF & " 0. " & -- + to_string(mailbox_state.messages(0)) & LF & " 1. " & to_string(mailbox_state.messages(1)) + ); + + elsif run("Test getting and deallocating actor state") then + actor := find("my actor"); + actor_state := get_actor_state(actor); + check_equal(actor_state.name.all, "my actor"); + check(actor_state.is_deferred); + deallocate(actor_state); + check(actor_state = null_actor_state); + + actor := new_actor("my actor", 17, 21); + actor_state := get_actor_state(actor); + check_false(actor_state.is_deferred); + + msg := new_msg; + send(net, actor, msg); + actor_state := get_actor_state(actor); + receive(net, actor, request_msg); + check(actor_state.inbox.id = inbox); + check(actor_state.inbox.size = 17); + check(actor_state.inbox.messages(0) = request_msg); + + reply_msg := new_msg; + reply(net, request_msg, reply_msg); + actor_state := get_actor_state(actor); + receive_reply(net, msg, reply_msg); + check(actor_state.outbox.id = outbox); + check(actor_state.outbox.size = 21); + check(actor_state.outbox.messages(0) = reply_msg); + deallocate(actor_state); + check(actor_state = null_actor_state); + + actor2 := new_actor; + subscribe(actor, actor2, inbound); + subscribe(self, actor); + actor_state := get_actor_state(actor); + check(actor_state.subscriptions(0) = subscription_t'(subscriber => actor, + publisher => actor2, + traffic_type => inbound + ) + ); + check(actor_state.subscribers(0) = subscription_t'(subscriber => self, + publisher => actor, + traffic_type => published + ) + ); + deallocate(actor_state); + check(actor_state = null_actor_state); + + elsif run("Test making a string of actor state") then + actor := find("my actor"); + check_equal(get_actor_state_string(actor), + "Name: my actor" & LF & " Is deferred: yes" & LF & -- + get_mailbox_state_string(actor, inbox, " ") & LF & -- + get_mailbox_state_string(actor, outbox, " ") & LF & -- + " Subscriptions:" & LF & " Subscribers:"); + + actor := new_actor("my actor"); + msg := new_msg; + send(net, actor, msg); + + check_equal(get_actor_state_string(actor), + "Name: my actor" & LF & " Is deferred: no" & LF & -- + get_mailbox_state_string(actor, inbox, " ") & LF & -- + get_mailbox_state_string(actor, outbox, " ") & LF & -- + " Subscriptions:" & LF & " Subscribers:"); + + receive(net, actor, request_msg); + reply_msg := new_msg; + reply(net, request_msg, reply_msg); + + check_equal(get_actor_state_string(actor), + "Name: my actor" & LF & " Is deferred: no" & LF & -- + get_mailbox_state_string(actor, inbox, " ") & LF & -- + get_mailbox_state_string(actor, outbox, " ") & LF & -- + " Subscriptions:" & LF & " Subscribers:"); + + actor2 := new_actor("actor 2"); + subscribe(actor, actor2); + subscribe(actor, actor2, inbound); + subscribe(self, actor, inbound); + subscribe(self, actor, outbound); + + info(get_actor_state_string(actor)); + check_equal(get_actor_state_string(actor), + "Name: my actor" & LF & " Is deferred: no" & LF & -- + get_mailbox_state_string(actor, inbox, " ") & LF & -- + get_mailbox_state_string(actor, outbox, " ") & LF & -- + " Subscriptions:" & LF & " published traffic from actor 2" & LF & -- + " inbound traffic to actor 2" & LF & " Subscribers:" & LF & -- + " test runner subscribes to outbound traffic" & LF & -- + " test runner subscribes to inbound traffic"); + + elsif run("Test getting messenger and deallocating state") then + reset_messenger; + messenger_state := get_messenger_state; + check(messenger_state = null_messenger_state); + deallocate(messenger_state); + check(messenger_state = null_messenger_state); + + actor := new_actor("actor"); + messenger_state := get_messenger_state; + check_equal(messenger_state.active_actors'length, 1); + check(messenger_state.deferred_actors = null); + actor_state := get_actor_state(actor); + check_equal(messenger_state.active_actors(0).name.all, actor_state.name.all); + check_equal(messenger_state.active_actors(0).is_deferred, actor_state.is_deferred); + check(messenger_state.active_actors(0).inbox = actor_state.inbox); + check(messenger_state.active_actors(0).outbox = actor_state.outbox); + check(messenger_state.active_actors(0).subscriptions = actor_state.subscriptions); + check(messenger_state.active_actors(0).subscribers = actor_state.subscribers); + deallocate(messenger_state); + check(messenger_state = null_messenger_state); + + actor2 := new_actor("actor 2"); + messenger_state := get_messenger_state; + check_equal(messenger_state.active_actors'length, 2); + check(messenger_state.deferred_actors = null); + actor_state := get_actor_state(actor2); + check_equal(messenger_state.active_actors(1).name.all, actor_state.name.all); + check_equal(messenger_state.active_actors(1).is_deferred, actor_state.is_deferred); + check(messenger_state.active_actors(1).inbox = actor_state.inbox); + check(messenger_state.active_actors(1).outbox = actor_state.outbox); + check(messenger_state.active_actors(1).subscriptions = actor_state.subscriptions); + check(messenger_state.active_actors(1).subscribers = actor_state.subscribers); + deallocate(messenger_state); + check(messenger_state = null_messenger_state); + + destroy(actor); + + messenger_state := get_messenger_state; + check_equal(messenger_state.active_actors'length, 1); + check(messenger_state.deferred_actors = null); + actor_state := get_actor_state(actor2); + check_equal(messenger_state.active_actors(0).name.all, actor_state.name.all); + check_equal(messenger_state.active_actors(0).is_deferred, actor_state.is_deferred); + check(messenger_state.active_actors(0).inbox = actor_state.inbox); + check(messenger_state.active_actors(0).outbox = actor_state.outbox); + check(messenger_state.active_actors(0).subscriptions = actor_state.subscriptions); + check(messenger_state.active_actors(0).subscribers = actor_state.subscribers); + deallocate(messenger_state); + check(messenger_state = null_messenger_state); + + actor3 := find("actor 3"); + messenger_state := get_messenger_state; + check_equal(messenger_state.active_actors'length, 1); + check_equal(messenger_state.deferred_actors'length, 1); + actor_state := get_actor_state(actor2); + check_equal(messenger_state.active_actors(0).name.all, actor_state.name.all); + check_equal(messenger_state.active_actors(0).is_deferred, actor_state.is_deferred); + check(messenger_state.active_actors(0).inbox = actor_state.inbox); + check(messenger_state.active_actors(0).outbox = actor_state.outbox); + check(messenger_state.active_actors(0).subscriptions = actor_state.subscriptions); + check(messenger_state.active_actors(0).subscribers = actor_state.subscribers); + actor_state := get_actor_state(actor3); + check_equal(messenger_state.deferred_actors(0).name.all, actor_state.name.all); + check_equal(messenger_state.deferred_actors(0).is_deferred, actor_state.is_deferred); + check(messenger_state.deferred_actors(0).inbox = actor_state.inbox); + check(messenger_state.deferred_actors(0).outbox = actor_state.outbox); + check(messenger_state.deferred_actors(0).subscriptions = actor_state.subscriptions); + check(messenger_state.deferred_actors(0).subscribers = actor_state.subscribers); + deallocate(messenger_state); + check(messenger_state = null_messenger_state); + + destroy(actor2); + + messenger_state := get_messenger_state; + check(messenger_state.active_actors = null); + check_equal(messenger_state.deferred_actors'length, 1); + actor_state := get_actor_state(actor3); + check_equal(messenger_state.deferred_actors(0).name.all, actor_state.name.all); + check_equal(messenger_state.deferred_actors(0).is_deferred, actor_state.is_deferred); + check(messenger_state.deferred_actors(0).inbox = actor_state.inbox); + check(messenger_state.deferred_actors(0).outbox = actor_state.outbox); + check(messenger_state.deferred_actors(0).subscriptions = actor_state.subscriptions); + check(messenger_state.deferred_actors(0).subscribers = actor_state.subscribers); + deallocate(messenger_state); + check(messenger_state = null_messenger_state); + + elsif run("Test making a string of messenger state") then + reset_messenger; + check_equal(get_messenger_state_string, + "Active actors:" & LF & "Deferred actors:"); + + actor := new_actor("actor"); + check_equal(get_messenger_state_string, + "Active actors:" & LF & get_actor_state_string(actor, " ") & LF & LF & "Deferred actors:"); + + actor2 := new_actor("actor 2"); + check_equal(get_messenger_state_string, + "Active actors:" & LF & get_actor_state_string(actor, " ") & LF & LF & -- + get_actor_state_string(actor2, " ") & LF & LF & "Deferred actors:"); + + actor3 := find("actor 3"); + actor4 := find("actor 4"); + actor5 := new_actor("actor 5"); + check_equal(get_messenger_state_string(" "), + " Active actors:" & LF & get_actor_state_string(actor, " ") & LF & LF & -- + get_actor_state_string(actor2, " ") & LF & LF & get_actor_state_string(actor5, " ") & -- + LF & LF & " Deferred actors:" & LF & get_actor_state_string(actor3, " ") & LF & -- + LF & get_actor_state_string(actor4, " ")); + + destroy(actor); + destroy(actor2); + destroy(actor5); + check_equal(get_messenger_state_string, + "Active actors:" & LF & "Deferred actors:" & LF & get_actor_state_string(actor3, " ") & -- + LF & LF & get_actor_state_string(actor4, " ")); + + elsif run("Test trace log") then + mock(com_logger); + my_sender := new_actor("sender"); + my_receiver := new_actor("receiver"); + + msg := new_msg(new_msg_type("msg type"), sender => my_sender); + send(net, my_receiver, msg); + check_only_log(com_logger, "[1:- sender -> receiver (msg type)] => receiver inbox", trace); + receive(net, my_receiver, msg); + check_only_log(com_logger, "receiver inbox => [1:- sender -> receiver (msg type)]", trace); + + request_msg := new_msg; + send(net, my_receiver, request_msg); + check_only_log(com_logger, "[2:- - -> receiver (-)] => receiver inbox", trace); + receive(net, my_receiver, reply_msg); + check_only_log(com_logger, "receiver inbox => [2:- - -> receiver (-)]", trace); + + reply(net, request_msg, reply_msg); + check_only_log(com_logger, "[3:2 receiver -> - (-)] => receiver outbox", trace); + receive_reply(net, request_msg, reply_msg); + check_only_log(com_logger, "receiver outbox => [3:2 receiver -> - (-)]", trace); + + unmock(com_logger); + + -- Deprecated APIs + elsif run("Test that use of deprecated API leads to an error") then + mock(com_logger); + deprecated_message := compose("hello world"); + check_only_log(com_logger, "DEPRECATED INTERFACE ERROR. compose()", failure); + unmock(com_logger); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 100 ms); + + my_receiver : process is + variable self : actor_t; + variable msg : msg_t; + begin + wait until start_receiver; + self := new_actor("my_receiver"); + receive(net, self, msg); + check(msg.sender = find("test runner")); + check(msg.receiver = self); + hello_world_received <= check_equal(pop_string(msg), "hello world"); + wait; + end process; + + server : process is + variable self : actor_t; + variable request_msg, reply_msg : msg_t; + begin + wait until start_server; + self := new_actor("server"); + receive(net, self, request_msg); + if check_equal(pop_string(request_msg), "request") then + reply_msg := new_msg; + push_string(reply_msg, "request acknowledge"); + send(net, request_msg.sender, reply_msg); + end if; + wait; + end process server; + + subscribers : for i in 1 to 2 generate + process is + variable self, publisher : actor_t; + variable msg : msg_t; + begin + wait until start_subscribers; + self := new_actor("subscriber " & integer'image(i)); + publisher := find("publisher"); + subscribe(self, publisher); + receive(net, self, msg); + check(sender(msg) = find("publisher")); + check(receiver(msg) = self); + if check_equal(pop_string(msg), "hello subscriber") then + hello_subscriber_received(i) <= '1'; + hello_subscriber_received(3 - i) <= 'Z'; + end if; + wait; + end process; + end generate subscribers; + + server2 : process is + variable self : actor_t; + variable request_msg1, request_msg2, request_msg3 : msg_t; + variable reply_msg : msg_t; + begin + wait until start_server2; + self := new_actor("server2"); + + receive(net, self, request_msg1); + check_equal(pop_string(request_msg1), "request1"); + + receive(net, self, request_msg2); + check_equal(pop_string(request_msg2), "request2"); + + reply_msg := new_msg; + push_string(reply_msg, "reply2"); + reply(net, request_msg2, reply_msg); + check(reply_msg.sender = self); + check(reply_msg.receiver = find("test runner")); + + acknowledge(net, request_msg1, false); + + receive(net, self, request_msg3); + check_equal(pop_string(request_msg3), "request3"); + + acknowledge(net, request_msg3, true); + wait; + end process server2; + + server3 : process is + variable self : actor_t; + variable request_msg, reply_msg : msg_t; + begin + wait until start_server3; + self := new_actor("server3"); + + receive(net, self, request_msg); + check_equal(pop_string(request_msg), "request1"); + reply_msg := new_msg; + push_string(reply_msg, "reply1"); + reply(net, request_msg, reply_msg); + + receive(net, self, request_msg); + check_equal(pop_string(request_msg), "request2"); + acknowledge(net, request_msg, true); + + receive(net, self, request_msg); + check_equal(pop_string(request_msg), "request3"); + acknowledge(net, request_msg, false); + + wait; + end process server3; + + server4 : process is + variable self : actor_t; + variable request_msg, reply_msg : msg_t; + begin + wait until start_server4; + self := new_actor("server4", 1); + + receive(net, self, request_msg); + receive(net, self, request_msg); + receive(net, self, request_msg); + reply_msg := new_msg; + push_string(reply_msg, "reply3"); + reply(net, request_msg, reply_msg); + receive(net, self, request_msg); + reply_msg := new_msg; + push_string(reply_msg, "reply4"); + reply(net, request_msg, reply_msg); + wait; + end process server4; + + server5 : process is + variable self : actor_t; + variable request_msg, reply_msg : msg_t; + begin + wait until start_server5; + self := new_actor("server5"); + + receive(net, self, request_msg); + check_equal(pop_string(request_msg), "request"); + reply_msg := new_msg; + push_string(reply_msg, "reply"); + reply(net, request_msg, reply_msg); + + receive(net, self, request_msg); + check_equal(pop_string(request_msg), "request2"); + reply_msg := new_msg; + push_string(reply_msg, "reply2"); + wait for 10 ns; + reply(net, request_msg, reply_msg); + + receive(net, self, request_msg); + check_equal(pop_string(request_msg), "request3"); + reply_msg := new_msg; + push_string(reply_msg, "reply3"); + reply(net, request_msg, reply_msg); + + wait; + end process server5; + + server6 : process is + variable self : actor_t; + variable request_msg, reply_msg : msg_t; + begin + wait until start_server6; + self := new_actor("server6"); + loop + receive(net, self, request_msg); + reply_msg := new_msg; + push_string(reply_msg, "reply to " & pop_string(request_msg)); + reply(net, request_msg, reply_msg); + end loop; + end process server6; + + limited_inbox_actor : process is + variable self : actor_t; + variable msg : msg_t; + begin + wait until start_limited_inbox; + self := new_actor("limited inbox", 2); + wait for 10 ns; + receive(net, self, msg); + receive(net, self, msg); + receive(net, self, msg); + limited_inbox_actor_done <= true; + wait; + end process limited_inbox_actor; + + limited_inbox_subscriber : process is + variable self : actor_t; + variable msg : msg_t; + begin + wait until start_limited_inbox_subscriber; + self := new_actor("limited inbox subscriber", 1); + subscribe(self, find("test runner")); + wait for 10 ns; + receive(net, self, msg); + wait; + end process limited_inbox_subscriber; + + publishers : for i in 0 to 2 generate + process is + variable self : actor_t; + variable msg : msg_t; + begin + wait until start_publishers; + self := new_actor("publisher " & integer'image(i)); + msg := new_msg; + push_string(msg, name(self)); + publish(net, self, msg); + wait; + end process; + end generate publishers; + +end test_fixture; diff --git a/vunit/vhdl/com/test/tb_com_codec.vhd b/vunit/vhdl/com/test/tb_com_codec.vhd index 00d1be20a..6bb247d09 100644 --- a/vunit/vhdl/com/test/tb_com_codec.vhd +++ b/vunit/vhdl/com/test/tb_com_codec.vhd @@ -1,245 +1,245 @@ --- Test suite for com codec package --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library vunit_lib; -context vunit_lib.vunit_context; -context vunit_lib.com_context; - -library tb_com_lib; -use tb_com_lib.custom_codec_pkg.all; -use tb_com_lib.custom_types_pkg.all; - -library ieee; -use ieee.std_logic_1164.all; -use ieee.float_pkg.all; - -use std.textio.all; - -entity tb_com_codec is - generic ( - runner_cfg : string); -end entity tb_com_codec; - -architecture test_fixture of tb_com_codec is -begin - test_runner : process - constant f64 : float64 := (others => '0'); - constant special_chars : string(1 to 3) := "),("; - constant comma : character := ','; - constant lp : character := '('; - constant rp : character := ')'; - variable null_array4_t : array4_t(10 to 8); - variable null_array5_t : array5_t(1 to 0, 1 to 0); - variable null_array5_2_t : array5_t(0 to 1, 1 to 0); - variable null_array5_3_t : array5_t(1 to 0, 0 to 1); - variable null_array6_t : array6_t(apple downto banana); --- variable null_array7_t : array7_t(1 to 2, apple downto banana); - variable t1 : time; - variable my_record4 : record4_t; - variable my_record5 : record5_t; - variable my_record6 : record6_t; - variable my_record7 : record7_t; - variable e1, e2, e3 : line; - - variable a1 : array1_t; - variable a2 : array2_t; - variable a3 : array3_t; - variable a4 : array4_t(1 to 5); - variable a4_null : array4_t(1 to 0); - variable a5_null : array5_t(1 to 0, 1 to 0); - variable a5_null2 : array5_t(0 to 1, 1 to 0); - variable a5_null3 : array5_t(1 to 0, 0 to 1); - variable a5 : array5_t(1 to 5, 1 to 3); - variable a6_null : array6_t(apple downto orange); - variable a6 : array6_t(apple to orange); --- variable a7_null : array7_t(1 to 2, apple downto banana); --- variable a7 : array7_t(1 to 5, apple to melon); - variable a8 : array8_t; - variable a9 : array9_t; - variable a10 : array10_t; - - variable enum1 : enum1_t; - - variable rec1 : record1_t; - variable rec2 : record2_t; - variable rec3 : record3_t; - variable rec9 : record9_t; - - -- Temp variables to make test case pass Riviera-PRO 2016.10 - variable range_left, range_right : integer; - - variable msg, msg2 : msg_t; - - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test that custom enumeration type can be encoded and decoded") then - enum1 := decode(encode(red)); - check_relation(enum1 = red); - enum1 := decode(encode(green)); - check_relation(enum1 = green); - enum1 := decode(encode(blue)); - check_relation(enum1 = blue); - elsif run("Test that custom enumeration type can be pushed and popped") then - msg := new_msg; - push_enum1_t(msg, red); - check_relation(pop_enum1_t(msg) = red, result("for pop_enum1")); - elsif run("Test that custom record type can be encoded and decoded") then - rec1 := decode(encode_record1_t((character'pos(lp), -1, -2, -3))); - check_relation(rec1 = (character'pos(lp), -1, -2, -3)); - - rec2 := decode(encode_record2_t((command, 1, -1, -2, -3, '1'))); - check_relation(rec2 = (command, 1, -1, -2, -3, '1')); - rec2 := decode(command(1, -1, -2, -3, '1')); - check_relation(rec2 = (command, 1, -1, -2, -3, '1')); - - rec3 := decode(encode_record3_t((char => comma))); - check_relation(rec3 = (char => comma)); - rec3 := decode(encode_record3_t((char => lp))); - check_relation(rec3 = (char => lp)); - rec3 := decode(encode_record3_t((char => rp))); - check_relation(rec3 = (char => rp)); - elsif run("Test that custom record type can be pushed and popped") then - msg := new_msg; - rec1 := (character'pos(lp), -1, -2, -3); - push_record1_t(msg, rec1); - check_relation(pop_record1_t(msg) = rec1, result("for pop_record1_t")); - elsif run("Test that custom array can be encoded and decoded") then - a1 := decode(encode_array1_t((0, 1, 2, 3, 4))); - check_relation(a1 = (0, 1, 2, 3, 4)); - check_relation(a1'left = -2); - check_relation(a1'right = 2); - - a2 := decode(encode_array2_t((0, 1, 2, 3, 4))); - check_relation(a2 = (0, 1, 2, 3, 4)); - check_relation(a2'left = 2); - check_relation(a2'right = -2); - - a3 := decode(encode_array3_t(((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)))); - check_relation(a3 = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14))); - check_relation(a3'left(1) = -2); - check_relation(a3'right(1) = 2); - check_relation(a3'left(2) = -1); - check_relation(a3'right(2) = 1); - - a4_null := decode(encode(null_array4_t)); - check_relation(a4_null = null_array4_t); - a4 := decode(encode_array4_t((0, 1, 2, 3, 4))); - check_relation(a4 = (0, 1, 2, 3, 4)); - - a5_null := decode(encode(null_array5_t)); - check_relation(a5_null = null_array5_t); - a5_null2 := decode(encode(null_array5_2_t)); - check_relation(a5_null2 = null_array5_2_t); - a5_null3 := decode(encode(null_array5_3_t)); - check_relation(a5_null3 = null_array5_3_t); - a5 := decode(encode_array5_t(((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)))); - check_relation(a5 = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14))); - - a6_null := decode(encode(null_array6_t)); - check_relation(a6_null = null_array6_t); - a6 := decode(encode_array6_t((0, 1, 2, 3, 4))); - check_relation(a6 = (0, 1, 2, 3, 4)); - - -- This test has been removed since it fails under Active-HDL. @TODO - -- Investigate futher if this can be reintroduced or separated into its - -- own test that is selectively executed in the acceptance tests - -- depending on simulator. - --a7_null := decode(encode(null_array7_t)); - --check_relation(a7_null = null_array7_t); - --a7 := decode(encode_array7_t(((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)))); - --check_relation(a7 = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14))); - - a8 := decode(encode_array8_t(((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)))); - check_relation(a8 = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14))); - check_relation(a8'left(1) = -2); - check_relation(a8'right(1) = 2); - check_relation(a8'left(2) = -1); - check_relation(a8'right(2) = 1); - - a9 := decode(encode_array9_t((0, 1, 2, 3, 4))); - check_relation(a9 = (0, 1, 2, 3, 4)); - check_relation(a9'left = -2); - check_relation(a9'right = 2); - - a10 := decode(encode_array10_t(((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)))); - check_relation(a10 = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14))); - check_relation(a10'left(1) = -2); - check_relation(a10'right(1) = 2); - check_relation(a10'left(2) = -1); - check_relation(a10'right(2) = 1); - elsif run("Test that custom array can be pushed and popped") then - msg := new_msg; - a1 := (0, 1, 2, 3, 4); - push_array1_t(msg, a1); - check_relation(pop_array1_t(msg) = a1, result("for pop_array1_t")); - - a3 := ((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)); - push_array3_t(msg, a3); - check_relation(pop_array3_t(msg) = a3, result("for pop_array3_t")); - - a4 := (0, 1, 2, 3, 4); - push_array4_t(msg, a4); - check_relation(pop_array4_t(msg) = a4, result("for pop_array4_t")); - - a5 := ((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)); - push_array5_t(msg, a5); - check_relation(pop_array5_t(msg) = a5, result("for pop_array5_t")); - - elsif run("Test that all provided codecs can be used within a composite") then - my_record4 := (17, 42.21, -365 ns, true, '0', 'U', error, open_ok, read_mode, 21); - check_relation(my_record4 = decode(encode(my_record4))); - - my_record5 := ('f', "abc", (true, false, false), ('1', '0', '0'), (17, 21, 42), (-3.14, 2.71, 1000.1000), - (-13 ns, 14 ps, 3 ms), "1UX", (1.12, -0.25), 'g'); - check_relation(my_record5 = decode(encode(my_record5))); - - my_record6 := ((112.3, 0.48), "100", "011", "1XU", "LHW", "1U0X1", "01LZ1", to_float(234.56, f64), - (12.3, -0.48)); - check_relation(my_record6 = record6_t'(decode(encode(my_record6)))); - - my_record7 := (my_record4, my_record5, my_record6); - check_relation(my_record7 = record7_t'(decode(encode(my_record7)))); - elsif run("Test that the values of different enumeration types used for msg_type record elements get different encodings") then - write(e1, encode(record2_msg_type_t'(command))); - write(e2, encode(record8_msg_type_t'(read))); - write(e3, encode(record8_msg_type_t'(write))); - check_relation(e1.all /= e2.all); - check_relation(e1.all /= e3.all); - check_relation(e2.all /= e3.all); - deallocate(e1); - deallocate(e2); - deallocate(e3); - elsif run("Test that records with different msg_type enumeration types can classified with a single get_msg_type function") then - write(e1, encode(record2_msg_type_t'(command))); - write(e2, encode(record8_msg_type_t'(read))); - write(e3, encode(record8_msg_type_t'(write))); - check(get_record2_msg_type_t(e1.all) = command); - check(get_record8_msg_type_t(e2.all) = read); - check(get_record8_msg_type_t(e3.all) = write); - check(get_msg_type(e1.all) = command); - check(get_msg_type(e2.all) = read); - check(get_msg_type(e3.all) = write); - deallocate(e1); - deallocate(e2); - deallocate(e3); - elsif run("Test that records containing arrays can be encoded and decoded") then - rec9 := decode(encode(record9_t'(foo, x"a5", "foo", ((1, 2, 3, 4, 5, 6), (4, 3, 2, 1, 0, -1))))); - check_relation(rec9 = (foo, x"a5", "foo", ((1, 2, 3, 4, 5, 6), (4, 3, 2, 1, 0, -1)))); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 100 ms); -end test_fixture; +-- Test suite for com codec package +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; + +library tb_com_lib; +use tb_com_lib.custom_codec_pkg.all; +use tb_com_lib.custom_types_pkg.all; + +library ieee; +use ieee.std_logic_1164.all; +use ieee.float_pkg.all; + +use std.textio.all; + +entity tb_com_codec is + generic ( + runner_cfg : string); +end entity tb_com_codec; + +architecture test_fixture of tb_com_codec is +begin + test_runner : process + constant f64 : float64 := (others => '0'); + constant special_chars : string(1 to 3) := "),("; + constant comma : character := ','; + constant lp : character := '('; + constant rp : character := ')'; + variable null_array4_t : array4_t(10 to 8); + variable null_array5_t : array5_t(1 to 0, 1 to 0); + variable null_array5_2_t : array5_t(0 to 1, 1 to 0); + variable null_array5_3_t : array5_t(1 to 0, 0 to 1); + variable null_array6_t : array6_t(apple downto banana); +-- variable null_array7_t : array7_t(1 to 2, apple downto banana); + variable t1 : time; + variable my_record4 : record4_t; + variable my_record5 : record5_t; + variable my_record6 : record6_t; + variable my_record7 : record7_t; + variable e1, e2, e3 : line; + + variable a1 : array1_t; + variable a2 : array2_t; + variable a3 : array3_t; + variable a4 : array4_t(1 to 5); + variable a4_null : array4_t(1 to 0); + variable a5_null : array5_t(1 to 0, 1 to 0); + variable a5_null2 : array5_t(0 to 1, 1 to 0); + variable a5_null3 : array5_t(1 to 0, 0 to 1); + variable a5 : array5_t(1 to 5, 1 to 3); + variable a6_null : array6_t(apple downto orange); + variable a6 : array6_t(apple to orange); +-- variable a7_null : array7_t(1 to 2, apple downto banana); +-- variable a7 : array7_t(1 to 5, apple to melon); + variable a8 : array8_t; + variable a9 : array9_t; + variable a10 : array10_t; + + variable enum1 : enum1_t; + + variable rec1 : record1_t; + variable rec2 : record2_t; + variable rec3 : record3_t; + variable rec9 : record9_t; + + -- Temp variables to make test case pass Riviera-PRO 2016.10 + variable range_left, range_right : integer; + + variable msg, msg2 : msg_t; + + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test that custom enumeration type can be encoded and decoded") then + enum1 := decode(encode(red)); + check_relation(enum1 = red); + enum1 := decode(encode(green)); + check_relation(enum1 = green); + enum1 := decode(encode(blue)); + check_relation(enum1 = blue); + elsif run("Test that custom enumeration type can be pushed and popped") then + msg := new_msg; + push_enum1_t(msg, red); + check_relation(pop_enum1_t(msg) = red, result("for pop_enum1")); + elsif run("Test that custom record type can be encoded and decoded") then + rec1 := decode(encode_record1_t((character'pos(lp), -1, -2, -3))); + check_relation(rec1 = (character'pos(lp), -1, -2, -3)); + + rec2 := decode(encode_record2_t((command, 1, -1, -2, -3, '1'))); + check_relation(rec2 = (command, 1, -1, -2, -3, '1')); + rec2 := decode(command(1, -1, -2, -3, '1')); + check_relation(rec2 = (command, 1, -1, -2, -3, '1')); + + rec3 := decode(encode_record3_t((char => comma))); + check_relation(rec3 = (char => comma)); + rec3 := decode(encode_record3_t((char => lp))); + check_relation(rec3 = (char => lp)); + rec3 := decode(encode_record3_t((char => rp))); + check_relation(rec3 = (char => rp)); + elsif run("Test that custom record type can be pushed and popped") then + msg := new_msg; + rec1 := (character'pos(lp), -1, -2, -3); + push_record1_t(msg, rec1); + check_relation(pop_record1_t(msg) = rec1, result("for pop_record1_t")); + elsif run("Test that custom array can be encoded and decoded") then + a1 := decode(encode_array1_t((0, 1, 2, 3, 4))); + check_relation(a1 = (0, 1, 2, 3, 4)); + check_relation(a1'left = -2); + check_relation(a1'right = 2); + + a2 := decode(encode_array2_t((0, 1, 2, 3, 4))); + check_relation(a2 = (0, 1, 2, 3, 4)); + check_relation(a2'left = 2); + check_relation(a2'right = -2); + + a3 := decode(encode_array3_t(((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)))); + check_relation(a3 = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14))); + check_relation(a3'left(1) = -2); + check_relation(a3'right(1) = 2); + check_relation(a3'left(2) = -1); + check_relation(a3'right(2) = 1); + + a4_null := decode(encode(null_array4_t)); + check_relation(a4_null = null_array4_t); + a4 := decode(encode_array4_t((0, 1, 2, 3, 4))); + check_relation(a4 = (0, 1, 2, 3, 4)); + + a5_null := decode(encode(null_array5_t)); + check_relation(a5_null = null_array5_t); + a5_null2 := decode(encode(null_array5_2_t)); + check_relation(a5_null2 = null_array5_2_t); + a5_null3 := decode(encode(null_array5_3_t)); + check_relation(a5_null3 = null_array5_3_t); + a5 := decode(encode_array5_t(((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)))); + check_relation(a5 = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14))); + + a6_null := decode(encode(null_array6_t)); + check_relation(a6_null = null_array6_t); + a6 := decode(encode_array6_t((0, 1, 2, 3, 4))); + check_relation(a6 = (0, 1, 2, 3, 4)); + + -- This test has been removed since it fails under Active-HDL. @TODO + -- Investigate futher if this can be reintroduced or separated into its + -- own test that is selectively executed in the acceptance tests + -- depending on simulator. + --a7_null := decode(encode(null_array7_t)); + --check_relation(a7_null = null_array7_t); + --a7 := decode(encode_array7_t(((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)))); + --check_relation(a7 = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14))); + + a8 := decode(encode_array8_t(((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)))); + check_relation(a8 = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14))); + check_relation(a8'left(1) = -2); + check_relation(a8'right(1) = 2); + check_relation(a8'left(2) = -1); + check_relation(a8'right(2) = 1); + + a9 := decode(encode_array9_t((0, 1, 2, 3, 4))); + check_relation(a9 = (0, 1, 2, 3, 4)); + check_relation(a9'left = -2); + check_relation(a9'right = 2); + + a10 := decode(encode_array10_t(((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)))); + check_relation(a10 = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14))); + check_relation(a10'left(1) = -2); + check_relation(a10'right(1) = 2); + check_relation(a10'left(2) = -1); + check_relation(a10'right(2) = 1); + elsif run("Test that custom array can be pushed and popped") then + msg := new_msg; + a1 := (0, 1, 2, 3, 4); + push_array1_t(msg, a1); + check_relation(pop_array1_t(msg) = a1, result("for pop_array1_t")); + + a3 := ((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)); + push_array3_t(msg, a3); + check_relation(pop_array3_t(msg) = a3, result("for pop_array3_t")); + + a4 := (0, 1, 2, 3, 4); + push_array4_t(msg, a4); + check_relation(pop_array4_t(msg) = a4, result("for pop_array4_t")); + + a5 := ((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)); + push_array5_t(msg, a5); + check_relation(pop_array5_t(msg) = a5, result("for pop_array5_t")); + + elsif run("Test that all provided codecs can be used within a composite") then + my_record4 := (17, 42.21, -365 ns, true, '0', 'U', error, open_ok, read_mode, 21); + check_relation(my_record4 = decode(encode(my_record4))); + + my_record5 := ('f', "abc", (true, false, false), ('1', '0', '0'), (17, 21, 42), (-3.14, 2.71, 1000.1000), + (-13 ns, 14 ps, 3 ms), "1UX", (1.12, -0.25), 'g'); + check_relation(my_record5 = decode(encode(my_record5))); + + my_record6 := ((112.3, 0.48), "100", "011", "1XU", "LHW", "1U0X1", "01LZ1", to_float(234.56, f64), + (12.3, -0.48)); + check_relation(my_record6 = record6_t'(decode(encode(my_record6)))); + + my_record7 := (my_record4, my_record5, my_record6); + check_relation(my_record7 = record7_t'(decode(encode(my_record7)))); + elsif run("Test that the values of different enumeration types used for msg_type record elements get different encodings") then + write(e1, encode(record2_msg_type_t'(command))); + write(e2, encode(record8_msg_type_t'(read))); + write(e3, encode(record8_msg_type_t'(write))); + check_relation(e1.all /= e2.all); + check_relation(e1.all /= e3.all); + check_relation(e2.all /= e3.all); + deallocate(e1); + deallocate(e2); + deallocate(e3); + elsif run("Test that records with different msg_type enumeration types can classified with a single get_msg_type function") then + write(e1, encode(record2_msg_type_t'(command))); + write(e2, encode(record8_msg_type_t'(read))); + write(e3, encode(record8_msg_type_t'(write))); + check(get_record2_msg_type_t(e1.all) = command); + check(get_record8_msg_type_t(e2.all) = read); + check(get_record8_msg_type_t(e3.all) = write); + check(get_msg_type(e1.all) = command); + check(get_msg_type(e2.all) = read); + check(get_msg_type(e3.all) = write); + deallocate(e1); + deallocate(e2); + deallocate(e3); + elsif run("Test that records containing arrays can be encoded and decoded") then + rec9 := decode(encode(record9_t'(foo, x"a5", "foo", ((1, 2, 3, 4, 5, 6), (4, 3, 2, 1, 0, -1))))); + check_relation(rec9 = (foo, x"a5", "foo", ((1, 2, 3, 4, 5, 6), (4, 3, 2, 1, 0, -1)))); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 100 ms); +end test_fixture; diff --git a/vunit/vhdl/com/test/tb_com_deprecated.vhd b/vunit/vhdl/com/test/tb_com_deprecated.vhd index 51af7a4c7..1dc516333 100644 --- a/vunit/vhdl/com/test/tb_com_deprecated.vhd +++ b/vunit/vhdl/com/test/tb_com_deprecated.vhd @@ -1,454 +1,454 @@ --- Test suite for deprecated parts of the com package --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; -context vunit_lib.com_context; - -library ieee; -use ieee.std_logic_1164.all; - -use std.textio.all; - -entity tb_com_deprecated is - generic ( - runner_cfg : string); -end entity tb_com_deprecated; - -architecture test_fixture of tb_com_deprecated is - signal hello_world_received, start_receiver, start_server, - start_server2, start_server3, start_server5, - start_subscribers : boolean := false; - signal start_limited_inbox, start_limited_inbox_subscriber, - limited_inbox_actor_done : boolean := false; - signal hello_subscriber_received : std_logic_vector(1 to 2) := "ZZ"; - - constant com_logger : logger_t := get_logger("vunit_lib:com"); -begin - test_runner : process - variable actor_to_destroy, actor_to_keep, actor, actor2, self, - receiver, server, deferred_actor, publisher, subscriber : actor_t; - variable status : com_status_t; - variable receipt, receipt2 : receipt_t; - variable n_actors : natural; - variable message : message_ptr_t; - variable reply_message : message_ptr_t; - variable request_message : message_ptr_t; - variable t_start, t_stop : time; - variable ack : boolean; - begin - test_runner_setup(runner, runner_cfg); - allow_timeout; - allow_deprecated; - - while test_suite loop - reset_messenger; - self := create("test runner"); - if run("Test that a created actor can be destroyed") then - actor_to_destroy := create("actor to destroy"); - actor_to_keep := create("actor to keep"); - n_actors := num_of_actors; - destroy(actor_to_destroy, status); - check(num_of_actors = n_actors - 1, "Expected one less actor"); - check(status = ok, "Expected destroy status to be ok"); - check(actor_to_destroy = null_actor_c, "Destroyed actor should be nullified"); - check(find("actor to destroy", false) = null_actor_c, "A destroyed actor should not be found"); - check(find("actor to keep", false) /= null_actor_c, - "Actors other than the one destroyed must not be affected"); - elsif run("Test that a non-existing actor cannot be destroyed") then - actor := null_actor_c; - mock(com_logger); - destroy(actor, status); - check_only_log(com_logger, "UNKNOWN ACTOR ERROR.", failure); - unmock(com_logger); - elsif run("Test that a created actor get the correct inbox size") then - actor := new_actor("actor with max inbox"); - check(inbox_size(actor) = positive'high, "Expected maximum sized inbox"); - actor2 := new_actor("actor with bounded inbox", 23); - check(inbox_size(actor2) = 23, "Expected inbox size = 23"); - check(inbox_size(null_actor_c) = 0, "Expected no inbox on null actor"); - check(inbox_size(find("actor to be created")) = 1, - "Expected inbox size on actor with deferred creation to be one"); - check(inbox_size(new_actor("actor to be created", 42)) = 42, - "Expected inbox size on actor with deferred creation to change to given value when created"); - elsif run("Test that a message can be deleted") then - message := compose("hello"); - delete(message); - check(message = null, "Message not deleted"); - elsif run("Test that an actor can send a message to another actor") then - start_receiver <= true; - wait for 1 ns; - receiver := find("receiver"); - message := compose("hello world", self); - send(net, receiver, message); - check(message.sender = self); - check(message.receiver = receiver); - wait until hello_world_received for 1 ns; - check(hello_world_received, "Expected ""hello world"" to be received at the server"); - elsif run("Test that an actor can send a message in response to another message from an a priori unknown actor") then - start_server <= true; - wait for 1 ns; - server := find("server"); - message := compose("request", self); - send(net, server, message, receipt); - check(receipt.status = ok, "Expected send to succeed"); - receive(net, self, message); - if check(message.status = ok, "Expected no receive problems") then - check(message.payload.all = "request acknowledge", "Expected ""request acknowledge"""); - end if; - delete(message); - elsif run("Test that an actor can send a message to itself") then - send(net, self, "hello", receipt); - receive(net, self, message); - check(message.status = ok, "Expected no receive problems"); - check_equal(message.payload.all, "hello"); - elsif run("Test that an actor can poll for incoming messages") then - receive(net, self, message, 0 ns); - check(message.payload = null, "Expected no message payload"); - check(message.status = timeout, "Expected timeout"); - send(net, self, self, "hello again", receipt); - check(receipt.status = ok, "Expected send to succeed"); - receive(net, self, message, 0 ns); - if check(message.status = ok, "Expected no problems with receive") then - check(message.payload.all = "hello again", "Expected ""hello again"""); - check(message.sender = self, "Expected message from myself"); - end if; - delete(message); - elsif run("Test that an actor can send to an actor with deferred creation") then - deferred_actor := find("deferred actor"); - send(net, deferred_actor, "hello actor to be created", receipt); - check(receipt.status = ok, "Expected send to succeed"); - deferred_actor := create("deferred actor"); - receive(net, deferred_actor, message); - if check(message.status = ok, "Expected no problems with receive") then - check(message.payload.all = "hello actor to be created", "Expected ""hello actor to be created"""); - end if; - delete(message); - elsif run("Test that empty messages can be sent") then - send(net, self, "", receipt); - check(receipt.status = ok, "Expected send to succeed"); - receive(net, self, message); - if check(message.status = ok, "Expected no problems with receive") then - check(message.payload.all = "", "Expected an empty message"); - end if; - delete(message); - elsif run("Test that each sent message gets an increasing message number") then - send(net, self, "", receipt); - check(receipt.id = 1, "Expected first receipt id to be 1"); - send(net, self, "", receipt); - check(receipt.id = 2, "Expected second receipt id to be 2"); - receive(net, self, message); - check(message.id = 1, "Expected first message id to be 1"); - receive(net, self, message); - check(message.id = 2, "Expected second message id to be 2"); - elsif run("Test that a limited-inbox receiver can receive as expected without blocking") then - start_limited_inbox <= true; - actor := find("limited inbox"); - t_start := now; - send(net, self, actor, "First message", receipt); - t_stop := now; - check_equal(t_stop - t_start, 0 ns, "Expected no blocking on first message"); - t_start := now; - send(net, self, actor, "Second message", receipt, 0 ns); - t_stop := now; - check_equal(t_stop - t_start, 0 ns, "Expected no blocking on second message"); - t_start := now; - send(net, actor, "Third message", receipt, 11 ns); - t_stop := now; - check_equal(t_stop - t_start, 10 ns, "Expected a 10 ns blocking period on third message"); - elsif run("Test that sending to a limited-inbox receiver times out as expected") then - start_limited_inbox <= true; - actor := find("limited inbox"); - send(net, actor, "First message", receipt); - send(net, actor, "Second message", receipt, 0 ns); - mock(com_logger); - send(net, actor, "Third message", receipt, 9 ns); - check_log(com_logger, "FULL INBOX ERROR.", failure); - check_only_log(com_logger, "FULL INBOX ERROR.", failure); - unmock(com_logger); - - elsif run("Test that an actor can publish messages to multiple subscribers") then - publisher := create("publisher"); - start_subscribers <= true; - wait for 1 ns; - publish(net, publisher, "hello subscriber", status); - check(status = ok, "Expected publish to succeed"); - wait until hello_subscriber_received = "11" for 1 ns; - check(hello_subscriber_received = "11", "Expected ""hello subscribers"" to be received at the subscribers"); - - elsif run("Test that a subscriber can unsubscribe") then - subscribe(self, self, status); - check(status = ok, "Expected subscription to be ok"); - publish(net, self, "hello subscriber", status); - check(status = ok, "Expected publish to succeed"); - receive(net, self, message, 0 ns); - check(message.status = ok, "Expected no problems with receive"); - check(message.payload.all = "hello subscriber", "Expected a ""hello subscriber"" message"); - unsubscribe(self, self, status); - publish(net, self, "hello subscriber", status); - check(status = ok, "Expected publish to succeed"); - receive(net, self, message, 0 ns); - check(message.status = timeout, "Expected no message"); - elsif run("Test that a destroyed subscriber is not addressed by the publisher") then - subscriber := create("subscriber"); - subscribe(subscriber, self, status); - check(status = ok, "Expected subscription to be ok"); - publish(net, self, "hello subscriber", status); - check(status = ok, "Expected publish to succeed"); - receive(net, subscriber, message, 0 ns); - if check(message.status = ok, "Expected no problems with receive") then - check(message.payload.all = "hello subscriber", "Expected a ""hello subscriber"" message"); - end if; - destroy(subscriber, status); - check(status = ok, "Expected destroy status to be ok"); - publish(net, self, "hello subscriber", status); - check(status = ok, "Expected publish to succeed. Got " & com_status_t'image(status) & "."); - elsif run("Test that an actor can only subscribe once to the same publisher") then - subscribe(self, self, status); - check(status = ok, "Expected subscription to be ok"); - mock(com_logger); - subscribe(self, self, status); - check_only_log(com_logger, "ALREADY A SUBSCRIBER ERROR.", failure); - unmock(com_logger); - - elsif run("Test that a client can wait for an out-of-order request reply") then - start_server2 <= true; - server := find("server2"); - send(net, self, server, "request1", receipt); - send(net, self, server, "request2", receipt2); - - receive_reply(net, self, receipt2.id, reply_message); - check(reply_message.payload.all = "reply2", "Expected ""reply2"""); - check(reply_message.request_id = receipt2.id, "Expected request_id = " & integer'image(receipt2.id) & - " but got " & integer'image(reply_message.request_id)); - receive_reply(net, self, receipt.id, reply_message); - check(reply_message.payload.all = "reply1", "Expected ""reply1"""); - check(reply_message.request_id = receipt.id, "Expected request_id = " & integer'image(receipt.id) & - " but got " & integer'image(reply_message.request_id)); - delete(reply_message); - elsif run("Test that a synchronous request can be made") then - start_server3 <= true; - server := find("server3"); - - request(net, self, server, "request1", reply_message); - check(reply_message.payload.all = "reply1", "Expected ""reply1"""); - delete(reply_message); - - request(net, self, server, "request2", ack, status); - check(status = ok, "Expected request to succeed"); - check(ack, "Expected positive acknowledgement"); - - request(net, self, server, "request3", ack, status); - check(status = ok, "Expected request to succeed"); - check(not ack, "Expected negative acknowledgement"); - - t_start := now; - request(net, self, server, "request4", reply_message, 3 ns); - check(reply_message.status = timeout, "Expected timeout"); - check(now - t_start = 3 ns, "Expected timeout after 3 ns"); - delete(reply_message); - - send(net, self, server, "A message", receipt); - send(net, self, server, "This will sit in the inbox for 3 ns", receipt); - t_start := now; - request(net, self, server, - "The send part will block for 3 ns, the receive part should timout after 2 to get a total of 5 ns", - reply_message, 5 ns); - check(reply_message.status = timeout, "Expected timeout"); - check(now - t_start = 5 ns, "Expected timeout after 5 ns"); - delete(reply_message); - elsif run("Test that an anonymous request can be made") then - start_server5 <= true; - server := find("server5"); - - request_message := compose("request"); - send(net, server, request_message); - wait for 10 ns; - receive_reply(net, request_message, reply_message); - check_equal(reply_message.payload.all, "reply"); - - request_message := compose("request2"); - send(net, server, request_message); - receive_reply(net, request_message, reply_message); - check_equal(reply_message.payload.all, "reply2"); - - request_message := compose("request3"); - request(net, server, request_message, reply_message); - check_equal(reply_message.payload.all, "reply3"); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 100 ms); - - receiver : process is - variable self : actor_t; - variable message : message_ptr_t; - variable status : com_status_t; - begin - wait until start_receiver; - self := create("receiver"); - wait_for_messages(net, self, status); - message := get_message(self); - if check(message.payload.all = "hello world", "Expected ""hello world""") then - hello_world_received <= true; - end if; - delete(message); - wait; - end process receiver; - - server : process is - variable self : actor_t; - variable message : message_ptr_t; - variable receipt : receipt_t; - begin - wait until start_server; - self := create("server"); - receive(net, self, message); - if check(message.payload.all = "request", "Expected ""request""") then - send(net, message.sender, "request acknowledge", receipt); - check(receipt.status = ok, "Expected send to succeed"); - end if; - delete(message); - wait; - end process server; - - subscribers : for i in 1 to 2 generate - process is - variable self, publisher : actor_t; - variable message : message_ptr_t; - variable status : com_status_t; - begin - wait until start_subscribers; - self := create("subscriber " & integer'image(i)); - publisher := find("publisher"); - subscribe(self, publisher, status); - receive(net, self, message); - if check(message.payload.all = "hello subscriber", "Expected ""hello subscriber""") then - hello_subscriber_received(i) <= '1'; - hello_subscriber_received(3 - i) <= 'Z'; - end if; - delete(message); - wait; - end process; - end generate subscribers; - - server2 : process is - variable self : actor_t; - variable request_message1, request_message2 : message_ptr_t; - variable receipt : receipt_t; - begin - wait until start_server2; - self := create("server2"); - receive(net, self, request_message1); - check(request_message1.payload.all = "request1", "Expected ""request1"""); - receive(net, self, request_message2); - check(request_message2.payload.all = "request2", "Expected ""request2"""); - - reply(net, request_message2.sender, request_message2.id, "reply2", receipt); - check(receipt.status = ok, "Expected reply to succeed"); - reply(net, request_message1.sender, request_message1.id, "reply1", receipt); - check(receipt.status = ok, "Expected reply to succeed"); - - delete(request_message1); - delete(request_message2); - wait; - end process server2; - - server3 : process is - variable self : actor_t; - variable request_message : message_ptr_t; - variable receipt : receipt_t; - begin - wait until start_server3; - self := create("server3", 1); - - receive(net, self, request_message); - check(request_message.payload.all = "request1", "Expected ""request1"""); - reply(net, request_message.sender, request_message.id, "reply1", receipt); - check(receipt.status = ok, "Expected reply to succeed"); - delete(request_message); - - receive(net, self, request_message); - check(request_message.payload.all = "request2", "Expected ""request2"""); - acknowledge(net, request_message.sender, request_message.id, true, receipt); - check(receipt.status = ok, "Expected acknowledge to succeed"); - delete(request_message); - - receive(net, self, request_message); - acknowledge(net, request_message.sender, request_message.id, false, receipt); - delete(request_message); - - receive(net, self, request_message); - delete(request_message); - - receive(net, self, request_message); - wait for 3 ns; - receive(net, self, request_message); - delete(request_message); - wait; - end process server3; - - server5 : process is - variable self : actor_t; - variable request_message : message_ptr_t; - variable reply_message : message_ptr_t; - begin - wait until start_server5; - self := create("server5"); - - receive(net, self, request_message); - check_equal(request_message.payload.all, "request"); - reply_message := compose("reply"); - reply(net, request_message, reply_message); - - receive(net, self, request_message); - check_equal(request_message.payload.all, "request2"); - reply_message := compose("reply2"); - wait for 10 ns; - reply(net, request_message, reply_message); - - receive(net, self, request_message); - check_equal(request_message.payload.all, "request3"); - reply_message := compose("reply3"); - reply(net, request_message, reply_message); - - wait; - end process server5; - - limited_inbox_actor : process is - variable self : actor_t; - variable msg : msg_t; - begin - wait until start_limited_inbox; - self := create("limited inbox", 2); - wait for 10 ns; - receive(net, self, msg); - receive(net, self, msg); - receive(net, self, msg); - limited_inbox_actor_done <= true; - wait; - end process limited_inbox_actor; - - limited_inbox_subscriber : process is - variable self : actor_t; - variable message : message_ptr_t; - begin - wait until start_limited_inbox_subscriber; - self := create("limited inbox subscriber", 1); - subscribe(self, find("test runner")); - wait for 10 ns; - receive(net, self, message); - wait; - end process limited_inbox_subscriber; - - -end test_fixture; +-- Test suite for deprecated parts of the com package +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; + +library ieee; +use ieee.std_logic_1164.all; + +use std.textio.all; + +entity tb_com_deprecated is + generic ( + runner_cfg : string); +end entity tb_com_deprecated; + +architecture test_fixture of tb_com_deprecated is + signal hello_world_received, start_receiver, start_server, + start_server2, start_server3, start_server5, + start_subscribers : boolean := false; + signal start_limited_inbox, start_limited_inbox_subscriber, + limited_inbox_actor_done : boolean := false; + signal hello_subscriber_received : std_logic_vector(1 to 2) := "ZZ"; + + constant com_logger : logger_t := get_logger("vunit_lib:com"); +begin + test_runner : process + variable actor_to_destroy, actor_to_keep, actor, actor2, self, + receiver, server, deferred_actor, publisher, subscriber : actor_t; + variable status : com_status_t; + variable receipt, receipt2 : receipt_t; + variable n_actors : natural; + variable message : message_ptr_t; + variable reply_message : message_ptr_t; + variable request_message : message_ptr_t; + variable t_start, t_stop : time; + variable ack : boolean; + begin + test_runner_setup(runner, runner_cfg); + allow_timeout; + allow_deprecated; + + while test_suite loop + reset_messenger; + self := create("test runner"); + if run("Test that a created actor can be destroyed") then + actor_to_destroy := create("actor to destroy"); + actor_to_keep := create("actor to keep"); + n_actors := num_of_actors; + destroy(actor_to_destroy, status); + check(num_of_actors = n_actors - 1, "Expected one less actor"); + check(status = ok, "Expected destroy status to be ok"); + check(actor_to_destroy = null_actor_c, "Destroyed actor should be nullified"); + check(find("actor to destroy", false) = null_actor_c, "A destroyed actor should not be found"); + check(find("actor to keep", false) /= null_actor_c, + "Actors other than the one destroyed must not be affected"); + elsif run("Test that a non-existing actor cannot be destroyed") then + actor := null_actor_c; + mock(com_logger); + destroy(actor, status); + check_only_log(com_logger, "UNKNOWN ACTOR ERROR.", failure); + unmock(com_logger); + elsif run("Test that a created actor get the correct inbox size") then + actor := new_actor("actor with max inbox"); + check(inbox_size(actor) = positive'high, "Expected maximum sized inbox"); + actor2 := new_actor("actor with bounded inbox", 23); + check(inbox_size(actor2) = 23, "Expected inbox size = 23"); + check(inbox_size(null_actor_c) = 0, "Expected no inbox on null actor"); + check(inbox_size(find("actor to be created")) = 1, + "Expected inbox size on actor with deferred creation to be one"); + check(inbox_size(new_actor("actor to be created", 42)) = 42, + "Expected inbox size on actor with deferred creation to change to given value when created"); + elsif run("Test that a message can be deleted") then + message := compose("hello"); + delete(message); + check(message = null, "Message not deleted"); + elsif run("Test that an actor can send a message to another actor") then + start_receiver <= true; + wait for 1 ns; + receiver := find("receiver"); + message := compose("hello world", self); + send(net, receiver, message); + check(message.sender = self); + check(message.receiver = receiver); + wait until hello_world_received for 1 ns; + check(hello_world_received, "Expected ""hello world"" to be received at the server"); + elsif run("Test that an actor can send a message in response to another message from an a priori unknown actor") then + start_server <= true; + wait for 1 ns; + server := find("server"); + message := compose("request", self); + send(net, server, message, receipt); + check(receipt.status = ok, "Expected send to succeed"); + receive(net, self, message); + if check(message.status = ok, "Expected no receive problems") then + check(message.payload.all = "request acknowledge", "Expected ""request acknowledge"""); + end if; + delete(message); + elsif run("Test that an actor can send a message to itself") then + send(net, self, "hello", receipt); + receive(net, self, message); + check(message.status = ok, "Expected no receive problems"); + check_equal(message.payload.all, "hello"); + elsif run("Test that an actor can poll for incoming messages") then + receive(net, self, message, 0 ns); + check(message.payload = null, "Expected no message payload"); + check(message.status = timeout, "Expected timeout"); + send(net, self, self, "hello again", receipt); + check(receipt.status = ok, "Expected send to succeed"); + receive(net, self, message, 0 ns); + if check(message.status = ok, "Expected no problems with receive") then + check(message.payload.all = "hello again", "Expected ""hello again"""); + check(message.sender = self, "Expected message from myself"); + end if; + delete(message); + elsif run("Test that an actor can send to an actor with deferred creation") then + deferred_actor := find("deferred actor"); + send(net, deferred_actor, "hello actor to be created", receipt); + check(receipt.status = ok, "Expected send to succeed"); + deferred_actor := create("deferred actor"); + receive(net, deferred_actor, message); + if check(message.status = ok, "Expected no problems with receive") then + check(message.payload.all = "hello actor to be created", "Expected ""hello actor to be created"""); + end if; + delete(message); + elsif run("Test that empty messages can be sent") then + send(net, self, "", receipt); + check(receipt.status = ok, "Expected send to succeed"); + receive(net, self, message); + if check(message.status = ok, "Expected no problems with receive") then + check(message.payload.all = "", "Expected an empty message"); + end if; + delete(message); + elsif run("Test that each sent message gets an increasing message number") then + send(net, self, "", receipt); + check(receipt.id = 1, "Expected first receipt id to be 1"); + send(net, self, "", receipt); + check(receipt.id = 2, "Expected second receipt id to be 2"); + receive(net, self, message); + check(message.id = 1, "Expected first message id to be 1"); + receive(net, self, message); + check(message.id = 2, "Expected second message id to be 2"); + elsif run("Test that a limited-inbox receiver can receive as expected without blocking") then + start_limited_inbox <= true; + actor := find("limited inbox"); + t_start := now; + send(net, self, actor, "First message", receipt); + t_stop := now; + check_equal(t_stop - t_start, 0 ns, "Expected no blocking on first message"); + t_start := now; + send(net, self, actor, "Second message", receipt, 0 ns); + t_stop := now; + check_equal(t_stop - t_start, 0 ns, "Expected no blocking on second message"); + t_start := now; + send(net, actor, "Third message", receipt, 11 ns); + t_stop := now; + check_equal(t_stop - t_start, 10 ns, "Expected a 10 ns blocking period on third message"); + elsif run("Test that sending to a limited-inbox receiver times out as expected") then + start_limited_inbox <= true; + actor := find("limited inbox"); + send(net, actor, "First message", receipt); + send(net, actor, "Second message", receipt, 0 ns); + mock(com_logger); + send(net, actor, "Third message", receipt, 9 ns); + check_log(com_logger, "FULL INBOX ERROR.", failure); + check_only_log(com_logger, "FULL INBOX ERROR.", failure); + unmock(com_logger); + + elsif run("Test that an actor can publish messages to multiple subscribers") then + publisher := create("publisher"); + start_subscribers <= true; + wait for 1 ns; + publish(net, publisher, "hello subscriber", status); + check(status = ok, "Expected publish to succeed"); + wait until hello_subscriber_received = "11" for 1 ns; + check(hello_subscriber_received = "11", "Expected ""hello subscribers"" to be received at the subscribers"); + + elsif run("Test that a subscriber can unsubscribe") then + subscribe(self, self, status); + check(status = ok, "Expected subscription to be ok"); + publish(net, self, "hello subscriber", status); + check(status = ok, "Expected publish to succeed"); + receive(net, self, message, 0 ns); + check(message.status = ok, "Expected no problems with receive"); + check(message.payload.all = "hello subscriber", "Expected a ""hello subscriber"" message"); + unsubscribe(self, self, status); + publish(net, self, "hello subscriber", status); + check(status = ok, "Expected publish to succeed"); + receive(net, self, message, 0 ns); + check(message.status = timeout, "Expected no message"); + elsif run("Test that a destroyed subscriber is not addressed by the publisher") then + subscriber := create("subscriber"); + subscribe(subscriber, self, status); + check(status = ok, "Expected subscription to be ok"); + publish(net, self, "hello subscriber", status); + check(status = ok, "Expected publish to succeed"); + receive(net, subscriber, message, 0 ns); + if check(message.status = ok, "Expected no problems with receive") then + check(message.payload.all = "hello subscriber", "Expected a ""hello subscriber"" message"); + end if; + destroy(subscriber, status); + check(status = ok, "Expected destroy status to be ok"); + publish(net, self, "hello subscriber", status); + check(status = ok, "Expected publish to succeed. Got " & com_status_t'image(status) & "."); + elsif run("Test that an actor can only subscribe once to the same publisher") then + subscribe(self, self, status); + check(status = ok, "Expected subscription to be ok"); + mock(com_logger); + subscribe(self, self, status); + check_only_log(com_logger, "ALREADY A SUBSCRIBER ERROR.", failure); + unmock(com_logger); + + elsif run("Test that a client can wait for an out-of-order request reply") then + start_server2 <= true; + server := find("server2"); + send(net, self, server, "request1", receipt); + send(net, self, server, "request2", receipt2); + + receive_reply(net, self, receipt2.id, reply_message); + check(reply_message.payload.all = "reply2", "Expected ""reply2"""); + check(reply_message.request_id = receipt2.id, "Expected request_id = " & integer'image(receipt2.id) & + " but got " & integer'image(reply_message.request_id)); + receive_reply(net, self, receipt.id, reply_message); + check(reply_message.payload.all = "reply1", "Expected ""reply1"""); + check(reply_message.request_id = receipt.id, "Expected request_id = " & integer'image(receipt.id) & + " but got " & integer'image(reply_message.request_id)); + delete(reply_message); + elsif run("Test that a synchronous request can be made") then + start_server3 <= true; + server := find("server3"); + + request(net, self, server, "request1", reply_message); + check(reply_message.payload.all = "reply1", "Expected ""reply1"""); + delete(reply_message); + + request(net, self, server, "request2", ack, status); + check(status = ok, "Expected request to succeed"); + check(ack, "Expected positive acknowledgement"); + + request(net, self, server, "request3", ack, status); + check(status = ok, "Expected request to succeed"); + check(not ack, "Expected negative acknowledgement"); + + t_start := now; + request(net, self, server, "request4", reply_message, 3 ns); + check(reply_message.status = timeout, "Expected timeout"); + check(now - t_start = 3 ns, "Expected timeout after 3 ns"); + delete(reply_message); + + send(net, self, server, "A message", receipt); + send(net, self, server, "This will sit in the inbox for 3 ns", receipt); + t_start := now; + request(net, self, server, + "The send part will block for 3 ns, the receive part should timout after 2 to get a total of 5 ns", + reply_message, 5 ns); + check(reply_message.status = timeout, "Expected timeout"); + check(now - t_start = 5 ns, "Expected timeout after 5 ns"); + delete(reply_message); + elsif run("Test that an anonymous request can be made") then + start_server5 <= true; + server := find("server5"); + + request_message := compose("request"); + send(net, server, request_message); + wait for 10 ns; + receive_reply(net, request_message, reply_message); + check_equal(reply_message.payload.all, "reply"); + + request_message := compose("request2"); + send(net, server, request_message); + receive_reply(net, request_message, reply_message); + check_equal(reply_message.payload.all, "reply2"); + + request_message := compose("request3"); + request(net, server, request_message, reply_message); + check_equal(reply_message.payload.all, "reply3"); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 100 ms); + + receiver : process is + variable self : actor_t; + variable message : message_ptr_t; + variable status : com_status_t; + begin + wait until start_receiver; + self := create("receiver"); + wait_for_messages(net, self, status); + message := get_message(self); + if check(message.payload.all = "hello world", "Expected ""hello world""") then + hello_world_received <= true; + end if; + delete(message); + wait; + end process receiver; + + server : process is + variable self : actor_t; + variable message : message_ptr_t; + variable receipt : receipt_t; + begin + wait until start_server; + self := create("server"); + receive(net, self, message); + if check(message.payload.all = "request", "Expected ""request""") then + send(net, message.sender, "request acknowledge", receipt); + check(receipt.status = ok, "Expected send to succeed"); + end if; + delete(message); + wait; + end process server; + + subscribers : for i in 1 to 2 generate + process is + variable self, publisher : actor_t; + variable message : message_ptr_t; + variable status : com_status_t; + begin + wait until start_subscribers; + self := create("subscriber " & integer'image(i)); + publisher := find("publisher"); + subscribe(self, publisher, status); + receive(net, self, message); + if check(message.payload.all = "hello subscriber", "Expected ""hello subscriber""") then + hello_subscriber_received(i) <= '1'; + hello_subscriber_received(3 - i) <= 'Z'; + end if; + delete(message); + wait; + end process; + end generate subscribers; + + server2 : process is + variable self : actor_t; + variable request_message1, request_message2 : message_ptr_t; + variable receipt : receipt_t; + begin + wait until start_server2; + self := create("server2"); + receive(net, self, request_message1); + check(request_message1.payload.all = "request1", "Expected ""request1"""); + receive(net, self, request_message2); + check(request_message2.payload.all = "request2", "Expected ""request2"""); + + reply(net, request_message2.sender, request_message2.id, "reply2", receipt); + check(receipt.status = ok, "Expected reply to succeed"); + reply(net, request_message1.sender, request_message1.id, "reply1", receipt); + check(receipt.status = ok, "Expected reply to succeed"); + + delete(request_message1); + delete(request_message2); + wait; + end process server2; + + server3 : process is + variable self : actor_t; + variable request_message : message_ptr_t; + variable receipt : receipt_t; + begin + wait until start_server3; + self := create("server3", 1); + + receive(net, self, request_message); + check(request_message.payload.all = "request1", "Expected ""request1"""); + reply(net, request_message.sender, request_message.id, "reply1", receipt); + check(receipt.status = ok, "Expected reply to succeed"); + delete(request_message); + + receive(net, self, request_message); + check(request_message.payload.all = "request2", "Expected ""request2"""); + acknowledge(net, request_message.sender, request_message.id, true, receipt); + check(receipt.status = ok, "Expected acknowledge to succeed"); + delete(request_message); + + receive(net, self, request_message); + acknowledge(net, request_message.sender, request_message.id, false, receipt); + delete(request_message); + + receive(net, self, request_message); + delete(request_message); + + receive(net, self, request_message); + wait for 3 ns; + receive(net, self, request_message); + delete(request_message); + wait; + end process server3; + + server5 : process is + variable self : actor_t; + variable request_message : message_ptr_t; + variable reply_message : message_ptr_t; + begin + wait until start_server5; + self := create("server5"); + + receive(net, self, request_message); + check_equal(request_message.payload.all, "request"); + reply_message := compose("reply"); + reply(net, request_message, reply_message); + + receive(net, self, request_message); + check_equal(request_message.payload.all, "request2"); + reply_message := compose("reply2"); + wait for 10 ns; + reply(net, request_message, reply_message); + + receive(net, self, request_message); + check_equal(request_message.payload.all, "request3"); + reply_message := compose("reply3"); + reply(net, request_message, reply_message); + + wait; + end process server5; + + limited_inbox_actor : process is + variable self : actor_t; + variable msg : msg_t; + begin + wait until start_limited_inbox; + self := create("limited inbox", 2); + wait for 10 ns; + receive(net, self, msg); + receive(net, self, msg); + receive(net, self, msg); + limited_inbox_actor_done <= true; + wait; + end process limited_inbox_actor; + + limited_inbox_subscriber : process is + variable self : actor_t; + variable message : message_ptr_t; + begin + wait until start_limited_inbox_subscriber; + self := create("limited inbox subscriber", 1); + subscribe(self, find("test runner")); + wait for 10 ns; + receive(net, self, message); + wait; + end process limited_inbox_subscriber; + + +end test_fixture; diff --git a/vunit/vhdl/com/test/tb_com_msg_building.vhd b/vunit/vhdl/com/test/tb_com_msg_building.vhd index be52bd38d..a6bf58459 100644 --- a/vunit/vhdl/com/test/tb_com_msg_building.vhd +++ b/vunit/vhdl/com/test/tb_com_msg_building.vhd @@ -1,301 +1,301 @@ --- Test suite for com package --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; -context vunit_lib.com_context; -use vunit_lib.queue_pkg.all; -use vunit_lib.integer_vector_ptr_pkg.all; -use vunit_lib.integer_array_pkg.all; - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use ieee.numeric_bit.all; -use ieee.math_complex.all; -use ieee.fixed_pkg.all; -use ieee.float_pkg.all; - -use std.textio.all; - -entity tb_com_msg_building is - generic ( - runner_cfg : string); -end entity tb_com_msg_building; - -architecture test_fixture of tb_com_msg_building is -begin - test_runner : process - variable actor : actor_t; - variable msg, msg2, msg2_copy : msg_t; - variable queue, queue_copy : queue_t; - variable bv : bit_vector(0 to 5); - variable sulv : std_ulogic_vector(0 to 5); - variable boolv : boolean_vector(0 to 1); - variable integer_vector_ptr, integer_vector_ptr_copy : integer_vector_ptr_t; - variable integer_array, integer_array_copy : integer_array_t; - - constant my_msg_type : msg_type_t := new_msg_type("my msg type"); - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test that a message can be created") then - msg := new_msg; - check_equal(msg.id, no_message_id); - check(msg.status = ok); - check(msg.sender = null_actor); - check(msg.receiver = null_actor); - check_equal(msg.request_id, no_message_id); - check_equal(length(msg.data), 0); - - actor := new_actor("sender"); - msg2 := new_msg(sender => actor); - check_equal(msg2.id, no_message_id); - check(msg2.status = ok); - check(msg2.sender = actor); - check(msg2.receiver = null_actor); - check_equal(msg2.request_id, no_message_id); - check_equal(length(msg2.data), 0); - elsif run("Test that a message can be deleted") then - actor := new_actor("sender"); - - msg := new_msg(sender => actor); - msg.id := 1; - msg.status := null_message_error; - msg.receiver := actor; - msg.request_id := 1; - push(msg.data, 1); - - delete(msg); - - check_equal(msg.id, no_message_id); - check(msg.status = null_message_error); - check(msg.sender = null_actor); - check(msg.receiver = null_actor); - check_equal(msg.request_id, no_message_id); - check(msg.data = null_queue); - elsif run("Test push and pop integer") then - msg := new_msg; - push_integer(msg, 11); - push_integer(msg, 22); - check_equal(pop_integer(msg), 11, "data"); - check_equal(pop_integer(msg), 22, "data"); - elsif run("Test push and pop character") then - msg := new_msg; - push_character(msg, '1'); - push_character(msg, '2'); - assert pop_character(msg) = '1'; - assert pop_character(msg) = '2'; - elsif run("Test push and pop queue") then - msg := new_msg; - queue := new_queue; - push(queue, 22); - queue_copy := queue; - push_queue_ref(msg, queue); - assert queue = null_queue report "Ownership was transfered"; - assert pop_queue_ref(msg) = queue_copy report "Queue should come back"; - elsif run("Test push and pop string") then - msg := new_msg; - push_string(msg, "hello world"); - push_string(msg, "two"); - assert pop_string(msg) = "hello world"; - assert pop_string(msg) = "two"; - elsif run("Test push and pop bit") then - msg := new_msg; - push_bit(msg, '0'); - push_bit(msg, '1'); - check(pop_bit(msg) = '0'); - check(pop_bit(msg) = '1'); - elsif run("Test push and pop std_ulogic") then - msg := new_msg; - push_std_ulogic(msg, 'U'); - push_std_ulogic(msg, 'X'); - assert pop_std_ulogic(msg) = 'U'; - assert pop_std_ulogic(msg) = 'X'; - elsif run("Test push and pop severity_level") then - msg := new_msg; - push_severity_level(msg, note); - push_severity_level(msg, error); - check(pop_severity_level(msg) = note); - check(pop_severity_level(msg) = error); - elsif run("Test push and pop file_open_status") then - msg := new_msg; - push_file_open_status(msg, open_ok); - push_file_open_status(msg, mode_error); - check(pop_file_open_status(msg) = open_ok); - check(pop_file_open_status(msg) = mode_error); - elsif run("Test push and pop file_open_kind") then - msg := new_msg; - push_file_open_kind(msg, read_mode); - push_file_open_kind(msg, append_mode); - check(pop_file_open_kind(msg) = read_mode); - check(pop_file_open_kind(msg) = append_mode); - elsif run("Test push and pop bit_vector") then - msg := new_msg; - push_bit_vector(msg, "1"); - push_bit_vector(msg, "010101"); - bv(0 to 0) := pop_bit_vector(msg); - check(bv(0) = '1'); - bv := pop_bit_vector(msg) ; - check(bv = "010101"); - elsif run("Test push and pop std_ulogic_vector") then - msg := new_msg; - push_std_ulogic_vector(msg, "1"); - push_std_ulogic_vector(msg, "010101"); - sulv(0 to 0) := pop_std_ulogic_vector(msg); - check(sulv(0) = '1'); - sulv := pop_std_ulogic_vector(msg); - check(sulv = "010101"); - elsif run("Test push and pop complex") then - msg := new_msg; - push_complex(msg, (1.0, 2.2)); - push_complex(msg, (-1.0, -2.2)); - check(pop_complex(msg) = (1.0, 2.2)); - check(pop_complex(msg) = (-1.0, -2.2)); - elsif run("Test push and pop complex_polar") then - msg := new_msg; - push_complex_polar(msg, (1.0, 0.707)); - push_complex_polar(msg, (3.14, -0.707)); - check(pop_complex_polar(msg) = (1.0, 0.707)); - check(pop_complex_polar(msg) = (3.14, -0.707)); - elsif run("Test push and pop ieee.numeric_bit.unsigned") then - msg := new_msg; - push_numeric_bit_unsigned(msg, "1"); - push_numeric_bit_unsigned(msg, "010101"); - check(pop_numeric_bit_unsigned(msg) = "1"); - check(pop_numeric_bit_unsigned(msg) = "010101"); - elsif run("Test push and pop ieee.numeric_bit.signed") then - msg := new_msg; - push_numeric_bit_signed(msg, "1"); - push_numeric_bit_signed(msg, "010101"); - check(pop_numeric_bit_signed(msg) = "1"); - check(pop_numeric_bit_signed(msg) = "010101"); - elsif run("Test push and pop ieee.numeric_std.unsigned") then - msg := new_msg; - push_numeric_std_unsigned(msg, "1"); - push_numeric_std_unsigned(msg, "010101"); - check(pop_numeric_std_unsigned(msg) = "1"); - check(pop_numeric_std_unsigned(msg) = "010101"); - elsif run("Test push and pop ieee.numeric_std.signed") then - msg := new_msg; - push_numeric_std_signed(msg, "1"); - push_numeric_std_signed(msg, "010101"); - check(pop_numeric_std_signed(msg) = "1"); - check(pop_numeric_std_signed(msg) = "010101"); - elsif run("Test push and pop signed and unsigned") then - msg := new_msg; - push_std_ulogic_vector(msg, std_ulogic_vector(ieee.numeric_std.to_unsigned(11, 16))); - push_std_ulogic_vector(msg, std_ulogic_vector(ieee.numeric_std.to_signed(-1, 8))); - assert ieee.numeric_std.unsigned(pop_std_ulogic_vector(msg)) = ieee.numeric_std.to_unsigned(11, 16); - assert ieee.numeric_std.signed(pop_std_ulogic_vector(msg)) = ieee.numeric_std.to_signed(-1, 8); - elsif run("Test push and pop real") then - msg := new_msg; - push_real(msg, 1.0); - push_real(msg, -1.0); - assert pop_real(msg) = 1.0; - assert pop_real(msg) = -1.0; - elsif run("Test push and pop time") then - msg := new_msg; - push_time(msg, 1 fs); - push_time(msg, 1 ps); - assert pop_time(msg) = 1 fs; - assert pop_time(msg) = 1 ps; - elsif run("Test push and pop boolean_vector") then - msg := new_msg; - push_boolean_vector(msg, (1 => true)); - push_boolean_vector(msg, (false, true)); - boolv(0 to 0) := pop_boolean_vector(msg); - check(boolv(0) = true); - boolv := pop_boolean_vector(msg); - check(boolv = (false, true)); - elsif run("Test push and pop integer_vector") then - msg := new_msg; - push_integer_vector(msg, (1 => 17)); - push_integer_vector(msg, (-21, 42)); - check(pop_integer_vector(msg) = (1 => 17)); - check(pop_integer_vector(msg) = (-21, 42)); - elsif run("Test push and pop real_vector") then - msg := new_msg; - push_real_vector(msg, (1 => 17.17)); - push_real_vector(msg, (-21.21, 42.42)); - check(pop_real_vector(msg) = (1 => 17.17)); - check(pop_real_vector(msg) = (-21.21, 42.42)); - elsif run("Test push and pop time_vector") then - msg := new_msg; - push_time_vector(msg, (1 => 17.17 ms)); - push_time_vector(msg, (-21.21 min, 42.42 us)); - check(pop_time_vector(msg) = (1 => 17.17 ms)); - check(pop_time_vector(msg) = (-21.21 min, 42.42 us)); - elsif run("Test push and pop ufixed") then - msg := new_msg; - push_ufixed(msg, to_ufixed(17.17, 6, -9)); - push_ufixed(msg, to_ufixed(42.42, 6, -9)); - check(pop_ufixed(msg) = to_ufixed(17.17, 6, -9)); - check(pop_ufixed(msg) = to_ufixed(42.42, 6, -9)); - elsif run("Test push and pop sfixed") then - msg := new_msg; - push_sfixed(msg, to_sfixed(17.17, 6, -9)); - push_sfixed(msg, to_sfixed(-21.21, 6, -9)); - check(pop_sfixed(msg) = to_sfixed(17.17, 6, -9)); - check(pop_sfixed(msg) = to_sfixed(-21.21, 6, -9)); - elsif run("Test push and pop float") then - msg := new_msg; - push_float(msg, to_float(17.17)); - push_float(msg, to_float(-21.21)); - check(pop_float(msg) = to_float(17.17)); - check(pop_float(msg) = to_float(-21.21)); - elsif run("Test push and pop of msg_type") then - msg := new_msg; - push_msg_type(msg, my_msg_type); - check(pop_msg_type(msg) = my_msg_type); - elsif run("Test push and pop of integer_vector_ptr_t") then - msg := new_msg; - integer_vector_ptr := new_integer_vector_ptr; - integer_vector_ptr_copy := integer_vector_ptr; - push_integer_vector_ptr_ref(msg, integer_vector_ptr); - assert integer_vector_ptr = null_ptr report "Ownership was transfered"; - check(pop_integer_vector_ptr_ref(msg) = integer_vector_ptr_copy); - elsif run("Test push and pop of integer_array_t") then - msg := new_msg; - integer_array := new_3d(1, 2, 3, 4); - integer_array_copy := integer_array; - push_integer_array_t_ref(msg, integer_array); - check(integer_array = null_integer_array); - check(pop_integer_array_t_ref(msg) = integer_array_copy); - elsif run("Test push and pop of msg_t") then - msg := new_msg; - msg2 := new_msg; - msg2_copy := msg2; - push(msg, msg2); - assert msg2 = null_msg report "Ownership was transfered"; - check(pop(msg) = msg2_copy); - elsif run("Test setting and getting msg_type") then - msg := new_msg; - check(message_type(msg) = null_msg_type); - msg := new_msg(my_msg_type); - check(message_type(msg) = my_msg_type); - delete(msg); - check(message_type(msg) = null_msg_type); - elsif run("Test message for being empty") then - msg := new_msg; - check(is_empty(msg)); - push_integer(msg, 11); - check_false(is_empty(msg)); - check_equal(pop_integer(msg), 11); - check(is_empty(msg)); - push_integer(msg, 11); - delete(msg); - check(is_empty(msg)); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; -end test_fixture; +-- Test suite for com package +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +use vunit_lib.queue_pkg.all; +use vunit_lib.integer_vector_ptr_pkg.all; +use vunit_lib.integer_array_pkg.all; + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.numeric_bit.all; +use ieee.math_complex.all; +use ieee.fixed_pkg.all; +use ieee.float_pkg.all; + +use std.textio.all; + +entity tb_com_msg_building is + generic ( + runner_cfg : string); +end entity tb_com_msg_building; + +architecture test_fixture of tb_com_msg_building is +begin + test_runner : process + variable actor : actor_t; + variable msg, msg2, msg2_copy : msg_t; + variable queue, queue_copy : queue_t; + variable bv : bit_vector(0 to 5); + variable sulv : std_ulogic_vector(0 to 5); + variable boolv : boolean_vector(0 to 1); + variable integer_vector_ptr, integer_vector_ptr_copy : integer_vector_ptr_t; + variable integer_array, integer_array_copy : integer_array_t; + + constant my_msg_type : msg_type_t := new_msg_type("my msg type"); + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test that a message can be created") then + msg := new_msg; + check_equal(msg.id, no_message_id); + check(msg.status = ok); + check(msg.sender = null_actor); + check(msg.receiver = null_actor); + check_equal(msg.request_id, no_message_id); + check_equal(length(msg.data), 0); + + actor := new_actor("sender"); + msg2 := new_msg(sender => actor); + check_equal(msg2.id, no_message_id); + check(msg2.status = ok); + check(msg2.sender = actor); + check(msg2.receiver = null_actor); + check_equal(msg2.request_id, no_message_id); + check_equal(length(msg2.data), 0); + elsif run("Test that a message can be deleted") then + actor := new_actor("sender"); + + msg := new_msg(sender => actor); + msg.id := 1; + msg.status := null_message_error; + msg.receiver := actor; + msg.request_id := 1; + push(msg.data, 1); + + delete(msg); + + check_equal(msg.id, no_message_id); + check(msg.status = null_message_error); + check(msg.sender = null_actor); + check(msg.receiver = null_actor); + check_equal(msg.request_id, no_message_id); + check(msg.data = null_queue); + elsif run("Test push and pop integer") then + msg := new_msg; + push_integer(msg, 11); + push_integer(msg, 22); + check_equal(pop_integer(msg), 11, "data"); + check_equal(pop_integer(msg), 22, "data"); + elsif run("Test push and pop character") then + msg := new_msg; + push_character(msg, '1'); + push_character(msg, '2'); + assert pop_character(msg) = '1'; + assert pop_character(msg) = '2'; + elsif run("Test push and pop queue") then + msg := new_msg; + queue := new_queue; + push(queue, 22); + queue_copy := queue; + push_queue_ref(msg, queue); + assert queue = null_queue report "Ownership was transfered"; + assert pop_queue_ref(msg) = queue_copy report "Queue should come back"; + elsif run("Test push and pop string") then + msg := new_msg; + push_string(msg, "hello world"); + push_string(msg, "two"); + assert pop_string(msg) = "hello world"; + assert pop_string(msg) = "two"; + elsif run("Test push and pop bit") then + msg := new_msg; + push_bit(msg, '0'); + push_bit(msg, '1'); + check(pop_bit(msg) = '0'); + check(pop_bit(msg) = '1'); + elsif run("Test push and pop std_ulogic") then + msg := new_msg; + push_std_ulogic(msg, 'U'); + push_std_ulogic(msg, 'X'); + assert pop_std_ulogic(msg) = 'U'; + assert pop_std_ulogic(msg) = 'X'; + elsif run("Test push and pop severity_level") then + msg := new_msg; + push_severity_level(msg, note); + push_severity_level(msg, error); + check(pop_severity_level(msg) = note); + check(pop_severity_level(msg) = error); + elsif run("Test push and pop file_open_status") then + msg := new_msg; + push_file_open_status(msg, open_ok); + push_file_open_status(msg, mode_error); + check(pop_file_open_status(msg) = open_ok); + check(pop_file_open_status(msg) = mode_error); + elsif run("Test push and pop file_open_kind") then + msg := new_msg; + push_file_open_kind(msg, read_mode); + push_file_open_kind(msg, append_mode); + check(pop_file_open_kind(msg) = read_mode); + check(pop_file_open_kind(msg) = append_mode); + elsif run("Test push and pop bit_vector") then + msg := new_msg; + push_bit_vector(msg, "1"); + push_bit_vector(msg, "010101"); + bv(0 to 0) := pop_bit_vector(msg); + check(bv(0) = '1'); + bv := pop_bit_vector(msg) ; + check(bv = "010101"); + elsif run("Test push and pop std_ulogic_vector") then + msg := new_msg; + push_std_ulogic_vector(msg, "1"); + push_std_ulogic_vector(msg, "010101"); + sulv(0 to 0) := pop_std_ulogic_vector(msg); + check(sulv(0) = '1'); + sulv := pop_std_ulogic_vector(msg); + check(sulv = "010101"); + elsif run("Test push and pop complex") then + msg := new_msg; + push_complex(msg, (1.0, 2.2)); + push_complex(msg, (-1.0, -2.2)); + check(pop_complex(msg) = (1.0, 2.2)); + check(pop_complex(msg) = (-1.0, -2.2)); + elsif run("Test push and pop complex_polar") then + msg := new_msg; + push_complex_polar(msg, (1.0, 0.707)); + push_complex_polar(msg, (3.14, -0.707)); + check(pop_complex_polar(msg) = (1.0, 0.707)); + check(pop_complex_polar(msg) = (3.14, -0.707)); + elsif run("Test push and pop ieee.numeric_bit.unsigned") then + msg := new_msg; + push_numeric_bit_unsigned(msg, "1"); + push_numeric_bit_unsigned(msg, "010101"); + check(pop_numeric_bit_unsigned(msg) = "1"); + check(pop_numeric_bit_unsigned(msg) = "010101"); + elsif run("Test push and pop ieee.numeric_bit.signed") then + msg := new_msg; + push_numeric_bit_signed(msg, "1"); + push_numeric_bit_signed(msg, "010101"); + check(pop_numeric_bit_signed(msg) = "1"); + check(pop_numeric_bit_signed(msg) = "010101"); + elsif run("Test push and pop ieee.numeric_std.unsigned") then + msg := new_msg; + push_numeric_std_unsigned(msg, "1"); + push_numeric_std_unsigned(msg, "010101"); + check(pop_numeric_std_unsigned(msg) = "1"); + check(pop_numeric_std_unsigned(msg) = "010101"); + elsif run("Test push and pop ieee.numeric_std.signed") then + msg := new_msg; + push_numeric_std_signed(msg, "1"); + push_numeric_std_signed(msg, "010101"); + check(pop_numeric_std_signed(msg) = "1"); + check(pop_numeric_std_signed(msg) = "010101"); + elsif run("Test push and pop signed and unsigned") then + msg := new_msg; + push_std_ulogic_vector(msg, std_ulogic_vector(ieee.numeric_std.to_unsigned(11, 16))); + push_std_ulogic_vector(msg, std_ulogic_vector(ieee.numeric_std.to_signed(-1, 8))); + assert ieee.numeric_std.unsigned(pop_std_ulogic_vector(msg)) = ieee.numeric_std.to_unsigned(11, 16); + assert ieee.numeric_std.signed(pop_std_ulogic_vector(msg)) = ieee.numeric_std.to_signed(-1, 8); + elsif run("Test push and pop real") then + msg := new_msg; + push_real(msg, 1.0); + push_real(msg, -1.0); + assert pop_real(msg) = 1.0; + assert pop_real(msg) = -1.0; + elsif run("Test push and pop time") then + msg := new_msg; + push_time(msg, 1 fs); + push_time(msg, 1 ps); + assert pop_time(msg) = 1 fs; + assert pop_time(msg) = 1 ps; + elsif run("Test push and pop boolean_vector") then + msg := new_msg; + push_boolean_vector(msg, (1 => true)); + push_boolean_vector(msg, (false, true)); + boolv(0 to 0) := pop_boolean_vector(msg); + check(boolv(0) = true); + boolv := pop_boolean_vector(msg); + check(boolv = (false, true)); + elsif run("Test push and pop integer_vector") then + msg := new_msg; + push_integer_vector(msg, (1 => 17)); + push_integer_vector(msg, (-21, 42)); + check(pop_integer_vector(msg) = (1 => 17)); + check(pop_integer_vector(msg) = (-21, 42)); + elsif run("Test push and pop real_vector") then + msg := new_msg; + push_real_vector(msg, (1 => 17.17)); + push_real_vector(msg, (-21.21, 42.42)); + check(pop_real_vector(msg) = (1 => 17.17)); + check(pop_real_vector(msg) = (-21.21, 42.42)); + elsif run("Test push and pop time_vector") then + msg := new_msg; + push_time_vector(msg, (1 => 17.17 ms)); + push_time_vector(msg, (-21.21 min, 42.42 us)); + check(pop_time_vector(msg) = (1 => 17.17 ms)); + check(pop_time_vector(msg) = (-21.21 min, 42.42 us)); + elsif run("Test push and pop ufixed") then + msg := new_msg; + push_ufixed(msg, to_ufixed(17.17, 6, -9)); + push_ufixed(msg, to_ufixed(42.42, 6, -9)); + check(pop_ufixed(msg) = to_ufixed(17.17, 6, -9)); + check(pop_ufixed(msg) = to_ufixed(42.42, 6, -9)); + elsif run("Test push and pop sfixed") then + msg := new_msg; + push_sfixed(msg, to_sfixed(17.17, 6, -9)); + push_sfixed(msg, to_sfixed(-21.21, 6, -9)); + check(pop_sfixed(msg) = to_sfixed(17.17, 6, -9)); + check(pop_sfixed(msg) = to_sfixed(-21.21, 6, -9)); + elsif run("Test push and pop float") then + msg := new_msg; + push_float(msg, to_float(17.17)); + push_float(msg, to_float(-21.21)); + check(pop_float(msg) = to_float(17.17)); + check(pop_float(msg) = to_float(-21.21)); + elsif run("Test push and pop of msg_type") then + msg := new_msg; + push_msg_type(msg, my_msg_type); + check(pop_msg_type(msg) = my_msg_type); + elsif run("Test push and pop of integer_vector_ptr_t") then + msg := new_msg; + integer_vector_ptr := new_integer_vector_ptr; + integer_vector_ptr_copy := integer_vector_ptr; + push_integer_vector_ptr_ref(msg, integer_vector_ptr); + assert integer_vector_ptr = null_ptr report "Ownership was transfered"; + check(pop_integer_vector_ptr_ref(msg) = integer_vector_ptr_copy); + elsif run("Test push and pop of integer_array_t") then + msg := new_msg; + integer_array := new_3d(1, 2, 3, 4); + integer_array_copy := integer_array; + push_integer_array_t_ref(msg, integer_array); + check(integer_array = null_integer_array); + check(pop_integer_array_t_ref(msg) = integer_array_copy); + elsif run("Test push and pop of msg_t") then + msg := new_msg; + msg2 := new_msg; + msg2_copy := msg2; + push(msg, msg2); + assert msg2 = null_msg report "Ownership was transfered"; + check(pop(msg) = msg2_copy); + elsif run("Test setting and getting msg_type") then + msg := new_msg; + check(message_type(msg) = null_msg_type); + msg := new_msg(my_msg_type); + check(message_type(msg) = my_msg_type); + delete(msg); + check(message_type(msg) = null_msg_type); + elsif run("Test message for being empty") then + msg := new_msg; + check(is_empty(msg)); + push_integer(msg, 11); + check_false(is_empty(msg)); + check_equal(pop_integer(msg), 11); + check(is_empty(msg)); + push_integer(msg, 11); + delete(msg); + check(is_empty(msg)); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; +end test_fixture; diff --git a/vunit/vhdl/compile_vunit_lib.py b/vunit/vhdl/compile_vunit_lib.py index 37d705df7..150ee0161 100644 --- a/vunit/vhdl/compile_vunit_lib.py +++ b/vunit/vhdl/compile_vunit_lib.py @@ -1,10 +1,10 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from vunit import VUnit - -ui = VUnit.from_argv() -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from vunit import VUnit + +ui = VUnit.from_argv() +ui.main() diff --git a/vunit/vhdl/core/src/core_pkg.vhd b/vunit/vhdl/core/src/core_pkg.vhd index c4bd25ca3..f4e98e110 100644 --- a/vunit/vhdl/core/src/core_pkg.vhd +++ b/vunit/vhdl/core/src/core_pkg.vhd @@ -1,133 +1,133 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use std.textio.all; -use work.stop_pkg; -use work.integer_vector_ptr_pkg.all; -use work.string_ptr_pkg.all; - -package core_pkg is - procedure setup(file_name : string); - procedure test_start(test_name : string); - procedure test_suite_done; - procedure stop(status : natural); - - -- @TODO add core acceptance tests - procedure core_failure(msg : string); - procedure mock_core_failure; - procedure check_core_failure(msg : string := ""); - procedure unmock_core_failure; - procedure check_and_unmock_core_failure(msg : string := ""); - -end package; - -package body core_pkg is - file test_results : text; - - procedure setup(file_name : string) is - begin - file_open(test_results, file_name, write_mode); - end procedure; - - procedure test_start(test_name : string) is - variable l : line; - begin - write(l, string'("test_start:")); - write(l, test_name); - writeline(test_results, l); - end procedure; - - procedure test_suite_done is - variable l : line; - begin - write(l, string'("test_suite_done")); - writeline(test_results, l); - file_close(test_results); - end procedure; - - constant is_mocked_idx : natural := 0; - constant core_failure_called_idx : natural := 1; - constant core_failure_message_idx : natural := 2; - constant core_failure_mock_state_length : natural := 3; - - impure function new_core_failure_mock_state return integer_vector_ptr_t is - constant state : integer_vector_ptr_t := new_integer_vector_ptr(core_failure_mock_state_length); - begin - set(state, is_mocked_idx, 0); - set(state, core_failure_called_idx, 0); - set(state, core_failure_message_idx, to_integer(new_string_ptr)); - return state; - end; - - constant core_failure_mock_state : integer_vector_ptr_t := new_core_failure_mock_state; - - procedure mock_core_failure is - begin - set(core_failure_mock_state, is_mocked_idx, 1); - set(core_failure_mock_state, core_failure_called_idx, 0); - end; - - impure function core_failure_is_mocked return boolean is - begin - return get(core_failure_mock_state, is_mocked_idx) = 1; - end; - - procedure core_failure(msg : string) is - begin - if core_failure_is_mocked then - set(core_failure_mock_state, core_failure_called_idx, 1); - reallocate(to_string_ptr(get(core_failure_mock_state, core_failure_message_idx)), msg); - else - report msg severity failure; - end if; - end; - - procedure check_core_failure(msg : string := "") is - variable got_msg : string_ptr_t; - begin - if not core_failure_is_mocked then - report "core_failure not mocked" severity failure; - end if; - - if get(core_failure_mock_state, core_failure_called_idx) /= 1 then - report "core_failure not called" severity failure; - end if; - - got_msg := to_string_ptr(get(core_failure_mock_state, core_failure_message_idx)); - if msg /= "" and to_string(got_msg) /= msg then - report "Got core_failure message " & to_string(got_msg) & " expected " & msg severity failure; - end if; - - set(core_failure_mock_state, core_failure_called_idx, 0); - end; - - procedure unmock_core_failure is - variable got_msg : string_ptr_t; - begin - if not core_failure_is_mocked then - report "core_failure not mocked" severity failure; - end if; - - got_msg := to_string_ptr(get(core_failure_mock_state, core_failure_message_idx)); - if get(core_failure_mock_state, core_failure_called_idx) /= 0 then - report "core_failure was unexpectedly called with messsage " & to_string(got_msg) severity failure; - end if; - - set(core_failure_mock_state, is_mocked_idx, 0); - end; - - procedure check_and_unmock_core_failure(msg : string := "") is - begin - check_core_failure(msg); - unmock_core_failure; - end; - - procedure stop(status : natural) is - begin - stop_pkg.stop(status); - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use std.textio.all; +use work.stop_pkg; +use work.integer_vector_ptr_pkg.all; +use work.string_ptr_pkg.all; + +package core_pkg is + procedure setup(file_name : string); + procedure test_start(test_name : string); + procedure test_suite_done; + procedure stop(status : natural); + + -- @TODO add core acceptance tests + procedure core_failure(msg : string); + procedure mock_core_failure; + procedure check_core_failure(msg : string := ""); + procedure unmock_core_failure; + procedure check_and_unmock_core_failure(msg : string := ""); + +end package; + +package body core_pkg is + file test_results : text; + + procedure setup(file_name : string) is + begin + file_open(test_results, file_name, write_mode); + end procedure; + + procedure test_start(test_name : string) is + variable l : line; + begin + write(l, string'("test_start:")); + write(l, test_name); + writeline(test_results, l); + end procedure; + + procedure test_suite_done is + variable l : line; + begin + write(l, string'("test_suite_done")); + writeline(test_results, l); + file_close(test_results); + end procedure; + + constant is_mocked_idx : natural := 0; + constant core_failure_called_idx : natural := 1; + constant core_failure_message_idx : natural := 2; + constant core_failure_mock_state_length : natural := 3; + + impure function new_core_failure_mock_state return integer_vector_ptr_t is + constant state : integer_vector_ptr_t := new_integer_vector_ptr(core_failure_mock_state_length); + begin + set(state, is_mocked_idx, 0); + set(state, core_failure_called_idx, 0); + set(state, core_failure_message_idx, to_integer(new_string_ptr)); + return state; + end; + + constant core_failure_mock_state : integer_vector_ptr_t := new_core_failure_mock_state; + + procedure mock_core_failure is + begin + set(core_failure_mock_state, is_mocked_idx, 1); + set(core_failure_mock_state, core_failure_called_idx, 0); + end; + + impure function core_failure_is_mocked return boolean is + begin + return get(core_failure_mock_state, is_mocked_idx) = 1; + end; + + procedure core_failure(msg : string) is + begin + if core_failure_is_mocked then + set(core_failure_mock_state, core_failure_called_idx, 1); + reallocate(to_string_ptr(get(core_failure_mock_state, core_failure_message_idx)), msg); + else + report msg severity failure; + end if; + end; + + procedure check_core_failure(msg : string := "") is + variable got_msg : string_ptr_t; + begin + if not core_failure_is_mocked then + report "core_failure not mocked" severity failure; + end if; + + if get(core_failure_mock_state, core_failure_called_idx) /= 1 then + report "core_failure not called" severity failure; + end if; + + got_msg := to_string_ptr(get(core_failure_mock_state, core_failure_message_idx)); + if msg /= "" and to_string(got_msg) /= msg then + report "Got core_failure message " & to_string(got_msg) & " expected " & msg severity failure; + end if; + + set(core_failure_mock_state, core_failure_called_idx, 0); + end; + + procedure unmock_core_failure is + variable got_msg : string_ptr_t; + begin + if not core_failure_is_mocked then + report "core_failure not mocked" severity failure; + end if; + + got_msg := to_string_ptr(get(core_failure_mock_state, core_failure_message_idx)); + if get(core_failure_mock_state, core_failure_called_idx) /= 0 then + report "core_failure was unexpectedly called with messsage " & to_string(got_msg) severity failure; + end if; + + set(core_failure_mock_state, is_mocked_idx, 0); + end; + + procedure check_and_unmock_core_failure(msg : string := "") is + begin + check_core_failure(msg); + unmock_core_failure; + end; + + procedure stop(status : natural) is + begin + stop_pkg.stop(status); + end; + +end package body; diff --git a/vunit/vhdl/core/src/stop_body_2008.vhd b/vunit/vhdl/core/src/stop_body_2008.vhd index 545a53b21..026670866 100644 --- a/vunit/vhdl/core/src/stop_body_2008.vhd +++ b/vunit/vhdl/core/src/stop_body_2008.vhd @@ -1,15 +1,15 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -package body stop_pkg is - procedure stop(status : integer) is - begin - if status /= 0 then - report "Stopping simulation with status " & integer'image(status) severity failure; - end if; - std.env.stop(status); - end procedure; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +package body stop_pkg is + procedure stop(status : integer) is + begin + if status /= 0 then + report "Stopping simulation with status " & integer'image(status) severity failure; + end if; + std.env.stop(status); + end procedure; +end package body; diff --git a/vunit/vhdl/core/src/stop_body_93-2002.vhd b/vunit/vhdl/core/src/stop_body_93-2002.vhd index 59bcfa09d..fc259f4ad 100644 --- a/vunit/vhdl/core/src/stop_body_93-2002.vhd +++ b/vunit/vhdl/core/src/stop_body_93-2002.vhd @@ -1,12 +1,12 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -package body stop_pkg is - procedure stop(status : integer) is - begin - report "Stopping simulation with status " & integer'image(status) severity failure; - end procedure; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +package body stop_pkg is + procedure stop(status : integer) is + begin + report "Stopping simulation with status " & integer'image(status) severity failure; + end procedure; +end package body; diff --git a/vunit/vhdl/core/src/stop_pkg.vhd b/vunit/vhdl/core/src/stop_pkg.vhd index bed6a9558..b4e2de3dd 100644 --- a/vunit/vhdl/core/src/stop_pkg.vhd +++ b/vunit/vhdl/core/src/stop_pkg.vhd @@ -1,9 +1,9 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -package stop_pkg is - procedure stop(status : integer); -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +package stop_pkg is + procedure stop(status : integer); +end package; diff --git a/vunit/vhdl/data_types/run.py b/vunit/vhdl/data_types/run.py index a22dccbc0..0bd5d709f 100644 --- a/vunit/vhdl/data_types/run.py +++ b/vunit/vhdl/data_types/run.py @@ -1,22 +1,22 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname, basename -from vunit import VUnit -from glob import glob - -root = dirname(__file__) - -ui = VUnit.from_argv() -lib = ui.library("vunit_lib") -for file_name in glob(join(root, "test", "*.vhd")): - if basename(file_name).endswith("2008.vhd") and ui.vhdl_standard != "2008": - continue - if basename(file_name) == "tb_codec-2008.vhd" and ui.vhdl_standard != "2008": - continue - lib.add_source_file(file_name) - -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname, basename +from vunit import VUnit +from glob import glob + +root = dirname(__file__) + +ui = VUnit.from_argv() +lib = ui.library("vunit_lib") +for file_name in glob(join(root, "test", "*.vhd")): + if basename(file_name).endswith("2008.vhd") and ui.vhdl_standard != "2008": + continue + if basename(file_name) == "tb_codec-2008.vhd" and ui.vhdl_standard != "2008": + continue + lib.add_source_file(file_name) + +ui.main() diff --git a/vunit/vhdl/data_types/src/codec-2008.vhd b/vunit/vhdl/data_types/src/codec-2008.vhd index 5632273f2..febe0d9d4 100644 --- a/vunit/vhdl/data_types/src/codec-2008.vhd +++ b/vunit/vhdl/data_types/src/codec-2008.vhd @@ -1,253 +1,253 @@ --- This file provides functionality to encode/decode standard types to/from string. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.math_complex.all; -use ieee.numeric_bit.all; -use ieee.numeric_std.all; -use ieee.fixed_pkg.all; -use ieee.float_pkg.all; - -use std.textio.all; - -package codec_2008_pkg is - ----------------------------------------------------------------------------- - -- Predefined composite types - ----------------------------------------------------------------------------- - function encode ( - constant data : boolean_vector) - return string; - function decode ( - constant code : string) - return boolean_vector; - function encode ( - constant data : integer_vector) - return string; - function decode ( - constant code : string) - return integer_vector; - function encode ( - constant data : real_vector) - return string; - function decode ( - constant code : string) - return real_vector; - function encode ( - constant data : time_vector) - return string; - function decode ( - constant code : string) - return time_vector; - function encode ( - constant data : ufixed) - return string; - function decode ( - constant code : string) - return ufixed; - function encode ( - constant data : sfixed) - return string; - function decode ( - constant code : string) - return sfixed; - function encode ( - constant data : float) - return string; - function decode ( - constant code : string) - return float; - - ----------------------------------------------------------------------------- - -- Aliases - ----------------------------------------------------------------------------- - alias encode_boolean_vector is encode[boolean_vector return string]; - alias decode_boolean_vector is decode[string return boolean_vector]; - alias encode_integer_vector is encode[integer_vector return string]; - alias decode_integer_vector is decode[string return integer_vector]; - alias encode_real_vector is encode[real_vector return string]; - alias decode_real_vector is decode[string return real_vector]; - alias encode_time_vector is encode[time_vector return string]; - alias decode_time_vector is decode[string return time_vector]; - alias encode_ufixed is encode[ufixed return string]; - alias decode_ufixed is decode[string return ufixed]; - alias encode_sfixed is encode[sfixed return string]; - alias decode_sfixed is decode[string return sfixed]; - alias encode_float is encode[float return string]; - alias decode_float is decode[string return float]; - -end package; - -use work.codec_pkg.all; -use work.codec_builder_pkg.all; -use work.codec_builder_2008_pkg.all; - -package body codec_2008_pkg is - ----------------------------------------------------------------------------- - -- Predefined composite types - ----------------------------------------------------------------------------- - function encode ( - constant data : boolean_vector) - return string is - variable data_bv : bit_vector(data'range); - begin - for i in data'range loop - if data(i) then - data_bv(i) := '1'; - else - data_bv(i) := '0'; - end if; - end loop; - - return encode(data_bv); - end; - - function decode ( - constant code : string) - return boolean_vector is - variable ret_val : boolean_vector(get_range(code)'range) := (others => false); - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : integer_vector) - return string is - variable ret_val : string(1 to 9 + data'length*4); - variable index : positive := 10; - begin - ret_val(1 to 9) := encode_array_header(encode(data'left), encode(data'right), encode(data'ascending)); - for i in data'range loop - ret_val(index to index + 3) := encode(data(i)); - index := index + 4; - end loop; - - return ret_val; - end; - - function decode ( - constant code : string) - return integer_vector is - variable ret_val : integer_vector(get_range(code)'range) := (others => integer'left); - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : real_vector) - return string is - variable ret_val : string(1 to 9 + 13*data'length); - variable index : positive := 10; - begin - ret_val(1 to 9) := encode_array_header(encode(data'left), encode(data'right), encode(data'ascending)); - for i in data'range loop - ret_val(index to index + 12) := encode(data(i)); - index := index + 13; - end loop; - - return ret_val; - end; - - function decode ( - constant code : string) - return real_vector is - variable ret_val : real_vector(get_range(code)'range) := (others => real'left); - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : time_vector) - return string is - variable ret_val : string(1 to 9 + 8*data'length); - variable index : positive := 10; - begin - ret_val(1 to 9) := encode_array_header(encode(data'left), encode(data'right), encode(data'ascending)); - for i in data'range loop - ret_val(index to index + 7) := encode(data(i)); - index := index + 8; - end loop; - - return ret_val; - end; - - function decode ( - constant code : string) - return time_vector is - variable ret_val : time_vector(get_range(code)'range) := (others => time'left); - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : ufixed) - return string is - begin - return encode(std_ulogic_array(data)); - end; - - function decode ( - constant code : string) - return ufixed is - variable ret_val : ufixed(get_range(code)'range); - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : sfixed) - return string is - begin - return encode(std_ulogic_array(data)); - end; - - function decode ( - constant code : string) - return sfixed is - variable ret_val : sfixed(get_range(code)'range); - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : float) - return string is - begin - return encode(std_ulogic_array(data)); - end; - - function decode ( - constant code : string) - return float is - variable ret_val : float(get_range(code)'range); - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - -end package body codec_2008_pkg; +-- This file provides functionality to encode/decode standard types to/from string. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.math_complex.all; +use ieee.numeric_bit.all; +use ieee.numeric_std.all; +use ieee.fixed_pkg.all; +use ieee.float_pkg.all; + +use std.textio.all; + +package codec_2008_pkg is + ----------------------------------------------------------------------------- + -- Predefined composite types + ----------------------------------------------------------------------------- + function encode ( + constant data : boolean_vector) + return string; + function decode ( + constant code : string) + return boolean_vector; + function encode ( + constant data : integer_vector) + return string; + function decode ( + constant code : string) + return integer_vector; + function encode ( + constant data : real_vector) + return string; + function decode ( + constant code : string) + return real_vector; + function encode ( + constant data : time_vector) + return string; + function decode ( + constant code : string) + return time_vector; + function encode ( + constant data : ufixed) + return string; + function decode ( + constant code : string) + return ufixed; + function encode ( + constant data : sfixed) + return string; + function decode ( + constant code : string) + return sfixed; + function encode ( + constant data : float) + return string; + function decode ( + constant code : string) + return float; + + ----------------------------------------------------------------------------- + -- Aliases + ----------------------------------------------------------------------------- + alias encode_boolean_vector is encode[boolean_vector return string]; + alias decode_boolean_vector is decode[string return boolean_vector]; + alias encode_integer_vector is encode[integer_vector return string]; + alias decode_integer_vector is decode[string return integer_vector]; + alias encode_real_vector is encode[real_vector return string]; + alias decode_real_vector is decode[string return real_vector]; + alias encode_time_vector is encode[time_vector return string]; + alias decode_time_vector is decode[string return time_vector]; + alias encode_ufixed is encode[ufixed return string]; + alias decode_ufixed is decode[string return ufixed]; + alias encode_sfixed is encode[sfixed return string]; + alias decode_sfixed is decode[string return sfixed]; + alias encode_float is encode[float return string]; + alias decode_float is decode[string return float]; + +end package; + +use work.codec_pkg.all; +use work.codec_builder_pkg.all; +use work.codec_builder_2008_pkg.all; + +package body codec_2008_pkg is + ----------------------------------------------------------------------------- + -- Predefined composite types + ----------------------------------------------------------------------------- + function encode ( + constant data : boolean_vector) + return string is + variable data_bv : bit_vector(data'range); + begin + for i in data'range loop + if data(i) then + data_bv(i) := '1'; + else + data_bv(i) := '0'; + end if; + end loop; + + return encode(data_bv); + end; + + function decode ( + constant code : string) + return boolean_vector is + variable ret_val : boolean_vector(get_range(code)'range) := (others => false); + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : integer_vector) + return string is + variable ret_val : string(1 to 9 + data'length*4); + variable index : positive := 10; + begin + ret_val(1 to 9) := encode_array_header(encode(data'left), encode(data'right), encode(data'ascending)); + for i in data'range loop + ret_val(index to index + 3) := encode(data(i)); + index := index + 4; + end loop; + + return ret_val; + end; + + function decode ( + constant code : string) + return integer_vector is + variable ret_val : integer_vector(get_range(code)'range) := (others => integer'left); + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : real_vector) + return string is + variable ret_val : string(1 to 9 + 13*data'length); + variable index : positive := 10; + begin + ret_val(1 to 9) := encode_array_header(encode(data'left), encode(data'right), encode(data'ascending)); + for i in data'range loop + ret_val(index to index + 12) := encode(data(i)); + index := index + 13; + end loop; + + return ret_val; + end; + + function decode ( + constant code : string) + return real_vector is + variable ret_val : real_vector(get_range(code)'range) := (others => real'left); + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : time_vector) + return string is + variable ret_val : string(1 to 9 + 8*data'length); + variable index : positive := 10; + begin + ret_val(1 to 9) := encode_array_header(encode(data'left), encode(data'right), encode(data'ascending)); + for i in data'range loop + ret_val(index to index + 7) := encode(data(i)); + index := index + 8; + end loop; + + return ret_val; + end; + + function decode ( + constant code : string) + return time_vector is + variable ret_val : time_vector(get_range(code)'range) := (others => time'left); + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : ufixed) + return string is + begin + return encode(std_ulogic_array(data)); + end; + + function decode ( + constant code : string) + return ufixed is + variable ret_val : ufixed(get_range(code)'range); + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : sfixed) + return string is + begin + return encode(std_ulogic_array(data)); + end; + + function decode ( + constant code : string) + return sfixed is + variable ret_val : sfixed(get_range(code)'range); + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : float) + return string is + begin + return encode(std_ulogic_array(data)); + end; + + function decode ( + constant code : string) + return float is + variable ret_val : float(get_range(code)'range); + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + +end package body codec_2008_pkg; diff --git a/vunit/vhdl/data_types/src/codec.vhd b/vunit/vhdl/data_types/src/codec.vhd index a6d363ca7..798942570 100644 --- a/vunit/vhdl/data_types/src/codec.vhd +++ b/vunit/vhdl/data_types/src/codec.vhd @@ -1,678 +1,678 @@ --- This file provides functionality to encode/decode standard types to/from string. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.math_complex.all; -use ieee.numeric_bit.all; -use ieee.numeric_std.all; -use ieee.math_real.all; - -use std.textio.all; - -use work.codec_builder_pkg.all; - -package codec_pkg is - ----------------------------------------------------------------------------- - -- Predefined scalar types - ----------------------------------------------------------------------------- - function encode ( - constant data : integer) - return string; - function decode ( - constant code : string) - return integer; - function encode ( - constant data : real) - return string; - function decode ( - constant code : string) - return real; - function encode ( - constant data : time) - return string; - function decode ( - constant code : string) - return time; - function encode ( - constant data : boolean) - return string; - function decode ( - constant code : string) - return boolean; - function encode ( - constant data : bit) - return string; - function decode ( - constant code : string) - return bit; - function encode ( - constant data : std_ulogic) - return string; - function decode ( - constant code : string) - return std_ulogic; - function encode ( - constant data : severity_level) - return string; - function decode ( - constant code : string) - return severity_level; - function encode ( - constant data : file_open_status) - return string; - function decode ( - constant code : string) - return file_open_status; - function encode ( - constant data : file_open_kind) - return string; - function decode ( - constant code : string) - return file_open_kind; - function encode ( - constant data : character) - return string; - function decode ( - constant code : string) - return character; - - ----------------------------------------------------------------------------- - -- Predefined composite types - ----------------------------------------------------------------------------- - function encode ( - constant data : string) - return string; - function decode ( - constant code : string) - return string; - function encode ( - constant data : bit_vector) - return string; - function decode ( - constant code : string) - return bit_vector; - function encode ( - constant data : std_ulogic_vector) - return string; - function decode ( - constant code : string) - return std_ulogic_vector; - function encode ( - constant data : complex) - return string; - function decode ( - constant code : string) - return complex; - function encode ( - constant data : complex_polar) - return string; - function decode ( - constant code : string) - return complex_polar; - function encode ( - constant data : ieee.numeric_bit.unsigned) - return string; - function decode ( - constant code : string) - return ieee.numeric_bit.unsigned; - function encode ( - constant data : ieee.numeric_bit.signed) - return string; - function decode ( - constant code : string) - return ieee.numeric_bit.signed; - function encode ( - constant data : ieee.numeric_std.unsigned) - return string; - function decode ( - constant code : string) - return ieee.numeric_std.unsigned; - function encode ( - constant data : ieee.numeric_std.signed) - return string; - function decode ( - constant code : string) - return ieee.numeric_std.signed; - - ----------------------------------------------------------------------------- - -- Aliases - ----------------------------------------------------------------------------- - alias encode_integer is encode[integer return string]; - alias decode_integer is decode[string return integer]; - alias encode_real is encode[real return string]; - alias decode_real is decode[string return real]; - alias encode_time is encode[time return string]; - alias decode_time is decode[string return time]; - alias encode_boolean is encode[boolean return string]; - alias decode_boolean is decode[string return boolean]; - alias encode_bit is encode[bit return string]; - alias decode_bit is decode[string return bit]; - alias encode_std_ulogic is encode[std_ulogic return string]; - alias decode_std_ulogic is decode[string return std_ulogic]; - alias encode_severity_level is encode[severity_level return string]; - alias decode_severity_level is decode[string return severity_level]; - alias encode_file_open_status is encode[file_open_status return string]; - alias decode_file_open_status is decode[string return file_open_status]; - alias encode_file_open_kind is encode[file_open_kind return string]; - alias decode_file_open_kind is decode[string return file_open_kind]; - alias encode_character is encode[character return string]; - alias decode_character is decode[string return character]; - - alias encode_string is encode[string return string]; - alias decode_string is decode[string return string]; - alias encode_bit_vector is encode[bit_vector return string]; - alias decode_bit_vector is decode[string return bit_vector]; - alias encode_std_ulogic_vector is encode[std_ulogic_vector return string]; - alias decode_std_ulogic_vector is decode[string return std_ulogic_vector]; - alias encode_complex is encode[complex return string]; - alias decode_complex is decode[string return complex]; - alias encode_complex_polar is encode[complex_polar return string]; - alias decode_complex_polar is decode[string return complex_polar]; - alias encode_numeric_bit_unsigned is encode[ieee.numeric_bit.unsigned return string]; - alias decode_numeric_bit_unsigned is decode[string return ieee.numeric_bit.unsigned]; - alias encode_numeric_bit_signed is encode[ieee.numeric_bit.signed return string]; - alias decode_numeric_bit_signed is decode[string return ieee.numeric_bit.signed]; - alias encode_numeric_std_unsigned is encode[ieee.numeric_std.unsigned return string]; - alias decode_numeric_std_unsigned is decode[string return ieee.numeric_std.unsigned]; - alias encode_numeric_std_signed is encode[ieee.numeric_std.signed return string]; - alias decode_numeric_std_signed is decode[string return ieee.numeric_std.signed]; - - ----------------------------------------------------------------------------- - -- Support - ----------------------------------------------------------------------------- - type range_t is array (integer range <>) of bit; - - function get_range ( - constant code : string) - return range_t; - function encode ( - constant data : std_ulogic_array) - return string; - -end package; - -package body codec_pkg is - ----------------------------------------------------------------------------- - -- Predefined scalar types - ----------------------------------------------------------------------------- - function encode ( - constant data : integer) - return string is - begin - return to_byte_array(bit_vector(ieee.numeric_bit.to_signed(data, 32))); - end function encode; - - function decode ( - constant code : string) - return integer is - variable ret_val : integer; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end function decode; - - function encode ( - constant data : real) - return string is - constant is_signed : boolean := data < 0.0; - variable val : real := data; - variable exp : integer; - variable low : integer; - variable high : integer; - - function log2 (a : real) return integer is - variable y : real; - variable n : integer := 0; - begin - if (a = 1.0 or a = 0.0) then - return 0; - end if; - y := a; - if(a > 1.0) then - while y >= 2.0 loop - y := y / 2.0; - n := n + 1; - end loop; - return n; - end if; - -- o < y < 1 - while y < 1.0 loop - y := y * 2.0; - n := n - 1; - end loop; - return n; - end function; - begin - if is_signed then - val := -val; - end if; - - exp := log2(val); - -- Assume 53 mantissa bits - val := val * 2.0 ** (-exp + 53); - high := integer(floor(val * 2.0 ** (-31))); - low := integer(val - real(high) * 2.0 ** 31); - - return encode(is_signed) & encode(exp) & encode(low) & encode(high); - end; - - function decode ( - constant code : string) - return real is - variable ret_val : real; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - constant simulator_resolution : time := get_simulator_resolution; - - function encode ( - constant data : time) - return string is - - function modulo(t : time; m : natural) return integer is - begin - return (integer((t - (t/m)*m)/simulator_resolution) mod m); - end function; - - variable ret_val : string(1 to time_code_length); - variable t : time; - variable ascii : natural; - begin - -- @TODO assumes time is time_code_length bytes - t := data; - for i in time_code_length downto 1 loop - ascii := modulo(t, 256); - ret_val(i) := character'val(ascii); - t := (t - (ascii * simulator_resolution))/256; - end loop; - return ret_val; - end; - - function decode ( - constant code : string) - return time is - variable ret_val : time; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : boolean) - return string is - begin - if data then - return "T"; - else - return "F"; - end if; - end; - - function decode ( - constant code : string) - return boolean is - variable ret_val : boolean; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : bit) - return string is - begin - if data = '1' then - return "1"; - else - return "0"; - end if; - end; - - function decode ( - constant code : string) - return bit is - variable ret_val : bit; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : std_ulogic) - return string is - begin - return std_ulogic'image(data)(2 to 2); - end; - - function decode ( - constant code : string) - return std_ulogic is - variable ret_val : std_ulogic; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : severity_level) - return string is - begin - return (1 => character'val(severity_level'pos(data))); - end; - - function decode ( - constant code : string) - return severity_level is - variable ret_val : severity_level; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : file_open_status) - return string is - begin - return (1 => character'val(file_open_status'pos(data))); - end; - - function decode ( - constant code : string) - return file_open_status is - variable ret_val : file_open_status; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : file_open_kind) - return string is - begin - return (1 => character'val(file_open_kind'pos(data))); - end; - - function decode ( - constant code : string) - return file_open_kind is - variable ret_val : file_open_kind; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : character) - return string is - begin - return (1 => data); - end; - - function decode ( - constant code : string) - return character is - variable ret_val : character; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - ----------------------------------------------------------------------------- - -- Predefined composite types - ----------------------------------------------------------------------------- - function get_range ( - constant code : string) - return range_t is - constant range_left : integer := decode(code(1 to 4)); - constant range_right : integer := decode(code(5 to 8)); - constant is_ascending : boolean := decode(code(9 to 9)); - constant ret_val_ascending : range_t(range_left to range_right) := (others => '0'); - constant ret_val_descending : range_t(range_left downto range_right) := (others => '0'); - begin - if is_ascending then - return ret_val_ascending; - else - return ret_val_descending; - end if; - end function get_range; - - function encode ( - constant data : std_ulogic_array) - return string is - variable ret_val : string(1 to 9 + (data'length+1)/2); - variable index : positive := 10; - variable i : integer := data'left; - variable byte : natural; - begin - if data'length = 0 then - return encode_array_header(encode(1), encode(0), encode(true)); - end if; - ret_val(1 to 9) := encode_array_header(encode(data'left), encode(data'right), encode(data'ascending)); - if data'ascending then - while i <= data'right loop - byte := std_ulogic'pos(data(i)); - if i /= data'right then - byte := byte + std_ulogic'pos(data(i + 1)) * 16; - end if; - ret_val(index) := character'val(byte); - i := i + 2; - index := index + 1; - end loop; - else - while i >= data'right loop - byte := std_ulogic'pos(data(i)); - if i /= data'right then - byte := byte + std_ulogic'pos(data(i - 1)) * 16; - end if; - ret_val(index) := character'val(byte); - i := i - 2; - index := index + 1; - end loop; - end if; - - return ret_val; - end; - - function encode ( - constant data : string) - return string is - begin - -- Modelsim sets data'right to 0 which is out of the positive index range used by - -- strings. - if data'length = 0 then - return encode_array_header(encode(data'left), encode(data'right), encode(data'ascending)); - else - return encode_array_header(encode(data'left), encode(data'right), encode(data'ascending)) & data; - end if; - end; - - function decode ( - constant code : string) - return string is - variable ret_val : string(get_range(code)'range) := (others => NUL); - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : bit_vector) - return string is - variable ret_val : string(1 to 9 + (data'length + 7) / 8); - begin - if data'length = 0 then - return encode_array_header(encode(1), encode(0), encode(true)); - end if; - ret_val(1 to 9) := encode_array_header(encode(data'left), encode(data'right), encode(data'ascending)); - ret_val(10 to ret_val'length) := to_byte_array(data); - - return ret_val; - end; - - function decode ( - constant code : string) - return bit_vector is - variable ret_val : bit_vector(get_range(code)'range) := (others => '0'); - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : std_ulogic_vector) - return string is - begin - return encode(std_ulogic_array(data)); - end; - - function decode ( - constant code : string) - return std_ulogic_vector is - variable ret_val : std_ulogic_vector(get_range(code)'range) := (others => 'U'); - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : complex) - return string is - begin - return encode(data.re) & encode(data.im); - end; - - function decode ( - constant code : string) - return complex is - variable ret_val : complex; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : complex_polar) - return string is - begin - return encode(data.mag) & encode(data.arg); - end; - - function decode ( - constant code : string) - return complex_polar is - variable ret_val : complex_polar; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : ieee.numeric_bit.unsigned) - return string is - begin - return encode(bit_vector(data)); - end; - - function decode ( - constant code : string) - return ieee.numeric_bit.unsigned is - variable ret_val : ieee.numeric_bit.unsigned(get_range(code)'range) := (others => '0'); - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : ieee.numeric_bit.signed) - return string is - begin - return encode(bit_vector(data)); - end; - - function decode ( - constant code : string) - return ieee.numeric_bit.signed is - variable ret_val : ieee.numeric_bit.signed(get_range(code)'range) := (others => '0'); - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : ieee.numeric_std.unsigned) - return string is - begin - return encode(std_ulogic_vector(data)); - end; - - function decode ( - constant code : string) - return ieee.numeric_std.unsigned is - variable ret_val : ieee.numeric_std.unsigned(get_range(code)'range) := (others => 'U'); - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; - - function encode ( - constant data : ieee.numeric_std.signed) - return string is - begin - return encode(std_ulogic_vector(data)); - end; - - function decode ( - constant code : string) - return ieee.numeric_std.signed is - variable ret_val : ieee.numeric_std.signed(get_range(code)'range) := (others => 'U'); - variable index : positive := code'left; - begin - decode(code, index, ret_val); - - return ret_val; - end; -end package body codec_pkg; +-- This file provides functionality to encode/decode standard types to/from string. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.math_complex.all; +use ieee.numeric_bit.all; +use ieee.numeric_std.all; +use ieee.math_real.all; + +use std.textio.all; + +use work.codec_builder_pkg.all; + +package codec_pkg is + ----------------------------------------------------------------------------- + -- Predefined scalar types + ----------------------------------------------------------------------------- + function encode ( + constant data : integer) + return string; + function decode ( + constant code : string) + return integer; + function encode ( + constant data : real) + return string; + function decode ( + constant code : string) + return real; + function encode ( + constant data : time) + return string; + function decode ( + constant code : string) + return time; + function encode ( + constant data : boolean) + return string; + function decode ( + constant code : string) + return boolean; + function encode ( + constant data : bit) + return string; + function decode ( + constant code : string) + return bit; + function encode ( + constant data : std_ulogic) + return string; + function decode ( + constant code : string) + return std_ulogic; + function encode ( + constant data : severity_level) + return string; + function decode ( + constant code : string) + return severity_level; + function encode ( + constant data : file_open_status) + return string; + function decode ( + constant code : string) + return file_open_status; + function encode ( + constant data : file_open_kind) + return string; + function decode ( + constant code : string) + return file_open_kind; + function encode ( + constant data : character) + return string; + function decode ( + constant code : string) + return character; + + ----------------------------------------------------------------------------- + -- Predefined composite types + ----------------------------------------------------------------------------- + function encode ( + constant data : string) + return string; + function decode ( + constant code : string) + return string; + function encode ( + constant data : bit_vector) + return string; + function decode ( + constant code : string) + return bit_vector; + function encode ( + constant data : std_ulogic_vector) + return string; + function decode ( + constant code : string) + return std_ulogic_vector; + function encode ( + constant data : complex) + return string; + function decode ( + constant code : string) + return complex; + function encode ( + constant data : complex_polar) + return string; + function decode ( + constant code : string) + return complex_polar; + function encode ( + constant data : ieee.numeric_bit.unsigned) + return string; + function decode ( + constant code : string) + return ieee.numeric_bit.unsigned; + function encode ( + constant data : ieee.numeric_bit.signed) + return string; + function decode ( + constant code : string) + return ieee.numeric_bit.signed; + function encode ( + constant data : ieee.numeric_std.unsigned) + return string; + function decode ( + constant code : string) + return ieee.numeric_std.unsigned; + function encode ( + constant data : ieee.numeric_std.signed) + return string; + function decode ( + constant code : string) + return ieee.numeric_std.signed; + + ----------------------------------------------------------------------------- + -- Aliases + ----------------------------------------------------------------------------- + alias encode_integer is encode[integer return string]; + alias decode_integer is decode[string return integer]; + alias encode_real is encode[real return string]; + alias decode_real is decode[string return real]; + alias encode_time is encode[time return string]; + alias decode_time is decode[string return time]; + alias encode_boolean is encode[boolean return string]; + alias decode_boolean is decode[string return boolean]; + alias encode_bit is encode[bit return string]; + alias decode_bit is decode[string return bit]; + alias encode_std_ulogic is encode[std_ulogic return string]; + alias decode_std_ulogic is decode[string return std_ulogic]; + alias encode_severity_level is encode[severity_level return string]; + alias decode_severity_level is decode[string return severity_level]; + alias encode_file_open_status is encode[file_open_status return string]; + alias decode_file_open_status is decode[string return file_open_status]; + alias encode_file_open_kind is encode[file_open_kind return string]; + alias decode_file_open_kind is decode[string return file_open_kind]; + alias encode_character is encode[character return string]; + alias decode_character is decode[string return character]; + + alias encode_string is encode[string return string]; + alias decode_string is decode[string return string]; + alias encode_bit_vector is encode[bit_vector return string]; + alias decode_bit_vector is decode[string return bit_vector]; + alias encode_std_ulogic_vector is encode[std_ulogic_vector return string]; + alias decode_std_ulogic_vector is decode[string return std_ulogic_vector]; + alias encode_complex is encode[complex return string]; + alias decode_complex is decode[string return complex]; + alias encode_complex_polar is encode[complex_polar return string]; + alias decode_complex_polar is decode[string return complex_polar]; + alias encode_numeric_bit_unsigned is encode[ieee.numeric_bit.unsigned return string]; + alias decode_numeric_bit_unsigned is decode[string return ieee.numeric_bit.unsigned]; + alias encode_numeric_bit_signed is encode[ieee.numeric_bit.signed return string]; + alias decode_numeric_bit_signed is decode[string return ieee.numeric_bit.signed]; + alias encode_numeric_std_unsigned is encode[ieee.numeric_std.unsigned return string]; + alias decode_numeric_std_unsigned is decode[string return ieee.numeric_std.unsigned]; + alias encode_numeric_std_signed is encode[ieee.numeric_std.signed return string]; + alias decode_numeric_std_signed is decode[string return ieee.numeric_std.signed]; + + ----------------------------------------------------------------------------- + -- Support + ----------------------------------------------------------------------------- + type range_t is array (integer range <>) of bit; + + function get_range ( + constant code : string) + return range_t; + function encode ( + constant data : std_ulogic_array) + return string; + +end package; + +package body codec_pkg is + ----------------------------------------------------------------------------- + -- Predefined scalar types + ----------------------------------------------------------------------------- + function encode ( + constant data : integer) + return string is + begin + return to_byte_array(bit_vector(ieee.numeric_bit.to_signed(data, 32))); + end function encode; + + function decode ( + constant code : string) + return integer is + variable ret_val : integer; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end function decode; + + function encode ( + constant data : real) + return string is + constant is_signed : boolean := data < 0.0; + variable val : real := data; + variable exp : integer; + variable low : integer; + variable high : integer; + + function log2 (a : real) return integer is + variable y : real; + variable n : integer := 0; + begin + if (a = 1.0 or a = 0.0) then + return 0; + end if; + y := a; + if(a > 1.0) then + while y >= 2.0 loop + y := y / 2.0; + n := n + 1; + end loop; + return n; + end if; + -- o < y < 1 + while y < 1.0 loop + y := y * 2.0; + n := n - 1; + end loop; + return n; + end function; + begin + if is_signed then + val := -val; + end if; + + exp := log2(val); + -- Assume 53 mantissa bits + val := val * 2.0 ** (-exp + 53); + high := integer(floor(val * 2.0 ** (-31))); + low := integer(val - real(high) * 2.0 ** 31); + + return encode(is_signed) & encode(exp) & encode(low) & encode(high); + end; + + function decode ( + constant code : string) + return real is + variable ret_val : real; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + constant simulator_resolution : time := get_simulator_resolution; + + function encode ( + constant data : time) + return string is + + function modulo(t : time; m : natural) return integer is + begin + return (integer((t - (t/m)*m)/simulator_resolution) mod m); + end function; + + variable ret_val : string(1 to time_code_length); + variable t : time; + variable ascii : natural; + begin + -- @TODO assumes time is time_code_length bytes + t := data; + for i in time_code_length downto 1 loop + ascii := modulo(t, 256); + ret_val(i) := character'val(ascii); + t := (t - (ascii * simulator_resolution))/256; + end loop; + return ret_val; + end; + + function decode ( + constant code : string) + return time is + variable ret_val : time; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : boolean) + return string is + begin + if data then + return "T"; + else + return "F"; + end if; + end; + + function decode ( + constant code : string) + return boolean is + variable ret_val : boolean; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : bit) + return string is + begin + if data = '1' then + return "1"; + else + return "0"; + end if; + end; + + function decode ( + constant code : string) + return bit is + variable ret_val : bit; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : std_ulogic) + return string is + begin + return std_ulogic'image(data)(2 to 2); + end; + + function decode ( + constant code : string) + return std_ulogic is + variable ret_val : std_ulogic; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : severity_level) + return string is + begin + return (1 => character'val(severity_level'pos(data))); + end; + + function decode ( + constant code : string) + return severity_level is + variable ret_val : severity_level; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : file_open_status) + return string is + begin + return (1 => character'val(file_open_status'pos(data))); + end; + + function decode ( + constant code : string) + return file_open_status is + variable ret_val : file_open_status; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : file_open_kind) + return string is + begin + return (1 => character'val(file_open_kind'pos(data))); + end; + + function decode ( + constant code : string) + return file_open_kind is + variable ret_val : file_open_kind; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : character) + return string is + begin + return (1 => data); + end; + + function decode ( + constant code : string) + return character is + variable ret_val : character; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + ----------------------------------------------------------------------------- + -- Predefined composite types + ----------------------------------------------------------------------------- + function get_range ( + constant code : string) + return range_t is + constant range_left : integer := decode(code(1 to 4)); + constant range_right : integer := decode(code(5 to 8)); + constant is_ascending : boolean := decode(code(9 to 9)); + constant ret_val_ascending : range_t(range_left to range_right) := (others => '0'); + constant ret_val_descending : range_t(range_left downto range_right) := (others => '0'); + begin + if is_ascending then + return ret_val_ascending; + else + return ret_val_descending; + end if; + end function get_range; + + function encode ( + constant data : std_ulogic_array) + return string is + variable ret_val : string(1 to 9 + (data'length+1)/2); + variable index : positive := 10; + variable i : integer := data'left; + variable byte : natural; + begin + if data'length = 0 then + return encode_array_header(encode(1), encode(0), encode(true)); + end if; + ret_val(1 to 9) := encode_array_header(encode(data'left), encode(data'right), encode(data'ascending)); + if data'ascending then + while i <= data'right loop + byte := std_ulogic'pos(data(i)); + if i /= data'right then + byte := byte + std_ulogic'pos(data(i + 1)) * 16; + end if; + ret_val(index) := character'val(byte); + i := i + 2; + index := index + 1; + end loop; + else + while i >= data'right loop + byte := std_ulogic'pos(data(i)); + if i /= data'right then + byte := byte + std_ulogic'pos(data(i - 1)) * 16; + end if; + ret_val(index) := character'val(byte); + i := i - 2; + index := index + 1; + end loop; + end if; + + return ret_val; + end; + + function encode ( + constant data : string) + return string is + begin + -- Modelsim sets data'right to 0 which is out of the positive index range used by + -- strings. + if data'length = 0 then + return encode_array_header(encode(data'left), encode(data'right), encode(data'ascending)); + else + return encode_array_header(encode(data'left), encode(data'right), encode(data'ascending)) & data; + end if; + end; + + function decode ( + constant code : string) + return string is + variable ret_val : string(get_range(code)'range) := (others => NUL); + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : bit_vector) + return string is + variable ret_val : string(1 to 9 + (data'length + 7) / 8); + begin + if data'length = 0 then + return encode_array_header(encode(1), encode(0), encode(true)); + end if; + ret_val(1 to 9) := encode_array_header(encode(data'left), encode(data'right), encode(data'ascending)); + ret_val(10 to ret_val'length) := to_byte_array(data); + + return ret_val; + end; + + function decode ( + constant code : string) + return bit_vector is + variable ret_val : bit_vector(get_range(code)'range) := (others => '0'); + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : std_ulogic_vector) + return string is + begin + return encode(std_ulogic_array(data)); + end; + + function decode ( + constant code : string) + return std_ulogic_vector is + variable ret_val : std_ulogic_vector(get_range(code)'range) := (others => 'U'); + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : complex) + return string is + begin + return encode(data.re) & encode(data.im); + end; + + function decode ( + constant code : string) + return complex is + variable ret_val : complex; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : complex_polar) + return string is + begin + return encode(data.mag) & encode(data.arg); + end; + + function decode ( + constant code : string) + return complex_polar is + variable ret_val : complex_polar; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : ieee.numeric_bit.unsigned) + return string is + begin + return encode(bit_vector(data)); + end; + + function decode ( + constant code : string) + return ieee.numeric_bit.unsigned is + variable ret_val : ieee.numeric_bit.unsigned(get_range(code)'range) := (others => '0'); + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : ieee.numeric_bit.signed) + return string is + begin + return encode(bit_vector(data)); + end; + + function decode ( + constant code : string) + return ieee.numeric_bit.signed is + variable ret_val : ieee.numeric_bit.signed(get_range(code)'range) := (others => '0'); + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : ieee.numeric_std.unsigned) + return string is + begin + return encode(std_ulogic_vector(data)); + end; + + function decode ( + constant code : string) + return ieee.numeric_std.unsigned is + variable ret_val : ieee.numeric_std.unsigned(get_range(code)'range) := (others => 'U'); + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; + + function encode ( + constant data : ieee.numeric_std.signed) + return string is + begin + return encode(std_ulogic_vector(data)); + end; + + function decode ( + constant code : string) + return ieee.numeric_std.signed is + variable ret_val : ieee.numeric_std.signed(get_range(code)'range) := (others => 'U'); + variable index : positive := code'left; + begin + decode(code, index, ret_val); + + return ret_val; + end; +end package body codec_pkg; diff --git a/vunit/vhdl/data_types/src/codec_builder-2008.vhd b/vunit/vhdl/data_types/src/codec_builder-2008.vhd index 982c3fec9..ce2945fa6 100644 --- a/vunit/vhdl/data_types/src/codec_builder-2008.vhd +++ b/vunit/vhdl/data_types/src/codec_builder-2008.vhd @@ -1,128 +1,128 @@ --- This package contains support functions for standard codec building --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.math_complex.all; -use ieee.numeric_bit.all; -use ieee.numeric_std.all; -use ieee.fixed_pkg.all; -use ieee.float_pkg.all; - -use std.textio.all; - -use work.codec_builder_pkg.all; - -package codec_builder_2008_pkg is - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out boolean_vector); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out integer_vector); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out real_vector); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out time_vector); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out ufixed); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out sfixed); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out float); -end package codec_builder_2008_pkg; - -package body codec_builder_2008_pkg is - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out boolean_vector) is - variable result_bv : bit_vector(result'range); - begin - decode(code, index, result_bv); - for i in result'range loop - result(i) := result_bv(i) = '1'; - end loop; - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out integer_vector) is - begin - index := index + 9; - for i in result'range loop - decode(code, index, result(i)); - end loop; - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out real_vector) is - begin - index := index + 9; - for i in result'range loop - decode(code, index, result(i)); - end loop; - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out time_vector) is - begin - index := index + 9; - for i in result'range loop - decode(code, index, result(i)); - end loop; - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out ufixed) is - variable result_sula : std_ulogic_array(result'range); - begin - decode(code, index, result_sula); - result := ufixed(result_sula); - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out sfixed) is - variable result_sula : std_ulogic_array(result'range); - begin - decode(code, index, result_sula); - result := sfixed(result_sula); - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out float) is - variable result_sula : std_ulogic_array(result'range); - begin - decode(code, index, result_sula); - result := float(result_sula); - end; - -end package body codec_builder_2008_pkg; +-- This package contains support functions for standard codec building +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.math_complex.all; +use ieee.numeric_bit.all; +use ieee.numeric_std.all; +use ieee.fixed_pkg.all; +use ieee.float_pkg.all; + +use std.textio.all; + +use work.codec_builder_pkg.all; + +package codec_builder_2008_pkg is + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out boolean_vector); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out integer_vector); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out real_vector); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out time_vector); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out ufixed); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out sfixed); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out float); +end package codec_builder_2008_pkg; + +package body codec_builder_2008_pkg is + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out boolean_vector) is + variable result_bv : bit_vector(result'range); + begin + decode(code, index, result_bv); + for i in result'range loop + result(i) := result_bv(i) = '1'; + end loop; + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out integer_vector) is + begin + index := index + 9; + for i in result'range loop + decode(code, index, result(i)); + end loop; + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out real_vector) is + begin + index := index + 9; + for i in result'range loop + decode(code, index, result(i)); + end loop; + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out time_vector) is + begin + index := index + 9; + for i in result'range loop + decode(code, index, result(i)); + end loop; + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out ufixed) is + variable result_sula : std_ulogic_array(result'range); + begin + decode(code, index, result_sula); + result := ufixed(result_sula); + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out sfixed) is + variable result_sula : std_ulogic_array(result'range); + begin + decode(code, index, result_sula); + result := sfixed(result_sula); + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out float) is + variable result_sula : std_ulogic_array(result'range); + begin + decode(code, index, result_sula); + result := float(result_sula); + end; + +end package body codec_builder_2008_pkg; diff --git a/vunit/vhdl/data_types/src/codec_builder.vhd b/vunit/vhdl/data_types/src/codec_builder.vhd index 73d12b9d5..1aa3c29b6 100644 --- a/vunit/vhdl/data_types/src/codec_builder.vhd +++ b/vunit/vhdl/data_types/src/codec_builder.vhd @@ -1,434 +1,434 @@ --- This package contains support functions for standard codec building --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.math_complex.all; -use ieee.numeric_bit.all; -use ieee.numeric_std.all; - -use std.textio.all; - -package codec_builder_pkg is - type std_ulogic_array is array (integer range <>) of std_ulogic; - - function get_simulator_resolution return time; - function to_byte_array ( - constant value : bit_vector) - return string; - function from_byte_array ( - constant byte_array : string) - return bit_vector; - - constant integer_code_length : positive := 4; - constant boolean_code_length : positive := 1; - constant real_code_length : positive := boolean_code_length + 3 * integer_code_length; - constant std_ulogic_code_length : positive := 1; - constant bit_code_length : positive := 1; - constant time_code_length : positive := 8; - constant severity_level_code_length : positive := 1; - constant file_open_status_code_length : positive := 1; - constant file_open_kind_code_length : positive := 1; - constant complex_code_length : positive := 2 * real_code_length; - constant complex_polar_code_length : positive := 2 * real_code_length; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out integer); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out real); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out time); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out boolean); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out bit); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out std_ulogic); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out severity_level); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out file_open_status); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out file_open_kind); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out character); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out std_ulogic_array); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out string); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out bit_vector); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out std_ulogic_vector); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out complex); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out complex_polar); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out ieee.numeric_bit.unsigned); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out ieee.numeric_bit.signed); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out ieee.numeric_std.unsigned); - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out ieee.numeric_std.signed); - function encode_array_header ( - constant range_left1 : string; - constant range_right1 : string; - constant is_ascending1 : string; - constant range_left2 : string := ""; - constant range_right2 : string := ""; - constant is_ascending2 : string := "T") - return string; -end package codec_builder_pkg; - -package body codec_builder_pkg is - function get_simulator_resolution return time is - type time_array_t is array (integer range <>) of time; - variable resolution : time; - constant resolutions : time_array_t(1 to 8) := ( - 1.0e-15 sec, 1.0e-12 sec , 1.0e-9 sec, 1.0e-6 sec, 1.0e-3 sec, 1 sec, 1 min, 1 hr); - begin - for r in resolutions'range loop - resolution := resolutions(r); - exit when resolution > 0 sec; - end loop; - - return resolution; - end; - - constant simulator_resolution : time := get_simulator_resolution; - - function to_byte_array ( - constant value : bit_vector) - return string is - variable ret_val : string(1 to (value'length + 7) / 8); - variable value_int : ieee.numeric_bit.unsigned(value'length - 1 downto 0) := ieee.numeric_bit.unsigned(value); - begin - for i in ret_val'reverse_range loop - ret_val(i) := character'val(to_integer(value_int and to_unsigned(255, value_int'length))); - value_int := value_int srl 8; - end loop; - - return ret_val; - end function to_byte_array; - - function from_byte_array ( - constant byte_array : string) - return bit_vector is - constant byte_array_int : string(1 to byte_array'length) := byte_array; - variable ret_val : bit_vector(byte_array'length*8-1 downto 0); - begin - for i in byte_array_int'range loop - ret_val((byte_array_int'length-i)*8 + 7 downto (byte_array_int'length-i)*8) := bit_vector(ieee.numeric_bit.to_unsigned(character'pos(byte_array_int(i)), 8)); - end loop; - - return ret_val; - end function from_byte_array; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out integer) is - begin - result := to_integer(ieee.numeric_bit.signed(from_byte_array(code(index to index + integer_code_length - 1)))); - index := index + integer_code_length; - end procedure decode; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out real) is - variable is_signed : boolean; - variable exp, low, high : integer; - variable result_i : real; - begin - decode(code, index, is_signed); - decode(code, index, exp); - decode(code, index, low); - decode(code, index, high); - - result_i := (real(low) + real(high) * 2.0**31) * 2.0 ** (exp - 53); - if is_signed then - result_i := -result_i; - end if; - result := result_i; - end procedure decode; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out time) is - constant code_int : string(1 to time_code_length) := code(index to index + time_code_length - 1); - variable r : time; - variable b : integer; - begin - -- @TODO assumes time is time_code_length bytes - r := simulator_resolution * 0; - - for i in code_int'range loop - b := character'pos(code_int(i)); - r := r * 256; - if i = 1 and b >= 128 then - b := b - 256; - end if; - r := r + b * simulator_resolution; - end loop; - - index := index + time_code_length; - result := r; - end procedure decode; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out boolean) is - begin - result := code(index) = 'T'; - index := index + boolean_code_length; - end procedure decode; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out bit) is - begin - if code(index) = '1' then - result := '1'; - else - result := '0'; - end if; - index := index + bit_code_length; - end procedure decode; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out std_ulogic) is - begin - result := std_ulogic'value("'" & code(index) & "'"); - index := index + std_ulogic_code_length; - end procedure decode; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out severity_level) is - begin - result := severity_level'val(character'pos(code(index))); - index := index + severity_level_code_length; - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out file_open_status) is - begin - result := file_open_status'val(character'pos(code(index))); - index := index + file_open_status_code_length; - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out file_open_kind) is - begin - result := file_open_kind'val(character'pos(code(index))); - index := index + file_open_kind_code_length; - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out character) is - begin - result := code(index); - index := index + 1; - end procedure decode; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out std_ulogic_array) is - variable i : integer := result'left; - variable upper_nibble : natural; - begin - index := index + 9; - if result'ascending then - while i <= result'right loop - if i /= result'right then - upper_nibble := character'pos(code(index))/16; - result(i + 1) := std_ulogic'val(upper_nibble); - else - upper_nibble := 0; - end if; - result(i) := std_ulogic'val(character'pos(code(index)) - upper_nibble*16); - i := i + 2; - index := index + 1; - end loop; - else - while i >= result'right loop - if i /= result'right then - upper_nibble := character'pos(code(index))/16; - result(i - 1) := std_ulogic'val(upper_nibble); - else - upper_nibble := 0; - end if; - result(i) := std_ulogic'val(character'pos(code(index)) - upper_nibble*16); - i := i - 2; - index := index + 1; - end loop; - end if; - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out string) is - begin - result := code(index + 9 to index + 9 + result'length - 1); - index := index + 9 + result'length; - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out bit_vector) is - constant n_bytes : natural := (result'length + 7) / 8; - variable result_temp : bit_vector(n_bytes * 8 - 1 downto 0); - begin - result_temp := from_byte_array(code(index + 9 to index + 9 + n_bytes - 1)); - result := result_temp(result'length - 1 downto 0); - - index := index + 9 + n_bytes; - end procedure decode; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out std_ulogic_vector) is - variable result_sula : std_ulogic_array(result'range); - begin - decode(code, index, result_sula); - result := std_ulogic_vector(result_sula); - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out complex) is - begin - decode(code, index, result.re); - decode(code, index, result.im); - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out complex_polar) is - begin - decode(code, index, result.mag); - decode(code, index, result.arg); - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out ieee.numeric_bit.unsigned) is - variable result_bv : bit_vector(result'range); - begin - decode(code, index, result_bv); - result := ieee.numeric_bit.unsigned(result_bv); - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out ieee.numeric_bit.signed) is - variable result_bv : bit_vector(result'range); - begin - decode(code, index, result_bv); - result := ieee.numeric_bit.signed(result_bv); - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out ieee.numeric_std.unsigned) is - variable result_slv : std_ulogic_vector(result'range); - begin - decode(code, index, result_slv); - result := ieee.numeric_std.unsigned(result_slv); - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out ieee.numeric_std.signed) is - variable result_slv : std_ulogic_vector(result'range); - begin - decode(code, index, result_slv); - result := ieee.numeric_std.signed(result_slv); - end; - - function encode_array_header ( - constant range_left1 : string; - constant range_right1 : string; - constant is_ascending1 : string; - constant range_left2 : string := ""; - constant range_right2 : string := ""; - constant is_ascending2 : string := "T") - return string is - begin - if range_left2 = "" then - return range_left1 & range_right1 & is_ascending1; - else - return range_left1 & range_right1 & is_ascending1 & range_left2 & range_right2 & is_ascending2; - end if; - end function encode_array_header; -end package body codec_builder_pkg; +-- This package contains support functions for standard codec building +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.math_complex.all; +use ieee.numeric_bit.all; +use ieee.numeric_std.all; + +use std.textio.all; + +package codec_builder_pkg is + type std_ulogic_array is array (integer range <>) of std_ulogic; + + function get_simulator_resolution return time; + function to_byte_array ( + constant value : bit_vector) + return string; + function from_byte_array ( + constant byte_array : string) + return bit_vector; + + constant integer_code_length : positive := 4; + constant boolean_code_length : positive := 1; + constant real_code_length : positive := boolean_code_length + 3 * integer_code_length; + constant std_ulogic_code_length : positive := 1; + constant bit_code_length : positive := 1; + constant time_code_length : positive := 8; + constant severity_level_code_length : positive := 1; + constant file_open_status_code_length : positive := 1; + constant file_open_kind_code_length : positive := 1; + constant complex_code_length : positive := 2 * real_code_length; + constant complex_polar_code_length : positive := 2 * real_code_length; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out integer); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out real); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out time); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out boolean); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out bit); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out std_ulogic); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out severity_level); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out file_open_status); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out file_open_kind); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out character); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out std_ulogic_array); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out string); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out bit_vector); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out std_ulogic_vector); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out complex); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out complex_polar); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out ieee.numeric_bit.unsigned); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out ieee.numeric_bit.signed); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out ieee.numeric_std.unsigned); + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out ieee.numeric_std.signed); + function encode_array_header ( + constant range_left1 : string; + constant range_right1 : string; + constant is_ascending1 : string; + constant range_left2 : string := ""; + constant range_right2 : string := ""; + constant is_ascending2 : string := "T") + return string; +end package codec_builder_pkg; + +package body codec_builder_pkg is + function get_simulator_resolution return time is + type time_array_t is array (integer range <>) of time; + variable resolution : time; + constant resolutions : time_array_t(1 to 8) := ( + 1.0e-15 sec, 1.0e-12 sec , 1.0e-9 sec, 1.0e-6 sec, 1.0e-3 sec, 1 sec, 1 min, 1 hr); + begin + for r in resolutions'range loop + resolution := resolutions(r); + exit when resolution > 0 sec; + end loop; + + return resolution; + end; + + constant simulator_resolution : time := get_simulator_resolution; + + function to_byte_array ( + constant value : bit_vector) + return string is + variable ret_val : string(1 to (value'length + 7) / 8); + variable value_int : ieee.numeric_bit.unsigned(value'length - 1 downto 0) := ieee.numeric_bit.unsigned(value); + begin + for i in ret_val'reverse_range loop + ret_val(i) := character'val(to_integer(value_int and to_unsigned(255, value_int'length))); + value_int := value_int srl 8; + end loop; + + return ret_val; + end function to_byte_array; + + function from_byte_array ( + constant byte_array : string) + return bit_vector is + constant byte_array_int : string(1 to byte_array'length) := byte_array; + variable ret_val : bit_vector(byte_array'length*8-1 downto 0); + begin + for i in byte_array_int'range loop + ret_val((byte_array_int'length-i)*8 + 7 downto (byte_array_int'length-i)*8) := bit_vector(ieee.numeric_bit.to_unsigned(character'pos(byte_array_int(i)), 8)); + end loop; + + return ret_val; + end function from_byte_array; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out integer) is + begin + result := to_integer(ieee.numeric_bit.signed(from_byte_array(code(index to index + integer_code_length - 1)))); + index := index + integer_code_length; + end procedure decode; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out real) is + variable is_signed : boolean; + variable exp, low, high : integer; + variable result_i : real; + begin + decode(code, index, is_signed); + decode(code, index, exp); + decode(code, index, low); + decode(code, index, high); + + result_i := (real(low) + real(high) * 2.0**31) * 2.0 ** (exp - 53); + if is_signed then + result_i := -result_i; + end if; + result := result_i; + end procedure decode; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out time) is + constant code_int : string(1 to time_code_length) := code(index to index + time_code_length - 1); + variable r : time; + variable b : integer; + begin + -- @TODO assumes time is time_code_length bytes + r := simulator_resolution * 0; + + for i in code_int'range loop + b := character'pos(code_int(i)); + r := r * 256; + if i = 1 and b >= 128 then + b := b - 256; + end if; + r := r + b * simulator_resolution; + end loop; + + index := index + time_code_length; + result := r; + end procedure decode; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out boolean) is + begin + result := code(index) = 'T'; + index := index + boolean_code_length; + end procedure decode; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out bit) is + begin + if code(index) = '1' then + result := '1'; + else + result := '0'; + end if; + index := index + bit_code_length; + end procedure decode; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out std_ulogic) is + begin + result := std_ulogic'value("'" & code(index) & "'"); + index := index + std_ulogic_code_length; + end procedure decode; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out severity_level) is + begin + result := severity_level'val(character'pos(code(index))); + index := index + severity_level_code_length; + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out file_open_status) is + begin + result := file_open_status'val(character'pos(code(index))); + index := index + file_open_status_code_length; + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out file_open_kind) is + begin + result := file_open_kind'val(character'pos(code(index))); + index := index + file_open_kind_code_length; + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out character) is + begin + result := code(index); + index := index + 1; + end procedure decode; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out std_ulogic_array) is + variable i : integer := result'left; + variable upper_nibble : natural; + begin + index := index + 9; + if result'ascending then + while i <= result'right loop + if i /= result'right then + upper_nibble := character'pos(code(index))/16; + result(i + 1) := std_ulogic'val(upper_nibble); + else + upper_nibble := 0; + end if; + result(i) := std_ulogic'val(character'pos(code(index)) - upper_nibble*16); + i := i + 2; + index := index + 1; + end loop; + else + while i >= result'right loop + if i /= result'right then + upper_nibble := character'pos(code(index))/16; + result(i - 1) := std_ulogic'val(upper_nibble); + else + upper_nibble := 0; + end if; + result(i) := std_ulogic'val(character'pos(code(index)) - upper_nibble*16); + i := i - 2; + index := index + 1; + end loop; + end if; + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out string) is + begin + result := code(index + 9 to index + 9 + result'length - 1); + index := index + 9 + result'length; + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out bit_vector) is + constant n_bytes : natural := (result'length + 7) / 8; + variable result_temp : bit_vector(n_bytes * 8 - 1 downto 0); + begin + result_temp := from_byte_array(code(index + 9 to index + 9 + n_bytes - 1)); + result := result_temp(result'length - 1 downto 0); + + index := index + 9 + n_bytes; + end procedure decode; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out std_ulogic_vector) is + variable result_sula : std_ulogic_array(result'range); + begin + decode(code, index, result_sula); + result := std_ulogic_vector(result_sula); + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out complex) is + begin + decode(code, index, result.re); + decode(code, index, result.im); + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out complex_polar) is + begin + decode(code, index, result.mag); + decode(code, index, result.arg); + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out ieee.numeric_bit.unsigned) is + variable result_bv : bit_vector(result'range); + begin + decode(code, index, result_bv); + result := ieee.numeric_bit.unsigned(result_bv); + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out ieee.numeric_bit.signed) is + variable result_bv : bit_vector(result'range); + begin + decode(code, index, result_bv); + result := ieee.numeric_bit.signed(result_bv); + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out ieee.numeric_std.unsigned) is + variable result_slv : std_ulogic_vector(result'range); + begin + decode(code, index, result_slv); + result := ieee.numeric_std.unsigned(result_slv); + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out ieee.numeric_std.signed) is + variable result_slv : std_ulogic_vector(result'range); + begin + decode(code, index, result_slv); + result := ieee.numeric_std.signed(result_slv); + end; + + function encode_array_header ( + constant range_left1 : string; + constant range_right1 : string; + constant is_ascending1 : string; + constant range_left2 : string := ""; + constant range_right2 : string := ""; + constant is_ascending2 : string := "T") + return string is + begin + if range_left2 = "" then + return range_left1 & range_right1 & is_ascending1; + else + return range_left1 & range_right1 & is_ascending1 & range_left2 & range_right2 & is_ascending2; + end if; + end function encode_array_header; +end package body codec_builder_pkg; diff --git a/vunit/vhdl/data_types/src/data_types_context.vhd b/vunit/vhdl/data_types/src/data_types_context.vhd index fcc2b462d..b31fa7602 100644 --- a/vunit/vhdl/data_types/src/data_types_context.vhd +++ b/vunit/vhdl/data_types/src/data_types_context.vhd @@ -1,18 +1,18 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -context data_types_context is - library vunit_lib; - use vunit_lib.integer_vector_ptr_pkg.all; - use vunit_lib.integer_vector_ptr_pool_pkg.all; - use vunit_lib.integer_array_pkg.all; - use vunit_lib.string_ptr_pkg.all; - use vunit_lib.queue_pkg.all; - use vunit_lib.queue_pool_pkg.all; - use vunit_lib.string_ptr_pkg.all; - use vunit_lib.string_ptr_pool_pkg.all; - use vunit_lib.dict_pkg.all; -end context; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +context data_types_context is + library vunit_lib; + use vunit_lib.integer_vector_ptr_pkg.all; + use vunit_lib.integer_vector_ptr_pool_pkg.all; + use vunit_lib.integer_array_pkg.all; + use vunit_lib.string_ptr_pkg.all; + use vunit_lib.queue_pkg.all; + use vunit_lib.queue_pool_pkg.all; + use vunit_lib.string_ptr_pkg.all; + use vunit_lib.string_ptr_pool_pkg.all; + use vunit_lib.dict_pkg.all; +end context; diff --git a/vunit/vhdl/data_types/src/dict_pkg.vhd b/vunit/vhdl/data_types/src/dict_pkg.vhd index 07147acef..394cdc746 100644 --- a/vunit/vhdl/data_types/src/dict_pkg.vhd +++ b/vunit/vhdl/data_types/src/dict_pkg.vhd @@ -1,333 +1,333 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.string_ptr_pkg.all; -use work.string_ptr_pool_pkg.all; -use work.integer_vector_ptr_pkg.all; -use work.integer_vector_ptr_pool_pkg.all; - -package dict_pkg is - type dict_t is record - p_meta : integer_vector_ptr_t; - p_bucket_lengths : integer_vector_ptr_t; - p_bucket_keys : integer_vector_ptr_t; - p_bucket_values : integer_vector_ptr_t; - end record; - constant null_dict : dict_t := (others => null_ptr); - - impure function new_dict - return dict_t; - - procedure deallocate ( - variable dict : inout dict_t - ); - - procedure set ( - dict : dict_t; - key, value : string - ); - - impure function get ( - dict : dict_t; - key : string - ) return string; - - impure function has_key ( - dict : dict_t; - key : string - ) return boolean; - - impure function num_keys ( - dict : dict_t - ) return natural; - - procedure remove ( - dict : dict_t; - key : string - ); -end package; - -package body dict_pkg is - constant int_pool : integer_vector_ptr_pool_t := new_integer_vector_ptr_pool; - constant str_pool : string_ptr_pool_t := new_string_ptr_pool; - constant meta_num_keys : natural := 0; - constant meta_length : natural := meta_num_keys+1; - constant new_bucket_size : natural := 1; - - impure function new_dict - return dict_t is - variable dict : dict_t; - variable tmp : integer_vector_ptr_t; - constant num_buckets : natural := 1; - begin - dict := (p_meta => new_integer_vector_ptr(int_pool, meta_length), - p_bucket_lengths => new_integer_vector_ptr(int_pool, num_buckets), - p_bucket_keys => new_integer_vector_ptr(int_pool, num_buckets), - p_bucket_values => new_integer_vector_ptr(int_pool, num_buckets)); - set(dict.p_meta, meta_num_keys, 0); - for i in 0 to length(dict.p_bucket_lengths)-1 loop - -- Zero items in bucket - set(dict.p_bucket_lengths, i, 0); - tmp := new_integer_vector_ptr(int_pool, new_bucket_size); - set(dict.p_bucket_keys, i, to_integer(tmp)); - tmp := new_integer_vector_ptr(int_pool, new_bucket_size); - set(dict.p_bucket_values, i, to_integer(tmp)); - end loop; - return dict; - end; - - procedure deallocate ( - variable dict : inout dict_t - ) is - constant num_buckets : natural := length(dict.p_bucket_lengths); - - variable bucket_values : integer_vector_ptr_t; - variable bucket_keys : integer_vector_ptr_t; - variable bucket_length : natural; - - variable idx : natural; - variable key_hash : natural; - variable key : string_ptr_t; - variable value : string_ptr_t; - begin - for bucket_idx in 0 to num_buckets-1 loop - bucket_keys := to_integer_vector_ptr(get(dict.p_bucket_keys, bucket_idx)); - bucket_values := to_integer_vector_ptr(get(dict.p_bucket_values, bucket_idx)); - bucket_length := get(dict.p_bucket_lengths, bucket_idx); - for idx in 0 to bucket_length-1 loop - key := to_string_ptr(get(bucket_keys, idx)); - value := to_string_ptr(get(bucket_values, idx)); - recycle(str_pool, key); - recycle(str_pool, value); - end loop; - recycle(int_pool, bucket_values); - recycle(int_pool, bucket_keys); - end loop; - recycle(int_pool, dict.p_meta); - recycle(int_pool, dict.p_bucket_lengths); - recycle(int_pool, dict.p_bucket_values); - recycle(int_pool, dict.p_bucket_keys); - end; - - -- DJB2 hash - impure function hash ( - str : string - ) return natural is - variable value : natural := 5381; - begin - for i in str'range loop - value := (33*value + character'pos(str(i))) mod 2**(31-6); - end loop; - return value; - end; - - impure function get_value_ptr ( - dict : dict_t; - key_hash : natural; - key : string - ) return string_ptr_t is - constant num_buckets : natural := length(dict.p_bucket_lengths); - constant bucket_idx : natural := key_hash mod num_buckets; - constant bucket_length : natural := get(dict.p_bucket_lengths, bucket_idx); - constant bucket_values : integer_vector_ptr_t := to_integer_vector_ptr(get(dict.p_bucket_values, bucket_idx)); - constant bucket_keys : integer_vector_ptr_t := to_integer_vector_ptr(get(dict.p_bucket_keys, bucket_idx)); - begin - for i in 0 to bucket_length-1 loop - if to_string(to_string_ptr(get(bucket_keys, i))) = key then - return to_string_ptr(get(bucket_values, i)); - end if; - end loop; - return null_string_ptr; - end; - - procedure remove ( - dict : dict_t; - bucket_idx : natural; - i : natural; - deallocate_item : boolean := true - ) is - constant bucket_length : natural := get(dict.p_bucket_lengths, bucket_idx); - constant bucket_values : integer_vector_ptr_t := to_integer_vector_ptr(get(dict.p_bucket_values, bucket_idx)); - constant bucket_keys : integer_vector_ptr_t := to_integer_vector_ptr(get(dict.p_bucket_keys, bucket_idx)); - variable key, value : string_ptr_t; - begin - if deallocate_item then - key := to_string_ptr(get(bucket_keys, i)); - value := to_string_ptr(get(bucket_values, i)); - recycle(str_pool, key); - recycle(str_pool, value); - end if; - set(bucket_keys, i, get(bucket_keys, bucket_length-1)); - set(bucket_values, i, get(bucket_values, bucket_length-1)); - set(dict.p_bucket_lengths, bucket_idx, bucket_length-1); - set(dict.p_meta, meta_num_keys, num_keys(dict)-1); - end; - - procedure remove ( - dict : dict_t; - key_hash : natural; - key : string - ) is - constant num_buckets : natural := length(dict.p_bucket_lengths); - constant bucket_idx : natural := key_hash mod num_buckets; - constant bucket_length : natural := get(dict.p_bucket_lengths, bucket_idx); - constant bucket_keys : integer_vector_ptr_t := to_integer_vector_ptr(get(dict.p_bucket_keys, bucket_idx)); - begin - for i in 0 to bucket_length-1 loop - if to_string(to_string_ptr(get(bucket_keys, i))) = key then - remove(dict, bucket_idx, i); - return; - end if; - end loop; - end; - - procedure insert_new ( - dict : dict_t; - key_hash : natural; - key, value : string_ptr_t - ); - - procedure relocate_items ( - dict : dict_t; - old_num_buckets : natural - ) is - constant num_buckets : natural := length(dict.p_bucket_lengths); - variable bucket_values : integer_vector_ptr_t; - variable bucket_keys : integer_vector_ptr_t; - variable idx : natural; - variable key_hash : natural; - variable key : string_ptr_t; - variable value : string_ptr_t; - begin - for bucket_idx in 0 to old_num_buckets-1 loop - bucket_keys := to_integer_vector_ptr(get(dict.p_bucket_keys, bucket_idx)); - bucket_values := to_integer_vector_ptr(get(dict.p_bucket_values, bucket_idx)); - - idx := 0; - while idx < get(dict.p_bucket_lengths, bucket_idx) loop - key := to_string_ptr(get(bucket_keys, idx)); - - key_hash := hash(to_string(key)); - if key_hash mod num_buckets /= bucket_idx then - -- Key hash belongs in another bucket now - value := to_string_ptr(get(bucket_values, idx)); - - -- Move key - remove(dict, bucket_idx, idx, deallocate_item => false); - insert_new(dict, key_hash, key, value); - else - - idx := idx + 1; - end if; - end loop; - - resize(bucket_keys, get(dict.p_bucket_lengths, bucket_idx)); - resize(bucket_values, get(dict.p_bucket_lengths, bucket_idx)); - end loop; - end; - - procedure resize ( - dict : dict_t; - num_buckets : natural - ) is - constant old_num_buckets : natural := length(dict.p_bucket_lengths); - begin - resize(dict.p_bucket_lengths, num_buckets); - resize(dict.p_bucket_keys, num_buckets); - resize(dict.p_bucket_values, num_buckets); - - -- Create new buckets - for i in old_num_buckets to num_buckets-1 loop - set(dict.p_bucket_keys, i, to_integer(new_integer_vector_ptr(int_pool, new_bucket_size))); - set(dict.p_bucket_values, i, to_integer(new_integer_vector_ptr(int_pool, new_bucket_size))); - set(dict.p_bucket_lengths, i, 0); - end loop; - - relocate_items(dict, old_num_buckets); - end; - - procedure set ( - dict : dict_t; - key, value : string - ) is - constant key_hash : natural := hash(key); - constant old_value_ptr : string_ptr_t := get_value_ptr(dict, key_hash, key); - begin - if old_value_ptr /= null_string_ptr then - -- Reuse existing value storage - reallocate(old_value_ptr, value); - else - insert_new(dict, key_hash, new_string_ptr(str_pool, key), new_string_ptr(str_pool, value)); - end if; - end; - - procedure insert_new ( - dict : dict_t; - key_hash : natural; - key, value : string_ptr_t - ) is - constant num_buckets : natural := length(dict.p_bucket_lengths); - constant bucket_idx : natural := key_hash mod num_buckets; - constant bucket_length : natural := get(dict.p_bucket_lengths, bucket_idx); - constant bucket_values : integer_vector_ptr_t := to_integer_vector_ptr(get(dict.p_bucket_values, bucket_idx)); - constant bucket_keys : integer_vector_ptr_t := to_integer_vector_ptr(get(dict.p_bucket_keys, bucket_idx)); - constant bucket_max_length : natural := length(bucket_values); - constant num_keys : natural := get(dict.p_meta, meta_num_keys); - begin - if num_keys > num_buckets then - -- Average bucket length is larger than 1, reallocate - -- Occupancy is to high, resize - resize(dict, 2*num_buckets); - insert_new(dict, key_hash, key, value); - return; - elsif bucket_length = bucket_max_length then - -- Bucket size to small, resize - resize(bucket_keys, bucket_max_length+1); - resize(bucket_values, bucket_max_length+1); - end if; - set(dict.p_meta, meta_num_keys, num_keys+1); - set(dict.p_bucket_lengths, bucket_idx, bucket_length+1); - -- Create new value storage - set(bucket_keys, bucket_length, to_integer(key)); - set(bucket_values, bucket_length, to_integer(value)); - end; - - impure function get ( - dict : dict_t; - key : string - ) return string is - constant key_hash : natural := hash(key); - constant value_ptr : string_ptr_t := get_value_ptr(dict, key_hash, key); - begin - assert value_ptr /= null_string_ptr report "missing key '" & key & "'"; - return to_string(value_ptr); - end; - - impure function has_key ( - dict : dict_t; - key : string - ) return boolean is - constant key_hash : natural := hash(key); - begin - return get_value_ptr(dict, key_hash, key) /= null_string_ptr; - end; - - procedure remove ( - dict : dict_t; - key : string - ) is - constant key_hash : natural := hash(key); - begin - remove(dict, key_hash, key); - end; - - impure function num_keys ( - dict : dict_t - ) return natural is begin - return get(dict.p_meta, meta_num_keys); - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.string_ptr_pkg.all; +use work.string_ptr_pool_pkg.all; +use work.integer_vector_ptr_pkg.all; +use work.integer_vector_ptr_pool_pkg.all; + +package dict_pkg is + type dict_t is record + p_meta : integer_vector_ptr_t; + p_bucket_lengths : integer_vector_ptr_t; + p_bucket_keys : integer_vector_ptr_t; + p_bucket_values : integer_vector_ptr_t; + end record; + constant null_dict : dict_t := (others => null_ptr); + + impure function new_dict + return dict_t; + + procedure deallocate ( + variable dict : inout dict_t + ); + + procedure set ( + dict : dict_t; + key, value : string + ); + + impure function get ( + dict : dict_t; + key : string + ) return string; + + impure function has_key ( + dict : dict_t; + key : string + ) return boolean; + + impure function num_keys ( + dict : dict_t + ) return natural; + + procedure remove ( + dict : dict_t; + key : string + ); +end package; + +package body dict_pkg is + constant int_pool : integer_vector_ptr_pool_t := new_integer_vector_ptr_pool; + constant str_pool : string_ptr_pool_t := new_string_ptr_pool; + constant meta_num_keys : natural := 0; + constant meta_length : natural := meta_num_keys+1; + constant new_bucket_size : natural := 1; + + impure function new_dict + return dict_t is + variable dict : dict_t; + variable tmp : integer_vector_ptr_t; + constant num_buckets : natural := 1; + begin + dict := (p_meta => new_integer_vector_ptr(int_pool, meta_length), + p_bucket_lengths => new_integer_vector_ptr(int_pool, num_buckets), + p_bucket_keys => new_integer_vector_ptr(int_pool, num_buckets), + p_bucket_values => new_integer_vector_ptr(int_pool, num_buckets)); + set(dict.p_meta, meta_num_keys, 0); + for i in 0 to length(dict.p_bucket_lengths)-1 loop + -- Zero items in bucket + set(dict.p_bucket_lengths, i, 0); + tmp := new_integer_vector_ptr(int_pool, new_bucket_size); + set(dict.p_bucket_keys, i, to_integer(tmp)); + tmp := new_integer_vector_ptr(int_pool, new_bucket_size); + set(dict.p_bucket_values, i, to_integer(tmp)); + end loop; + return dict; + end; + + procedure deallocate ( + variable dict : inout dict_t + ) is + constant num_buckets : natural := length(dict.p_bucket_lengths); + + variable bucket_values : integer_vector_ptr_t; + variable bucket_keys : integer_vector_ptr_t; + variable bucket_length : natural; + + variable idx : natural; + variable key_hash : natural; + variable key : string_ptr_t; + variable value : string_ptr_t; + begin + for bucket_idx in 0 to num_buckets-1 loop + bucket_keys := to_integer_vector_ptr(get(dict.p_bucket_keys, bucket_idx)); + bucket_values := to_integer_vector_ptr(get(dict.p_bucket_values, bucket_idx)); + bucket_length := get(dict.p_bucket_lengths, bucket_idx); + for idx in 0 to bucket_length-1 loop + key := to_string_ptr(get(bucket_keys, idx)); + value := to_string_ptr(get(bucket_values, idx)); + recycle(str_pool, key); + recycle(str_pool, value); + end loop; + recycle(int_pool, bucket_values); + recycle(int_pool, bucket_keys); + end loop; + recycle(int_pool, dict.p_meta); + recycle(int_pool, dict.p_bucket_lengths); + recycle(int_pool, dict.p_bucket_values); + recycle(int_pool, dict.p_bucket_keys); + end; + + -- DJB2 hash + impure function hash ( + str : string + ) return natural is + variable value : natural := 5381; + begin + for i in str'range loop + value := (33*value + character'pos(str(i))) mod 2**(31-6); + end loop; + return value; + end; + + impure function get_value_ptr ( + dict : dict_t; + key_hash : natural; + key : string + ) return string_ptr_t is + constant num_buckets : natural := length(dict.p_bucket_lengths); + constant bucket_idx : natural := key_hash mod num_buckets; + constant bucket_length : natural := get(dict.p_bucket_lengths, bucket_idx); + constant bucket_values : integer_vector_ptr_t := to_integer_vector_ptr(get(dict.p_bucket_values, bucket_idx)); + constant bucket_keys : integer_vector_ptr_t := to_integer_vector_ptr(get(dict.p_bucket_keys, bucket_idx)); + begin + for i in 0 to bucket_length-1 loop + if to_string(to_string_ptr(get(bucket_keys, i))) = key then + return to_string_ptr(get(bucket_values, i)); + end if; + end loop; + return null_string_ptr; + end; + + procedure remove ( + dict : dict_t; + bucket_idx : natural; + i : natural; + deallocate_item : boolean := true + ) is + constant bucket_length : natural := get(dict.p_bucket_lengths, bucket_idx); + constant bucket_values : integer_vector_ptr_t := to_integer_vector_ptr(get(dict.p_bucket_values, bucket_idx)); + constant bucket_keys : integer_vector_ptr_t := to_integer_vector_ptr(get(dict.p_bucket_keys, bucket_idx)); + variable key, value : string_ptr_t; + begin + if deallocate_item then + key := to_string_ptr(get(bucket_keys, i)); + value := to_string_ptr(get(bucket_values, i)); + recycle(str_pool, key); + recycle(str_pool, value); + end if; + set(bucket_keys, i, get(bucket_keys, bucket_length-1)); + set(bucket_values, i, get(bucket_values, bucket_length-1)); + set(dict.p_bucket_lengths, bucket_idx, bucket_length-1); + set(dict.p_meta, meta_num_keys, num_keys(dict)-1); + end; + + procedure remove ( + dict : dict_t; + key_hash : natural; + key : string + ) is + constant num_buckets : natural := length(dict.p_bucket_lengths); + constant bucket_idx : natural := key_hash mod num_buckets; + constant bucket_length : natural := get(dict.p_bucket_lengths, bucket_idx); + constant bucket_keys : integer_vector_ptr_t := to_integer_vector_ptr(get(dict.p_bucket_keys, bucket_idx)); + begin + for i in 0 to bucket_length-1 loop + if to_string(to_string_ptr(get(bucket_keys, i))) = key then + remove(dict, bucket_idx, i); + return; + end if; + end loop; + end; + + procedure insert_new ( + dict : dict_t; + key_hash : natural; + key, value : string_ptr_t + ); + + procedure relocate_items ( + dict : dict_t; + old_num_buckets : natural + ) is + constant num_buckets : natural := length(dict.p_bucket_lengths); + variable bucket_values : integer_vector_ptr_t; + variable bucket_keys : integer_vector_ptr_t; + variable idx : natural; + variable key_hash : natural; + variable key : string_ptr_t; + variable value : string_ptr_t; + begin + for bucket_idx in 0 to old_num_buckets-1 loop + bucket_keys := to_integer_vector_ptr(get(dict.p_bucket_keys, bucket_idx)); + bucket_values := to_integer_vector_ptr(get(dict.p_bucket_values, bucket_idx)); + + idx := 0; + while idx < get(dict.p_bucket_lengths, bucket_idx) loop + key := to_string_ptr(get(bucket_keys, idx)); + + key_hash := hash(to_string(key)); + if key_hash mod num_buckets /= bucket_idx then + -- Key hash belongs in another bucket now + value := to_string_ptr(get(bucket_values, idx)); + + -- Move key + remove(dict, bucket_idx, idx, deallocate_item => false); + insert_new(dict, key_hash, key, value); + else + + idx := idx + 1; + end if; + end loop; + + resize(bucket_keys, get(dict.p_bucket_lengths, bucket_idx)); + resize(bucket_values, get(dict.p_bucket_lengths, bucket_idx)); + end loop; + end; + + procedure resize ( + dict : dict_t; + num_buckets : natural + ) is + constant old_num_buckets : natural := length(dict.p_bucket_lengths); + begin + resize(dict.p_bucket_lengths, num_buckets); + resize(dict.p_bucket_keys, num_buckets); + resize(dict.p_bucket_values, num_buckets); + + -- Create new buckets + for i in old_num_buckets to num_buckets-1 loop + set(dict.p_bucket_keys, i, to_integer(new_integer_vector_ptr(int_pool, new_bucket_size))); + set(dict.p_bucket_values, i, to_integer(new_integer_vector_ptr(int_pool, new_bucket_size))); + set(dict.p_bucket_lengths, i, 0); + end loop; + + relocate_items(dict, old_num_buckets); + end; + + procedure set ( + dict : dict_t; + key, value : string + ) is + constant key_hash : natural := hash(key); + constant old_value_ptr : string_ptr_t := get_value_ptr(dict, key_hash, key); + begin + if old_value_ptr /= null_string_ptr then + -- Reuse existing value storage + reallocate(old_value_ptr, value); + else + insert_new(dict, key_hash, new_string_ptr(str_pool, key), new_string_ptr(str_pool, value)); + end if; + end; + + procedure insert_new ( + dict : dict_t; + key_hash : natural; + key, value : string_ptr_t + ) is + constant num_buckets : natural := length(dict.p_bucket_lengths); + constant bucket_idx : natural := key_hash mod num_buckets; + constant bucket_length : natural := get(dict.p_bucket_lengths, bucket_idx); + constant bucket_values : integer_vector_ptr_t := to_integer_vector_ptr(get(dict.p_bucket_values, bucket_idx)); + constant bucket_keys : integer_vector_ptr_t := to_integer_vector_ptr(get(dict.p_bucket_keys, bucket_idx)); + constant bucket_max_length : natural := length(bucket_values); + constant num_keys : natural := get(dict.p_meta, meta_num_keys); + begin + if num_keys > num_buckets then + -- Average bucket length is larger than 1, reallocate + -- Occupancy is to high, resize + resize(dict, 2*num_buckets); + insert_new(dict, key_hash, key, value); + return; + elsif bucket_length = bucket_max_length then + -- Bucket size to small, resize + resize(bucket_keys, bucket_max_length+1); + resize(bucket_values, bucket_max_length+1); + end if; + set(dict.p_meta, meta_num_keys, num_keys+1); + set(dict.p_bucket_lengths, bucket_idx, bucket_length+1); + -- Create new value storage + set(bucket_keys, bucket_length, to_integer(key)); + set(bucket_values, bucket_length, to_integer(value)); + end; + + impure function get ( + dict : dict_t; + key : string + ) return string is + constant key_hash : natural := hash(key); + constant value_ptr : string_ptr_t := get_value_ptr(dict, key_hash, key); + begin + assert value_ptr /= null_string_ptr report "missing key '" & key & "'"; + return to_string(value_ptr); + end; + + impure function has_key ( + dict : dict_t; + key : string + ) return boolean is + constant key_hash : natural := hash(key); + begin + return get_value_ptr(dict, key_hash, key) /= null_string_ptr; + end; + + procedure remove ( + dict : dict_t; + key : string + ) is + constant key_hash : natural := hash(key); + begin + remove(dict, key_hash, key); + end; + + impure function num_keys ( + dict : dict_t + ) return natural is begin + return get(dict.p_meta, meta_num_keys); + end; + +end package body; diff --git a/vunit/vhdl/data_types/src/integer_array_pkg-body.vhd b/vunit/vhdl/data_types/src/integer_array_pkg-body.vhd index 7def909f4..c4f5ca6ec 100644 --- a/vunit/vhdl/data_types/src/integer_array_pkg-body.vhd +++ b/vunit/vhdl/data_types/src/integer_array_pkg-body.vhd @@ -1,456 +1,456 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use std.textio.all; - -package body integer_array_pkg is - type binary_file_t is file of character; - - procedure read_byte ( - file fread : binary_file_t; - variable result : out integer - ) is - variable chr : character; - begin - assert not endfile(fread) report "Premature end of file"; - read(fread, chr); - result := character'pos(chr); - end; - - procedure write_byte ( - file fwrite : binary_file_t; - value : natural range 0 to 255 - ) is begin - write(fwrite, character'val(value)); - end; - - procedure read_integer ( - file fread : binary_file_t; - variable result : out integer; - bytes_per_word : natural range 1 to 4 := 4; - is_signed : boolean := true - ) is - variable tmp, byte : integer; - begin - tmp := 0; - for i in 0 to bytes_per_word - 1 loop - read_byte(fread, byte); - if i = bytes_per_word-1 and is_signed and byte >= 128 then - byte := byte - 256; - end if; - tmp := tmp + byte*256**i; - end loop; - result := tmp; - end; - - procedure write_integer ( - file fwrite : binary_file_t; - value : integer; - bytes_per_word : natural range 1 to 4 := 4; - is_signed : boolean := true - ) is - variable tmp, byte : integer; - begin - tmp := value; - for i in 0 to bytes_per_word-1 loop - byte := tmp mod 256; - write_byte(fwrite, byte); - tmp := (tmp - byte)/256; - end loop; - end; - - impure function length ( - arr : integer_array_t - ) return integer is begin - return arr.length; - end; - - impure function width ( - arr : integer_array_t - ) return integer is begin - return arr.width; - end; - - impure function height ( - arr : integer_array_t - ) return integer is begin - return arr.height; - end; - - impure function depth ( - arr : integer_array_t - ) return integer is begin - return arr.depth; - end; - - impure function bit_width ( - arr : integer_array_t - ) return integer is begin - return arr.bit_width; - end; - - impure function is_signed ( - arr : integer_array_t - ) return boolean is begin - return arr.is_signed; - end; - - impure function bytes_per_word ( - arr : integer_array_t - ) return integer is begin - return (arr.bit_width + 7)/8; - end; - - impure function lower_limit ( - arr : integer_array_t - ) return integer is begin - return arr.lower_limit; - end; - - impure function upper_limit ( - arr : integer_array_t - ) return integer is begin - return arr.upper_limit; - end; - - procedure validate_data ( - arr : integer_array_t - ) is begin - assert arr.data /= null_ptr report "Data is not allocated"; - end; - - procedure validate_bounds ( - name : string; - val, bound : integer - ) is begin - assert 0 <= val and val < bound - report (name & "=" & integer'image(val) & " " & - "is out of bounds " & - "0 <= " & name &" < " & integer'image(bound)); - end; - - procedure validate_value ( - arr : integer_array_t; - value : integer - ) is begin - assert arr.lower_limit <= value and value <= arr.upper_limit - report ("value=" & integer'image(value) & " " & - "is out of bounds " & - integer'image(arr.lower_limit) & - " <= value <= " & - integer'image(arr.upper_limit)); - end; - - procedure realloc ( - variable arr : inout integer_array_t; - new_length : integer - ) is begin - if arr.data = null_ptr then - -- Array was empty - arr.data := new_integer_vector_ptr(new_length); - elsif new_length > length(arr.data) then - -- Reallocate if more length is required - -- Add extra length to avoid excessive reallocation when appending - resize(arr.data, new_length + length(arr.data)); - end if; - arr.length := new_length; - end; - - procedure reshape ( - variable arr : inout integer_array_t; - length : integer - ) is begin - reshape(arr, length, 1, 1); - end; - - procedure reshape ( - variable arr : inout integer_array_t; - width, height : integer - ) is begin - reshape(arr, width, height, 1); - end; - - procedure reshape ( - variable arr : inout integer_array_t; - width, height, depth : integer - ) is begin - arr.width := width; - arr.height := height; - arr.depth := depth; - realloc(arr, width*height*depth); - end; - - procedure append ( - variable arr : inout integer_array_t; - value : integer - ) is begin - reshape(arr, arr.length+1); - set(arr, arr.length-1, value); - end; - - impure function get ( - arr : integer_array_t; - idx : integer - ) return integer is begin - validate_data(arr); - validate_bounds("idx", idx, arr.length); - return get(arr.data, idx); - end; - - impure function get ( - arr : integer_array_t; - x, y : integer - ) return integer is begin - validate_data(arr); - validate_bounds("x", x, arr.width); - validate_bounds("y", y, arr.height); - return get(arr.data, y*arr.width + x); - end; - - impure function get ( - arr : integer_array_t; - x,y,z : integer - ) return integer is begin - validate_data(arr); - validate_bounds("x", x, arr.width); - validate_bounds("y", y, arr.height); - validate_bounds("z", z, arr.depth); - return get(arr.data, (y*arr.width + x)*arr.depth + z); - end; - - procedure set ( - arr : integer_array_t; - idx : integer; - value : integer - ) is begin - validate_data(arr); - validate_bounds("idx", idx, arr.length); - validate_value(arr, value); - set(arr.data, idx, value); - end; - - procedure set ( - arr : integer_array_t; - x,y : integer; - value : integer - ) is begin - validate_data(arr); - validate_bounds("x", x, arr.width); - validate_bounds("y", y, arr.height); - validate_value(arr, value); - set(arr.data, y*arr.width + x, value); - end; - - procedure set ( - arr : integer_array_t; - x,y,z : integer; - value : integer - ) is begin - validate_data(arr); - validate_bounds("x", x, arr.width); - validate_bounds("y", y, arr.height); - validate_bounds("z", z, arr.depth); - validate_value(arr, value); - set(arr.data, (y*arr.width + x)*arr.depth + z, value); - end; - - procedure set_word_size ( - variable arr : inout integer_array_t; - bit_width : natural := 32; - is_signed : boolean := true - ) is begin - assert (1 <= bit_width and bit_width < 32) or (bit_width = 32 and is_signed) - report "Unsupported combination of bit_width and is_signed"; - arr.bit_width := bit_width; - arr.is_signed := is_signed; - if arr.is_signed then - if arr.bit_width = 32 then - -- avoid overflow warning - arr.lower_limit := integer'left; - arr.upper_limit := integer'right; - else - arr.lower_limit := -2**(arr.bit_width-1); - arr.upper_limit := 2**(arr.bit_width-1)-1; - end if; - else - arr.lower_limit := 0; - if arr.bit_width = 31 then - arr.upper_limit := integer'right; - else - arr.upper_limit := 2**arr.bit_width-1; - end if; - end if; - end; - - impure function new_1d ( - length : integer := 0; - bit_width : natural := 32; - is_signed : boolean := true - ) return integer_array_t is begin - return new_3d(width => length, - height => 1, - depth => 1, - bit_width => bit_width, - is_signed => is_signed); - end; - - impure function new_2d ( - width : integer := 0; - height : integer := 0; - bit_width : natural := 32; - is_signed : boolean := true - ) return integer_array_t is begin - return new_3d(width => width, - height => height, - depth => 1, - bit_width => bit_width, - is_signed => is_signed); - end; - - impure function new_3d ( - width : integer := 0; - height : integer := 0; - depth : integer := 0; - bit_width : natural := 32; - is_signed : boolean := true - ) return integer_array_t is - variable arr : integer_array_t := null_integer_array; - begin - set_word_size(arr, bit_width, is_signed); - arr.length := width * height * depth; - arr.width := width; - arr.height := height; - arr.depth := depth; - if arr.length > 0 then - arr.data := new_integer_vector_ptr(arr.length); - else - arr.data := null_ptr; - end if; - return arr; - end; - - impure function copy ( - arr : integer_array_t - ) return integer_array_t is - variable arr_copy : integer_array_t; - begin - arr_copy := new_3d(arr.width, arr.height, - arr.depth, arr.bit_width, arr.is_signed); - for i in 0 to arr.length-1 loop - set(arr_copy, i, get(arr, i)); - end loop; - return arr_copy; - end; - - procedure deallocate ( - variable arr : inout integer_array_t - ) is begin - if arr.data /= null_ptr then - deallocate(arr.data); - end if; - arr := null_integer_array; - end; - - impure function is_null ( - arr : integer_array_t - ) return boolean is begin - return arr = null_integer_array; - end; - - procedure save_csv ( - arr : integer_array_t; - file_name : string - ) is - file fwrite : text; - variable l : line; - begin - file_open(fwrite, file_name, write_mode); - for y in 0 to arr.height-1 loop - for x in 0 to arr.width-1 loop - for z in 0 to arr.depth-1 loop - write(l, integer'image(get(arr, x, y, z))); - if x /= arr.width-1 or z /= arr.depth-1 then - write(l, ','); - end if; - end loop; - end loop; - writeline(fwrite, l); - end loop; - file_close(fwrite); - end; - - impure function load_csv ( - file_name : string; - bit_width : natural := 32; - is_signed : boolean := true - ) return integer_array_t is - variable arr : integer_array_t; - file fread : text; - variable l : line; - variable tmp : integer; - variable ctmp : character; - variable is_good : boolean; - variable width : integer := 0; - variable height : integer := 0; - begin - arr := new_1d(bit_width => bit_width, is_signed => is_signed); - file_open(fread, file_name, read_mode); - while not endfile(fread) loop - readline(fread, l); - height := height + 1; - loop - read(l, tmp, is_good); - exit when not is_good; - if height = 1 then - width := width + 1; - end if; - append(arr, tmp); - read(l, ctmp, is_good); - exit when not is_good; - end loop; - end loop; - file_close(fread); - reshape(arr, width, height); - return arr; - end; - - procedure save_raw ( - arr : integer_array_t; - file_name : string - ) is - file fwrite : binary_file_t; - begin - file_open(fwrite, file_name, write_mode); - for idx in 0 to arr.length-1 loop - write_integer(fwrite, - get(arr, idx), - bytes_per_word => (arr.bit_width+7)/8, - is_signed => arr.is_signed); - end loop; - file_close(fwrite); - end; - - impure function load_raw ( - file_name : string; - bit_width : natural := 32; - is_signed : boolean := true - ) return integer_array_t is - variable arr : integer_array_t; - file fread : binary_file_t; - variable tmp : integer; - begin - arr := new_1d(bit_width => bit_width, is_signed => is_signed); - file_open(fread, file_name, read_mode); - while not endfile(fread) loop - read_integer(fread, tmp, - bytes_per_word => (arr.bit_width+7)/8, - is_signed => arr.is_signed); - append(arr, tmp); - end loop; - file_close(fread); - return arr; - end; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use std.textio.all; + +package body integer_array_pkg is + type binary_file_t is file of character; + + procedure read_byte ( + file fread : binary_file_t; + variable result : out integer + ) is + variable chr : character; + begin + assert not endfile(fread) report "Premature end of file"; + read(fread, chr); + result := character'pos(chr); + end; + + procedure write_byte ( + file fwrite : binary_file_t; + value : natural range 0 to 255 + ) is begin + write(fwrite, character'val(value)); + end; + + procedure read_integer ( + file fread : binary_file_t; + variable result : out integer; + bytes_per_word : natural range 1 to 4 := 4; + is_signed : boolean := true + ) is + variable tmp, byte : integer; + begin + tmp := 0; + for i in 0 to bytes_per_word - 1 loop + read_byte(fread, byte); + if i = bytes_per_word-1 and is_signed and byte >= 128 then + byte := byte - 256; + end if; + tmp := tmp + byte*256**i; + end loop; + result := tmp; + end; + + procedure write_integer ( + file fwrite : binary_file_t; + value : integer; + bytes_per_word : natural range 1 to 4 := 4; + is_signed : boolean := true + ) is + variable tmp, byte : integer; + begin + tmp := value; + for i in 0 to bytes_per_word-1 loop + byte := tmp mod 256; + write_byte(fwrite, byte); + tmp := (tmp - byte)/256; + end loop; + end; + + impure function length ( + arr : integer_array_t + ) return integer is begin + return arr.length; + end; + + impure function width ( + arr : integer_array_t + ) return integer is begin + return arr.width; + end; + + impure function height ( + arr : integer_array_t + ) return integer is begin + return arr.height; + end; + + impure function depth ( + arr : integer_array_t + ) return integer is begin + return arr.depth; + end; + + impure function bit_width ( + arr : integer_array_t + ) return integer is begin + return arr.bit_width; + end; + + impure function is_signed ( + arr : integer_array_t + ) return boolean is begin + return arr.is_signed; + end; + + impure function bytes_per_word ( + arr : integer_array_t + ) return integer is begin + return (arr.bit_width + 7)/8; + end; + + impure function lower_limit ( + arr : integer_array_t + ) return integer is begin + return arr.lower_limit; + end; + + impure function upper_limit ( + arr : integer_array_t + ) return integer is begin + return arr.upper_limit; + end; + + procedure validate_data ( + arr : integer_array_t + ) is begin + assert arr.data /= null_ptr report "Data is not allocated"; + end; + + procedure validate_bounds ( + name : string; + val, bound : integer + ) is begin + assert 0 <= val and val < bound + report (name & "=" & integer'image(val) & " " & + "is out of bounds " & + "0 <= " & name &" < " & integer'image(bound)); + end; + + procedure validate_value ( + arr : integer_array_t; + value : integer + ) is begin + assert arr.lower_limit <= value and value <= arr.upper_limit + report ("value=" & integer'image(value) & " " & + "is out of bounds " & + integer'image(arr.lower_limit) & + " <= value <= " & + integer'image(arr.upper_limit)); + end; + + procedure realloc ( + variable arr : inout integer_array_t; + new_length : integer + ) is begin + if arr.data = null_ptr then + -- Array was empty + arr.data := new_integer_vector_ptr(new_length); + elsif new_length > length(arr.data) then + -- Reallocate if more length is required + -- Add extra length to avoid excessive reallocation when appending + resize(arr.data, new_length + length(arr.data)); + end if; + arr.length := new_length; + end; + + procedure reshape ( + variable arr : inout integer_array_t; + length : integer + ) is begin + reshape(arr, length, 1, 1); + end; + + procedure reshape ( + variable arr : inout integer_array_t; + width, height : integer + ) is begin + reshape(arr, width, height, 1); + end; + + procedure reshape ( + variable arr : inout integer_array_t; + width, height, depth : integer + ) is begin + arr.width := width; + arr.height := height; + arr.depth := depth; + realloc(arr, width*height*depth); + end; + + procedure append ( + variable arr : inout integer_array_t; + value : integer + ) is begin + reshape(arr, arr.length+1); + set(arr, arr.length-1, value); + end; + + impure function get ( + arr : integer_array_t; + idx : integer + ) return integer is begin + validate_data(arr); + validate_bounds("idx", idx, arr.length); + return get(arr.data, idx); + end; + + impure function get ( + arr : integer_array_t; + x, y : integer + ) return integer is begin + validate_data(arr); + validate_bounds("x", x, arr.width); + validate_bounds("y", y, arr.height); + return get(arr.data, y*arr.width + x); + end; + + impure function get ( + arr : integer_array_t; + x,y,z : integer + ) return integer is begin + validate_data(arr); + validate_bounds("x", x, arr.width); + validate_bounds("y", y, arr.height); + validate_bounds("z", z, arr.depth); + return get(arr.data, (y*arr.width + x)*arr.depth + z); + end; + + procedure set ( + arr : integer_array_t; + idx : integer; + value : integer + ) is begin + validate_data(arr); + validate_bounds("idx", idx, arr.length); + validate_value(arr, value); + set(arr.data, idx, value); + end; + + procedure set ( + arr : integer_array_t; + x,y : integer; + value : integer + ) is begin + validate_data(arr); + validate_bounds("x", x, arr.width); + validate_bounds("y", y, arr.height); + validate_value(arr, value); + set(arr.data, y*arr.width + x, value); + end; + + procedure set ( + arr : integer_array_t; + x,y,z : integer; + value : integer + ) is begin + validate_data(arr); + validate_bounds("x", x, arr.width); + validate_bounds("y", y, arr.height); + validate_bounds("z", z, arr.depth); + validate_value(arr, value); + set(arr.data, (y*arr.width + x)*arr.depth + z, value); + end; + + procedure set_word_size ( + variable arr : inout integer_array_t; + bit_width : natural := 32; + is_signed : boolean := true + ) is begin + assert (1 <= bit_width and bit_width < 32) or (bit_width = 32 and is_signed) + report "Unsupported combination of bit_width and is_signed"; + arr.bit_width := bit_width; + arr.is_signed := is_signed; + if arr.is_signed then + if arr.bit_width = 32 then + -- avoid overflow warning + arr.lower_limit := integer'left; + arr.upper_limit := integer'right; + else + arr.lower_limit := -2**(arr.bit_width-1); + arr.upper_limit := 2**(arr.bit_width-1)-1; + end if; + else + arr.lower_limit := 0; + if arr.bit_width = 31 then + arr.upper_limit := integer'right; + else + arr.upper_limit := 2**arr.bit_width-1; + end if; + end if; + end; + + impure function new_1d ( + length : integer := 0; + bit_width : natural := 32; + is_signed : boolean := true + ) return integer_array_t is begin + return new_3d(width => length, + height => 1, + depth => 1, + bit_width => bit_width, + is_signed => is_signed); + end; + + impure function new_2d ( + width : integer := 0; + height : integer := 0; + bit_width : natural := 32; + is_signed : boolean := true + ) return integer_array_t is begin + return new_3d(width => width, + height => height, + depth => 1, + bit_width => bit_width, + is_signed => is_signed); + end; + + impure function new_3d ( + width : integer := 0; + height : integer := 0; + depth : integer := 0; + bit_width : natural := 32; + is_signed : boolean := true + ) return integer_array_t is + variable arr : integer_array_t := null_integer_array; + begin + set_word_size(arr, bit_width, is_signed); + arr.length := width * height * depth; + arr.width := width; + arr.height := height; + arr.depth := depth; + if arr.length > 0 then + arr.data := new_integer_vector_ptr(arr.length); + else + arr.data := null_ptr; + end if; + return arr; + end; + + impure function copy ( + arr : integer_array_t + ) return integer_array_t is + variable arr_copy : integer_array_t; + begin + arr_copy := new_3d(arr.width, arr.height, + arr.depth, arr.bit_width, arr.is_signed); + for i in 0 to arr.length-1 loop + set(arr_copy, i, get(arr, i)); + end loop; + return arr_copy; + end; + + procedure deallocate ( + variable arr : inout integer_array_t + ) is begin + if arr.data /= null_ptr then + deallocate(arr.data); + end if; + arr := null_integer_array; + end; + + impure function is_null ( + arr : integer_array_t + ) return boolean is begin + return arr = null_integer_array; + end; + + procedure save_csv ( + arr : integer_array_t; + file_name : string + ) is + file fwrite : text; + variable l : line; + begin + file_open(fwrite, file_name, write_mode); + for y in 0 to arr.height-1 loop + for x in 0 to arr.width-1 loop + for z in 0 to arr.depth-1 loop + write(l, integer'image(get(arr, x, y, z))); + if x /= arr.width-1 or z /= arr.depth-1 then + write(l, ','); + end if; + end loop; + end loop; + writeline(fwrite, l); + end loop; + file_close(fwrite); + end; + + impure function load_csv ( + file_name : string; + bit_width : natural := 32; + is_signed : boolean := true + ) return integer_array_t is + variable arr : integer_array_t; + file fread : text; + variable l : line; + variable tmp : integer; + variable ctmp : character; + variable is_good : boolean; + variable width : integer := 0; + variable height : integer := 0; + begin + arr := new_1d(bit_width => bit_width, is_signed => is_signed); + file_open(fread, file_name, read_mode); + while not endfile(fread) loop + readline(fread, l); + height := height + 1; + loop + read(l, tmp, is_good); + exit when not is_good; + if height = 1 then + width := width + 1; + end if; + append(arr, tmp); + read(l, ctmp, is_good); + exit when not is_good; + end loop; + end loop; + file_close(fread); + reshape(arr, width, height); + return arr; + end; + + procedure save_raw ( + arr : integer_array_t; + file_name : string + ) is + file fwrite : binary_file_t; + begin + file_open(fwrite, file_name, write_mode); + for idx in 0 to arr.length-1 loop + write_integer(fwrite, + get(arr, idx), + bytes_per_word => (arr.bit_width+7)/8, + is_signed => arr.is_signed); + end loop; + file_close(fwrite); + end; + + impure function load_raw ( + file_name : string; + bit_width : natural := 32; + is_signed : boolean := true + ) return integer_array_t is + variable arr : integer_array_t; + file fread : binary_file_t; + variable tmp : integer; + begin + arr := new_1d(bit_width => bit_width, is_signed => is_signed); + file_open(fread, file_name, read_mode); + while not endfile(fread) loop + read_integer(fread, tmp, + bytes_per_word => (arr.bit_width+7)/8, + is_signed => arr.is_signed); + append(arr, tmp); + end loop; + file_close(fread); + return arr; + end; +end package body; diff --git a/vunit/vhdl/data_types/src/integer_array_pkg.vhd b/vunit/vhdl/data_types/src/integer_array_pkg.vhd index 9f5e0ea37..cc20a3bf0 100644 --- a/vunit/vhdl/data_types/src/integer_array_pkg.vhd +++ b/vunit/vhdl/data_types/src/integer_array_pkg.vhd @@ -1,181 +1,181 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.integer_vector_ptr_pkg.all; - -package integer_array_pkg is - type integer_array_t is record - -- All fields are considered private, use functions to access these - length : natural; - width : natural; - height : natural; - depth : natural; - bit_width : natural; - is_signed : boolean; - lower_limit : integer; - upper_limit : integer; - data : integer_vector_ptr_t; - end record; - - -- Ensure null_integer_array is the default VHDL value of the record - constant null_integer_array : integer_array_t := ( - length => 0, - width => 0, - height => 0, - depth => 0, - bit_width => 0, - is_signed => false, - lower_limit => integer'low, - upper_limit => integer'low, - data => null_ptr - ); - - type integer_array_vec_t is array (natural range <>) of integer_array_t; - - impure function new_1d ( - length : integer := 0; - bit_width : natural := 32; - is_signed : boolean := true - ) return integer_array_t; - - impure function new_2d ( - width : integer := 0; - height : integer := 0; - bit_width : natural := 32; - is_signed : boolean := true - ) return integer_array_t; - - impure function new_3d ( - width : integer := 0; - height : integer := 0; - depth : integer := 0; - bit_width : natural := 32; - is_signed : boolean := true - ) return integer_array_t; - - impure function copy ( - arr : integer_array_t - ) return integer_array_t; - - impure function load_csv ( - file_name : string; - bit_width : natural := 32; - is_signed : boolean := true - ) return integer_array_t; - - impure function load_raw ( - file_name : string; - bit_width : natural := 32; - is_signed : boolean := true - ) return integer_array_t; - - procedure deallocate ( - variable arr : inout integer_array_t - ); - - impure function is_null ( - arr : integer_array_t - ) return boolean; - - impure function length ( - arr : integer_array_t - ) return integer; - - impure function width ( - arr : integer_array_t - ) return integer; - - impure function height ( - arr : integer_array_t - ) return integer; - - impure function depth ( - arr : integer_array_t - ) return integer; - - impure function bit_width ( - arr : integer_array_t - ) return integer; - - impure function is_signed ( - arr : integer_array_t - ) return boolean; - - impure function bytes_per_word ( - arr : integer_array_t - ) return integer; - - impure function lower_limit ( - arr : integer_array_t - ) return integer; - - impure function upper_limit ( - arr : integer_array_t - ) return integer; - - impure function get ( - arr : integer_array_t; - idx : integer - ) return integer; - - impure function get ( - arr : integer_array_t; - x,y : integer - ) return integer; - - impure function get ( - arr : integer_array_t; - x,y,z : integer - ) return integer; - - procedure set ( - arr : integer_array_t; - idx : integer; - value : integer - ); - - procedure set ( - arr : integer_array_t; - x,y : integer; - value : integer - ); - - procedure set ( - arr : integer_array_t; - x,y,z : integer; - value : integer - ); - - procedure append ( - variable arr : inout integer_array_t; - value : integer - ); - - procedure reshape ( - variable arr : inout integer_array_t; - length : integer - ); - - procedure reshape ( - variable arr : inout integer_array_t; - width, height : integer - ); - - procedure reshape ( - variable arr : inout integer_array_t; - width, height, depth : integer - ); - - procedure save_csv ( - arr : integer_array_t; - file_name : string - ); - - procedure save_raw ( - arr : integer_array_t; - file_name : string - ); -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.integer_vector_ptr_pkg.all; + +package integer_array_pkg is + type integer_array_t is record + -- All fields are considered private, use functions to access these + length : natural; + width : natural; + height : natural; + depth : natural; + bit_width : natural; + is_signed : boolean; + lower_limit : integer; + upper_limit : integer; + data : integer_vector_ptr_t; + end record; + + -- Ensure null_integer_array is the default VHDL value of the record + constant null_integer_array : integer_array_t := ( + length => 0, + width => 0, + height => 0, + depth => 0, + bit_width => 0, + is_signed => false, + lower_limit => integer'low, + upper_limit => integer'low, + data => null_ptr + ); + + type integer_array_vec_t is array (natural range <>) of integer_array_t; + + impure function new_1d ( + length : integer := 0; + bit_width : natural := 32; + is_signed : boolean := true + ) return integer_array_t; + + impure function new_2d ( + width : integer := 0; + height : integer := 0; + bit_width : natural := 32; + is_signed : boolean := true + ) return integer_array_t; + + impure function new_3d ( + width : integer := 0; + height : integer := 0; + depth : integer := 0; + bit_width : natural := 32; + is_signed : boolean := true + ) return integer_array_t; + + impure function copy ( + arr : integer_array_t + ) return integer_array_t; + + impure function load_csv ( + file_name : string; + bit_width : natural := 32; + is_signed : boolean := true + ) return integer_array_t; + + impure function load_raw ( + file_name : string; + bit_width : natural := 32; + is_signed : boolean := true + ) return integer_array_t; + + procedure deallocate ( + variable arr : inout integer_array_t + ); + + impure function is_null ( + arr : integer_array_t + ) return boolean; + + impure function length ( + arr : integer_array_t + ) return integer; + + impure function width ( + arr : integer_array_t + ) return integer; + + impure function height ( + arr : integer_array_t + ) return integer; + + impure function depth ( + arr : integer_array_t + ) return integer; + + impure function bit_width ( + arr : integer_array_t + ) return integer; + + impure function is_signed ( + arr : integer_array_t + ) return boolean; + + impure function bytes_per_word ( + arr : integer_array_t + ) return integer; + + impure function lower_limit ( + arr : integer_array_t + ) return integer; + + impure function upper_limit ( + arr : integer_array_t + ) return integer; + + impure function get ( + arr : integer_array_t; + idx : integer + ) return integer; + + impure function get ( + arr : integer_array_t; + x,y : integer + ) return integer; + + impure function get ( + arr : integer_array_t; + x,y,z : integer + ) return integer; + + procedure set ( + arr : integer_array_t; + idx : integer; + value : integer + ); + + procedure set ( + arr : integer_array_t; + x,y : integer; + value : integer + ); + + procedure set ( + arr : integer_array_t; + x,y,z : integer; + value : integer + ); + + procedure append ( + variable arr : inout integer_array_t; + value : integer + ); + + procedure reshape ( + variable arr : inout integer_array_t; + length : integer + ); + + procedure reshape ( + variable arr : inout integer_array_t; + width, height : integer + ); + + procedure reshape ( + variable arr : inout integer_array_t; + width, height, depth : integer + ); + + procedure save_csv ( + arr : integer_array_t; + file_name : string + ); + + procedure save_raw ( + arr : integer_array_t; + file_name : string + ); +end package; diff --git a/vunit/vhdl/data_types/src/integer_vector_ptr_pkg-body-200x.vhd b/vunit/vhdl/data_types/src/integer_vector_ptr_pkg-body-200x.vhd index 03fcbea51..e9fb86110 100644 --- a/vunit/vhdl/data_types/src/integer_vector_ptr_pkg-body-200x.vhd +++ b/vunit/vhdl/data_types/src/integer_vector_ptr_pkg-body-200x.vhd @@ -1,225 +1,225 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -package body integer_vector_ptr_pkg is - type integer_vector_ptr_storage_t is protected - impure function new_integer_vector_ptr ( - len : natural := 0; - value : val_t := 0 - ) return natural; - - procedure deallocate ( - ref : natural - ); - - impure function length ( - ref : natural - ) return integer; - - procedure set ( - ref : natural; - index : natural; - value : val_t - ); - - impure function get ( - ref : natural; - index : natural - ) return val_t; - - procedure reallocate ( - ref : natural; - len : natural; - value : val_t := 0 - ); - - procedure resize ( - ref : natural; - len : natural; - drop : natural := 0; - value : val_t := 0 - ); - end protected; - - type integer_vector_ptr_storage_t is protected body - variable current_index : integer := 0; - variable ptrs : vava_t := null; - - impure function new_integer_vector_ptr ( - len : natural := 0; - value : val_t := 0 - ) return natural is - variable old_ptrs : vava_t; - variable retval : ptr_t := (ref => current_index); - begin - if ptrs = null then - ptrs := new vav_t'(0 => null); - elsif ptrs'length <= current_index then - -- Reallocate ptr pointers to larger ptr - -- Use more size to trade size for speed - old_ptrs := ptrs; - ptrs := new vav_t'(0 to ptrs'length + 2**16 => null); - for i in old_ptrs'range loop - ptrs(i) := old_ptrs(i); - end loop; - deallocate(old_ptrs); - end if; - ptrs(current_index) := new integer_vector_t'(0 to len-1 => value); - current_index := current_index + 1; - return retval.ref; - end; - - procedure deallocate ( - ref : natural - ) is begin - deallocate(ptrs(ref)); - ptrs(ref) := null; - end; - - impure function length ( - ref : natural - ) return integer is begin - return ptrs(ref)'length; - end; - - procedure set ( - ref : natural; - index : natural; - value : val_t - ) is begin - ptrs(ref)(index) := value; - end; - - impure function get ( - ref : natural; - index : natural - ) return val_t is begin - return ptrs(ref)(index); - end; - - procedure reallocate ( - ref : natural; - len : natural; - value : val_t := 0 - ) is begin - deallocate(ptrs(ref)); - ptrs(ref) := new integer_vector_t'(0 to len - 1 => value); - end; - - procedure resize ( - ref : natural; - len : natural; - drop : natural := 0; - value : val_t := 0 - ) is - variable old_ptr, new_ptr : integer_vector_access_t; - variable min_len : natural := len; - begin - new_ptr := new integer_vector_t'(0 to len - 1 => value); - old_ptr := ptrs(ref); - if min_len > old_ptr'length - drop then - min_len := old_ptr'length - drop; - end if; - for i in 0 to min_len-1 loop - new_ptr(i) := old_ptr(drop + i); - end loop; - ptrs(ref) := new_ptr; - deallocate(old_ptr); - end; - - end protected body; - - shared variable integer_vector_ptr_storage : integer_vector_ptr_storage_t; - - function to_integer ( - value : ptr_t - ) return integer is begin - return value.ref; - end; - - impure function to_integer_vector_ptr ( - value : integer - ) return ptr_t is begin - -- @TODO maybe assert that the ref is valid - return (ref => value); - end; - - impure function new_integer_vector_ptr ( - len : natural := 0; - value : val_t := 0 - ) return ptr_t is begin - return (ref => integer_vector_ptr_storage.new_integer_vector_ptr(len, value)); - end; - - procedure deallocate ( - ptr : ptr_t - ) is begin - integer_vector_ptr_storage.deallocate(ptr.ref); - end; - - impure function length ( - ptr : ptr_t - ) return integer is begin - return integer_vector_ptr_storage.length(ptr.ref); - end; - - procedure set ( - ptr : ptr_t; - index : natural; - value : val_t - ) is begin - integer_vector_ptr_storage.set(ptr.ref, index, value); - end; - - impure function get ( - ptr : ptr_t; - index : natural - ) return val_t is begin - return integer_vector_ptr_storage.get(ptr.ref, index); - end; - - procedure reallocate ( - ptr : ptr_t; - len : natural; - value : val_t := 0 - ) is begin - integer_vector_ptr_storage.reallocate(ptr.ref, len, value); - end; - - procedure resize ( - ptr : ptr_t; - len : natural; - drop : natural := 0; - value : val_t := 0 - ) is begin - integer_vector_ptr_storage.resize(ptr.ref, len, drop, value); - end; - - function encode ( - data : ptr_t - ) return string is begin - return encode(data.ref); - end; - - function decode ( - code : string - ) return ptr_t is - variable ret_val : ptr_t; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - return ret_val; - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out ptr_t - ) is begin - decode(code, index, result.ref); - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +package body integer_vector_ptr_pkg is + type integer_vector_ptr_storage_t is protected + impure function new_integer_vector_ptr ( + len : natural := 0; + value : val_t := 0 + ) return natural; + + procedure deallocate ( + ref : natural + ); + + impure function length ( + ref : natural + ) return integer; + + procedure set ( + ref : natural; + index : natural; + value : val_t + ); + + impure function get ( + ref : natural; + index : natural + ) return val_t; + + procedure reallocate ( + ref : natural; + len : natural; + value : val_t := 0 + ); + + procedure resize ( + ref : natural; + len : natural; + drop : natural := 0; + value : val_t := 0 + ); + end protected; + + type integer_vector_ptr_storage_t is protected body + variable current_index : integer := 0; + variable ptrs : vava_t := null; + + impure function new_integer_vector_ptr ( + len : natural := 0; + value : val_t := 0 + ) return natural is + variable old_ptrs : vava_t; + variable retval : ptr_t := (ref => current_index); + begin + if ptrs = null then + ptrs := new vav_t'(0 => null); + elsif ptrs'length <= current_index then + -- Reallocate ptr pointers to larger ptr + -- Use more size to trade size for speed + old_ptrs := ptrs; + ptrs := new vav_t'(0 to ptrs'length + 2**16 => null); + for i in old_ptrs'range loop + ptrs(i) := old_ptrs(i); + end loop; + deallocate(old_ptrs); + end if; + ptrs(current_index) := new integer_vector_t'(0 to len-1 => value); + current_index := current_index + 1; + return retval.ref; + end; + + procedure deallocate ( + ref : natural + ) is begin + deallocate(ptrs(ref)); + ptrs(ref) := null; + end; + + impure function length ( + ref : natural + ) return integer is begin + return ptrs(ref)'length; + end; + + procedure set ( + ref : natural; + index : natural; + value : val_t + ) is begin + ptrs(ref)(index) := value; + end; + + impure function get ( + ref : natural; + index : natural + ) return val_t is begin + return ptrs(ref)(index); + end; + + procedure reallocate ( + ref : natural; + len : natural; + value : val_t := 0 + ) is begin + deallocate(ptrs(ref)); + ptrs(ref) := new integer_vector_t'(0 to len - 1 => value); + end; + + procedure resize ( + ref : natural; + len : natural; + drop : natural := 0; + value : val_t := 0 + ) is + variable old_ptr, new_ptr : integer_vector_access_t; + variable min_len : natural := len; + begin + new_ptr := new integer_vector_t'(0 to len - 1 => value); + old_ptr := ptrs(ref); + if min_len > old_ptr'length - drop then + min_len := old_ptr'length - drop; + end if; + for i in 0 to min_len-1 loop + new_ptr(i) := old_ptr(drop + i); + end loop; + ptrs(ref) := new_ptr; + deallocate(old_ptr); + end; + + end protected body; + + shared variable integer_vector_ptr_storage : integer_vector_ptr_storage_t; + + function to_integer ( + value : ptr_t + ) return integer is begin + return value.ref; + end; + + impure function to_integer_vector_ptr ( + value : integer + ) return ptr_t is begin + -- @TODO maybe assert that the ref is valid + return (ref => value); + end; + + impure function new_integer_vector_ptr ( + len : natural := 0; + value : val_t := 0 + ) return ptr_t is begin + return (ref => integer_vector_ptr_storage.new_integer_vector_ptr(len, value)); + end; + + procedure deallocate ( + ptr : ptr_t + ) is begin + integer_vector_ptr_storage.deallocate(ptr.ref); + end; + + impure function length ( + ptr : ptr_t + ) return integer is begin + return integer_vector_ptr_storage.length(ptr.ref); + end; + + procedure set ( + ptr : ptr_t; + index : natural; + value : val_t + ) is begin + integer_vector_ptr_storage.set(ptr.ref, index, value); + end; + + impure function get ( + ptr : ptr_t; + index : natural + ) return val_t is begin + return integer_vector_ptr_storage.get(ptr.ref, index); + end; + + procedure reallocate ( + ptr : ptr_t; + len : natural; + value : val_t := 0 + ) is begin + integer_vector_ptr_storage.reallocate(ptr.ref, len, value); + end; + + procedure resize ( + ptr : ptr_t; + len : natural; + drop : natural := 0; + value : val_t := 0 + ) is begin + integer_vector_ptr_storage.resize(ptr.ref, len, drop, value); + end; + + function encode ( + data : ptr_t + ) return string is begin + return encode(data.ref); + end; + + function decode ( + code : string + ) return ptr_t is + variable ret_val : ptr_t; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + return ret_val; + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out ptr_t + ) is begin + decode(code, index, result.ref); + end; + +end package body; diff --git a/vunit/vhdl/data_types/src/integer_vector_ptr_pkg-body-93.vhd b/vunit/vhdl/data_types/src/integer_vector_ptr_pkg-body-93.vhd index 047bd917a..50153500c 100644 --- a/vunit/vhdl/data_types/src/integer_vector_ptr_pkg-body-93.vhd +++ b/vunit/vhdl/data_types/src/integer_vector_ptr_pkg-body-93.vhd @@ -1,129 +1,129 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -package body integer_vector_ptr_pkg is - shared variable current_index : integer := 0; - shared variable ptrs : vava_t := null; - - impure function new_integer_vector_ptr ( - len : natural := 0; - value : val_t := 0 - ) return ptr_t is - variable old_ptrs : vava_t; - begin - if ptrs = null then - ptrs := new vav_t'(0 => null); - elsif ptrs'length <= current_index then - -- Reallocate ptr pointers to larger ptr - -- Use more size to trade size for speed - old_ptrs := ptrs; - ptrs := new vav_t'(0 to ptrs'length + 2**16 => null); - for i in old_ptrs'range loop - ptrs(i) := old_ptrs(i); - end loop; - deallocate(old_ptrs); - end if; - ptrs(current_index) := new integer_vector_t'(0 to len-1 => value); - current_index := current_index + 1; - return (ref => current_index-1); - end; - - procedure deallocate ( - ptr : ptr_t - ) is begin - deallocate(ptrs(ptr.ref)); - ptrs(ptr.ref) := null; - end; - - impure function length ( - ptr : ptr_t - ) return integer is begin - return ptrs(ptr.ref)'length; - end; - - procedure set ( - ptr : ptr_t; - index : natural; - value : val_t - ) is begin - ptrs(ptr.ref)(index) := value; - end; - - impure function get ( - ptr : ptr_t; - index : natural - ) return val_t is begin - return ptrs(ptr.ref)(index); - end; - - procedure reallocate ( - ptr : ptr_t; - len : natural; - value : val_t := 0 - ) is begin - deallocate(ptrs(ptr.ref)); - ptrs(ptr.ref) := new integer_vector_t'(0 to len - 1 => value); - end; - - procedure resize ( - ptr : ptr_t; - len : natural; - drop : natural := 0; - value : val_t := 0 - ) is - variable old_ptr, new_ptr : integer_vector_access_t; - variable min_len : natural := len; - begin - new_ptr := new integer_vector_t'(0 to len - 1 => value); - old_ptr := ptrs(ptr.ref); - if min_len > old_ptr'length - drop then - min_len := old_ptr'length - drop; - end if; - for i in 0 to min_len-1 loop - new_ptr(i) := old_ptr(drop + i); - end loop; - ptrs(ptr.ref) := new_ptr; - deallocate(old_ptr); - end; - - function to_integer ( - value : ptr_t - ) return integer is begin - return value.ref; - end; - - impure function to_integer_vector_ptr ( - value : integer - ) return ptr_t is begin - -- @TODO maybe assert that the ref is valid - return (ref => value); - end; - - function encode ( - data : ptr_t - ) return string is begin - return encode(data.ref); - end; - - function decode ( - code : string - ) return ptr_t is - variable ret_val : ptr_t; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - return ret_val; - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out ptr_t - ) is begin - decode(code, index, result.ref); - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +package body integer_vector_ptr_pkg is + shared variable current_index : integer := 0; + shared variable ptrs : vava_t := null; + + impure function new_integer_vector_ptr ( + len : natural := 0; + value : val_t := 0 + ) return ptr_t is + variable old_ptrs : vava_t; + begin + if ptrs = null then + ptrs := new vav_t'(0 => null); + elsif ptrs'length <= current_index then + -- Reallocate ptr pointers to larger ptr + -- Use more size to trade size for speed + old_ptrs := ptrs; + ptrs := new vav_t'(0 to ptrs'length + 2**16 => null); + for i in old_ptrs'range loop + ptrs(i) := old_ptrs(i); + end loop; + deallocate(old_ptrs); + end if; + ptrs(current_index) := new integer_vector_t'(0 to len-1 => value); + current_index := current_index + 1; + return (ref => current_index-1); + end; + + procedure deallocate ( + ptr : ptr_t + ) is begin + deallocate(ptrs(ptr.ref)); + ptrs(ptr.ref) := null; + end; + + impure function length ( + ptr : ptr_t + ) return integer is begin + return ptrs(ptr.ref)'length; + end; + + procedure set ( + ptr : ptr_t; + index : natural; + value : val_t + ) is begin + ptrs(ptr.ref)(index) := value; + end; + + impure function get ( + ptr : ptr_t; + index : natural + ) return val_t is begin + return ptrs(ptr.ref)(index); + end; + + procedure reallocate ( + ptr : ptr_t; + len : natural; + value : val_t := 0 + ) is begin + deallocate(ptrs(ptr.ref)); + ptrs(ptr.ref) := new integer_vector_t'(0 to len - 1 => value); + end; + + procedure resize ( + ptr : ptr_t; + len : natural; + drop : natural := 0; + value : val_t := 0 + ) is + variable old_ptr, new_ptr : integer_vector_access_t; + variable min_len : natural := len; + begin + new_ptr := new integer_vector_t'(0 to len - 1 => value); + old_ptr := ptrs(ptr.ref); + if min_len > old_ptr'length - drop then + min_len := old_ptr'length - drop; + end if; + for i in 0 to min_len-1 loop + new_ptr(i) := old_ptr(drop + i); + end loop; + ptrs(ptr.ref) := new_ptr; + deallocate(old_ptr); + end; + + function to_integer ( + value : ptr_t + ) return integer is begin + return value.ref; + end; + + impure function to_integer_vector_ptr ( + value : integer + ) return ptr_t is begin + -- @TODO maybe assert that the ref is valid + return (ref => value); + end; + + function encode ( + data : ptr_t + ) return string is begin + return encode(data.ref); + end; + + function decode ( + code : string + ) return ptr_t is + variable ret_val : ptr_t; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + return ret_val; + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out ptr_t + ) is begin + decode(code, index, result.ref); + end; + +end package body; diff --git a/vunit/vhdl/data_types/src/integer_vector_ptr_pkg.vhd b/vunit/vhdl/data_types/src/integer_vector_ptr_pkg.vhd index e305122b0..8b84c1467 100644 --- a/vunit/vhdl/data_types/src/integer_vector_ptr_pkg.vhd +++ b/vunit/vhdl/data_types/src/integer_vector_ptr_pkg.vhd @@ -1,96 +1,96 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- --- The purpose of this package is to provide an integer vector access type (pointer) --- that can itself be used in arrays and returned from functions unlike a --- real access type. This is achieved by letting the actual value be a handle --- into a singleton datastructure of integer vector access types. --- - -use work.integer_vector_pkg.all; - -use work.codec_pkg.all; -use work.codec_builder_pkg.all; - -package integer_vector_ptr_pkg is - subtype index_t is integer range -1 to integer'high; - type integer_vector_ptr_t is record - ref : index_t; - end record; - constant null_ptr : integer_vector_ptr_t := (ref => -1); - - alias ptr_t is integer_vector_ptr_t; - alias val_t is integer; - alias vec_t is integer_vector_t; - alias vav_t is integer_vector_access_vector_t; - alias vava_t is integer_vector_access_vector_access_t; - - function to_integer ( - value : ptr_t - ) return integer; - - impure function to_integer_vector_ptr ( - value : val_t - ) return ptr_t; - - impure function new_integer_vector_ptr ( - len : natural := 0; - value : val_t := 0 - ) return ptr_t; - - procedure deallocate ( - ptr : ptr_t - ); - - impure function length ( - ptr : ptr_t - ) return integer; - - procedure set ( - ptr : ptr_t; - index : natural; - value : val_t - ); - - impure function get ( - ptr : ptr_t; - index : natural - ) return val_t; - - procedure reallocate ( - ptr : ptr_t; - len : natural; - value : val_t := 0 - ); - - procedure resize ( - ptr : ptr_t; - len : natural; - drop : natural := 0; - value : val_t := 0 - ); - - function encode ( - data : ptr_t - ) return string; - - function decode ( - code : string - ) return ptr_t; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out ptr_t - ); - - alias encode_integer_vector_ptr_t is encode[ptr_t return string]; - alias decode_integer_vector_ptr_t is decode[string return ptr_t]; - - constant integer_vector_ptr_t_code_length : positive := integer_code_length; - -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- +-- The purpose of this package is to provide an integer vector access type (pointer) +-- that can itself be used in arrays and returned from functions unlike a +-- real access type. This is achieved by letting the actual value be a handle +-- into a singleton datastructure of integer vector access types. +-- + +use work.integer_vector_pkg.all; + +use work.codec_pkg.all; +use work.codec_builder_pkg.all; + +package integer_vector_ptr_pkg is + subtype index_t is integer range -1 to integer'high; + type integer_vector_ptr_t is record + ref : index_t; + end record; + constant null_ptr : integer_vector_ptr_t := (ref => -1); + + alias ptr_t is integer_vector_ptr_t; + alias val_t is integer; + alias vec_t is integer_vector_t; + alias vav_t is integer_vector_access_vector_t; + alias vava_t is integer_vector_access_vector_access_t; + + function to_integer ( + value : ptr_t + ) return integer; + + impure function to_integer_vector_ptr ( + value : val_t + ) return ptr_t; + + impure function new_integer_vector_ptr ( + len : natural := 0; + value : val_t := 0 + ) return ptr_t; + + procedure deallocate ( + ptr : ptr_t + ); + + impure function length ( + ptr : ptr_t + ) return integer; + + procedure set ( + ptr : ptr_t; + index : natural; + value : val_t + ); + + impure function get ( + ptr : ptr_t; + index : natural + ) return val_t; + + procedure reallocate ( + ptr : ptr_t; + len : natural; + value : val_t := 0 + ); + + procedure resize ( + ptr : ptr_t; + len : natural; + drop : natural := 0; + value : val_t := 0 + ); + + function encode ( + data : ptr_t + ) return string; + + function decode ( + code : string + ) return ptr_t; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out ptr_t + ); + + alias encode_integer_vector_ptr_t is encode[ptr_t return string]; + alias decode_integer_vector_ptr_t is decode[string return ptr_t]; + + constant integer_vector_ptr_t_code_length : positive := integer_code_length; + +end package; diff --git a/vunit/vhdl/data_types/src/integer_vector_ptr_pool_pkg.vhd b/vunit/vhdl/data_types/src/integer_vector_ptr_pool_pkg.vhd index 301069d50..0dccf4c41 100644 --- a/vunit/vhdl/data_types/src/integer_vector_ptr_pool_pkg.vhd +++ b/vunit/vhdl/data_types/src/integer_vector_ptr_pool_pkg.vhd @@ -1,69 +1,69 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -use work.integer_vector_ptr_pkg.all; -use work.queue_pkg.all; - -package integer_vector_ptr_pool_pkg is - type integer_vector_ptr_pool_t is record - ptrs : queue_t; - end record; - constant null_integer_vector_ptr_pool : integer_vector_ptr_pool_t := (others => null_queue); - - impure function new_integer_vector_ptr_pool - return integer_vector_ptr_pool_t; - - impure function new_integer_vector_ptr ( - pool : integer_vector_ptr_pool_t; - min_length : natural := 0 - ) return integer_vector_ptr_t; - - procedure recycle ( - pool : integer_vector_ptr_pool_t; - variable ptr : inout integer_vector_ptr_t - ); -end package; - -package body integer_vector_ptr_pool_pkg is - impure function new_integer_vector_ptr_pool - return integer_vector_ptr_pool_t is begin - return (ptrs => new_queue); - end; - - impure function new_integer_vector_ptr ( - pool : integer_vector_ptr_pool_t; - min_length : natural := 0 - ) return integer_vector_ptr_t is - variable ptr : integer_vector_ptr_t; - begin - if length(pool.ptrs) > 0 then - -- Reuse - ptr := pop_integer_vector_ptr_ref(pool.ptrs); - if length(ptr) < min_length then - reallocate(ptr, min_length); - end if; - else - -- Allocate new - ptr := new_integer_vector_ptr(min_length); - end if; - return ptr; - end; - - procedure recycle ( - pool : integer_vector_ptr_pool_t; - variable ptr : inout integer_vector_ptr_t - ) is begin - if ptr = null_ptr then - return; - end if; - push_integer_vector_ptr_ref(pool.ptrs, ptr); - ptr := null_ptr; - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +use work.integer_vector_ptr_pkg.all; +use work.queue_pkg.all; + +package integer_vector_ptr_pool_pkg is + type integer_vector_ptr_pool_t is record + ptrs : queue_t; + end record; + constant null_integer_vector_ptr_pool : integer_vector_ptr_pool_t := (others => null_queue); + + impure function new_integer_vector_ptr_pool + return integer_vector_ptr_pool_t; + + impure function new_integer_vector_ptr ( + pool : integer_vector_ptr_pool_t; + min_length : natural := 0 + ) return integer_vector_ptr_t; + + procedure recycle ( + pool : integer_vector_ptr_pool_t; + variable ptr : inout integer_vector_ptr_t + ); +end package; + +package body integer_vector_ptr_pool_pkg is + impure function new_integer_vector_ptr_pool + return integer_vector_ptr_pool_t is begin + return (ptrs => new_queue); + end; + + impure function new_integer_vector_ptr ( + pool : integer_vector_ptr_pool_t; + min_length : natural := 0 + ) return integer_vector_ptr_t is + variable ptr : integer_vector_ptr_t; + begin + if length(pool.ptrs) > 0 then + -- Reuse + ptr := pop_integer_vector_ptr_ref(pool.ptrs); + if length(ptr) < min_length then + reallocate(ptr, min_length); + end if; + else + -- Allocate new + ptr := new_integer_vector_ptr(min_length); + end if; + return ptr; + end; + + procedure recycle ( + pool : integer_vector_ptr_pool_t; + variable ptr : inout integer_vector_ptr_t + ) is begin + if ptr = null_ptr then + return; + end if; + push_integer_vector_ptr_ref(pool.ptrs, ptr); + ptr := null_ptr; + end; + +end package body; diff --git a/vunit/vhdl/data_types/src/queue_pkg-2008.vhd b/vunit/vhdl/data_types/src/queue_pkg-2008.vhd index 18f7ab510..c6ebfdde4 100644 --- a/vunit/vhdl/data_types/src/queue_pkg-2008.vhd +++ b/vunit/vhdl/data_types/src/queue_pkg-2008.vhd @@ -1,206 +1,206 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.fixed_pkg.all; -use ieee.float_pkg.all; - -use work.queue_pkg.all; -use work.codec_2008_pkg.all; -use work.codec_builder_2008_pkg.all; - -package queue_2008_pkg is - procedure push ( - queue : queue_t; - value : boolean_vector - ); - - impure function pop ( - queue : queue_t - ) return boolean_vector; - - alias push_boolean_vector is push[queue_t, boolean_vector]; - alias pop_boolean_vector is pop[queue_t return boolean_vector]; - - procedure push ( - queue : queue_t; - value : integer_vector - ); - - impure function pop ( - queue : queue_t - ) return integer_vector; - - alias push_integer_vector is push[queue_t, integer_vector]; - alias pop_integer_vector is pop[queue_t return integer_vector]; - - procedure push ( - queue : queue_t; - value : real_vector - ); - - impure function pop ( - queue : queue_t - ) return real_vector; - - alias push_real_vector is push[queue_t, real_vector]; - alias pop_real_vector is pop[queue_t return real_vector]; - - procedure push ( - queue : queue_t; - value : time_vector - ); - - impure function pop ( - queue : queue_t - ) return time_vector; - - alias push_time_vector is push[queue_t, time_vector]; - alias pop_time_vector is pop[queue_t return time_vector]; - - procedure push ( - queue : queue_t; - value : ufixed - ); - - impure function pop ( - queue : queue_t - ) return ufixed; - - alias push_ufixed is push[queue_t, ufixed]; - alias pop_ufixed is pop[queue_t return ufixed]; - - procedure push ( - queue : queue_t; - value : sfixed - ); - - impure function pop ( - queue : queue_t - ) return sfixed; - - alias push_sfixed is push[queue_t, sfixed]; - alias pop_sfixed is pop[queue_t return sfixed]; - - procedure push ( - queue : queue_t; - value : float - ); - - impure function pop ( - queue : queue_t - ) return float; - - alias push_float is push[queue_t, float]; - alias pop_float is pop[queue_t return float]; -end package; - -package body queue_2008_pkg is - procedure push ( - queue : queue_t; - value : boolean_vector - ) is begin - push_type(queue, vhdl_boolean_vector); - push_variable_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return boolean_vector is begin - check_type(queue, vhdl_boolean_vector); - return decode(pop_variable_string(queue)); - end; - - procedure push ( - queue : queue_t; - value : integer_vector - ) is begin - push_type(queue, vhdl_integer_vector); - push_variable_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return integer_vector is begin - check_type(queue, vhdl_integer_vector); - return decode(pop_variable_string(queue)); - end; - - procedure push ( - queue : queue_t; - value : real_vector - ) is begin - push_type(queue, vhdl_real_vector); - push_variable_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return real_vector is begin - check_type(queue, vhdl_real_vector); - return decode(pop_variable_string(queue)); - end; - - procedure push ( - queue : queue_t; - value : time_vector - ) is begin - push_type(queue, vhdl_time_vector); - push_variable_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return time_vector is begin - check_type(queue, vhdl_time_vector); - return decode(pop_variable_string(queue)); - end; - - procedure push ( - queue : queue_t; - value : ufixed - ) is begin - push_type(queue, ieee_ufixed); - push_variable_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return ufixed is begin - check_type(queue, ieee_ufixed); - return decode(pop_variable_string(queue)); - end; - - procedure push ( - queue : queue_t; - value : sfixed - ) is begin - push_type(queue, ieee_sfixed); - push_variable_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return sfixed is begin - check_type(queue, ieee_sfixed); - return decode(pop_variable_string(queue)); - end; - - procedure push ( - queue : queue_t; - value : float - ) is begin - push_type(queue, ieee_float); - push_variable_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return float is begin - check_type(queue, ieee_float); - return decode(pop_variable_string(queue)); - end; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.fixed_pkg.all; +use ieee.float_pkg.all; + +use work.queue_pkg.all; +use work.codec_2008_pkg.all; +use work.codec_builder_2008_pkg.all; + +package queue_2008_pkg is + procedure push ( + queue : queue_t; + value : boolean_vector + ); + + impure function pop ( + queue : queue_t + ) return boolean_vector; + + alias push_boolean_vector is push[queue_t, boolean_vector]; + alias pop_boolean_vector is pop[queue_t return boolean_vector]; + + procedure push ( + queue : queue_t; + value : integer_vector + ); + + impure function pop ( + queue : queue_t + ) return integer_vector; + + alias push_integer_vector is push[queue_t, integer_vector]; + alias pop_integer_vector is pop[queue_t return integer_vector]; + + procedure push ( + queue : queue_t; + value : real_vector + ); + + impure function pop ( + queue : queue_t + ) return real_vector; + + alias push_real_vector is push[queue_t, real_vector]; + alias pop_real_vector is pop[queue_t return real_vector]; + + procedure push ( + queue : queue_t; + value : time_vector + ); + + impure function pop ( + queue : queue_t + ) return time_vector; + + alias push_time_vector is push[queue_t, time_vector]; + alias pop_time_vector is pop[queue_t return time_vector]; + + procedure push ( + queue : queue_t; + value : ufixed + ); + + impure function pop ( + queue : queue_t + ) return ufixed; + + alias push_ufixed is push[queue_t, ufixed]; + alias pop_ufixed is pop[queue_t return ufixed]; + + procedure push ( + queue : queue_t; + value : sfixed + ); + + impure function pop ( + queue : queue_t + ) return sfixed; + + alias push_sfixed is push[queue_t, sfixed]; + alias pop_sfixed is pop[queue_t return sfixed]; + + procedure push ( + queue : queue_t; + value : float + ); + + impure function pop ( + queue : queue_t + ) return float; + + alias push_float is push[queue_t, float]; + alias pop_float is pop[queue_t return float]; +end package; + +package body queue_2008_pkg is + procedure push ( + queue : queue_t; + value : boolean_vector + ) is begin + push_type(queue, vhdl_boolean_vector); + push_variable_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return boolean_vector is begin + check_type(queue, vhdl_boolean_vector); + return decode(pop_variable_string(queue)); + end; + + procedure push ( + queue : queue_t; + value : integer_vector + ) is begin + push_type(queue, vhdl_integer_vector); + push_variable_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return integer_vector is begin + check_type(queue, vhdl_integer_vector); + return decode(pop_variable_string(queue)); + end; + + procedure push ( + queue : queue_t; + value : real_vector + ) is begin + push_type(queue, vhdl_real_vector); + push_variable_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return real_vector is begin + check_type(queue, vhdl_real_vector); + return decode(pop_variable_string(queue)); + end; + + procedure push ( + queue : queue_t; + value : time_vector + ) is begin + push_type(queue, vhdl_time_vector); + push_variable_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return time_vector is begin + check_type(queue, vhdl_time_vector); + return decode(pop_variable_string(queue)); + end; + + procedure push ( + queue : queue_t; + value : ufixed + ) is begin + push_type(queue, ieee_ufixed); + push_variable_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return ufixed is begin + check_type(queue, ieee_ufixed); + return decode(pop_variable_string(queue)); + end; + + procedure push ( + queue : queue_t; + value : sfixed + ) is begin + push_type(queue, ieee_sfixed); + push_variable_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return sfixed is begin + check_type(queue, ieee_sfixed); + return decode(pop_variable_string(queue)); + end; + + procedure push ( + queue : queue_t; + value : float + ) is begin + push_type(queue, ieee_float); + push_variable_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return float is begin + check_type(queue, ieee_float); + return decode(pop_variable_string(queue)); + end; +end package body; diff --git a/vunit/vhdl/data_types/src/queue_pkg-body.vhd b/vunit/vhdl/data_types/src/queue_pkg-body.vhd index a622f2ce6..d78907d75 100644 --- a/vunit/vhdl/data_types/src/queue_pkg-body.vhd +++ b/vunit/vhdl/data_types/src/queue_pkg-body.vhd @@ -1,604 +1,604 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.math_real.all; -use ieee.math_complex.all; -use work.codec_pkg.all; -use work.codec_builder_pkg.all; - -package body queue_pkg is - constant tail_idx : natural := 0; - constant head_idx : natural := 1; - constant num_meta : natural := head_idx + 1; - constant queue_t_code_length : positive := integer_vector_ptr_t_code_length + string_ptr_t_code_length; - - impure function new_queue - return queue_t is begin - return (p_meta => new_integer_vector_ptr(num_meta), - data => new_string_ptr); - end; - - impure function length ( - queue : queue_t - ) return natural is - constant head : integer := get(queue.p_meta, head_idx); - constant tail : integer := get(queue.p_meta, tail_idx); - begin - return tail - head; - end; - - impure function is_empty ( - queue : queue_t - ) return boolean is begin - return length(queue) = 0; - end; - - procedure flush ( - queue : queue_t - ) is begin - assert queue /= null_queue report "Flush null queue"; - set(queue.p_meta, head_idx, 0); - set(queue.p_meta, tail_idx, 0); - end; - - impure function copy ( - queue : queue_t - ) return queue_t is - constant result : queue_t := new_queue; - begin - for i in 0 to length(queue) - 1 loop - unsafe_push(result, get(queue.data, 1 + i)); - end loop; - return result; - end; - - function encode ( - data : queue_t - ) return string is begin - return encode(data.p_meta) & encode(to_integer(data.data)); - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out queue_t - ) is begin - decode(code, index, result.p_meta); - decode(code, index, result.data); - end; - - function decode ( - code : string - ) return queue_t is - variable ret_val : queue_t; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - return ret_val; - end; - - procedure unsafe_push ( - queue : queue_t; - value : character - ) is - variable tail : integer; - variable head : integer; - begin - assert queue /= null_queue report "Push to null queue"; - tail := get(queue.p_meta, tail_idx); - head := get(queue.p_meta, head_idx); - if length(queue.data) < tail + 1 then - -- Allocate more new data, double data to avoid - -- to much copying. - -- Also normalize the queue by dropping unnused data before head - resize(queue.data, 2 * length(queue) + 1, drop => head); - tail := tail - head; - head := 0; - set(queue.p_meta, head_idx, head); - end if; - set(queue.data, 1 + tail, value); - set(queue.p_meta, tail_idx, tail + 1); - end; - - impure function unsafe_pop ( - queue : queue_t - ) return character is - variable head : integer; - variable data : character; - begin - assert queue /= null_queue report "Pop from null queue"; - assert length(queue) > 0 report "Pop from empty queue"; - head := get(queue.p_meta, head_idx); - data := get(queue.data, 1 + head); - set(queue.p_meta, head_idx, head + 1); - return data; - end; - - procedure push_type ( - queue : queue_t; - element_type : queue_element_type_t - ) is begin - unsafe_push(queue, character'val(queue_element_type_t'pos(element_type))); - end; - - impure function pop_type ( - queue : queue_t - ) return queue_element_type_t is begin - return queue_element_type_t'val(character'pos(unsafe_pop(queue))); - end; - - procedure check_type ( - queue : queue_t; - element_type : queue_element_type_t - ) is - constant popped_type : queue_element_type_t := pop_type(queue); - begin - if popped_type /= element_type then - report "Got queue element of type " & queue_element_type_t'image(popped_type) & - ", expected " & queue_element_type_t'image(element_type) & "." severity error; - end if; - end; - - procedure push ( - queue : queue_t; - value : character - ) is begin - push_type(queue, vhdl_character); - unsafe_push(queue, value); - end; - - impure function pop ( - queue : queue_t - ) return character is begin - check_type(queue, vhdl_character); - return unsafe_pop(queue); - end; - - procedure push_fix_string ( - queue : queue_t; - value : string - ) is begin - for i in value'range loop - unsafe_push(queue, value(i)); - end loop; - end; - - impure function pop_fix_string ( - queue : queue_t; - length : natural - ) return string is - variable result : string(1 to length); - begin - for i in result'range loop - result(i) := unsafe_pop(queue); - end loop; - - return result; - end; - - procedure unsafe_push ( - queue : queue_t; - value : integer - ) is begin - push_fix_string(queue, encode(value)); - end; - - impure function unsafe_pop ( - queue : queue_t - ) return integer is begin - return decode(pop_fix_string(queue, integer_code_length)); - end; - - procedure push ( - queue : queue_t; - value : integer - ) is begin - push_type(queue, vhdl_integer); - push_fix_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return integer is begin - check_type(queue, vhdl_integer); - return decode(pop_fix_string(queue, integer_code_length)); - end; - - procedure push_byte ( - queue : queue_t; - value : natural range 0 to 255 - ) is begin - push_type(queue, vunit_byte); - unsafe_push(queue, character'val(value)); - end; - - impure function pop_byte ( - queue : queue_t - ) return integer is begin - check_type(queue, vunit_byte); - return character'pos(unsafe_pop(queue)); - end; - - procedure push_variable_string ( - queue : queue_t; - value : string - ) is begin - unsafe_push(queue, value'length); - push_fix_string(queue, value); - end; - - impure function pop_variable_string ( - queue : queue_t - ) return string is - constant length : integer := unsafe_pop(queue); - begin - return pop_fix_string(queue, length); - end; - - procedure push ( - queue : queue_t; - value : boolean - ) is begin - push_type(queue, vhdl_boolean); - push_fix_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return boolean is begin - check_type(queue, vhdl_boolean); - return decode(pop_fix_string(queue, boolean_code_length)); - end; - - procedure unsafe_push ( - queue : queue_t; - value : boolean - ) is begin - push_fix_string(queue, encode(value)); - end; - - impure function unsafe_pop ( - queue : queue_t - ) return boolean is begin - return decode(pop_fix_string(queue, boolean_code_length)); - end; - - procedure push ( - queue : queue_t; - value : real - ) is begin - push_type(queue, vhdl_real); - push_fix_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return real is begin - check_type(queue, vhdl_real); - return decode(pop_fix_string(queue, real_code_length)); - end; - - procedure push ( - queue : queue_t; - value : bit - ) is begin - push_type(queue, vhdl_bit); - push_fix_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return bit is begin - check_type(queue, vhdl_bit); - return decode(pop_fix_string(queue, bit_code_length)); - end; - - procedure push ( - queue : queue_t; - value : std_ulogic - ) is begin - push_type(queue, ieee_std_ulogic); - push_fix_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return std_ulogic is begin - check_type(queue, ieee_std_ulogic); - return decode(pop_fix_string(queue, std_ulogic_code_length)); - end; - - procedure push ( - queue : queue_t; - value : severity_level - ) is begin - push_type(queue, vhdl_severity_level); - push_fix_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return severity_level is begin - check_type(queue, vhdl_severity_level); - return decode(pop_fix_string(queue, severity_level_code_length)); - end; - - procedure push ( - queue : queue_t; - value : file_open_status - ) is begin - push_type(queue, vhdl_file_open_status); - push_fix_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return file_open_status is begin - check_type(queue, vhdl_file_open_status); - return decode(pop_fix_string(queue, file_open_status_code_length)); - end; - - procedure push ( - queue : queue_t; - value : file_open_kind - ) is begin - push_type(queue, vhdl_file_open_kind); - push_fix_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return file_open_kind is begin - check_type(queue, vhdl_file_open_kind); - return decode(pop_fix_string(queue, file_open_kind_code_length)); - end; - - procedure push ( - queue : queue_t; - value : bit_vector - ) is begin - push_type(queue, vhdl_bit_vector); - push_variable_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return bit_vector is begin - check_type(queue, vhdl_bit_vector); - return decode(pop_variable_string(queue)); - end; - - procedure push ( - queue : queue_t; - value : std_ulogic_vector - ) is begin - push_type(queue, vhdl_std_ulogic_vector); - push_variable_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return std_ulogic_vector is begin - check_type(queue, vhdl_std_ulogic_vector); - return decode(pop_variable_string(queue)); - end; - - procedure push ( - queue : queue_t; - value : complex - ) is begin - push_type(queue, ieee_complex); - push_fix_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return complex is begin - check_type(queue, ieee_complex); - return decode(pop_fix_string(queue, complex_code_length)); - end; - - procedure push ( - queue : queue_t; - value : complex_polar - ) is begin - push_type(queue, ieee_complex_polar); - push_fix_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return complex_polar is begin - check_type(queue, ieee_complex_polar); - return decode(pop_fix_string(queue, complex_polar_code_length)); - end; - - procedure push ( - queue : queue_t; - value : ieee.numeric_bit.unsigned - ) is begin - push_type(queue, ieee_numeric_bit_unsigned); - push_variable_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return ieee.numeric_bit.unsigned is begin - check_type(queue, ieee_numeric_bit_unsigned); - return decode(pop_variable_string(queue)); - end; - - procedure push ( - queue : queue_t; - value : ieee.numeric_bit.signed - ) is begin - push_type(queue, ieee_numeric_bit_signed); - push_variable_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return ieee.numeric_bit.signed is begin - check_type(queue, ieee_numeric_bit_signed); - return decode(pop_variable_string(queue)); - end; - - procedure push ( - queue : queue_t; - value : ieee.numeric_std.unsigned - ) is begin - push_type(queue, ieee_numeric_std_unsigned); - push_variable_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return ieee.numeric_std.unsigned is begin - check_type(queue, ieee_numeric_std_unsigned); - return decode(pop_variable_string(queue)); - end; - - procedure push ( - queue : queue_t; - value : ieee.numeric_std.signed - ) is begin - push_type(queue, ieee_numeric_std_signed); - push_variable_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return ieee.numeric_std.signed is begin - check_type(queue, ieee_numeric_std_signed); - return decode(pop_variable_string(queue)); - end; - - procedure push ( - queue : queue_t; - value : string - ) is begin - push_type(queue, vhdl_string); - push_variable_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return string is begin - check_type(queue, vhdl_string); - return decode(pop_variable_string(queue)); - end; - - procedure push ( - queue : queue_t; - value : time - ) is begin - push_type(queue, vhdl_time); - push_fix_string(queue, encode(value)); - end; - - impure function pop ( - queue : queue_t - ) return time is begin - check_type(queue, vhdl_time); - return decode(pop_fix_string(queue, time_code_length)); - end; - - procedure push ( - queue : queue_t; - variable value : inout integer_vector_ptr_t - ) is begin - push_type(queue, vunit_integer_vector_ptr_t); - push_fix_string(queue, encode(value)); - value := null_ptr; - end; - - impure function pop ( - queue : queue_t - ) return integer_vector_ptr_t is begin - check_type(queue, vunit_integer_vector_ptr_t); - return decode(pop_fix_string(queue, integer_vector_ptr_t_code_length)); - end; - - procedure unsafe_push ( - queue : queue_t; - value : integer_vector_ptr_t - ) is begin - push_fix_string(queue, encode(value)); - end; - - impure function unsafe_pop ( - queue : queue_t - ) return integer_vector_ptr_t is begin - return decode(pop_fix_string(queue, integer_vector_ptr_t_code_length)); - end; - - procedure push ( - queue : queue_t; - variable value : inout string_ptr_t - ) is begin - push_type(queue, vunit_string_ptr_t); - push_fix_string(queue, encode(value)); - value := null_string_ptr; - end; - - impure function pop ( - queue : queue_t - ) return string_ptr_t is begin - check_type(queue, vunit_string_ptr_t); - return decode(pop_fix_string(queue, string_ptr_t_code_length)); - end; - - procedure push ( - queue : queue_t; - variable value : inout queue_t - ) is begin - push_type(queue, vunit_queue_t); - push_fix_string(queue, encode(value)); - value := null_queue; - end; - - impure function pop ( - queue : queue_t - ) return queue_t is begin - check_type(queue, vunit_queue_t); - return decode(pop_fix_string(queue, queue_t_code_length)); - end; - - procedure push_ref ( - constant queue : queue_t; - value : inout integer_array_t - ) is begin - push_type(queue, vunit_integer_array_t); - unsafe_push(queue, value.length); - unsafe_push(queue, value.width); - unsafe_push(queue, value.height); - unsafe_push(queue, value.depth); - unsafe_push(queue, value.bit_width); - unsafe_push(queue, value.is_signed); - unsafe_push(queue, value.lower_limit); - unsafe_push(queue, value.upper_limit); - unsafe_push(queue, value.data); - value := null_integer_array; - end; - - impure function pop_ref ( - queue : queue_t - ) return integer_array_t is begin - check_type(queue, vunit_integer_array_t); - return ( - length => unsafe_pop(queue), - width => unsafe_pop(queue), - height => unsafe_pop(queue), - depth => unsafe_pop(queue), - bit_width => unsafe_pop(queue), - is_signed => unsafe_pop(queue), - lower_limit => unsafe_pop(queue), - upper_limit => unsafe_pop(queue), - data => unsafe_pop(queue) - ); - end; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.math_real.all; +use ieee.math_complex.all; +use work.codec_pkg.all; +use work.codec_builder_pkg.all; + +package body queue_pkg is + constant tail_idx : natural := 0; + constant head_idx : natural := 1; + constant num_meta : natural := head_idx + 1; + constant queue_t_code_length : positive := integer_vector_ptr_t_code_length + string_ptr_t_code_length; + + impure function new_queue + return queue_t is begin + return (p_meta => new_integer_vector_ptr(num_meta), + data => new_string_ptr); + end; + + impure function length ( + queue : queue_t + ) return natural is + constant head : integer := get(queue.p_meta, head_idx); + constant tail : integer := get(queue.p_meta, tail_idx); + begin + return tail - head; + end; + + impure function is_empty ( + queue : queue_t + ) return boolean is begin + return length(queue) = 0; + end; + + procedure flush ( + queue : queue_t + ) is begin + assert queue /= null_queue report "Flush null queue"; + set(queue.p_meta, head_idx, 0); + set(queue.p_meta, tail_idx, 0); + end; + + impure function copy ( + queue : queue_t + ) return queue_t is + constant result : queue_t := new_queue; + begin + for i in 0 to length(queue) - 1 loop + unsafe_push(result, get(queue.data, 1 + i)); + end loop; + return result; + end; + + function encode ( + data : queue_t + ) return string is begin + return encode(data.p_meta) & encode(to_integer(data.data)); + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out queue_t + ) is begin + decode(code, index, result.p_meta); + decode(code, index, result.data); + end; + + function decode ( + code : string + ) return queue_t is + variable ret_val : queue_t; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + return ret_val; + end; + + procedure unsafe_push ( + queue : queue_t; + value : character + ) is + variable tail : integer; + variable head : integer; + begin + assert queue /= null_queue report "Push to null queue"; + tail := get(queue.p_meta, tail_idx); + head := get(queue.p_meta, head_idx); + if length(queue.data) < tail + 1 then + -- Allocate more new data, double data to avoid + -- to much copying. + -- Also normalize the queue by dropping unnused data before head + resize(queue.data, 2 * length(queue) + 1, drop => head); + tail := tail - head; + head := 0; + set(queue.p_meta, head_idx, head); + end if; + set(queue.data, 1 + tail, value); + set(queue.p_meta, tail_idx, tail + 1); + end; + + impure function unsafe_pop ( + queue : queue_t + ) return character is + variable head : integer; + variable data : character; + begin + assert queue /= null_queue report "Pop from null queue"; + assert length(queue) > 0 report "Pop from empty queue"; + head := get(queue.p_meta, head_idx); + data := get(queue.data, 1 + head); + set(queue.p_meta, head_idx, head + 1); + return data; + end; + + procedure push_type ( + queue : queue_t; + element_type : queue_element_type_t + ) is begin + unsafe_push(queue, character'val(queue_element_type_t'pos(element_type))); + end; + + impure function pop_type ( + queue : queue_t + ) return queue_element_type_t is begin + return queue_element_type_t'val(character'pos(unsafe_pop(queue))); + end; + + procedure check_type ( + queue : queue_t; + element_type : queue_element_type_t + ) is + constant popped_type : queue_element_type_t := pop_type(queue); + begin + if popped_type /= element_type then + report "Got queue element of type " & queue_element_type_t'image(popped_type) & + ", expected " & queue_element_type_t'image(element_type) & "." severity error; + end if; + end; + + procedure push ( + queue : queue_t; + value : character + ) is begin + push_type(queue, vhdl_character); + unsafe_push(queue, value); + end; + + impure function pop ( + queue : queue_t + ) return character is begin + check_type(queue, vhdl_character); + return unsafe_pop(queue); + end; + + procedure push_fix_string ( + queue : queue_t; + value : string + ) is begin + for i in value'range loop + unsafe_push(queue, value(i)); + end loop; + end; + + impure function pop_fix_string ( + queue : queue_t; + length : natural + ) return string is + variable result : string(1 to length); + begin + for i in result'range loop + result(i) := unsafe_pop(queue); + end loop; + + return result; + end; + + procedure unsafe_push ( + queue : queue_t; + value : integer + ) is begin + push_fix_string(queue, encode(value)); + end; + + impure function unsafe_pop ( + queue : queue_t + ) return integer is begin + return decode(pop_fix_string(queue, integer_code_length)); + end; + + procedure push ( + queue : queue_t; + value : integer + ) is begin + push_type(queue, vhdl_integer); + push_fix_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return integer is begin + check_type(queue, vhdl_integer); + return decode(pop_fix_string(queue, integer_code_length)); + end; + + procedure push_byte ( + queue : queue_t; + value : natural range 0 to 255 + ) is begin + push_type(queue, vunit_byte); + unsafe_push(queue, character'val(value)); + end; + + impure function pop_byte ( + queue : queue_t + ) return integer is begin + check_type(queue, vunit_byte); + return character'pos(unsafe_pop(queue)); + end; + + procedure push_variable_string ( + queue : queue_t; + value : string + ) is begin + unsafe_push(queue, value'length); + push_fix_string(queue, value); + end; + + impure function pop_variable_string ( + queue : queue_t + ) return string is + constant length : integer := unsafe_pop(queue); + begin + return pop_fix_string(queue, length); + end; + + procedure push ( + queue : queue_t; + value : boolean + ) is begin + push_type(queue, vhdl_boolean); + push_fix_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return boolean is begin + check_type(queue, vhdl_boolean); + return decode(pop_fix_string(queue, boolean_code_length)); + end; + + procedure unsafe_push ( + queue : queue_t; + value : boolean + ) is begin + push_fix_string(queue, encode(value)); + end; + + impure function unsafe_pop ( + queue : queue_t + ) return boolean is begin + return decode(pop_fix_string(queue, boolean_code_length)); + end; + + procedure push ( + queue : queue_t; + value : real + ) is begin + push_type(queue, vhdl_real); + push_fix_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return real is begin + check_type(queue, vhdl_real); + return decode(pop_fix_string(queue, real_code_length)); + end; + + procedure push ( + queue : queue_t; + value : bit + ) is begin + push_type(queue, vhdl_bit); + push_fix_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return bit is begin + check_type(queue, vhdl_bit); + return decode(pop_fix_string(queue, bit_code_length)); + end; + + procedure push ( + queue : queue_t; + value : std_ulogic + ) is begin + push_type(queue, ieee_std_ulogic); + push_fix_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return std_ulogic is begin + check_type(queue, ieee_std_ulogic); + return decode(pop_fix_string(queue, std_ulogic_code_length)); + end; + + procedure push ( + queue : queue_t; + value : severity_level + ) is begin + push_type(queue, vhdl_severity_level); + push_fix_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return severity_level is begin + check_type(queue, vhdl_severity_level); + return decode(pop_fix_string(queue, severity_level_code_length)); + end; + + procedure push ( + queue : queue_t; + value : file_open_status + ) is begin + push_type(queue, vhdl_file_open_status); + push_fix_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return file_open_status is begin + check_type(queue, vhdl_file_open_status); + return decode(pop_fix_string(queue, file_open_status_code_length)); + end; + + procedure push ( + queue : queue_t; + value : file_open_kind + ) is begin + push_type(queue, vhdl_file_open_kind); + push_fix_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return file_open_kind is begin + check_type(queue, vhdl_file_open_kind); + return decode(pop_fix_string(queue, file_open_kind_code_length)); + end; + + procedure push ( + queue : queue_t; + value : bit_vector + ) is begin + push_type(queue, vhdl_bit_vector); + push_variable_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return bit_vector is begin + check_type(queue, vhdl_bit_vector); + return decode(pop_variable_string(queue)); + end; + + procedure push ( + queue : queue_t; + value : std_ulogic_vector + ) is begin + push_type(queue, vhdl_std_ulogic_vector); + push_variable_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return std_ulogic_vector is begin + check_type(queue, vhdl_std_ulogic_vector); + return decode(pop_variable_string(queue)); + end; + + procedure push ( + queue : queue_t; + value : complex + ) is begin + push_type(queue, ieee_complex); + push_fix_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return complex is begin + check_type(queue, ieee_complex); + return decode(pop_fix_string(queue, complex_code_length)); + end; + + procedure push ( + queue : queue_t; + value : complex_polar + ) is begin + push_type(queue, ieee_complex_polar); + push_fix_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return complex_polar is begin + check_type(queue, ieee_complex_polar); + return decode(pop_fix_string(queue, complex_polar_code_length)); + end; + + procedure push ( + queue : queue_t; + value : ieee.numeric_bit.unsigned + ) is begin + push_type(queue, ieee_numeric_bit_unsigned); + push_variable_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return ieee.numeric_bit.unsigned is begin + check_type(queue, ieee_numeric_bit_unsigned); + return decode(pop_variable_string(queue)); + end; + + procedure push ( + queue : queue_t; + value : ieee.numeric_bit.signed + ) is begin + push_type(queue, ieee_numeric_bit_signed); + push_variable_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return ieee.numeric_bit.signed is begin + check_type(queue, ieee_numeric_bit_signed); + return decode(pop_variable_string(queue)); + end; + + procedure push ( + queue : queue_t; + value : ieee.numeric_std.unsigned + ) is begin + push_type(queue, ieee_numeric_std_unsigned); + push_variable_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return ieee.numeric_std.unsigned is begin + check_type(queue, ieee_numeric_std_unsigned); + return decode(pop_variable_string(queue)); + end; + + procedure push ( + queue : queue_t; + value : ieee.numeric_std.signed + ) is begin + push_type(queue, ieee_numeric_std_signed); + push_variable_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return ieee.numeric_std.signed is begin + check_type(queue, ieee_numeric_std_signed); + return decode(pop_variable_string(queue)); + end; + + procedure push ( + queue : queue_t; + value : string + ) is begin + push_type(queue, vhdl_string); + push_variable_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return string is begin + check_type(queue, vhdl_string); + return decode(pop_variable_string(queue)); + end; + + procedure push ( + queue : queue_t; + value : time + ) is begin + push_type(queue, vhdl_time); + push_fix_string(queue, encode(value)); + end; + + impure function pop ( + queue : queue_t + ) return time is begin + check_type(queue, vhdl_time); + return decode(pop_fix_string(queue, time_code_length)); + end; + + procedure push ( + queue : queue_t; + variable value : inout integer_vector_ptr_t + ) is begin + push_type(queue, vunit_integer_vector_ptr_t); + push_fix_string(queue, encode(value)); + value := null_ptr; + end; + + impure function pop ( + queue : queue_t + ) return integer_vector_ptr_t is begin + check_type(queue, vunit_integer_vector_ptr_t); + return decode(pop_fix_string(queue, integer_vector_ptr_t_code_length)); + end; + + procedure unsafe_push ( + queue : queue_t; + value : integer_vector_ptr_t + ) is begin + push_fix_string(queue, encode(value)); + end; + + impure function unsafe_pop ( + queue : queue_t + ) return integer_vector_ptr_t is begin + return decode(pop_fix_string(queue, integer_vector_ptr_t_code_length)); + end; + + procedure push ( + queue : queue_t; + variable value : inout string_ptr_t + ) is begin + push_type(queue, vunit_string_ptr_t); + push_fix_string(queue, encode(value)); + value := null_string_ptr; + end; + + impure function pop ( + queue : queue_t + ) return string_ptr_t is begin + check_type(queue, vunit_string_ptr_t); + return decode(pop_fix_string(queue, string_ptr_t_code_length)); + end; + + procedure push ( + queue : queue_t; + variable value : inout queue_t + ) is begin + push_type(queue, vunit_queue_t); + push_fix_string(queue, encode(value)); + value := null_queue; + end; + + impure function pop ( + queue : queue_t + ) return queue_t is begin + check_type(queue, vunit_queue_t); + return decode(pop_fix_string(queue, queue_t_code_length)); + end; + + procedure push_ref ( + constant queue : queue_t; + value : inout integer_array_t + ) is begin + push_type(queue, vunit_integer_array_t); + unsafe_push(queue, value.length); + unsafe_push(queue, value.width); + unsafe_push(queue, value.height); + unsafe_push(queue, value.depth); + unsafe_push(queue, value.bit_width); + unsafe_push(queue, value.is_signed); + unsafe_push(queue, value.lower_limit); + unsafe_push(queue, value.upper_limit); + unsafe_push(queue, value.data); + value := null_integer_array; + end; + + impure function pop_ref ( + queue : queue_t + ) return integer_array_t is begin + check_type(queue, vunit_integer_array_t); + return ( + length => unsafe_pop(queue), + width => unsafe_pop(queue), + height => unsafe_pop(queue), + depth => unsafe_pop(queue), + bit_width => unsafe_pop(queue), + is_signed => unsafe_pop(queue), + lower_limit => unsafe_pop(queue), + upper_limit => unsafe_pop(queue), + data => unsafe_pop(queue) + ); + end; +end package body; diff --git a/vunit/vhdl/data_types/src/queue_pkg.vhd b/vunit/vhdl/data_types/src/queue_pkg.vhd index f063b8b3f..132d8ea23 100644 --- a/vunit/vhdl/data_types/src/queue_pkg.vhd +++ b/vunit/vhdl/data_types/src/queue_pkg.vhd @@ -1,393 +1,393 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.math_complex.all; -use ieee.numeric_bit.all; -use ieee.numeric_std.all; -use work.integer_vector_ptr_pkg.all; -use work.string_ptr_pkg.all; -use work.integer_array_pkg.all; - -package queue_pkg is - type queue_t is record - p_meta : integer_vector_ptr_t; - data : string_ptr_t; - end record; - type queue_vec_t is array(integer range <>) of queue_t; - constant null_queue : queue_t := (p_meta => null_ptr, data => null_string_ptr); - - impure function new_queue - return queue_t; - - -- Returns the length of the queue in bytes - impure function length ( - queue : queue_t - ) return natural; - - impure function is_empty ( - queue : queue_t - ) return boolean; - - procedure flush ( - queue : queue_t - ); - - impure function copy ( - queue : queue_t - ) return queue_t; - - procedure push ( - queue : queue_t; - value : integer - ); - - impure function pop ( - queue : queue_t - ) return integer; - - alias push_integer is push[queue_t, integer]; - alias pop_integer is pop[queue_t return integer]; - - procedure push_byte ( - queue : queue_t; - value : natural range 0 to 255 - ); - - impure function pop_byte ( - queue : queue_t - ) return integer; - - procedure push ( - queue : queue_t; - value : character - ); - - impure function pop ( - queue : queue_t - ) return character; - - alias push_character is push[queue_t, character]; - alias pop_character is pop[queue_t return character]; - - procedure push ( - queue : queue_t; - value : boolean - ); - - impure function pop ( - queue : queue_t - ) return boolean; - - alias push_boolean is push[queue_t, boolean]; - alias pop_boolean is pop[queue_t return boolean]; - - procedure push ( - queue : queue_t; - value : real - ); - - impure function pop ( - queue : queue_t - ) return real; - - alias push_real is push[queue_t, real]; - alias pop_real is pop[queue_t return real]; - - procedure push ( - queue : queue_t; - value : bit - ); - - impure function pop ( - queue : queue_t - ) return bit; - - alias push_bit is push[queue_t, bit]; - alias pop_bit is pop[queue_t return bit]; - - procedure push ( - queue : queue_t; - value : std_ulogic - ); - - impure function pop ( - queue : queue_t - ) return std_ulogic; - - alias push_std_ulogic is push[queue_t, std_ulogic]; - alias pop_std_ulogic is pop[queue_t return std_ulogic]; - - procedure push ( - queue : queue_t; - value : severity_level - ); - - impure function pop ( - queue : queue_t - ) return severity_level; - - alias push_severity_level is push[queue_t, severity_level]; - alias pop_severity_level is pop[queue_t return severity_level]; - - procedure push ( - queue : queue_t; - value : file_open_status - ); - - impure function pop ( - queue : queue_t - ) return file_open_status; - - alias push_file_open_status is push[queue_t, file_open_status]; - alias pop_file_open_status is pop[queue_t return file_open_status]; - - procedure push ( - queue : queue_t; - value : file_open_kind - ); - - impure function pop ( - queue : queue_t - ) return file_open_kind; - - alias push_file_open_kind is push[queue_t, file_open_kind]; - alias pop_file_open_kind is pop[queue_t return file_open_kind]; - - procedure push ( - queue : queue_t; - value : bit_vector - ); - - impure function pop ( - queue : queue_t - ) return bit_vector; - - alias push_bit_vector is push[queue_t, bit_vector]; - alias pop_bit_vector is pop[queue_t return bit_vector]; - - procedure push ( - queue : queue_t; - value : std_ulogic_vector - ); - - impure function pop ( - queue : queue_t - ) return std_ulogic_vector; - - alias push_std_ulogic_vector is push[queue_t, std_ulogic_vector]; - alias pop_std_ulogic_vector is pop[queue_t return std_ulogic_vector]; - - procedure push ( - queue : queue_t; - value : complex - ); - - impure function pop ( - queue : queue_t - ) return complex; - - alias push_complex is push[queue_t, complex]; - alias pop_complex is pop[queue_t return complex]; - - procedure push ( - queue : queue_t; - value : complex_polar - ); - - impure function pop ( - queue : queue_t - ) return complex_polar; - - alias push_complex_polar is push[queue_t, complex_polar]; - alias pop_complex_polar is pop[queue_t return complex_polar]; - - procedure push ( - queue : queue_t; - value : ieee.numeric_bit.unsigned - ); - - impure function pop ( - queue : queue_t - ) return ieee.numeric_bit.unsigned; - - alias push_numeric_bit_unsigned is push[queue_t, ieee.numeric_bit.unsigned]; - alias pop_numeric_bit_unsigned is pop[queue_t return ieee.numeric_bit.unsigned]; - - procedure push ( - queue : queue_t; - value : ieee.numeric_bit.signed - ); - - impure function pop ( - queue : queue_t - ) return ieee.numeric_bit.signed; - - alias push_numeric_bit_signed is push[queue_t, ieee.numeric_bit.signed]; - alias pop_numeric_bit_signed is pop[queue_t return ieee.numeric_bit.signed]; - - procedure push ( - queue : queue_t; - value : ieee.numeric_std.unsigned - ); - - impure function pop ( - queue : queue_t - ) return ieee.numeric_std.unsigned; - - alias push_numeric_std_unsigned is push[queue_t, ieee.numeric_std.unsigned]; - alias pop_numeric_std_unsigned is pop[queue_t return ieee.numeric_std.unsigned]; - - procedure push ( - queue : queue_t; - value : ieee.numeric_std.signed - ); - - impure function pop ( - queue : queue_t - ) return ieee.numeric_std.signed; - - alias push_numeric_std_signed is push[queue_t, ieee.numeric_std.signed]; - alias pop_numeric_std_signed is pop[queue_t return ieee.numeric_std.signed]; - - procedure push ( - queue : queue_t; - value : string - ); - - impure function pop ( - queue : queue_t - ) return string; - - alias push_string is push[queue_t, string]; - alias pop_string is pop[queue_t return string]; - - procedure push ( - queue : queue_t; - value : time - ); - - impure function pop ( - queue : queue_t - ) return time; - - alias push_time is push[queue_t, time]; - alias pop_time is pop[queue_t return time]; - - procedure push ( - queue : queue_t; - variable value : inout integer_vector_ptr_t - ); - - impure function pop ( - queue : queue_t - ) return integer_vector_ptr_t; - - alias push_integer_vector_ptr_ref is push[queue_t, integer_vector_ptr_t]; - alias pop_integer_vector_ptr_ref is pop[queue_t return integer_vector_ptr_t]; - - procedure push ( - queue : queue_t; - variable value : inout string_ptr_t - ); - - impure function pop ( - queue : queue_t - ) return string_ptr_t; - - alias push_string_ptr_ref is push[queue_t, string_ptr_t]; - alias pop_string_ptr_ref is pop[queue_t return string_ptr_t]; - - procedure push ( - queue : queue_t; - variable value : inout queue_t - ); - - impure function pop ( - queue : queue_t - ) return queue_t; - - alias push_queue_ref is push[queue_t, queue_t]; - alias pop_queue_ref is pop[queue_t return queue_t]; - - procedure push_ref ( - constant queue : queue_t; - value : inout integer_array_t - ); - - impure function pop_ref ( - queue : queue_t - ) return integer_array_t; - - alias push_integer_array_t_ref is push_ref[queue_t, integer_array_t]; - alias pop_integer_array_t_ref is pop_ref[queue_t return integer_array_t]; - - -- Private - type queue_element_type_t is ( - vhdl_character, vhdl_integer, vunit_byte, vhdl_string, vhdl_boolean, vhdl_real, vhdl_bit, ieee_std_ulogic, - vhdl_severity_level, vhdl_file_open_status, vhdl_file_open_kind, vhdl_bit_vector, vhdl_std_ulogic_vector, - ieee_complex, ieee_complex_polar, ieee_numeric_bit_unsigned, ieee_numeric_bit_signed, - ieee_numeric_std_unsigned, ieee_numeric_std_signed, vhdl_time, vunit_integer_vector_ptr_t, - vunit_string_ptr_t, vunit_queue_t, vunit_integer_array_t, vhdl_boolean_vector, vhdl_integer_vector, - vhdl_real_vector, vhdl_time_vector, ieee_ufixed, ieee_sfixed, ieee_float - ); - - function encode ( - data : queue_t - ) return string; - - function decode ( - code : string - ) return queue_t; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out queue_t - ); - - alias encode_queue_t is encode[queue_t return string]; - alias decode_queue_t is decode[string return queue_t]; - - procedure push_type ( - queue : queue_t; - element_type : queue_element_type_t - ); - - procedure check_type ( - queue : queue_t; - element_type : queue_element_type_t - ); - - procedure unsafe_push ( - queue : queue_t; - value : character - ); - - impure function unsafe_pop ( - queue : queue_t - ) return character; - - procedure push_variable_string ( - queue : queue_t; - value : string - ); - - impure function pop_variable_string ( - queue : queue_t - ) return string; - - procedure push_fix_string ( - queue : queue_t; - value : string - ); - - impure function pop_fix_string ( - queue : queue_t; - length : natural - ) return string; -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.math_complex.all; +use ieee.numeric_bit.all; +use ieee.numeric_std.all; +use work.integer_vector_ptr_pkg.all; +use work.string_ptr_pkg.all; +use work.integer_array_pkg.all; + +package queue_pkg is + type queue_t is record + p_meta : integer_vector_ptr_t; + data : string_ptr_t; + end record; + type queue_vec_t is array(integer range <>) of queue_t; + constant null_queue : queue_t := (p_meta => null_ptr, data => null_string_ptr); + + impure function new_queue + return queue_t; + + -- Returns the length of the queue in bytes + impure function length ( + queue : queue_t + ) return natural; + + impure function is_empty ( + queue : queue_t + ) return boolean; + + procedure flush ( + queue : queue_t + ); + + impure function copy ( + queue : queue_t + ) return queue_t; + + procedure push ( + queue : queue_t; + value : integer + ); + + impure function pop ( + queue : queue_t + ) return integer; + + alias push_integer is push[queue_t, integer]; + alias pop_integer is pop[queue_t return integer]; + + procedure push_byte ( + queue : queue_t; + value : natural range 0 to 255 + ); + + impure function pop_byte ( + queue : queue_t + ) return integer; + + procedure push ( + queue : queue_t; + value : character + ); + + impure function pop ( + queue : queue_t + ) return character; + + alias push_character is push[queue_t, character]; + alias pop_character is pop[queue_t return character]; + + procedure push ( + queue : queue_t; + value : boolean + ); + + impure function pop ( + queue : queue_t + ) return boolean; + + alias push_boolean is push[queue_t, boolean]; + alias pop_boolean is pop[queue_t return boolean]; + + procedure push ( + queue : queue_t; + value : real + ); + + impure function pop ( + queue : queue_t + ) return real; + + alias push_real is push[queue_t, real]; + alias pop_real is pop[queue_t return real]; + + procedure push ( + queue : queue_t; + value : bit + ); + + impure function pop ( + queue : queue_t + ) return bit; + + alias push_bit is push[queue_t, bit]; + alias pop_bit is pop[queue_t return bit]; + + procedure push ( + queue : queue_t; + value : std_ulogic + ); + + impure function pop ( + queue : queue_t + ) return std_ulogic; + + alias push_std_ulogic is push[queue_t, std_ulogic]; + alias pop_std_ulogic is pop[queue_t return std_ulogic]; + + procedure push ( + queue : queue_t; + value : severity_level + ); + + impure function pop ( + queue : queue_t + ) return severity_level; + + alias push_severity_level is push[queue_t, severity_level]; + alias pop_severity_level is pop[queue_t return severity_level]; + + procedure push ( + queue : queue_t; + value : file_open_status + ); + + impure function pop ( + queue : queue_t + ) return file_open_status; + + alias push_file_open_status is push[queue_t, file_open_status]; + alias pop_file_open_status is pop[queue_t return file_open_status]; + + procedure push ( + queue : queue_t; + value : file_open_kind + ); + + impure function pop ( + queue : queue_t + ) return file_open_kind; + + alias push_file_open_kind is push[queue_t, file_open_kind]; + alias pop_file_open_kind is pop[queue_t return file_open_kind]; + + procedure push ( + queue : queue_t; + value : bit_vector + ); + + impure function pop ( + queue : queue_t + ) return bit_vector; + + alias push_bit_vector is push[queue_t, bit_vector]; + alias pop_bit_vector is pop[queue_t return bit_vector]; + + procedure push ( + queue : queue_t; + value : std_ulogic_vector + ); + + impure function pop ( + queue : queue_t + ) return std_ulogic_vector; + + alias push_std_ulogic_vector is push[queue_t, std_ulogic_vector]; + alias pop_std_ulogic_vector is pop[queue_t return std_ulogic_vector]; + + procedure push ( + queue : queue_t; + value : complex + ); + + impure function pop ( + queue : queue_t + ) return complex; + + alias push_complex is push[queue_t, complex]; + alias pop_complex is pop[queue_t return complex]; + + procedure push ( + queue : queue_t; + value : complex_polar + ); + + impure function pop ( + queue : queue_t + ) return complex_polar; + + alias push_complex_polar is push[queue_t, complex_polar]; + alias pop_complex_polar is pop[queue_t return complex_polar]; + + procedure push ( + queue : queue_t; + value : ieee.numeric_bit.unsigned + ); + + impure function pop ( + queue : queue_t + ) return ieee.numeric_bit.unsigned; + + alias push_numeric_bit_unsigned is push[queue_t, ieee.numeric_bit.unsigned]; + alias pop_numeric_bit_unsigned is pop[queue_t return ieee.numeric_bit.unsigned]; + + procedure push ( + queue : queue_t; + value : ieee.numeric_bit.signed + ); + + impure function pop ( + queue : queue_t + ) return ieee.numeric_bit.signed; + + alias push_numeric_bit_signed is push[queue_t, ieee.numeric_bit.signed]; + alias pop_numeric_bit_signed is pop[queue_t return ieee.numeric_bit.signed]; + + procedure push ( + queue : queue_t; + value : ieee.numeric_std.unsigned + ); + + impure function pop ( + queue : queue_t + ) return ieee.numeric_std.unsigned; + + alias push_numeric_std_unsigned is push[queue_t, ieee.numeric_std.unsigned]; + alias pop_numeric_std_unsigned is pop[queue_t return ieee.numeric_std.unsigned]; + + procedure push ( + queue : queue_t; + value : ieee.numeric_std.signed + ); + + impure function pop ( + queue : queue_t + ) return ieee.numeric_std.signed; + + alias push_numeric_std_signed is push[queue_t, ieee.numeric_std.signed]; + alias pop_numeric_std_signed is pop[queue_t return ieee.numeric_std.signed]; + + procedure push ( + queue : queue_t; + value : string + ); + + impure function pop ( + queue : queue_t + ) return string; + + alias push_string is push[queue_t, string]; + alias pop_string is pop[queue_t return string]; + + procedure push ( + queue : queue_t; + value : time + ); + + impure function pop ( + queue : queue_t + ) return time; + + alias push_time is push[queue_t, time]; + alias pop_time is pop[queue_t return time]; + + procedure push ( + queue : queue_t; + variable value : inout integer_vector_ptr_t + ); + + impure function pop ( + queue : queue_t + ) return integer_vector_ptr_t; + + alias push_integer_vector_ptr_ref is push[queue_t, integer_vector_ptr_t]; + alias pop_integer_vector_ptr_ref is pop[queue_t return integer_vector_ptr_t]; + + procedure push ( + queue : queue_t; + variable value : inout string_ptr_t + ); + + impure function pop ( + queue : queue_t + ) return string_ptr_t; + + alias push_string_ptr_ref is push[queue_t, string_ptr_t]; + alias pop_string_ptr_ref is pop[queue_t return string_ptr_t]; + + procedure push ( + queue : queue_t; + variable value : inout queue_t + ); + + impure function pop ( + queue : queue_t + ) return queue_t; + + alias push_queue_ref is push[queue_t, queue_t]; + alias pop_queue_ref is pop[queue_t return queue_t]; + + procedure push_ref ( + constant queue : queue_t; + value : inout integer_array_t + ); + + impure function pop_ref ( + queue : queue_t + ) return integer_array_t; + + alias push_integer_array_t_ref is push_ref[queue_t, integer_array_t]; + alias pop_integer_array_t_ref is pop_ref[queue_t return integer_array_t]; + + -- Private + type queue_element_type_t is ( + vhdl_character, vhdl_integer, vunit_byte, vhdl_string, vhdl_boolean, vhdl_real, vhdl_bit, ieee_std_ulogic, + vhdl_severity_level, vhdl_file_open_status, vhdl_file_open_kind, vhdl_bit_vector, vhdl_std_ulogic_vector, + ieee_complex, ieee_complex_polar, ieee_numeric_bit_unsigned, ieee_numeric_bit_signed, + ieee_numeric_std_unsigned, ieee_numeric_std_signed, vhdl_time, vunit_integer_vector_ptr_t, + vunit_string_ptr_t, vunit_queue_t, vunit_integer_array_t, vhdl_boolean_vector, vhdl_integer_vector, + vhdl_real_vector, vhdl_time_vector, ieee_ufixed, ieee_sfixed, ieee_float + ); + + function encode ( + data : queue_t + ) return string; + + function decode ( + code : string + ) return queue_t; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out queue_t + ); + + alias encode_queue_t is encode[queue_t return string]; + alias decode_queue_t is decode[string return queue_t]; + + procedure push_type ( + queue : queue_t; + element_type : queue_element_type_t + ); + + procedure check_type ( + queue : queue_t; + element_type : queue_element_type_t + ); + + procedure unsafe_push ( + queue : queue_t; + value : character + ); + + impure function unsafe_pop ( + queue : queue_t + ) return character; + + procedure push_variable_string ( + queue : queue_t; + value : string + ); + + impure function pop_variable_string ( + queue : queue_t + ) return string; + + procedure push_fix_string ( + queue : queue_t; + value : string + ); + + impure function pop_fix_string ( + queue : queue_t; + length : natural + ) return string; +end package; diff --git a/vunit/vhdl/data_types/src/queue_pool_pkg.vhd b/vunit/vhdl/data_types/src/queue_pool_pkg.vhd index 5fe922f8b..abc5f24da 100644 --- a/vunit/vhdl/data_types/src/queue_pool_pkg.vhd +++ b/vunit/vhdl/data_types/src/queue_pool_pkg.vhd @@ -1,62 +1,62 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.integer_vector_ptr_pool_pkg.all; -use work.string_ptr_pool_pkg.all; -use work.queue_pkg.all; - -package queue_pool_pkg is - type queue_pool_t is record - index_pool : integer_vector_ptr_pool_t; - data_pool : string_ptr_pool_t; - end record; - constant null_queue_pool : queue_pool_t := ( - index_pool => null_integer_vector_ptr_pool, - data_pool => null_string_ptr_pool); - - impure function new_queue_pool - return queue_pool_t; - - impure function new_queue ( - pool : queue_pool_t - ) return queue_t; - - procedure recycle ( - pool : queue_pool_t; - variable queue : inout queue_t - ); -end package; - -package body queue_pool_pkg is - impure function new_queue_pool - return queue_pool_t is begin - return ( - index_pool => new_integer_vector_ptr_pool, - data_pool => new_string_ptr_pool - ); - end; - - impure function new_queue ( - pool : queue_pool_t - ) return queue_t is - variable queue : queue_t; - begin - queue := ( - p_meta => new_integer_vector_ptr(pool.index_pool, 2), - data => new_string_ptr(pool.data_pool, 0) - ); - flush(queue); - return queue; - end; - - procedure recycle ( - pool : queue_pool_t; - variable queue : inout queue_t - ) is begin - recycle(pool.index_pool, queue.p_meta); - recycle(pool.data_pool, queue.data); - end; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.integer_vector_ptr_pool_pkg.all; +use work.string_ptr_pool_pkg.all; +use work.queue_pkg.all; + +package queue_pool_pkg is + type queue_pool_t is record + index_pool : integer_vector_ptr_pool_t; + data_pool : string_ptr_pool_t; + end record; + constant null_queue_pool : queue_pool_t := ( + index_pool => null_integer_vector_ptr_pool, + data_pool => null_string_ptr_pool); + + impure function new_queue_pool + return queue_pool_t; + + impure function new_queue ( + pool : queue_pool_t + ) return queue_t; + + procedure recycle ( + pool : queue_pool_t; + variable queue : inout queue_t + ); +end package; + +package body queue_pool_pkg is + impure function new_queue_pool + return queue_pool_t is begin + return ( + index_pool => new_integer_vector_ptr_pool, + data_pool => new_string_ptr_pool + ); + end; + + impure function new_queue ( + pool : queue_pool_t + ) return queue_t is + variable queue : queue_t; + begin + queue := ( + p_meta => new_integer_vector_ptr(pool.index_pool, 2), + data => new_string_ptr(pool.data_pool, 0) + ); + flush(queue); + return queue; + end; + + procedure recycle ( + pool : queue_pool_t; + variable queue : inout queue_t + ) is begin + recycle(pool.index_pool, queue.p_meta); + recycle(pool.data_pool, queue.data); + end; +end package body; diff --git a/vunit/vhdl/data_types/src/string_ptr_pkg-body-200x.vhd b/vunit/vhdl/data_types/src/string_ptr_pkg-body-200x.vhd index e8273fde5..83406a721 100644 --- a/vunit/vhdl/data_types/src/string_ptr_pkg-body-200x.vhd +++ b/vunit/vhdl/data_types/src/string_ptr_pkg-body-200x.vhd @@ -1,269 +1,269 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -package body string_ptr_pkg is - type string_ptr_storage_t is protected - impure function new_string_ptr ( - length : natural := 0 - ) return natural; - - procedure deallocate ( - ref : natural - ); - - impure function length ( - ref : natural - ) return integer; - - procedure set ( - ref : natural; - index : natural; - value : val_t - ); - - impure function get ( - ref : natural; - index : natural - ) return val_t; - - procedure reallocate ( - ref : natural; - length : natural - ); - - procedure reallocate ( - ref : natural; - value : string - ); - - procedure resize ( - ref : natural; - length : natural; - drop : natural := 0 - ); - - impure function to_string ( - ref : natural - ) return string; - end protected; - - type string_ptr_storage_t is protected body - variable current_index : integer := 0; - variable ptrs : vava_t := null; - - impure function new_string_ptr ( - length : natural := 0 - ) return natural is - variable old_ptrs : string_access_vector_access_t; - begin - if ptrs = null then - ptrs := new vav_t'(0 => null); - elsif ptrs'length <= current_index then - -- Reallocate ptr pointers to larger ptr - -- Use more size to trade size for speed - old_ptrs := ptrs; - ptrs := new vav_t'(0 to ptrs'length + 2**16 => null); - for i in old_ptrs'range loop - ptrs(i) := old_ptrs(i); - end loop; - deallocate(old_ptrs); - end if; - ptrs(current_index) := new string'(1 to length => val_t'low); - current_index := current_index + 1; - return current_index-1; - end; - - procedure deallocate ( - ref : natural - ) is begin - deallocate(ptrs(ref)); - ptrs(ref) := null; - end; - - impure function length ( - ref : natural - ) return integer is begin - return ptrs(ref)'length; - end; - - procedure set ( - ref : natural; - index : natural; - value : val_t - ) is begin - ptrs(ref)(index) := value; - end; - - impure function get ( - ref : natural; - index : natural - ) return val_t is begin - return ptrs(ref)(index); - end; - - procedure reallocate ( - ref : natural; - length : natural - ) is - variable old_ptr, new_ptr : string_access_t; - begin - deallocate(ptrs(ref)); - ptrs(ref) := new string'(1 to length => val_t'low); - end; - - procedure reallocate ( - ref : natural; - value : string - ) is - variable old_ptr, new_ptr : string_access_t; - variable n_value : string(1 to value'length) := value; - begin - deallocate(ptrs(ref)); - ptrs(ref) := new string'(n_value); - end; - - procedure resize ( - ref : natural; - length : natural; - drop : natural := 0 - ) is - variable old_ptr, new_ptr : string_access_t; - variable min_length : natural := length; - begin - new_ptr := new string'(1 to length => val_t'low); - old_ptr := ptrs(ref); - if min_length > old_ptr'length - drop then - min_length := old_ptr'length - drop; - end if; - for i in 1 to min_length loop - new_ptr(i) := old_ptr(drop + i); - end loop; - ptrs(ref) := new_ptr; - deallocate(old_ptr); - end; - - impure function to_string ( - ref : natural - ) return string is begin - return ptrs(ref).all; - end; - - end protected body; - - shared variable string_ptr_storage : string_ptr_storage_t; - - function to_integer ( - value : ptr_t - ) return integer is begin - return value.ref; - end; - - impure function to_string_ptr ( - value : integer - ) return ptr_t is begin - -- @TODO maybe assert that the ref is valid - return (ref => value); - end; - - impure function new_string_ptr ( - length : natural := 0 - ) return ptr_t is begin - return (ref => string_ptr_storage.new_string_ptr(length)); - end; - - impure function new_string_ptr ( - value : string - ) return ptr_t is - variable result : ptr_t := new_string_ptr(value'length); - variable n_value : string(1 to value'length) := value; - begin - for i in 1 to n_value'length loop - set(result, i, n_value(i)); - end loop; - return result; - end; - - procedure deallocate ( - ptr : ptr_t - ) is - begin - string_ptr_storage.deallocate(ptr.ref); - end; - - impure function length ( - ptr : ptr_t - ) return integer is begin - return string_ptr_storage.length(ptr.ref); - end; - - procedure set ( - ptr : ptr_t; - index : natural; - value : val_t - ) is begin - string_ptr_storage.set(ptr.ref, index, value); - end; - - impure function get ( - ptr : ptr_t; - index : natural - ) return val_t is begin - return string_ptr_storage.get(ptr.ref, index); - end; - - procedure reallocate ( - ptr : ptr_t; - length : natural - ) is begin - string_ptr_storage.reallocate(ptr.ref, length); - end; - - procedure reallocate ( - ptr : ptr_t; - value : string - ) is begin - string_ptr_storage.reallocate(ptr.ref, value); - end; - - procedure resize ( - ptr : ptr_t; - length : natural; - drop : natural := 0 - ) is begin - string_ptr_storage.resize(ptr.ref, length, drop); - end; - - impure function to_string ( - ptr : ptr_t - ) return string is begin - return string_ptr_storage.to_string(ptr.ref); - end; - - function encode ( - data : ptr_t - ) return string is begin - return encode(data.ref); - end; - - function decode ( - code : string - ) return ptr_t is - variable ret_val : ptr_t; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - return ret_val; - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out ptr_t - ) is begin - decode(code, index, result.ref); - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +package body string_ptr_pkg is + type string_ptr_storage_t is protected + impure function new_string_ptr ( + length : natural := 0 + ) return natural; + + procedure deallocate ( + ref : natural + ); + + impure function length ( + ref : natural + ) return integer; + + procedure set ( + ref : natural; + index : natural; + value : val_t + ); + + impure function get ( + ref : natural; + index : natural + ) return val_t; + + procedure reallocate ( + ref : natural; + length : natural + ); + + procedure reallocate ( + ref : natural; + value : string + ); + + procedure resize ( + ref : natural; + length : natural; + drop : natural := 0 + ); + + impure function to_string ( + ref : natural + ) return string; + end protected; + + type string_ptr_storage_t is protected body + variable current_index : integer := 0; + variable ptrs : vava_t := null; + + impure function new_string_ptr ( + length : natural := 0 + ) return natural is + variable old_ptrs : string_access_vector_access_t; + begin + if ptrs = null then + ptrs := new vav_t'(0 => null); + elsif ptrs'length <= current_index then + -- Reallocate ptr pointers to larger ptr + -- Use more size to trade size for speed + old_ptrs := ptrs; + ptrs := new vav_t'(0 to ptrs'length + 2**16 => null); + for i in old_ptrs'range loop + ptrs(i) := old_ptrs(i); + end loop; + deallocate(old_ptrs); + end if; + ptrs(current_index) := new string'(1 to length => val_t'low); + current_index := current_index + 1; + return current_index-1; + end; + + procedure deallocate ( + ref : natural + ) is begin + deallocate(ptrs(ref)); + ptrs(ref) := null; + end; + + impure function length ( + ref : natural + ) return integer is begin + return ptrs(ref)'length; + end; + + procedure set ( + ref : natural; + index : natural; + value : val_t + ) is begin + ptrs(ref)(index) := value; + end; + + impure function get ( + ref : natural; + index : natural + ) return val_t is begin + return ptrs(ref)(index); + end; + + procedure reallocate ( + ref : natural; + length : natural + ) is + variable old_ptr, new_ptr : string_access_t; + begin + deallocate(ptrs(ref)); + ptrs(ref) := new string'(1 to length => val_t'low); + end; + + procedure reallocate ( + ref : natural; + value : string + ) is + variable old_ptr, new_ptr : string_access_t; + variable n_value : string(1 to value'length) := value; + begin + deallocate(ptrs(ref)); + ptrs(ref) := new string'(n_value); + end; + + procedure resize ( + ref : natural; + length : natural; + drop : natural := 0 + ) is + variable old_ptr, new_ptr : string_access_t; + variable min_length : natural := length; + begin + new_ptr := new string'(1 to length => val_t'low); + old_ptr := ptrs(ref); + if min_length > old_ptr'length - drop then + min_length := old_ptr'length - drop; + end if; + for i in 1 to min_length loop + new_ptr(i) := old_ptr(drop + i); + end loop; + ptrs(ref) := new_ptr; + deallocate(old_ptr); + end; + + impure function to_string ( + ref : natural + ) return string is begin + return ptrs(ref).all; + end; + + end protected body; + + shared variable string_ptr_storage : string_ptr_storage_t; + + function to_integer ( + value : ptr_t + ) return integer is begin + return value.ref; + end; + + impure function to_string_ptr ( + value : integer + ) return ptr_t is begin + -- @TODO maybe assert that the ref is valid + return (ref => value); + end; + + impure function new_string_ptr ( + length : natural := 0 + ) return ptr_t is begin + return (ref => string_ptr_storage.new_string_ptr(length)); + end; + + impure function new_string_ptr ( + value : string + ) return ptr_t is + variable result : ptr_t := new_string_ptr(value'length); + variable n_value : string(1 to value'length) := value; + begin + for i in 1 to n_value'length loop + set(result, i, n_value(i)); + end loop; + return result; + end; + + procedure deallocate ( + ptr : ptr_t + ) is + begin + string_ptr_storage.deallocate(ptr.ref); + end; + + impure function length ( + ptr : ptr_t + ) return integer is begin + return string_ptr_storage.length(ptr.ref); + end; + + procedure set ( + ptr : ptr_t; + index : natural; + value : val_t + ) is begin + string_ptr_storage.set(ptr.ref, index, value); + end; + + impure function get ( + ptr : ptr_t; + index : natural + ) return val_t is begin + return string_ptr_storage.get(ptr.ref, index); + end; + + procedure reallocate ( + ptr : ptr_t; + length : natural + ) is begin + string_ptr_storage.reallocate(ptr.ref, length); + end; + + procedure reallocate ( + ptr : ptr_t; + value : string + ) is begin + string_ptr_storage.reallocate(ptr.ref, value); + end; + + procedure resize ( + ptr : ptr_t; + length : natural; + drop : natural := 0 + ) is begin + string_ptr_storage.resize(ptr.ref, length, drop); + end; + + impure function to_string ( + ptr : ptr_t + ) return string is begin + return string_ptr_storage.to_string(ptr.ref); + end; + + function encode ( + data : ptr_t + ) return string is begin + return encode(data.ref); + end; + + function decode ( + code : string + ) return ptr_t is + variable ret_val : ptr_t; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + return ret_val; + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out ptr_t + ) is begin + decode(code, index, result.ref); + end; + +end package body; diff --git a/vunit/vhdl/data_types/src/string_ptr_pkg-body-93.vhd b/vunit/vhdl/data_types/src/string_ptr_pkg-body-93.vhd index 54e605665..7f0684109 100644 --- a/vunit/vhdl/data_types/src/string_ptr_pkg-body-93.vhd +++ b/vunit/vhdl/data_types/src/string_ptr_pkg-body-93.vhd @@ -1,158 +1,158 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -package body string_ptr_pkg is - shared variable current_index : integer := 0; - shared variable ptrs : vava_t := null; - - impure function new_string_ptr ( - length : natural := 0 - ) return ptr_t is - variable old_ptrs : vava_t; - variable retval : ptr_t := (ref => current_index); - begin - if ptrs = null then - ptrs := new vav_t'(0 => null); - elsif ptrs'length <= current_index then - -- Reallocate ptr pointers to larger ptr - -- Use more size to trade size for speed - old_ptrs := ptrs; - ptrs := new vav_t'(0 to ptrs'length + 2**16 => null); - for i in old_ptrs'range loop - ptrs(i) := old_ptrs(i); - end loop; - deallocate(old_ptrs); - end if; - ptrs(current_index) := new string'(1 to length => val_t'low); - current_index := current_index + 1; - return retval; - end; - - procedure deallocate ( - ptr : ptr_t - ) is begin - deallocate(ptrs(ptr.ref)); - ptrs(ptr.ref) := null; - end; - - impure function length ( - ptr : ptr_t - ) return integer is begin - return ptrs(ptr.ref)'length; - end; - - procedure set ( - ptr : ptr_t; - index : natural; - value : val_t - ) is begin - ptrs(ptr.ref)(index) := value; - end; - - impure function get ( - ptr : ptr_t; - index : natural - ) return val_t is begin - return ptrs(ptr.ref)(index); - end; - - procedure reallocate ( - ptr : ptr_t; - length : natural - ) is - variable old_ptr, new_ptr : string_access_t; - begin - deallocate(ptrs(ptr.ref)); - ptrs(ptr.ref) := new string'(1 to length => val_t'low); - end; - - procedure reallocate ( - ptr : ptr_t; - value : string - ) is - variable old_ptr, new_ptr : string_access_t; - variable n_value : string(1 to value'length) := value; - begin - deallocate(ptrs(ptr.ref)); - ptrs(ptr.ref) := new string'(n_value); - end; - - procedure resize ( - ptr : ptr_t; - length : natural; - drop : natural := 0 - ) is - variable old_ptr, new_ptr : string_access_t; - variable min_length : natural := length; - begin - new_ptr := new string'(1 to length => val_t'low); - old_ptr := ptrs(ptr.ref); - if min_length > old_ptr'length - drop then - min_length := old_ptr'length - drop; - end if; - for i in 1 to min_length loop - new_ptr(i) := old_ptr(drop + i); - end loop; - ptrs(ptr.ref) := new_ptr; - deallocate(old_ptr); - end; - - impure function to_string ( - ptr : ptr_t - ) return string is begin - return ptrs(ptr.ref).all; - end; - - function to_integer ( - value : ptr_t - ) return integer is begin - return value.ref; - end; - - impure function to_string_ptr ( - value : integer - ) return ptr_t is begin - -- @TODO maybe assert that the ref is valid - return (ref => value); - end; - - impure function new_string_ptr ( - value : string - ) return ptr_t is - variable result : ptr_t := new_string_ptr(value'length); - variable n_value : string(1 to value'length) := value; - begin - for i in 1 to n_value'length loop - set(result, i, n_value(i)); - end loop; - return result; - end; - - function encode ( - data : ptr_t - ) return string is begin - return encode(data.ref); - end; - - function decode ( - code : string - ) return ptr_t is - variable ret_val : ptr_t; - variable index : positive := code'left; - begin - decode(code, index, ret_val); - return ret_val; - end; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out ptr_t - ) is begin - decode(code, index, result.ref); - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +package body string_ptr_pkg is + shared variable current_index : integer := 0; + shared variable ptrs : vava_t := null; + + impure function new_string_ptr ( + length : natural := 0 + ) return ptr_t is + variable old_ptrs : vava_t; + variable retval : ptr_t := (ref => current_index); + begin + if ptrs = null then + ptrs := new vav_t'(0 => null); + elsif ptrs'length <= current_index then + -- Reallocate ptr pointers to larger ptr + -- Use more size to trade size for speed + old_ptrs := ptrs; + ptrs := new vav_t'(0 to ptrs'length + 2**16 => null); + for i in old_ptrs'range loop + ptrs(i) := old_ptrs(i); + end loop; + deallocate(old_ptrs); + end if; + ptrs(current_index) := new string'(1 to length => val_t'low); + current_index := current_index + 1; + return retval; + end; + + procedure deallocate ( + ptr : ptr_t + ) is begin + deallocate(ptrs(ptr.ref)); + ptrs(ptr.ref) := null; + end; + + impure function length ( + ptr : ptr_t + ) return integer is begin + return ptrs(ptr.ref)'length; + end; + + procedure set ( + ptr : ptr_t; + index : natural; + value : val_t + ) is begin + ptrs(ptr.ref)(index) := value; + end; + + impure function get ( + ptr : ptr_t; + index : natural + ) return val_t is begin + return ptrs(ptr.ref)(index); + end; + + procedure reallocate ( + ptr : ptr_t; + length : natural + ) is + variable old_ptr, new_ptr : string_access_t; + begin + deallocate(ptrs(ptr.ref)); + ptrs(ptr.ref) := new string'(1 to length => val_t'low); + end; + + procedure reallocate ( + ptr : ptr_t; + value : string + ) is + variable old_ptr, new_ptr : string_access_t; + variable n_value : string(1 to value'length) := value; + begin + deallocate(ptrs(ptr.ref)); + ptrs(ptr.ref) := new string'(n_value); + end; + + procedure resize ( + ptr : ptr_t; + length : natural; + drop : natural := 0 + ) is + variable old_ptr, new_ptr : string_access_t; + variable min_length : natural := length; + begin + new_ptr := new string'(1 to length => val_t'low); + old_ptr := ptrs(ptr.ref); + if min_length > old_ptr'length - drop then + min_length := old_ptr'length - drop; + end if; + for i in 1 to min_length loop + new_ptr(i) := old_ptr(drop + i); + end loop; + ptrs(ptr.ref) := new_ptr; + deallocate(old_ptr); + end; + + impure function to_string ( + ptr : ptr_t + ) return string is begin + return ptrs(ptr.ref).all; + end; + + function to_integer ( + value : ptr_t + ) return integer is begin + return value.ref; + end; + + impure function to_string_ptr ( + value : integer + ) return ptr_t is begin + -- @TODO maybe assert that the ref is valid + return (ref => value); + end; + + impure function new_string_ptr ( + value : string + ) return ptr_t is + variable result : ptr_t := new_string_ptr(value'length); + variable n_value : string(1 to value'length) := value; + begin + for i in 1 to n_value'length loop + set(result, i, n_value(i)); + end loop; + return result; + end; + + function encode ( + data : ptr_t + ) return string is begin + return encode(data.ref); + end; + + function decode ( + code : string + ) return ptr_t is + variable ret_val : ptr_t; + variable index : positive := code'left; + begin + decode(code, index, ret_val); + return ret_val; + end; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out ptr_t + ) is begin + decode(code, index, result.ref); + end; + +end package body; diff --git a/vunit/vhdl/data_types/src/string_ptr_pkg.vhd b/vunit/vhdl/data_types/src/string_ptr_pkg.vhd index a6e8afa1c..43376b315 100644 --- a/vunit/vhdl/data_types/src/string_ptr_pkg.vhd +++ b/vunit/vhdl/data_types/src/string_ptr_pkg.vhd @@ -1,104 +1,104 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com --- --- The purpose of this package is to provide an string access type (pointer) --- that can itself be used in arrays and returned from functions unlike a --- real access type. This is achieved by letting the actual value be a handle --- into a singleton datastructure of string access types. --- - -use work.string_pkg.all; - -use work.codec_pkg.all; -use work.codec_builder_pkg.all; - -package string_ptr_pkg is - subtype index_t is integer range -1 to integer'high; - type string_ptr_t is record - ref : index_t; - end record; - constant null_string_ptr : string_ptr_t := (ref => -1); - - alias ptr_t is string_ptr_t; - alias val_t is character; - alias vav_t is string_access_vector_t; - alias vava_t is string_access_vector_access_t; - - function to_integer ( - value : string_ptr_t - ) return integer; - - impure function to_string_ptr ( - value : integer - ) return string_ptr_t; - - impure function new_string_ptr ( - length : natural := 0 - ) return string_ptr_t; - - impure function new_string_ptr ( - value : string - ) return string_ptr_t; - - procedure deallocate ( - ptr : string_ptr_t - ); - - impure function length ( - ptr : string_ptr_t - ) return integer; - - procedure set ( - ptr : string_ptr_t; - index : natural; - value : character - ); - - impure function get ( - ptr : string_ptr_t; - index : natural - ) return character; - - procedure reallocate ( - ptr : string_ptr_t; - length : natural - ); - - procedure reallocate ( - ptr : string_ptr_t; - value : string - ); - - procedure resize ( - ptr : string_ptr_t; - length : natural; - drop : natural := 0 - ); - - impure function to_string ( - ptr : string_ptr_t - ) return string; - - function encode ( - data : string_ptr_t - ) return string; - - function decode ( - code : string - ) return string_ptr_t; - - procedure decode ( - constant code : string; - variable index : inout positive; - variable result : out string_ptr_t - ); - - alias encode_string_ptr_t is encode[string_ptr_t return string]; - alias decode_string_ptr_t is decode[string return string_ptr_t]; - - constant string_ptr_t_code_length : positive := integer_code_length; - -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +-- +-- The purpose of this package is to provide an string access type (pointer) +-- that can itself be used in arrays and returned from functions unlike a +-- real access type. This is achieved by letting the actual value be a handle +-- into a singleton datastructure of string access types. +-- + +use work.string_pkg.all; + +use work.codec_pkg.all; +use work.codec_builder_pkg.all; + +package string_ptr_pkg is + subtype index_t is integer range -1 to integer'high; + type string_ptr_t is record + ref : index_t; + end record; + constant null_string_ptr : string_ptr_t := (ref => -1); + + alias ptr_t is string_ptr_t; + alias val_t is character; + alias vav_t is string_access_vector_t; + alias vava_t is string_access_vector_access_t; + + function to_integer ( + value : string_ptr_t + ) return integer; + + impure function to_string_ptr ( + value : integer + ) return string_ptr_t; + + impure function new_string_ptr ( + length : natural := 0 + ) return string_ptr_t; + + impure function new_string_ptr ( + value : string + ) return string_ptr_t; + + procedure deallocate ( + ptr : string_ptr_t + ); + + impure function length ( + ptr : string_ptr_t + ) return integer; + + procedure set ( + ptr : string_ptr_t; + index : natural; + value : character + ); + + impure function get ( + ptr : string_ptr_t; + index : natural + ) return character; + + procedure reallocate ( + ptr : string_ptr_t; + length : natural + ); + + procedure reallocate ( + ptr : string_ptr_t; + value : string + ); + + procedure resize ( + ptr : string_ptr_t; + length : natural; + drop : natural := 0 + ); + + impure function to_string ( + ptr : string_ptr_t + ) return string; + + function encode ( + data : string_ptr_t + ) return string; + + function decode ( + code : string + ) return string_ptr_t; + + procedure decode ( + constant code : string; + variable index : inout positive; + variable result : out string_ptr_t + ); + + alias encode_string_ptr_t is encode[string_ptr_t return string]; + alias decode_string_ptr_t is decode[string return string_ptr_t]; + + constant string_ptr_t_code_length : positive := integer_code_length; + +end package; diff --git a/vunit/vhdl/data_types/src/string_ptr_pool_pkg.vhd b/vunit/vhdl/data_types/src/string_ptr_pool_pkg.vhd index 33190b96a..02f14290a 100644 --- a/vunit/vhdl/data_types/src/string_ptr_pool_pkg.vhd +++ b/vunit/vhdl/data_types/src/string_ptr_pool_pkg.vhd @@ -1,90 +1,90 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -use work.string_ptr_pkg.all; -use work.queue_pkg.all; - -package string_ptr_pool_pkg is - type string_ptr_pool_t is record - ptrs : queue_t; - end record; - constant null_string_ptr_pool : string_ptr_pool_t := (others => null_queue); - - impure function new_string_ptr_pool - return string_ptr_pool_t; - - impure function new_string_ptr ( - pool : string_ptr_pool_t; - min_length : natural := 0 - ) return string_ptr_t; - - impure function new_string_ptr ( - pool : string_ptr_pool_t; - value : string - ) return string_ptr_t; - - procedure recycle ( - pool : string_ptr_pool_t; - variable ptr : inout string_ptr_t - ); -end package; - -package body string_ptr_pool_pkg is - impure function new_string_ptr_pool - return string_ptr_pool_t is begin - return (ptrs => new_queue); - end; - - impure function new_string_ptr ( - pool : string_ptr_pool_t; - min_length : natural := 0 - ) return string_ptr_t is - variable ptr : string_ptr_t; - begin - if length(pool.ptrs) > 0 then - -- Reuse - ptr := to_string_ptr(pop(pool.ptrs)); - if length(ptr) < min_length then - reallocate(ptr, min_length); - end if; - else - -- Allocate new - ptr := new_string_ptr(min_length); - end if; - return ptr; - end; - - impure function new_string_ptr ( - pool : string_ptr_pool_t; - value : string - ) return string_ptr_t is - variable ptr : string_ptr_t; - begin - if length(pool.ptrs) > 0 then - -- Reuse - ptr := to_string_ptr(pop(pool.ptrs)); - reallocate(ptr, value); - else - -- Allocate new - ptr := new_string_ptr(value); - end if; - return ptr; - end; - - procedure recycle ( - pool : string_ptr_pool_t; - variable ptr : inout string_ptr_t - ) is begin - if ptr = null_string_ptr then - return; - end if; - push(pool.ptrs, to_integer(ptr)); - ptr := null_string_ptr; - end; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +use work.string_ptr_pkg.all; +use work.queue_pkg.all; + +package string_ptr_pool_pkg is + type string_ptr_pool_t is record + ptrs : queue_t; + end record; + constant null_string_ptr_pool : string_ptr_pool_t := (others => null_queue); + + impure function new_string_ptr_pool + return string_ptr_pool_t; + + impure function new_string_ptr ( + pool : string_ptr_pool_t; + min_length : natural := 0 + ) return string_ptr_t; + + impure function new_string_ptr ( + pool : string_ptr_pool_t; + value : string + ) return string_ptr_t; + + procedure recycle ( + pool : string_ptr_pool_t; + variable ptr : inout string_ptr_t + ); +end package; + +package body string_ptr_pool_pkg is + impure function new_string_ptr_pool + return string_ptr_pool_t is begin + return (ptrs => new_queue); + end; + + impure function new_string_ptr ( + pool : string_ptr_pool_t; + min_length : natural := 0 + ) return string_ptr_t is + variable ptr : string_ptr_t; + begin + if length(pool.ptrs) > 0 then + -- Reuse + ptr := to_string_ptr(pop(pool.ptrs)); + if length(ptr) < min_length then + reallocate(ptr, min_length); + end if; + else + -- Allocate new + ptr := new_string_ptr(min_length); + end if; + return ptr; + end; + + impure function new_string_ptr ( + pool : string_ptr_pool_t; + value : string + ) return string_ptr_t is + variable ptr : string_ptr_t; + begin + if length(pool.ptrs) > 0 then + -- Reuse + ptr := to_string_ptr(pop(pool.ptrs)); + reallocate(ptr, value); + else + -- Allocate new + ptr := new_string_ptr(value); + end if; + return ptr; + end; + + procedure recycle ( + pool : string_ptr_pool_t; + variable ptr : inout string_ptr_t + ) is begin + if ptr = null_string_ptr then + return; + end if; + push(pool.ptrs, to_integer(ptr)); + ptr := null_string_ptr; + end; +end package body; diff --git a/vunit/vhdl/data_types/test/tb_codec-2008.vhd b/vunit/vhdl/data_types/test/tb_codec-2008.vhd index 5dc3329f0..a9047a6d1 100644 --- a/vunit/vhdl/data_types/test/tb_codec-2008.vhd +++ b/vunit/vhdl/data_types/test/tb_codec-2008.vhd @@ -1,134 +1,134 @@ --- Test suite for com codec package --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library vunit_lib; -context vunit_lib.vunit_context; -use vunit_lib.queue_pkg.all; -use vunit_lib.integer_vector_ptr_pkg.all; -use vunit_lib.codec_2008_pkg.all; - -library ieee; -use ieee.std_logic_1164.all; -use ieee.float_pkg.all; -use ieee.math_complex.all; -use ieee.numeric_bit.all; -use ieee.numeric_std.all; -use ieee.fixed_pkg.all; - -use std.textio.all; - -entity tb_codec_2008 is - generic ( - runner_cfg : string); -end entity tb_codec_2008; - -architecture test_fixture of tb_codec_2008 is -begin - test_runner : process - constant f64 : float64 := (others => '0'); - constant positive_zero : float64 := to_float( - std_logic_vector'(B"0_00000000000_0000000000000000000000000000000000000000000000000000"), f64); - constant negative_zero : float64 := to_float( - std_logic_vector'(B"1_00000000000_0000000000000000000000000000000000000000000000000000"), f64); - constant positive_infinity : float64 := to_float( - std_logic_vector'(B"0_11111111111_0000000000000000000000000000000000000000000000000000"), f64); - constant negative_infinity : float64 := to_float( - std_logic_vector'(B"1_11111111111_0000000000000000000000000000000000000000000000000000"), f64); - constant nan : float64 := to_float( - std_logic_vector'(B"1_11111111111_0000000000000000000000000000000000000000000000000001"), f64); - variable null_boolean_vector : boolean_vector(1 to 0); - variable null_integer_vector : integer_vector(1 to 0); - variable null_real_vector : real_vector(1 to 0); - variable null_time_vector : time_vector(1 to 0); - - variable boolean_vector_5_downto_3 : boolean_vector(5 downto 3); - variable integer_vector_5_downto_3 : integer_vector(5 downto 3); - variable real_vector_5_downto_3 : real_vector(5 downto 3); - variable time_vector_5_downto_3 : time_vector(5 downto 3); - - -- Temp variables to make test case pass Riviera-PRO 2016.10 - variable range_left, range_right : integer; - - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test that boolean_vector can be encoded and decoded") then - boolean_vector_5_downto_3 := (true, false, true); - check_relation(decode_boolean_vector(encode_boolean_vector((true, false, true))) = boolean_vector'(true, false, true)); - check_relation(decode_boolean_vector(encode_boolean_vector((0 => true))) = boolean_vector'(0 => true)); - check_relation(decode_boolean_vector(encode_boolean_vector(null_boolean_vector)) = null_boolean_vector); - check_relation(decode_boolean_vector(encode_boolean_vector(boolean_vector_5_downto_3)) = boolean_vector'(true, false, true)); - range_left := decode_boolean_vector(encode_boolean_vector(boolean_vector_5_downto_3))'left; - range_right := decode_boolean_vector(encode_boolean_vector(boolean_vector_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); - elsif run("Test that integer_vector can be encoded and decoded") then - integer_vector_5_downto_3 := (-42, 0, 17); - check_relation(decode_integer_vector(encode_integer_vector((-2147483648, -2147483648, -2147483648))) = integer_vector'(-2147483648, -2147483648, -2147483648)); - check_relation(decode_integer_vector(encode_integer_vector((-42, 0, 17))) = integer_vector'(-42, 0, 17)); - check_relation(decode_integer_vector(encode_integer_vector((0 => -42))) = integer_vector'(0 => -42)); - check_relation(decode_integer_vector(encode_integer_vector(null_integer_vector)) = null_integer_vector); - check_relation(decode_integer_vector(encode_integer_vector(integer_vector_5_downto_3)) = integer_vector'(-42, 0, 17)); - range_left := decode_integer_vector(encode_integer_vector(integer_vector_5_downto_3))'left; - range_right := decode_integer_vector(encode_integer_vector(integer_vector_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); - elsif run("Test that real_vector can be encoded and decoded") then - real_vector_5_downto_3 := (-42.42, 0.001, 17.17); - check_relation(decode_real_vector(encode_real_vector((-42.42, 0.001, 17.17))) = real_vector'(-42.42, 0.001, 17.17)); - check_relation(decode_real_vector(encode_real_vector((0 => -42.42))) = real_vector'(0 => -42.42)); - check_relation(decode_real_vector(encode_real_vector(null_real_vector)) = null_real_vector); - check_relation(decode_real_vector(encode_real_vector(real_vector_5_downto_3)) = real_vector'(-42.42, 0.001, 17.17)); - range_left := decode_real_vector(encode_real_vector(real_vector_5_downto_3))'left; - range_right := decode_real_vector(encode_real_vector(real_vector_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); - elsif run("Test that time_vector can be encoded and decoded") then - time_vector_5_downto_3 := (-42 ms, 0 sec, 17 min); - check_relation(decode_time_vector(encode_time_vector((-42 ms, 0 sec, 17 min))) = time_vector'(-42 ms, 0 sec, 17 min)); - check_relation(decode_time_vector(encode_time_vector((0 => -42 ms))) = time_vector'(0 => -42 ms)); - check_relation(decode_time_vector(encode_time_vector(null_time_vector)) = null_time_vector); - check_relation(decode_time_vector(encode_time_vector(time_vector_5_downto_3)) = time_vector'(-42 ms, 0 sec, 17 min)); - range_left := decode_time_vector(encode_time_vector(time_vector_5_downto_3))'left; - range_right := decode_time_vector(encode_time_vector(time_vector_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); - elsif run("Test that ufixed can be encoded and decoded") then - check_relation(decode_ufixed(encode_ufixed(to_ufixed(6.5, 3, -3))) = to_ufixed(6.5, 3, -3)); - check_relation(decode_ufixed(encode_ufixed(to_ufixed(8.0, 3, 1))) = to_ufixed(8.0, 3, 1)); - check_relation(decode_ufixed(encode_ufixed(to_ufixed(0.25, -2, -4))) = to_ufixed(0.25, -2, -4)); - elsif run("Test that sfixed can be encoded and decoded") then - check_relation(decode_sfixed(encode_sfixed(to_sfixed(6.5, 3, -3))) = to_sfixed(6.5, 3, -3)); - check_relation(decode_sfixed(encode_sfixed(to_sfixed(8.0, 4, 1))) = to_sfixed(8.0, 4, 1)); - check_relation(decode_sfixed(encode_sfixed(to_sfixed(0.25, -1, -4))) = to_sfixed(0.25, -1, -4)); - check_relation(decode_sfixed(encode_sfixed(to_sfixed(-6.5, 3, -3))) = to_sfixed(-6.5, 3, -3)); - check_relation(decode_sfixed(encode_sfixed(to_sfixed(-8.0, 4, 1))) = to_sfixed(-8.0, 4, 1)); - check_relation(decode_sfixed(encode_sfixed(to_sfixed(-0.25, -1, -4))) = to_sfixed(-0.25, -1, -4)); - elsif run("Test that float can be encoded and decoded") then - check_relation(decode_float(encode_float(to_float(real'low, 11, 52))) = to_float(real'low, 11, 52)); - check_relation(decode_float(encode_float(to_float(real'high, 11, 52))) = to_float(real'high, 11, 52)); - - check_relation(to_string(decode_float(encode_float(positive_zero))) = to_string(positive_zero)); - check_relation(to_string(decode_float(encode_float(negative_zero))) = to_string(negative_zero)); - check_relation(to_string(decode_float(encode_float(positive_infinity))) = to_string(positive_infinity)); - check_relation(to_string(decode_float(encode_float(negative_infinity))) = to_string(negative_infinity)); - check_relation(to_string(decode_float(encode_float(nan))) = to_string(nan)); - check_relation(to_string(decode_float(encode_float(nan))) /= to_string(positive_zero)); - check_relation(to_string(decode_float(encode_float(negative_zero))) /= to_string(positive_zero)); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 100 ms); -end test_fixture; +-- Test suite for com codec package +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library vunit_lib; +context vunit_lib.vunit_context; +use vunit_lib.queue_pkg.all; +use vunit_lib.integer_vector_ptr_pkg.all; +use vunit_lib.codec_2008_pkg.all; + +library ieee; +use ieee.std_logic_1164.all; +use ieee.float_pkg.all; +use ieee.math_complex.all; +use ieee.numeric_bit.all; +use ieee.numeric_std.all; +use ieee.fixed_pkg.all; + +use std.textio.all; + +entity tb_codec_2008 is + generic ( + runner_cfg : string); +end entity tb_codec_2008; + +architecture test_fixture of tb_codec_2008 is +begin + test_runner : process + constant f64 : float64 := (others => '0'); + constant positive_zero : float64 := to_float( + std_logic_vector'(B"0_00000000000_0000000000000000000000000000000000000000000000000000"), f64); + constant negative_zero : float64 := to_float( + std_logic_vector'(B"1_00000000000_0000000000000000000000000000000000000000000000000000"), f64); + constant positive_infinity : float64 := to_float( + std_logic_vector'(B"0_11111111111_0000000000000000000000000000000000000000000000000000"), f64); + constant negative_infinity : float64 := to_float( + std_logic_vector'(B"1_11111111111_0000000000000000000000000000000000000000000000000000"), f64); + constant nan : float64 := to_float( + std_logic_vector'(B"1_11111111111_0000000000000000000000000000000000000000000000000001"), f64); + variable null_boolean_vector : boolean_vector(1 to 0); + variable null_integer_vector : integer_vector(1 to 0); + variable null_real_vector : real_vector(1 to 0); + variable null_time_vector : time_vector(1 to 0); + + variable boolean_vector_5_downto_3 : boolean_vector(5 downto 3); + variable integer_vector_5_downto_3 : integer_vector(5 downto 3); + variable real_vector_5_downto_3 : real_vector(5 downto 3); + variable time_vector_5_downto_3 : time_vector(5 downto 3); + + -- Temp variables to make test case pass Riviera-PRO 2016.10 + variable range_left, range_right : integer; + + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test that boolean_vector can be encoded and decoded") then + boolean_vector_5_downto_3 := (true, false, true); + check_relation(decode_boolean_vector(encode_boolean_vector((true, false, true))) = boolean_vector'(true, false, true)); + check_relation(decode_boolean_vector(encode_boolean_vector((0 => true))) = boolean_vector'(0 => true)); + check_relation(decode_boolean_vector(encode_boolean_vector(null_boolean_vector)) = null_boolean_vector); + check_relation(decode_boolean_vector(encode_boolean_vector(boolean_vector_5_downto_3)) = boolean_vector'(true, false, true)); + range_left := decode_boolean_vector(encode_boolean_vector(boolean_vector_5_downto_3))'left; + range_right := decode_boolean_vector(encode_boolean_vector(boolean_vector_5_downto_3))'right; + check_relation(range_left = 5); + check_relation(range_right = 3); + elsif run("Test that integer_vector can be encoded and decoded") then + integer_vector_5_downto_3 := (-42, 0, 17); + check_relation(decode_integer_vector(encode_integer_vector((-2147483648, -2147483648, -2147483648))) = integer_vector'(-2147483648, -2147483648, -2147483648)); + check_relation(decode_integer_vector(encode_integer_vector((-42, 0, 17))) = integer_vector'(-42, 0, 17)); + check_relation(decode_integer_vector(encode_integer_vector((0 => -42))) = integer_vector'(0 => -42)); + check_relation(decode_integer_vector(encode_integer_vector(null_integer_vector)) = null_integer_vector); + check_relation(decode_integer_vector(encode_integer_vector(integer_vector_5_downto_3)) = integer_vector'(-42, 0, 17)); + range_left := decode_integer_vector(encode_integer_vector(integer_vector_5_downto_3))'left; + range_right := decode_integer_vector(encode_integer_vector(integer_vector_5_downto_3))'right; + check_relation(range_left = 5); + check_relation(range_right = 3); + elsif run("Test that real_vector can be encoded and decoded") then + real_vector_5_downto_3 := (-42.42, 0.001, 17.17); + check_relation(decode_real_vector(encode_real_vector((-42.42, 0.001, 17.17))) = real_vector'(-42.42, 0.001, 17.17)); + check_relation(decode_real_vector(encode_real_vector((0 => -42.42))) = real_vector'(0 => -42.42)); + check_relation(decode_real_vector(encode_real_vector(null_real_vector)) = null_real_vector); + check_relation(decode_real_vector(encode_real_vector(real_vector_5_downto_3)) = real_vector'(-42.42, 0.001, 17.17)); + range_left := decode_real_vector(encode_real_vector(real_vector_5_downto_3))'left; + range_right := decode_real_vector(encode_real_vector(real_vector_5_downto_3))'right; + check_relation(range_left = 5); + check_relation(range_right = 3); + elsif run("Test that time_vector can be encoded and decoded") then + time_vector_5_downto_3 := (-42 ms, 0 sec, 17 min); + check_relation(decode_time_vector(encode_time_vector((-42 ms, 0 sec, 17 min))) = time_vector'(-42 ms, 0 sec, 17 min)); + check_relation(decode_time_vector(encode_time_vector((0 => -42 ms))) = time_vector'(0 => -42 ms)); + check_relation(decode_time_vector(encode_time_vector(null_time_vector)) = null_time_vector); + check_relation(decode_time_vector(encode_time_vector(time_vector_5_downto_3)) = time_vector'(-42 ms, 0 sec, 17 min)); + range_left := decode_time_vector(encode_time_vector(time_vector_5_downto_3))'left; + range_right := decode_time_vector(encode_time_vector(time_vector_5_downto_3))'right; + check_relation(range_left = 5); + check_relation(range_right = 3); + elsif run("Test that ufixed can be encoded and decoded") then + check_relation(decode_ufixed(encode_ufixed(to_ufixed(6.5, 3, -3))) = to_ufixed(6.5, 3, -3)); + check_relation(decode_ufixed(encode_ufixed(to_ufixed(8.0, 3, 1))) = to_ufixed(8.0, 3, 1)); + check_relation(decode_ufixed(encode_ufixed(to_ufixed(0.25, -2, -4))) = to_ufixed(0.25, -2, -4)); + elsif run("Test that sfixed can be encoded and decoded") then + check_relation(decode_sfixed(encode_sfixed(to_sfixed(6.5, 3, -3))) = to_sfixed(6.5, 3, -3)); + check_relation(decode_sfixed(encode_sfixed(to_sfixed(8.0, 4, 1))) = to_sfixed(8.0, 4, 1)); + check_relation(decode_sfixed(encode_sfixed(to_sfixed(0.25, -1, -4))) = to_sfixed(0.25, -1, -4)); + check_relation(decode_sfixed(encode_sfixed(to_sfixed(-6.5, 3, -3))) = to_sfixed(-6.5, 3, -3)); + check_relation(decode_sfixed(encode_sfixed(to_sfixed(-8.0, 4, 1))) = to_sfixed(-8.0, 4, 1)); + check_relation(decode_sfixed(encode_sfixed(to_sfixed(-0.25, -1, -4))) = to_sfixed(-0.25, -1, -4)); + elsif run("Test that float can be encoded and decoded") then + check_relation(decode_float(encode_float(to_float(real'low, 11, 52))) = to_float(real'low, 11, 52)); + check_relation(decode_float(encode_float(to_float(real'high, 11, 52))) = to_float(real'high, 11, 52)); + + check_relation(to_string(decode_float(encode_float(positive_zero))) = to_string(positive_zero)); + check_relation(to_string(decode_float(encode_float(negative_zero))) = to_string(negative_zero)); + check_relation(to_string(decode_float(encode_float(positive_infinity))) = to_string(positive_infinity)); + check_relation(to_string(decode_float(encode_float(negative_infinity))) = to_string(negative_infinity)); + check_relation(to_string(decode_float(encode_float(nan))) = to_string(nan)); + check_relation(to_string(decode_float(encode_float(nan))) /= to_string(positive_zero)); + check_relation(to_string(decode_float(encode_float(negative_zero))) /= to_string(positive_zero)); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 100 ms); +end test_fixture; diff --git a/vunit/vhdl/data_types/test/tb_codec.vhd b/vunit/vhdl/data_types/test/tb_codec.vhd index 9f6ac0da7..ce372c4ab 100644 --- a/vunit/vhdl/data_types/test/tb_codec.vhd +++ b/vunit/vhdl/data_types/test/tb_codec.vhd @@ -1,258 +1,258 @@ --- Test suite for com codec package --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library vunit_lib; -use vunit_lib.string_ops.all; -use vunit_lib.check_pkg.all; -use vunit_lib.run_pkg.all; -use vunit_lib.path.all; -use vunit_lib.queue_pkg.all; -use vunit_lib.integer_vector_ptr_pkg.all; -use vunit_lib.codec_pkg.all; - -library ieee; -use ieee.std_logic_1164.all; -use ieee.math_complex.all; -use ieee.math_real.all; -use ieee.numeric_bit.all; -use ieee.numeric_std.all; - -use std.textio.all; - -entity tb_codec is - generic ( - runner_cfg : string); -end entity tb_codec; - -architecture test_fixture of tb_codec is -begin - test_runner : process - -- Standard "=" for these types return false when both operands are empty - -- vectors. However, I want decode(encode("")) = "" to return true when verifying that - -- empty vectors can be encoded/decoded correctly - function "=" ( - constant l, r : ieee.numeric_bit.unsigned) - return boolean is - begin - if l'length = 0 and r'length = 0 then - return true; - end if; - - return ieee.numeric_bit."="(l, r); - end function "="; - - function "=" ( - constant l, r : ieee.numeric_bit.signed) - return boolean is - begin - if l'length = 0 and r'length = 0 then - return true; - end if; - - return ieee.numeric_bit."="(l, r); - end function "="; - - function "=" ( - constant l, r : ieee.numeric_std.unsigned) - return boolean is - begin - if l'length = 0 and r'length = 0 then - return true; - end if; - - return ieee.numeric_std."="(l, r); - end function "="; - - function "=" ( - constant l, r : ieee.numeric_std.signed) - return boolean is - begin - if l'length = 0 and r'length = 0 then - return true; - end if; - - return ieee.numeric_std."="(l, r); - end function "="; - - variable r1, r2 : real; - constant positive_zero : real := 0.0; - constant negative_zero : real := -1.0/1.0e45; - constant positive_infinity : real := 1.0e39; - constant negative_infinity : real := -1.0e39; - - constant special_chars : string(1 to 3) := "),("; - constant comma : character := ','; - constant lp : character := '('; - constant rp : character := ')'; - variable null_string : string(10 to 9); - variable t1 : time; - variable string_15_downto_4 : string(15 downto 4); - variable bit_vector_5_downto_3 : bit_vector(5 downto 3); - variable std_ulogic_vector_5_downto_3 : std_ulogic_vector(5 downto 3); - variable numeric_bit_unsigned_5_downto_3 : ieee.numeric_bit.unsigned(5 downto 3); - variable numeric_bit_signed_5_downto_3 : ieee.numeric_bit.signed(5 downto 3); - variable numeric_std_unsigned_5_downto_3 : ieee.numeric_std.unsigned(5 downto 3); - variable numeric_std_signed_5_downto_3 : ieee.numeric_std.signed(5 downto 3); - - -- Temp variables to make test case pass Riviera-PRO 2016.10 - variable range_left, range_right : integer; - - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test that integer can be encoded and decoded") then - check_relation(decode_integer(encode_integer(integer'low)) = integer'low); - check_relation(decode_integer(encode_integer(integer'high)) = integer'high); - elsif run("Test that real can be encoded and decoded") then - check_relation(decode_real(encode_real(real'low)) = real'low); - check_relation(decode_real(encode_real(real'high)) = real'high); - - check_relation(decode_real(encode_real(positive_zero)) = positive_zero); - check_relation(decode_real(encode_real(negative_zero)) = negative_zero); - check_relation(decode_real(encode_real(positive_infinity)) = positive_infinity); - check_relation(decode_real(encode_real(negative_infinity)) = negative_infinity); --- check_relation(decode_real(encode_real(negative_zero)) /= positive_zero); - - -- ModelSim doesn't support float meta values for the real type. Positive/negative zero as - -- well as NaN get the same internal representation (positive - -- zero). Positive/negative infinity seem to maintain correct internal - -- representation but doing things like 1.0/0.0 isn't supported. The tests - -- for encoding/decoding of these values still pass so I've kept them for tests - -- with other simulators --- check_relation(decode_real(encode_real(to_real(nan))) /= to_real(positive_zero)); --- check_relation(decode_real(encode_real(to_real(negative_zero))) /= to_real(positive_zero)); - - -- r1 := to_real(to_float( - -- std_logic_vector'(B"0_01111111111_0000000000000000000000000000000000000000000000000001"), f64)); - -- r2 := to_real(to_float( - -- std_logic_vector'(B"0_01111111111_0000000000000000000000000000000000000000000000000010"), f64)); - -- check_relation(decode_real(encode_real(r1)) = r1); - -- check_relation(decode_real(encode_real(r2)) = r2); - -- check_relation(r1 /= r2, "Should be different values in a double precision implementation"); - elsif run("Test that time can be encoded and decoded") then - t1 := time'low; - check_relation(decode_time(encode_time(t1)) = t1); - t1 := time'high; - check_relation(decode_time(encode_time(t1)) = t1); - check_relation(decode_time(encode_time(17 ns)) = 17 ns); - check_relation(decode_time(encode_time(-17 ns)) = -17 ns); - elsif run("Test that boolean can be encoded and decoded") then - check_relation(decode_boolean(encode_boolean(true)) = true); - check_relation(decode_boolean(encode_boolean(false)) = false); - elsif run("Test that bit can be encoded and decoded") then - check_relation(decode_bit(encode_bit('0')) = bit'('0')); - check_relation(decode_bit(encode_bit('1')) = bit'('1')); - elsif run("Test that std_ulogic can be encoded and decoded") then - for i in std_ulogic'pos(std_ulogic'left) to std_ulogic'pos(std_ulogic'right) loop - check_relation(decode_std_ulogic(encode_std_ulogic(std_ulogic'val(i))) = std_ulogic'val(i)); - end loop; - elsif run("Test that severity_level can be encoded and decoded") then - for i in severity_level'pos(severity_level'left) to severity_level'pos(severity_level'right) loop - check_relation(decode_severity_level(encode_severity_level(severity_level'val(i))) = severity_level'val(i)); - end loop; - elsif run("Test that file_open_status can be encoded and decoded") then - for i in file_open_status'pos(file_open_status'left) to file_open_status'pos(file_open_status'right) loop - check_relation(decode_file_open_status(encode_file_open_status(file_open_status'val(i))) = file_open_status'val(i)); - end loop; - elsif run("Test that file_open_kind can be encoded and decoded") then - for i in file_open_kind'pos(file_open_kind'left) to file_open_kind'pos(file_open_kind'right) loop - check_relation(decode_file_open_kind(encode_file_open_kind(file_open_kind'val(i))) = file_open_kind'val(i)); - end loop; - elsif run("Test that character can be encoded and decoded") then - for i in character'pos(character'left) to character'pos(character'right) loop - check_relation(decode_character(encode_character(character'val(i))) = character'val(i)); - end loop; - elsif run("Test that string can be encoded and decoded") then - string_15_downto_4 := "Hello world!"; - check_relation(decode_string(encode_string("The quick brown fox jumps over the lazy dog")) = string'("The quick brown fox jumps over the lazy dog")); - check_relation(decode_string(encode_string(special_chars)) = string'(special_chars)); - range_left := decode_string(encode_string(null_string))'left; - range_right := decode_string(encode_string(null_string))'right; - check_relation(range_left = 10); - check_relation(range_right = 9); - check_relation(decode_string(encode_string(string_15_downto_4)) = string'("Hello world!")); - range_left := decode_string(encode_string(string_15_downto_4))'left; - range_right := decode_string(encode_string(string_15_downto_4))'right; - check_relation(range_left = 15); - check_relation(range_right = 4); - elsif run("Test that bit_vector can be encoded and decoded") then - bit_vector_5_downto_3 := "101"; - check_relation(decode_bit_vector(encode_bit_vector("101")) = bit_vector'("101")); - check_relation(decode_bit_vector(encode_bit_vector("1")) = bit_vector'("1")); - check_relation(decode_bit_vector(encode_bit_vector("")) = bit_vector'("")); - check_relation(decode_bit_vector(encode_bit_vector(bit_vector_5_downto_3)) = bit_vector'("101")); - range_left := decode_bit_vector(encode_bit_vector(bit_vector_5_downto_3))'left; - range_right := decode_bit_vector(encode_bit_vector(bit_vector_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); - elsif run("Test that std_ulogic_vector can be encoded and decoded") then - std_ulogic_vector_5_downto_3 := "XU1"; - check_relation(decode_std_ulogic_vector(encode_std_ulogic_vector("XU1")) = std_ulogic_vector'("XU1")); - check_relation(decode_std_ulogic_vector(encode_std_ulogic_vector("X")) = std_ulogic_vector'("X")); - check_relation(decode_std_ulogic_vector(encode_std_ulogic_vector("")) = std_ulogic_vector'("")); - check_relation(decode_std_ulogic_vector(encode_std_ulogic_vector(std_ulogic_vector_5_downto_3)) = std_ulogic_vector'("XU1")); - range_left := decode_std_ulogic_vector(encode_std_ulogic_vector(std_ulogic_vector_5_downto_3))'left; - range_right := decode_std_ulogic_vector(encode_std_ulogic_vector(std_ulogic_vector_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); - elsif run("Test that complex can be encoded and decoded") then - check_relation(decode_complex(encode_complex((-17.17, 42.42))) = complex'(-17.17, 42.42)); - elsif run("Test that complex_polar can be encoded and decoded") then - check_relation(decode_complex_polar(encode_complex_polar((17.17, 0.42))) = complex_polar'(17.17, 0.42)); - elsif run("Test that unsigned from numeric_bit can be encoded and decoded") then - numeric_bit_unsigned_5_downto_3 := "101"; - check_relation(decode_numeric_bit_unsigned(encode_numeric_bit_unsigned("101")) = ieee.numeric_bit.unsigned'("101")); - check_relation(decode_numeric_bit_unsigned(encode_numeric_bit_unsigned("1")) = ieee.numeric_bit.unsigned'("1")); - check_relation(decode_numeric_bit_unsigned(encode_numeric_bit_unsigned("")) = ieee.numeric_bit.unsigned'("")); - check_relation(decode_numeric_bit_unsigned(encode_numeric_bit_unsigned(numeric_bit_unsigned_5_downto_3)) = ieee.numeric_bit.unsigned'("101")); - range_left := decode_numeric_bit_unsigned(encode_numeric_bit_unsigned(numeric_bit_unsigned_5_downto_3))'left; - range_right := decode_numeric_bit_unsigned(encode_numeric_bit_unsigned(numeric_bit_unsigned_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); - elsif run("Test that signed from numeric_bit can be encoded and decoded") then - numeric_bit_signed_5_downto_3 := "101"; - check_relation(decode_numeric_bit_signed(encode_numeric_bit_signed("101")) = ieee.numeric_bit.signed'("101")); - check_relation(decode_numeric_bit_signed(encode_numeric_bit_signed("1")) = ieee.numeric_bit.signed'("1")); - check_relation(decode_numeric_bit_signed(encode_numeric_bit_signed("")) = ieee.numeric_bit.signed'("")); - check_relation(decode_numeric_bit_signed(encode_numeric_bit_signed(numeric_bit_signed_5_downto_3)) = ieee.numeric_bit.signed'("101")); - range_left := decode_numeric_bit_signed(encode_numeric_bit_signed(numeric_bit_signed_5_downto_3))'left; - range_right := decode_numeric_bit_signed(encode_numeric_bit_signed(numeric_bit_signed_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); - elsif run("Test that unsigned from numeric_std can be encoded and decoded") then - numeric_std_unsigned_5_downto_3 := "101"; - check_relation(decode_numeric_std_unsigned(encode_numeric_std_unsigned("101")) = ieee.numeric_std.unsigned'("101")); - check_relation(decode_numeric_std_unsigned(encode_numeric_std_unsigned("1")) = ieee.numeric_std.unsigned'("1")); - check_relation(decode_numeric_std_unsigned(encode_numeric_std_unsigned("")) = ieee.numeric_std.unsigned'("")); - check_relation(decode_numeric_std_unsigned(encode_numeric_std_unsigned(numeric_std_unsigned_5_downto_3)) = ieee.numeric_std.unsigned'("101")); - range_left := decode_numeric_std_unsigned(encode_numeric_std_unsigned(numeric_std_unsigned_5_downto_3))'left; - range_right := decode_numeric_std_unsigned(encode_numeric_std_unsigned(numeric_std_unsigned_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); - elsif run("Test that signed from numeric_std can be encoded and decoded") then - numeric_std_signed_5_downto_3 := "101"; - check_relation(decode_numeric_std_signed(encode_numeric_std_signed("101")) = ieee.numeric_std.signed'("101")); - check_relation(decode_numeric_std_signed(encode_numeric_std_signed("1")) = ieee.numeric_std.signed'("1")); - check_relation(decode_numeric_std_signed(encode_numeric_std_signed("")) = ieee.numeric_std.signed'("")); - check_relation(decode_numeric_std_signed(encode_numeric_std_signed(numeric_std_signed_5_downto_3)) = ieee.numeric_std.signed'("101")); - range_left := decode_numeric_std_signed(encode_numeric_std_signed(numeric_std_signed_5_downto_3))'left; - range_right := decode_numeric_std_signed(encode_numeric_std_signed(numeric_std_signed_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 100 ms); -end test_fixture; +-- Test suite for com codec package +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library vunit_lib; +use vunit_lib.string_ops.all; +use vunit_lib.check_pkg.all; +use vunit_lib.run_pkg.all; +use vunit_lib.path.all; +use vunit_lib.queue_pkg.all; +use vunit_lib.integer_vector_ptr_pkg.all; +use vunit_lib.codec_pkg.all; + +library ieee; +use ieee.std_logic_1164.all; +use ieee.math_complex.all; +use ieee.math_real.all; +use ieee.numeric_bit.all; +use ieee.numeric_std.all; + +use std.textio.all; + +entity tb_codec is + generic ( + runner_cfg : string); +end entity tb_codec; + +architecture test_fixture of tb_codec is +begin + test_runner : process + -- Standard "=" for these types return false when both operands are empty + -- vectors. However, I want decode(encode("")) = "" to return true when verifying that + -- empty vectors can be encoded/decoded correctly + function "=" ( + constant l, r : ieee.numeric_bit.unsigned) + return boolean is + begin + if l'length = 0 and r'length = 0 then + return true; + end if; + + return ieee.numeric_bit."="(l, r); + end function "="; + + function "=" ( + constant l, r : ieee.numeric_bit.signed) + return boolean is + begin + if l'length = 0 and r'length = 0 then + return true; + end if; + + return ieee.numeric_bit."="(l, r); + end function "="; + + function "=" ( + constant l, r : ieee.numeric_std.unsigned) + return boolean is + begin + if l'length = 0 and r'length = 0 then + return true; + end if; + + return ieee.numeric_std."="(l, r); + end function "="; + + function "=" ( + constant l, r : ieee.numeric_std.signed) + return boolean is + begin + if l'length = 0 and r'length = 0 then + return true; + end if; + + return ieee.numeric_std."="(l, r); + end function "="; + + variable r1, r2 : real; + constant positive_zero : real := 0.0; + constant negative_zero : real := -1.0/1.0e45; + constant positive_infinity : real := 1.0e39; + constant negative_infinity : real := -1.0e39; + + constant special_chars : string(1 to 3) := "),("; + constant comma : character := ','; + constant lp : character := '('; + constant rp : character := ')'; + variable null_string : string(10 to 9); + variable t1 : time; + variable string_15_downto_4 : string(15 downto 4); + variable bit_vector_5_downto_3 : bit_vector(5 downto 3); + variable std_ulogic_vector_5_downto_3 : std_ulogic_vector(5 downto 3); + variable numeric_bit_unsigned_5_downto_3 : ieee.numeric_bit.unsigned(5 downto 3); + variable numeric_bit_signed_5_downto_3 : ieee.numeric_bit.signed(5 downto 3); + variable numeric_std_unsigned_5_downto_3 : ieee.numeric_std.unsigned(5 downto 3); + variable numeric_std_signed_5_downto_3 : ieee.numeric_std.signed(5 downto 3); + + -- Temp variables to make test case pass Riviera-PRO 2016.10 + variable range_left, range_right : integer; + + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test that integer can be encoded and decoded") then + check_relation(decode_integer(encode_integer(integer'low)) = integer'low); + check_relation(decode_integer(encode_integer(integer'high)) = integer'high); + elsif run("Test that real can be encoded and decoded") then + check_relation(decode_real(encode_real(real'low)) = real'low); + check_relation(decode_real(encode_real(real'high)) = real'high); + + check_relation(decode_real(encode_real(positive_zero)) = positive_zero); + check_relation(decode_real(encode_real(negative_zero)) = negative_zero); + check_relation(decode_real(encode_real(positive_infinity)) = positive_infinity); + check_relation(decode_real(encode_real(negative_infinity)) = negative_infinity); +-- check_relation(decode_real(encode_real(negative_zero)) /= positive_zero); + + -- ModelSim doesn't support float meta values for the real type. Positive/negative zero as + -- well as NaN get the same internal representation (positive + -- zero). Positive/negative infinity seem to maintain correct internal + -- representation but doing things like 1.0/0.0 isn't supported. The tests + -- for encoding/decoding of these values still pass so I've kept them for tests + -- with other simulators +-- check_relation(decode_real(encode_real(to_real(nan))) /= to_real(positive_zero)); +-- check_relation(decode_real(encode_real(to_real(negative_zero))) /= to_real(positive_zero)); + + -- r1 := to_real(to_float( + -- std_logic_vector'(B"0_01111111111_0000000000000000000000000000000000000000000000000001"), f64)); + -- r2 := to_real(to_float( + -- std_logic_vector'(B"0_01111111111_0000000000000000000000000000000000000000000000000010"), f64)); + -- check_relation(decode_real(encode_real(r1)) = r1); + -- check_relation(decode_real(encode_real(r2)) = r2); + -- check_relation(r1 /= r2, "Should be different values in a double precision implementation"); + elsif run("Test that time can be encoded and decoded") then + t1 := time'low; + check_relation(decode_time(encode_time(t1)) = t1); + t1 := time'high; + check_relation(decode_time(encode_time(t1)) = t1); + check_relation(decode_time(encode_time(17 ns)) = 17 ns); + check_relation(decode_time(encode_time(-17 ns)) = -17 ns); + elsif run("Test that boolean can be encoded and decoded") then + check_relation(decode_boolean(encode_boolean(true)) = true); + check_relation(decode_boolean(encode_boolean(false)) = false); + elsif run("Test that bit can be encoded and decoded") then + check_relation(decode_bit(encode_bit('0')) = bit'('0')); + check_relation(decode_bit(encode_bit('1')) = bit'('1')); + elsif run("Test that std_ulogic can be encoded and decoded") then + for i in std_ulogic'pos(std_ulogic'left) to std_ulogic'pos(std_ulogic'right) loop + check_relation(decode_std_ulogic(encode_std_ulogic(std_ulogic'val(i))) = std_ulogic'val(i)); + end loop; + elsif run("Test that severity_level can be encoded and decoded") then + for i in severity_level'pos(severity_level'left) to severity_level'pos(severity_level'right) loop + check_relation(decode_severity_level(encode_severity_level(severity_level'val(i))) = severity_level'val(i)); + end loop; + elsif run("Test that file_open_status can be encoded and decoded") then + for i in file_open_status'pos(file_open_status'left) to file_open_status'pos(file_open_status'right) loop + check_relation(decode_file_open_status(encode_file_open_status(file_open_status'val(i))) = file_open_status'val(i)); + end loop; + elsif run("Test that file_open_kind can be encoded and decoded") then + for i in file_open_kind'pos(file_open_kind'left) to file_open_kind'pos(file_open_kind'right) loop + check_relation(decode_file_open_kind(encode_file_open_kind(file_open_kind'val(i))) = file_open_kind'val(i)); + end loop; + elsif run("Test that character can be encoded and decoded") then + for i in character'pos(character'left) to character'pos(character'right) loop + check_relation(decode_character(encode_character(character'val(i))) = character'val(i)); + end loop; + elsif run("Test that string can be encoded and decoded") then + string_15_downto_4 := "Hello world!"; + check_relation(decode_string(encode_string("The quick brown fox jumps over the lazy dog")) = string'("The quick brown fox jumps over the lazy dog")); + check_relation(decode_string(encode_string(special_chars)) = string'(special_chars)); + range_left := decode_string(encode_string(null_string))'left; + range_right := decode_string(encode_string(null_string))'right; + check_relation(range_left = 10); + check_relation(range_right = 9); + check_relation(decode_string(encode_string(string_15_downto_4)) = string'("Hello world!")); + range_left := decode_string(encode_string(string_15_downto_4))'left; + range_right := decode_string(encode_string(string_15_downto_4))'right; + check_relation(range_left = 15); + check_relation(range_right = 4); + elsif run("Test that bit_vector can be encoded and decoded") then + bit_vector_5_downto_3 := "101"; + check_relation(decode_bit_vector(encode_bit_vector("101")) = bit_vector'("101")); + check_relation(decode_bit_vector(encode_bit_vector("1")) = bit_vector'("1")); + check_relation(decode_bit_vector(encode_bit_vector("")) = bit_vector'("")); + check_relation(decode_bit_vector(encode_bit_vector(bit_vector_5_downto_3)) = bit_vector'("101")); + range_left := decode_bit_vector(encode_bit_vector(bit_vector_5_downto_3))'left; + range_right := decode_bit_vector(encode_bit_vector(bit_vector_5_downto_3))'right; + check_relation(range_left = 5); + check_relation(range_right = 3); + elsif run("Test that std_ulogic_vector can be encoded and decoded") then + std_ulogic_vector_5_downto_3 := "XU1"; + check_relation(decode_std_ulogic_vector(encode_std_ulogic_vector("XU1")) = std_ulogic_vector'("XU1")); + check_relation(decode_std_ulogic_vector(encode_std_ulogic_vector("X")) = std_ulogic_vector'("X")); + check_relation(decode_std_ulogic_vector(encode_std_ulogic_vector("")) = std_ulogic_vector'("")); + check_relation(decode_std_ulogic_vector(encode_std_ulogic_vector(std_ulogic_vector_5_downto_3)) = std_ulogic_vector'("XU1")); + range_left := decode_std_ulogic_vector(encode_std_ulogic_vector(std_ulogic_vector_5_downto_3))'left; + range_right := decode_std_ulogic_vector(encode_std_ulogic_vector(std_ulogic_vector_5_downto_3))'right; + check_relation(range_left = 5); + check_relation(range_right = 3); + elsif run("Test that complex can be encoded and decoded") then + check_relation(decode_complex(encode_complex((-17.17, 42.42))) = complex'(-17.17, 42.42)); + elsif run("Test that complex_polar can be encoded and decoded") then + check_relation(decode_complex_polar(encode_complex_polar((17.17, 0.42))) = complex_polar'(17.17, 0.42)); + elsif run("Test that unsigned from numeric_bit can be encoded and decoded") then + numeric_bit_unsigned_5_downto_3 := "101"; + check_relation(decode_numeric_bit_unsigned(encode_numeric_bit_unsigned("101")) = ieee.numeric_bit.unsigned'("101")); + check_relation(decode_numeric_bit_unsigned(encode_numeric_bit_unsigned("1")) = ieee.numeric_bit.unsigned'("1")); + check_relation(decode_numeric_bit_unsigned(encode_numeric_bit_unsigned("")) = ieee.numeric_bit.unsigned'("")); + check_relation(decode_numeric_bit_unsigned(encode_numeric_bit_unsigned(numeric_bit_unsigned_5_downto_3)) = ieee.numeric_bit.unsigned'("101")); + range_left := decode_numeric_bit_unsigned(encode_numeric_bit_unsigned(numeric_bit_unsigned_5_downto_3))'left; + range_right := decode_numeric_bit_unsigned(encode_numeric_bit_unsigned(numeric_bit_unsigned_5_downto_3))'right; + check_relation(range_left = 5); + check_relation(range_right = 3); + elsif run("Test that signed from numeric_bit can be encoded and decoded") then + numeric_bit_signed_5_downto_3 := "101"; + check_relation(decode_numeric_bit_signed(encode_numeric_bit_signed("101")) = ieee.numeric_bit.signed'("101")); + check_relation(decode_numeric_bit_signed(encode_numeric_bit_signed("1")) = ieee.numeric_bit.signed'("1")); + check_relation(decode_numeric_bit_signed(encode_numeric_bit_signed("")) = ieee.numeric_bit.signed'("")); + check_relation(decode_numeric_bit_signed(encode_numeric_bit_signed(numeric_bit_signed_5_downto_3)) = ieee.numeric_bit.signed'("101")); + range_left := decode_numeric_bit_signed(encode_numeric_bit_signed(numeric_bit_signed_5_downto_3))'left; + range_right := decode_numeric_bit_signed(encode_numeric_bit_signed(numeric_bit_signed_5_downto_3))'right; + check_relation(range_left = 5); + check_relation(range_right = 3); + elsif run("Test that unsigned from numeric_std can be encoded and decoded") then + numeric_std_unsigned_5_downto_3 := "101"; + check_relation(decode_numeric_std_unsigned(encode_numeric_std_unsigned("101")) = ieee.numeric_std.unsigned'("101")); + check_relation(decode_numeric_std_unsigned(encode_numeric_std_unsigned("1")) = ieee.numeric_std.unsigned'("1")); + check_relation(decode_numeric_std_unsigned(encode_numeric_std_unsigned("")) = ieee.numeric_std.unsigned'("")); + check_relation(decode_numeric_std_unsigned(encode_numeric_std_unsigned(numeric_std_unsigned_5_downto_3)) = ieee.numeric_std.unsigned'("101")); + range_left := decode_numeric_std_unsigned(encode_numeric_std_unsigned(numeric_std_unsigned_5_downto_3))'left; + range_right := decode_numeric_std_unsigned(encode_numeric_std_unsigned(numeric_std_unsigned_5_downto_3))'right; + check_relation(range_left = 5); + check_relation(range_right = 3); + elsif run("Test that signed from numeric_std can be encoded and decoded") then + numeric_std_signed_5_downto_3 := "101"; + check_relation(decode_numeric_std_signed(encode_numeric_std_signed("101")) = ieee.numeric_std.signed'("101")); + check_relation(decode_numeric_std_signed(encode_numeric_std_signed("1")) = ieee.numeric_std.signed'("1")); + check_relation(decode_numeric_std_signed(encode_numeric_std_signed("")) = ieee.numeric_std.signed'("")); + check_relation(decode_numeric_std_signed(encode_numeric_std_signed(numeric_std_signed_5_downto_3)) = ieee.numeric_std.signed'("101")); + range_left := decode_numeric_std_signed(encode_numeric_std_signed(numeric_std_signed_5_downto_3))'left; + range_right := decode_numeric_std_signed(encode_numeric_std_signed(numeric_std_signed_5_downto_3))'right; + check_relation(range_left = 5); + check_relation(range_right = 3); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 100 ms); +end test_fixture; diff --git a/vunit/vhdl/data_types/test/tb_dict.vhd b/vunit/vhdl/data_types/test/tb_dict.vhd index f4f154815..3630c658c 100644 --- a/vunit/vhdl/data_types/test/tb_dict.vhd +++ b/vunit/vhdl/data_types/test/tb_dict.vhd @@ -1,104 +1,104 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -use vunit_lib.check_pkg.all; -use vunit_lib.run_pkg.all; - -use work.dict_pkg.all; - -entity tb_dict is - generic ( - runner_cfg : string); -end entity; - -architecture a of tb_dict is -begin - - main : process - variable dict : dict_t; - constant many_keys : natural := 2**16; - constant long_key : string := "long--------------------------------------------------------key"; - begin - test_runner_setup(runner, runner_cfg); - - if run("test default is null") then - assert dict = null_dict report "expected null dict"; - - elsif run("test new dict") then - dict := new_dict; - assert dict /= null_dict report "expected non-null dict"; - - elsif run("test deallocate dict") then - dict := new_dict; - set(dict, "key", "value"); - deallocate(dict); - assert dict = null_dict report "expected null dict"; - - elsif run("test has key") then - dict := new_dict; - check_false(has_key(dict, "missing")); - set(dict, "key", "value"); - check(has_key(dict, "key")); - - elsif run("test remove key") then - dict := new_dict; - - set(dict, "key", "value"); - check(has_key(dict, "key")); - check_equal(num_keys(dict), 1); - - remove(dict, "key"); - check_false(has_key(dict, "key")); - check_equal(num_keys(dict), 0); - - remove(dict, "key"); - - elsif run("test set and get") then - dict := new_dict; - set(dict, "key", "value"); - check_equal(get(dict, "key"), "value"); - - set(dict, "key2", "value2"); - check_equal(get(dict, "key2"), "value2"); - - elsif run("test overwrite key") then - dict := new_dict; - set(dict, "key", "value"); - check_equal(get(dict, "key"), "value"); - - set(dict, "key", "value2"); - check_equal(get(dict, "key"), "value2"); - - elsif run("test set and get many keys") then - dict := new_dict; - - for k in 1 to 2 loop - - report integer'image(k); - - for i in 1 to many_keys loop - set(dict, long_key & integer'image(i), integer'image(i)); - check_equal(get(dict, long_key & integer'image(i)), integer'image(i)); - check_equal(num_keys(dict), i); - end loop; - - for i in 1 to many_keys loop - check_equal(get(dict, long_key & integer'image(i)), integer'image(i)); - end loop; - - for i in many_keys downto 1 loop - remove(dict, long_key & integer'image(i)); - check_equal(num_keys(dict), i-1); - end loop; - end loop; - - deallocate(dict); - end if; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +use vunit_lib.check_pkg.all; +use vunit_lib.run_pkg.all; + +use work.dict_pkg.all; + +entity tb_dict is + generic ( + runner_cfg : string); +end entity; + +architecture a of tb_dict is +begin + + main : process + variable dict : dict_t; + constant many_keys : natural := 2**16; + constant long_key : string := "long--------------------------------------------------------key"; + begin + test_runner_setup(runner, runner_cfg); + + if run("test default is null") then + assert dict = null_dict report "expected null dict"; + + elsif run("test new dict") then + dict := new_dict; + assert dict /= null_dict report "expected non-null dict"; + + elsif run("test deallocate dict") then + dict := new_dict; + set(dict, "key", "value"); + deallocate(dict); + assert dict = null_dict report "expected null dict"; + + elsif run("test has key") then + dict := new_dict; + check_false(has_key(dict, "missing")); + set(dict, "key", "value"); + check(has_key(dict, "key")); + + elsif run("test remove key") then + dict := new_dict; + + set(dict, "key", "value"); + check(has_key(dict, "key")); + check_equal(num_keys(dict), 1); + + remove(dict, "key"); + check_false(has_key(dict, "key")); + check_equal(num_keys(dict), 0); + + remove(dict, "key"); + + elsif run("test set and get") then + dict := new_dict; + set(dict, "key", "value"); + check_equal(get(dict, "key"), "value"); + + set(dict, "key2", "value2"); + check_equal(get(dict, "key2"), "value2"); + + elsif run("test overwrite key") then + dict := new_dict; + set(dict, "key", "value"); + check_equal(get(dict, "key"), "value"); + + set(dict, "key", "value2"); + check_equal(get(dict, "key"), "value2"); + + elsif run("test set and get many keys") then + dict := new_dict; + + for k in 1 to 2 loop + + report integer'image(k); + + for i in 1 to many_keys loop + set(dict, long_key & integer'image(i), integer'image(i)); + check_equal(get(dict, long_key & integer'image(i)), integer'image(i)); + check_equal(num_keys(dict), i); + end loop; + + for i in 1 to many_keys loop + check_equal(get(dict, long_key & integer'image(i)), integer'image(i)); + end loop; + + for i in many_keys downto 1 loop + remove(dict, long_key & integer'image(i)); + check_equal(num_keys(dict), i-1); + end loop; + end loop; + + deallocate(dict); + end if; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/vhdl/data_types/test/tb_integer_array.vhd b/vunit/vhdl/data_types/test/tb_integer_array.vhd index f57933a0d..7642cb36e 100644 --- a/vunit/vhdl/data_types/test/tb_integer_array.vhd +++ b/vunit/vhdl/data_types/test/tb_integer_array.vhd @@ -1,366 +1,366 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- @TODO add explicit check of csv string data - --- vunit: fail_on_warning --- vunit: run_all_in_same_sim - -use std.textio.all; - -library vunit_lib; ---context vunit_lib.vunit_context; -use work.integer_array_pkg.all; -use vunit_lib.check_pkg.all; -use vunit_lib.run_pkg.all; - -entity tb_integer_array is - generic ( - output_path : string; - runner_cfg : string); -end entity; - -architecture a of tb_integer_array is -begin - - main : process - variable arr : integer_array_t := null_integer_array; - variable other_arr : integer_array_t := null_integer_array; - - impure function num_bytes(file_name : string) return integer is - type binary_file_t is file of character; - file fread : binary_file_t; - variable num_bytes : integer := 0; - variable chr : character; - begin - file_open(fread, file_name, read_mode); - while not endfile(fread) loop - num_bytes := num_bytes + 1; - read(fread, chr); - end loop; - file_close(fread); - return num_bytes; - end function; - - procedure test_save_and_load_raw(bit_width : integer; - is_signed : boolean) is - variable arr : integer_array_t; - variable other_arr : integer_array_t; - - impure function file_name return string is - begin - if is_signed then - return output_path & "s" & integer'image(bit_width) & ".raw"; - else - return output_path & "u" & integer'image(bit_width) & ".raw"; - end if; - end function; - - constant bytes_per_word : integer := (bit_width+7)/8; - - begin - arr := new_1d(bit_width => bit_width, is_signed => is_signed); - append(arr, arr.lower_limit); - append(arr, 0); - append(arr, arr.upper_limit); - save_raw(arr, file_name); - other_arr := load_raw(file_name, - bit_width => bit_width, is_signed => is_signed); - check_equal(get(other_arr, 0), get(arr, 0)); - check_equal(get(other_arr, 1), get(arr, 1)); - check_equal(get(other_arr, 2), get(arr, 2)); - check_equal(num_bytes(file_name), bytes_per_word * arr.length); - end procedure; - begin - test_runner_setup(runner, runner_cfg); - while test_suite loop - deallocate(arr); - deallocate(other_arr); - - if run("Has length") then - arr := new_1d; - check_equal(arr.length, 0); - - elsif run("Has bit_width") then - arr := new_1d; - check_equal(arr.bit_width, 32); - - elsif run("Has is_signed") then - arr := new_1d; - check_equal(arr.is_signed, true); - - elsif run("Has new_1d") then - arr := new_1d(length => 10, bit_width => 16, is_signed => false); - check_equal(arr.length, 10); - check_equal(arr.bit_width, 16); - check_equal(arr.is_signed, false); - - elsif run("Has new_2d") then - arr := new_2d(width => 7, height => 13, bit_width => 28, is_signed => true); - check_equal(arr.width, 7); - check_equal(arr.height, 13); - check_equal(arr.length, 7*13); - check_equal(arr.bit_width, 28); - check_equal(arr.is_signed, true); - - elsif run("Has new_3d") then - arr := new_3d(width => 7, height => 13, depth => 5, - bit_width => 28, is_signed => true); - check_equal(arr.width, 7); - check_equal(arr.height, 13); - check_equal(arr.depth, 5); - check_equal(arr.length, 5*7*13); - check_equal(arr.bit_width, 28); - check_equal(arr.is_signed, true); - - elsif run("Has copy") then - arr := new_3d(width => 7, height => 13, depth => 5, - bit_width => 28, is_signed => true); - for i in 0 to arr.length-1 loop - set(arr, idx=>i, value => i); - end loop; - - other_arr := copy(arr); - check_equal(arr.width, other_arr.width); - check_equal(arr.height, other_arr.height); - check_equal(arr.depth, other_arr.depth); - check_equal(arr.length, other_arr.length); - check_equal(arr.bit_width, other_arr.bit_width); - check_equal(arr.is_signed, other_arr.is_signed); - for i in 0 to other_arr.length-1 loop - check_equal(get(arr, i), get(other_arr, i)); - end loop; - - elsif run("Has set") then - arr := new_1d(length => 1); - set(arr, 0,7); - - elsif run("Has set 2d") then - arr := new_2d(width => 1, height => 2); - set(arr, x => 0, y => 0, value => 7); - set(arr, x => 0, y => 1, value => 11); - - elsif run("Test reshape") then - arr := new_1d(length => 1); - set(arr, 0, value => 100); - - reshape(arr, 2); - check_equal(arr.length, 2); - check_equal(get(arr, 0), 100); - set(arr, 1, value => 200); - check_equal(get(arr, 1), 200); - - reshape(arr, 1); - check_equal(arr.length, 1); - check_equal(get(arr, 0), 100); - - elsif run("Test reshape 2d") then - arr := new_1d(length => 3); - for i in 0 to 2 loop - set(arr, i, value => 10+i); - end loop; - - reshape(arr, 1, 3); - check_equal(arr.width, 1); - check_equal(arr.height, 3); - check_equal(arr.depth, 1); - for i in 0 to 2 loop - check_equal(get(arr, i), 10+i); - end loop; - - for i in 0 to 2 loop - check_equal(get(arr, 0, i), 10+i); - end loop; - - reshape(arr, 3, 1); - check_equal(arr.width, 3); - check_equal(arr.height, 1); - check_equal(arr.depth, 1); - for i in 0 to 2 loop - check_equal(get(arr, i), 10+i); - end loop; - for i in 0 to 2 loop - check_equal(get(arr, i, 0), 10+i); - end loop; - - reshape(arr, 2, 1); - check_equal(arr.width, 2); - check_equal(arr.height, 1); - check_equal(arr.depth, 1); - check_equal(get(arr, 0, 0), 10); - check_equal(get(arr, 1, 0), 11); - - elsif run("Test reshape 3d") then - arr := new_1d(length => 6); - for i in 0 to 5 loop - set(arr, i, value => 10+i); - end loop; - - reshape(arr, 1, 2, 3); - check_equal(arr.width, 1); - check_equal(arr.height, 2); - check_equal(arr.depth, 3); - for i in 0 to 5 loop - check_equal(get(arr, i), 10+i); - end loop; - - for i in 0 to 5 loop - check_equal(get(arr, 0, i / 3, i mod 3), 10+i); - end loop; - - - elsif run("Has get") then - arr := new_1d(2); - set(arr, 0, 7); - set(arr, 1, 11); - check_equal(get(arr, 0), 7); - check_equal(get(arr, 1), 11); - - elsif run("Has get 2d") then - arr := new_2d(width => 2, height => 3); - for i in 0 to 5 loop - set(arr, i mod 2, i/2, 10 + i); - end loop; - - for i in 0 to 5 loop - check_equal(get(arr, i mod 2, i/2), 10 + i); - end loop; - - for i in 0 to 5 loop - check_equal(get(arr, i), 10 + i); - end loop; - - elsif run("Has set and get 2d") then - arr := new_3d(width => 2, height => 3, depth => 5); - for x in 0 to arr.width-1 loop - for y in 0 to arr.height-1 loop - for z in 0 to arr.depth-1 loop - set(arr, x,y,z, 1000*x + 100*y + z); - end loop; - end loop; - end loop; - - for x in 0 to arr.width-1 loop - for y in 0 to arr.height-1 loop - for z in 0 to arr.depth-1 loop - check_equal(get(arr, x,y,z), 1000*x + 100*y + z); - end loop; - end loop; - end loop; - - elsif run("Has append") then - arr := new_1d; - append(arr, 11); - check_equal(arr.length, 1); - check_equal(get(arr, 0), 11); - - append(arr, 7); - check_equal(arr.length, 2); - check_equal(get(arr, 1), 7); - - elsif run("Deallocate sets length to 0") then - arr := new_1d; - append(arr, 10); - check_equal(arr.length, 1); - deallocate(arr); - check_equal(arr.length, 0); - - elsif run("Deallocate sets width height depth to 0") then - arr := new_3d(width => 2, height => 3, depth => 5); - check_equal(arr.width, 2); - check_equal(arr.height, 3); - check_equal(arr.depth, 5); - deallocate(arr); - check_equal(arr.width, 0); - check_equal(arr.height, 0); - check_equal(arr.depth, 0); - - elsif run("Can save and load csv") then - arr := new_1d; - append(arr, integer'left); - append(arr, 0); - append(arr, integer'right); - save_csv(arr, output_path & "can_save.csv"); - other_arr := load_csv(output_path & "can_save.csv"); - check_equal(other_arr.length, arr.length); - for idx in 0 to arr.length-1 loop - check_equal(get(arr, idx), get(other_arr, idx)); - end loop; - - elsif run("Can save and load csv 2d") then - arr := new_1d; - append(arr, integer'left); - append(arr, 0); - append(arr, integer'right); - append(arr, 1); - reshape(arr, 2, 2); - save_csv(arr, output_path & "can_save_2d.csv"); - - other_arr := load_csv(output_path & "can_save_2d.csv"); - check_equal(other_arr.length, arr.length); - check_equal(other_arr.width, arr.width); - check_equal(other_arr.height, arr.height); - check_equal(other_arr.depth, arr.depth); - - for x in 0 to arr.width-1 loop - for y in 0 to arr.height-1 loop - check_equal(get(arr, x,y), get(other_arr, x,y)); - end loop; - end loop; - - elsif run("Can save and load csv 3d") then - arr := new_1d; - for i in 0 to 30 loop - append(arr, i); - end loop; - reshape(arr, 2, 3, 5); - save_csv(arr, output_path & "can_save_3d.csv"); - - other_arr := load_csv(output_path & "can_save_3d.csv"); - check_equal(other_arr.length, arr.length); - check_equal(other_arr.width, arr.width*arr.depth); - check_equal(other_arr.height, arr.height); - - for idx in 0 to arr.length-1 loop - check_equal(get(arr, idx), get(other_arr, idx)); - end loop; - - elsif run("Can save and load raw") then - for bit_width in 1 to 31 loop - for is_signed in 0 to 1 loop - test_save_and_load_raw(bit_width => bit_width, is_signed => is_signed=1); - end loop; - end loop; - test_save_and_load_raw(bit_width => 32, is_signed => true); - - elsif run("Save signed and load unsigned") then - arr := new_1d(bit_width => 14, - is_signed => true); - append(arr, -1); - append(arr, -2**13); - save_raw(arr, output_path & "s14_to_u16.csv"); - other_arr := load_raw(output_path & "s14_to_u16.csv", - bit_width => 16, - is_signed => false); - check_equal(other_arr.length, 2); - check_equal(get(other_arr, 0), 2**16-1); - check_equal(get(other_arr, 1), 2**16 - 2**13); - - elsif run("is null") then - deallocate(arr); - check_true(is_null(arr)); - arr := new_1d; - check_false(is_null(arr)); - append(arr, 1); - deallocate(arr); - check_true(is_null(arr)); - end if; - - end loop; - test_runner_cleanup(runner); - wait; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- @TODO add explicit check of csv string data + +-- vunit: fail_on_warning +-- vunit: run_all_in_same_sim + +use std.textio.all; + +library vunit_lib; +--context vunit_lib.vunit_context; +use work.integer_array_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.run_pkg.all; + +entity tb_integer_array is + generic ( + output_path : string; + runner_cfg : string); +end entity; + +architecture a of tb_integer_array is +begin + + main : process + variable arr : integer_array_t := null_integer_array; + variable other_arr : integer_array_t := null_integer_array; + + impure function num_bytes(file_name : string) return integer is + type binary_file_t is file of character; + file fread : binary_file_t; + variable num_bytes : integer := 0; + variable chr : character; + begin + file_open(fread, file_name, read_mode); + while not endfile(fread) loop + num_bytes := num_bytes + 1; + read(fread, chr); + end loop; + file_close(fread); + return num_bytes; + end function; + + procedure test_save_and_load_raw(bit_width : integer; + is_signed : boolean) is + variable arr : integer_array_t; + variable other_arr : integer_array_t; + + impure function file_name return string is + begin + if is_signed then + return output_path & "s" & integer'image(bit_width) & ".raw"; + else + return output_path & "u" & integer'image(bit_width) & ".raw"; + end if; + end function; + + constant bytes_per_word : integer := (bit_width+7)/8; + + begin + arr := new_1d(bit_width => bit_width, is_signed => is_signed); + append(arr, arr.lower_limit); + append(arr, 0); + append(arr, arr.upper_limit); + save_raw(arr, file_name); + other_arr := load_raw(file_name, + bit_width => bit_width, is_signed => is_signed); + check_equal(get(other_arr, 0), get(arr, 0)); + check_equal(get(other_arr, 1), get(arr, 1)); + check_equal(get(other_arr, 2), get(arr, 2)); + check_equal(num_bytes(file_name), bytes_per_word * arr.length); + end procedure; + begin + test_runner_setup(runner, runner_cfg); + while test_suite loop + deallocate(arr); + deallocate(other_arr); + + if run("Has length") then + arr := new_1d; + check_equal(arr.length, 0); + + elsif run("Has bit_width") then + arr := new_1d; + check_equal(arr.bit_width, 32); + + elsif run("Has is_signed") then + arr := new_1d; + check_equal(arr.is_signed, true); + + elsif run("Has new_1d") then + arr := new_1d(length => 10, bit_width => 16, is_signed => false); + check_equal(arr.length, 10); + check_equal(arr.bit_width, 16); + check_equal(arr.is_signed, false); + + elsif run("Has new_2d") then + arr := new_2d(width => 7, height => 13, bit_width => 28, is_signed => true); + check_equal(arr.width, 7); + check_equal(arr.height, 13); + check_equal(arr.length, 7*13); + check_equal(arr.bit_width, 28); + check_equal(arr.is_signed, true); + + elsif run("Has new_3d") then + arr := new_3d(width => 7, height => 13, depth => 5, + bit_width => 28, is_signed => true); + check_equal(arr.width, 7); + check_equal(arr.height, 13); + check_equal(arr.depth, 5); + check_equal(arr.length, 5*7*13); + check_equal(arr.bit_width, 28); + check_equal(arr.is_signed, true); + + elsif run("Has copy") then + arr := new_3d(width => 7, height => 13, depth => 5, + bit_width => 28, is_signed => true); + for i in 0 to arr.length-1 loop + set(arr, idx=>i, value => i); + end loop; + + other_arr := copy(arr); + check_equal(arr.width, other_arr.width); + check_equal(arr.height, other_arr.height); + check_equal(arr.depth, other_arr.depth); + check_equal(arr.length, other_arr.length); + check_equal(arr.bit_width, other_arr.bit_width); + check_equal(arr.is_signed, other_arr.is_signed); + for i in 0 to other_arr.length-1 loop + check_equal(get(arr, i), get(other_arr, i)); + end loop; + + elsif run("Has set") then + arr := new_1d(length => 1); + set(arr, 0,7); + + elsif run("Has set 2d") then + arr := new_2d(width => 1, height => 2); + set(arr, x => 0, y => 0, value => 7); + set(arr, x => 0, y => 1, value => 11); + + elsif run("Test reshape") then + arr := new_1d(length => 1); + set(arr, 0, value => 100); + + reshape(arr, 2); + check_equal(arr.length, 2); + check_equal(get(arr, 0), 100); + set(arr, 1, value => 200); + check_equal(get(arr, 1), 200); + + reshape(arr, 1); + check_equal(arr.length, 1); + check_equal(get(arr, 0), 100); + + elsif run("Test reshape 2d") then + arr := new_1d(length => 3); + for i in 0 to 2 loop + set(arr, i, value => 10+i); + end loop; + + reshape(arr, 1, 3); + check_equal(arr.width, 1); + check_equal(arr.height, 3); + check_equal(arr.depth, 1); + for i in 0 to 2 loop + check_equal(get(arr, i), 10+i); + end loop; + + for i in 0 to 2 loop + check_equal(get(arr, 0, i), 10+i); + end loop; + + reshape(arr, 3, 1); + check_equal(arr.width, 3); + check_equal(arr.height, 1); + check_equal(arr.depth, 1); + for i in 0 to 2 loop + check_equal(get(arr, i), 10+i); + end loop; + for i in 0 to 2 loop + check_equal(get(arr, i, 0), 10+i); + end loop; + + reshape(arr, 2, 1); + check_equal(arr.width, 2); + check_equal(arr.height, 1); + check_equal(arr.depth, 1); + check_equal(get(arr, 0, 0), 10); + check_equal(get(arr, 1, 0), 11); + + elsif run("Test reshape 3d") then + arr := new_1d(length => 6); + for i in 0 to 5 loop + set(arr, i, value => 10+i); + end loop; + + reshape(arr, 1, 2, 3); + check_equal(arr.width, 1); + check_equal(arr.height, 2); + check_equal(arr.depth, 3); + for i in 0 to 5 loop + check_equal(get(arr, i), 10+i); + end loop; + + for i in 0 to 5 loop + check_equal(get(arr, 0, i / 3, i mod 3), 10+i); + end loop; + + + elsif run("Has get") then + arr := new_1d(2); + set(arr, 0, 7); + set(arr, 1, 11); + check_equal(get(arr, 0), 7); + check_equal(get(arr, 1), 11); + + elsif run("Has get 2d") then + arr := new_2d(width => 2, height => 3); + for i in 0 to 5 loop + set(arr, i mod 2, i/2, 10 + i); + end loop; + + for i in 0 to 5 loop + check_equal(get(arr, i mod 2, i/2), 10 + i); + end loop; + + for i in 0 to 5 loop + check_equal(get(arr, i), 10 + i); + end loop; + + elsif run("Has set and get 2d") then + arr := new_3d(width => 2, height => 3, depth => 5); + for x in 0 to arr.width-1 loop + for y in 0 to arr.height-1 loop + for z in 0 to arr.depth-1 loop + set(arr, x,y,z, 1000*x + 100*y + z); + end loop; + end loop; + end loop; + + for x in 0 to arr.width-1 loop + for y in 0 to arr.height-1 loop + for z in 0 to arr.depth-1 loop + check_equal(get(arr, x,y,z), 1000*x + 100*y + z); + end loop; + end loop; + end loop; + + elsif run("Has append") then + arr := new_1d; + append(arr, 11); + check_equal(arr.length, 1); + check_equal(get(arr, 0), 11); + + append(arr, 7); + check_equal(arr.length, 2); + check_equal(get(arr, 1), 7); + + elsif run("Deallocate sets length to 0") then + arr := new_1d; + append(arr, 10); + check_equal(arr.length, 1); + deallocate(arr); + check_equal(arr.length, 0); + + elsif run("Deallocate sets width height depth to 0") then + arr := new_3d(width => 2, height => 3, depth => 5); + check_equal(arr.width, 2); + check_equal(arr.height, 3); + check_equal(arr.depth, 5); + deallocate(arr); + check_equal(arr.width, 0); + check_equal(arr.height, 0); + check_equal(arr.depth, 0); + + elsif run("Can save and load csv") then + arr := new_1d; + append(arr, integer'left); + append(arr, 0); + append(arr, integer'right); + save_csv(arr, output_path & "can_save.csv"); + other_arr := load_csv(output_path & "can_save.csv"); + check_equal(other_arr.length, arr.length); + for idx in 0 to arr.length-1 loop + check_equal(get(arr, idx), get(other_arr, idx)); + end loop; + + elsif run("Can save and load csv 2d") then + arr := new_1d; + append(arr, integer'left); + append(arr, 0); + append(arr, integer'right); + append(arr, 1); + reshape(arr, 2, 2); + save_csv(arr, output_path & "can_save_2d.csv"); + + other_arr := load_csv(output_path & "can_save_2d.csv"); + check_equal(other_arr.length, arr.length); + check_equal(other_arr.width, arr.width); + check_equal(other_arr.height, arr.height); + check_equal(other_arr.depth, arr.depth); + + for x in 0 to arr.width-1 loop + for y in 0 to arr.height-1 loop + check_equal(get(arr, x,y), get(other_arr, x,y)); + end loop; + end loop; + + elsif run("Can save and load csv 3d") then + arr := new_1d; + for i in 0 to 30 loop + append(arr, i); + end loop; + reshape(arr, 2, 3, 5); + save_csv(arr, output_path & "can_save_3d.csv"); + + other_arr := load_csv(output_path & "can_save_3d.csv"); + check_equal(other_arr.length, arr.length); + check_equal(other_arr.width, arr.width*arr.depth); + check_equal(other_arr.height, arr.height); + + for idx in 0 to arr.length-1 loop + check_equal(get(arr, idx), get(other_arr, idx)); + end loop; + + elsif run("Can save and load raw") then + for bit_width in 1 to 31 loop + for is_signed in 0 to 1 loop + test_save_and_load_raw(bit_width => bit_width, is_signed => is_signed=1); + end loop; + end loop; + test_save_and_load_raw(bit_width => 32, is_signed => true); + + elsif run("Save signed and load unsigned") then + arr := new_1d(bit_width => 14, + is_signed => true); + append(arr, -1); + append(arr, -2**13); + save_raw(arr, output_path & "s14_to_u16.csv"); + other_arr := load_raw(output_path & "s14_to_u16.csv", + bit_width => 16, + is_signed => false); + check_equal(other_arr.length, 2); + check_equal(get(other_arr, 0), 2**16-1); + check_equal(get(other_arr, 1), 2**16 - 2**13); + + elsif run("is null") then + deallocate(arr); + check_true(is_null(arr)); + arr := new_1d; + check_false(is_null(arr)); + append(arr, 1); + deallocate(arr); + check_true(is_null(arr)); + end if; + + end loop; + test_runner_cleanup(runner); + wait; + end process; +end architecture; diff --git a/vunit/vhdl/data_types/test/tb_integer_vector_ptr.vhd b/vunit/vhdl/data_types/test/tb_integer_vector_ptr.vhd index 1869e43da..c60c83b3e 100644 --- a/vunit/vhdl/data_types/test/tb_integer_vector_ptr.vhd +++ b/vunit/vhdl/data_types/test/tb_integer_vector_ptr.vhd @@ -1,114 +1,114 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; ---context vunit_lib.vunit_context; -use vunit_lib.check_pkg.all; -use vunit_lib.run_pkg.all; - -use work.integer_vector_ptr_pkg.all; - -entity tb_integer_vector_ptr is - generic (runner_cfg : string); -end; - -architecture a of tb_integer_vector_ptr is -begin - main : process - variable ptr, ptr2 : integer_vector_ptr_t; - constant a_random_value : integer := 77; - constant another_random_value : integer := 999; - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("test_allocate") then - ptr := new_integer_vector_ptr; - check_equal(length(ptr), 0); - - ptr := new_integer_vector_ptr(1); - check_equal(length(ptr), 1); - check_equal(get(ptr, 0), 0, "init value"); - - ptr := new_integer_vector_ptr(2, value => 3); - check_equal(length(ptr), 2); - check_equal(get(ptr, 0), 3, "init value"); - check_equal(get(ptr, 1), 3, "init value"); - - reallocate(ptr, 5, value => 11); - check_equal(length(ptr), 5); - - for i in 0 to length(ptr)-1 loop - check_equal(get(ptr, i), 11, "init value"); - end loop; - - elsif run("test_element_access") then - ptr := new_integer_vector_ptr(1); - set(ptr, 0, a_random_value); - check_equal(get(ptr, 0), a_random_value); - - ptr2 := new_integer_vector_ptr(2); - set(ptr2, 0, another_random_value); - set(ptr2, 1, a_random_value); - check_equal(get(ptr2, 0), another_random_value); - check_equal(get(ptr2, 1), a_random_value); - - check_equal(get(ptr, 0), a_random_value, - "Checking that ptr was not affected by ptr2"); - - elsif run("test_resize") then - ptr := new_integer_vector_ptr(1); - check_equal(length(ptr), 1); - set(ptr, 0, a_random_value); - check_equal(get(ptr, 0), a_random_value); - - resize(ptr, 2); - check_equal(length(ptr), 2); - set(ptr, 1, another_random_value); - check_equal(get(ptr, 0), a_random_value, - "Checking that grown ptr still contain old value"); - check_equal(get(ptr, 1), another_random_value); - - resize(ptr, 1); - check_equal(length(ptr), 1); - check_equal(get(ptr, 0), a_random_value, - "Checking that shrunk ptr still contain old value"); - - elsif run("test_resize_with_drop") then - ptr := new_integer_vector_ptr(8); - for i in 0 to 7 loop - set(ptr, i, i); - end loop; - resize(ptr, 4, drop => 4); - - for i in 0 to 3 loop - check_equal(get(ptr, i), 4+i); - end loop; - - elsif run("test_resize_with_default") then - ptr := new_integer_vector_ptr(0); - resize(ptr, 2, value => a_random_value); - check_equal(length(ptr), 2); - check_equal(get(ptr, 0), a_random_value); - check_equal(get(ptr, 1), a_random_value); - - elsif run("test_from_and_to_integer") then - ptr := new_integer_vector_ptr(2); - assert to_integer_vector_ptr(to_integer(ptr)) = ptr; - elsif run("Test codecs") then - ptr := new_integer_vector_ptr(0); - check(decode(encode(ptr)) = ptr); - - ptr2 := new_integer_vector_ptr(2); - set(ptr2, 0, another_random_value); - set(ptr2, 1, a_random_value); - check(decode(encode(ptr2)) = ptr2); - end if; - end loop; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +--context vunit_lib.vunit_context; +use vunit_lib.check_pkg.all; +use vunit_lib.run_pkg.all; + +use work.integer_vector_ptr_pkg.all; + +entity tb_integer_vector_ptr is + generic (runner_cfg : string); +end; + +architecture a of tb_integer_vector_ptr is +begin + main : process + variable ptr, ptr2 : integer_vector_ptr_t; + constant a_random_value : integer := 77; + constant another_random_value : integer := 999; + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("test_allocate") then + ptr := new_integer_vector_ptr; + check_equal(length(ptr), 0); + + ptr := new_integer_vector_ptr(1); + check_equal(length(ptr), 1); + check_equal(get(ptr, 0), 0, "init value"); + + ptr := new_integer_vector_ptr(2, value => 3); + check_equal(length(ptr), 2); + check_equal(get(ptr, 0), 3, "init value"); + check_equal(get(ptr, 1), 3, "init value"); + + reallocate(ptr, 5, value => 11); + check_equal(length(ptr), 5); + + for i in 0 to length(ptr)-1 loop + check_equal(get(ptr, i), 11, "init value"); + end loop; + + elsif run("test_element_access") then + ptr := new_integer_vector_ptr(1); + set(ptr, 0, a_random_value); + check_equal(get(ptr, 0), a_random_value); + + ptr2 := new_integer_vector_ptr(2); + set(ptr2, 0, another_random_value); + set(ptr2, 1, a_random_value); + check_equal(get(ptr2, 0), another_random_value); + check_equal(get(ptr2, 1), a_random_value); + + check_equal(get(ptr, 0), a_random_value, + "Checking that ptr was not affected by ptr2"); + + elsif run("test_resize") then + ptr := new_integer_vector_ptr(1); + check_equal(length(ptr), 1); + set(ptr, 0, a_random_value); + check_equal(get(ptr, 0), a_random_value); + + resize(ptr, 2); + check_equal(length(ptr), 2); + set(ptr, 1, another_random_value); + check_equal(get(ptr, 0), a_random_value, + "Checking that grown ptr still contain old value"); + check_equal(get(ptr, 1), another_random_value); + + resize(ptr, 1); + check_equal(length(ptr), 1); + check_equal(get(ptr, 0), a_random_value, + "Checking that shrunk ptr still contain old value"); + + elsif run("test_resize_with_drop") then + ptr := new_integer_vector_ptr(8); + for i in 0 to 7 loop + set(ptr, i, i); + end loop; + resize(ptr, 4, drop => 4); + + for i in 0 to 3 loop + check_equal(get(ptr, i), 4+i); + end loop; + + elsif run("test_resize_with_default") then + ptr := new_integer_vector_ptr(0); + resize(ptr, 2, value => a_random_value); + check_equal(length(ptr), 2); + check_equal(get(ptr, 0), a_random_value); + check_equal(get(ptr, 1), a_random_value); + + elsif run("test_from_and_to_integer") then + ptr := new_integer_vector_ptr(2); + assert to_integer_vector_ptr(to_integer(ptr)) = ptr; + elsif run("Test codecs") then + ptr := new_integer_vector_ptr(0); + check(decode(encode(ptr)) = ptr); + + ptr2 := new_integer_vector_ptr(2); + set(ptr2, 0, another_random_value); + set(ptr2, 1, a_random_value); + check(decode(encode(ptr2)) = ptr2); + end if; + end loop; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/vhdl/data_types/test/tb_integer_vector_ptr_pool.vhd b/vunit/vhdl/data_types/test/tb_integer_vector_ptr_pool.vhd index 12bf15ee3..91a3fbce4 100644 --- a/vunit/vhdl/data_types/test/tb_integer_vector_ptr_pool.vhd +++ b/vunit/vhdl/data_types/test/tb_integer_vector_ptr_pool.vhd @@ -1,56 +1,56 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; ---context vunit_lib.vunit_context; -use vunit_lib.check_pkg.all; -use vunit_lib.run_pkg.all; - -use work.integer_vector_ptr_pkg.all; -use work.integer_vector_ptr_pool_pkg.all; - - -entity tb_integer_vector_ptr_pool is - generic (runner_cfg : string); -end entity; - -architecture a of tb_integer_vector_ptr_pool is -begin - main : process - variable pool : integer_vector_ptr_pool_t; - variable ptr, old_ptr : integer_vector_ptr_t; - begin - test_runner_setup(runner, runner_cfg); - - if run("Test default pool is null") then - assert pool = null_integer_vector_ptr_pool report "Expected null pool"; - - elsif run("Test allocated ptr has length") then - pool := new_integer_vector_ptr_pool; - ptr := new_integer_vector_ptr(pool, 77); - assert ptr /= null_ptr report "Expected non null ptr"; - check_equal(length(ptr), 77); - recycle(pool, ptr); - - elsif run("Test recycled ptr is null") then - pool := new_integer_vector_ptr_pool; - ptr := new_integer_vector_ptr(pool); - assert ptr /= null_ptr report "Expected non null ptr"; - recycle(pool, ptr); - assert ptr = null_ptr report "Expected null ptr"; - - elsif run("Test ptr is recycled") then - pool := new_integer_vector_ptr_pool; - ptr := new_integer_vector_ptr(pool, 2); - old_ptr := ptr; - recycle(pool, ptr); - ptr := new_integer_vector_ptr(pool, 2); - assert ptr = old_ptr report "Was recycled"; - end if; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +--context vunit_lib.vunit_context; +use vunit_lib.check_pkg.all; +use vunit_lib.run_pkg.all; + +use work.integer_vector_ptr_pkg.all; +use work.integer_vector_ptr_pool_pkg.all; + + +entity tb_integer_vector_ptr_pool is + generic (runner_cfg : string); +end entity; + +architecture a of tb_integer_vector_ptr_pool is +begin + main : process + variable pool : integer_vector_ptr_pool_t; + variable ptr, old_ptr : integer_vector_ptr_t; + begin + test_runner_setup(runner, runner_cfg); + + if run("Test default pool is null") then + assert pool = null_integer_vector_ptr_pool report "Expected null pool"; + + elsif run("Test allocated ptr has length") then + pool := new_integer_vector_ptr_pool; + ptr := new_integer_vector_ptr(pool, 77); + assert ptr /= null_ptr report "Expected non null ptr"; + check_equal(length(ptr), 77); + recycle(pool, ptr); + + elsif run("Test recycled ptr is null") then + pool := new_integer_vector_ptr_pool; + ptr := new_integer_vector_ptr(pool); + assert ptr /= null_ptr report "Expected non null ptr"; + recycle(pool, ptr); + assert ptr = null_ptr report "Expected null ptr"; + + elsif run("Test ptr is recycled") then + pool := new_integer_vector_ptr_pool; + ptr := new_integer_vector_ptr(pool, 2); + old_ptr := ptr; + recycle(pool, ptr); + ptr := new_integer_vector_ptr(pool, 2); + assert ptr = old_ptr report "Was recycled"; + end if; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/vhdl/data_types/test/tb_queue-2008.vhd b/vunit/vhdl/data_types/test/tb_queue-2008.vhd index b8bdd3958..765c6da8b 100644 --- a/vunit/vhdl/data_types/test/tb_queue-2008.vhd +++ b/vunit/vhdl/data_types/test/tb_queue-2008.vhd @@ -1,84 +1,84 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.fixed_pkg.all; -use ieee.float_pkg.all; - -library vunit_lib; -context vunit_lib.vunit_context; - -use work.queue_pkg.all; -use work.queue_2008_pkg.all; - -entity tb_queue_2008 is - generic (runner_cfg : string); -end entity; - -architecture a of tb_queue_2008 is -begin - main : process - variable queue : queue_t; - variable boolv : boolean_vector(0 to 1); - begin - test_runner_setup(runner, runner_cfg); - - if run("Test push and pop boolean_vector") then - queue := new_queue; - push_boolean_vector(queue, (1 => true)); - push_boolean_vector(queue, (false, true)); - boolv(0 to 0) := pop_boolean_vector(queue); - check(boolv(0) = true); - boolv := pop_boolean_vector(queue); - check(boolv = (false, true)); - - elsif run("Test push and pop integer_vector") then - queue := new_queue; - push_integer_vector(queue, (1 => 17)); - push_integer_vector(queue, (-21, 42)); - check(pop_integer_vector(queue) = (1 => 17)); - check(pop_integer_vector(queue) = (-21, 42)); - - elsif run("Test push and pop real_vector") then - queue := new_queue; - push_real_vector(queue, (1 => 17.17)); - push_real_vector(queue, (-21.21, 42.42)); - check(pop_real_vector(queue) = (1 => 17.17)); - check(pop_real_vector(queue) = (-21.21, 42.42)); - - elsif run("Test push and pop time_vector") then - queue := new_queue; - push_time_vector(queue, (1 => 17.17 ms)); - push_time_vector(queue, (-21.21 min, 42.42 us)); - check(pop_time_vector(queue) = (1 => 17.17 ms)); - check(pop_time_vector(queue) = (-21.21 min, 42.42 us)); - - elsif run("Test push and pop ufixed") then - queue := new_queue; - push_ufixed(queue, to_ufixed(17.17, 6, -9)); - push_ufixed(queue, to_ufixed(42.42, 6, -9)); - check(pop_ufixed(queue) = to_ufixed(17.17, 6, -9)); - check(pop_ufixed(queue) = to_ufixed(42.42, 6, -9)); - - elsif run("Test push and pop sfixed") then - queue := new_queue; - push_sfixed(queue, to_sfixed(17.17, 6, -9)); - push_sfixed(queue, to_sfixed(-21.21, 6, -9)); - check(pop_sfixed(queue) = to_sfixed(17.17, 6, -9)); - check(pop_sfixed(queue) = to_sfixed(-21.21, 6, -9)); - - elsif run("Test push and pop float") then - queue := new_queue; - push_float(queue, to_float(17.17)); - push_float(queue, to_float(-21.21)); - check(pop_float(queue) = to_float(17.17)); - check(pop_float(queue) = to_float(-21.21)); - - end if; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.fixed_pkg.all; +use ieee.float_pkg.all; + +library vunit_lib; +context vunit_lib.vunit_context; + +use work.queue_pkg.all; +use work.queue_2008_pkg.all; + +entity tb_queue_2008 is + generic (runner_cfg : string); +end entity; + +architecture a of tb_queue_2008 is +begin + main : process + variable queue : queue_t; + variable boolv : boolean_vector(0 to 1); + begin + test_runner_setup(runner, runner_cfg); + + if run("Test push and pop boolean_vector") then + queue := new_queue; + push_boolean_vector(queue, (1 => true)); + push_boolean_vector(queue, (false, true)); + boolv(0 to 0) := pop_boolean_vector(queue); + check(boolv(0) = true); + boolv := pop_boolean_vector(queue); + check(boolv = (false, true)); + + elsif run("Test push and pop integer_vector") then + queue := new_queue; + push_integer_vector(queue, (1 => 17)); + push_integer_vector(queue, (-21, 42)); + check(pop_integer_vector(queue) = (1 => 17)); + check(pop_integer_vector(queue) = (-21, 42)); + + elsif run("Test push and pop real_vector") then + queue := new_queue; + push_real_vector(queue, (1 => 17.17)); + push_real_vector(queue, (-21.21, 42.42)); + check(pop_real_vector(queue) = (1 => 17.17)); + check(pop_real_vector(queue) = (-21.21, 42.42)); + + elsif run("Test push and pop time_vector") then + queue := new_queue; + push_time_vector(queue, (1 => 17.17 ms)); + push_time_vector(queue, (-21.21 min, 42.42 us)); + check(pop_time_vector(queue) = (1 => 17.17 ms)); + check(pop_time_vector(queue) = (-21.21 min, 42.42 us)); + + elsif run("Test push and pop ufixed") then + queue := new_queue; + push_ufixed(queue, to_ufixed(17.17, 6, -9)); + push_ufixed(queue, to_ufixed(42.42, 6, -9)); + check(pop_ufixed(queue) = to_ufixed(17.17, 6, -9)); + check(pop_ufixed(queue) = to_ufixed(42.42, 6, -9)); + + elsif run("Test push and pop sfixed") then + queue := new_queue; + push_sfixed(queue, to_sfixed(17.17, 6, -9)); + push_sfixed(queue, to_sfixed(-21.21, 6, -9)); + check(pop_sfixed(queue) = to_sfixed(17.17, 6, -9)); + check(pop_sfixed(queue) = to_sfixed(-21.21, 6, -9)); + + elsif run("Test push and pop float") then + queue := new_queue; + push_float(queue, to_float(17.17)); + push_float(queue, to_float(-21.21)); + check(pop_float(queue) = to_float(17.17)); + check(pop_float(queue) = to_float(-21.21)); + + end if; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/vhdl/data_types/test/tb_queue.vhd b/vunit/vhdl/data_types/test/tb_queue.vhd index f06299a6e..77448f35c 100644 --- a/vunit/vhdl/data_types/test/tb_queue.vhd +++ b/vunit/vhdl/data_types/test/tb_queue.vhd @@ -1,338 +1,338 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use ieee.numeric_bit.all; -use ieee.math_complex.all; - -library vunit_lib; -use vunit_lib.check_pkg.all; -use vunit_lib.run_pkg.all; -use vunit_lib.integer_vector_ptr_pkg.all; -use vunit_lib.string_ptr_pkg.all; -use vunit_lib.integer_array_pkg.all; -use work.queue_pkg.all; - -entity tb_queue is - generic (runner_cfg : string); -end entity; - -architecture a of tb_queue is - constant test_string1 : string(1 to 7) := "abcdefg"; - constant test_string2 : string(7 downto 1) := "abcdefg"; - constant ascending_slv : std_logic_vector(22 to 23) := "UX"; - constant ascending_sulv : std_ulogic_vector(22 to 23) := "UX"; - constant descending_slv : std_logic_vector(9 downto 1) := "000111UUU"; - constant descending_sulv : std_ulogic_vector(9 downto 1) := "000111UUU"; -begin - main : process - variable queue, another_queue, queue_copy : queue_t; - variable bv : bit_vector(0 to 5); - variable integer_vector_ptr, integer_vector_ptr_copy : integer_vector_ptr_t; - variable string_ptr, string_ptr_copy : string_ptr_t; - variable integer_array, integer_array_copy: integer_array_t; - begin - test_runner_setup(runner, runner_cfg); - if run("Test default queue is null") then - assert queue = null_queue report "Expected null queue"; - - elsif run("Test is_empty") then - queue := new_queue; - check(is_empty(queue), "Empty queue"); - - push_integer(queue, 11); - check_false(is_empty(queue), "Empty queue"); - - check_equal(pop_integer(queue), 11, "data"); - check(is_empty(queue), "Empty queue"); - - elsif run("Test push and pop integer") then - queue := new_queue; - assert queue /= null_queue report "Expected non null queue"; - check_equal(length(queue), 0, "Empty queue length"); - - push_integer(queue, 11); - check_equal(length(queue), 5, "Length"); - push_integer(queue, 22); - check_equal(length(queue), 10, "Length"); - - check_equal(pop_integer(queue), 11, "data"); - check_equal(length(queue), 5, "Length"); - check_equal(pop_integer(queue), 22, "data"); - check_equal(length(queue), 0, "Length"); - - push_integer(queue, integer'low); - check_equal(pop_integer(queue), integer'low, "data"); - push_integer(queue, integer'high); - check_equal(pop_integer(queue), integer'high, "data"); - - elsif run("Test push and pop byte") then - queue := new_queue; - push_byte(queue, 11); - push_byte(queue, 22); - check_equal(pop_byte(queue), 11, "data"); - check_equal(pop_byte(queue), 22, "data"); - - elsif run("Test push and pop boolean") then - queue := new_queue; - push_boolean(queue, false); - push_boolean(queue, true); - check_false(pop_boolean(queue), "data"); - check_true(pop_boolean(queue), "data"); - - elsif run("Test push and pop character") then - queue := new_queue; - assert queue /= null_queue report "Expected non null queue"; - check_equal(length(queue), 0, "Empty queue length"); - - push_character(queue, '1'); - check_equal(length(queue), 2, "Length"); - push_character(queue, '2'); - check_equal(length(queue), 4, "Length"); - - assert pop_character(queue) = '1'; - check_equal(length(queue), 2, "Length"); - assert pop_character(queue) = '2'; - check_equal(length(queue), 0, "Length"); - - elsif run("Test flush queue") then - queue := new_queue; - push_character(queue, '1'); - check_equal(length(queue), 2, "Length"); - push_character(queue, '2'); - check_equal(length(queue), 4, "Length"); - flush(queue); - check_equal(length(queue), 0, "Length"); - - elsif run("Test push and pop queue_t") then - queue := new_queue; - another_queue := new_queue; - push(another_queue, 22); - queue_copy := another_queue; - push_queue_ref(queue, another_queue); - assert another_queue = null_queue report "Ownership was transfered"; - assert pop_queue_ref(queue) = queue_copy report "Queue should come back"; - - elsif run("Test push and pop string") then - queue := new_queue; - push_string(queue, "hello world"); - push_string(queue, "two"); - push_string(queue, test_string1); - push_string(queue, test_string2); - assert pop_string(queue) = "hello world"; - assert pop_string(queue) = "two"; - assert pop_string(queue) = test_string1; - assert pop_string(queue) = test_string2; - - elsif run("Test push and pop bit") then - queue := new_queue; - push_bit(queue, '0'); - push_bit(queue, '1'); - check(pop_bit(queue) = '0'); - check(pop_bit(queue) = '1'); - - elsif run("Test push and pop std_ulogic") then - queue := new_queue; - push_std_ulogic(queue, 'U'); - push_std_ulogic(queue, 'X'); - push_std_ulogic(queue, '0'); - push_std_ulogic(queue, '1'); - push_std_ulogic(queue, 'Z'); - push_std_ulogic(queue, 'W'); - push_std_ulogic(queue, 'L'); - push_std_ulogic(queue, 'H'); - push_std_ulogic(queue, '-'); - assert pop_std_ulogic(queue) = 'U'; - assert pop_std_ulogic(queue) = 'X'; - assert pop_std_ulogic(queue) = '0'; - assert pop_std_ulogic(queue) = '1'; - assert pop_std_ulogic(queue) = 'Z'; - assert pop_std_ulogic(queue) = 'W'; - assert pop_std_ulogic(queue) = 'L'; - assert pop_std_ulogic(queue) = 'H'; - assert pop_std_ulogic(queue) = '-'; - - elsif run("Test push and pop severity_level") then - queue := new_queue; - push_severity_level(queue, note); - push_severity_level(queue, error); - check(pop_severity_level(queue) = note); - check(pop_severity_level(queue) = error); - - elsif run("Test push and pop file_open_status") then - queue := new_queue; - push_file_open_status(queue, open_ok); - push_file_open_status(queue, mode_error); - check(pop_file_open_status(queue) = open_ok); - check(pop_file_open_status(queue) = mode_error); - - elsif run("Test push and pop file_open_kind") then - queue := new_queue; - push_file_open_kind(queue, read_mode); - push_file_open_kind(queue, append_mode); - check(pop_file_open_kind(queue) = read_mode); - check(pop_file_open_kind(queue) = append_mode); - - elsif run("Test push and pop bit_vector") then - queue := new_queue; - push_bit_vector(queue, "1"); - push_bit_vector(queue, "010101"); - bv(0 to 0) := pop_bit_vector(queue); - check(bv(0) = '1'); - bv := pop_bit_vector(queue); - check(bv = "010101"); - - elsif run("Test push and pop std_ulogic_vector") then - queue := new_queue; - push_std_ulogic_vector(queue, descending_sulv); - push_std_ulogic_vector(queue, ascending_sulv); - assert pop_std_ulogic_vector(queue) = descending_sulv; - assert pop_std_ulogic_vector(queue) = ascending_sulv; - - elsif run("Test push and pop complex") then - queue := new_queue; - push_complex(queue, (1.0, 2.2)); - push_complex(queue, (-1.0, -2.2)); - check(pop_complex(queue) = (1.0, 2.2)); - check(pop_complex(queue) = (-1.0, -2.2)); - - elsif run("Test push and pop complex_polar") then - queue := new_queue; - push_complex_polar(queue, (1.0, 0.707)); - push_complex_polar(queue, (3.14, -0.707)); - check(pop_complex_polar(queue) = (1.0, 0.707)); - check(pop_complex_polar(queue) = (3.14, -0.707)); - - elsif run("Test push and pop ieee.numeric_bit.unsigned") then - queue := new_queue; - push_numeric_bit_unsigned(queue, "1"); - push_numeric_bit_unsigned(queue, "010101"); - check(pop_numeric_bit_unsigned(queue) = "1"); - check(pop_numeric_bit_unsigned(queue) = "010101"); - - elsif run("Test push and pop ieee.numeric_bit.signed") then - queue := new_queue; - push_numeric_bit_signed(queue, "1"); - push_numeric_bit_signed(queue, "010101"); - check(pop_numeric_bit_signed(queue) = "1"); - check(pop_numeric_bit_signed(queue) = "010101"); - - elsif run("Test push and pop ieee.numeric_std.unsigned") then - queue := new_queue; - push_numeric_std_unsigned(queue, "1"); - push_numeric_std_unsigned(queue, "010101"); - check(pop_numeric_std_unsigned(queue) = "1"); - check(pop_numeric_std_unsigned(queue) = "010101"); - - elsif run("Test push and pop ieee.numeric_std.signed") then - queue := new_queue; - push_numeric_std_signed(queue, "1"); - push_numeric_std_signed(queue, "010101"); - check(pop_numeric_std_signed(queue) = "1"); - check(pop_numeric_std_signed(queue) = "010101"); - - elsif run("Test push and pop std_logic_vector") then - queue := new_queue; - push_std_ulogic_vector(queue, std_ulogic_vector(descending_slv)); - push_std_ulogic_vector(queue, std_ulogic_vector(ascending_slv)); - assert std_logic_vector(pop_std_ulogic_vector(queue)) = descending_slv; - assert std_logic_vector(pop_std_ulogic_vector(queue)) = ascending_slv; - - elsif run("Test push and pop signed and unsigned") then - queue := new_queue; - push_std_ulogic_vector(queue, std_ulogic_vector(ieee.numeric_std.to_unsigned(11, 16))); - push_std_ulogic_vector(queue, std_ulogic_vector(ieee.numeric_std.to_signed(-1, 8))); - assert ieee.numeric_std.unsigned(pop_std_ulogic_vector(queue)) = ieee.numeric_std.to_unsigned(11, 16); - assert ieee.numeric_std.signed(pop_std_ulogic_vector(queue)) = ieee.numeric_std.to_signed(-1, 8); - - elsif run("Test push and pop real") then - queue := new_queue; - push_real(queue, 1.0); - push_real(queue, -1.0); - push_real(queue, 2.0); - push_real(queue, 0.5); - push_real(queue, -0.5); - assert pop_real(queue) = 1.0; - assert pop_real(queue) = -1.0; - assert pop_real(queue) = 2.0; - assert pop_real(queue) = 0.5; - assert pop_real(queue) = -0.5; - - push_real(queue, 1.0 + 0.5**23); -- Single - push_real(queue, -(1.0 + 0.5**23)); -- Single - push_real(queue, 1.0 + 0.5**53); -- Double - push_real(queue, -(1.0 + 0.5**53)); -- Double - assert pop_real(queue) = 1.0 + 0.5**23; - assert pop_real(queue) = -(1.0 + 0.5**23); - assert pop_real(queue) = 1.0 + 0.5**53; - assert pop_real(queue) = -(1.0 + 0.5**53); - - elsif run("Test push and pop time") then - queue := new_queue; - push_time(queue, 1 fs); - push_time(queue, 1 ps); - push_time(queue, 1 ns); - push_time(queue, 1 ms); - push_time(queue, 1 sec); - push_time(queue, 1 min); - push_time(queue, 1 hr); - assert pop_time(queue) = 1 fs; - assert pop_time(queue) = 1 ps; - assert pop_time(queue) = 1 ns; - assert pop_time(queue) = 1 ms; - assert pop_time(queue) = 1 sec; - assert pop_time(queue) = 1 min; - assert pop_time(queue) = 1 hr; - - push_time(queue, 1 hr + 1 fs); - assert pop_time(queue) = 1 hr + 1 fs; - - push_time(queue, -1 fs); - push_time(queue, -1 hr); - push_time(queue, -1 hr - 1 fs); - assert pop_time(queue) = -1 fs; - assert pop_time(queue) = -1 hr; - assert pop_time(queue) = -1 hr - 1 fs; - - elsif run("Test push and pop integer_vector_ptr_t") then - queue := new_queue; - integer_vector_ptr := new_integer_vector_ptr; - integer_vector_ptr_copy := integer_vector_ptr; - push_integer_vector_ptr_ref(queue, integer_vector_ptr); - assert integer_vector_ptr = null_ptr report "Ownership was transfered"; - assert pop_integer_vector_ptr_ref(queue) = integer_vector_ptr_copy; - - elsif run("Test push and pop string_ptr_t") then - queue := new_queue; - string_ptr := new_string_ptr; - string_ptr_copy := string_ptr; - push_string_ptr_ref(queue, string_ptr); - assert string_ptr = null_string_ptr report "Ownership was transfered"; - assert pop_string_ptr_ref(queue) = string_ptr_copy; - - elsif run("Test push and pop integer_array_t") then - queue := new_queue; - integer_array := new_3d(1, 2, 3, 4); - integer_array_copy := integer_array; - push_integer_array_t_ref(queue, integer_array); - assert integer_array = null_integer_array; - assert pop_integer_array_t_ref(queue) = integer_array_copy; - - elsif run("Test codecs") then - queue := new_queue; - check(decode(encode(queue)) = queue); - - another_queue := new_queue; - push_string(another_queue, "hello world"); - push_real(another_queue, 1.0); - check(decode(encode(another_queue)) = another_queue); - end if; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.numeric_bit.all; +use ieee.math_complex.all; + +library vunit_lib; +use vunit_lib.check_pkg.all; +use vunit_lib.run_pkg.all; +use vunit_lib.integer_vector_ptr_pkg.all; +use vunit_lib.string_ptr_pkg.all; +use vunit_lib.integer_array_pkg.all; +use work.queue_pkg.all; + +entity tb_queue is + generic (runner_cfg : string); +end entity; + +architecture a of tb_queue is + constant test_string1 : string(1 to 7) := "abcdefg"; + constant test_string2 : string(7 downto 1) := "abcdefg"; + constant ascending_slv : std_logic_vector(22 to 23) := "UX"; + constant ascending_sulv : std_ulogic_vector(22 to 23) := "UX"; + constant descending_slv : std_logic_vector(9 downto 1) := "000111UUU"; + constant descending_sulv : std_ulogic_vector(9 downto 1) := "000111UUU"; +begin + main : process + variable queue, another_queue, queue_copy : queue_t; + variable bv : bit_vector(0 to 5); + variable integer_vector_ptr, integer_vector_ptr_copy : integer_vector_ptr_t; + variable string_ptr, string_ptr_copy : string_ptr_t; + variable integer_array, integer_array_copy: integer_array_t; + begin + test_runner_setup(runner, runner_cfg); + if run("Test default queue is null") then + assert queue = null_queue report "Expected null queue"; + + elsif run("Test is_empty") then + queue := new_queue; + check(is_empty(queue), "Empty queue"); + + push_integer(queue, 11); + check_false(is_empty(queue), "Empty queue"); + + check_equal(pop_integer(queue), 11, "data"); + check(is_empty(queue), "Empty queue"); + + elsif run("Test push and pop integer") then + queue := new_queue; + assert queue /= null_queue report "Expected non null queue"; + check_equal(length(queue), 0, "Empty queue length"); + + push_integer(queue, 11); + check_equal(length(queue), 5, "Length"); + push_integer(queue, 22); + check_equal(length(queue), 10, "Length"); + + check_equal(pop_integer(queue), 11, "data"); + check_equal(length(queue), 5, "Length"); + check_equal(pop_integer(queue), 22, "data"); + check_equal(length(queue), 0, "Length"); + + push_integer(queue, integer'low); + check_equal(pop_integer(queue), integer'low, "data"); + push_integer(queue, integer'high); + check_equal(pop_integer(queue), integer'high, "data"); + + elsif run("Test push and pop byte") then + queue := new_queue; + push_byte(queue, 11); + push_byte(queue, 22); + check_equal(pop_byte(queue), 11, "data"); + check_equal(pop_byte(queue), 22, "data"); + + elsif run("Test push and pop boolean") then + queue := new_queue; + push_boolean(queue, false); + push_boolean(queue, true); + check_false(pop_boolean(queue), "data"); + check_true(pop_boolean(queue), "data"); + + elsif run("Test push and pop character") then + queue := new_queue; + assert queue /= null_queue report "Expected non null queue"; + check_equal(length(queue), 0, "Empty queue length"); + + push_character(queue, '1'); + check_equal(length(queue), 2, "Length"); + push_character(queue, '2'); + check_equal(length(queue), 4, "Length"); + + assert pop_character(queue) = '1'; + check_equal(length(queue), 2, "Length"); + assert pop_character(queue) = '2'; + check_equal(length(queue), 0, "Length"); + + elsif run("Test flush queue") then + queue := new_queue; + push_character(queue, '1'); + check_equal(length(queue), 2, "Length"); + push_character(queue, '2'); + check_equal(length(queue), 4, "Length"); + flush(queue); + check_equal(length(queue), 0, "Length"); + + elsif run("Test push and pop queue_t") then + queue := new_queue; + another_queue := new_queue; + push(another_queue, 22); + queue_copy := another_queue; + push_queue_ref(queue, another_queue); + assert another_queue = null_queue report "Ownership was transfered"; + assert pop_queue_ref(queue) = queue_copy report "Queue should come back"; + + elsif run("Test push and pop string") then + queue := new_queue; + push_string(queue, "hello world"); + push_string(queue, "two"); + push_string(queue, test_string1); + push_string(queue, test_string2); + assert pop_string(queue) = "hello world"; + assert pop_string(queue) = "two"; + assert pop_string(queue) = test_string1; + assert pop_string(queue) = test_string2; + + elsif run("Test push and pop bit") then + queue := new_queue; + push_bit(queue, '0'); + push_bit(queue, '1'); + check(pop_bit(queue) = '0'); + check(pop_bit(queue) = '1'); + + elsif run("Test push and pop std_ulogic") then + queue := new_queue; + push_std_ulogic(queue, 'U'); + push_std_ulogic(queue, 'X'); + push_std_ulogic(queue, '0'); + push_std_ulogic(queue, '1'); + push_std_ulogic(queue, 'Z'); + push_std_ulogic(queue, 'W'); + push_std_ulogic(queue, 'L'); + push_std_ulogic(queue, 'H'); + push_std_ulogic(queue, '-'); + assert pop_std_ulogic(queue) = 'U'; + assert pop_std_ulogic(queue) = 'X'; + assert pop_std_ulogic(queue) = '0'; + assert pop_std_ulogic(queue) = '1'; + assert pop_std_ulogic(queue) = 'Z'; + assert pop_std_ulogic(queue) = 'W'; + assert pop_std_ulogic(queue) = 'L'; + assert pop_std_ulogic(queue) = 'H'; + assert pop_std_ulogic(queue) = '-'; + + elsif run("Test push and pop severity_level") then + queue := new_queue; + push_severity_level(queue, note); + push_severity_level(queue, error); + check(pop_severity_level(queue) = note); + check(pop_severity_level(queue) = error); + + elsif run("Test push and pop file_open_status") then + queue := new_queue; + push_file_open_status(queue, open_ok); + push_file_open_status(queue, mode_error); + check(pop_file_open_status(queue) = open_ok); + check(pop_file_open_status(queue) = mode_error); + + elsif run("Test push and pop file_open_kind") then + queue := new_queue; + push_file_open_kind(queue, read_mode); + push_file_open_kind(queue, append_mode); + check(pop_file_open_kind(queue) = read_mode); + check(pop_file_open_kind(queue) = append_mode); + + elsif run("Test push and pop bit_vector") then + queue := new_queue; + push_bit_vector(queue, "1"); + push_bit_vector(queue, "010101"); + bv(0 to 0) := pop_bit_vector(queue); + check(bv(0) = '1'); + bv := pop_bit_vector(queue); + check(bv = "010101"); + + elsif run("Test push and pop std_ulogic_vector") then + queue := new_queue; + push_std_ulogic_vector(queue, descending_sulv); + push_std_ulogic_vector(queue, ascending_sulv); + assert pop_std_ulogic_vector(queue) = descending_sulv; + assert pop_std_ulogic_vector(queue) = ascending_sulv; + + elsif run("Test push and pop complex") then + queue := new_queue; + push_complex(queue, (1.0, 2.2)); + push_complex(queue, (-1.0, -2.2)); + check(pop_complex(queue) = (1.0, 2.2)); + check(pop_complex(queue) = (-1.0, -2.2)); + + elsif run("Test push and pop complex_polar") then + queue := new_queue; + push_complex_polar(queue, (1.0, 0.707)); + push_complex_polar(queue, (3.14, -0.707)); + check(pop_complex_polar(queue) = (1.0, 0.707)); + check(pop_complex_polar(queue) = (3.14, -0.707)); + + elsif run("Test push and pop ieee.numeric_bit.unsigned") then + queue := new_queue; + push_numeric_bit_unsigned(queue, "1"); + push_numeric_bit_unsigned(queue, "010101"); + check(pop_numeric_bit_unsigned(queue) = "1"); + check(pop_numeric_bit_unsigned(queue) = "010101"); + + elsif run("Test push and pop ieee.numeric_bit.signed") then + queue := new_queue; + push_numeric_bit_signed(queue, "1"); + push_numeric_bit_signed(queue, "010101"); + check(pop_numeric_bit_signed(queue) = "1"); + check(pop_numeric_bit_signed(queue) = "010101"); + + elsif run("Test push and pop ieee.numeric_std.unsigned") then + queue := new_queue; + push_numeric_std_unsigned(queue, "1"); + push_numeric_std_unsigned(queue, "010101"); + check(pop_numeric_std_unsigned(queue) = "1"); + check(pop_numeric_std_unsigned(queue) = "010101"); + + elsif run("Test push and pop ieee.numeric_std.signed") then + queue := new_queue; + push_numeric_std_signed(queue, "1"); + push_numeric_std_signed(queue, "010101"); + check(pop_numeric_std_signed(queue) = "1"); + check(pop_numeric_std_signed(queue) = "010101"); + + elsif run("Test push and pop std_logic_vector") then + queue := new_queue; + push_std_ulogic_vector(queue, std_ulogic_vector(descending_slv)); + push_std_ulogic_vector(queue, std_ulogic_vector(ascending_slv)); + assert std_logic_vector(pop_std_ulogic_vector(queue)) = descending_slv; + assert std_logic_vector(pop_std_ulogic_vector(queue)) = ascending_slv; + + elsif run("Test push and pop signed and unsigned") then + queue := new_queue; + push_std_ulogic_vector(queue, std_ulogic_vector(ieee.numeric_std.to_unsigned(11, 16))); + push_std_ulogic_vector(queue, std_ulogic_vector(ieee.numeric_std.to_signed(-1, 8))); + assert ieee.numeric_std.unsigned(pop_std_ulogic_vector(queue)) = ieee.numeric_std.to_unsigned(11, 16); + assert ieee.numeric_std.signed(pop_std_ulogic_vector(queue)) = ieee.numeric_std.to_signed(-1, 8); + + elsif run("Test push and pop real") then + queue := new_queue; + push_real(queue, 1.0); + push_real(queue, -1.0); + push_real(queue, 2.0); + push_real(queue, 0.5); + push_real(queue, -0.5); + assert pop_real(queue) = 1.0; + assert pop_real(queue) = -1.0; + assert pop_real(queue) = 2.0; + assert pop_real(queue) = 0.5; + assert pop_real(queue) = -0.5; + + push_real(queue, 1.0 + 0.5**23); -- Single + push_real(queue, -(1.0 + 0.5**23)); -- Single + push_real(queue, 1.0 + 0.5**53); -- Double + push_real(queue, -(1.0 + 0.5**53)); -- Double + assert pop_real(queue) = 1.0 + 0.5**23; + assert pop_real(queue) = -(1.0 + 0.5**23); + assert pop_real(queue) = 1.0 + 0.5**53; + assert pop_real(queue) = -(1.0 + 0.5**53); + + elsif run("Test push and pop time") then + queue := new_queue; + push_time(queue, 1 fs); + push_time(queue, 1 ps); + push_time(queue, 1 ns); + push_time(queue, 1 ms); + push_time(queue, 1 sec); + push_time(queue, 1 min); + push_time(queue, 1 hr); + assert pop_time(queue) = 1 fs; + assert pop_time(queue) = 1 ps; + assert pop_time(queue) = 1 ns; + assert pop_time(queue) = 1 ms; + assert pop_time(queue) = 1 sec; + assert pop_time(queue) = 1 min; + assert pop_time(queue) = 1 hr; + + push_time(queue, 1 hr + 1 fs); + assert pop_time(queue) = 1 hr + 1 fs; + + push_time(queue, -1 fs); + push_time(queue, -1 hr); + push_time(queue, -1 hr - 1 fs); + assert pop_time(queue) = -1 fs; + assert pop_time(queue) = -1 hr; + assert pop_time(queue) = -1 hr - 1 fs; + + elsif run("Test push and pop integer_vector_ptr_t") then + queue := new_queue; + integer_vector_ptr := new_integer_vector_ptr; + integer_vector_ptr_copy := integer_vector_ptr; + push_integer_vector_ptr_ref(queue, integer_vector_ptr); + assert integer_vector_ptr = null_ptr report "Ownership was transfered"; + assert pop_integer_vector_ptr_ref(queue) = integer_vector_ptr_copy; + + elsif run("Test push and pop string_ptr_t") then + queue := new_queue; + string_ptr := new_string_ptr; + string_ptr_copy := string_ptr; + push_string_ptr_ref(queue, string_ptr); + assert string_ptr = null_string_ptr report "Ownership was transfered"; + assert pop_string_ptr_ref(queue) = string_ptr_copy; + + elsif run("Test push and pop integer_array_t") then + queue := new_queue; + integer_array := new_3d(1, 2, 3, 4); + integer_array_copy := integer_array; + push_integer_array_t_ref(queue, integer_array); + assert integer_array = null_integer_array; + assert pop_integer_array_t_ref(queue) = integer_array_copy; + + elsif run("Test codecs") then + queue := new_queue; + check(decode(encode(queue)) = queue); + + another_queue := new_queue; + push_string(another_queue, "hello world"); + push_real(another_queue, 1.0); + check(decode(encode(another_queue)) = another_queue); + end if; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/vhdl/data_types/test/tb_queue_pool.vhd b/vunit/vhdl/data_types/test/tb_queue_pool.vhd index 43f90b4e9..86470d96e 100644 --- a/vunit/vhdl/data_types/test/tb_queue_pool.vhd +++ b/vunit/vhdl/data_types/test/tb_queue_pool.vhd @@ -1,47 +1,47 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; ---context vunit_lib.vunit_context; -use vunit_lib.check_pkg.all; -use vunit_lib.run_pkg.all; - - -use work.queue_pkg.all; -use work.queue_pool_pkg.all; - - -entity tb_queue_pool is - generic (runner_cfg : string); -end entity; - -architecture a of tb_queue_pool is -begin - main : process - variable pool : queue_pool_t; - variable queue : queue_t; - begin - test_runner_setup(runner, runner_cfg); - if run("Test default pool is null") then - assert pool = null_queue_pool report "Expected null pool"; - - elsif run("Test allocated queue is empty") then - pool := new_queue_pool; - queue := new_queue(pool); - assert queue /= null_queue report "Expected non null queue"; - check_equal(length(queue), 0); - recycle(pool, queue); - - elsif run("Test recycled queue is null") then - pool := new_queue_pool; - queue := new_queue(pool); - assert queue /= null_queue report "Expected non null queue"; - recycle(pool, queue); - assert queue = null_queue report "Expected null queue"; - end if; - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +--context vunit_lib.vunit_context; +use vunit_lib.check_pkg.all; +use vunit_lib.run_pkg.all; + + +use work.queue_pkg.all; +use work.queue_pool_pkg.all; + + +entity tb_queue_pool is + generic (runner_cfg : string); +end entity; + +architecture a of tb_queue_pool is +begin + main : process + variable pool : queue_pool_t; + variable queue : queue_t; + begin + test_runner_setup(runner, runner_cfg); + if run("Test default pool is null") then + assert pool = null_queue_pool report "Expected null pool"; + + elsif run("Test allocated queue is empty") then + pool := new_queue_pool; + queue := new_queue(pool); + assert queue /= null_queue report "Expected non null queue"; + check_equal(length(queue), 0); + recycle(pool, queue); + + elsif run("Test recycled queue is null") then + pool := new_queue_pool; + queue := new_queue(pool); + assert queue /= null_queue report "Expected non null queue"; + recycle(pool, queue); + assert queue = null_queue report "Expected null queue"; + end if; + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/vhdl/data_types/test/tb_string_ptr.vhd b/vunit/vhdl/data_types/test/tb_string_ptr.vhd index fe1bdd045..be4a8b9f5 100644 --- a/vunit/vhdl/data_types/test/tb_string_ptr.vhd +++ b/vunit/vhdl/data_types/test/tb_string_ptr.vhd @@ -1,131 +1,131 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; ---context vunit_lib.vunit_context; -use vunit_lib.check_pkg.all; -use vunit_lib.run_pkg.all; - -use work.string_ptr_pkg.all; - -entity tb_string_ptr is - generic (runner_cfg : string); -end; - -architecture a of tb_string_ptr is -begin - main : process - variable ptr, ptr2 : string_ptr_t; - constant a_random_value : character := '7'; - constant another_random_value : character := '9'; - constant denormal_string : string(8 to 9) := "ab"; - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("test_new_string_ptr") then - ptr := new_string_ptr; - check_equal(length(ptr), 0); - - ptr := new_string_ptr(1); - check_equal(length(ptr), 1); - - ptr := new_string_ptr(2); - check_equal(length(ptr), 2); - - elsif run("test_new_string_ptr_with_string") then - ptr := new_string_ptr("hello"); - check_equal(length(ptr), 5); - check_equal(to_string(ptr), "hello"); - - elsif run("test_reallocate_string") then - ptr := new_string_ptr("hello"); - check_equal(length(ptr), 5); - check_equal(to_string(ptr), "hello"); - - reallocate(ptr, "foobar"); - check_equal(length(ptr), 6); - check_equal(to_string(ptr), "foobar"); - - elsif run("test_element_access") then - ptr := new_string_ptr(1); - set(ptr, 1, a_random_value); - assert get(ptr, 1) = a_random_value; - - ptr2 := new_string_ptr(2); - set(ptr2, 1, another_random_value); - set(ptr2, 2, a_random_value); - assert get(ptr2, 1) = another_random_value; - assert get(ptr2, 2) = a_random_value; - - assert get(ptr, 1) = a_random_value report - "Checking that ptr was not affected by ptr2"; - - elsif run("test_resize") then - ptr := new_string_ptr(1); - check_equal(length(ptr), 1); - set(ptr, 1, a_random_value); - assert get(ptr, 1) = a_random_value; - - resize(ptr, 2); - check_equal(length(ptr), 2); - set(ptr, 2, another_random_value); - assert get(ptr, 1) = a_random_value report - "Checking that resized ptr still contain old value"; - assert get(ptr, 2) = another_random_value; - - resize(ptr, 1); - check_equal(length(ptr), 1); - assert get(ptr, 1) = a_random_value report - "Checking that shrunk ptr still contain old value"; - - elsif run("test_resize_with_drop") then - - ptr := new_string_ptr(8); - for i in 1 to 8 loop - set(ptr, i, character'val(i)); - end loop; - resize(ptr, 4, drop => 4); - - for i in 1 to 4 loop - assert get(ptr, i) = character'val(4+i); - end loop; - - elsif run("test_from_and_to_integer") then - ptr := new_string_ptr(2); - assert to_string_ptr(to_integer(ptr)) = ptr; - - elsif run("test_to_string") then - ptr := new_string_ptr(3); - set(ptr, 1, 'a'); - set(ptr, 2, 'b'); - set(ptr, 3, 'c'); - assert to_string(ptr) = "abc"; - resize(ptr, 4); - set(ptr, 4, '1'); - assert to_string(ptr) = "abc1"; - - elsif run("Test codecs") then - ptr := new_string_ptr(0); - check(decode(encode(ptr)) = ptr); - - ptr2 := new_string_ptr(2); - set(ptr2, 1, another_random_value); - set(ptr2, 2, a_random_value); - check(decode(encode(ptr2)) = ptr2); - - elsif run("Test denormal string") then - ptr := new_string_ptr(denormal_string); - check_equal(to_string(ptr), "ab"); - - reallocate(ptr, denormal_string); - check_equal(to_string(ptr), "ab"); - end if; - end loop; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +--context vunit_lib.vunit_context; +use vunit_lib.check_pkg.all; +use vunit_lib.run_pkg.all; + +use work.string_ptr_pkg.all; + +entity tb_string_ptr is + generic (runner_cfg : string); +end; + +architecture a of tb_string_ptr is +begin + main : process + variable ptr, ptr2 : string_ptr_t; + constant a_random_value : character := '7'; + constant another_random_value : character := '9'; + constant denormal_string : string(8 to 9) := "ab"; + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("test_new_string_ptr") then + ptr := new_string_ptr; + check_equal(length(ptr), 0); + + ptr := new_string_ptr(1); + check_equal(length(ptr), 1); + + ptr := new_string_ptr(2); + check_equal(length(ptr), 2); + + elsif run("test_new_string_ptr_with_string") then + ptr := new_string_ptr("hello"); + check_equal(length(ptr), 5); + check_equal(to_string(ptr), "hello"); + + elsif run("test_reallocate_string") then + ptr := new_string_ptr("hello"); + check_equal(length(ptr), 5); + check_equal(to_string(ptr), "hello"); + + reallocate(ptr, "foobar"); + check_equal(length(ptr), 6); + check_equal(to_string(ptr), "foobar"); + + elsif run("test_element_access") then + ptr := new_string_ptr(1); + set(ptr, 1, a_random_value); + assert get(ptr, 1) = a_random_value; + + ptr2 := new_string_ptr(2); + set(ptr2, 1, another_random_value); + set(ptr2, 2, a_random_value); + assert get(ptr2, 1) = another_random_value; + assert get(ptr2, 2) = a_random_value; + + assert get(ptr, 1) = a_random_value report + "Checking that ptr was not affected by ptr2"; + + elsif run("test_resize") then + ptr := new_string_ptr(1); + check_equal(length(ptr), 1); + set(ptr, 1, a_random_value); + assert get(ptr, 1) = a_random_value; + + resize(ptr, 2); + check_equal(length(ptr), 2); + set(ptr, 2, another_random_value); + assert get(ptr, 1) = a_random_value report + "Checking that resized ptr still contain old value"; + assert get(ptr, 2) = another_random_value; + + resize(ptr, 1); + check_equal(length(ptr), 1); + assert get(ptr, 1) = a_random_value report + "Checking that shrunk ptr still contain old value"; + + elsif run("test_resize_with_drop") then + + ptr := new_string_ptr(8); + for i in 1 to 8 loop + set(ptr, i, character'val(i)); + end loop; + resize(ptr, 4, drop => 4); + + for i in 1 to 4 loop + assert get(ptr, i) = character'val(4+i); + end loop; + + elsif run("test_from_and_to_integer") then + ptr := new_string_ptr(2); + assert to_string_ptr(to_integer(ptr)) = ptr; + + elsif run("test_to_string") then + ptr := new_string_ptr(3); + set(ptr, 1, 'a'); + set(ptr, 2, 'b'); + set(ptr, 3, 'c'); + assert to_string(ptr) = "abc"; + resize(ptr, 4); + set(ptr, 4, '1'); + assert to_string(ptr) = "abc1"; + + elsif run("Test codecs") then + ptr := new_string_ptr(0); + check(decode(encode(ptr)) = ptr); + + ptr2 := new_string_ptr(2); + set(ptr2, 1, another_random_value); + set(ptr2, 2, a_random_value); + check(decode(encode(ptr2)) = ptr2); + + elsif run("Test denormal string") then + ptr := new_string_ptr(denormal_string); + check_equal(to_string(ptr), "ab"); + + reallocate(ptr, denormal_string); + check_equal(to_string(ptr), "ab"); + end if; + end loop; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/vhdl/data_types/test/tb_string_ptr_pool.vhd b/vunit/vhdl/data_types/test/tb_string_ptr_pool.vhd index a5c76fd94..120793825 100644 --- a/vunit/vhdl/data_types/test/tb_string_ptr_pool.vhd +++ b/vunit/vhdl/data_types/test/tb_string_ptr_pool.vhd @@ -1,66 +1,66 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; ---context vunit_lib.vunit_context; -use vunit_lib.check_pkg.all; -use vunit_lib.run_pkg.all; - -use work.string_ptr_pkg.all; -use work.string_ptr_pool_pkg.all; - - -entity tb_string_ptr_pool is - generic (runner_cfg : string); -end entity; - -architecture a of tb_string_ptr_pool is -begin - main : process - variable pool : string_ptr_pool_t; - variable ptr, old_ptr : string_ptr_t; - begin - test_runner_setup(runner, runner_cfg); - - if run("Test default pool is null") then - assert pool = null_string_ptr_pool report "Expected null pool"; - - elsif run("Test new ptr has length") then - pool := new_string_ptr_pool; - ptr := new_string_ptr(pool, 77); - assert ptr /= null_string_ptr report "Expected non null ptr"; - check_equal(length(ptr), 77); - recycle(pool, ptr); - - elsif run("Test allocate string recycled") then - pool := new_string_ptr_pool; - ptr := new_string_ptr(pool, "hello"); - check_equal(to_string(ptr), "hello"); - old_ptr := ptr; - recycle(pool, ptr); - ptr := new_string_ptr(pool, "foobar"); - check_equal(to_string(ptr), "foobar"); - assert ptr = old_ptr report "Was recycled"; - - elsif run("Test recycled ptr is null") then - pool := new_string_ptr_pool; - ptr := new_string_ptr(pool); - assert ptr /= null_string_ptr report "Expected non null ptr"; - recycle(pool, ptr); - assert ptr = null_string_ptr report "Expected null ptr"; - - elsif run("Test ptr is recycled") then - pool := new_string_ptr_pool; - ptr := new_string_ptr(pool, 2); - old_ptr := ptr; - recycle(pool, ptr); - ptr := new_string_ptr(pool, 2); - assert ptr = old_ptr report "Was recycled"; - end if; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +--context vunit_lib.vunit_context; +use vunit_lib.check_pkg.all; +use vunit_lib.run_pkg.all; + +use work.string_ptr_pkg.all; +use work.string_ptr_pool_pkg.all; + + +entity tb_string_ptr_pool is + generic (runner_cfg : string); +end entity; + +architecture a of tb_string_ptr_pool is +begin + main : process + variable pool : string_ptr_pool_t; + variable ptr, old_ptr : string_ptr_t; + begin + test_runner_setup(runner, runner_cfg); + + if run("Test default pool is null") then + assert pool = null_string_ptr_pool report "Expected null pool"; + + elsif run("Test new ptr has length") then + pool := new_string_ptr_pool; + ptr := new_string_ptr(pool, 77); + assert ptr /= null_string_ptr report "Expected non null ptr"; + check_equal(length(ptr), 77); + recycle(pool, ptr); + + elsif run("Test allocate string recycled") then + pool := new_string_ptr_pool; + ptr := new_string_ptr(pool, "hello"); + check_equal(to_string(ptr), "hello"); + old_ptr := ptr; + recycle(pool, ptr); + ptr := new_string_ptr(pool, "foobar"); + check_equal(to_string(ptr), "foobar"); + assert ptr = old_ptr report "Was recycled"; + + elsif run("Test recycled ptr is null") then + pool := new_string_ptr_pool; + ptr := new_string_ptr(pool); + assert ptr /= null_string_ptr report "Expected non null ptr"; + recycle(pool, ptr); + assert ptr = null_string_ptr report "Expected null ptr"; + + elsif run("Test ptr is recycled") then + pool := new_string_ptr_pool; + ptr := new_string_ptr(pool, 2); + old_ptr := ptr; + recycle(pool, ptr); + ptr := new_string_ptr(pool, 2); + assert ptr = old_ptr report "Was recycled"; + end if; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/vhdl/dictionary/run.py b/vunit/vhdl/dictionary/run.py index 7c8facb9d..a8e4991ac 100644 --- a/vunit/vhdl/dictionary/run.py +++ b/vunit/vhdl/dictionary/run.py @@ -1,15 +1,15 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -root = dirname(__file__) - -ui = VUnit.from_argv() -lib = ui.add_library("lib") -lib.add_source_files(join(root, "test", "*.vhd")) -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +root = dirname(__file__) + +ui = VUnit.from_argv() +lib = ui.add_library("lib") +lib.add_source_files(join(root, "test", "*.vhd")) +ui.main() diff --git a/vunit/vhdl/dictionary/src/dictionary.vhd b/vunit/vhdl/dictionary/src/dictionary.vhd index 91f893e1e..48fcef35a 100644 --- a/vunit/vhdl/dictionary/src/dictionary.vhd +++ b/vunit/vhdl/dictionary/src/dictionary.vhd @@ -1,132 +1,132 @@ --- This package provides a dictionary types and operations --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.string_ops.all; -use work.logger_pkg.all; -use std.textio.all; - -package dictionary is - subtype frozen_dictionary_t is string; - constant empty : frozen_dictionary_t := ""; - -- Deprecated - constant empty_c : frozen_dictionary_t := empty; - - function len ( - constant d : frozen_dictionary_t) - return natural; - - impure function get ( - constant d : frozen_dictionary_t; - constant key : string) - return string; - - impure function has_key ( - constant d : frozen_dictionary_t; - constant key : string) - return boolean; - - impure function get ( - d : frozen_dictionary_t; - key : string; - default_value : string) - return string; - - constant dictionary_logger : logger_t := get_logger("vunit_lib:dictionary"); - -end package dictionary; - -package body dictionary is - function len ( - constant d : frozen_dictionary_t) - return natural is - begin - return count(replace(d, "::", "__escaped_colon__"), ":"); - end; - - type dictionary_status_t is (valid_value, key_error, corrupt_dictionary); - - procedure get ( - constant d : in frozen_dictionary_t; - constant key : in string; - variable value : inout line; - variable status : out dictionary_status_t) is - variable key_value_pairs, key_value_pair : lines_t; - begin - if value /= null then - deallocate(value); - end if; - - if len(d) = 0 then - status := key_error; - return; - end if; - - key_value_pairs := split(replace(d, ",,", "__escaped_comma__"), ","); - for i in key_value_pairs'range loop - key_value_pair := split(replace(key_value_pairs(i).all, "::", "__escaped_colon__"), ":"); - if key_value_pair'length = 2 then - if strip(replace(replace(key_value_pair(0).all, "__escaped_comma__", ','), "__escaped_colon__", ':')) = strip(key) then - status := valid_value; - write(value, strip(replace(replace(key_value_pair(1).all, "__escaped_comma__", ','), "__escaped_colon__", ':'))); - return; - end if; - else - failure(dictionary_logger, "Corrupt frozen dictionary item """ & key_value_pairs(i).all & """ in """ & d & """."); - write(value, string'("will return when log is mocked out during unit test.")); - return; - end if; - - end loop; - - status := key_error; - return; - end procedure get; - - impure function get ( - constant d : frozen_dictionary_t; - constant key : string) - return string is - variable value : line; - variable status : dictionary_status_t; - begin - get(d, key, value, status); - if status = valid_value then - return value.all; - else - failure(dictionary_logger, "Key error! """ & key & """ wasn't found in """ & d & """."); - return "will return when log is mocked out during unit test."; - end if; - - end; - - impure function has_key ( - constant d : frozen_dictionary_t; - constant key : string) - return boolean is - variable value : line; - variable status : dictionary_status_t; - begin - get(d, key, value, status); - return status = valid_value; - end; - - impure function get ( - d : frozen_dictionary_t; - key : string; - default_value : string) - return string is - begin - if (has_key(d, key) = True) then - return get(d, key); - else - return default_value; - end if; - end function get; - - -end package body dictionary; +-- This package provides a dictionary types and operations +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.string_ops.all; +use work.logger_pkg.all; +use std.textio.all; + +package dictionary is + subtype frozen_dictionary_t is string; + constant empty : frozen_dictionary_t := ""; + -- Deprecated + constant empty_c : frozen_dictionary_t := empty; + + function len ( + constant d : frozen_dictionary_t) + return natural; + + impure function get ( + constant d : frozen_dictionary_t; + constant key : string) + return string; + + impure function has_key ( + constant d : frozen_dictionary_t; + constant key : string) + return boolean; + + impure function get ( + d : frozen_dictionary_t; + key : string; + default_value : string) + return string; + + constant dictionary_logger : logger_t := get_logger("vunit_lib:dictionary"); + +end package dictionary; + +package body dictionary is + function len ( + constant d : frozen_dictionary_t) + return natural is + begin + return count(replace(d, "::", "__escaped_colon__"), ":"); + end; + + type dictionary_status_t is (valid_value, key_error, corrupt_dictionary); + + procedure get ( + constant d : in frozen_dictionary_t; + constant key : in string; + variable value : inout line; + variable status : out dictionary_status_t) is + variable key_value_pairs, key_value_pair : lines_t; + begin + if value /= null then + deallocate(value); + end if; + + if len(d) = 0 then + status := key_error; + return; + end if; + + key_value_pairs := split(replace(d, ",,", "__escaped_comma__"), ","); + for i in key_value_pairs'range loop + key_value_pair := split(replace(key_value_pairs(i).all, "::", "__escaped_colon__"), ":"); + if key_value_pair'length = 2 then + if strip(replace(replace(key_value_pair(0).all, "__escaped_comma__", ','), "__escaped_colon__", ':')) = strip(key) then + status := valid_value; + write(value, strip(replace(replace(key_value_pair(1).all, "__escaped_comma__", ','), "__escaped_colon__", ':'))); + return; + end if; + else + failure(dictionary_logger, "Corrupt frozen dictionary item """ & key_value_pairs(i).all & """ in """ & d & """."); + write(value, string'("will return when log is mocked out during unit test.")); + return; + end if; + + end loop; + + status := key_error; + return; + end procedure get; + + impure function get ( + constant d : frozen_dictionary_t; + constant key : string) + return string is + variable value : line; + variable status : dictionary_status_t; + begin + get(d, key, value, status); + if status = valid_value then + return value.all; + else + failure(dictionary_logger, "Key error! """ & key & """ wasn't found in """ & d & """."); + return "will return when log is mocked out during unit test."; + end if; + + end; + + impure function has_key ( + constant d : frozen_dictionary_t; + constant key : string) + return boolean is + variable value : line; + variable status : dictionary_status_t; + begin + get(d, key, value, status); + return status = valid_value; + end; + + impure function get ( + d : frozen_dictionary_t; + key : string; + default_value : string) + return string is + begin + if (has_key(d, key) = True) then + return get(d, key); + else + return default_value; + end if; + end function get; + + +end package body dictionary; diff --git a/vunit/vhdl/dictionary/test/tb_dictionary.vhd b/vunit/vhdl/dictionary/test/tb_dictionary.vhd index 1c6087dc3..668bd074e 100644 --- a/vunit/vhdl/dictionary/test/tb_dictionary.vhd +++ b/vunit/vhdl/dictionary/test/tb_dictionary.vhd @@ -1,86 +1,86 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use vunit_lib.dictionary.all; -use std.textio.all; - -entity tb_dictionary is - generic ( - runner_cfg : string; - output_path : string); -end entity tb_dictionary; - -architecture test_fixture of tb_dictionary is -begin - - test_runner : process - variable value : line; - variable stat : checker_stat_t; - variable passed : boolean; - constant empty_dict : frozen_dictionary_t := empty; - constant test_dict : frozen_dictionary_t := "output path : c::\foo\bar, input path : c::\ying\yang, active python runner : true"; - constant corrupt_dict : frozen_dictionary_t := "output path : c::\foo\bar, input path, active python runner : true"; - begin - - test_runner_setup(runner, runner_cfg); - while test_suite loop - if run("Test that an empty frozen dictionary has zero length") then - check(len(empty_dict) = 0, "An empty frozen directory should be of zero length (got " & natural'image(len(empty_dict)) & ")."); - - elsif run("Test that a non-empty frozen dictionary has correct length") then - check(len(test_dict) = 3, "Expected length of test dictionary to be 3 (got " & natural'image(len(test_dict)) & ")."); - - elsif run("Test that the existence of a key can be queried") then - check(has_key(test_dict, "input path"), "Should find ""input path"" in dictionary"); - check(has_key(test_dict, " active python runner "), "Should strip key before searching for it in the dictionary"); - check_false(has_key(test_dict, "input_path"), "Shouldn't find ""input_path"" in dictionary"); - - elsif run("Test that getting a non-existing key from a frozen dictionary results in an assertion") then - mock(dictionary_logger); - write(value, get(empty_dict, "some_key")); - check_only_log(dictionary_logger, "Key error! ""some_key"" wasn't found in """".", failure); - unmock(dictionary_logger); - - elsif run("Test getting an existing key from a frozen dictionary") then - passed := get(test_dict, "input path") = "c:\ying\yang"; - check(passed, "Expected ""c:\ying\yang"" when getting input path key from test dictionary (got """ & get(test_dict, "input path") & """)."); - passed := get(test_dict, "output path") = "c:\foo\bar"; - check(passed, "Expected ""c:\foo\bar"" when getting ""output path"" key from test dictionary (got """ & get(test_dict, "input path") & """)."); - passed := get(test_dict, " output path ") = "c:\foo\bar"; - check(passed, "Expected ""c:\foo\bar"" when getting "" output path "" key from test dictionary (got """ & get(test_dict, "input path") & """)."); - - elsif run("Test that a corrupted directory results in an assertion") then - mock(dictionary_logger); - write(value, get(corrupt_dict, "input path")); - check_only_log(dictionary_logger, - "Corrupt frozen dictionary item "" input path"" in ""output path : c::\foo\bar, input path, active python runner : true"".", - failure); - unmock(dictionary_logger); - - elsif run("Test that get with default value returns value for existing key") then - passed := get(test_dict, "input path", "banana") = "c:\ying\yang"; - check(passed, "Expected ""c:\ying\yang"" when getting input path key from test dictionary (got """ & get(test_dict, "input path", "banana") & """)."); - - elsif run("Test that get with default value returns default value for non-existing key") then - passed := get(test_dict, "meatballs", "falafel") = "falafel"; - check(passed, "Expected ""falafel"" when getting meatballs key from test dictionary (got """ & get(test_dict, "meatballs", "falafel") & """)."); - end if; - end loop; - - reset_checker_stat; - test_runner_cleanup(runner); - wait; - end process; - - test_runner_watchdog(runner, 1 ns); -end test_fixture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use vunit_lib.dictionary.all; +use std.textio.all; + +entity tb_dictionary is + generic ( + runner_cfg : string; + output_path : string); +end entity tb_dictionary; + +architecture test_fixture of tb_dictionary is +begin + + test_runner : process + variable value : line; + variable stat : checker_stat_t; + variable passed : boolean; + constant empty_dict : frozen_dictionary_t := empty; + constant test_dict : frozen_dictionary_t := "output path : c::\foo\bar, input path : c::\ying\yang, active python runner : true"; + constant corrupt_dict : frozen_dictionary_t := "output path : c::\foo\bar, input path, active python runner : true"; + begin + + test_runner_setup(runner, runner_cfg); + while test_suite loop + if run("Test that an empty frozen dictionary has zero length") then + check(len(empty_dict) = 0, "An empty frozen directory should be of zero length (got " & natural'image(len(empty_dict)) & ")."); + + elsif run("Test that a non-empty frozen dictionary has correct length") then + check(len(test_dict) = 3, "Expected length of test dictionary to be 3 (got " & natural'image(len(test_dict)) & ")."); + + elsif run("Test that the existence of a key can be queried") then + check(has_key(test_dict, "input path"), "Should find ""input path"" in dictionary"); + check(has_key(test_dict, " active python runner "), "Should strip key before searching for it in the dictionary"); + check_false(has_key(test_dict, "input_path"), "Shouldn't find ""input_path"" in dictionary"); + + elsif run("Test that getting a non-existing key from a frozen dictionary results in an assertion") then + mock(dictionary_logger); + write(value, get(empty_dict, "some_key")); + check_only_log(dictionary_logger, "Key error! ""some_key"" wasn't found in """".", failure); + unmock(dictionary_logger); + + elsif run("Test getting an existing key from a frozen dictionary") then + passed := get(test_dict, "input path") = "c:\ying\yang"; + check(passed, "Expected ""c:\ying\yang"" when getting input path key from test dictionary (got """ & get(test_dict, "input path") & """)."); + passed := get(test_dict, "output path") = "c:\foo\bar"; + check(passed, "Expected ""c:\foo\bar"" when getting ""output path"" key from test dictionary (got """ & get(test_dict, "input path") & """)."); + passed := get(test_dict, " output path ") = "c:\foo\bar"; + check(passed, "Expected ""c:\foo\bar"" when getting "" output path "" key from test dictionary (got """ & get(test_dict, "input path") & """)."); + + elsif run("Test that a corrupted directory results in an assertion") then + mock(dictionary_logger); + write(value, get(corrupt_dict, "input path")); + check_only_log(dictionary_logger, + "Corrupt frozen dictionary item "" input path"" in ""output path : c::\foo\bar, input path, active python runner : true"".", + failure); + unmock(dictionary_logger); + + elsif run("Test that get with default value returns value for existing key") then + passed := get(test_dict, "input path", "banana") = "c:\ying\yang"; + check(passed, "Expected ""c:\ying\yang"" when getting input path key from test dictionary (got """ & get(test_dict, "input path", "banana") & """)."); + + elsif run("Test that get with default value returns default value for non-existing key") then + passed := get(test_dict, "meatballs", "falafel") = "falafel"; + check(passed, "Expected ""falafel"" when getting meatballs key from test dictionary (got """ & get(test_dict, "meatballs", "falafel") & """)."); + end if; + end loop; + + reset_checker_stat; + test_runner_cleanup(runner); + wait; + end process; + + test_runner_watchdog(runner, 1 ns); +end test_fixture; diff --git a/vunit/vhdl/logging/run.py b/vunit/vhdl/logging/run.py index 97f7c4c78..47810c6d5 100644 --- a/vunit/vhdl/logging/run.py +++ b/vunit/vhdl/logging/run.py @@ -1,14 +1,14 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -root = dirname(__file__) -ui = VUnit.from_argv() -lib = ui.library('vunit_lib') -lib.add_source_files(join(root, "test", "*.vhd")) -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +root = dirname(__file__) +ui = VUnit.from_argv() +lib = ui.library('vunit_lib') +lib.add_source_files(join(root, "test", "*.vhd")) +ui.main() diff --git a/vunit/vhdl/logging/src/ansi_pkg.vhd b/vunit/vhdl/logging/src/ansi_pkg.vhd index 3f1e81482..b0dd41205 100644 --- a/vunit/vhdl/logging/src/ansi_pkg.vhd +++ b/vunit/vhdl/logging/src/ansi_pkg.vhd @@ -1,233 +1,233 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use std.textio.all; - -library vunit_lib; -use vunit_lib.string_ptr_pkg.all; - -package ansi_pkg is - - type ansi_color_t is ( - no_color, - - black, - red, - green, - yellow, - blue, - magenta, - cyan, - white, - - -- Non standard foregrounds - lightblack, - lightred, - lightgreen, - lightyellow, - lightblue, - lightmagenta, - lightcyan, - lightwhite - ); - - type ansi_style_t is ( - dim, - normal, - bright - ); - - type ansi_colors_t is record - fg : ansi_color_t; - bg : ansi_color_t; - style : ansi_style_t; - end record; - constant no_colors : ansi_colors_t := (fg => no_color, bg => no_color, style => normal); - - impure function colorize(msg : string; - colors : ansi_colors_t := no_colors) return string; - - impure function colorize(msg : string; - fg : ansi_color_t := no_color; - bg : ansi_color_t := no_color; - style : ansi_style_t := normal) return string; - - impure function strip_color(msg : string) return string; - impure function length_without_color(msg : string) return natural; - - impure function color_start(fg : ansi_color_t := no_color; - bg : ansi_color_t := no_color; - style : ansi_style_t := normal) return string; - - impure function color_start(colors : ansi_colors_t := no_colors) return string; - - impure function color_end return string; - - procedure disable_colors; - procedure enable_colors; - - procedure ansi_color_demo; -end package; - -package body ansi_pkg is - constant colors_enabled : string_ptr_t := new_string_ptr("0"); - - impure function colors_are_enabled return boolean is - begin - return get(colors_enabled, 1) = '1'; - end; - - type color_to_code_t is array (ansi_color_t range <>) of integer; - type style_to_code_t is array (ansi_style_t range <>) of integer; - - constant color_to_code : color_to_code_t := ( - no_color => 39, - black => 30, - red => 31, - green => 32, - yellow => 33, - blue => 34, - magenta => 35, - cyan => 36, - white => 37, - - -- Non standard foregrounds - lightblack => 90, - lightred => 91, - lightgreen => 92, - lightyellow => 93, - lightblue => 94, - lightmagenta => 95, - lightcyan => 96, - lightwhite => 97); - - constant style_to_code : style_to_code_t := ( - bright => 1, - dim => 2, - normal => 22); - - impure function colorize(msg : string; - colors : ansi_colors_t := no_colors) return string is - begin - return colorize(msg, fg => colors.fg, bg => colors.bg, style => colors.style); - end; - - impure function length_without_color(msg : string) return natural is - variable idx : natural := msg'low; - variable len : natural := 0; - begin - while idx <= msg'high loop - if msg(idx) = character'val(27) then - idx := idx + 1; - - while idx <= msg'high and msg(idx) /= 'm' loop - idx := idx + 1; - end loop; - - idx := idx + 1; - else - idx := idx + 1; - len := len + 1; - end if; - end loop; - - return len; - end; - - impure function drop_color(msg : string) return string is - begin - for i in msg'low to msg'high loop - if msg(i) = 'm' then - return strip_color(msg(i+1 to msg'high)); - end if; - end loop; - - assert false report "incomplete color escape did not end with 'm'"; - return msg; - end; - - impure function strip_color(msg : string) return string is - begin - for i in msg'low to msg'high loop - if msg(i) = character'val(27) then - return msg(msg'low to i-1) & drop_color(msg(i+1 to msg'high)); - end if; - end loop; - - return msg; - end; - - impure function colorize(msg : string; - fg : ansi_color_t := no_color; - bg : ansi_color_t := no_color; - style : ansi_style_t := normal) return string is - begin - if fg = no_color and bg = no_color and style = normal then - return msg; - else - return color_start(fg, bg, style) & msg & color_end; - end if; - end; - - impure function color_start(colors : ansi_colors_t := no_colors) return string is - begin - return color_start(fg => colors.fg, bg => colors.bg, style => colors.style); - end; - - impure function color_start(fg : ansi_color_t := no_color; - bg : ansi_color_t := no_color; - style : ansi_style_t := normal) return string is - begin - if colors_are_enabled then - return (character'val(27) & '[' & - integer'image(style_to_code(style)) & ';' & - integer'image(color_to_code(fg)) & ';' & - integer'image(color_to_code(bg)+10) & 'm'); - else - return ""; - end if; - end; - - impure function color_end return string is - begin - if colors_are_enabled then - return character'val(27) & '[' & integer'image(0) & 'm'; - else - return ""; - end if; - end function; - - procedure ansi_color_demo is - variable l : line; - begin - for bg in ansi_color_t'low to ansi_color_t'high loop - write(l, colorize("bg=" & ansi_color_t'image(bg), bg => bg)); - writeline(output, l); - end loop; - - for style in ansi_style_t'low to ansi_style_t'high loop - for fg in ansi_color_t'low to ansi_color_t'high loop - write(l, colorize( - "fg=" & ansi_color_t'image(fg) & ", " & - "style=" & ansi_style_t'image(style), - fg => fg, style => style)); - writeline(output, l); - end loop; - end loop; - - end procedure; - - procedure disable_colors is - begin - set(colors_enabled, 1, '0'); - end; - - procedure enable_colors is - begin - set(colors_enabled, 1, '1'); - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use std.textio.all; + +library vunit_lib; +use vunit_lib.string_ptr_pkg.all; + +package ansi_pkg is + + type ansi_color_t is ( + no_color, + + black, + red, + green, + yellow, + blue, + magenta, + cyan, + white, + + -- Non standard foregrounds + lightblack, + lightred, + lightgreen, + lightyellow, + lightblue, + lightmagenta, + lightcyan, + lightwhite + ); + + type ansi_style_t is ( + dim, + normal, + bright + ); + + type ansi_colors_t is record + fg : ansi_color_t; + bg : ansi_color_t; + style : ansi_style_t; + end record; + constant no_colors : ansi_colors_t := (fg => no_color, bg => no_color, style => normal); + + impure function colorize(msg : string; + colors : ansi_colors_t := no_colors) return string; + + impure function colorize(msg : string; + fg : ansi_color_t := no_color; + bg : ansi_color_t := no_color; + style : ansi_style_t := normal) return string; + + impure function strip_color(msg : string) return string; + impure function length_without_color(msg : string) return natural; + + impure function color_start(fg : ansi_color_t := no_color; + bg : ansi_color_t := no_color; + style : ansi_style_t := normal) return string; + + impure function color_start(colors : ansi_colors_t := no_colors) return string; + + impure function color_end return string; + + procedure disable_colors; + procedure enable_colors; + + procedure ansi_color_demo; +end package; + +package body ansi_pkg is + constant colors_enabled : string_ptr_t := new_string_ptr("0"); + + impure function colors_are_enabled return boolean is + begin + return get(colors_enabled, 1) = '1'; + end; + + type color_to_code_t is array (ansi_color_t range <>) of integer; + type style_to_code_t is array (ansi_style_t range <>) of integer; + + constant color_to_code : color_to_code_t := ( + no_color => 39, + black => 30, + red => 31, + green => 32, + yellow => 33, + blue => 34, + magenta => 35, + cyan => 36, + white => 37, + + -- Non standard foregrounds + lightblack => 90, + lightred => 91, + lightgreen => 92, + lightyellow => 93, + lightblue => 94, + lightmagenta => 95, + lightcyan => 96, + lightwhite => 97); + + constant style_to_code : style_to_code_t := ( + bright => 1, + dim => 2, + normal => 22); + + impure function colorize(msg : string; + colors : ansi_colors_t := no_colors) return string is + begin + return colorize(msg, fg => colors.fg, bg => colors.bg, style => colors.style); + end; + + impure function length_without_color(msg : string) return natural is + variable idx : natural := msg'low; + variable len : natural := 0; + begin + while idx <= msg'high loop + if msg(idx) = character'val(27) then + idx := idx + 1; + + while idx <= msg'high and msg(idx) /= 'm' loop + idx := idx + 1; + end loop; + + idx := idx + 1; + else + idx := idx + 1; + len := len + 1; + end if; + end loop; + + return len; + end; + + impure function drop_color(msg : string) return string is + begin + for i in msg'low to msg'high loop + if msg(i) = 'm' then + return strip_color(msg(i+1 to msg'high)); + end if; + end loop; + + assert false report "incomplete color escape did not end with 'm'"; + return msg; + end; + + impure function strip_color(msg : string) return string is + begin + for i in msg'low to msg'high loop + if msg(i) = character'val(27) then + return msg(msg'low to i-1) & drop_color(msg(i+1 to msg'high)); + end if; + end loop; + + return msg; + end; + + impure function colorize(msg : string; + fg : ansi_color_t := no_color; + bg : ansi_color_t := no_color; + style : ansi_style_t := normal) return string is + begin + if fg = no_color and bg = no_color and style = normal then + return msg; + else + return color_start(fg, bg, style) & msg & color_end; + end if; + end; + + impure function color_start(colors : ansi_colors_t := no_colors) return string is + begin + return color_start(fg => colors.fg, bg => colors.bg, style => colors.style); + end; + + impure function color_start(fg : ansi_color_t := no_color; + bg : ansi_color_t := no_color; + style : ansi_style_t := normal) return string is + begin + if colors_are_enabled then + return (character'val(27) & '[' & + integer'image(style_to_code(style)) & ';' & + integer'image(color_to_code(fg)) & ';' & + integer'image(color_to_code(bg)+10) & 'm'); + else + return ""; + end if; + end; + + impure function color_end return string is + begin + if colors_are_enabled then + return character'val(27) & '[' & integer'image(0) & 'm'; + else + return ""; + end if; + end function; + + procedure ansi_color_demo is + variable l : line; + begin + for bg in ansi_color_t'low to ansi_color_t'high loop + write(l, colorize("bg=" & ansi_color_t'image(bg), bg => bg)); + writeline(output, l); + end loop; + + for style in ansi_style_t'low to ansi_style_t'high loop + for fg in ansi_color_t'low to ansi_color_t'high loop + write(l, colorize( + "fg=" & ansi_color_t'image(fg) & ", " & + "style=" & ansi_style_t'image(style), + fg => fg, style => style)); + writeline(output, l); + end loop; + end loop; + + end procedure; + + procedure disable_colors is + begin + set(colors_enabled, 1, '0'); + end; + + procedure enable_colors is + begin + set(colors_enabled, 1, '1'); + end; + +end package body; diff --git a/vunit/vhdl/logging/src/file_pkg.vhd b/vunit/vhdl/logging/src/file_pkg.vhd index c6b94b8ff..737f7b48f 100644 --- a/vunit/vhdl/logging/src/file_pkg.vhd +++ b/vunit/vhdl/logging/src/file_pkg.vhd @@ -1,264 +1,264 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use std.textio.all; -use work.integer_vector_ptr_pkg.all; -use work.string_ptr_pkg.all; - -package file_pkg is - -- NOTE: This package is private, do not use outside of VUnit - - type file_id_t is record - p_data : integer_vector_ptr_t; - end record; - constant null_file_id : file_id_t := (p_data => null_ptr); - - function to_integer(file_id : file_id_t) return integer; - impure function to_file_id(value : integer) return file_id_t; - - impure function is_open(file_id : file_id_t) return boolean; - procedure file_close(file_id : file_id_t); - procedure file_open_for_write(file_id : inout file_id_t; - file_name : string); - procedure writeline(file_id : file_id_t; - l : inout line); -end package; - -package body file_pkg is - - constant next_id : integer_vector_ptr_t := new_integer_vector_ptr(len => 1, value => 0); - - constant id_idx : natural := 0; - constant open_idx : natural := 1; - constant name_idx : natural := 2; - constant file_id_length : natural := name_idx + 1; - - - -- VHDL does not support file arrays so we have to hard code N-files - file f0 : text; - file f1 : text; - file f2 : text; - file f3 : text; - file f4 : text; - file f5 : text; - file f6 : text; - file f7 : text; - file f8 : text; - file f9 : text; - file f10 : text; - file f11 : text; - file f12 : text; - file f13 : text; - file f14 : text; - file f15 : text; - file f16 : text; - file f17 : text; - file f18 : text; - file f19 : text; - file f20 : text; - file f21 : text; - file f22 : text; - file f23 : text; - file f24 : text; - file f25 : text; - file f26 : text; - file f27 : text; - file f28 : text; - file f29 : text; - file f30 : text; - file f31 : text; - - function to_integer(file_id : file_id_t) return integer is - begin - return to_integer(file_id.p_data); - end; - - impure function to_file_id(value : integer) return file_id_t is - begin - return (p_data => to_integer_vector_ptr(value)); - end; - - procedure assert_status(status : file_open_status; file_name : string) is - begin - assert status = open_ok - report "Failed to open file " & file_name & " - " & file_open_status'image(status) severity failure; - end procedure; - - impure function is_open(file_id : file_id_t) return boolean is - begin - return (file_id /= null_file_id) and get(file_id.p_data, open_idx) = 1; - end; - - procedure file_close(file_id : file_id_t) is - variable id : natural; - begin - if not is_open(file_id) then - return; - end if; - - id := get(file_id.p_data, id_idx); - case id is - when 0 => file_close(f0); - when 1 => file_close(f1); - when 2 => file_close(f2); - when 3 => file_close(f3); - when 4 => file_close(f4); - when 5 => file_close(f5); - when 6 => file_close(f6); - when 7 => file_close(f7); - when 8 => file_close(f8); - when 9 => file_close(f9); - when 10 => file_close(f10); - when 11 => file_close(f11); - when 12 => file_close(f12); - when 13 => file_close(f13); - when 14 => file_close(f14); - when 15 => file_close(f15); - when 16 => file_close(f16); - when 17 => file_close(f17); - when 18 => file_close(f18); - when 19 => file_close(f19); - when 20 => file_close(f20); - when 21 => file_close(f21); - when 22 => file_close(f22); - when 23 => file_close(f23); - when 24 => file_close(f24); - when 25 => file_close(f25); - when 26 => file_close(f26); - when 27 => file_close(f27); - when 28 => file_close(f28); - when 29 => file_close(f29); - when 30 => file_close(f30); - when 31 => file_close(f31); - when others => null; - end case; - - set(file_id.p_data, open_idx, 0); - end; - - procedure file_open_for_write(file_id : inout file_id_t; - file_name : string) is - variable status : file_open_status; - variable id : natural; - file ftmp : text; - begin - if is_open(file_id) then - file_close(file_id); - end if; - - if file_id = null_file_id then - id := get(next_id, 0); - set(next_id, 0, id + 1); - - file_id.p_data := new_integer_vector_ptr(len => file_id_length); - set(file_id.p_data, id_idx, id); - set(file_id.p_data, name_idx, to_integer(new_string_ptr(file_name))); - else - reallocate(to_string_ptr(get(file_id.p_data, name_idx)), file_name); - end if; - - set(file_id.p_data, open_idx, 1); - - id := get(file_id.p_data, id_idx); - case id is - when 0 => file_open(status, f0, file_name, write_mode); - when 1 => file_open(status, f1, file_name, write_mode); - when 2 => file_open(status, f2, file_name, write_mode); - when 3 => file_open(status, f3, file_name, write_mode); - when 4 => file_open(status, f4, file_name, write_mode); - when 5 => file_open(status, f5, file_name, write_mode); - when 6 => file_open(status, f6, file_name, write_mode); - when 7 => file_open(status, f7, file_name, write_mode); - when 8 => file_open(status, f8, file_name, write_mode); - when 9 => file_open(status, f9, file_name, write_mode); - when 10 => file_open(status, f10, file_name, write_mode); - when 11 => file_open(status, f11, file_name, write_mode); - when 12 => file_open(status, f12, file_name, write_mode); - when 13 => file_open(status, f13, file_name, write_mode); - when 14 => file_open(status, f14, file_name, write_mode); - when 15 => file_open(status, f15, file_name, write_mode); - when 16 => file_open(status, f16, file_name, write_mode); - when 17 => file_open(status, f17, file_name, write_mode); - when 18 => file_open(status, f18, file_name, write_mode); - when 19 => file_open(status, f19, file_name, write_mode); - when 20 => file_open(status, f20, file_name, write_mode); - when 21 => file_open(status, f21, file_name, write_mode); - when 22 => file_open(status, f22, file_name, write_mode); - when 23 => file_open(status, f23, file_name, write_mode); - when 24 => file_open(status, f24, file_name, write_mode); - when 25 => file_open(status, f25, file_name, write_mode); - when 26 => file_open(status, f26, file_name, write_mode); - when 27 => file_open(status, f27, file_name, write_mode); - when 28 => file_open(status, f28, file_name, write_mode); - when 29 => file_open(status, f29, file_name, write_mode); - when 30 => file_open(status, f30, file_name, write_mode); - when 31 => file_open(status, f31, file_name, write_mode); - - when others => - report ("Warning: All 32 file objects in use, " & - "reduced logging performance due to append open for each writeline (" & file_name & ")") - severity warning; - - file_open(status, ftmp, file_name, write_mode); - file_close(ftmp); - - end case; - - assert_status(status, file_name); - end; - - procedure writeline(file_id : file_id_t; - l : inout line) is - variable id : natural := get(file_id.p_data, id_idx); - variable name_ptr : string_ptr_t; - file ftmp : text; - variable status : file_open_status; - begin - case id is - when 0 => writeline(f0, l); - when 1 => writeline(f1, l); - when 2 => writeline(f2, l); - when 3 => writeline(f3, l); - when 4 => writeline(f4, l); - when 5 => writeline(f5, l); - when 6 => writeline(f6, l); - when 7 => writeline(f7, l); - when 8 => writeline(f8, l); - when 9 => writeline(f9, l); - when 10 => writeline(f10, l); - when 11 => writeline(f11, l); - when 12 => writeline(f12, l); - when 13 => writeline(f13, l); - when 14 => writeline(f14, l); - when 15 => writeline(f15, l); - when 16 => writeline(f16, l); - when 17 => writeline(f17, l); - when 18 => writeline(f18, l); - when 19 => writeline(f19, l); - when 20 => writeline(f20, l); - when 21 => writeline(f21, l); - when 22 => writeline(f22, l); - when 23 => writeline(f23, l); - when 24 => writeline(f24, l); - when 25 => writeline(f25, l); - when 26 => writeline(f26, l); - when 27 => writeline(f27, l); - when 28 => writeline(f28, l); - when 29 => writeline(f29, l); - when 30 => writeline(f30, l); - when 31 => writeline(f31, l); - - when others => - name_ptr := to_string_ptr(get(file_id.p_data, name_idx)); - file_open(status, ftmp, to_string(name_ptr), append_mode); - assert_status(status, to_string(name_ptr)); - writeline(ftmp, l); - file_close(ftmp); - end case; - - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use std.textio.all; +use work.integer_vector_ptr_pkg.all; +use work.string_ptr_pkg.all; + +package file_pkg is + -- NOTE: This package is private, do not use outside of VUnit + + type file_id_t is record + p_data : integer_vector_ptr_t; + end record; + constant null_file_id : file_id_t := (p_data => null_ptr); + + function to_integer(file_id : file_id_t) return integer; + impure function to_file_id(value : integer) return file_id_t; + + impure function is_open(file_id : file_id_t) return boolean; + procedure file_close(file_id : file_id_t); + procedure file_open_for_write(file_id : inout file_id_t; + file_name : string); + procedure writeline(file_id : file_id_t; + l : inout line); +end package; + +package body file_pkg is + + constant next_id : integer_vector_ptr_t := new_integer_vector_ptr(len => 1, value => 0); + + constant id_idx : natural := 0; + constant open_idx : natural := 1; + constant name_idx : natural := 2; + constant file_id_length : natural := name_idx + 1; + + + -- VHDL does not support file arrays so we have to hard code N-files + file f0 : text; + file f1 : text; + file f2 : text; + file f3 : text; + file f4 : text; + file f5 : text; + file f6 : text; + file f7 : text; + file f8 : text; + file f9 : text; + file f10 : text; + file f11 : text; + file f12 : text; + file f13 : text; + file f14 : text; + file f15 : text; + file f16 : text; + file f17 : text; + file f18 : text; + file f19 : text; + file f20 : text; + file f21 : text; + file f22 : text; + file f23 : text; + file f24 : text; + file f25 : text; + file f26 : text; + file f27 : text; + file f28 : text; + file f29 : text; + file f30 : text; + file f31 : text; + + function to_integer(file_id : file_id_t) return integer is + begin + return to_integer(file_id.p_data); + end; + + impure function to_file_id(value : integer) return file_id_t is + begin + return (p_data => to_integer_vector_ptr(value)); + end; + + procedure assert_status(status : file_open_status; file_name : string) is + begin + assert status = open_ok + report "Failed to open file " & file_name & " - " & file_open_status'image(status) severity failure; + end procedure; + + impure function is_open(file_id : file_id_t) return boolean is + begin + return (file_id /= null_file_id) and get(file_id.p_data, open_idx) = 1; + end; + + procedure file_close(file_id : file_id_t) is + variable id : natural; + begin + if not is_open(file_id) then + return; + end if; + + id := get(file_id.p_data, id_idx); + case id is + when 0 => file_close(f0); + when 1 => file_close(f1); + when 2 => file_close(f2); + when 3 => file_close(f3); + when 4 => file_close(f4); + when 5 => file_close(f5); + when 6 => file_close(f6); + when 7 => file_close(f7); + when 8 => file_close(f8); + when 9 => file_close(f9); + when 10 => file_close(f10); + when 11 => file_close(f11); + when 12 => file_close(f12); + when 13 => file_close(f13); + when 14 => file_close(f14); + when 15 => file_close(f15); + when 16 => file_close(f16); + when 17 => file_close(f17); + when 18 => file_close(f18); + when 19 => file_close(f19); + when 20 => file_close(f20); + when 21 => file_close(f21); + when 22 => file_close(f22); + when 23 => file_close(f23); + when 24 => file_close(f24); + when 25 => file_close(f25); + when 26 => file_close(f26); + when 27 => file_close(f27); + when 28 => file_close(f28); + when 29 => file_close(f29); + when 30 => file_close(f30); + when 31 => file_close(f31); + when others => null; + end case; + + set(file_id.p_data, open_idx, 0); + end; + + procedure file_open_for_write(file_id : inout file_id_t; + file_name : string) is + variable status : file_open_status; + variable id : natural; + file ftmp : text; + begin + if is_open(file_id) then + file_close(file_id); + end if; + + if file_id = null_file_id then + id := get(next_id, 0); + set(next_id, 0, id + 1); + + file_id.p_data := new_integer_vector_ptr(len => file_id_length); + set(file_id.p_data, id_idx, id); + set(file_id.p_data, name_idx, to_integer(new_string_ptr(file_name))); + else + reallocate(to_string_ptr(get(file_id.p_data, name_idx)), file_name); + end if; + + set(file_id.p_data, open_idx, 1); + + id := get(file_id.p_data, id_idx); + case id is + when 0 => file_open(status, f0, file_name, write_mode); + when 1 => file_open(status, f1, file_name, write_mode); + when 2 => file_open(status, f2, file_name, write_mode); + when 3 => file_open(status, f3, file_name, write_mode); + when 4 => file_open(status, f4, file_name, write_mode); + when 5 => file_open(status, f5, file_name, write_mode); + when 6 => file_open(status, f6, file_name, write_mode); + when 7 => file_open(status, f7, file_name, write_mode); + when 8 => file_open(status, f8, file_name, write_mode); + when 9 => file_open(status, f9, file_name, write_mode); + when 10 => file_open(status, f10, file_name, write_mode); + when 11 => file_open(status, f11, file_name, write_mode); + when 12 => file_open(status, f12, file_name, write_mode); + when 13 => file_open(status, f13, file_name, write_mode); + when 14 => file_open(status, f14, file_name, write_mode); + when 15 => file_open(status, f15, file_name, write_mode); + when 16 => file_open(status, f16, file_name, write_mode); + when 17 => file_open(status, f17, file_name, write_mode); + when 18 => file_open(status, f18, file_name, write_mode); + when 19 => file_open(status, f19, file_name, write_mode); + when 20 => file_open(status, f20, file_name, write_mode); + when 21 => file_open(status, f21, file_name, write_mode); + when 22 => file_open(status, f22, file_name, write_mode); + when 23 => file_open(status, f23, file_name, write_mode); + when 24 => file_open(status, f24, file_name, write_mode); + when 25 => file_open(status, f25, file_name, write_mode); + when 26 => file_open(status, f26, file_name, write_mode); + when 27 => file_open(status, f27, file_name, write_mode); + when 28 => file_open(status, f28, file_name, write_mode); + when 29 => file_open(status, f29, file_name, write_mode); + when 30 => file_open(status, f30, file_name, write_mode); + when 31 => file_open(status, f31, file_name, write_mode); + + when others => + report ("Warning: All 32 file objects in use, " & + "reduced logging performance due to append open for each writeline (" & file_name & ")") + severity warning; + + file_open(status, ftmp, file_name, write_mode); + file_close(ftmp); + + end case; + + assert_status(status, file_name); + end; + + procedure writeline(file_id : file_id_t; + l : inout line) is + variable id : natural := get(file_id.p_data, id_idx); + variable name_ptr : string_ptr_t; + file ftmp : text; + variable status : file_open_status; + begin + case id is + when 0 => writeline(f0, l); + when 1 => writeline(f1, l); + when 2 => writeline(f2, l); + when 3 => writeline(f3, l); + when 4 => writeline(f4, l); + when 5 => writeline(f5, l); + when 6 => writeline(f6, l); + when 7 => writeline(f7, l); + when 8 => writeline(f8, l); + when 9 => writeline(f9, l); + when 10 => writeline(f10, l); + when 11 => writeline(f11, l); + when 12 => writeline(f12, l); + when 13 => writeline(f13, l); + when 14 => writeline(f14, l); + when 15 => writeline(f15, l); + when 16 => writeline(f16, l); + when 17 => writeline(f17, l); + when 18 => writeline(f18, l); + when 19 => writeline(f19, l); + when 20 => writeline(f20, l); + when 21 => writeline(f21, l); + when 22 => writeline(f22, l); + when 23 => writeline(f23, l); + when 24 => writeline(f24, l); + when 25 => writeline(f25, l); + when 26 => writeline(f26, l); + when 27 => writeline(f27, l); + when 28 => writeline(f28, l); + when 29 => writeline(f29, l); + when 30 => writeline(f30, l); + when 31 => writeline(f31, l); + + when others => + name_ptr := to_string_ptr(get(file_id.p_data, name_idx)); + file_open(status, ftmp, to_string(name_ptr), append_mode); + assert_status(status, to_string(name_ptr)); + writeline(ftmp, l); + file_close(ftmp); + end case; + + end; + +end package body; diff --git a/vunit/vhdl/logging/src/log_deprecated_pkg.vhd b/vunit/vhdl/logging/src/log_deprecated_pkg.vhd index cc263ceaf..f66f0bcf4 100644 --- a/vunit/vhdl/logging/src/log_deprecated_pkg.vhd +++ b/vunit/vhdl/logging/src/log_deprecated_pkg.vhd @@ -1,208 +1,208 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.log_levels_pkg.all; -use work.logger_pkg.all; -use work.log_handler_pkg.all; -use work.integer_vector_ptr_pkg.all; -use work.core_pkg.core_failure; - -use std.textio.all; - -package log_deprecated_pkg is - alias verbose_csv is work.log_handler_pkg.csv [return deprecated_log_format_t]; - alias verbose is work.log_levels_pkg.trace [return log_level_t]; - - -- Deprecated interface to better support legacy testbenches. Calls to this - -- procedure will be mapped to contemporary functionality using best effort: - -- - -- * default_src sets the name of an uninitialized logger. Empty string names are not supported - -- and will be replaced with "anonymous". Changing logger - -- name of an already initialized logger is not allowed. In this case the - -- empty string is the only valid value. - -- * Changing the separator and append parameters to non-default values is not - -- supported - -- * The logger is configured with private display and file log handlers independent - -- of the predefined handlers used by default_logger - procedure logger_init ( - variable logger : inout logger_t; - default_src : string := ""; - file_name : string := "log.csv"; - display_format : deprecated_log_format_t := raw; - file_format : deprecated_log_format_t := off; - stop_level : log_level_t := failure; - separator : character := ','; - append : boolean := false); - - -- This logger_init is used to reinitialize the default_logger and its predefined - -- display and file handlers - procedure logger_init ( - default_src : string := ""; - file_name : string := "log.csv"; - display_format : deprecated_log_format_t := raw; - file_format : deprecated_log_format_t := off; - stop_level : log_level_t := failure; - separator : character := ','; - append : boolean := false); - - -- VERBOSE is alias for TRACE - procedure verbose(logger : logger_t; - msg : string; - line_num : natural := 0; - file_name : string := ""); - procedure verbose(msg : string; - line_num : natural := 0; - file_name : string := ""); - - -end package log_deprecated_pkg; - -package body log_deprecated_pkg is - constant anonymous_counter : integer_vector_ptr_t := new_integer_vector_ptr(1); - - procedure logger_init ( - variable logger : inout logger_t; - default_src : string := ""; - file_name : string := "log.csv"; - display_format : deprecated_log_format_t := raw; - file_format : deprecated_log_format_t := off; - stop_level : log_level_t := failure; - separator : character := ','; - append : boolean := false) is - - variable logger_display_handler, logger_file_handler : log_handler_t; - variable new_logger : boolean := false; - - procedure create_logger is - variable name : line; - begin - new_logger := true; - if default_src = "" then - write(name, "anonymous" & integer'image(get(anonymous_counter, 0))); - warning("Empty string logger names not supported. Using """ & name.all & """"); - set(anonymous_counter, 0, get(anonymous_counter, 0) + 1); - else - write(name, default_src); - end if; - logger := get_logger(name.all); - deallocate(name); - end procedure create_logger; - - procedure configure_handlers(variable logger_display_handler, logger_file_handler : inout log_handler_t) is - function real_format(format : deprecated_log_format_t) return log_format_t is - begin - if format = off then - return raw; - else - return format; - end if; - end function; - - impure function get_logger_display_handler return log_handler_t is - begin - for idx in 0 to num_log_handlers(logger) - 1 loop - if get_file_name(get_log_handler(logger, idx)) = stdout_file_name then - return get_log_handler(logger, idx); - end if; - end loop; - - return null_log_handler; - end function; - - impure function get_logger_file_handler return log_handler_t is - begin - for idx in 0 to num_log_handlers(logger) - 1 loop - if get_file_name(get_log_handler(logger, idx)) /= stdout_file_name then - return get_log_handler(logger, idx); - end if; - end loop; - - return null_log_handler; - end function; - begin - logger_display_handler := get_logger_display_handler; - if new_logger or (logger_display_handler = null_log_handler) then - logger_display_handler := new_log_handler(stdout_file_name, real_format(display_format), true); - else - init_log_handler(logger_display_handler, real_format(display_format), stdout_file_name, true); - end if; - - logger_file_handler := get_logger_file_handler; - if new_logger or (logger_file_handler = null_log_handler) then - logger_file_handler := new_log_handler(file_name, real_format(file_format), false); - else - init_log_handler(logger_file_handler, real_format(file_format), file_name, false); - end if; - - set_log_handlers(logger, (logger_display_handler, logger_file_handler)); - end procedure; - - procedure filter_output ( - variable handler : inout log_handler_t; - constant format : in deprecated_log_format_t) is - begin - if format = off then - hide_all(logger, handler); - else - show_all(logger, handler); - end if; - end procedure filter_output; - - begin - warning("Using deprecated procedure logger_init. Using best effort mapping to contemporary functionality"); - - if logger = null_logger then - create_logger; - elsif default_src /= "" and default_src /= get_full_name(logger) then - core_failure("Changing logger name is not supported"); - end if; - - configure_handlers(logger_display_handler, logger_file_handler); - - filter_output(logger_display_handler, display_format); - filter_output(logger_file_handler, file_format); - - set_stop_level(logger, stop_level); - - if separator /= ',' then - core_failure("Changing CSV separator is not supported"); - end if; - - if append then - core_failure("Appending new log to existing file is not supported"); - end if; - end; - - procedure logger_init ( - default_src : string := ""; - file_name : string := "log.csv"; - display_format : deprecated_log_format_t := raw; - file_format : deprecated_log_format_t := off; - stop_level : log_level_t := failure; - separator : character := ','; - append : boolean := false) is - variable logger : logger_t := default_logger; - begin - logger_init(logger, default_src, file_name, display_format, file_format, stop_level, separator, append); - end; - - procedure verbose(logger : logger_t; - msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - warning("Mapping deprecated procedure verbose to trace"); - trace(logger, msg, line_num, file_name); - end procedure; - - procedure verbose(msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - verbose(default_logger, msg, line_num, file_name); - end procedure; - -end package body log_deprecated_pkg; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.log_levels_pkg.all; +use work.logger_pkg.all; +use work.log_handler_pkg.all; +use work.integer_vector_ptr_pkg.all; +use work.core_pkg.core_failure; + +use std.textio.all; + +package log_deprecated_pkg is + alias verbose_csv is work.log_handler_pkg.csv [return deprecated_log_format_t]; + alias verbose is work.log_levels_pkg.trace [return log_level_t]; + + -- Deprecated interface to better support legacy testbenches. Calls to this + -- procedure will be mapped to contemporary functionality using best effort: + -- + -- * default_src sets the name of an uninitialized logger. Empty string names are not supported + -- and will be replaced with "anonymous". Changing logger + -- name of an already initialized logger is not allowed. In this case the + -- empty string is the only valid value. + -- * Changing the separator and append parameters to non-default values is not + -- supported + -- * The logger is configured with private display and file log handlers independent + -- of the predefined handlers used by default_logger + procedure logger_init ( + variable logger : inout logger_t; + default_src : string := ""; + file_name : string := "log.csv"; + display_format : deprecated_log_format_t := raw; + file_format : deprecated_log_format_t := off; + stop_level : log_level_t := failure; + separator : character := ','; + append : boolean := false); + + -- This logger_init is used to reinitialize the default_logger and its predefined + -- display and file handlers + procedure logger_init ( + default_src : string := ""; + file_name : string := "log.csv"; + display_format : deprecated_log_format_t := raw; + file_format : deprecated_log_format_t := off; + stop_level : log_level_t := failure; + separator : character := ','; + append : boolean := false); + + -- VERBOSE is alias for TRACE + procedure verbose(logger : logger_t; + msg : string; + line_num : natural := 0; + file_name : string := ""); + procedure verbose(msg : string; + line_num : natural := 0; + file_name : string := ""); + + +end package log_deprecated_pkg; + +package body log_deprecated_pkg is + constant anonymous_counter : integer_vector_ptr_t := new_integer_vector_ptr(1); + + procedure logger_init ( + variable logger : inout logger_t; + default_src : string := ""; + file_name : string := "log.csv"; + display_format : deprecated_log_format_t := raw; + file_format : deprecated_log_format_t := off; + stop_level : log_level_t := failure; + separator : character := ','; + append : boolean := false) is + + variable logger_display_handler, logger_file_handler : log_handler_t; + variable new_logger : boolean := false; + + procedure create_logger is + variable name : line; + begin + new_logger := true; + if default_src = "" then + write(name, "anonymous" & integer'image(get(anonymous_counter, 0))); + warning("Empty string logger names not supported. Using """ & name.all & """"); + set(anonymous_counter, 0, get(anonymous_counter, 0) + 1); + else + write(name, default_src); + end if; + logger := get_logger(name.all); + deallocate(name); + end procedure create_logger; + + procedure configure_handlers(variable logger_display_handler, logger_file_handler : inout log_handler_t) is + function real_format(format : deprecated_log_format_t) return log_format_t is + begin + if format = off then + return raw; + else + return format; + end if; + end function; + + impure function get_logger_display_handler return log_handler_t is + begin + for idx in 0 to num_log_handlers(logger) - 1 loop + if get_file_name(get_log_handler(logger, idx)) = stdout_file_name then + return get_log_handler(logger, idx); + end if; + end loop; + + return null_log_handler; + end function; + + impure function get_logger_file_handler return log_handler_t is + begin + for idx in 0 to num_log_handlers(logger) - 1 loop + if get_file_name(get_log_handler(logger, idx)) /= stdout_file_name then + return get_log_handler(logger, idx); + end if; + end loop; + + return null_log_handler; + end function; + begin + logger_display_handler := get_logger_display_handler; + if new_logger or (logger_display_handler = null_log_handler) then + logger_display_handler := new_log_handler(stdout_file_name, real_format(display_format), true); + else + init_log_handler(logger_display_handler, real_format(display_format), stdout_file_name, true); + end if; + + logger_file_handler := get_logger_file_handler; + if new_logger or (logger_file_handler = null_log_handler) then + logger_file_handler := new_log_handler(file_name, real_format(file_format), false); + else + init_log_handler(logger_file_handler, real_format(file_format), file_name, false); + end if; + + set_log_handlers(logger, (logger_display_handler, logger_file_handler)); + end procedure; + + procedure filter_output ( + variable handler : inout log_handler_t; + constant format : in deprecated_log_format_t) is + begin + if format = off then + hide_all(logger, handler); + else + show_all(logger, handler); + end if; + end procedure filter_output; + + begin + warning("Using deprecated procedure logger_init. Using best effort mapping to contemporary functionality"); + + if logger = null_logger then + create_logger; + elsif default_src /= "" and default_src /= get_full_name(logger) then + core_failure("Changing logger name is not supported"); + end if; + + configure_handlers(logger_display_handler, logger_file_handler); + + filter_output(logger_display_handler, display_format); + filter_output(logger_file_handler, file_format); + + set_stop_level(logger, stop_level); + + if separator /= ',' then + core_failure("Changing CSV separator is not supported"); + end if; + + if append then + core_failure("Appending new log to existing file is not supported"); + end if; + end; + + procedure logger_init ( + default_src : string := ""; + file_name : string := "log.csv"; + display_format : deprecated_log_format_t := raw; + file_format : deprecated_log_format_t := off; + stop_level : log_level_t := failure; + separator : character := ','; + append : boolean := false) is + variable logger : logger_t := default_logger; + begin + logger_init(logger, default_src, file_name, display_format, file_format, stop_level, separator, append); + end; + + procedure verbose(logger : logger_t; + msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + warning("Mapping deprecated procedure verbose to trace"); + trace(logger, msg, line_num, file_name); + end procedure; + + procedure verbose(msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + verbose(default_logger, msg, line_num, file_name); + end procedure; + +end package body log_deprecated_pkg; diff --git a/vunit/vhdl/logging/src/log_handler_pkg-body.vhd b/vunit/vhdl/logging/src/log_handler_pkg-body.vhd index 5ad030556..b98e8515e 100644 --- a/vunit/vhdl/logging/src/log_handler_pkg-body.vhd +++ b/vunit/vhdl/logging/src/log_handler_pkg-body.vhd @@ -1,331 +1,331 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use std.textio.all; - -library vunit_lib; -use vunit_lib.string_ptr_pkg.all; -use vunit_lib.integer_vector_ptr_pkg.all; - -use work.ansi_pkg.all; -use work.string_ops.upper; -use work.file_pkg.all; - -package body log_handler_pkg is - - constant display_handler_id : natural := 0; - constant next_log_handler_id : integer_vector_ptr_t := new_integer_vector_ptr(1, value => display_handler_id+1); - - constant id_idx : natural := 0; - constant file_name_idx : natural := 1; - constant format_idx : natural := 2; - constant use_color_idx : natural := 3; - constant file_id_idx : natural := 4; - constant max_logger_name_idx : natural := 5; - constant log_handler_length : natural := max_logger_name_idx + 1; - - constant max_time_str : string := time'image(1 sec); - constant max_time_length : natural := max_time_str'length; - - procedure assert_status(status : file_open_status; file_name : string) is - begin - assert status = open_ok - report "Failed to open file " & file_name & " - " & file_open_status'image(status) severity failure; - end procedure; - - procedure init_log_file(log_handler : log_handler_t; file_name : string) is - variable file_id : file_id_t := to_file_id(get(log_handler.p_data, file_id_idx)); - begin - if (file_name /= null_file_name) and (file_name /= stdout_file_name) then - file_open_for_write(file_id, file_name); - end if; - set(log_handler.p_data, file_id_idx, to_integer(file_id)); - end procedure; - - impure function new_log_handler(id : natural; - file_name : string; - format : log_format_t; - use_color : boolean) return log_handler_t is - constant log_handler : log_handler_t := (p_data => new_integer_vector_ptr(log_handler_length)); - begin - set(log_handler.p_data, id_idx, id); - set(log_handler.p_data, file_name_idx, to_integer(new_string_ptr(file_name))); - set(log_handler.p_data, file_id_idx, to_integer(null_file_id)); - init_log_file(log_handler, file_name); - set(log_handler.p_data, max_logger_name_idx, 0); - set_format(log_handler, format, use_color); - return log_handler; - end; - - impure function new_log_handler(file_name : string; - format : log_format_t; - use_color : boolean) return log_handler_t is - constant id : natural := get(next_log_handler_id, 0); - begin - set(next_log_handler_id, 0, id + 1); - return new_log_handler(id, file_name, format, use_color); - end; - - -- Display handler; Write to stdout - constant p_display_handler : log_handler_t := new_log_handler(display_handler_id, - stdout_file_name, - format => verbose, - use_color => true); - - impure function display_handler return log_handler_t is - begin - return p_display_handler; - end function; - - impure function get_id(log_handler : log_handler_t) return natural is - begin - return get(log_handler.p_data, id_idx); - end; - - impure function get_file_name (log_handler : log_handler_t) return string is - begin - return to_string(to_string_ptr(get(log_handler.p_data, file_name_idx))); - end; - - procedure init_log_handler(log_handler : log_handler_t; - format : log_format_t; - file_name : string; - use_color : boolean := false) is - variable file_name_ptr : string_ptr_t := to_string_ptr(get(log_handler.p_data, file_name_idx)); - begin - assert log_handler /= null_log_handler; - reallocate(file_name_ptr, file_name); - init_log_file(log_handler, file_name); - set_format(log_handler, format, use_color); - end; - - procedure set_format(log_handler : log_handler_t; - format : log_format_t; - use_color : boolean := false) is - begin - set(log_handler.p_data, format_idx, log_format_t'pos(format)); - if use_color then - set(log_handler.p_data, use_color_idx, 1); - else - set(log_handler.p_data, use_color_idx, 0); - end if; - end; - - procedure get_format(constant log_handler : in log_handler_t; - variable format : out log_format_t; - variable use_color : out boolean) is - begin - format := log_format_t'val(get(log_handler.p_data, format_idx)); - use_color := get(log_handler.p_data, use_color_idx) = 1; - end; - - procedure set_max_logger_name_length(log_handler : log_handler_t; value : natural) is - begin - set(log_handler.p_data, max_logger_name_idx, value); - end; - - - impure function get_max_logger_name_length(log_handler : log_handler_t) return natural is - begin - return get(log_handler.p_data, max_logger_name_idx); - end; - - procedure update_max_logger_name_length(log_handler : log_handler_t; value : natural) is - begin - if get_max_logger_name_length(log_handler) < value then - set_max_logger_name_length(log_handler, value); - end if; - end; - - procedure log_to_handler(log_handler : log_handler_t; - logger_name : string; - msg : string; - log_level : log_level_t; - log_time : time; - sequence_number : natural; - line_num : natural := 0; - file_name : string := "") is - - variable l : line; - constant log_file_name : string := get_file_name(log_handler); - - procedure log_to_line(variable l : inout line) is - variable use_color : boolean := get(log_handler.p_data, use_color_idx) = 1; - - procedure pad(variable l : inout line; len : integer) is - begin - if len > 0 then - write(l, string'(1 to len => ' ')); - end if; - end; - - procedure write_time(variable l : inout line; justify : boolean := false) is - constant time_string : string := time'image(log_time); - begin - if justify then - pad(l, max_time_length - time_string'length); - end if; - - if use_color then - write(l, color_start(fg => lightcyan)); - end if; - - write(l, time_string); - - if use_color then - write(l, color_end); - end if; - end procedure; - - procedure write_level(variable l : inout line; justify : boolean := false) is - constant level_name : string := get_name(log_level); - variable color : ansi_colors_t; - begin - if justify then - pad(l, max_level_length - level_name'length); - end if; - - if use_color then - color := get_color(log_level); - write(l, color_start(fg => color.fg, bg => color.bg, style => color.style)); - end if; - - write(l, upper(level_name)); - - if use_color then - write(l, color_end); - end if; - end; - - procedure write_source(variable l : inout line; justify : boolean := false) is - begin - if use_color then - write(l, color_start(fg => white, style => bright)); - - for i in logger_name 'range loop - if logger_name(i) = ':' then - write(l, color_start(fg => lightcyan, style => bright)); - write(l, logger_name(i)); - write(l, color_start(fg => white, style => bright)); - else - write(l, logger_name(i)); - end if; - end loop; - else - write(l, logger_name); - end if; - - if use_color then - write(l, color_end); - end if; - - if justify then - pad(l, get_max_logger_name_length(log_handler) - logger_name'length); - end if; - - end; - - procedure write_location(variable l : inout line) is - begin - if file_name /= "" then - write(l, " (" & file_name & ":" & integer'image(line_num) & ")"); - end if; - end; - - procedure write_message(variable l : inout line; multi_line_align : boolean := false) is - variable prefix_len : natural; - variable location_written : boolean := false; - begin - if not multi_line_align then - write(l, msg); - else - prefix_len := length_without_color(l.all); - for i in msg'range loop - - if msg(i) = LF and not location_written then - location_written := true; - write_location(l); - end if; - - write(l, msg(i)); - - if msg(i) = LF then - write(l, string'(1 to prefix_len => ' ')); - end if; - end loop; - end if; - - if not location_written then - write_location(l); - end if; - - end procedure; - - constant format : log_format_t := log_format_t'val(get(log_handler.p_data, format_idx)); - begin - case format is - when raw => - write_message(l); - - when csv => - write(l, string'(integer'image(sequence_number) & ',')); - write_time(l); - write(l, ','); - write_level(l); - write(l, ','); - - if line_num = 0 then - write(l, string'(",,")); - else - write(l, file_name); - write(l, ','); - write(l, integer'image(line_num)); - write(l, ','); - end if; - - write_source(l); - write(l, ','); - write(l, msg); - - when level => - write_level(l, justify => true); - write(l, string'(" - ")); - write_message(l, multi_line_align => true); - - when verbose => - write_time(l, justify => true); - write(l, string'(" - ")); - write_source(l, justify => true); - write(l, string'(" - ")); - write_level(l, justify => true); - write(l, string'(" - ")); - write_message(l, multi_line_align => true); - end case; - end; - - procedure log_to_file(variable l : inout line) is - file fptr : text; - variable status : file_open_status; - begin - file_open(status, fptr, log_file_name, append_mode); - assert_status(status, log_file_name); - writeline(fptr, l); - file_close(fptr); - end; - - begin - if log_file_name = null_file_name then - null; - elsif log_file_name = stdout_file_name then - log_to_line(l); - writeline(OUTPUT, l); - else - log_to_line(l); - writeline(to_file_id(get(log_handler.p_data, file_id_idx)), l); - end if; - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use std.textio.all; + +library vunit_lib; +use vunit_lib.string_ptr_pkg.all; +use vunit_lib.integer_vector_ptr_pkg.all; + +use work.ansi_pkg.all; +use work.string_ops.upper; +use work.file_pkg.all; + +package body log_handler_pkg is + + constant display_handler_id : natural := 0; + constant next_log_handler_id : integer_vector_ptr_t := new_integer_vector_ptr(1, value => display_handler_id+1); + + constant id_idx : natural := 0; + constant file_name_idx : natural := 1; + constant format_idx : natural := 2; + constant use_color_idx : natural := 3; + constant file_id_idx : natural := 4; + constant max_logger_name_idx : natural := 5; + constant log_handler_length : natural := max_logger_name_idx + 1; + + constant max_time_str : string := time'image(1 sec); + constant max_time_length : natural := max_time_str'length; + + procedure assert_status(status : file_open_status; file_name : string) is + begin + assert status = open_ok + report "Failed to open file " & file_name & " - " & file_open_status'image(status) severity failure; + end procedure; + + procedure init_log_file(log_handler : log_handler_t; file_name : string) is + variable file_id : file_id_t := to_file_id(get(log_handler.p_data, file_id_idx)); + begin + if (file_name /= null_file_name) and (file_name /= stdout_file_name) then + file_open_for_write(file_id, file_name); + end if; + set(log_handler.p_data, file_id_idx, to_integer(file_id)); + end procedure; + + impure function new_log_handler(id : natural; + file_name : string; + format : log_format_t; + use_color : boolean) return log_handler_t is + constant log_handler : log_handler_t := (p_data => new_integer_vector_ptr(log_handler_length)); + begin + set(log_handler.p_data, id_idx, id); + set(log_handler.p_data, file_name_idx, to_integer(new_string_ptr(file_name))); + set(log_handler.p_data, file_id_idx, to_integer(null_file_id)); + init_log_file(log_handler, file_name); + set(log_handler.p_data, max_logger_name_idx, 0); + set_format(log_handler, format, use_color); + return log_handler; + end; + + impure function new_log_handler(file_name : string; + format : log_format_t; + use_color : boolean) return log_handler_t is + constant id : natural := get(next_log_handler_id, 0); + begin + set(next_log_handler_id, 0, id + 1); + return new_log_handler(id, file_name, format, use_color); + end; + + -- Display handler; Write to stdout + constant p_display_handler : log_handler_t := new_log_handler(display_handler_id, + stdout_file_name, + format => verbose, + use_color => true); + + impure function display_handler return log_handler_t is + begin + return p_display_handler; + end function; + + impure function get_id(log_handler : log_handler_t) return natural is + begin + return get(log_handler.p_data, id_idx); + end; + + impure function get_file_name (log_handler : log_handler_t) return string is + begin + return to_string(to_string_ptr(get(log_handler.p_data, file_name_idx))); + end; + + procedure init_log_handler(log_handler : log_handler_t; + format : log_format_t; + file_name : string; + use_color : boolean := false) is + variable file_name_ptr : string_ptr_t := to_string_ptr(get(log_handler.p_data, file_name_idx)); + begin + assert log_handler /= null_log_handler; + reallocate(file_name_ptr, file_name); + init_log_file(log_handler, file_name); + set_format(log_handler, format, use_color); + end; + + procedure set_format(log_handler : log_handler_t; + format : log_format_t; + use_color : boolean := false) is + begin + set(log_handler.p_data, format_idx, log_format_t'pos(format)); + if use_color then + set(log_handler.p_data, use_color_idx, 1); + else + set(log_handler.p_data, use_color_idx, 0); + end if; + end; + + procedure get_format(constant log_handler : in log_handler_t; + variable format : out log_format_t; + variable use_color : out boolean) is + begin + format := log_format_t'val(get(log_handler.p_data, format_idx)); + use_color := get(log_handler.p_data, use_color_idx) = 1; + end; + + procedure set_max_logger_name_length(log_handler : log_handler_t; value : natural) is + begin + set(log_handler.p_data, max_logger_name_idx, value); + end; + + + impure function get_max_logger_name_length(log_handler : log_handler_t) return natural is + begin + return get(log_handler.p_data, max_logger_name_idx); + end; + + procedure update_max_logger_name_length(log_handler : log_handler_t; value : natural) is + begin + if get_max_logger_name_length(log_handler) < value then + set_max_logger_name_length(log_handler, value); + end if; + end; + + procedure log_to_handler(log_handler : log_handler_t; + logger_name : string; + msg : string; + log_level : log_level_t; + log_time : time; + sequence_number : natural; + line_num : natural := 0; + file_name : string := "") is + + variable l : line; + constant log_file_name : string := get_file_name(log_handler); + + procedure log_to_line(variable l : inout line) is + variable use_color : boolean := get(log_handler.p_data, use_color_idx) = 1; + + procedure pad(variable l : inout line; len : integer) is + begin + if len > 0 then + write(l, string'(1 to len => ' ')); + end if; + end; + + procedure write_time(variable l : inout line; justify : boolean := false) is + constant time_string : string := time'image(log_time); + begin + if justify then + pad(l, max_time_length - time_string'length); + end if; + + if use_color then + write(l, color_start(fg => lightcyan)); + end if; + + write(l, time_string); + + if use_color then + write(l, color_end); + end if; + end procedure; + + procedure write_level(variable l : inout line; justify : boolean := false) is + constant level_name : string := get_name(log_level); + variable color : ansi_colors_t; + begin + if justify then + pad(l, max_level_length - level_name'length); + end if; + + if use_color then + color := get_color(log_level); + write(l, color_start(fg => color.fg, bg => color.bg, style => color.style)); + end if; + + write(l, upper(level_name)); + + if use_color then + write(l, color_end); + end if; + end; + + procedure write_source(variable l : inout line; justify : boolean := false) is + begin + if use_color then + write(l, color_start(fg => white, style => bright)); + + for i in logger_name 'range loop + if logger_name(i) = ':' then + write(l, color_start(fg => lightcyan, style => bright)); + write(l, logger_name(i)); + write(l, color_start(fg => white, style => bright)); + else + write(l, logger_name(i)); + end if; + end loop; + else + write(l, logger_name); + end if; + + if use_color then + write(l, color_end); + end if; + + if justify then + pad(l, get_max_logger_name_length(log_handler) - logger_name'length); + end if; + + end; + + procedure write_location(variable l : inout line) is + begin + if file_name /= "" then + write(l, " (" & file_name & ":" & integer'image(line_num) & ")"); + end if; + end; + + procedure write_message(variable l : inout line; multi_line_align : boolean := false) is + variable prefix_len : natural; + variable location_written : boolean := false; + begin + if not multi_line_align then + write(l, msg); + else + prefix_len := length_without_color(l.all); + for i in msg'range loop + + if msg(i) = LF and not location_written then + location_written := true; + write_location(l); + end if; + + write(l, msg(i)); + + if msg(i) = LF then + write(l, string'(1 to prefix_len => ' ')); + end if; + end loop; + end if; + + if not location_written then + write_location(l); + end if; + + end procedure; + + constant format : log_format_t := log_format_t'val(get(log_handler.p_data, format_idx)); + begin + case format is + when raw => + write_message(l); + + when csv => + write(l, string'(integer'image(sequence_number) & ',')); + write_time(l); + write(l, ','); + write_level(l); + write(l, ','); + + if line_num = 0 then + write(l, string'(",,")); + else + write(l, file_name); + write(l, ','); + write(l, integer'image(line_num)); + write(l, ','); + end if; + + write_source(l); + write(l, ','); + write(l, msg); + + when level => + write_level(l, justify => true); + write(l, string'(" - ")); + write_message(l, multi_line_align => true); + + when verbose => + write_time(l, justify => true); + write(l, string'(" - ")); + write_source(l, justify => true); + write(l, string'(" - ")); + write_level(l, justify => true); + write(l, string'(" - ")); + write_message(l, multi_line_align => true); + end case; + end; + + procedure log_to_file(variable l : inout line) is + file fptr : text; + variable status : file_open_status; + begin + file_open(status, fptr, log_file_name, append_mode); + assert_status(status, log_file_name); + writeline(fptr, l); + file_close(fptr); + end; + + begin + if log_file_name = null_file_name then + null; + elsif log_file_name = stdout_file_name then + log_to_line(l); + writeline(OUTPUT, l); + else + log_to_line(l); + writeline(to_file_id(get(log_handler.p_data, file_id_idx)), l); + end if; + end; + +end package body; diff --git a/vunit/vhdl/logging/src/log_handler_pkg.vhd b/vunit/vhdl/logging/src/log_handler_pkg.vhd index e36427ebd..eda359737 100644 --- a/vunit/vhdl/logging/src/log_handler_pkg.vhd +++ b/vunit/vhdl/logging/src/log_handler_pkg.vhd @@ -1,82 +1,82 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.integer_vector_ptr_pkg.all; -use work.log_levels_pkg.all; - -package log_handler_pkg is - type deprecated_log_format_t is ( - -- The raw log format contains just the message without any other information - raw, - - -- The level log format contains the level and message - level, - - -- The verbose log format contains all information - verbose, - - -- The csv format contains all information in machine readable format - csv, - - -- Deprecated value not supported by new interfaces but kept for backward - -- compatibility reasons. NOT for new designs - off); - - subtype log_format_t is deprecated_log_format_t range raw to csv; - - -- Log handler record, all fields are private - type log_handler_t is record - p_data : integer_vector_ptr_t; - end record; - constant null_log_handler : log_handler_t := (p_data => null_ptr); - type log_handler_vec_t is array (natural range <>) of log_handler_t; - - -- Display handler; Write to stdout - impure function display_handler return log_handler_t; - - -- Get the name of the file used by the handler - impure function get_file_name (log_handler : log_handler_t) return string; - - -- File name used by the display handler - constant stdout_file_name : string := ">1"; - - constant null_file_name : string := ""; - - -- Set the format to be used by the log handler - procedure set_format(log_handler : log_handler_t; - format : log_format_t; - use_color : boolean := false); - - -- Get the format used by the log handler - procedure get_format(constant log_handler : in log_handler_t; - variable format : out log_format_t; - variable use_color : out boolean); - - --------------------------------------------- - -- Private parts not intended for public use - --------------------------------------------- - impure function get_id(log_handler : log_handler_t) return natural; - procedure update_max_logger_name_length(log_handler : log_handler_t; value : natural); - impure function get_max_logger_name_length(log_handler : log_handler_t) return natural; - - procedure log_to_handler(log_handler : log_handler_t; - logger_name : string; - msg : string; - log_level : log_level_t; - log_time : time; - sequence_number : natural; - line_num : natural := 0; - file_name : string := ""); - - impure function new_log_handler(file_name : string; - format : log_format_t; - use_color : boolean) return log_handler_t; - - procedure init_log_handler(log_handler : log_handler_t; - format : log_format_t; - file_name : string; - use_color : boolean := false); -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.integer_vector_ptr_pkg.all; +use work.log_levels_pkg.all; + +package log_handler_pkg is + type deprecated_log_format_t is ( + -- The raw log format contains just the message without any other information + raw, + + -- The level log format contains the level and message + level, + + -- The verbose log format contains all information + verbose, + + -- The csv format contains all information in machine readable format + csv, + + -- Deprecated value not supported by new interfaces but kept for backward + -- compatibility reasons. NOT for new designs + off); + + subtype log_format_t is deprecated_log_format_t range raw to csv; + + -- Log handler record, all fields are private + type log_handler_t is record + p_data : integer_vector_ptr_t; + end record; + constant null_log_handler : log_handler_t := (p_data => null_ptr); + type log_handler_vec_t is array (natural range <>) of log_handler_t; + + -- Display handler; Write to stdout + impure function display_handler return log_handler_t; + + -- Get the name of the file used by the handler + impure function get_file_name (log_handler : log_handler_t) return string; + + -- File name used by the display handler + constant stdout_file_name : string := ">1"; + + constant null_file_name : string := ""; + + -- Set the format to be used by the log handler + procedure set_format(log_handler : log_handler_t; + format : log_format_t; + use_color : boolean := false); + + -- Get the format used by the log handler + procedure get_format(constant log_handler : in log_handler_t; + variable format : out log_format_t; + variable use_color : out boolean); + + --------------------------------------------- + -- Private parts not intended for public use + --------------------------------------------- + impure function get_id(log_handler : log_handler_t) return natural; + procedure update_max_logger_name_length(log_handler : log_handler_t; value : natural); + impure function get_max_logger_name_length(log_handler : log_handler_t) return natural; + + procedure log_to_handler(log_handler : log_handler_t; + logger_name : string; + msg : string; + log_level : log_level_t; + log_time : time; + sequence_number : natural; + line_num : natural := 0; + file_name : string := ""); + + impure function new_log_handler(file_name : string; + format : log_format_t; + use_color : boolean) return log_handler_t; + + procedure init_log_handler(log_handler : log_handler_t; + format : log_format_t; + file_name : string; + use_color : boolean := false); +end package; diff --git a/vunit/vhdl/logging/src/log_levels_pkg-body.vhd b/vunit/vhdl/logging/src/log_levels_pkg-body.vhd index 7821f6e8d..6427f0ca0 100644 --- a/vunit/vhdl/logging/src/log_levels_pkg-body.vhd +++ b/vunit/vhdl/logging/src/log_levels_pkg-body.vhd @@ -1,131 +1,131 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.string_ptr_pkg.all; -use work.integer_vector_ptr_pkg.all; -use work.core_pkg.all; - -package body log_levels_pkg is - - type levels_t is record - names : integer_vector_ptr_t; - colors : integer_vector_ptr_t; - max_level_length : integer_vector_ptr_t; - end record; - - procedure add_level( - levels : levels_t; - name : string; - log_level : log_level_t; - fg, bg : ansi_color_t := no_color; - style : ansi_style_t := normal) is - - constant log_level_idx : natural := log_level_t'pos(log_level); - begin - if get(levels.max_level_length, 0) < name'length then - set(levels.max_level_length, 0, name'length); - end if; - - set(levels.names, log_level_idx, to_integer(new_string_ptr(name))); - - set(levels.colors, log_level_idx, - to_integer(integer_vector_ptr_t'(new_integer_vector_ptr(3)))); - set(to_integer_vector_ptr(get(levels.colors, log_level_idx)), 0, - ansi_color_t'pos(fg)); - set(to_integer_vector_ptr(get(levels.colors, log_level_idx)), 1, - ansi_color_t'pos(bg)); - set(to_integer_vector_ptr(get(levels.colors, log_level_idx)), 2, - ansi_style_t'pos(style)); - - end; - - impure function create_levels return levels_t is - variable result : levels_t; - variable name_ptr : string_ptr_t; - - procedure add_level(log_level : log_level_t; fg, bg : ansi_color_t := no_color; style : ansi_style_t := normal) is - begin - add_level(result, log_level_t'image(log_level), log_level, fg, bg, style); - end; - begin - result := (names => new_integer_vector_ptr(log_level_t'pos(log_level_t'high)+1, - value => to_integer(null_string_ptr)), - colors => new_integer_vector_ptr(log_level_t'pos(log_level_t'high)+1, - value => to_integer(null_ptr)), - max_level_length => new_integer_vector_ptr(1, value => 0)); - - add_level(trace, fg => magenta, style => bright); - add_level(debug, fg => cyan, style => bright); - add_level(pass, fg => green, style => bright); - add_level(info, fg => white, style => bright); - add_level(warning, fg => yellow, style => bright); - add_level(error, fg => red, style => bright); - add_level(failure, fg => white, bg => red, style => bright); - - return result; - end; - - constant levels : levels_t := create_levels; - - impure function new_log_level(name : string; - fg : ansi_color_t := no_color; - bg : ansi_color_t := no_color; - style : ansi_style_t := normal) return log_level_t is - variable log_level : log_level_t := null_log_level; - begin - - -- Take first free log level - for level in legal_log_level_t'low to legal_log_level_t'high loop - if not is_valid(level) then - log_level := level; - exit; - end if; - end loop; - - if log_level = null_log_level then - core_failure("Cannot create custom log level " & name & - " already used all " & integer'image(max_num_custom_log_levels) & - " custom log levels"); - return null_log_level; - end if; - - add_level(levels, name, log_level, fg, bg, style); - - return log_level; - end; - - impure function is_valid(log_level : log_level_t) return boolean is - variable name_ptr : string_ptr_t := to_string_ptr(get(levels.names, log_level_t'pos(log_level))); - begin - return name_ptr /= null_string_ptr; - end; - - impure function get_color(log_level : log_level_t) return ansi_colors_t is - variable color_ptr : integer_vector_ptr_t := to_integer_vector_ptr(get(levels.colors, log_level_t'pos(log_level))); - begin - if color_ptr = null_ptr then - return no_colors; - end if; - return (fg => ansi_color_t'val(get(color_ptr, 0)), - bg => ansi_color_t'val(get(color_ptr, 1)), - style => ansi_style_t'val(get(color_ptr, 2))); - end; - - impure function get_name(log_level : log_level_t) return string is - variable name_ptr : string_ptr_t := to_string_ptr(get(levels.names, log_level_t'pos(log_level))); - begin - if name_ptr = null_string_ptr then - core_failure("Use of undefined level " & log_level_t'image(log_level) & "."); - return log_level_t'image(log_level); - end if; - return to_string(name_ptr); - end; - - impure function max_level_length return natural is - begin - return get(levels.max_level_length, 0); - end; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.string_ptr_pkg.all; +use work.integer_vector_ptr_pkg.all; +use work.core_pkg.all; + +package body log_levels_pkg is + + type levels_t is record + names : integer_vector_ptr_t; + colors : integer_vector_ptr_t; + max_level_length : integer_vector_ptr_t; + end record; + + procedure add_level( + levels : levels_t; + name : string; + log_level : log_level_t; + fg, bg : ansi_color_t := no_color; + style : ansi_style_t := normal) is + + constant log_level_idx : natural := log_level_t'pos(log_level); + begin + if get(levels.max_level_length, 0) < name'length then + set(levels.max_level_length, 0, name'length); + end if; + + set(levels.names, log_level_idx, to_integer(new_string_ptr(name))); + + set(levels.colors, log_level_idx, + to_integer(integer_vector_ptr_t'(new_integer_vector_ptr(3)))); + set(to_integer_vector_ptr(get(levels.colors, log_level_idx)), 0, + ansi_color_t'pos(fg)); + set(to_integer_vector_ptr(get(levels.colors, log_level_idx)), 1, + ansi_color_t'pos(bg)); + set(to_integer_vector_ptr(get(levels.colors, log_level_idx)), 2, + ansi_style_t'pos(style)); + + end; + + impure function create_levels return levels_t is + variable result : levels_t; + variable name_ptr : string_ptr_t; + + procedure add_level(log_level : log_level_t; fg, bg : ansi_color_t := no_color; style : ansi_style_t := normal) is + begin + add_level(result, log_level_t'image(log_level), log_level, fg, bg, style); + end; + begin + result := (names => new_integer_vector_ptr(log_level_t'pos(log_level_t'high)+1, + value => to_integer(null_string_ptr)), + colors => new_integer_vector_ptr(log_level_t'pos(log_level_t'high)+1, + value => to_integer(null_ptr)), + max_level_length => new_integer_vector_ptr(1, value => 0)); + + add_level(trace, fg => magenta, style => bright); + add_level(debug, fg => cyan, style => bright); + add_level(pass, fg => green, style => bright); + add_level(info, fg => white, style => bright); + add_level(warning, fg => yellow, style => bright); + add_level(error, fg => red, style => bright); + add_level(failure, fg => white, bg => red, style => bright); + + return result; + end; + + constant levels : levels_t := create_levels; + + impure function new_log_level(name : string; + fg : ansi_color_t := no_color; + bg : ansi_color_t := no_color; + style : ansi_style_t := normal) return log_level_t is + variable log_level : log_level_t := null_log_level; + begin + + -- Take first free log level + for level in legal_log_level_t'low to legal_log_level_t'high loop + if not is_valid(level) then + log_level := level; + exit; + end if; + end loop; + + if log_level = null_log_level then + core_failure("Cannot create custom log level " & name & + " already used all " & integer'image(max_num_custom_log_levels) & + " custom log levels"); + return null_log_level; + end if; + + add_level(levels, name, log_level, fg, bg, style); + + return log_level; + end; + + impure function is_valid(log_level : log_level_t) return boolean is + variable name_ptr : string_ptr_t := to_string_ptr(get(levels.names, log_level_t'pos(log_level))); + begin + return name_ptr /= null_string_ptr; + end; + + impure function get_color(log_level : log_level_t) return ansi_colors_t is + variable color_ptr : integer_vector_ptr_t := to_integer_vector_ptr(get(levels.colors, log_level_t'pos(log_level))); + begin + if color_ptr = null_ptr then + return no_colors; + end if; + return (fg => ansi_color_t'val(get(color_ptr, 0)), + bg => ansi_color_t'val(get(color_ptr, 1)), + style => ansi_style_t'val(get(color_ptr, 2))); + end; + + impure function get_name(log_level : log_level_t) return string is + variable name_ptr : string_ptr_t := to_string_ptr(get(levels.names, log_level_t'pos(log_level))); + begin + if name_ptr = null_string_ptr then + core_failure("Use of undefined level " & log_level_t'image(log_level) & "."); + return log_level_t'image(log_level); + end if; + return to_string(name_ptr); + end; + + impure function max_level_length return natural is + begin + return get(levels.max_level_length, 0); + end; +end package body; diff --git a/vunit/vhdl/logging/src/log_levels_pkg.vhd b/vunit/vhdl/logging/src/log_levels_pkg.vhd index ab81503f7..09fdc6afc 100644 --- a/vunit/vhdl/logging/src/log_levels_pkg.vhd +++ b/vunit/vhdl/logging/src/log_levels_pkg.vhd @@ -1,50 +1,50 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.string_ptr_pkg.all; -use work.ansi_pkg.all; - -package log_levels_pkg is - - type log_level_t is ( - null_log_level, - - trace, - debug, - pass, - info, - warning, - error, - failure, - - custom_level1, - custom_level2, - custom_level3, - custom_level4, - custom_level5, - custom_level6, - custom_level7, - custom_level8); - - type log_level_vec_t is array (natural range <>) of log_level_t; - constant null_vec : log_level_vec_t(1 to 0) := (others => info); - - subtype alert_log_level_t is log_level_t range warning to failure; - subtype legal_log_level_t is log_level_t range log_level_t'succ(null_log_level) to log_level_t'high; - - constant max_num_custom_log_levels : natural := ( - 1 + log_level_t'pos(log_level_t'high) - log_level_t'pos(custom_level1)); - - impure function new_log_level(name : string; - fg : ansi_color_t := no_color; - bg : ansi_color_t := no_color; - style : ansi_style_t := normal) return log_level_t; - impure function is_valid(log_level : log_level_t) return boolean; - - impure function get_name(log_level : log_level_t) return string; - impure function get_color(log_level : log_level_t) return ansi_colors_t; - impure function max_level_length return natural; -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.string_ptr_pkg.all; +use work.ansi_pkg.all; + +package log_levels_pkg is + + type log_level_t is ( + null_log_level, + + trace, + debug, + pass, + info, + warning, + error, + failure, + + custom_level1, + custom_level2, + custom_level3, + custom_level4, + custom_level5, + custom_level6, + custom_level7, + custom_level8); + + type log_level_vec_t is array (natural range <>) of log_level_t; + constant null_vec : log_level_vec_t(1 to 0) := (others => info); + + subtype alert_log_level_t is log_level_t range warning to failure; + subtype legal_log_level_t is log_level_t range log_level_t'succ(null_log_level) to log_level_t'high; + + constant max_num_custom_log_levels : natural := ( + 1 + log_level_t'pos(log_level_t'high) - log_level_t'pos(custom_level1)); + + impure function new_log_level(name : string; + fg : ansi_color_t := no_color; + bg : ansi_color_t := no_color; + style : ansi_style_t := normal) return log_level_t; + impure function is_valid(log_level : log_level_t) return boolean; + + impure function get_name(log_level : log_level_t) return string; + impure function get_color(log_level : log_level_t) return ansi_colors_t; + impure function max_level_length return natural; +end package; diff --git a/vunit/vhdl/logging/src/logger_pkg-body.vhd b/vunit/vhdl/logging/src/logger_pkg-body.vhd index e0cc9f92b..5ffdc1210 100644 --- a/vunit/vhdl/logging/src/logger_pkg-body.vhd +++ b/vunit/vhdl/logging/src/logger_pkg-body.vhd @@ -1,1251 +1,1251 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.string_ptr_pkg.all; -use work.integer_vector_ptr_pkg.all; -use work.queue_pkg.all; -use work.core_pkg.core_failure; -use std.textio.all; -use work.string_ops.all; -use work.print_pkg.print; -use work.ansi_pkg.all; - -package body logger_pkg is - constant root_logger_id : natural := 0; - constant next_logger_id : integer_vector_ptr_t := new_integer_vector_ptr(1, value => root_logger_id + 1); - constant global_log_count : integer_vector_ptr_t := new_integer_vector_ptr(1, value => 0); - constant p_mock_queue_length : integer_vector_ptr_t := new_integer_vector_ptr(1, value => 0); - constant mock_queue : queue_t := new_queue; - - constant id_idx : natural := 0; - constant name_idx : natural := 1; - constant parent_idx : natural := 2; - constant children_idx : natural := 3; - constant log_count_idx : natural := 4; - constant stop_counts_idx : natural := 5; - constant handlers_idx : natural := 6; - constant state_idx : natural := 7; - constant log_level_filters_idx : natural := 8; - constant logger_length : natural := 9; - - constant log_level_invisible : integer := 0; - constant log_level_visible : integer := 1; - - constant stop_count_unset : integer := 0; - constant stop_count_infinite : integer := integer'high; - - constant enabled_state : natural := 0; - constant disabled_state : natural := 1; - constant mocked_state : natural := 2; - - constant n_log_levels : natural := log_level_t'pos(log_level_t'high) + 1; - - impure function to_integer(logger : logger_t) return integer is - begin - return to_integer(logger.p_data); - end; - - procedure add_child(logger : logger_t; child : logger_t) is - constant children : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, children_idx)); - begin - resize(children, length(children)+1); - set(children, length(children)-1, to_integer(child)); - end; - - impure function new_logger(id : natural; - name : string; - parent : logger_t) return logger_t is - variable logger : logger_t; - variable log_handler : log_handler_t; - begin - logger := (p_data => new_integer_vector_ptr(logger_length)); - set(logger.p_data, id_idx, id); - set(logger.p_data, name_idx, to_integer(new_string_ptr(name))); - set(logger.p_data, parent_idx, to_integer(parent)); - set(logger.p_data, children_idx, to_integer(new_integer_vector_ptr)); - set(logger.p_data, log_count_idx, to_integer(new_integer_vector_ptr(log_level_t'pos(log_level_t'high)+1, value => 0))); - set(logger.p_data, stop_counts_idx, to_integer(new_integer_vector_ptr(log_level_t'pos(log_level_t'high)+1, value => stop_count_unset))); - set(logger.p_data, handlers_idx, to_integer(new_integer_vector_ptr)); - set(logger.p_data, state_idx, to_integer(new_integer_vector_ptr(log_level_t'pos(log_level_t'high)+1, value => enabled_state))); - set(logger.p_data, log_level_filters_idx, to_integer(new_integer_vector_ptr)); - - if parent /= null_logger then - add_child(parent, logger); - - -- Re-use parent log handlers and log level settings - set_log_handlers(logger, get_log_handlers(parent)); - - for i in 0 to num_log_handlers(parent)-1 loop - log_handler := get_log_handler(parent, i); - show(logger, log_handler, get_visible_log_levels(parent, log_handler)); - hide(logger, log_handler, get_invisible_log_levels(parent, log_handler)); - end loop; - - end if; - - return logger; - end; - - procedure p_set_log_handlers(logger : logger_t; - log_handlers : log_handler_vec_t) is - constant handlers : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, handlers_idx)); - begin - resize(handlers, log_handlers'length); - - for i in log_handlers'range loop - set(handlers, i, to_integer(log_handlers(i).p_data)); - update_max_logger_name_length(log_handlers(i), get_full_name(logger)'length); - end loop; - end; - - -- @NOTE this procedure needs to be above root logger creation to around - -- Riviera-PRO elaboration bug - procedure set_log_level_filter(logger : logger_t; - log_handler : log_handler_t; - log_levels : log_level_vec_t; - visible : boolean; - include_children : boolean) is - constant log_level_filters : integer_vector_ptr_t := - to_integer_vector_ptr(get(logger.p_data, log_level_filters_idx)); - constant handler_id : natural := get_id(log_handler); - variable log_level_filter : integer_vector_ptr_t; - variable log_level_setting : natural; - - begin - if handler_id >= length(log_level_filters) then - resize(log_level_filters, handler_id + 1, value => to_integer(null_ptr)); - end if; - - log_level_filter := to_integer_vector_ptr(get(log_level_filters, handler_id)); - - if log_level_filter = null_ptr then - -- Only show valid log levels by default - log_level_filter := new_integer_vector_ptr(len => n_log_levels, value => log_level_invisible); - for log_level in log_level_t'low to log_level_t'high loop - if is_valid(log_level) then - set(log_level_filter, log_level_t'pos(log_level), log_level_visible); - end if; - end loop; - - set(log_level_filters, handler_id, to_integer(log_level_filter)); - end if; - - if visible then - log_level_setting := log_level_visible; - else - log_level_setting := log_level_invisible; - end if; - - for i in log_levels'range loop - set(log_level_filter, log_level_t'pos(log_levels(i)), log_level_setting); - end loop; - - if include_children then - for i in 0 to num_children(logger)-1 loop - set_log_level_filter(get_child(logger, i), log_handler, log_levels, visible, - include_children => true); - end loop; - end if; - end; - - impure function new_logger(name : string; parent : logger_t) return logger_t is - constant id : natural := get(next_logger_id, 0); - begin - set(next_logger_id, 0, id + 1); - return new_logger(id, name, parent); - end; - - impure function get_id(logger : logger_t) return natural is - begin - return get(logger.p_data, id_idx); - end; - - impure function get_real_parent(parent : logger_t) return logger_t is - begin - if parent = null_logger then - return root_logger; - end if; - return parent; - end; - - impure function head(name : string; dot_idx : natural) return string is - begin - if dot_idx = 0 then - return name; - else - return name(name'left to dot_idx-1); - end if; - end; - - impure function tail(name : string; dot_idx : natural) return string is - begin - if dot_idx = 0 then - return ""; - else - return name(dot_idx+1 to name'right); - end if; - end; - - impure function validate_logger_name(name : string; - parent : logger_t) return boolean is - function join(s1, s2 : string) return string is - begin - if s1 = "" then - return s2; - else - return s1 & ":" & s2; - end if; - end; - - constant full_name : string := join(get_name(parent), name); - begin - if name = "" then - core_failure("Invalid logger name """ & full_name & """"); - end if; - - for i in full_name'range loop - if full_name(i) = ',' then - core_failure("Invalid logger name """ & full_name & """"); - return false; - end if; - end loop; - - return true; - end; - - impure function get_logger(name : string; - parent : logger_t := null_logger) return logger_t is - constant real_parent : logger_t := get_real_parent(parent); - variable child, logger : logger_t; - constant stripped_name : string := strip(name, ":"); - constant dot_idx : integer := find(stripped_name, ':'); - constant head_name : string := head(stripped_name, dot_idx); - constant tail_name : string := tail(stripped_name, dot_idx); - begin - if not validate_logger_name(head_name, real_parent) then - return null_logger; - end if; - - logger := null_logger; - for i in 0 to num_children(real_parent)-1 loop - child := get_child(real_parent, i); - - if get_name(child) = head_name then - logger := child; - exit; - end if; - end loop; - - if logger = null_logger then - logger := new_logger(head_name, real_parent); - set_log_handlers(logger, get_log_handlers(real_parent)); - end if; - - if dot_idx /= 0 then - return get_logger(tail_name, logger); - end if; - - return logger; - end; - - impure function get_full_name(logger : logger_t) return string is - variable parent : logger_t := get_parent(logger); - begin - if parent = null_logger or get_id(parent) = root_logger_id then - return get_name(logger); - else - return get_full_name(parent) & ":" & get_name(logger); - end if; - end; - - impure function get_max_name_length(logger : logger_t) return natural is - variable result : natural := 0; - variable child_result : natural; - begin - if num_children(logger) = 0 then - return get_full_name(logger)'length; - end if; - - for i in 0 to num_children(logger)-1 loop - child_result := get_max_name_length(get_child(logger, i)); - if child_result > result then - result := child_result; - end if; - end loop; - - return result; - end; - - impure function get_name(logger : logger_t) return string is - begin - return to_string(to_string_ptr(get(logger.p_data, name_idx))); - end; - - impure function get_parent(logger : logger_t) return logger_t is - begin - return (p_data => to_integer_vector_ptr(get(logger.p_data, parent_idx))); - end; - - impure function get_state(logger : logger_t; log_level : log_level_t) return natural is - constant state_vec : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, state_idx)); - begin - return get(state_vec, log_level_t'pos(log_level)); - end; - - procedure set_state(logger : logger_t; log_level : log_level_t; state : natural) is - constant state_vec : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, state_idx)); - begin - set(state_vec, log_level_t'pos(log_level), state); - end; - - impure function is_disabled(logger : logger_t; - log_level : log_level_t) return boolean is - begin - return get_state(logger, log_level) = disabled_state; - end; - - impure function is_mocked(logger : logger_t; log_level : log_level_t) return boolean is - begin - return get_state(logger, log_level) = mocked_state; - end; - - impure function is_mocked(logger : logger_t) return boolean is - begin - for log_level in legal_log_level_t'low to legal_log_level_t'high loop - if is_mocked(logger, log_level) then - return true; - end if; - end loop; - return false; - end; - - impure function num_children(logger : logger_t) return natural is - constant children : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, children_idx)); - begin - return length(children); - end; - - impure function get_child(logger : logger_t; idx : natural) return logger_t is - constant children : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, children_idx)); - begin - return (p_data => to_integer_vector_ptr(get(children, idx))); - end; - - procedure p_set_stop_count(logger : logger_t; - log_level : log_level_t; - value : natural; - unset_children : boolean := false) is - constant stop_counts : integer_vector_ptr_t := to_integer_vector_ptr( - get(logger.p_data, stop_counts_idx)); - constant log_level_idx : natural := log_level_t'pos(log_level); - begin - if log_level_idx >= length(stop_counts) then - resize(stop_counts, log_level_idx + 1, value => integer'high); - end if; - - set(stop_counts, log_level_idx, value); - - if unset_children then - for idx in 0 to num_children(logger)-1 loop - p_set_stop_count(get_child(logger, idx), log_level, stop_count_unset, - unset_children => true); - end loop; - end if; - end; - - - procedure set_stop_count(logger : logger_t; - log_level : log_level_t; - value : positive; - unset_children : boolean := false) is - begin - p_set_stop_count(logger, log_level, value, unset_children); - end; - - procedure disable_stop(logger : logger_t; - log_level : log_level_t; - unset_children : boolean := false) is - begin - p_set_stop_count(logger, log_level, stop_count_infinite, unset_children); - end; - - procedure set_stop_count(log_level : log_level_t; - value : positive) is - begin - set_stop_count(root_logger, log_level, value, unset_children => true); - end; - - procedure disable_stop(log_level : log_level_t) is - begin - disable_stop(root_logger, log_level, unset_children => true); - end; - - procedure set_stop_level(level : alert_log_level_t) is - begin - set_stop_level(root_logger, level); - end; - - procedure set_stop_level(logger : logger_t; - log_level : alert_log_level_t) is - variable stop_count : natural; - begin - for level in log_level_t'low to log_level_t'high loop - disable_stop(logger, level, - unset_children => true); - end loop; - - for level in alert_log_level_t'low to alert_log_level_t'high loop - if level >= log_level then - set_stop_count(logger, level, 1, - unset_children => true); - end if; - end loop; - end; - - procedure unset_stop_count(logger : logger_t; - log_level : log_level_t; - unset_children : boolean := false) is - begin - p_set_stop_count(logger, log_level, stop_count_unset, unset_children); - end; - - impure function p_get_stop_count(logger : logger_t; - log_level : log_level_t) return natural is - constant stop_counts : integer_vector_ptr_t := to_integer_vector_ptr( - get(logger.p_data, stop_counts_idx)); - constant log_level_idx : natural := log_level_t'pos(log_level); - begin - if log_level_idx >= length(stop_counts) then - resize(stop_counts, log_level_idx + 1, value => integer'high); - end if; - - return get(stop_counts, log_level_idx); - end; - - impure function get_stop_count(logger : logger_t; - log_level : log_level_t) return positive is - constant stop_count : integer := p_get_stop_count(logger, log_level); - begin - if stop_count = stop_count_unset then - core_failure("Logger " & get_full_name(logger) & " has no stop count set"); - end if; - - return stop_count; - end; - - impure function has_stop_count(logger : logger_t; - log_level : log_level_t) return boolean is - constant stop_count : integer := p_get_stop_count(logger, log_level); - begin - return stop_count /= stop_count_unset; - end; - - impure function get_log_level_filter(logger : logger_t; - log_handler : log_handler_t) return integer_vector_ptr_t is - constant log_level_filters : integer_vector_ptr_t := - to_integer_vector_ptr(get(logger.p_data, log_level_filters_idx)); - constant handler_id : natural := get_id(log_handler); - begin - if handler_id >= length(log_level_filters) then - resize(log_level_filters, handler_id + 1, value => to_integer(null_ptr)); - end if; - - return to_integer_vector_ptr(get(log_level_filters, handler_id)); - end; - - impure function get_log_level_filter(logger : logger_t; - log_handler : log_handler_t; - visible : boolean) return log_level_vec_t is - variable ret : log_level_vec_t(0 to n_log_levels - 1); - variable idx : natural := 0; - constant log_level_filter : integer_vector_ptr_t := get_log_level_filter(logger, log_handler); - variable log_level_setting : natural; - variable log_level : log_level_t; - begin - if log_level_filter = null_ptr then - return null_vec; - end if; - - if visible then - log_level_setting := log_level_visible; - else - log_level_setting := log_level_invisible; - end if; - - for i in 0 to length(log_level_filter) - 1 loop - log_level := log_level_t'val(i); - if get(log_level_filter, i) = log_level_setting and is_valid(log_level) then - ret(idx) := log_level; - idx := idx + 1; - end if; - end loop; - - return ret(0 to idx - 1); - end; - - impure function get_visible_log_levels(logger : logger_t; - log_handler : log_handler_t) return log_level_vec_t is - begin - return get_log_level_filter(logger, log_handler, visible => true); - end; - - impure function get_invisible_log_levels(logger : logger_t; - log_handler : log_handler_t) return log_level_vec_t is - begin - return get_log_level_filter(logger, log_handler, visible => false); - end; - - procedure disable(logger : logger_t; - log_level : log_level_t; - include_children : boolean := true) is - begin - set_state(logger, log_level, disabled_state); - - if include_children then - for idx in 0 to num_children(logger)-1 loop - disable(get_child(logger, idx), log_level, include_children => true); - end loop; - end if; - end; - - -- Disable logging for the specified level to this handler from specific - -- logger and all children. - procedure hide(logger : logger_t; - log_handler : log_handler_t; - log_level : log_level_t; - include_children : boolean := true) is - begin - set_log_level_filter(logger, log_handler, (0 => log_level), visible => false, - include_children => include_children); - end; - - -- Disable logging for the specified level to this handler - procedure hide(log_handler : log_handler_t; - log_level : log_level_t) is - begin - hide(root_logger, log_handler, log_level, include_children => true); - end; - -- Disable logging for the specified levels to this handler from specific - -- logger and all children. - procedure hide(logger : logger_t; - log_handler : log_handler_t; - log_levels : log_level_vec_t; - include_children : boolean := true) is - begin - set_log_level_filter(logger, log_handler, log_levels, visible => false, - include_children => include_children); - end; - - -- Disable logging for the specified levels to this handler - procedure hide(log_handler : log_handler_t; - log_levels : log_level_vec_t) is - begin - hide(root_logger, log_handler, log_levels); - end; - - procedure hide_all(logger : logger_t; - log_handler : log_handler_t; - include_children : boolean := true) is - begin - for log_level in log_level_t'low to log_level_t'high loop - hide(logger, log_handler, log_level, include_children => include_children); - end loop; - end; - - procedure hide_all(log_handler : log_handler_t) is - begin - hide_all(root_logger, log_handler, include_children => true); - end; - - procedure show(logger : logger_t; - log_handler : log_handler_t; - log_level : log_level_t; - include_children : boolean := true) is - begin - set_log_level_filter(logger, log_handler, (0 => log_level), visible => true, - include_children => include_children); - end; - - procedure show(log_handler : log_handler_t; - log_level : log_level_t) is - begin - show(root_logger, log_handler, log_level, include_children => true); - end; - - procedure show(logger : logger_t; - log_handler : log_handler_t; - log_levels : log_level_vec_t; - include_children : boolean := true) is - begin - set_log_level_filter(logger, log_handler, log_levels, visible => true, - include_children => include_children); - end; - - procedure show(log_handler : log_handler_t; - log_levels : log_level_vec_t) is - begin - show(root_logger, log_handler, log_levels, include_children => true); - end; - - procedure show_all(logger : logger_t; - log_handler : log_handler_t; - include_children : boolean := true) is - begin - for log_level in log_level_t'low to log_level_t'high loop - show(logger, log_handler, log_level, - include_children => include_children); - end loop; - end; - - procedure show_all(log_handler : log_handler_t) is - begin - show_all(root_logger, log_handler, include_children => true); - end; - - impure function is_visible(logger : logger_t; - log_level : log_level_t) return boolean is - constant state : natural := get_state(logger, log_level); - begin - if state = mocked_state then - return true; - elsif state = disabled_state then - return false; - end if; - - for i in 0 to num_log_handlers(logger)-1 loop - if is_visible(logger, get_log_handler(logger, i), log_level) then - return true; - end if; - end loop; - - return false; - end; - - impure function is_visible(logger : logger_t; - log_handler : log_handler_t; - log_level : log_level_t) return boolean is - constant log_level_filter : integer_vector_ptr_t := get_log_level_filter(logger, log_handler); - begin - if log_level_filter /= null_ptr then - return get(log_level_filter, log_level_t'pos(log_level)) = log_level_visible; - else - return false; - end if; - end; - - impure function num_log_handlers(logger : logger_t) return natural is - constant handlers : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, handlers_idx)); - begin - return length(handlers); - end; - - impure function get_log_handler(logger : logger_t; idx : natural) return log_handler_t is - constant handlers : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, handlers_idx)); - begin - return (p_data => to_integer_vector_ptr(get(handlers, idx))); - end; - - impure function get_log_handlers(logger : logger_t) return log_handler_vec_t is - constant handlers : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, handlers_idx)); - variable result : log_handler_vec_t(0 to length(handlers)-1); - begin - for i in result'range loop - result(i) := (p_data => to_integer_vector_ptr(get(handlers, i))); - end loop; - return result; - end; - - procedure set_log_handlers(logger : logger_t; - log_handlers : log_handler_vec_t; - include_children : boolean := true) is - begin - p_set_log_handlers(logger, log_handlers); - - if include_children then - for i in 0 to num_children(logger)-1 loop - set_log_handlers(get_child(logger, i), log_handlers, - include_children => true); - end loop; - end if; - end; - - procedure clear_log_count(logger : logger_t; idx : natural) is - constant log_counts : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, idx)); - begin - for lvl in log_level_t'low to log_level_t'high loop - set(log_counts, log_level_t'pos(lvl), 0); - end loop; - end; - - impure function get_log_count(logger : logger_t; - idx : natural; - log_level : log_level_t := null_log_level) return natural is - constant log_counts : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, idx)); - variable result : natural; - begin - if log_level = null_log_level then - result := 0; - for lvl in log_level_t'low to log_level_t'high loop - result := result + get(log_counts, log_level_t'pos(lvl)); - end loop; - else - result := get(log_counts, log_level_t'pos(log_level)); - end if; - - return result; - end; - - procedure reset_log_count(logger : logger_t; - log_level : log_level_t := null_log_level; - include_children : boolean := true) is - constant log_counts : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, log_count_idx)); - begin - if log_level = null_log_level then - for lvl in log_level_t'low to log_level_t'high loop - set(log_counts, log_level_t'pos(lvl), 0); - end loop; - else - set(log_counts, log_level_t'pos(log_level), 0); - end if; - - if include_children then - for idx in 0 to num_children(logger)-1 loop - reset_log_count(get_child(logger, idx), log_level, include_children => true); - end loop; - end if; - end; - - impure function get_log_count return natural is - begin - return get(global_log_count, 0); - end; - - impure function get_log_count(logger : logger_t; log_level : log_level_t := null_log_level) return natural is - begin - return get_log_count(logger, log_count_idx, log_level); - end; - - procedure decrease_stop_count(logger : logger_t; - log_level : log_level_t) is - constant stop_count : natural := p_get_stop_count(logger, log_level); - variable parent : logger_t; - begin - if stop_count = stop_count_unset then - parent := get_parent(logger); - if parent = null_logger then - core_failure("Stop condition not set on root_logger"); - else - decrease_stop_count(parent, log_level); - end if; - elsif stop_count = stop_count_infinite then - null; - elsif stop_count = 1 then - core_failure("Stop simulation on log level " & get_name(log_level)); - else - p_set_stop_count(logger, log_level, stop_count - 1, unset_children => false); - end if; - end; - - procedure count_log(logger : logger_t; log_level : log_level_t) is - constant log_counts : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, log_count_idx)); - begin - set(global_log_count, 0, get(global_log_count, 0) + 1); - set(log_counts, log_level_t'pos(log_level), get(log_counts, log_level_t'pos(log_level)) + 1); - end; - - procedure mock(logger : logger_t) is - begin - for log_level in legal_log_level_t'low to legal_log_level_t'high loop - mock(logger, log_level); - end loop; - end; - - procedure mock(logger : logger_t; log_level : log_level_t) is - begin - set_state(logger, log_level, mocked_state); - end; - - impure function make_string(logger_name : string; - msg : string; - log_level : log_level_t; - log_time : time; - line_num : natural; - file_name : string; - check_time : boolean) return string is - constant without_time : string := (" logger = " & logger_name & LF & - " log_level = " & get_name(log_level) & LF & - " msg = " & msg & LF & - " file_name:line_num = " & file_name & ":" & integer'image(line_num)); - begin - if check_time then - return " time = " & time'image(log_time) & LF & without_time; - else - return without_time; - end if; - end; - - impure function pop_log_item_string(check_time : boolean) return string is - constant got_logger_name : string := pop_string(mock_queue); - constant got_level : log_level_t := log_level_t'val(pop_byte(mock_queue)); - constant got_msg : string := pop_string(mock_queue); - constant got_log_time : time := pop_time(mock_queue); - constant got_line_num : natural := pop_integer(mock_queue); - constant got_file_name : string := pop_string(mock_queue); - begin - set(p_mock_queue_length, 0, get(p_mock_queue_length, 0) - 1); - return make_string(got_logger_name, got_msg, got_level, got_log_time, got_line_num, got_file_name, check_time); - end; - - procedure check_log(logger : logger_t; - msg : string; - log_level : log_level_t; - log_time : time := no_time_check; - line_num : natural := 0; - file_name : string := "") is - - constant expected_item : string := make_string(get_full_name(logger), - msg, log_level, log_time, line_num, file_name, - log_time /= no_time_check); - - procedure check_log_when_not_empty is - constant got_item : string := pop_log_item_string(log_time /= no_time_check); - begin - if expected_item /= got_item then - core_failure("log item mismatch:" & LF & LF & "Got:" & LF & got_item & LF & LF & "expected:" & LF & expected_item & LF); - end if; - end; - begin - if length(mock_queue) > 0 then - check_log_when_not_empty; - else - core_failure("log item mismatch - Got no log item " & LF & LF & "expected" & LF & expected_item & LF); - end if; - end; - - procedure check_only_log(logger : logger_t; - msg : string; - log_level : log_level_t; - log_time : time := no_time_check; - line_num : natural := 0; - file_name : string := "") is - begin - check_log(logger, msg, log_level, log_time, line_num, file_name); - check_no_log; - end; - - procedure check_no_log is - variable fail : boolean := length(mock_queue) > 0; - begin - while length(mock_queue) > 0 loop - report "Got unexpected log item " & LF & LF & pop_log_item_string(true) & LF; - end loop; - - if fail then - core_failure("Got unexpected log items"); - end if; - end; - - impure function mock_queue_length return natural is - begin - return get(p_mock_queue_length, 0); - end; - - procedure unmock(logger : logger_t) is - begin - check_no_log; - - for log_level in legal_log_level_t'low to legal_log_level_t'high loop - if is_mocked(logger, log_level) then - set_state(logger, log_level, enabled_state); - end if; - end loop; - end; - - procedure mock_log(logger : logger_t; - msg : string; - log_level : log_level_t; - log_time : time; - line_num : natural := 0; - file_name : string := "") is - begin - report ("Got mocked log item " & LF & - make_string(get_full_name(logger), msg, log_level, log_time, line_num, file_name, - check_time => true) & LF); - push_string(mock_queue, get_full_name(logger)); - push_byte(mock_queue, log_level_t'pos(log_level)); - push_string(mock_queue, msg); - push_time(mock_queue, log_time); - push_integer(mock_queue, line_num); - push_string(mock_queue, file_name); - - set(p_mock_queue_length, 0, get(p_mock_queue_length, 0) + 1); - end; - - procedure log(logger : logger_t; - msg : string; - log_level : log_level_t := info; - line_num : natural := 0; - file_name : string := "") is - - variable log_handler : log_handler_t; - constant t_now : time := now; - constant sequence_number : natural := get_log_count; - variable state : natural; - begin - if logger = null_logger then - core_failure("Attempt to log to uninitialized logger"); - return; - end if; - - state := get_state(logger, log_level); - - if state = mocked_state then - mock_log(logger, msg, log_level, t_now, line_num, file_name); - else - if state = enabled_state then - for i in 0 to num_log_handlers(logger) - 1 loop - log_handler := get_log_handler(logger, i); - if is_visible(logger, log_handler, log_level) then - log_to_handler(log_handler, get_full_name(logger), msg, log_level, - t_now, sequence_number, - line_num, file_name); - end if; - end loop; - - decrease_stop_count(logger, log_level); - end if; - - -- Count even if disabled - count_log(logger, log_level); - end if; - end procedure; - - procedure debug(logger : logger_t; - msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - log(logger, msg, debug, line_num, file_name); - end procedure; - - procedure pass(logger : logger_t; - msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - log(logger, msg, pass, line_num, file_name); - end procedure; - - procedure trace(logger : logger_t; - msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - log(logger, msg, trace, line_num, file_name); - end procedure; - - procedure info(logger : logger_t; - msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - log(logger, msg, info, line_num, file_name); - end procedure; - - procedure warning(logger : logger_t; - msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - log(logger, msg, warning, line_num, file_name); - end procedure; - - procedure error(logger : logger_t; - msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - log(logger, msg, error, line_num, file_name); - end procedure; - - procedure failure(logger : logger_t; - msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - log(logger, msg, failure, line_num, file_name); - end procedure; - - procedure warning_if(logger : logger_t; - condition : boolean; - msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - if condition then - warning(logger, msg, line_num => line_num, file_name => file_name); - end if; - end; - - procedure error_if(logger : logger_t; - condition : boolean; - msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - if condition then - error(logger, msg, line_num => line_num, file_name => file_name); - end if; - end; - - procedure failure_if(logger : logger_t; - condition : boolean; - msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - if condition then - failure(logger, msg, line_num => line_num, file_name => file_name); - end if; - end; - - impure function new_root_logger return logger_t is - variable logger : logger_t := new_logger(root_logger_id, "", null_logger); - begin - p_set_log_handlers(logger, (0 => display_handler)); - - for log_level in legal_log_level_t'low to legal_log_level_t'high loop - case log_level is - when error|failure => - set_stop_count(logger, log_level, 1); - when others => - disable_stop(logger, log_level); - end case; - end loop; - - hide_all(logger, display_handler); - show(logger, display_handler, (info, warning, error, failure)); - return logger; - end; - - constant p_root_logger : logger_t := new_root_logger; - impure function root_logger return logger_t is - begin - return p_root_logger; - end; - - constant p_default_logger : logger_t := get_logger("default"); - impure function default_logger return logger_t is - begin - return p_default_logger; - end; - - procedure log(msg : string; - log_level : log_level_t := info; - line_num : natural := 0; - file_name : string := "") is - begin - log(default_logger, msg, log_level, line_num, file_name); - end; - - procedure debug(msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - debug(default_logger, msg, line_num, file_name); - end procedure; - - procedure pass(msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - pass(default_logger, msg, line_num, file_name); - end procedure; - - procedure trace(msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - trace(default_logger, msg, line_num, file_name); - end procedure; - - procedure info(msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - info(default_logger, msg, line_num, file_name); - end procedure; - - procedure warning(msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - warning(default_logger, msg, line_num, file_name); - end procedure; - - procedure error(msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - error(default_logger, msg, line_num, file_name); - end procedure; - - procedure failure(msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - failure(default_logger, msg, line_num, file_name); - end procedure; - - procedure warning_if(condition : boolean; - msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - if condition then - warning(msg, line_num => line_num, file_name => file_name); - end if; - end; - - procedure error_if(condition : boolean; - msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - if condition then - error(msg, line_num => line_num, file_name => file_name); - end if; - end; - - procedure failure_if(condition : boolean; - msg : string; - line_num : natural := 0; - file_name : string := "") is - begin - if condition then - failure(msg, line_num => line_num, file_name => file_name); - end if; - end; - - impure function level_to_color(log_level : log_level_t) return string is - begin - return colorize(upper(get_name(log_level)), get_color(log_level)); - end; - - impure function source_to_color(logger_name : string) return string is - variable l : line; - - impure function create_string return string is - variable lines : lines_t; - variable num_items : natural; - begin - lines := split(logger_name, ":"); - num_items := integer'(lines.all'length); - for idx in 0 to num_items - 1 loop - write(l, colorize(lines(idx).all, fg => white, style => bright)); - deallocate(lines(idx)); - if idx /= lines'length - 1 then - write(l, colorize(":", fg => lightcyan, style => bright)); - end if; - end loop; - deallocate(lines); - return l.all; - end; - - constant result : string := create_string; - begin - deallocate(l); - return result; - end; - - impure function final_log_check(allow_disabled_errors : boolean := false; - allow_disabled_failures : boolean := false; - fail_on_warning : boolean := false) return boolean is - - impure function p_final_log_check(logger : logger_t) return boolean is - - impure function check_log_level(log_level : log_level_t; allow_disabled : boolean) return boolean is - - function disabled_str(disabled : boolean) return string is - begin - if disabled then - return " disabled"; - else - return ""; - end if; - end; - - function plural_suffix(is_plural : boolean) return string is - begin - if is_plural then - return "s"; - else - return ""; - end if; - end; - - variable count : natural; - variable level_is_disabled : boolean := is_disabled(logger, log_level); - begin - count := get_log_count(logger, log_level); - if count > 0 and not (allow_disabled and level_is_disabled) then - print(level_to_color(failure) & " - Logger " & source_to_color(get_full_name(logger)) & - " has " & integer'image(count) & disabled_str(level_is_disabled) & " " & - get_name(log_level) & plural_suffix(count > 1)); - return false; - end if; - return true; - end; - - variable failed : boolean := false; - begin - for idx in 0 to num_children(logger)-1 loop - if not p_final_log_check(get_child(logger, idx)) then - failed := true; - end if; - end loop; - - if is_mocked(logger) then - print(level_to_color(failure) & " - Logger " & source_to_color(get_full_name(logger)) & - " is still mocked."); - failed := true; - end if; - - if fail_on_warning and not check_log_level(warning, true) then - failed := true; - end if; - - if not check_log_level(error, allow_disabled_errors) then - failed := true; - end if; - - if not check_log_level(failure, allow_disabled_failures) then - failed := true; - end if; - - return not failed; - end; - - begin - if p_final_log_check(root_logger) then - return true; - else - core_failure("Final log check failed"); - return false; - end if; - end; - - procedure final_log_check(allow_disabled_errors : boolean := false; - allow_disabled_failures : boolean := false; - fail_on_warning : boolean := false) is - variable result : boolean; - begin - result := final_log_check(allow_disabled_errors => allow_disabled_errors, - allow_disabled_failures => allow_disabled_failures, - fail_on_warning => fail_on_warning); - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.string_ptr_pkg.all; +use work.integer_vector_ptr_pkg.all; +use work.queue_pkg.all; +use work.core_pkg.core_failure; +use std.textio.all; +use work.string_ops.all; +use work.print_pkg.print; +use work.ansi_pkg.all; + +package body logger_pkg is + constant root_logger_id : natural := 0; + constant next_logger_id : integer_vector_ptr_t := new_integer_vector_ptr(1, value => root_logger_id + 1); + constant global_log_count : integer_vector_ptr_t := new_integer_vector_ptr(1, value => 0); + constant p_mock_queue_length : integer_vector_ptr_t := new_integer_vector_ptr(1, value => 0); + constant mock_queue : queue_t := new_queue; + + constant id_idx : natural := 0; + constant name_idx : natural := 1; + constant parent_idx : natural := 2; + constant children_idx : natural := 3; + constant log_count_idx : natural := 4; + constant stop_counts_idx : natural := 5; + constant handlers_idx : natural := 6; + constant state_idx : natural := 7; + constant log_level_filters_idx : natural := 8; + constant logger_length : natural := 9; + + constant log_level_invisible : integer := 0; + constant log_level_visible : integer := 1; + + constant stop_count_unset : integer := 0; + constant stop_count_infinite : integer := integer'high; + + constant enabled_state : natural := 0; + constant disabled_state : natural := 1; + constant mocked_state : natural := 2; + + constant n_log_levels : natural := log_level_t'pos(log_level_t'high) + 1; + + impure function to_integer(logger : logger_t) return integer is + begin + return to_integer(logger.p_data); + end; + + procedure add_child(logger : logger_t; child : logger_t) is + constant children : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, children_idx)); + begin + resize(children, length(children)+1); + set(children, length(children)-1, to_integer(child)); + end; + + impure function new_logger(id : natural; + name : string; + parent : logger_t) return logger_t is + variable logger : logger_t; + variable log_handler : log_handler_t; + begin + logger := (p_data => new_integer_vector_ptr(logger_length)); + set(logger.p_data, id_idx, id); + set(logger.p_data, name_idx, to_integer(new_string_ptr(name))); + set(logger.p_data, parent_idx, to_integer(parent)); + set(logger.p_data, children_idx, to_integer(new_integer_vector_ptr)); + set(logger.p_data, log_count_idx, to_integer(new_integer_vector_ptr(log_level_t'pos(log_level_t'high)+1, value => 0))); + set(logger.p_data, stop_counts_idx, to_integer(new_integer_vector_ptr(log_level_t'pos(log_level_t'high)+1, value => stop_count_unset))); + set(logger.p_data, handlers_idx, to_integer(new_integer_vector_ptr)); + set(logger.p_data, state_idx, to_integer(new_integer_vector_ptr(log_level_t'pos(log_level_t'high)+1, value => enabled_state))); + set(logger.p_data, log_level_filters_idx, to_integer(new_integer_vector_ptr)); + + if parent /= null_logger then + add_child(parent, logger); + + -- Re-use parent log handlers and log level settings + set_log_handlers(logger, get_log_handlers(parent)); + + for i in 0 to num_log_handlers(parent)-1 loop + log_handler := get_log_handler(parent, i); + show(logger, log_handler, get_visible_log_levels(parent, log_handler)); + hide(logger, log_handler, get_invisible_log_levels(parent, log_handler)); + end loop; + + end if; + + return logger; + end; + + procedure p_set_log_handlers(logger : logger_t; + log_handlers : log_handler_vec_t) is + constant handlers : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, handlers_idx)); + begin + resize(handlers, log_handlers'length); + + for i in log_handlers'range loop + set(handlers, i, to_integer(log_handlers(i).p_data)); + update_max_logger_name_length(log_handlers(i), get_full_name(logger)'length); + end loop; + end; + + -- @NOTE this procedure needs to be above root logger creation to around + -- Riviera-PRO elaboration bug + procedure set_log_level_filter(logger : logger_t; + log_handler : log_handler_t; + log_levels : log_level_vec_t; + visible : boolean; + include_children : boolean) is + constant log_level_filters : integer_vector_ptr_t := + to_integer_vector_ptr(get(logger.p_data, log_level_filters_idx)); + constant handler_id : natural := get_id(log_handler); + variable log_level_filter : integer_vector_ptr_t; + variable log_level_setting : natural; + + begin + if handler_id >= length(log_level_filters) then + resize(log_level_filters, handler_id + 1, value => to_integer(null_ptr)); + end if; + + log_level_filter := to_integer_vector_ptr(get(log_level_filters, handler_id)); + + if log_level_filter = null_ptr then + -- Only show valid log levels by default + log_level_filter := new_integer_vector_ptr(len => n_log_levels, value => log_level_invisible); + for log_level in log_level_t'low to log_level_t'high loop + if is_valid(log_level) then + set(log_level_filter, log_level_t'pos(log_level), log_level_visible); + end if; + end loop; + + set(log_level_filters, handler_id, to_integer(log_level_filter)); + end if; + + if visible then + log_level_setting := log_level_visible; + else + log_level_setting := log_level_invisible; + end if; + + for i in log_levels'range loop + set(log_level_filter, log_level_t'pos(log_levels(i)), log_level_setting); + end loop; + + if include_children then + for i in 0 to num_children(logger)-1 loop + set_log_level_filter(get_child(logger, i), log_handler, log_levels, visible, + include_children => true); + end loop; + end if; + end; + + impure function new_logger(name : string; parent : logger_t) return logger_t is + constant id : natural := get(next_logger_id, 0); + begin + set(next_logger_id, 0, id + 1); + return new_logger(id, name, parent); + end; + + impure function get_id(logger : logger_t) return natural is + begin + return get(logger.p_data, id_idx); + end; + + impure function get_real_parent(parent : logger_t) return logger_t is + begin + if parent = null_logger then + return root_logger; + end if; + return parent; + end; + + impure function head(name : string; dot_idx : natural) return string is + begin + if dot_idx = 0 then + return name; + else + return name(name'left to dot_idx-1); + end if; + end; + + impure function tail(name : string; dot_idx : natural) return string is + begin + if dot_idx = 0 then + return ""; + else + return name(dot_idx+1 to name'right); + end if; + end; + + impure function validate_logger_name(name : string; + parent : logger_t) return boolean is + function join(s1, s2 : string) return string is + begin + if s1 = "" then + return s2; + else + return s1 & ":" & s2; + end if; + end; + + constant full_name : string := join(get_name(parent), name); + begin + if name = "" then + core_failure("Invalid logger name """ & full_name & """"); + end if; + + for i in full_name'range loop + if full_name(i) = ',' then + core_failure("Invalid logger name """ & full_name & """"); + return false; + end if; + end loop; + + return true; + end; + + impure function get_logger(name : string; + parent : logger_t := null_logger) return logger_t is + constant real_parent : logger_t := get_real_parent(parent); + variable child, logger : logger_t; + constant stripped_name : string := strip(name, ":"); + constant dot_idx : integer := find(stripped_name, ':'); + constant head_name : string := head(stripped_name, dot_idx); + constant tail_name : string := tail(stripped_name, dot_idx); + begin + if not validate_logger_name(head_name, real_parent) then + return null_logger; + end if; + + logger := null_logger; + for i in 0 to num_children(real_parent)-1 loop + child := get_child(real_parent, i); + + if get_name(child) = head_name then + logger := child; + exit; + end if; + end loop; + + if logger = null_logger then + logger := new_logger(head_name, real_parent); + set_log_handlers(logger, get_log_handlers(real_parent)); + end if; + + if dot_idx /= 0 then + return get_logger(tail_name, logger); + end if; + + return logger; + end; + + impure function get_full_name(logger : logger_t) return string is + variable parent : logger_t := get_parent(logger); + begin + if parent = null_logger or get_id(parent) = root_logger_id then + return get_name(logger); + else + return get_full_name(parent) & ":" & get_name(logger); + end if; + end; + + impure function get_max_name_length(logger : logger_t) return natural is + variable result : natural := 0; + variable child_result : natural; + begin + if num_children(logger) = 0 then + return get_full_name(logger)'length; + end if; + + for i in 0 to num_children(logger)-1 loop + child_result := get_max_name_length(get_child(logger, i)); + if child_result > result then + result := child_result; + end if; + end loop; + + return result; + end; + + impure function get_name(logger : logger_t) return string is + begin + return to_string(to_string_ptr(get(logger.p_data, name_idx))); + end; + + impure function get_parent(logger : logger_t) return logger_t is + begin + return (p_data => to_integer_vector_ptr(get(logger.p_data, parent_idx))); + end; + + impure function get_state(logger : logger_t; log_level : log_level_t) return natural is + constant state_vec : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, state_idx)); + begin + return get(state_vec, log_level_t'pos(log_level)); + end; + + procedure set_state(logger : logger_t; log_level : log_level_t; state : natural) is + constant state_vec : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, state_idx)); + begin + set(state_vec, log_level_t'pos(log_level), state); + end; + + impure function is_disabled(logger : logger_t; + log_level : log_level_t) return boolean is + begin + return get_state(logger, log_level) = disabled_state; + end; + + impure function is_mocked(logger : logger_t; log_level : log_level_t) return boolean is + begin + return get_state(logger, log_level) = mocked_state; + end; + + impure function is_mocked(logger : logger_t) return boolean is + begin + for log_level in legal_log_level_t'low to legal_log_level_t'high loop + if is_mocked(logger, log_level) then + return true; + end if; + end loop; + return false; + end; + + impure function num_children(logger : logger_t) return natural is + constant children : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, children_idx)); + begin + return length(children); + end; + + impure function get_child(logger : logger_t; idx : natural) return logger_t is + constant children : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, children_idx)); + begin + return (p_data => to_integer_vector_ptr(get(children, idx))); + end; + + procedure p_set_stop_count(logger : logger_t; + log_level : log_level_t; + value : natural; + unset_children : boolean := false) is + constant stop_counts : integer_vector_ptr_t := to_integer_vector_ptr( + get(logger.p_data, stop_counts_idx)); + constant log_level_idx : natural := log_level_t'pos(log_level); + begin + if log_level_idx >= length(stop_counts) then + resize(stop_counts, log_level_idx + 1, value => integer'high); + end if; + + set(stop_counts, log_level_idx, value); + + if unset_children then + for idx in 0 to num_children(logger)-1 loop + p_set_stop_count(get_child(logger, idx), log_level, stop_count_unset, + unset_children => true); + end loop; + end if; + end; + + + procedure set_stop_count(logger : logger_t; + log_level : log_level_t; + value : positive; + unset_children : boolean := false) is + begin + p_set_stop_count(logger, log_level, value, unset_children); + end; + + procedure disable_stop(logger : logger_t; + log_level : log_level_t; + unset_children : boolean := false) is + begin + p_set_stop_count(logger, log_level, stop_count_infinite, unset_children); + end; + + procedure set_stop_count(log_level : log_level_t; + value : positive) is + begin + set_stop_count(root_logger, log_level, value, unset_children => true); + end; + + procedure disable_stop(log_level : log_level_t) is + begin + disable_stop(root_logger, log_level, unset_children => true); + end; + + procedure set_stop_level(level : alert_log_level_t) is + begin + set_stop_level(root_logger, level); + end; + + procedure set_stop_level(logger : logger_t; + log_level : alert_log_level_t) is + variable stop_count : natural; + begin + for level in log_level_t'low to log_level_t'high loop + disable_stop(logger, level, + unset_children => true); + end loop; + + for level in alert_log_level_t'low to alert_log_level_t'high loop + if level >= log_level then + set_stop_count(logger, level, 1, + unset_children => true); + end if; + end loop; + end; + + procedure unset_stop_count(logger : logger_t; + log_level : log_level_t; + unset_children : boolean := false) is + begin + p_set_stop_count(logger, log_level, stop_count_unset, unset_children); + end; + + impure function p_get_stop_count(logger : logger_t; + log_level : log_level_t) return natural is + constant stop_counts : integer_vector_ptr_t := to_integer_vector_ptr( + get(logger.p_data, stop_counts_idx)); + constant log_level_idx : natural := log_level_t'pos(log_level); + begin + if log_level_idx >= length(stop_counts) then + resize(stop_counts, log_level_idx + 1, value => integer'high); + end if; + + return get(stop_counts, log_level_idx); + end; + + impure function get_stop_count(logger : logger_t; + log_level : log_level_t) return positive is + constant stop_count : integer := p_get_stop_count(logger, log_level); + begin + if stop_count = stop_count_unset then + core_failure("Logger " & get_full_name(logger) & " has no stop count set"); + end if; + + return stop_count; + end; + + impure function has_stop_count(logger : logger_t; + log_level : log_level_t) return boolean is + constant stop_count : integer := p_get_stop_count(logger, log_level); + begin + return stop_count /= stop_count_unset; + end; + + impure function get_log_level_filter(logger : logger_t; + log_handler : log_handler_t) return integer_vector_ptr_t is + constant log_level_filters : integer_vector_ptr_t := + to_integer_vector_ptr(get(logger.p_data, log_level_filters_idx)); + constant handler_id : natural := get_id(log_handler); + begin + if handler_id >= length(log_level_filters) then + resize(log_level_filters, handler_id + 1, value => to_integer(null_ptr)); + end if; + + return to_integer_vector_ptr(get(log_level_filters, handler_id)); + end; + + impure function get_log_level_filter(logger : logger_t; + log_handler : log_handler_t; + visible : boolean) return log_level_vec_t is + variable ret : log_level_vec_t(0 to n_log_levels - 1); + variable idx : natural := 0; + constant log_level_filter : integer_vector_ptr_t := get_log_level_filter(logger, log_handler); + variable log_level_setting : natural; + variable log_level : log_level_t; + begin + if log_level_filter = null_ptr then + return null_vec; + end if; + + if visible then + log_level_setting := log_level_visible; + else + log_level_setting := log_level_invisible; + end if; + + for i in 0 to length(log_level_filter) - 1 loop + log_level := log_level_t'val(i); + if get(log_level_filter, i) = log_level_setting and is_valid(log_level) then + ret(idx) := log_level; + idx := idx + 1; + end if; + end loop; + + return ret(0 to idx - 1); + end; + + impure function get_visible_log_levels(logger : logger_t; + log_handler : log_handler_t) return log_level_vec_t is + begin + return get_log_level_filter(logger, log_handler, visible => true); + end; + + impure function get_invisible_log_levels(logger : logger_t; + log_handler : log_handler_t) return log_level_vec_t is + begin + return get_log_level_filter(logger, log_handler, visible => false); + end; + + procedure disable(logger : logger_t; + log_level : log_level_t; + include_children : boolean := true) is + begin + set_state(logger, log_level, disabled_state); + + if include_children then + for idx in 0 to num_children(logger)-1 loop + disable(get_child(logger, idx), log_level, include_children => true); + end loop; + end if; + end; + + -- Disable logging for the specified level to this handler from specific + -- logger and all children. + procedure hide(logger : logger_t; + log_handler : log_handler_t; + log_level : log_level_t; + include_children : boolean := true) is + begin + set_log_level_filter(logger, log_handler, (0 => log_level), visible => false, + include_children => include_children); + end; + + -- Disable logging for the specified level to this handler + procedure hide(log_handler : log_handler_t; + log_level : log_level_t) is + begin + hide(root_logger, log_handler, log_level, include_children => true); + end; + -- Disable logging for the specified levels to this handler from specific + -- logger and all children. + procedure hide(logger : logger_t; + log_handler : log_handler_t; + log_levels : log_level_vec_t; + include_children : boolean := true) is + begin + set_log_level_filter(logger, log_handler, log_levels, visible => false, + include_children => include_children); + end; + + -- Disable logging for the specified levels to this handler + procedure hide(log_handler : log_handler_t; + log_levels : log_level_vec_t) is + begin + hide(root_logger, log_handler, log_levels); + end; + + procedure hide_all(logger : logger_t; + log_handler : log_handler_t; + include_children : boolean := true) is + begin + for log_level in log_level_t'low to log_level_t'high loop + hide(logger, log_handler, log_level, include_children => include_children); + end loop; + end; + + procedure hide_all(log_handler : log_handler_t) is + begin + hide_all(root_logger, log_handler, include_children => true); + end; + + procedure show(logger : logger_t; + log_handler : log_handler_t; + log_level : log_level_t; + include_children : boolean := true) is + begin + set_log_level_filter(logger, log_handler, (0 => log_level), visible => true, + include_children => include_children); + end; + + procedure show(log_handler : log_handler_t; + log_level : log_level_t) is + begin + show(root_logger, log_handler, log_level, include_children => true); + end; + + procedure show(logger : logger_t; + log_handler : log_handler_t; + log_levels : log_level_vec_t; + include_children : boolean := true) is + begin + set_log_level_filter(logger, log_handler, log_levels, visible => true, + include_children => include_children); + end; + + procedure show(log_handler : log_handler_t; + log_levels : log_level_vec_t) is + begin + show(root_logger, log_handler, log_levels, include_children => true); + end; + + procedure show_all(logger : logger_t; + log_handler : log_handler_t; + include_children : boolean := true) is + begin + for log_level in log_level_t'low to log_level_t'high loop + show(logger, log_handler, log_level, + include_children => include_children); + end loop; + end; + + procedure show_all(log_handler : log_handler_t) is + begin + show_all(root_logger, log_handler, include_children => true); + end; + + impure function is_visible(logger : logger_t; + log_level : log_level_t) return boolean is + constant state : natural := get_state(logger, log_level); + begin + if state = mocked_state then + return true; + elsif state = disabled_state then + return false; + end if; + + for i in 0 to num_log_handlers(logger)-1 loop + if is_visible(logger, get_log_handler(logger, i), log_level) then + return true; + end if; + end loop; + + return false; + end; + + impure function is_visible(logger : logger_t; + log_handler : log_handler_t; + log_level : log_level_t) return boolean is + constant log_level_filter : integer_vector_ptr_t := get_log_level_filter(logger, log_handler); + begin + if log_level_filter /= null_ptr then + return get(log_level_filter, log_level_t'pos(log_level)) = log_level_visible; + else + return false; + end if; + end; + + impure function num_log_handlers(logger : logger_t) return natural is + constant handlers : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, handlers_idx)); + begin + return length(handlers); + end; + + impure function get_log_handler(logger : logger_t; idx : natural) return log_handler_t is + constant handlers : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, handlers_idx)); + begin + return (p_data => to_integer_vector_ptr(get(handlers, idx))); + end; + + impure function get_log_handlers(logger : logger_t) return log_handler_vec_t is + constant handlers : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, handlers_idx)); + variable result : log_handler_vec_t(0 to length(handlers)-1); + begin + for i in result'range loop + result(i) := (p_data => to_integer_vector_ptr(get(handlers, i))); + end loop; + return result; + end; + + procedure set_log_handlers(logger : logger_t; + log_handlers : log_handler_vec_t; + include_children : boolean := true) is + begin + p_set_log_handlers(logger, log_handlers); + + if include_children then + for i in 0 to num_children(logger)-1 loop + set_log_handlers(get_child(logger, i), log_handlers, + include_children => true); + end loop; + end if; + end; + + procedure clear_log_count(logger : logger_t; idx : natural) is + constant log_counts : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, idx)); + begin + for lvl in log_level_t'low to log_level_t'high loop + set(log_counts, log_level_t'pos(lvl), 0); + end loop; + end; + + impure function get_log_count(logger : logger_t; + idx : natural; + log_level : log_level_t := null_log_level) return natural is + constant log_counts : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, idx)); + variable result : natural; + begin + if log_level = null_log_level then + result := 0; + for lvl in log_level_t'low to log_level_t'high loop + result := result + get(log_counts, log_level_t'pos(lvl)); + end loop; + else + result := get(log_counts, log_level_t'pos(log_level)); + end if; + + return result; + end; + + procedure reset_log_count(logger : logger_t; + log_level : log_level_t := null_log_level; + include_children : boolean := true) is + constant log_counts : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, log_count_idx)); + begin + if log_level = null_log_level then + for lvl in log_level_t'low to log_level_t'high loop + set(log_counts, log_level_t'pos(lvl), 0); + end loop; + else + set(log_counts, log_level_t'pos(log_level), 0); + end if; + + if include_children then + for idx in 0 to num_children(logger)-1 loop + reset_log_count(get_child(logger, idx), log_level, include_children => true); + end loop; + end if; + end; + + impure function get_log_count return natural is + begin + return get(global_log_count, 0); + end; + + impure function get_log_count(logger : logger_t; log_level : log_level_t := null_log_level) return natural is + begin + return get_log_count(logger, log_count_idx, log_level); + end; + + procedure decrease_stop_count(logger : logger_t; + log_level : log_level_t) is + constant stop_count : natural := p_get_stop_count(logger, log_level); + variable parent : logger_t; + begin + if stop_count = stop_count_unset then + parent := get_parent(logger); + if parent = null_logger then + core_failure("Stop condition not set on root_logger"); + else + decrease_stop_count(parent, log_level); + end if; + elsif stop_count = stop_count_infinite then + null; + elsif stop_count = 1 then + core_failure("Stop simulation on log level " & get_name(log_level)); + else + p_set_stop_count(logger, log_level, stop_count - 1, unset_children => false); + end if; + end; + + procedure count_log(logger : logger_t; log_level : log_level_t) is + constant log_counts : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, log_count_idx)); + begin + set(global_log_count, 0, get(global_log_count, 0) + 1); + set(log_counts, log_level_t'pos(log_level), get(log_counts, log_level_t'pos(log_level)) + 1); + end; + + procedure mock(logger : logger_t) is + begin + for log_level in legal_log_level_t'low to legal_log_level_t'high loop + mock(logger, log_level); + end loop; + end; + + procedure mock(logger : logger_t; log_level : log_level_t) is + begin + set_state(logger, log_level, mocked_state); + end; + + impure function make_string(logger_name : string; + msg : string; + log_level : log_level_t; + log_time : time; + line_num : natural; + file_name : string; + check_time : boolean) return string is + constant without_time : string := (" logger = " & logger_name & LF & + " log_level = " & get_name(log_level) & LF & + " msg = " & msg & LF & + " file_name:line_num = " & file_name & ":" & integer'image(line_num)); + begin + if check_time then + return " time = " & time'image(log_time) & LF & without_time; + else + return without_time; + end if; + end; + + impure function pop_log_item_string(check_time : boolean) return string is + constant got_logger_name : string := pop_string(mock_queue); + constant got_level : log_level_t := log_level_t'val(pop_byte(mock_queue)); + constant got_msg : string := pop_string(mock_queue); + constant got_log_time : time := pop_time(mock_queue); + constant got_line_num : natural := pop_integer(mock_queue); + constant got_file_name : string := pop_string(mock_queue); + begin + set(p_mock_queue_length, 0, get(p_mock_queue_length, 0) - 1); + return make_string(got_logger_name, got_msg, got_level, got_log_time, got_line_num, got_file_name, check_time); + end; + + procedure check_log(logger : logger_t; + msg : string; + log_level : log_level_t; + log_time : time := no_time_check; + line_num : natural := 0; + file_name : string := "") is + + constant expected_item : string := make_string(get_full_name(logger), + msg, log_level, log_time, line_num, file_name, + log_time /= no_time_check); + + procedure check_log_when_not_empty is + constant got_item : string := pop_log_item_string(log_time /= no_time_check); + begin + if expected_item /= got_item then + core_failure("log item mismatch:" & LF & LF & "Got:" & LF & got_item & LF & LF & "expected:" & LF & expected_item & LF); + end if; + end; + begin + if length(mock_queue) > 0 then + check_log_when_not_empty; + else + core_failure("log item mismatch - Got no log item " & LF & LF & "expected" & LF & expected_item & LF); + end if; + end; + + procedure check_only_log(logger : logger_t; + msg : string; + log_level : log_level_t; + log_time : time := no_time_check; + line_num : natural := 0; + file_name : string := "") is + begin + check_log(logger, msg, log_level, log_time, line_num, file_name); + check_no_log; + end; + + procedure check_no_log is + variable fail : boolean := length(mock_queue) > 0; + begin + while length(mock_queue) > 0 loop + report "Got unexpected log item " & LF & LF & pop_log_item_string(true) & LF; + end loop; + + if fail then + core_failure("Got unexpected log items"); + end if; + end; + + impure function mock_queue_length return natural is + begin + return get(p_mock_queue_length, 0); + end; + + procedure unmock(logger : logger_t) is + begin + check_no_log; + + for log_level in legal_log_level_t'low to legal_log_level_t'high loop + if is_mocked(logger, log_level) then + set_state(logger, log_level, enabled_state); + end if; + end loop; + end; + + procedure mock_log(logger : logger_t; + msg : string; + log_level : log_level_t; + log_time : time; + line_num : natural := 0; + file_name : string := "") is + begin + report ("Got mocked log item " & LF & + make_string(get_full_name(logger), msg, log_level, log_time, line_num, file_name, + check_time => true) & LF); + push_string(mock_queue, get_full_name(logger)); + push_byte(mock_queue, log_level_t'pos(log_level)); + push_string(mock_queue, msg); + push_time(mock_queue, log_time); + push_integer(mock_queue, line_num); + push_string(mock_queue, file_name); + + set(p_mock_queue_length, 0, get(p_mock_queue_length, 0) + 1); + end; + + procedure log(logger : logger_t; + msg : string; + log_level : log_level_t := info; + line_num : natural := 0; + file_name : string := "") is + + variable log_handler : log_handler_t; + constant t_now : time := now; + constant sequence_number : natural := get_log_count; + variable state : natural; + begin + if logger = null_logger then + core_failure("Attempt to log to uninitialized logger"); + return; + end if; + + state := get_state(logger, log_level); + + if state = mocked_state then + mock_log(logger, msg, log_level, t_now, line_num, file_name); + else + if state = enabled_state then + for i in 0 to num_log_handlers(logger) - 1 loop + log_handler := get_log_handler(logger, i); + if is_visible(logger, log_handler, log_level) then + log_to_handler(log_handler, get_full_name(logger), msg, log_level, + t_now, sequence_number, + line_num, file_name); + end if; + end loop; + + decrease_stop_count(logger, log_level); + end if; + + -- Count even if disabled + count_log(logger, log_level); + end if; + end procedure; + + procedure debug(logger : logger_t; + msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + log(logger, msg, debug, line_num, file_name); + end procedure; + + procedure pass(logger : logger_t; + msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + log(logger, msg, pass, line_num, file_name); + end procedure; + + procedure trace(logger : logger_t; + msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + log(logger, msg, trace, line_num, file_name); + end procedure; + + procedure info(logger : logger_t; + msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + log(logger, msg, info, line_num, file_name); + end procedure; + + procedure warning(logger : logger_t; + msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + log(logger, msg, warning, line_num, file_name); + end procedure; + + procedure error(logger : logger_t; + msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + log(logger, msg, error, line_num, file_name); + end procedure; + + procedure failure(logger : logger_t; + msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + log(logger, msg, failure, line_num, file_name); + end procedure; + + procedure warning_if(logger : logger_t; + condition : boolean; + msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + if condition then + warning(logger, msg, line_num => line_num, file_name => file_name); + end if; + end; + + procedure error_if(logger : logger_t; + condition : boolean; + msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + if condition then + error(logger, msg, line_num => line_num, file_name => file_name); + end if; + end; + + procedure failure_if(logger : logger_t; + condition : boolean; + msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + if condition then + failure(logger, msg, line_num => line_num, file_name => file_name); + end if; + end; + + impure function new_root_logger return logger_t is + variable logger : logger_t := new_logger(root_logger_id, "", null_logger); + begin + p_set_log_handlers(logger, (0 => display_handler)); + + for log_level in legal_log_level_t'low to legal_log_level_t'high loop + case log_level is + when error|failure => + set_stop_count(logger, log_level, 1); + when others => + disable_stop(logger, log_level); + end case; + end loop; + + hide_all(logger, display_handler); + show(logger, display_handler, (info, warning, error, failure)); + return logger; + end; + + constant p_root_logger : logger_t := new_root_logger; + impure function root_logger return logger_t is + begin + return p_root_logger; + end; + + constant p_default_logger : logger_t := get_logger("default"); + impure function default_logger return logger_t is + begin + return p_default_logger; + end; + + procedure log(msg : string; + log_level : log_level_t := info; + line_num : natural := 0; + file_name : string := "") is + begin + log(default_logger, msg, log_level, line_num, file_name); + end; + + procedure debug(msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + debug(default_logger, msg, line_num, file_name); + end procedure; + + procedure pass(msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + pass(default_logger, msg, line_num, file_name); + end procedure; + + procedure trace(msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + trace(default_logger, msg, line_num, file_name); + end procedure; + + procedure info(msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + info(default_logger, msg, line_num, file_name); + end procedure; + + procedure warning(msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + warning(default_logger, msg, line_num, file_name); + end procedure; + + procedure error(msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + error(default_logger, msg, line_num, file_name); + end procedure; + + procedure failure(msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + failure(default_logger, msg, line_num, file_name); + end procedure; + + procedure warning_if(condition : boolean; + msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + if condition then + warning(msg, line_num => line_num, file_name => file_name); + end if; + end; + + procedure error_if(condition : boolean; + msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + if condition then + error(msg, line_num => line_num, file_name => file_name); + end if; + end; + + procedure failure_if(condition : boolean; + msg : string; + line_num : natural := 0; + file_name : string := "") is + begin + if condition then + failure(msg, line_num => line_num, file_name => file_name); + end if; + end; + + impure function level_to_color(log_level : log_level_t) return string is + begin + return colorize(upper(get_name(log_level)), get_color(log_level)); + end; + + impure function source_to_color(logger_name : string) return string is + variable l : line; + + impure function create_string return string is + variable lines : lines_t; + variable num_items : natural; + begin + lines := split(logger_name, ":"); + num_items := integer'(lines.all'length); + for idx in 0 to num_items - 1 loop + write(l, colorize(lines(idx).all, fg => white, style => bright)); + deallocate(lines(idx)); + if idx /= lines'length - 1 then + write(l, colorize(":", fg => lightcyan, style => bright)); + end if; + end loop; + deallocate(lines); + return l.all; + end; + + constant result : string := create_string; + begin + deallocate(l); + return result; + end; + + impure function final_log_check(allow_disabled_errors : boolean := false; + allow_disabled_failures : boolean := false; + fail_on_warning : boolean := false) return boolean is + + impure function p_final_log_check(logger : logger_t) return boolean is + + impure function check_log_level(log_level : log_level_t; allow_disabled : boolean) return boolean is + + function disabled_str(disabled : boolean) return string is + begin + if disabled then + return " disabled"; + else + return ""; + end if; + end; + + function plural_suffix(is_plural : boolean) return string is + begin + if is_plural then + return "s"; + else + return ""; + end if; + end; + + variable count : natural; + variable level_is_disabled : boolean := is_disabled(logger, log_level); + begin + count := get_log_count(logger, log_level); + if count > 0 and not (allow_disabled and level_is_disabled) then + print(level_to_color(failure) & " - Logger " & source_to_color(get_full_name(logger)) & + " has " & integer'image(count) & disabled_str(level_is_disabled) & " " & + get_name(log_level) & plural_suffix(count > 1)); + return false; + end if; + return true; + end; + + variable failed : boolean := false; + begin + for idx in 0 to num_children(logger)-1 loop + if not p_final_log_check(get_child(logger, idx)) then + failed := true; + end if; + end loop; + + if is_mocked(logger) then + print(level_to_color(failure) & " - Logger " & source_to_color(get_full_name(logger)) & + " is still mocked."); + failed := true; + end if; + + if fail_on_warning and not check_log_level(warning, true) then + failed := true; + end if; + + if not check_log_level(error, allow_disabled_errors) then + failed := true; + end if; + + if not check_log_level(failure, allow_disabled_failures) then + failed := true; + end if; + + return not failed; + end; + + begin + if p_final_log_check(root_logger) then + return true; + else + core_failure("Final log check failed"); + return false; + end if; + end; + + procedure final_log_check(allow_disabled_errors : boolean := false; + allow_disabled_failures : boolean := false; + fail_on_warning : boolean := false) is + variable result : boolean; + begin + result := final_log_check(allow_disabled_errors => allow_disabled_errors, + allow_disabled_failures => allow_disabled_failures, + fail_on_warning => fail_on_warning); + end; + +end package body; diff --git a/vunit/vhdl/logging/src/logger_pkg.vhd b/vunit/vhdl/logging/src/logger_pkg.vhd index e9eaa2a8f..11e93e0a2 100644 --- a/vunit/vhdl/logging/src/logger_pkg.vhd +++ b/vunit/vhdl/logging/src/logger_pkg.vhd @@ -1,387 +1,387 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.log_levels_pkg.all; -use work.log_handler_pkg.all; -use work.integer_vector_ptr_pkg.all; - -package logger_pkg is - - -- Logger record, all fields are private - type logger_t is record - p_data : integer_vector_ptr_t; - end record; - constant null_logger : logger_t := (p_data => null_ptr); - type logger_vec_t is array (natural range <>) of logger_t; - impure function root_logger return logger_t; - - -- Get a logger with name. - -- Can also optionally be relative to a parent logger - impure function get_logger(name : string; - parent : logger_t := null_logger) return logger_t; - - ------------------------------------- - -- Log procedures for each log level - ------------------------------------- - procedure trace(logger : logger_t; - msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure debug(logger : logger_t; - msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure pass(logger : logger_t; - msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure info(logger : logger_t; - msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure warning(logger : logger_t; - msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure error(logger : logger_t; - msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure failure(logger : logger_t; - msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure warning_if(logger : logger_t; - condition : boolean; - msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure error_if(logger : logger_t; - condition : boolean; - msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure failure_if(logger : logger_t; - condition : boolean; - msg : string; - line_num : natural := 0; - file_name : string := ""); - - ------------------------------------------------ - -- Log procedure short hands for default logger - ------------------------------------------------ - - -- The default logger, all log calls without logger argument go to this logger. - impure function default_logger return logger_t; - - procedure trace(msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure debug(msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure pass(msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure info(msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure warning(msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure error(msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure failure(msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure warning_if(condition : boolean; - msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure error_if(condition : boolean; - msg : string; - line_num : natural := 0; - file_name : string := ""); - - procedure failure_if(condition : boolean; - msg : string; - line_num : natural := 0; - file_name : string := ""); - - -- Log procedure with level as argument - procedure log(logger : logger_t; - msg : string; - log_level : log_level_t := info; - line_num : natural := 0; - file_name : string := ""); - - procedure log(msg : string; - log_level : log_level_t := info; - line_num : natural := 0; - file_name : string := ""); - - -- Get the name of this logger get_name(get_logger("parent:child")) = "child" - impure function get_name(logger : logger_t) return string; - - -- Get the full name of this logger get_name(get_logger("parent:child")) = "parent:child" - impure function get_full_name(logger : logger_t) return string; - - -- Get the parent of this logger - impure function get_parent(logger : logger_t) return logger_t; - - -- Get the number of children of this logger - impure function num_children(logger : logger_t) return natural; - - -- Get the idx'th child of this logger - impure function get_child(logger : logger_t; idx : natural) return logger_t; - - -- Set the threshold for stopping simulation for a specific log level and - -- logger tree - procedure set_stop_count(logger : logger_t; - log_level : log_level_t; - value : positive; - unset_children : boolean := false); - - -- Disable stopping simulation for a specific log level and - -- logger tree - procedure disable_stop(logger : logger_t; - log_level : log_level_t; - unset_children : boolean := false); - - -- Set the threshold for stopping simulation for a specific log level in - -- the entire logging tree. - - -- NOTE: Removes all other stop count settings for log_level in entire tree. - procedure set_stop_count(log_level : log_level_t; - value : positive); - - -- Disable stopping simulation for a specific log level in - -- the entire logging tree. - - -- NOTE: Removes all other stop count settings for log_level in entire tree. - procedure disable_stop(log_level : log_level_t); - - -- Shorthand for configuring the stop counts for (warning, error, failure) in - -- a logger subtree. Set stop count to infinite for all levels < log_level and - -- 1 for all (warning, error, failure) >= log_level. - - -- NOTE: Removes all other stop count settings from logger subtree. - procedure set_stop_level(logger : logger_t; - log_level : alert_log_level_t); - - -- Shorthand for configuring the stop counts in entire logger tree. - -- Same behavior as set_stop_level for specific logger subtree above - procedure set_stop_level(level : alert_log_level_t); - - -- Unset stop count for stopping simulation for a specific log level and - -- logger tree - procedure unset_stop_count(logger : logger_t; - log_level : log_level_t; - unset_children : boolean := false); - - -- Returns true if logger has stop count set - impure function has_stop_count(logger : logger_t; - log_level : log_level_t) return boolean; - - -- Get the stop count for logger and log_level if set, else fail - impure function get_stop_count(logger : logger_t; - log_level : log_level_t) return positive; - - -- Disable a log_level from specific logger including all children. - -- Disable is used when a log message is unwanted and it should be ignored. - - -- NOTE: A disabled log message is still counted to get a disabled log count - -- statistics. - -- errors and failures can be disabled but the final_log_check must - -- explicitly allow it as well as an extra safety mechanism. - procedure disable(logger : logger_t; - log_level : log_level_t; - include_children : boolean := true); - - -- Returns true if the logger and log_level is disabled - impure function is_disabled(logger : logger_t; - log_level : log_level_t) return boolean; - - -- Hide log messages of specified level to this handler. - procedure hide(log_handler : log_handler_t; - log_level : log_level_t); - - -- Hide log messages from the logger of the specified level to this handler - procedure hide(logger : logger_t; - log_handler : log_handler_t; - log_level : log_level_t; - include_children : boolean := true); - - -- Hide log messages of the specified levels to this handler. - procedure hide(log_handler : log_handler_t; - log_levels : log_level_vec_t); - - -- Hide log messages from the logger of the specified levels to this handler - procedure hide(logger : logger_t; - log_handler : log_handler_t; - log_levels : log_level_vec_t; - include_children : boolean := true); - - -- Show log messages of the specified log_level to this handler - procedure show(log_handler : log_handler_t; - log_level : log_level_t); - - -- Show log messages from the logger of the specified log_level to this handler - procedure show(logger : logger_t; - log_handler : log_handler_t; - log_level : log_level_t; - include_children : boolean := true); - - -- Show log messages of the specified log_levels to this handler - procedure show(log_handler : log_handler_t; - log_levels : log_level_vec_t); - - -- Show log messages from the logger of the specified log_levels to this handler - procedure show(logger : logger_t; - log_handler : log_handler_t; - log_levels : log_level_vec_t; - include_children : boolean := true); - - -- Show all log levels to the log handler - procedure show_all(log_handler : log_handler_t); - - -- Show all log levels to the handler from specific logger - procedure show_all(logger : logger_t; - log_handler : log_handler_t; - include_children : boolean := true); - - -- Hide all log levels from this handler - procedure hide_all(log_handler : log_handler_t); - - -- Hide all log levels from this handler from specific logger - procedure hide_all(logger : logger_t; - log_handler : log_handler_t; - include_children : boolean := true); - - -- Return true if logging to this logger at this level is visible anywhere - -- Can be used to avoid expensive string creation when not logging a specific - -- level - impure function is_visible(logger : logger_t; - log_level : log_level_t) return boolean; - - -- Return true if logging to this logger at this level is visible to handler - impure function is_visible(logger : logger_t; - log_handler : log_handler_t; - log_level : log_level_t) return boolean; - - -- Get the current visible log levels for a specific logger to this log handler - impure function get_visible_log_levels(logger : logger_t; - log_handler : log_handler_t) return log_level_vec_t; - - -- Get the current invisible log levels for a specific logger to this log handler - impure function get_invisible_log_levels(logger : logger_t; - log_handler : log_handler_t) return log_level_vec_t; - - -- Get the number of log handlers attached to this logger - impure function num_log_handlers(logger : logger_t) return natural; - - -- Get the idx'th log handler attached to this logger - impure function get_log_handler(logger : logger_t; idx : natural) return log_handler_t; - - -- Get all log handlers attached to this logger - impure function get_log_handlers(logger : logger_t) return log_handler_vec_t; - - -- Set the log handlers for this logger - procedure set_log_handlers(logger : logger_t; - log_handlers : log_handler_vec_t; - include_children : boolean := true); - - -- Get the total number of log calls to all loggers - impure function get_log_count return natural; - - -- Get number of log calls to a specific level or all levels when level = null_log_level - impure function get_log_count(logger : logger_t; - log_level : log_level_t := null_log_level) return natural; - - -- Reset the log call count of a specific level or all levels when level = null_log_level - procedure reset_log_count(logger : logger_t; - log_level : log_level_t := null_log_level; - include_children : boolean := true); - - - -- Perform a check of the log counts and fail unless there are no errors or failures. - -- By default no disabled errors or failures are not allowed. - -- Disabled errors and failrues can be allowed by setting the corresponding - -- arguments to true. - -- By default warnings are allowed but failure on warning can be enabled. - -- When fail on warning is enabled it also allows disabled warnings. - procedure final_log_check(allow_disabled_errors : boolean := false; - allow_disabled_failures : boolean := false; - fail_on_warning : boolean := false); - - impure function final_log_check(allow_disabled_errors : boolean := false; - allow_disabled_failures : boolean := false; - fail_on_warning : boolean := false) return boolean; - - --------------------------------------------------------------------- - -- Mock procedures to enable unit testing of code performing logging - --------------------------------------------------------------------- - - -- Mock the logger preventing simulaton abort and recording all logs to it - procedure mock(logger : logger_t); - - -- Mock log_level of logger preventing simulaton abort and recording all logs to it - procedure mock(logger : logger_t; log_level : log_level_t); - - -- Unmock the logger returning it to its normal state - -- Results in failures if there are still unchecked log calls recorded - procedure unmock(logger : logger_t); - - -- Returns true if the logger is mocked - impure function is_mocked(logger : logger_t) return boolean; - - -- Constant to ignore time value when checking log call - constant no_time_check : time := -1 ns; - - -- Check that the earliest recorded log call in the mock state matches this - -- call or fails. Also consumes this recorded log call such that subsequent - -- check_log calls can be used to verify a sequence of log calls - procedure check_log(logger : logger_t; - msg : string; - log_level : log_level_t; - log_time : time := no_time_check; - line_num : natural := 0; - file_name : string := ""); - - -- Check that there is only one recorded log call remaining - procedure check_only_log(logger : logger_t; - msg : string; - log_level : log_level_t; - log_time : time := no_time_check; - line_num : natural := 0; - file_name : string := ""); - - -- Check that there are no remaining recorded log calls, automatically called - -- during unmock - procedure check_no_log; - - -- Return the number of unchecked messages in the mock queue - impure function mock_queue_length return natural; - -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.log_levels_pkg.all; +use work.log_handler_pkg.all; +use work.integer_vector_ptr_pkg.all; + +package logger_pkg is + + -- Logger record, all fields are private + type logger_t is record + p_data : integer_vector_ptr_t; + end record; + constant null_logger : logger_t := (p_data => null_ptr); + type logger_vec_t is array (natural range <>) of logger_t; + impure function root_logger return logger_t; + + -- Get a logger with name. + -- Can also optionally be relative to a parent logger + impure function get_logger(name : string; + parent : logger_t := null_logger) return logger_t; + + ------------------------------------- + -- Log procedures for each log level + ------------------------------------- + procedure trace(logger : logger_t; + msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure debug(logger : logger_t; + msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure pass(logger : logger_t; + msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure info(logger : logger_t; + msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure warning(logger : logger_t; + msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure error(logger : logger_t; + msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure failure(logger : logger_t; + msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure warning_if(logger : logger_t; + condition : boolean; + msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure error_if(logger : logger_t; + condition : boolean; + msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure failure_if(logger : logger_t; + condition : boolean; + msg : string; + line_num : natural := 0; + file_name : string := ""); + + ------------------------------------------------ + -- Log procedure short hands for default logger + ------------------------------------------------ + + -- The default logger, all log calls without logger argument go to this logger. + impure function default_logger return logger_t; + + procedure trace(msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure debug(msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure pass(msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure info(msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure warning(msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure error(msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure failure(msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure warning_if(condition : boolean; + msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure error_if(condition : boolean; + msg : string; + line_num : natural := 0; + file_name : string := ""); + + procedure failure_if(condition : boolean; + msg : string; + line_num : natural := 0; + file_name : string := ""); + + -- Log procedure with level as argument + procedure log(logger : logger_t; + msg : string; + log_level : log_level_t := info; + line_num : natural := 0; + file_name : string := ""); + + procedure log(msg : string; + log_level : log_level_t := info; + line_num : natural := 0; + file_name : string := ""); + + -- Get the name of this logger get_name(get_logger("parent:child")) = "child" + impure function get_name(logger : logger_t) return string; + + -- Get the full name of this logger get_name(get_logger("parent:child")) = "parent:child" + impure function get_full_name(logger : logger_t) return string; + + -- Get the parent of this logger + impure function get_parent(logger : logger_t) return logger_t; + + -- Get the number of children of this logger + impure function num_children(logger : logger_t) return natural; + + -- Get the idx'th child of this logger + impure function get_child(logger : logger_t; idx : natural) return logger_t; + + -- Set the threshold for stopping simulation for a specific log level and + -- logger tree + procedure set_stop_count(logger : logger_t; + log_level : log_level_t; + value : positive; + unset_children : boolean := false); + + -- Disable stopping simulation for a specific log level and + -- logger tree + procedure disable_stop(logger : logger_t; + log_level : log_level_t; + unset_children : boolean := false); + + -- Set the threshold for stopping simulation for a specific log level in + -- the entire logging tree. + + -- NOTE: Removes all other stop count settings for log_level in entire tree. + procedure set_stop_count(log_level : log_level_t; + value : positive); + + -- Disable stopping simulation for a specific log level in + -- the entire logging tree. + + -- NOTE: Removes all other stop count settings for log_level in entire tree. + procedure disable_stop(log_level : log_level_t); + + -- Shorthand for configuring the stop counts for (warning, error, failure) in + -- a logger subtree. Set stop count to infinite for all levels < log_level and + -- 1 for all (warning, error, failure) >= log_level. + + -- NOTE: Removes all other stop count settings from logger subtree. + procedure set_stop_level(logger : logger_t; + log_level : alert_log_level_t); + + -- Shorthand for configuring the stop counts in entire logger tree. + -- Same behavior as set_stop_level for specific logger subtree above + procedure set_stop_level(level : alert_log_level_t); + + -- Unset stop count for stopping simulation for a specific log level and + -- logger tree + procedure unset_stop_count(logger : logger_t; + log_level : log_level_t; + unset_children : boolean := false); + + -- Returns true if logger has stop count set + impure function has_stop_count(logger : logger_t; + log_level : log_level_t) return boolean; + + -- Get the stop count for logger and log_level if set, else fail + impure function get_stop_count(logger : logger_t; + log_level : log_level_t) return positive; + + -- Disable a log_level from specific logger including all children. + -- Disable is used when a log message is unwanted and it should be ignored. + + -- NOTE: A disabled log message is still counted to get a disabled log count + -- statistics. + -- errors and failures can be disabled but the final_log_check must + -- explicitly allow it as well as an extra safety mechanism. + procedure disable(logger : logger_t; + log_level : log_level_t; + include_children : boolean := true); + + -- Returns true if the logger and log_level is disabled + impure function is_disabled(logger : logger_t; + log_level : log_level_t) return boolean; + + -- Hide log messages of specified level to this handler. + procedure hide(log_handler : log_handler_t; + log_level : log_level_t); + + -- Hide log messages from the logger of the specified level to this handler + procedure hide(logger : logger_t; + log_handler : log_handler_t; + log_level : log_level_t; + include_children : boolean := true); + + -- Hide log messages of the specified levels to this handler. + procedure hide(log_handler : log_handler_t; + log_levels : log_level_vec_t); + + -- Hide log messages from the logger of the specified levels to this handler + procedure hide(logger : logger_t; + log_handler : log_handler_t; + log_levels : log_level_vec_t; + include_children : boolean := true); + + -- Show log messages of the specified log_level to this handler + procedure show(log_handler : log_handler_t; + log_level : log_level_t); + + -- Show log messages from the logger of the specified log_level to this handler + procedure show(logger : logger_t; + log_handler : log_handler_t; + log_level : log_level_t; + include_children : boolean := true); + + -- Show log messages of the specified log_levels to this handler + procedure show(log_handler : log_handler_t; + log_levels : log_level_vec_t); + + -- Show log messages from the logger of the specified log_levels to this handler + procedure show(logger : logger_t; + log_handler : log_handler_t; + log_levels : log_level_vec_t; + include_children : boolean := true); + + -- Show all log levels to the log handler + procedure show_all(log_handler : log_handler_t); + + -- Show all log levels to the handler from specific logger + procedure show_all(logger : logger_t; + log_handler : log_handler_t; + include_children : boolean := true); + + -- Hide all log levels from this handler + procedure hide_all(log_handler : log_handler_t); + + -- Hide all log levels from this handler from specific logger + procedure hide_all(logger : logger_t; + log_handler : log_handler_t; + include_children : boolean := true); + + -- Return true if logging to this logger at this level is visible anywhere + -- Can be used to avoid expensive string creation when not logging a specific + -- level + impure function is_visible(logger : logger_t; + log_level : log_level_t) return boolean; + + -- Return true if logging to this logger at this level is visible to handler + impure function is_visible(logger : logger_t; + log_handler : log_handler_t; + log_level : log_level_t) return boolean; + + -- Get the current visible log levels for a specific logger to this log handler + impure function get_visible_log_levels(logger : logger_t; + log_handler : log_handler_t) return log_level_vec_t; + + -- Get the current invisible log levels for a specific logger to this log handler + impure function get_invisible_log_levels(logger : logger_t; + log_handler : log_handler_t) return log_level_vec_t; + + -- Get the number of log handlers attached to this logger + impure function num_log_handlers(logger : logger_t) return natural; + + -- Get the idx'th log handler attached to this logger + impure function get_log_handler(logger : logger_t; idx : natural) return log_handler_t; + + -- Get all log handlers attached to this logger + impure function get_log_handlers(logger : logger_t) return log_handler_vec_t; + + -- Set the log handlers for this logger + procedure set_log_handlers(logger : logger_t; + log_handlers : log_handler_vec_t; + include_children : boolean := true); + + -- Get the total number of log calls to all loggers + impure function get_log_count return natural; + + -- Get number of log calls to a specific level or all levels when level = null_log_level + impure function get_log_count(logger : logger_t; + log_level : log_level_t := null_log_level) return natural; + + -- Reset the log call count of a specific level or all levels when level = null_log_level + procedure reset_log_count(logger : logger_t; + log_level : log_level_t := null_log_level; + include_children : boolean := true); + + + -- Perform a check of the log counts and fail unless there are no errors or failures. + -- By default no disabled errors or failures are not allowed. + -- Disabled errors and failrues can be allowed by setting the corresponding + -- arguments to true. + -- By default warnings are allowed but failure on warning can be enabled. + -- When fail on warning is enabled it also allows disabled warnings. + procedure final_log_check(allow_disabled_errors : boolean := false; + allow_disabled_failures : boolean := false; + fail_on_warning : boolean := false); + + impure function final_log_check(allow_disabled_errors : boolean := false; + allow_disabled_failures : boolean := false; + fail_on_warning : boolean := false) return boolean; + + --------------------------------------------------------------------- + -- Mock procedures to enable unit testing of code performing logging + --------------------------------------------------------------------- + + -- Mock the logger preventing simulaton abort and recording all logs to it + procedure mock(logger : logger_t); + + -- Mock log_level of logger preventing simulaton abort and recording all logs to it + procedure mock(logger : logger_t; log_level : log_level_t); + + -- Unmock the logger returning it to its normal state + -- Results in failures if there are still unchecked log calls recorded + procedure unmock(logger : logger_t); + + -- Returns true if the logger is mocked + impure function is_mocked(logger : logger_t) return boolean; + + -- Constant to ignore time value when checking log call + constant no_time_check : time := -1 ns; + + -- Check that the earliest recorded log call in the mock state matches this + -- call or fails. Also consumes this recorded log call such that subsequent + -- check_log calls can be used to verify a sequence of log calls + procedure check_log(logger : logger_t; + msg : string; + log_level : log_level_t; + log_time : time := no_time_check; + line_num : natural := 0; + file_name : string := ""); + + -- Check that there is only one recorded log call remaining + procedure check_only_log(logger : logger_t; + msg : string; + log_level : log_level_t; + log_time : time := no_time_check; + line_num : natural := 0; + file_name : string := ""); + + -- Check that there are no remaining recorded log calls, automatically called + -- during unmock + procedure check_no_log; + + -- Return the number of unchecked messages in the mock queue + impure function mock_queue_length return natural; + +end package; diff --git a/vunit/vhdl/logging/src/print_pkg-body.vhd b/vunit/vhdl/logging/src/print_pkg-body.vhd index 9154b3e19..5fa0413a0 100644 --- a/vunit/vhdl/logging/src/print_pkg-body.vhd +++ b/vunit/vhdl/logging/src/print_pkg-body.vhd @@ -1,44 +1,44 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.log_handler_pkg.stdout_file_name; - -package body print_pkg is - - procedure print(constant str : in string; file f : text) is - variable l : line; - begin - write(l, str); - writeline(f, l); - end procedure; - - procedure print(str : string) is - begin - print(str, OUTPUT); - end procedure; - - procedure print(str : string; - file_name : string; - mode : file_open_kind range write_mode to append_mode := append_mode) is - variable l : line; - variable status : file_open_status; - file f : text; - begin - if file_name = stdout_file_name then - print(str); - else - file_open(status, f, file_name, mode); - - if status /= open_ok then - report "Failed to open file " & file_name & " - " & file_open_status'image(status) severity failure; - end if; - - print(str, f); - file_close(f); - end if; - end procedure; - -end package body print_pkg; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.log_handler_pkg.stdout_file_name; + +package body print_pkg is + + procedure print(constant str : in string; file f : text) is + variable l : line; + begin + write(l, str); + writeline(f, l); + end procedure; + + procedure print(str : string) is + begin + print(str, OUTPUT); + end procedure; + + procedure print(str : string; + file_name : string; + mode : file_open_kind range write_mode to append_mode := append_mode) is + variable l : line; + variable status : file_open_status; + file f : text; + begin + if file_name = stdout_file_name then + print(str); + else + file_open(status, f, file_name, mode); + + if status /= open_ok then + report "Failed to open file " & file_name & " - " & file_open_status'image(status) severity failure; + end if; + + print(str, f); + file_close(f); + end if; + end procedure; + +end package body print_pkg; diff --git a/vunit/vhdl/logging/src/print_pkg.vhd b/vunit/vhdl/logging/src/print_pkg.vhd index a0077f6df..fb2644006 100644 --- a/vunit/vhdl/logging/src/print_pkg.vhd +++ b/vunit/vhdl/logging/src/print_pkg.vhd @@ -1,22 +1,22 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use std.textio.all; - -package print_pkg is - - -- Print to an open file object. No internal flushing. - procedure print(constant str : in string; file f : text); - - -- Print to stdout - procedure print(str : string); - - -- Print to named file - procedure print(str : string; - file_name : string; - mode : file_open_kind range write_mode to append_mode := append_mode); - -end package print_pkg; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use std.textio.all; + +package print_pkg is + + -- Print to an open file object. No internal flushing. + procedure print(constant str : in string; file f : text); + + -- Print to stdout + procedure print(str : string); + + -- Print to named file + procedure print(str : string; + file_name : string; + mode : file_open_kind range write_mode to append_mode := append_mode); + +end package print_pkg; diff --git a/vunit/vhdl/logging/test/tb_deprecated.vhd b/vunit/vhdl/logging/test/tb_deprecated.vhd index 05ace6c22..2c3e5d8fa 100644 --- a/vunit/vhdl/logging/test/tb_deprecated.vhd +++ b/vunit/vhdl/logging/test/tb_deprecated.vhd @@ -1,183 +1,183 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com -------------------------------------------------------------------------------- --- This testbench verifies deprecated interfaces -------------------------------------------------------------------------------- - -library vunit_lib; -use vunit_lib.run_pkg.all; - -use work.log_levels_pkg.all; -use work.logger_pkg.all; -use work.log_handler_pkg.all; -use work.log_deprecated_pkg.all; -use work.core_pkg.all; -use work.test_support_pkg.all; - -entity tb_log_deprecated is - generic ( - runner_cfg : string); -end entity; - -architecture a of tb_log_deprecated is -begin - test_runner : process - variable my_logger, my_logger2, uninitialized_logger : logger_t; - constant deprecated_msg : string := - "Using deprecated procedure logger_init. Using best effort mapping to contemporary functionality"; - - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - logger_init(my_logger); - - if run("Test log") then - set_format(display_handler, format => raw); - - mock(default_logger); - log("Hello world"); - check_only_log(default_logger, "Hello world", info); - unmock(default_logger); - - mock_core_failure; - log(uninitialized_logger, "Hello world"); - check_and_unmock_core_failure("Attempt to log to uninitialized logger"); - - elsif run("Test initializing logger") then - mock(default_logger); - logger_init(my_logger2); - check_log(default_logger, deprecated_msg, warning); - check_log(default_logger, "Empty string logger names not supported. Using ""anonymous1""", warning); - unmock(default_logger); - - assert_equal(get_name(my_logger2), "anonymous1"); - - assert_equal(num_log_handlers(my_logger2), 2); - assert(get_file_name(get_file_handler(my_logger2)) = "log.csv"); - assert(get_file_name(get_display_handler(my_logger2)) = stdout_file_name); - - assert(get_display_handler(my_logger2) /= get_display_handler(default_logger)); - assert(get_file_handler(my_logger2) /= get_file_handler(default_logger)); - check_format(my_logger2, get_display_handler(my_logger2), raw); - check_format(my_logger2, get_file_handler(my_logger2), off); - - elsif run("Test changing logger name") then - mock(default_logger); - mock_core_failure; - logger_init(my_logger, default_src => "my_logger"); - check_log(default_logger, deprecated_msg, warning); - check_core_failure("Changing logger name is not supported"); - unmock(default_logger); - unmock_core_failure; - assert_equal(get_name(my_logger), "anonymous0"); - - mock(default_logger); - mock_core_failure; - logger_init(default_src => "my_logger"); - check_log(default_logger, deprecated_msg, warning); - check_core_failure("Changing logger name is not supported"); - unmock(default_logger); - unmock_core_failure; - assert_equal(get_name(default_logger), "default"); - - elsif run("Test changing file name") then - mock(default_logger); - logger_init(my_logger, file_name => "my_logger.csv"); - check_log(default_logger, deprecated_msg, warning); - unmock(default_logger); - assert_equal(get_file_name(get_file_handler(my_logger)), "my_logger.csv"); - - mock(default_logger); - logger_init(file_format => csv, file_name => "my_logger.csv"); - check_log(default_logger, deprecated_msg, warning); - unmock(default_logger); - assert_equal(get_file_name(get_file_handler(default_logger)), "my_logger.csv"); - - elsif run("Test changing display format") then - mock(default_logger); - logger_init(my_logger, display_format => verbose); - check_log(default_logger, deprecated_msg, warning); - unmock(default_logger); - check_format(my_logger, get_display_handler(my_logger), verbose); - - mock(default_logger); - logger_init(display_format => verbose); - check_log(default_logger, deprecated_msg, warning); - unmock(default_logger); - check_format(default_logger, get_display_handler(default_logger), verbose); - - elsif run("Test changing file format") then - mock(default_logger); - logger_init(my_logger, file_format => verbose); - check_log(default_logger, deprecated_msg, warning); - unmock(default_logger); - check_format(my_logger, get_file_handler(my_logger), verbose); - - mock(default_logger); - logger_init(file_format => verbose); - check_log(default_logger, deprecated_msg, warning); - unmock(default_logger); - check_format(default_logger, get_file_handler(default_logger), verbose); - - elsif run("Test changing stop level") then - mock(default_logger); - logger_init(my_logger, stop_level => error); - check_log(default_logger, deprecated_msg, warning); - unmock(default_logger); - - mock(default_logger); - logger_init(stop_level => error); - check_log(default_logger, deprecated_msg, warning); - unmock(default_logger); - - elsif run("Test changing separator") then - mock_core_failure; - mock(default_logger); - logger_init(my_logger, separator => ';'); - check_log(default_logger, deprecated_msg, warning); - check_core_failure("Changing CSV separator is not supported"); - unmock(default_logger); - unmock_core_failure; - - mock(default_logger); - mock_core_failure; - logger_init(separator => ';'); - check_log(default_logger, deprecated_msg, warning); - check_core_failure("Changing CSV separator is not supported"); - unmock(default_logger); - unmock_core_failure; - - elsif run("Test changing append") then - mock_core_failure; - mock(default_logger); - logger_init(my_logger, append => true); - check_log(default_logger, deprecated_msg, warning); - check_core_failure("Appending new log to existing file is not supported"); - unmock(default_logger); - unmock_core_failure; - - mock_core_failure; - mock(default_logger); - logger_init(append => true); - check_log(default_logger, deprecated_msg, warning); - check_core_failure("Appending new log to existing file is not supported"); - unmock(default_logger); - unmock_core_failure; - - elsif run("Test verbose procedures and log level") then - mock(default_logger); - verbose("hello", 17, "foo.vhd"); - check_log(default_logger, "Mapping deprecated procedure verbose to trace", warning); - check_log(default_logger, "hello", verbose, 0 ns, 17, "foo.vhd"); - unmock(default_logger); - - end if; - end loop; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +------------------------------------------------------------------------------- +-- This testbench verifies deprecated interfaces +------------------------------------------------------------------------------- + +library vunit_lib; +use vunit_lib.run_pkg.all; + +use work.log_levels_pkg.all; +use work.logger_pkg.all; +use work.log_handler_pkg.all; +use work.log_deprecated_pkg.all; +use work.core_pkg.all; +use work.test_support_pkg.all; + +entity tb_log_deprecated is + generic ( + runner_cfg : string); +end entity; + +architecture a of tb_log_deprecated is +begin + test_runner : process + variable my_logger, my_logger2, uninitialized_logger : logger_t; + constant deprecated_msg : string := + "Using deprecated procedure logger_init. Using best effort mapping to contemporary functionality"; + + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + logger_init(my_logger); + + if run("Test log") then + set_format(display_handler, format => raw); + + mock(default_logger); + log("Hello world"); + check_only_log(default_logger, "Hello world", info); + unmock(default_logger); + + mock_core_failure; + log(uninitialized_logger, "Hello world"); + check_and_unmock_core_failure("Attempt to log to uninitialized logger"); + + elsif run("Test initializing logger") then + mock(default_logger); + logger_init(my_logger2); + check_log(default_logger, deprecated_msg, warning); + check_log(default_logger, "Empty string logger names not supported. Using ""anonymous1""", warning); + unmock(default_logger); + + assert_equal(get_name(my_logger2), "anonymous1"); + + assert_equal(num_log_handlers(my_logger2), 2); + assert(get_file_name(get_file_handler(my_logger2)) = "log.csv"); + assert(get_file_name(get_display_handler(my_logger2)) = stdout_file_name); + + assert(get_display_handler(my_logger2) /= get_display_handler(default_logger)); + assert(get_file_handler(my_logger2) /= get_file_handler(default_logger)); + check_format(my_logger2, get_display_handler(my_logger2), raw); + check_format(my_logger2, get_file_handler(my_logger2), off); + + elsif run("Test changing logger name") then + mock(default_logger); + mock_core_failure; + logger_init(my_logger, default_src => "my_logger"); + check_log(default_logger, deprecated_msg, warning); + check_core_failure("Changing logger name is not supported"); + unmock(default_logger); + unmock_core_failure; + assert_equal(get_name(my_logger), "anonymous0"); + + mock(default_logger); + mock_core_failure; + logger_init(default_src => "my_logger"); + check_log(default_logger, deprecated_msg, warning); + check_core_failure("Changing logger name is not supported"); + unmock(default_logger); + unmock_core_failure; + assert_equal(get_name(default_logger), "default"); + + elsif run("Test changing file name") then + mock(default_logger); + logger_init(my_logger, file_name => "my_logger.csv"); + check_log(default_logger, deprecated_msg, warning); + unmock(default_logger); + assert_equal(get_file_name(get_file_handler(my_logger)), "my_logger.csv"); + + mock(default_logger); + logger_init(file_format => csv, file_name => "my_logger.csv"); + check_log(default_logger, deprecated_msg, warning); + unmock(default_logger); + assert_equal(get_file_name(get_file_handler(default_logger)), "my_logger.csv"); + + elsif run("Test changing display format") then + mock(default_logger); + logger_init(my_logger, display_format => verbose); + check_log(default_logger, deprecated_msg, warning); + unmock(default_logger); + check_format(my_logger, get_display_handler(my_logger), verbose); + + mock(default_logger); + logger_init(display_format => verbose); + check_log(default_logger, deprecated_msg, warning); + unmock(default_logger); + check_format(default_logger, get_display_handler(default_logger), verbose); + + elsif run("Test changing file format") then + mock(default_logger); + logger_init(my_logger, file_format => verbose); + check_log(default_logger, deprecated_msg, warning); + unmock(default_logger); + check_format(my_logger, get_file_handler(my_logger), verbose); + + mock(default_logger); + logger_init(file_format => verbose); + check_log(default_logger, deprecated_msg, warning); + unmock(default_logger); + check_format(default_logger, get_file_handler(default_logger), verbose); + + elsif run("Test changing stop level") then + mock(default_logger); + logger_init(my_logger, stop_level => error); + check_log(default_logger, deprecated_msg, warning); + unmock(default_logger); + + mock(default_logger); + logger_init(stop_level => error); + check_log(default_logger, deprecated_msg, warning); + unmock(default_logger); + + elsif run("Test changing separator") then + mock_core_failure; + mock(default_logger); + logger_init(my_logger, separator => ';'); + check_log(default_logger, deprecated_msg, warning); + check_core_failure("Changing CSV separator is not supported"); + unmock(default_logger); + unmock_core_failure; + + mock(default_logger); + mock_core_failure; + logger_init(separator => ';'); + check_log(default_logger, deprecated_msg, warning); + check_core_failure("Changing CSV separator is not supported"); + unmock(default_logger); + unmock_core_failure; + + elsif run("Test changing append") then + mock_core_failure; + mock(default_logger); + logger_init(my_logger, append => true); + check_log(default_logger, deprecated_msg, warning); + check_core_failure("Appending new log to existing file is not supported"); + unmock(default_logger); + unmock_core_failure; + + mock_core_failure; + mock(default_logger); + logger_init(append => true); + check_log(default_logger, deprecated_msg, warning); + check_core_failure("Appending new log to existing file is not supported"); + unmock(default_logger); + unmock_core_failure; + + elsif run("Test verbose procedures and log level") then + mock(default_logger); + verbose("hello", 17, "foo.vhd"); + check_log(default_logger, "Mapping deprecated procedure verbose to trace", warning); + check_log(default_logger, "hello", verbose, 0 ns, 17, "foo.vhd"); + unmock(default_logger); + + end if; + end loop; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/vhdl/logging/test/tb_log.vhd b/vunit/vhdl/logging/test/tb_log.vhd index fe49bc95b..ade7107f3 100644 --- a/vunit/vhdl/logging/test/tb_log.vhd +++ b/vunit/vhdl/logging/test/tb_log.vhd @@ -1,898 +1,898 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use std.textio.all; - -library vunit_lib; -use vunit_lib.run_pkg.all; -use vunit_lib.dict_pkg.all; - -use work.log_levels_pkg.all; -use work.logger_pkg.all; -use work.log_handler_pkg.all; -use work.core_pkg.all; -use work.test_support_pkg.all; -use work.print_pkg.all; - -entity tb_log is - generic ( - runner_cfg : string); -end entity; - -architecture a of tb_log is -begin - main : process - constant main_path : string := main'instance_name; - - procedure check_empty_log_file(constant file_name : in string) is - file fptr : text; - variable status : file_open_status; - begin - file_open(status, fptr, file_name, read_mode); - assert status = open_ok report "Expected a file " & file_name severity failure; - assert endfile(fptr) report "Expected " & file_name & " to be empty" severity FAILURE; - file_close(fptr); - end; - - procedure flush_file_handler(file_handler : log_handler_t) is - begin - -- Dummy init to flush file handler - init_log_handler(file_handler, - file_name => get_file_name(file_handler) & ".dummy", - format => raw); - end; - - procedure check_file(file_name : string; - entries : dict_t) is - file fptr : text; - variable l : line; - variable status : file_open_status; - begin - file_open(status, fptr, file_name, read_mode); - assert status = open_ok - report "Failed opening " & file_name & " (" & file_open_status'image(status) & ")." - severity failure; - - if status = open_ok then - for i in 0 to num_keys(entries)-1 loop - readline(fptr, l); - assert l.all = get(entries, integer'image(i)) - report "(" & integer'image(i) & ") " & LF & "Got:" & LF & l.all & LF & "expected:" & LF & get(entries, integer'image(i)) - severity failure; - end loop; - end if; - end; - - procedure check_log_file (file_handler : log_handler_t; - file_name : string; - entries : dict_t) is - file fptr : text; - variable l : line; - variable status : file_open_status; - begin - assert get_file_name(file_handler) = file_name report "file name mismatch"; - flush_file_handler(file_handler); - check_file(file_name, entries); - end; - - constant log_file_name : string := output_path(runner_cfg) & "my_log.csv"; - constant print_file_name : string := output_path(runner_cfg) & "print.csv"; - variable logger : logger_t := get_logger("logger"); - variable nested_logger : logger_t := get_logger("nested", parent => logger); - variable other_logger : logger_t := get_logger("other"); - variable tmp_logger : logger_t; - variable entries : dict_t := new_dict; - variable entries2 : dict_t := new_dict; - variable tmp : integer; - file fptr : text; - variable status : file_open_status; - - procedure perform_logging(logger : logger_t) is - begin - trace(logger, "message 1"); - wait for 1 ns; - debug(logger, "message 2"); - wait for 1 ns; - info(logger, "message 3"); - wait for 1 ns; - warning(logger, "message 4"); - wait for 1 ns; - error(logger, "message 5"); - wait for 1 ns; - failure(logger, "message 6"); - wait for 1 ns; - end procedure; - - constant max_time_str : string := time'image(1 sec); - constant time_padding : string(max_time_str'range) := (others => ' '); - impure function format_time(t : time) return string is - constant time_str : string := time'image(t); - begin - return (1 to (max_time_str'length - time_str'length) => ' ') & time_str; - end function; - - variable file_handler : log_handler_t := new_log_handler(log_file_name, - format => verbose, - use_color => false); - variable file_handlers : log_handler_vec_t(0 to 63); - variable loggers : logger_vec_t(file_handlers'range); - begin - - -- Check defaults before test runner setup - assert_equal(get_log_count, 0); - assert_equal(num_log_handlers(logger), 1); - assert_true(get_log_handler(logger, 0) = display_handler); - assert_true(get_log_handlers(logger) = (0 => display_handler)); - assert_true(get_visible_log_levels(logger, display_handler) = (info, warning, error, failure)); - - -- Check default stop count - for log_level in legal_log_level_t'low to legal_log_level_t'high loop - case log_level is - when error|failure => - assert_equal(get_stop_count(root_logger, log_level), 1); - when others => - assert_equal(get_stop_count(root_logger, log_level), integer'high); - end case; - end loop; - - test_runner_setup(runner, runner_cfg); - set_log_handlers(root_logger, (display_handler, file_handler)); - show_all(root_logger, file_handler); - show_all(root_logger, display_handler); - - if run("raw format") then - set_format(display_handler, format => raw); - init_log_handler(file_handler, file_name => log_file_name, format => raw); - disable_stop(logger, error); - disable_stop(logger, failure); - perform_logging(logger); - set(entries, "0", "message 1"); - set(entries, "1", "message 2"); - set(entries, "2", "message 3"); - set(entries, "3", "message 4"); - set(entries, "4", "message 5"); - set(entries, "5", "message 6"); - - check_log_file(file_handler, log_file_name, entries); - reset_log_count(logger, error); - reset_log_count(logger, failure); - - elsif run("Can get file name") then - init_log_handler(file_handler, file_name => log_file_name, format => raw); - assert_equal(get_file_name(file_handler), log_file_name); - assert_equal(get_file_name(display_handler), stdout_file_name); - - elsif run("Can print independent of logging") then - print("message 1", print_file_name); - print("message 2", print_file_name); - - file_open(status, fptr, print_file_name, append_mode); - assert status = open_ok - report "Failed to open file " & print_file_name & " - " & file_open_status'image(status) severity failure; - print("message 3", fptr); - file_close(fptr); - - set(entries, "0", "message 1"); - set(entries, "1", "message 2"); - set(entries, "2", "message 3"); - check_file(print_file_name, entries); - - print("message 6", print_file_name, write_mode); - set(entries2, "0", "message 6"); - check_file(print_file_name, entries2); - - elsif run("Can use 'instance_name") then - tmp_logger := get_logger(tmp_logger'instance_name); - assert_equal(get_name(tmp_logger), "tmp_logger"); - assert_equal(get_name(get_parent(tmp_logger)), "main"); - assert_equal(get_name(get_parent(get_parent(tmp_logger))), "tb_log(a)"); - assert_equal(get_full_name(tmp_logger), "tb_log(a):main:tmp_logger"); - - assert_equal(get_full_name(get_parent(get_parent(tmp_logger))), "tb_log(a)"); - assert_true(get_logger("tb_log(a):main:tmp_logger") = tmp_logger); - - tmp_logger := get_logger(main_path); - assert_true(main_path(main_path'right) = ':'); - assert_equal(get_name(tmp_logger), "main"); - assert_equal(get_name(get_parent(tmp_logger)), "tb_log(a)"); - assert_equal(get_full_name(tmp_logger), "tb_log(a):main"); - - assert_equal(get_full_name(get_parent(tmp_logger)), "tb_log(a)"); - assert_true(get_logger("tb_log(a):main") = tmp_logger); - - elsif run("level format") then - set_format(display_handler, format => level); - init_log_handler(file_handler, file_name => log_file_name, format => level); - disable_stop(logger, error); - disable_stop(logger, failure); - perform_logging(logger); - set(entries, "0", " TRACE - message 1"); - set(entries, "1", " DEBUG - message 2"); - set(entries, "2", " INFO - message 3"); - set(entries, "3", "WARNING - message 4"); - set(entries, "4", " ERROR - message 5"); - set(entries, "5", "FAILURE - message 6"); - check_log_file(file_handler, log_file_name, entries); - reset_log_count(logger, error); - reset_log_count(logger, failure); - - elsif run("level format aligns multi line logs") then - set_format(display_handler, format => level); - init_log_handler(file_handler, file_name => log_file_name, format => level); - info(logger, "hello" & LF & "world" & LF & " !"); - set(entries, "0", " INFO - hello"); - set(entries, "1", " world"); - set(entries, "2", " !"); - check_log_file(file_handler, log_file_name, entries); - - elsif run("csv format") then - set_format(display_handler, format => csv); - init_log_handler(file_handler, file_name => log_file_name, format => csv); - - wait for 3 ns; - tmp := get_log_count; - warning(nested_logger, "msg1"); - info(logger, "msg2", file_name => "file_name.vhd", line_num => 11); - set(entries, "0", integer'image(tmp+0) & "," & time'image(3 ns) & ",WARNING,,,logger:nested,msg1"); - set(entries, "1", integer'image(tmp+1) & "," & time'image(3 ns) & ",INFO,file_name.vhd,11,logger,msg2"); - check_log_file(file_handler, log_file_name, entries); - - elsif run("verbose format") then - set_format(display_handler, format => verbose); - init_log_handler(file_handler, file_name => log_file_name, format => verbose); - disable_stop(logger, error); - disable_stop(logger, failure); - perform_logging(logger); - set(entries, "0", format_time(0 ns) & " - logger - TRACE - message 1"); - set(entries, "1", format_time(1 ns) & " - logger - DEBUG - message 2"); - set(entries, "2", format_time(2 ns) & " - logger - INFO - message 3"); - set(entries, "3", format_time(3 ns) & " - logger - WARNING - message 4"); - set(entries, "4", format_time(4 ns) & " - logger - ERROR - message 5"); - set(entries, "5", format_time(5 ns) & " - logger - FAILURE - message 6"); - check_log_file(file_handler, log_file_name, entries); - reset_log_count(logger, error); - reset_log_count(logger, failure); - - elsif run("verbose format with file and line numbers") then - set_format(display_handler, format => verbose); - init_log_handler(file_handler, file_name => log_file_name, format => verbose); - info(logger, "message", file_name => "tb_log.vhd", line_num => 188); - info(logger, "hello" & LF & "world", file_name => "tb_log.vhd", line_num => 189); - set(entries, "0", format_time(0 ns) & " - logger - INFO - message (tb_log.vhd:188)"); - set(entries, "1", format_time(0 ns) & " - logger - INFO - hello (tb_log.vhd:189)"); - set(entries, "2", time_padding & " world"); - check_log_file(file_handler, log_file_name, entries); - - elsif run("verbose format aligns multi line logs") then - set_format(display_handler, format => verbose); - init_log_handler(file_handler, file_name => log_file_name, format => verbose); - info(logger, "hello" & LF & "world" & LF & " !"); - set(entries, "0", format_time(0 ns) & " - logger - INFO - hello"); - set(entries, "1", time_padding & " world"); - set(entries, "2", time_padding & " !"); - check_log_file(file_handler, log_file_name, entries); - - elsif run("hierarchical format") then - set_format(display_handler, format => verbose); - init_log_handler(file_handler, file_name => log_file_name, format => verbose); - disable_stop(nested_logger, error); - disable_stop(nested_logger, failure); - perform_logging(nested_logger); - set(entries, "0", format_time(0 ns) & " - logger:nested - TRACE - message 1"); - set(entries, "1", format_time(1 ns) & " - logger:nested - DEBUG - message 2"); - set(entries, "2", format_time(2 ns) & " - logger:nested - INFO - message 3"); - set(entries, "3", format_time(3 ns) & " - logger:nested - WARNING - message 4"); - set(entries, "4", format_time(4 ns) & " - logger:nested - ERROR - message 5"); - set(entries, "5", format_time(5 ns) & " - logger:nested - FAILURE - message 6"); - check_log_file(file_handler, log_file_name, entries); - reset_log_count(nested_logger, error); - reset_log_count(nested_logger, failure); - - elsif run("can log to default logger") then - init_log_handler(file_handler, file_name => log_file_name, format => level); - - disable_stop(default_logger, error); - disable_stop(default_logger, failure); - debug("message 1"); - wait for 1 ns; - trace("message 2"); - wait for 1 ns; - info("message 3"); - wait for 1 ns; - warning("message 4"); - wait for 1 ns; - error("message 5"); - wait for 1 ns; - failure("message 6"); - - set(entries, "0", " DEBUG - message 1"); - set(entries, "1", " TRACE - message 2"); - set(entries, "2", " INFO - message 3"); - set(entries, "3", "WARNING - message 4"); - set(entries, "4", " ERROR - message 5"); - set(entries, "5", "FAILURE - message 6"); - check_log_file(file_handler, log_file_name, entries); - reset_log_count(default_logger, error); - reset_log_count(default_logger, failure); - - elsif run("can show and hide from handler") then - init_log_handler(file_handler, file_name => log_file_name, format => level); - disable_stop(root_logger, error); - disable_stop(root_logger, failure); - - hide_all(file_handler); - for log_level in trace to failure loop - assert_false(is_visible(default_logger, file_handler, log_level)); - assert_false(is_visible(logger, file_handler, log_level)); - assert_false(is_visible(nested_logger, file_handler, log_level)); - end loop; - - perform_logging(logger); - check_empty_log_file(log_file_name); - - show_all(file_handler); - for log_level in trace to failure loop - assert_true(is_visible(default_logger, file_handler, log_level)); - assert_true(is_visible(logger, file_handler, log_level)); - assert_true(is_visible(nested_logger, file_handler, log_level)); - end loop; - - perform_logging(logger); - set(entries, "0", " TRACE - message 1"); - set(entries, "1", " DEBUG - message 2"); - set(entries, "2", " INFO - message 3"); - set(entries, "3", "WARNING - message 4"); - set(entries, "4", " ERROR - message 5"); - set(entries, "5", "FAILURE - message 6"); - check_log_file(file_handler, log_file_name, entries); - reset_log_count(logger, error); - reset_log_count(logger, failure); - - elsif run("can show individual levels") then - init_log_handler(file_handler, file_name => log_file_name, format => level); - hide_all(file_handler); - show(file_handler, (warning, error, failure)); - disable_stop(root_logger, error); - disable_stop(root_logger, failure); - perform_logging(logger); - set(entries, "0", "WARNING - message 4"); - set(entries, "1", " ERROR - message 5"); - set(entries, "2", "FAILURE - message 6"); - check_log_file(file_handler, log_file_name, entries); - reset_log_count(logger, error); - reset_log_count(logger, failure); - - elsif run("can hide individual levels") then - init_log_handler(file_handler, file_name => log_file_name, format => level); - hide(file_handler, (trace, debug, info, error, failure)); - disable_stop(root_logger, error); - disable_stop(root_logger, failure); - perform_logging(logger); - set(entries, "0", "WARNING - message 4"); - check_log_file(file_handler, log_file_name, entries); - reset_log_count(logger, error); - reset_log_count(logger, failure); - - elsif run("visibility also set for nested loggers") then - init_log_handler(file_handler, file_name => log_file_name, format => level); - hide_all(logger, file_handler); - show(logger, file_handler, failure); - info(logger, "message 1"); - info(nested_logger, "message 2"); - info("message 3"); - set(entries, "0", " INFO - message 3"); - check_log_file(file_handler, log_file_name, entries); - - elsif run("can show and hide source") then - init_log_handler(file_handler, file_name => log_file_name, format => level); - hide_all(logger, file_handler); - - for log_level in trace to failure loop - assert_true(is_visible(default_logger, file_handler, log_level)); - assert_false(is_visible(logger, file_handler, log_level)); - assert_false(is_visible(nested_logger, file_handler, log_level)); - end loop; - - info(logger, "message"); - info(nested_logger, "message"); - info("message"); - set(entries, "0", " INFO - message"); - check_log_file(file_handler, log_file_name, entries); - - init_log_handler(file_handler, file_name => log_file_name, format => level); - show_all(logger, file_handler); - for log_level in trace to failure loop - assert_true(is_visible(default_logger, file_handler, log_level)); - assert_true(is_visible(logger, file_handler, log_level)); - assert_true(is_visible(nested_logger, file_handler, log_level)); - end loop; - - info(logger, "message 1"); - info(nested_logger, "message 2"); - info("message 3"); - set(entries, "0", " INFO - message 1"); - set(entries, "1", " INFO - message 2"); - set(entries, "2", " INFO - message 3"); - check_log_file(file_handler, log_file_name, entries); - - elsif run("mock and unmock") then - mock(logger); - unmock(logger); - - elsif run("mock check_only_log") then - mock(logger); - warning(logger, "message"); - check_only_log(logger, "message", warning, 0 ns); - unmock(logger); - - elsif run("mock individual levels") then - mock(logger, error); - - warning(logger, "message"); - assert_equal(mock_queue_length, 0); - check_no_log; - - error(logger, "message"); - assert_equal(mock_queue_length, 1); - check_only_log(logger, "message", error, 0 ns); - - unmock(logger); - - elsif run("mock_queue_length") then - mock(logger); - assert_equal(mock_queue_length, 0); - warning(logger, "message"); - assert_equal(mock_queue_length, 1); - warning(logger, "message2"); - assert_equal(mock_queue_length, 2); - check_log(logger, "message", warning, 0 ns); - assert_equal(mock_queue_length, 1); - check_only_log(logger, "message2", warning, 0 ns); - assert_equal(mock_queue_length, 0); - unmock(logger); - - elsif run("mocked logger does not stop simulation") then - mock(logger); - failure(logger, "message"); - check_only_log(logger, "message", failure, 0 ns); - unmock(logger); - - elsif run("mocked logger is always visible") then - assert_true(is_visible(logger, failure)); - - hide_all(logger, display_handler); - hide_all(logger, file_handler); - assert_false(is_visible(logger, failure)); - - mock(logger); - assert_true(is_visible(logger, failure)); - - unmock(logger); - assert_false(is_visible(logger, failure)); - - elsif run("mock check_log") then - mock(logger); - warning(logger, "message"); - wait for 1 ns; - info(logger, "another message"); - check_log(logger, "message", warning, 0 ns); - check_log(logger, "another message", info, 1 ns); - unmock(logger); - - elsif run("unmock with unchecked log fails") then - mock(logger); - warning(logger, "message"); - - mock_core_failure; - unmock(logger); - check_and_unmock_core_failure; - - elsif run("check_only_log with no log fails") then - mock(logger); - mock_core_failure; - check_only_log(logger, "message", warning, 0 ns); - check_and_unmock_core_failure; - unmock(logger); - - elsif run("check_log with wrong level fails") then - mock(logger); - debug(logger, "message"); - mock_core_failure; - check_log(logger, "message", warning, 0 ns); - check_and_unmock_core_failure; - unmock(logger); - - elsif run("check_log with wrong message fails") then - mock(logger); - warning(logger, "another message"); - mock_core_failure; - check_log(logger, "message", warning, 0 ns); - check_and_unmock_core_failure; - unmock(logger); - - elsif run("check_log with wrong time fails") then - mock(logger); - wait for 1 ns; - warning(logger, "message"); - mock_core_failure; - check_log(logger, "message", warning, 0 ns); - check_and_unmock_core_failure; - unmock(logger); - - elsif run("check_log with wrong logger fails") then - mock(logger); - failure(logger, "message"); - mock_core_failure; - check_only_log(default_logger, "message", failure); - check_and_unmock_core_failure; - unmock(logger); - - elsif run("log above stop count fails") then - set_stop_count(logger, failure, 2); - -- Should not fail - failure(logger, "message"); - mock_core_failure; - failure(logger, "message"); - check_and_unmock_core_failure; - reset_log_count(logger); - - set_stop_count(root_logger, failure, 2); - -- Should not fail - failure("message"); - mock_core_failure; - failure("message"); - check_and_unmock_core_failure; - reset_log_count(default_logger); - - elsif run("unset stop count on root logger fails") then - unset_stop_count(root_logger, warning); - mock_core_failure; - warning("failure"); - check_and_unmock_core_failure("Stop condition not set on root_logger"); - - elsif run("set_stop_level") then - set_stop_level(warning); - assert_equal(get_stop_count(root_logger, warning), 1); - assert_equal(get_stop_count(root_logger, error), 1); - assert_equal(get_stop_count(root_logger, failure), 1); - - set_stop_level(error); - assert_equal(get_stop_count(root_logger, warning), integer'high); - assert_equal(get_stop_count(root_logger, error), 1); - assert_equal(get_stop_count(root_logger, failure), 1); - - set_stop_level(failure); - assert_equal(get_stop_count(root_logger, warning), integer'high); - assert_equal(get_stop_count(root_logger, error), integer'high); - assert_equal(get_stop_count(root_logger, failure), 1); - - -- Sets others to infinite - set_stop_count(root_logger, info, 1); - set_stop_level(failure); - assert_equal(get_stop_count(root_logger, info), integer'high); - - elsif run("set_stop_level clears subtree") then - set_stop_count(get_logger("parent:my_logger"), error, 1); - set_stop_level(get_logger("parent"), failure); - assert_false(has_stop_count(get_logger("parent:my_logger"), error)); - - elsif run("new logger has unset stop counts") then - tmp_logger := get_logger("new_logger"); - - for log_level in legal_log_level_t'low to legal_log_level_t'high loop - assert_false(has_stop_count(tmp_logger, log_level)); - end loop; - - elsif run("Get logger") then - tmp_logger := get_logger("logger:child"); - assert_equal(get_name(tmp_logger), "child"); - assert_equal(get_full_name(tmp_logger), "logger:child"); - - tmp_logger := get_logger("logger:child:grandchild"); - assert_equal(get_name(tmp_logger), "grandchild"); - assert_equal(get_full_name(tmp_logger), "logger:child:grandchild"); - - tmp_logger := get_logger("default"); - assert_true(tmp_logger = default_logger); - - tmp_logger := get_logger("logger:nested"); - assert_true(tmp_logger = nested_logger); - - tmp_logger := get_logger("nested", parent => logger); - assert_true(tmp_logger = nested_logger); - - elsif run("Create hierarchical logger") then - tmp_logger := get_logger("logger:child"); - assert_false(tmp_logger = null_logger, "logger not null"); - assert_equal(get_name(tmp_logger), "child", "nested logger name"); - assert_true(get_parent(tmp_logger) = logger, "parent logger"); - - elsif run("Log counts") then - disable_stop(root_logger, error); - disable_stop(root_logger, failure); - tmp := 0; - - for lvl in trace to failure loop - if is_valid(lvl) then - log(logger, "msg", lvl); - assert_equal(get_log_count(logger, lvl), 1); - end if; - end loop; - - reset_log_count(logger); - - for lvl in trace to failure loop - if is_valid(lvl) then - assert_equal(get_log_count(logger, lvl), 0); - end if; - end loop; - - for lvl in trace to failure loop - if is_valid(lvl) then - assert_equal(get_log_count(logger, lvl), 0); - log(logger, "msg", lvl); - tmp := tmp + 1; - - for lvl2 in trace to failure loop - if is_valid(lvl2) then - if lvl2 <= lvl then - assert_equal(get_log_count(logger, lvl2), 1); - else - assert_equal(get_log_count(logger, lvl2), 0); - end if; - end if; - end loop; - - assert_equal(get_log_count(logger), tmp, "total"); - end if; - end loop; - - for lvl in trace to failure loop - if is_valid(lvl) then - reset_log_count(logger, lvl); - assert_equal(get_log_count(logger, lvl), 0, "log count is reset"); - tmp := tmp - 1; - assert_equal(get_log_count(logger), tmp, "total"); - - for lvl2 in trace to failure loop - if is_valid(lvl2) then - if lvl2 > lvl then - assert_equal(get_log_count(logger, lvl2), 1); - else - assert_equal(get_log_count(logger, lvl2), 0); - end if; - end if; - end loop; - end if; - end loop; - - elsif run("Test global log count") then - tmp := get_log_count; - info(logger, "msg"); - assert_equal(get_log_count - tmp, 1); - - trace(logger, "msg"); - assert_equal(get_log_count - tmp, 2); - - elsif run("Does not log counts when mocked") then - mock(logger); - - tmp := 0; - for lvl in trace to failure loop - if is_valid(lvl) then - log(logger, "message", lvl); - assert_equal(get_log_count(logger, lvl), 0); - check_only_log(logger, "message", lvl); - tmp := tmp + 1; - end if; - end loop; - - assert_equal(get_log_count(logger), 0); - - unmock(logger); - - assert_equal(get_log_count(logger), 0); - - for lvl in trace to failure loop - if is_valid(lvl) then - assert_equal(get_log_count(logger, lvl), 0); - end if; - end loop; - - elsif run("Test logger name validation") then - tmp_logger := get_logger("foo:bar"); - assert_equal(get_name(get_logger("foo")), "foo"); - - mock_core_failure; - tmp_logger := get_logger("foo,bar"); - check_core_failure("Invalid logger name ""foo,bar"""); - - tmp_logger := get_logger("parent:foo,bar"); - check_core_failure("Invalid logger name ""parent:foo,bar"""); - - tmp_logger := get_logger(""); - check_core_failure("Invalid logger name """""); - - tmp_logger := get_logger(":"); - check_core_failure("Invalid logger name """""); - - unmock_core_failure; - - elsif run("Test final log check fails for errors") then - disable_stop(error); - error(get_logger("parent:my_logger"), "message"); - mock_core_failure; - final_log_check; - check_and_unmock_core_failure; - - reset_log_count(get_logger("parent:my_logger"), error); - - elsif run("Test final log check fails for failures") then - disable_stop(failure); - failure(get_logger("parent:my_logger"), "message"); - failure(get_logger("parent:my_logger"), "message"); - mock_core_failure; - final_log_check; - check_and_unmock_core_failure; - - reset_log_count(get_logger("parent:my_logger"), failure); - - elsif run("Test final log check fails for unmocked logger") then - mock(get_logger("parent:my_logger"), failure); - mock_core_failure; - final_log_check; - check_and_unmock_core_failure; - - unmock(get_logger("parent:my_logger")); - - elsif run("Test final log check fails for disabled error") then - disable(get_logger("parent:my_logger"), error); - error(get_logger("parent:my_logger"), "message"); - mock_core_failure; - final_log_check; - check_and_unmock_core_failure; - - reset_log_count(get_logger("parent:my_logger"), error); - - elsif run("Test final log check fails for disabled failure") then - disable(get_logger("parent:my_logger"), failure); - failure(get_logger("parent:my_logger"), "message"); - mock_core_failure; - final_log_check; - check_and_unmock_core_failure; - - reset_log_count(get_logger("parent:my_logger"), failure); - - elsif run("Test final log check can allow disabled errors") then - disable(get_logger("parent:my_logger"), error); - error(get_logger("parent:my_logger"), "message"); - final_log_check(allow_disabled_errors => true); - reset_log_count(get_logger("parent:my_logger"), error); - - elsif run("Test final log check can allow disabled failures") then - disable(get_logger("parent:my_logger"), failure); - failure(get_logger("parent:my_logger"), "message"); - final_log_check(allow_disabled_failures => true); - reset_log_count(get_logger("parent:my_logger"), failure); - - elsif run("Test final log check optionally fails for warnings") then - warning(get_logger("parent:my_logger"), "message"); - final_log_check; -- Does not fail - - mock_core_failure; - final_log_check(fail_on_warning => true); - check_and_unmock_core_failure; - - -- Does not fail for disabled warnings - disable(get_logger("parent:my_logger"), warning); - final_log_check(fail_on_warning => true); - - elsif run("Test conditional logs") then - mock(logger); - - -- Warning - warning_if(logger, True, "message"); - check_only_log(logger, "message", warning); - - warning_if(logger, False, "message"); - check_no_log; - - -- Error - error_if(logger, True, "message"); - check_only_log(logger, "message", error); - - error_if(logger, False, "message"); - check_no_log; - - -- Failure - failure_if(logger, True, "message"); - check_only_log(logger, "message", failure); - - failure_if(logger, False, "message"); - check_no_log; - - unmock(logger); - - elsif run("Test conditional default logs") then - mock(default_logger); - - -- Warning - warning_if(True, "message"); - check_only_log(default_logger, "message", warning); - - warning_if(False, "message"); - check_no_log; - - -- Error - error_if(True, "message"); - check_only_log(default_logger, "message", error); - - error_if(False, "message"); - check_no_log; - - -- Failure - failure_if(True, "message"); - check_only_log(default_logger, "message", failure); - - failure_if(False, "message"); - check_no_log; - - unmock(default_logger); - - elsif run("Disabled log does not stop simulation") then - set_stop_count(logger, warning, 1); - - -- Disabled log level should not stop simulation - disable(logger, warning); - warning(logger, "message"); - - -- But still be counted - assert_equal(get_log_count(logger, warning), 1); - - elsif run("Disabled log is not visible") then - init_log_handler(file_handler, file_name => log_file_name, format => raw); - warning(logger, "message"); - set(entries, "0", "message"); - check_log_file(file_handler, log_file_name, entries); - - init_log_handler(file_handler, file_name => log_file_name, format => raw); - disable(logger, warning); - assert_false(is_visible(logger, warning)); - warning(logger, "message"); - set(entries, "0", "message"); - check_empty_log_file(log_file_name); - - elsif run("many log files") then - set_format(display_handler, format => raw); - - for i in file_handlers'range loop - file_handlers(i) := new_log_handler(log_file_name & integer'image(i), - format => raw, - use_color => false); - loggers(i) := get_logger("log_to_file" & integer'image(i)); - set_log_handlers(loggers(i), (0 => file_handlers(i))); - show(loggers(i), file_handlers(i), info); - end loop; - - for i in file_handlers'range loop - info(loggers(i), "a " & integer'image(i)); - info(loggers(i), "b " & integer'image(i)); - info(loggers(i), "c " & integer'image(i)); - end loop; - - for i in file_handlers'range loop - set(entries, "0", "a " & integer'image(i)); - set(entries, "1", "b " & integer'image(i)); - set(entries, "2", "c " & integer'image(i)); - - check_log_file(file_handlers(i), log_file_name & integer'image(i), entries); - end loop; - - end if; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use std.textio.all; + +library vunit_lib; +use vunit_lib.run_pkg.all; +use vunit_lib.dict_pkg.all; + +use work.log_levels_pkg.all; +use work.logger_pkg.all; +use work.log_handler_pkg.all; +use work.core_pkg.all; +use work.test_support_pkg.all; +use work.print_pkg.all; + +entity tb_log is + generic ( + runner_cfg : string); +end entity; + +architecture a of tb_log is +begin + main : process + constant main_path : string := main'instance_name; + + procedure check_empty_log_file(constant file_name : in string) is + file fptr : text; + variable status : file_open_status; + begin + file_open(status, fptr, file_name, read_mode); + assert status = open_ok report "Expected a file " & file_name severity failure; + assert endfile(fptr) report "Expected " & file_name & " to be empty" severity FAILURE; + file_close(fptr); + end; + + procedure flush_file_handler(file_handler : log_handler_t) is + begin + -- Dummy init to flush file handler + init_log_handler(file_handler, + file_name => get_file_name(file_handler) & ".dummy", + format => raw); + end; + + procedure check_file(file_name : string; + entries : dict_t) is + file fptr : text; + variable l : line; + variable status : file_open_status; + begin + file_open(status, fptr, file_name, read_mode); + assert status = open_ok + report "Failed opening " & file_name & " (" & file_open_status'image(status) & ")." + severity failure; + + if status = open_ok then + for i in 0 to num_keys(entries)-1 loop + readline(fptr, l); + assert l.all = get(entries, integer'image(i)) + report "(" & integer'image(i) & ") " & LF & "Got:" & LF & l.all & LF & "expected:" & LF & get(entries, integer'image(i)) + severity failure; + end loop; + end if; + end; + + procedure check_log_file (file_handler : log_handler_t; + file_name : string; + entries : dict_t) is + file fptr : text; + variable l : line; + variable status : file_open_status; + begin + assert get_file_name(file_handler) = file_name report "file name mismatch"; + flush_file_handler(file_handler); + check_file(file_name, entries); + end; + + constant log_file_name : string := output_path(runner_cfg) & "my_log.csv"; + constant print_file_name : string := output_path(runner_cfg) & "print.csv"; + variable logger : logger_t := get_logger("logger"); + variable nested_logger : logger_t := get_logger("nested", parent => logger); + variable other_logger : logger_t := get_logger("other"); + variable tmp_logger : logger_t; + variable entries : dict_t := new_dict; + variable entries2 : dict_t := new_dict; + variable tmp : integer; + file fptr : text; + variable status : file_open_status; + + procedure perform_logging(logger : logger_t) is + begin + trace(logger, "message 1"); + wait for 1 ns; + debug(logger, "message 2"); + wait for 1 ns; + info(logger, "message 3"); + wait for 1 ns; + warning(logger, "message 4"); + wait for 1 ns; + error(logger, "message 5"); + wait for 1 ns; + failure(logger, "message 6"); + wait for 1 ns; + end procedure; + + constant max_time_str : string := time'image(1 sec); + constant time_padding : string(max_time_str'range) := (others => ' '); + impure function format_time(t : time) return string is + constant time_str : string := time'image(t); + begin + return (1 to (max_time_str'length - time_str'length) => ' ') & time_str; + end function; + + variable file_handler : log_handler_t := new_log_handler(log_file_name, + format => verbose, + use_color => false); + variable file_handlers : log_handler_vec_t(0 to 63); + variable loggers : logger_vec_t(file_handlers'range); + begin + + -- Check defaults before test runner setup + assert_equal(get_log_count, 0); + assert_equal(num_log_handlers(logger), 1); + assert_true(get_log_handler(logger, 0) = display_handler); + assert_true(get_log_handlers(logger) = (0 => display_handler)); + assert_true(get_visible_log_levels(logger, display_handler) = (info, warning, error, failure)); + + -- Check default stop count + for log_level in legal_log_level_t'low to legal_log_level_t'high loop + case log_level is + when error|failure => + assert_equal(get_stop_count(root_logger, log_level), 1); + when others => + assert_equal(get_stop_count(root_logger, log_level), integer'high); + end case; + end loop; + + test_runner_setup(runner, runner_cfg); + set_log_handlers(root_logger, (display_handler, file_handler)); + show_all(root_logger, file_handler); + show_all(root_logger, display_handler); + + if run("raw format") then + set_format(display_handler, format => raw); + init_log_handler(file_handler, file_name => log_file_name, format => raw); + disable_stop(logger, error); + disable_stop(logger, failure); + perform_logging(logger); + set(entries, "0", "message 1"); + set(entries, "1", "message 2"); + set(entries, "2", "message 3"); + set(entries, "3", "message 4"); + set(entries, "4", "message 5"); + set(entries, "5", "message 6"); + + check_log_file(file_handler, log_file_name, entries); + reset_log_count(logger, error); + reset_log_count(logger, failure); + + elsif run("Can get file name") then + init_log_handler(file_handler, file_name => log_file_name, format => raw); + assert_equal(get_file_name(file_handler), log_file_name); + assert_equal(get_file_name(display_handler), stdout_file_name); + + elsif run("Can print independent of logging") then + print("message 1", print_file_name); + print("message 2", print_file_name); + + file_open(status, fptr, print_file_name, append_mode); + assert status = open_ok + report "Failed to open file " & print_file_name & " - " & file_open_status'image(status) severity failure; + print("message 3", fptr); + file_close(fptr); + + set(entries, "0", "message 1"); + set(entries, "1", "message 2"); + set(entries, "2", "message 3"); + check_file(print_file_name, entries); + + print("message 6", print_file_name, write_mode); + set(entries2, "0", "message 6"); + check_file(print_file_name, entries2); + + elsif run("Can use 'instance_name") then + tmp_logger := get_logger(tmp_logger'instance_name); + assert_equal(get_name(tmp_logger), "tmp_logger"); + assert_equal(get_name(get_parent(tmp_logger)), "main"); + assert_equal(get_name(get_parent(get_parent(tmp_logger))), "tb_log(a)"); + assert_equal(get_full_name(tmp_logger), "tb_log(a):main:tmp_logger"); + + assert_equal(get_full_name(get_parent(get_parent(tmp_logger))), "tb_log(a)"); + assert_true(get_logger("tb_log(a):main:tmp_logger") = tmp_logger); + + tmp_logger := get_logger(main_path); + assert_true(main_path(main_path'right) = ':'); + assert_equal(get_name(tmp_logger), "main"); + assert_equal(get_name(get_parent(tmp_logger)), "tb_log(a)"); + assert_equal(get_full_name(tmp_logger), "tb_log(a):main"); + + assert_equal(get_full_name(get_parent(tmp_logger)), "tb_log(a)"); + assert_true(get_logger("tb_log(a):main") = tmp_logger); + + elsif run("level format") then + set_format(display_handler, format => level); + init_log_handler(file_handler, file_name => log_file_name, format => level); + disable_stop(logger, error); + disable_stop(logger, failure); + perform_logging(logger); + set(entries, "0", " TRACE - message 1"); + set(entries, "1", " DEBUG - message 2"); + set(entries, "2", " INFO - message 3"); + set(entries, "3", "WARNING - message 4"); + set(entries, "4", " ERROR - message 5"); + set(entries, "5", "FAILURE - message 6"); + check_log_file(file_handler, log_file_name, entries); + reset_log_count(logger, error); + reset_log_count(logger, failure); + + elsif run("level format aligns multi line logs") then + set_format(display_handler, format => level); + init_log_handler(file_handler, file_name => log_file_name, format => level); + info(logger, "hello" & LF & "world" & LF & " !"); + set(entries, "0", " INFO - hello"); + set(entries, "1", " world"); + set(entries, "2", " !"); + check_log_file(file_handler, log_file_name, entries); + + elsif run("csv format") then + set_format(display_handler, format => csv); + init_log_handler(file_handler, file_name => log_file_name, format => csv); + + wait for 3 ns; + tmp := get_log_count; + warning(nested_logger, "msg1"); + info(logger, "msg2", file_name => "file_name.vhd", line_num => 11); + set(entries, "0", integer'image(tmp+0) & "," & time'image(3 ns) & ",WARNING,,,logger:nested,msg1"); + set(entries, "1", integer'image(tmp+1) & "," & time'image(3 ns) & ",INFO,file_name.vhd,11,logger,msg2"); + check_log_file(file_handler, log_file_name, entries); + + elsif run("verbose format") then + set_format(display_handler, format => verbose); + init_log_handler(file_handler, file_name => log_file_name, format => verbose); + disable_stop(logger, error); + disable_stop(logger, failure); + perform_logging(logger); + set(entries, "0", format_time(0 ns) & " - logger - TRACE - message 1"); + set(entries, "1", format_time(1 ns) & " - logger - DEBUG - message 2"); + set(entries, "2", format_time(2 ns) & " - logger - INFO - message 3"); + set(entries, "3", format_time(3 ns) & " - logger - WARNING - message 4"); + set(entries, "4", format_time(4 ns) & " - logger - ERROR - message 5"); + set(entries, "5", format_time(5 ns) & " - logger - FAILURE - message 6"); + check_log_file(file_handler, log_file_name, entries); + reset_log_count(logger, error); + reset_log_count(logger, failure); + + elsif run("verbose format with file and line numbers") then + set_format(display_handler, format => verbose); + init_log_handler(file_handler, file_name => log_file_name, format => verbose); + info(logger, "message", file_name => "tb_log.vhd", line_num => 188); + info(logger, "hello" & LF & "world", file_name => "tb_log.vhd", line_num => 189); + set(entries, "0", format_time(0 ns) & " - logger - INFO - message (tb_log.vhd:188)"); + set(entries, "1", format_time(0 ns) & " - logger - INFO - hello (tb_log.vhd:189)"); + set(entries, "2", time_padding & " world"); + check_log_file(file_handler, log_file_name, entries); + + elsif run("verbose format aligns multi line logs") then + set_format(display_handler, format => verbose); + init_log_handler(file_handler, file_name => log_file_name, format => verbose); + info(logger, "hello" & LF & "world" & LF & " !"); + set(entries, "0", format_time(0 ns) & " - logger - INFO - hello"); + set(entries, "1", time_padding & " world"); + set(entries, "2", time_padding & " !"); + check_log_file(file_handler, log_file_name, entries); + + elsif run("hierarchical format") then + set_format(display_handler, format => verbose); + init_log_handler(file_handler, file_name => log_file_name, format => verbose); + disable_stop(nested_logger, error); + disable_stop(nested_logger, failure); + perform_logging(nested_logger); + set(entries, "0", format_time(0 ns) & " - logger:nested - TRACE - message 1"); + set(entries, "1", format_time(1 ns) & " - logger:nested - DEBUG - message 2"); + set(entries, "2", format_time(2 ns) & " - logger:nested - INFO - message 3"); + set(entries, "3", format_time(3 ns) & " - logger:nested - WARNING - message 4"); + set(entries, "4", format_time(4 ns) & " - logger:nested - ERROR - message 5"); + set(entries, "5", format_time(5 ns) & " - logger:nested - FAILURE - message 6"); + check_log_file(file_handler, log_file_name, entries); + reset_log_count(nested_logger, error); + reset_log_count(nested_logger, failure); + + elsif run("can log to default logger") then + init_log_handler(file_handler, file_name => log_file_name, format => level); + + disable_stop(default_logger, error); + disable_stop(default_logger, failure); + debug("message 1"); + wait for 1 ns; + trace("message 2"); + wait for 1 ns; + info("message 3"); + wait for 1 ns; + warning("message 4"); + wait for 1 ns; + error("message 5"); + wait for 1 ns; + failure("message 6"); + + set(entries, "0", " DEBUG - message 1"); + set(entries, "1", " TRACE - message 2"); + set(entries, "2", " INFO - message 3"); + set(entries, "3", "WARNING - message 4"); + set(entries, "4", " ERROR - message 5"); + set(entries, "5", "FAILURE - message 6"); + check_log_file(file_handler, log_file_name, entries); + reset_log_count(default_logger, error); + reset_log_count(default_logger, failure); + + elsif run("can show and hide from handler") then + init_log_handler(file_handler, file_name => log_file_name, format => level); + disable_stop(root_logger, error); + disable_stop(root_logger, failure); + + hide_all(file_handler); + for log_level in trace to failure loop + assert_false(is_visible(default_logger, file_handler, log_level)); + assert_false(is_visible(logger, file_handler, log_level)); + assert_false(is_visible(nested_logger, file_handler, log_level)); + end loop; + + perform_logging(logger); + check_empty_log_file(log_file_name); + + show_all(file_handler); + for log_level in trace to failure loop + assert_true(is_visible(default_logger, file_handler, log_level)); + assert_true(is_visible(logger, file_handler, log_level)); + assert_true(is_visible(nested_logger, file_handler, log_level)); + end loop; + + perform_logging(logger); + set(entries, "0", " TRACE - message 1"); + set(entries, "1", " DEBUG - message 2"); + set(entries, "2", " INFO - message 3"); + set(entries, "3", "WARNING - message 4"); + set(entries, "4", " ERROR - message 5"); + set(entries, "5", "FAILURE - message 6"); + check_log_file(file_handler, log_file_name, entries); + reset_log_count(logger, error); + reset_log_count(logger, failure); + + elsif run("can show individual levels") then + init_log_handler(file_handler, file_name => log_file_name, format => level); + hide_all(file_handler); + show(file_handler, (warning, error, failure)); + disable_stop(root_logger, error); + disable_stop(root_logger, failure); + perform_logging(logger); + set(entries, "0", "WARNING - message 4"); + set(entries, "1", " ERROR - message 5"); + set(entries, "2", "FAILURE - message 6"); + check_log_file(file_handler, log_file_name, entries); + reset_log_count(logger, error); + reset_log_count(logger, failure); + + elsif run("can hide individual levels") then + init_log_handler(file_handler, file_name => log_file_name, format => level); + hide(file_handler, (trace, debug, info, error, failure)); + disable_stop(root_logger, error); + disable_stop(root_logger, failure); + perform_logging(logger); + set(entries, "0", "WARNING - message 4"); + check_log_file(file_handler, log_file_name, entries); + reset_log_count(logger, error); + reset_log_count(logger, failure); + + elsif run("visibility also set for nested loggers") then + init_log_handler(file_handler, file_name => log_file_name, format => level); + hide_all(logger, file_handler); + show(logger, file_handler, failure); + info(logger, "message 1"); + info(nested_logger, "message 2"); + info("message 3"); + set(entries, "0", " INFO - message 3"); + check_log_file(file_handler, log_file_name, entries); + + elsif run("can show and hide source") then + init_log_handler(file_handler, file_name => log_file_name, format => level); + hide_all(logger, file_handler); + + for log_level in trace to failure loop + assert_true(is_visible(default_logger, file_handler, log_level)); + assert_false(is_visible(logger, file_handler, log_level)); + assert_false(is_visible(nested_logger, file_handler, log_level)); + end loop; + + info(logger, "message"); + info(nested_logger, "message"); + info("message"); + set(entries, "0", " INFO - message"); + check_log_file(file_handler, log_file_name, entries); + + init_log_handler(file_handler, file_name => log_file_name, format => level); + show_all(logger, file_handler); + for log_level in trace to failure loop + assert_true(is_visible(default_logger, file_handler, log_level)); + assert_true(is_visible(logger, file_handler, log_level)); + assert_true(is_visible(nested_logger, file_handler, log_level)); + end loop; + + info(logger, "message 1"); + info(nested_logger, "message 2"); + info("message 3"); + set(entries, "0", " INFO - message 1"); + set(entries, "1", " INFO - message 2"); + set(entries, "2", " INFO - message 3"); + check_log_file(file_handler, log_file_name, entries); + + elsif run("mock and unmock") then + mock(logger); + unmock(logger); + + elsif run("mock check_only_log") then + mock(logger); + warning(logger, "message"); + check_only_log(logger, "message", warning, 0 ns); + unmock(logger); + + elsif run("mock individual levels") then + mock(logger, error); + + warning(logger, "message"); + assert_equal(mock_queue_length, 0); + check_no_log; + + error(logger, "message"); + assert_equal(mock_queue_length, 1); + check_only_log(logger, "message", error, 0 ns); + + unmock(logger); + + elsif run("mock_queue_length") then + mock(logger); + assert_equal(mock_queue_length, 0); + warning(logger, "message"); + assert_equal(mock_queue_length, 1); + warning(logger, "message2"); + assert_equal(mock_queue_length, 2); + check_log(logger, "message", warning, 0 ns); + assert_equal(mock_queue_length, 1); + check_only_log(logger, "message2", warning, 0 ns); + assert_equal(mock_queue_length, 0); + unmock(logger); + + elsif run("mocked logger does not stop simulation") then + mock(logger); + failure(logger, "message"); + check_only_log(logger, "message", failure, 0 ns); + unmock(logger); + + elsif run("mocked logger is always visible") then + assert_true(is_visible(logger, failure)); + + hide_all(logger, display_handler); + hide_all(logger, file_handler); + assert_false(is_visible(logger, failure)); + + mock(logger); + assert_true(is_visible(logger, failure)); + + unmock(logger); + assert_false(is_visible(logger, failure)); + + elsif run("mock check_log") then + mock(logger); + warning(logger, "message"); + wait for 1 ns; + info(logger, "another message"); + check_log(logger, "message", warning, 0 ns); + check_log(logger, "another message", info, 1 ns); + unmock(logger); + + elsif run("unmock with unchecked log fails") then + mock(logger); + warning(logger, "message"); + + mock_core_failure; + unmock(logger); + check_and_unmock_core_failure; + + elsif run("check_only_log with no log fails") then + mock(logger); + mock_core_failure; + check_only_log(logger, "message", warning, 0 ns); + check_and_unmock_core_failure; + unmock(logger); + + elsif run("check_log with wrong level fails") then + mock(logger); + debug(logger, "message"); + mock_core_failure; + check_log(logger, "message", warning, 0 ns); + check_and_unmock_core_failure; + unmock(logger); + + elsif run("check_log with wrong message fails") then + mock(logger); + warning(logger, "another message"); + mock_core_failure; + check_log(logger, "message", warning, 0 ns); + check_and_unmock_core_failure; + unmock(logger); + + elsif run("check_log with wrong time fails") then + mock(logger); + wait for 1 ns; + warning(logger, "message"); + mock_core_failure; + check_log(logger, "message", warning, 0 ns); + check_and_unmock_core_failure; + unmock(logger); + + elsif run("check_log with wrong logger fails") then + mock(logger); + failure(logger, "message"); + mock_core_failure; + check_only_log(default_logger, "message", failure); + check_and_unmock_core_failure; + unmock(logger); + + elsif run("log above stop count fails") then + set_stop_count(logger, failure, 2); + -- Should not fail + failure(logger, "message"); + mock_core_failure; + failure(logger, "message"); + check_and_unmock_core_failure; + reset_log_count(logger); + + set_stop_count(root_logger, failure, 2); + -- Should not fail + failure("message"); + mock_core_failure; + failure("message"); + check_and_unmock_core_failure; + reset_log_count(default_logger); + + elsif run("unset stop count on root logger fails") then + unset_stop_count(root_logger, warning); + mock_core_failure; + warning("failure"); + check_and_unmock_core_failure("Stop condition not set on root_logger"); + + elsif run("set_stop_level") then + set_stop_level(warning); + assert_equal(get_stop_count(root_logger, warning), 1); + assert_equal(get_stop_count(root_logger, error), 1); + assert_equal(get_stop_count(root_logger, failure), 1); + + set_stop_level(error); + assert_equal(get_stop_count(root_logger, warning), integer'high); + assert_equal(get_stop_count(root_logger, error), 1); + assert_equal(get_stop_count(root_logger, failure), 1); + + set_stop_level(failure); + assert_equal(get_stop_count(root_logger, warning), integer'high); + assert_equal(get_stop_count(root_logger, error), integer'high); + assert_equal(get_stop_count(root_logger, failure), 1); + + -- Sets others to infinite + set_stop_count(root_logger, info, 1); + set_stop_level(failure); + assert_equal(get_stop_count(root_logger, info), integer'high); + + elsif run("set_stop_level clears subtree") then + set_stop_count(get_logger("parent:my_logger"), error, 1); + set_stop_level(get_logger("parent"), failure); + assert_false(has_stop_count(get_logger("parent:my_logger"), error)); + + elsif run("new logger has unset stop counts") then + tmp_logger := get_logger("new_logger"); + + for log_level in legal_log_level_t'low to legal_log_level_t'high loop + assert_false(has_stop_count(tmp_logger, log_level)); + end loop; + + elsif run("Get logger") then + tmp_logger := get_logger("logger:child"); + assert_equal(get_name(tmp_logger), "child"); + assert_equal(get_full_name(tmp_logger), "logger:child"); + + tmp_logger := get_logger("logger:child:grandchild"); + assert_equal(get_name(tmp_logger), "grandchild"); + assert_equal(get_full_name(tmp_logger), "logger:child:grandchild"); + + tmp_logger := get_logger("default"); + assert_true(tmp_logger = default_logger); + + tmp_logger := get_logger("logger:nested"); + assert_true(tmp_logger = nested_logger); + + tmp_logger := get_logger("nested", parent => logger); + assert_true(tmp_logger = nested_logger); + + elsif run("Create hierarchical logger") then + tmp_logger := get_logger("logger:child"); + assert_false(tmp_logger = null_logger, "logger not null"); + assert_equal(get_name(tmp_logger), "child", "nested logger name"); + assert_true(get_parent(tmp_logger) = logger, "parent logger"); + + elsif run("Log counts") then + disable_stop(root_logger, error); + disable_stop(root_logger, failure); + tmp := 0; + + for lvl in trace to failure loop + if is_valid(lvl) then + log(logger, "msg", lvl); + assert_equal(get_log_count(logger, lvl), 1); + end if; + end loop; + + reset_log_count(logger); + + for lvl in trace to failure loop + if is_valid(lvl) then + assert_equal(get_log_count(logger, lvl), 0); + end if; + end loop; + + for lvl in trace to failure loop + if is_valid(lvl) then + assert_equal(get_log_count(logger, lvl), 0); + log(logger, "msg", lvl); + tmp := tmp + 1; + + for lvl2 in trace to failure loop + if is_valid(lvl2) then + if lvl2 <= lvl then + assert_equal(get_log_count(logger, lvl2), 1); + else + assert_equal(get_log_count(logger, lvl2), 0); + end if; + end if; + end loop; + + assert_equal(get_log_count(logger), tmp, "total"); + end if; + end loop; + + for lvl in trace to failure loop + if is_valid(lvl) then + reset_log_count(logger, lvl); + assert_equal(get_log_count(logger, lvl), 0, "log count is reset"); + tmp := tmp - 1; + assert_equal(get_log_count(logger), tmp, "total"); + + for lvl2 in trace to failure loop + if is_valid(lvl2) then + if lvl2 > lvl then + assert_equal(get_log_count(logger, lvl2), 1); + else + assert_equal(get_log_count(logger, lvl2), 0); + end if; + end if; + end loop; + end if; + end loop; + + elsif run("Test global log count") then + tmp := get_log_count; + info(logger, "msg"); + assert_equal(get_log_count - tmp, 1); + + trace(logger, "msg"); + assert_equal(get_log_count - tmp, 2); + + elsif run("Does not log counts when mocked") then + mock(logger); + + tmp := 0; + for lvl in trace to failure loop + if is_valid(lvl) then + log(logger, "message", lvl); + assert_equal(get_log_count(logger, lvl), 0); + check_only_log(logger, "message", lvl); + tmp := tmp + 1; + end if; + end loop; + + assert_equal(get_log_count(logger), 0); + + unmock(logger); + + assert_equal(get_log_count(logger), 0); + + for lvl in trace to failure loop + if is_valid(lvl) then + assert_equal(get_log_count(logger, lvl), 0); + end if; + end loop; + + elsif run("Test logger name validation") then + tmp_logger := get_logger("foo:bar"); + assert_equal(get_name(get_logger("foo")), "foo"); + + mock_core_failure; + tmp_logger := get_logger("foo,bar"); + check_core_failure("Invalid logger name ""foo,bar"""); + + tmp_logger := get_logger("parent:foo,bar"); + check_core_failure("Invalid logger name ""parent:foo,bar"""); + + tmp_logger := get_logger(""); + check_core_failure("Invalid logger name """""); + + tmp_logger := get_logger(":"); + check_core_failure("Invalid logger name """""); + + unmock_core_failure; + + elsif run("Test final log check fails for errors") then + disable_stop(error); + error(get_logger("parent:my_logger"), "message"); + mock_core_failure; + final_log_check; + check_and_unmock_core_failure; + + reset_log_count(get_logger("parent:my_logger"), error); + + elsif run("Test final log check fails for failures") then + disable_stop(failure); + failure(get_logger("parent:my_logger"), "message"); + failure(get_logger("parent:my_logger"), "message"); + mock_core_failure; + final_log_check; + check_and_unmock_core_failure; + + reset_log_count(get_logger("parent:my_logger"), failure); + + elsif run("Test final log check fails for unmocked logger") then + mock(get_logger("parent:my_logger"), failure); + mock_core_failure; + final_log_check; + check_and_unmock_core_failure; + + unmock(get_logger("parent:my_logger")); + + elsif run("Test final log check fails for disabled error") then + disable(get_logger("parent:my_logger"), error); + error(get_logger("parent:my_logger"), "message"); + mock_core_failure; + final_log_check; + check_and_unmock_core_failure; + + reset_log_count(get_logger("parent:my_logger"), error); + + elsif run("Test final log check fails for disabled failure") then + disable(get_logger("parent:my_logger"), failure); + failure(get_logger("parent:my_logger"), "message"); + mock_core_failure; + final_log_check; + check_and_unmock_core_failure; + + reset_log_count(get_logger("parent:my_logger"), failure); + + elsif run("Test final log check can allow disabled errors") then + disable(get_logger("parent:my_logger"), error); + error(get_logger("parent:my_logger"), "message"); + final_log_check(allow_disabled_errors => true); + reset_log_count(get_logger("parent:my_logger"), error); + + elsif run("Test final log check can allow disabled failures") then + disable(get_logger("parent:my_logger"), failure); + failure(get_logger("parent:my_logger"), "message"); + final_log_check(allow_disabled_failures => true); + reset_log_count(get_logger("parent:my_logger"), failure); + + elsif run("Test final log check optionally fails for warnings") then + warning(get_logger("parent:my_logger"), "message"); + final_log_check; -- Does not fail + + mock_core_failure; + final_log_check(fail_on_warning => true); + check_and_unmock_core_failure; + + -- Does not fail for disabled warnings + disable(get_logger("parent:my_logger"), warning); + final_log_check(fail_on_warning => true); + + elsif run("Test conditional logs") then + mock(logger); + + -- Warning + warning_if(logger, True, "message"); + check_only_log(logger, "message", warning); + + warning_if(logger, False, "message"); + check_no_log; + + -- Error + error_if(logger, True, "message"); + check_only_log(logger, "message", error); + + error_if(logger, False, "message"); + check_no_log; + + -- Failure + failure_if(logger, True, "message"); + check_only_log(logger, "message", failure); + + failure_if(logger, False, "message"); + check_no_log; + + unmock(logger); + + elsif run("Test conditional default logs") then + mock(default_logger); + + -- Warning + warning_if(True, "message"); + check_only_log(default_logger, "message", warning); + + warning_if(False, "message"); + check_no_log; + + -- Error + error_if(True, "message"); + check_only_log(default_logger, "message", error); + + error_if(False, "message"); + check_no_log; + + -- Failure + failure_if(True, "message"); + check_only_log(default_logger, "message", failure); + + failure_if(False, "message"); + check_no_log; + + unmock(default_logger); + + elsif run("Disabled log does not stop simulation") then + set_stop_count(logger, warning, 1); + + -- Disabled log level should not stop simulation + disable(logger, warning); + warning(logger, "message"); + + -- But still be counted + assert_equal(get_log_count(logger, warning), 1); + + elsif run("Disabled log is not visible") then + init_log_handler(file_handler, file_name => log_file_name, format => raw); + warning(logger, "message"); + set(entries, "0", "message"); + check_log_file(file_handler, log_file_name, entries); + + init_log_handler(file_handler, file_name => log_file_name, format => raw); + disable(logger, warning); + assert_false(is_visible(logger, warning)); + warning(logger, "message"); + set(entries, "0", "message"); + check_empty_log_file(log_file_name); + + elsif run("many log files") then + set_format(display_handler, format => raw); + + for i in file_handlers'range loop + file_handlers(i) := new_log_handler(log_file_name & integer'image(i), + format => raw, + use_color => false); + loggers(i) := get_logger("log_to_file" & integer'image(i)); + set_log_handlers(loggers(i), (0 => file_handlers(i))); + show(loggers(i), file_handlers(i), info); + end loop; + + for i in file_handlers'range loop + info(loggers(i), "a " & integer'image(i)); + info(loggers(i), "b " & integer'image(i)); + info(loggers(i), "c " & integer'image(i)); + end loop; + + for i in file_handlers'range loop + set(entries, "0", "a " & integer'image(i)); + set(entries, "1", "b " & integer'image(i)); + set(entries, "2", "c " & integer'image(i)); + + check_log_file(file_handlers(i), log_file_name & integer'image(i), entries); + end loop; + + end if; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/vhdl/logging/test/tb_log_levels.vhd b/vunit/vhdl/logging/test/tb_log_levels.vhd index 911945034..35e946b02 100644 --- a/vunit/vhdl/logging/test/tb_log_levels.vhd +++ b/vunit/vhdl/logging/test/tb_log_levels.vhd @@ -1,80 +1,80 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -use vunit_lib.run_pkg.all; - -use work.log_levels_pkg.all; -use work.core_pkg.all; -use work.test_support_pkg.all; -use work.checker_pkg.all; -use work.ansi_pkg.all; - -entity tb_log_levels is - generic ( - runner_cfg : string); -end entity; - -architecture a of tb_log_levels is -begin - main : process - variable level : log_level_t; - begin - - test_runner_setup(runner, runner_cfg); - - if run("Default levels have correct names") then - assert_equal(get_name(trace), "trace"); - assert_equal(get_name(debug), "debug"); - assert_equal(get_name(info), "info"); - assert_equal(get_name(warning), "warning"); - assert_equal(get_name(error), "error"); - assert_equal(get_name(failure), "failure"); - - assert_true(log_level_t'low < trace); - assert_true(trace < debug); - assert_true(debug < info); - assert_true(info < log_level_t'(warning)); - assert_true(log_level_t'(warning) < log_level_t'(error)); - assert_true(log_level_t'(error) < log_level_t'(failure)); - assert_true(log_level_t'(failure) < log_level_t'high); - - assert_true(is_valid(trace)); - assert_true(is_valid(debug)); - assert_true(is_valid(info)); - assert_true(is_valid(warning)); - assert_true(is_valid(error)); - assert_true(is_valid(failure)); - - elsif run("Can create level") then - level := new_log_level("my_level"); - assert_equal(get_name(level), "my_level"); - assert(get_color(level) = (fg => no_color, bg => no_color, style => normal)); - - level := new_log_level("my_level2", fg => red, bg => yellow, style => bright); - assert(get_color(level) = (fg => red, bg => yellow, style => bright)); - - elsif run("Can create max num custom levels") then - for i in 0 to max_num_custom_log_levels - 1 loop - level := new_log_level("my_level" & integer'image(i)); - assert_true(is_valid(level)); - assert_equal(get_name(level), "my_level" & integer'image(i)); - end loop; - - elsif run("Error on undefined level") then - mock_core_failure; - assert get_name(custom_level2) = "custom_level2"; - check_core_failure("Use of undefined level custom_level2."); - unmock_core_failure; - - elsif run("Max name length") then - level := new_log_level("a_long_log_level_name"); - assert_equal(max_level_length, 21); - end if; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +use vunit_lib.run_pkg.all; + +use work.log_levels_pkg.all; +use work.core_pkg.all; +use work.test_support_pkg.all; +use work.checker_pkg.all; +use work.ansi_pkg.all; + +entity tb_log_levels is + generic ( + runner_cfg : string); +end entity; + +architecture a of tb_log_levels is +begin + main : process + variable level : log_level_t; + begin + + test_runner_setup(runner, runner_cfg); + + if run("Default levels have correct names") then + assert_equal(get_name(trace), "trace"); + assert_equal(get_name(debug), "debug"); + assert_equal(get_name(info), "info"); + assert_equal(get_name(warning), "warning"); + assert_equal(get_name(error), "error"); + assert_equal(get_name(failure), "failure"); + + assert_true(log_level_t'low < trace); + assert_true(trace < debug); + assert_true(debug < info); + assert_true(info < log_level_t'(warning)); + assert_true(log_level_t'(warning) < log_level_t'(error)); + assert_true(log_level_t'(error) < log_level_t'(failure)); + assert_true(log_level_t'(failure) < log_level_t'high); + + assert_true(is_valid(trace)); + assert_true(is_valid(debug)); + assert_true(is_valid(info)); + assert_true(is_valid(warning)); + assert_true(is_valid(error)); + assert_true(is_valid(failure)); + + elsif run("Can create level") then + level := new_log_level("my_level"); + assert_equal(get_name(level), "my_level"); + assert(get_color(level) = (fg => no_color, bg => no_color, style => normal)); + + level := new_log_level("my_level2", fg => red, bg => yellow, style => bright); + assert(get_color(level) = (fg => red, bg => yellow, style => bright)); + + elsif run("Can create max num custom levels") then + for i in 0 to max_num_custom_log_levels - 1 loop + level := new_log_level("my_level" & integer'image(i)); + assert_true(is_valid(level)); + assert_equal(get_name(level), "my_level" & integer'image(i)); + end loop; + + elsif run("Error on undefined level") then + mock_core_failure; + assert get_name(custom_level2) = "custom_level2"; + check_core_failure("Use of undefined level custom_level2."); + unmock_core_failure; + + elsif run("Max name length") then + level := new_log_level("a_long_log_level_name"); + assert_equal(max_level_length, 21); + end if; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/vhdl/logging/test/test_support_pkg.vhd b/vunit/vhdl/logging/test/test_support_pkg.vhd index 6f27b01bd..2d26624cf 100644 --- a/vunit/vhdl/logging/test/test_support_pkg.vhd +++ b/vunit/vhdl/logging/test/test_support_pkg.vhd @@ -1,112 +1,112 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.log_handler_pkg.all; -use vunit_lib.core_pkg.all; - -package test_support_pkg is - procedure assert_true(value : boolean; msg : string := ""); - procedure assert_false(value : boolean; msg : string := ""); - procedure assert_equal(got, expected, msg : string := ""); - procedure assert_equal(got, expected : integer; msg : string := ""); - impure function get_display_handler(logger : logger_t) return log_handler_t; - impure function get_file_handler(logger : logger_t) return log_handler_t; - procedure check_stop_level(logger : logger_t; pass_level : log_level_t; stop_level : log_level_t); - procedure check_format(logger : logger_t; handler : log_handler_t; expected : deprecated_log_format_t); -end package; - -package body test_support_pkg is - impure function msg_suffix(msg : string) return string is - begin - if msg = "" then - return ""; - else - return " - " & msg; - end if; - end; - - procedure assert_true(value : boolean; msg : string := "") is - begin - assert value report "assert_true failed" & msg_suffix(msg) severity failure; - end; - - procedure assert_false(value : boolean; msg : string := "") is - begin - assert not value report "assert_false failed" & msg_suffix(msg) severity failure; - end; - - procedure assert_equal(got, expected, msg : string := "") is - begin - assert got = expected report "Got " & got & " expected " & expected & msg_suffix(msg) severity failure; - end; - - procedure assert_equal(got, expected : integer; msg : string := "") is - begin - assert_equal(integer'image(got), integer'image(expected), msg); - end; - - impure function get_display_handler(logger : logger_t) return log_handler_t is - begin - for idx in 0 to num_log_handlers(logger) - 1 loop - if get_file_name(get_log_handler(logger, idx)) = stdout_file_name then - return get_log_handler(logger, idx); - end if; - end loop; - - return null_log_handler; - end function; - - impure function get_file_handler(logger : logger_t) return log_handler_t is - begin - for idx in 0 to num_log_handlers(logger) - 1 loop - if get_file_name(get_log_handler(logger, idx)) /= stdout_file_name then - return get_log_handler(logger, idx); - end if; - end loop; - - return null_log_handler; - end function; - - procedure check_stop_level(logger : logger_t; pass_level : log_level_t; stop_level : log_level_t) is - begin - log(logger, "Hello world", pass_level); - mock_core_failure; - log(logger, "Hello world", stop_level); - check_and_unmock_core_failure; - reset_log_count(logger, stop_level); - reset_log_count(logger, pass_level); - end; - - procedure check_format(logger : logger_t; handler : log_handler_t; expected : deprecated_log_format_t) is - variable format : log_format_t; - variable use_color : boolean; - begin - get_format(handler, format, use_color); - - if get_file_name(handler) = stdout_file_name then - assert_true(use_color); - else - assert_true(not use_color); - end if; - - if expected = off then - for level in legal_log_level_t'low to legal_log_level_t'high loop - assert_false(is_visible(logger, handler, level), - "Level visible: " & log_level_t'image(level)); - end loop; - else - assert_true(format = expected); - for level in legal_log_level_t'low to legal_log_level_t'high loop - assert_true(is_visible(logger, handler, level), - "Level invisible: " & log_level_t'image(level)); - end loop; - end if; - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.log_handler_pkg.all; +use vunit_lib.core_pkg.all; + +package test_support_pkg is + procedure assert_true(value : boolean; msg : string := ""); + procedure assert_false(value : boolean; msg : string := ""); + procedure assert_equal(got, expected, msg : string := ""); + procedure assert_equal(got, expected : integer; msg : string := ""); + impure function get_display_handler(logger : logger_t) return log_handler_t; + impure function get_file_handler(logger : logger_t) return log_handler_t; + procedure check_stop_level(logger : logger_t; pass_level : log_level_t; stop_level : log_level_t); + procedure check_format(logger : logger_t; handler : log_handler_t; expected : deprecated_log_format_t); +end package; + +package body test_support_pkg is + impure function msg_suffix(msg : string) return string is + begin + if msg = "" then + return ""; + else + return " - " & msg; + end if; + end; + + procedure assert_true(value : boolean; msg : string := "") is + begin + assert value report "assert_true failed" & msg_suffix(msg) severity failure; + end; + + procedure assert_false(value : boolean; msg : string := "") is + begin + assert not value report "assert_false failed" & msg_suffix(msg) severity failure; + end; + + procedure assert_equal(got, expected, msg : string := "") is + begin + assert got = expected report "Got " & got & " expected " & expected & msg_suffix(msg) severity failure; + end; + + procedure assert_equal(got, expected : integer; msg : string := "") is + begin + assert_equal(integer'image(got), integer'image(expected), msg); + end; + + impure function get_display_handler(logger : logger_t) return log_handler_t is + begin + for idx in 0 to num_log_handlers(logger) - 1 loop + if get_file_name(get_log_handler(logger, idx)) = stdout_file_name then + return get_log_handler(logger, idx); + end if; + end loop; + + return null_log_handler; + end function; + + impure function get_file_handler(logger : logger_t) return log_handler_t is + begin + for idx in 0 to num_log_handlers(logger) - 1 loop + if get_file_name(get_log_handler(logger, idx)) /= stdout_file_name then + return get_log_handler(logger, idx); + end if; + end loop; + + return null_log_handler; + end function; + + procedure check_stop_level(logger : logger_t; pass_level : log_level_t; stop_level : log_level_t) is + begin + log(logger, "Hello world", pass_level); + mock_core_failure; + log(logger, "Hello world", stop_level); + check_and_unmock_core_failure; + reset_log_count(logger, stop_level); + reset_log_count(logger, pass_level); + end; + + procedure check_format(logger : logger_t; handler : log_handler_t; expected : deprecated_log_format_t) is + variable format : log_format_t; + variable use_color : boolean; + begin + get_format(handler, format, use_color); + + if get_file_name(handler) = stdout_file_name then + assert_true(use_color); + else + assert_true(not use_color); + end if; + + if expected = off then + for level in legal_log_level_t'low to legal_log_level_t'high loop + assert_false(is_visible(logger, handler, level), + "Level visible: " & log_level_t'image(level)); + end loop; + else + assert_true(format = expected); + for level in legal_log_level_t'low to legal_log_level_t'high loop + assert_true(is_visible(logger, handler, level), + "Level invisible: " & log_level_t'image(level)); + end loop; + end if; + end; + +end package body; diff --git a/vunit/vhdl/path/run.py b/vunit/vhdl/path/run.py index dc3678eec..50a13005a 100644 --- a/vunit/vhdl/path/run.py +++ b/vunit/vhdl/path/run.py @@ -1,14 +1,14 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -root = dirname(__file__) -ui = VUnit.from_argv() -lib = ui.add_library("lib") -lib.add_source_files(join(root, "test", "*.vhd")) -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +root = dirname(__file__) +ui = VUnit.from_argv() +lib = ui.add_library("lib") +lib.add_source_files(join(root, "test", "*.vhd")) +ui.main() diff --git a/vunit/vhdl/path/src/path.vhd b/vunit/vhdl/path/src/path.vhd index c9261d51b..258e95dc0 100644 --- a/vunit/vhdl/path/src/path.vhd +++ b/vunit/vhdl/path/src/path.vhd @@ -1,64 +1,64 @@ --- This package contains useful operation for manipulate path names --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.string_ops.all; -use std.textio.all; - -package path is - function join ( - constant p1 : string; - constant p2 : string := ""; - constant p3 : string := ""; - constant p4 : string := ""; - constant p5 : string := ""; - constant p6 : string := ""; - constant p7 : string := ""; - constant p8 : string := ""; - constant p9 : string := ""; - constant p10 : string := "") - return string; -end package; - -package body path is - function join ( - constant p1 : string; - constant p2 : string := ""; - constant p3 : string := ""; - constant p4 : string := ""; - constant p5 : string := ""; - constant p6 : string := ""; - constant p7 : string := ""; - constant p8 : string := ""; - constant p9 : string := ""; - constant p10 : string := "") - return string is - variable inputs : line_vector(1 to 10); - variable result : line; - begin - write(inputs(1), p1); - write(inputs(2), p2); - write(inputs(3), p3); - write(inputs(4), p4); - write(inputs(5), p5); - write(inputs(6), p6); - write(inputs(7), p7); - write(inputs(8), p8); - write(inputs(9), p9); - write(inputs(10), p10); - - write(result, string'("")); - for i in 1 to 10 loop - if rstrip(inputs(i).all, "/\") /= "" then - write(result, rstrip(inputs(i).all, "/\") & "/"); - end if; - deallocate(inputs(i)); - end loop; - - return rstrip(result.all, "/"); - end; -end package body; +-- This package contains useful operation for manipulate path names +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.string_ops.all; +use std.textio.all; + +package path is + function join ( + constant p1 : string; + constant p2 : string := ""; + constant p3 : string := ""; + constant p4 : string := ""; + constant p5 : string := ""; + constant p6 : string := ""; + constant p7 : string := ""; + constant p8 : string := ""; + constant p9 : string := ""; + constant p10 : string := "") + return string; +end package; + +package body path is + function join ( + constant p1 : string; + constant p2 : string := ""; + constant p3 : string := ""; + constant p4 : string := ""; + constant p5 : string := ""; + constant p6 : string := ""; + constant p7 : string := ""; + constant p8 : string := ""; + constant p9 : string := ""; + constant p10 : string := "") + return string is + variable inputs : line_vector(1 to 10); + variable result : line; + begin + write(inputs(1), p1); + write(inputs(2), p2); + write(inputs(3), p3); + write(inputs(4), p4); + write(inputs(5), p5); + write(inputs(6), p6); + write(inputs(7), p7); + write(inputs(8), p8); + write(inputs(9), p9); + write(inputs(10), p10); + + write(result, string'("")); + for i in 1 to 10 loop + if rstrip(inputs(i).all, "/\") /= "" then + write(result, rstrip(inputs(i).all, "/\") & "/"); + end if; + deallocate(inputs(i)); + end loop; + + return rstrip(result.all, "/"); + end; +end package body; diff --git a/vunit/vhdl/path/test/tb_path.vhd b/vunit/vhdl/path/test/tb_path.vhd index 9b61e385f..d1ec3fd76 100644 --- a/vunit/vhdl/path/test/tb_path.vhd +++ b/vunit/vhdl/path/test/tb_path.vhd @@ -1,41 +1,41 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -use vunit_lib.run_pkg.all; -use vunit_lib.run_types_pkg.all; -use vunit_lib.check_pkg.all; -use vunit_lib.path.all; - -entity tb_path is - generic ( - runner_cfg : string); -end entity tb_path; - -architecture test_fixture of tb_path is -begin - test_runner: process is - begin - test_runner_setup(runner, runner_cfg); - - while test_suite loop - if run("Test joining paths") then - check_equal(join(""), ""); - check_equal(join("", "p2"), "p2"); - check_equal(join("p1"), "p1"); - check_equal(join("p1", p5 => "p5", p10 => "p10"), "p1/p5/p10"); - check_equal(join("p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8", "p9", "p10"), - "p1/p2/p3/p4/p5/p6/p7/p8/p9/p10"); - elsif run("Verify that a separator ending a path component is ignored") then - check_equal(join("/p1/", "p2/", "p3", "/", "p4"), "/p1/p2/p3/p4"); - end if; - end loop; - - test_runner_cleanup(runner); - end process test_runner; - - test_runner_watchdog(runner, 1 us); -end test_fixture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +use vunit_lib.run_pkg.all; +use vunit_lib.run_types_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.path.all; + +entity tb_path is + generic ( + runner_cfg : string); +end entity tb_path; + +architecture test_fixture of tb_path is +begin + test_runner: process is + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test joining paths") then + check_equal(join(""), ""); + check_equal(join("", "p2"), "p2"); + check_equal(join("p1"), "p1"); + check_equal(join("p1", p5 => "p5", p10 => "p10"), "p1/p5/p10"); + check_equal(join("p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8", "p9", "p10"), + "p1/p2/p3/p4/p5/p6/p7/p8/p9/p10"); + elsif run("Verify that a separator ending a path component is ignored") then + check_equal(join("/p1/", "p2/", "p3", "/", "p4"), "/p1/p2/p3/p4"); + end if; + end loop; + + test_runner_cleanup(runner); + end process test_runner; + + test_runner_watchdog(runner, 1 us); +end test_fixture; diff --git a/vunit/vhdl/random/run.py b/vunit/vhdl/random/run.py index 2f0d4bbf7..d4c0d1c12 100644 --- a/vunit/vhdl/random/run.py +++ b/vunit/vhdl/random/run.py @@ -1,16 +1,16 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -root = dirname(__file__) - -ui = VUnit.from_argv() -ui.add_random() -lib = ui.library("vunit_lib") -lib.add_source_files(join(root, "test", "*.vhd")) -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +root = dirname(__file__) + +ui = VUnit.from_argv() +ui.add_random() +lib = ui.library("vunit_lib") +lib.add_source_files(join(root, "test", "*.vhd")) +ui.main() diff --git a/vunit/vhdl/random/src/random_pkg.vhd b/vunit/vhdl/random/src/random_pkg.vhd index 29d8ee01c..f3660d893 100644 --- a/vunit/vhdl/random/src/random_pkg.vhd +++ b/vunit/vhdl/random/src/random_pkg.vhd @@ -1,199 +1,199 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- Optional package that includes random functions for the data_types based on --- OSVVM - -library vunit_lib; -context vunit_lib.vunit_context; -use vunit_lib.integer_vector_ptr_pkg.all; -use vunit_lib.integer_array_pkg.all; - -library osvvm; -use osvvm.RandomPkg.all; - -package random_pkg is - procedure random_integer_vector_ptr(variable rnd : inout RandomPType; - variable integer_vector_ptr : inout integer_vector_ptr_t; - length : natural; - min_value : integer; - max_value : integer); - - impure function random_integer_vector_ptr(length : natural; - min_value : integer; - max_value : integer) return integer_vector_ptr_t; - - procedure random_integer_vector_ptr(variable rnd : inout RandomPType; - variable integer_vector_ptr : inout integer_vector_ptr_t; - length : natural; - bits_per_word : positive; - is_signed : boolean); - - impure function random_integer_vector_ptr(length : natural; - bits_per_word : positive; - is_signed : boolean) return integer_vector_ptr_t; - - procedure random_integer_array(variable rnd : inout RandomPType; - variable integer_array : inout integer_array_t; - width : natural := 1; - height : natural := 1; - depth : natural := 1; - min_value : integer := 0; - max_value : integer := 0); - - procedure random_integer_array(variable rnd : inout RandomPType; - variable integer_array : inout integer_array_t; - width : natural := 1; - height : natural := 1; - depth : natural := 1; - bits_per_word : integer := 1; - is_signed : boolean := false); - - impure function random_integer_array(width : natural := 1; - height : natural := 1; - depth : natural := 1; - min_value : integer := 0; - max_value : integer := 0) return integer_array_t; - - impure function random_integer_array(width : natural := 1; - height : natural := 1; - depth : natural := 1; - bits_per_word : integer := 1; - is_signed : boolean := false) return integer_array_t; -end package; - -package body random_pkg is - shared variable random_ptype : RandomPType; - - procedure random_integer_vector_ptr(variable rnd : inout RandomPType; - variable integer_vector_ptr : inout integer_vector_ptr_t; - length : natural; - min_value : integer; - max_value : integer) is - begin - if integer_vector_ptr = null_ptr then - integer_vector_ptr := new_integer_vector_ptr(length); - else - reallocate(integer_vector_ptr, length); - end if; - for i in 0 to length-1 loop - set(integer_vector_ptr, i, rnd.RandInt(min_value, max_value)); - end loop; - end procedure; - - impure function random_integer_vector_ptr(length : natural; - min_value : integer; - max_value : integer) return integer_vector_ptr_t is - variable integer_vector_ptr : integer_vector_ptr_t; - begin - random_integer_vector_ptr(random_ptype, integer_vector_ptr, length, min_value, max_value); - return integer_vector_ptr; - end function; - - procedure random_integer_vector_ptr(variable rnd : inout RandomPType; - variable integer_vector_ptr : inout integer_vector_ptr_t; - length : natural; - bits_per_word : positive; - is_signed : boolean) is - begin - if is_signed then - random_integer_vector_ptr(rnd, integer_vector_ptr, length, -(2**(bits_per_word-1)), 2**(bits_per_word-1)-1); - else - random_integer_vector_ptr(rnd, integer_vector_ptr, length, 0, 2**bits_per_word-1); - end if; - end; - - impure function random_integer_vector_ptr(length : natural; - bits_per_word : positive; - is_signed : boolean) return integer_vector_ptr_t is - variable integer_vector_ptr : integer_vector_ptr_t; - begin - random_integer_vector_ptr(random_ptype, integer_vector_ptr, length, bits_per_word, is_signed); - return integer_vector_ptr; - end; - - function ilog2(value : natural) return natural is - variable res : natural := 0; - variable pow : natural := 1; - begin - while pow < value loop - pow := pow*2; - res := res + 1; - end loop; - return res; - end function; - - procedure random_integer_array(variable rnd : inout RandomPType; - variable integer_array : inout integer_array_t; - width : natural := 1; - height : natural := 1; - depth : natural := 1; - min_value : integer := 0; - max_value : integer := 0) is - variable bit_width : integer; - variable is_signed : boolean; - begin - assert min_value <= max_value report "min_value must be lower or equal to max_value"; - is_signed := min_value < 0; - - if is_signed then - bit_width := ilog2(abs min_value) + 1; - else - bit_width := ilog2(max_value+1); - end if; - - deallocate(integer_array); - integer_array := new_3d(width => width, height => height, depth => depth, - bit_width => bit_width, is_signed => is_signed); - - for i in 0 to integer_array.length-1 loop - set(integer_array, i, rnd.RandInt(min_value, max_value)); - end loop; - end procedure; - - procedure random_integer_array(variable rnd : inout RandomPType; - variable integer_array : inout integer_array_t; - width : natural := 1; - height : natural := 1; - depth : natural := 1; - bits_per_word : integer := 1; - is_signed : boolean := false) is - variable min_value, max_value : integer; - begin - if is_signed then - min_value := -2**(bits_per_word-1); - max_value := 2**(bits_per_word-1)-1; - else - min_value := 0; - max_value := 2**bits_per_word-1; - end if; - random_integer_array(rnd, integer_array, width, height, depth, - min_value => min_value, max_value => max_value); - end procedure; - - impure function random_integer_array(width : natural := 1; - height : natural := 1; - depth : natural := 1; - min_value : integer := 0; - max_value : integer := 0) return integer_array_t is - variable integer_array : integer_array_t; - begin - random_integer_array(random_ptype, integer_array, width, height, depth, min_value, max_value); - return integer_array; - end; - - impure function random_integer_array(width : natural := 1; - height : natural := 1; - depth : natural := 1; - bits_per_word : integer := 1; - is_signed : boolean := false) return integer_array_t is - variable integer_array : integer_array_t; - begin - random_integer_array(random_ptype, integer_array, width, height, depth, bits_per_word, is_signed); - return integer_array; - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- Optional package that includes random functions for the data_types based on +-- OSVVM + +library vunit_lib; +context vunit_lib.vunit_context; +use vunit_lib.integer_vector_ptr_pkg.all; +use vunit_lib.integer_array_pkg.all; + +library osvvm; +use osvvm.RandomPkg.all; + +package random_pkg is + procedure random_integer_vector_ptr(variable rnd : inout RandomPType; + variable integer_vector_ptr : inout integer_vector_ptr_t; + length : natural; + min_value : integer; + max_value : integer); + + impure function random_integer_vector_ptr(length : natural; + min_value : integer; + max_value : integer) return integer_vector_ptr_t; + + procedure random_integer_vector_ptr(variable rnd : inout RandomPType; + variable integer_vector_ptr : inout integer_vector_ptr_t; + length : natural; + bits_per_word : positive; + is_signed : boolean); + + impure function random_integer_vector_ptr(length : natural; + bits_per_word : positive; + is_signed : boolean) return integer_vector_ptr_t; + + procedure random_integer_array(variable rnd : inout RandomPType; + variable integer_array : inout integer_array_t; + width : natural := 1; + height : natural := 1; + depth : natural := 1; + min_value : integer := 0; + max_value : integer := 0); + + procedure random_integer_array(variable rnd : inout RandomPType; + variable integer_array : inout integer_array_t; + width : natural := 1; + height : natural := 1; + depth : natural := 1; + bits_per_word : integer := 1; + is_signed : boolean := false); + + impure function random_integer_array(width : natural := 1; + height : natural := 1; + depth : natural := 1; + min_value : integer := 0; + max_value : integer := 0) return integer_array_t; + + impure function random_integer_array(width : natural := 1; + height : natural := 1; + depth : natural := 1; + bits_per_word : integer := 1; + is_signed : boolean := false) return integer_array_t; +end package; + +package body random_pkg is + shared variable random_ptype : RandomPType; + + procedure random_integer_vector_ptr(variable rnd : inout RandomPType; + variable integer_vector_ptr : inout integer_vector_ptr_t; + length : natural; + min_value : integer; + max_value : integer) is + begin + if integer_vector_ptr = null_ptr then + integer_vector_ptr := new_integer_vector_ptr(length); + else + reallocate(integer_vector_ptr, length); + end if; + for i in 0 to length-1 loop + set(integer_vector_ptr, i, rnd.RandInt(min_value, max_value)); + end loop; + end procedure; + + impure function random_integer_vector_ptr(length : natural; + min_value : integer; + max_value : integer) return integer_vector_ptr_t is + variable integer_vector_ptr : integer_vector_ptr_t; + begin + random_integer_vector_ptr(random_ptype, integer_vector_ptr, length, min_value, max_value); + return integer_vector_ptr; + end function; + + procedure random_integer_vector_ptr(variable rnd : inout RandomPType; + variable integer_vector_ptr : inout integer_vector_ptr_t; + length : natural; + bits_per_word : positive; + is_signed : boolean) is + begin + if is_signed then + random_integer_vector_ptr(rnd, integer_vector_ptr, length, -(2**(bits_per_word-1)), 2**(bits_per_word-1)-1); + else + random_integer_vector_ptr(rnd, integer_vector_ptr, length, 0, 2**bits_per_word-1); + end if; + end; + + impure function random_integer_vector_ptr(length : natural; + bits_per_word : positive; + is_signed : boolean) return integer_vector_ptr_t is + variable integer_vector_ptr : integer_vector_ptr_t; + begin + random_integer_vector_ptr(random_ptype, integer_vector_ptr, length, bits_per_word, is_signed); + return integer_vector_ptr; + end; + + function ilog2(value : natural) return natural is + variable res : natural := 0; + variable pow : natural := 1; + begin + while pow < value loop + pow := pow*2; + res := res + 1; + end loop; + return res; + end function; + + procedure random_integer_array(variable rnd : inout RandomPType; + variable integer_array : inout integer_array_t; + width : natural := 1; + height : natural := 1; + depth : natural := 1; + min_value : integer := 0; + max_value : integer := 0) is + variable bit_width : integer; + variable is_signed : boolean; + begin + assert min_value <= max_value report "min_value must be lower or equal to max_value"; + is_signed := min_value < 0; + + if is_signed then + bit_width := ilog2(abs min_value) + 1; + else + bit_width := ilog2(max_value+1); + end if; + + deallocate(integer_array); + integer_array := new_3d(width => width, height => height, depth => depth, + bit_width => bit_width, is_signed => is_signed); + + for i in 0 to integer_array.length-1 loop + set(integer_array, i, rnd.RandInt(min_value, max_value)); + end loop; + end procedure; + + procedure random_integer_array(variable rnd : inout RandomPType; + variable integer_array : inout integer_array_t; + width : natural := 1; + height : natural := 1; + depth : natural := 1; + bits_per_word : integer := 1; + is_signed : boolean := false) is + variable min_value, max_value : integer; + begin + if is_signed then + min_value := -2**(bits_per_word-1); + max_value := 2**(bits_per_word-1)-1; + else + min_value := 0; + max_value := 2**bits_per_word-1; + end if; + random_integer_array(rnd, integer_array, width, height, depth, + min_value => min_value, max_value => max_value); + end procedure; + + impure function random_integer_array(width : natural := 1; + height : natural := 1; + depth : natural := 1; + min_value : integer := 0; + max_value : integer := 0) return integer_array_t is + variable integer_array : integer_array_t; + begin + random_integer_array(random_ptype, integer_array, width, height, depth, min_value, max_value); + return integer_array; + end; + + impure function random_integer_array(width : natural := 1; + height : natural := 1; + depth : natural := 1; + bits_per_word : integer := 1; + is_signed : boolean := false) return integer_array_t is + variable integer_array : integer_array_t; + begin + random_integer_array(random_ptype, integer_array, width, height, depth, bits_per_word, is_signed); + return integer_array; + end; + +end package body; diff --git a/vunit/vhdl/random/test/tb_random_pkg.vhd b/vunit/vhdl/random/test/tb_random_pkg.vhd index 7cc7786c0..f38c0fdbe 100644 --- a/vunit/vhdl/random/test/tb_random_pkg.vhd +++ b/vunit/vhdl/random/test/tb_random_pkg.vhd @@ -1,102 +1,102 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -context vunit_lib.vunit_context; -use vunit_lib.integer_vector_ptr_pkg.all; -use vunit_lib.integer_array_pkg.all; -use vunit_lib.random_pkg.all; - -library osvvm; -use osvvm.RandomPkg.all; - -entity tb_random is - generic (runner_cfg : string); -end entity; - -architecture a of tb_random is -begin - main : process - variable rnd : RandomPType; - variable integer_vector_ptr, tmp : integer_vector_ptr_t; - variable integer_array : integer_array_t; - begin - test_runner_setup(runner, runner_cfg); - - if run("Test random integer_vector_ptr min max") then - integer_vector_ptr := random_integer_vector_ptr(256, 10, 14); - check_equal(length(integer_vector_ptr), 256); - for i in 0 to length(integer_vector_ptr)-1 loop - assert get(integer_vector_ptr, i) <= 14; - assert get(integer_vector_ptr, i) >= 10; - end loop; - - tmp := integer_vector_ptr; - random_integer_vector_ptr(rnd, integer_vector_ptr, 13, 2, 4); - check(tmp = integer_vector_ptr, "Reused pointer"); - check_equal(length(integer_vector_ptr), 13); - for i in 0 to length(integer_vector_ptr)-1 loop - assert get(integer_vector_ptr, i) <= 4; - assert get(integer_vector_ptr, i) >= 2; - end loop; - deallocate(integer_vector_ptr); - - elsif run("Test random integer_vector_ptr bits_per_word is_signed") then - integer_vector_ptr := random_integer_vector_ptr(100, bits_per_word => 10, is_signed => true); - check_equal(length(integer_vector_ptr), 100); - for i in 0 to length(integer_vector_ptr)-1 loop - assert get(integer_vector_ptr, i) < 2**9; - assert get(integer_vector_ptr, i) >= -2**9; - end loop; - - random_integer_vector_ptr(rnd, integer_vector_ptr, 100, bits_per_word => 4, is_signed => false); - check_equal(length(integer_vector_ptr), 100); - for i in 0 to length(integer_vector_ptr)-1 loop - assert get(integer_vector_ptr, i) < 2**4; - assert get(integer_vector_ptr, i) >= 0; - end loop; - - elsif run("Test random integer_array") then - integer_array := random_integer_array(256, min_value => 10, max_value => 14); - check_equal(integer_array.length, 256); - for i in 0 to integer_array.length-1 loop - assert get(integer_array, i) <= 14; - assert get(integer_array, i) >= 10; - end loop; - - random_integer_array(rnd, integer_array, 13, min_value => 2, max_value => 4); - check_equal(integer_array.width, 13); - check_equal(integer_array.height, 1); - check_equal(integer_array.depth, 1); - check_equal(integer_array.bit_width, 3); - check_equal(integer_array.is_signed, false); - - for i in 0 to integer_array.length-1 loop - assert get(integer_array, i) <= 4; - assert get(integer_array, i) >= 2; - end loop; - - random_integer_array(rnd, integer_array, 13, min_value => -2, max_value => 1); - check_equal(integer_array.bit_width, 2); - check_equal(integer_array.is_signed, true); - - random_integer_array(rnd, integer_array, 13, min_value => -4, max_value => -1); - check_equal(integer_array.bit_width, 3); - check_equal(integer_array.is_signed, true); - - random_integer_array(rnd, integer_array, 13, min_value => 0, max_value => 3); - check_equal(integer_array.bit_width, 2); - check_equal(integer_array.is_signed, false); - - random_integer_array(rnd, integer_array, 13, bits_per_word => 13, is_signed => false); - check_equal(integer_array.bit_width, 13); - check_equal(integer_array.is_signed, false); - - end if; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +use vunit_lib.integer_vector_ptr_pkg.all; +use vunit_lib.integer_array_pkg.all; +use vunit_lib.random_pkg.all; + +library osvvm; +use osvvm.RandomPkg.all; + +entity tb_random is + generic (runner_cfg : string); +end entity; + +architecture a of tb_random is +begin + main : process + variable rnd : RandomPType; + variable integer_vector_ptr, tmp : integer_vector_ptr_t; + variable integer_array : integer_array_t; + begin + test_runner_setup(runner, runner_cfg); + + if run("Test random integer_vector_ptr min max") then + integer_vector_ptr := random_integer_vector_ptr(256, 10, 14); + check_equal(length(integer_vector_ptr), 256); + for i in 0 to length(integer_vector_ptr)-1 loop + assert get(integer_vector_ptr, i) <= 14; + assert get(integer_vector_ptr, i) >= 10; + end loop; + + tmp := integer_vector_ptr; + random_integer_vector_ptr(rnd, integer_vector_ptr, 13, 2, 4); + check(tmp = integer_vector_ptr, "Reused pointer"); + check_equal(length(integer_vector_ptr), 13); + for i in 0 to length(integer_vector_ptr)-1 loop + assert get(integer_vector_ptr, i) <= 4; + assert get(integer_vector_ptr, i) >= 2; + end loop; + deallocate(integer_vector_ptr); + + elsif run("Test random integer_vector_ptr bits_per_word is_signed") then + integer_vector_ptr := random_integer_vector_ptr(100, bits_per_word => 10, is_signed => true); + check_equal(length(integer_vector_ptr), 100); + for i in 0 to length(integer_vector_ptr)-1 loop + assert get(integer_vector_ptr, i) < 2**9; + assert get(integer_vector_ptr, i) >= -2**9; + end loop; + + random_integer_vector_ptr(rnd, integer_vector_ptr, 100, bits_per_word => 4, is_signed => false); + check_equal(length(integer_vector_ptr), 100); + for i in 0 to length(integer_vector_ptr)-1 loop + assert get(integer_vector_ptr, i) < 2**4; + assert get(integer_vector_ptr, i) >= 0; + end loop; + + elsif run("Test random integer_array") then + integer_array := random_integer_array(256, min_value => 10, max_value => 14); + check_equal(integer_array.length, 256); + for i in 0 to integer_array.length-1 loop + assert get(integer_array, i) <= 14; + assert get(integer_array, i) >= 10; + end loop; + + random_integer_array(rnd, integer_array, 13, min_value => 2, max_value => 4); + check_equal(integer_array.width, 13); + check_equal(integer_array.height, 1); + check_equal(integer_array.depth, 1); + check_equal(integer_array.bit_width, 3); + check_equal(integer_array.is_signed, false); + + for i in 0 to integer_array.length-1 loop + assert get(integer_array, i) <= 4; + assert get(integer_array, i) >= 2; + end loop; + + random_integer_array(rnd, integer_array, 13, min_value => -2, max_value => 1); + check_equal(integer_array.bit_width, 2); + check_equal(integer_array.is_signed, true); + + random_integer_array(rnd, integer_array, 13, min_value => -4, max_value => -1); + check_equal(integer_array.bit_width, 3); + check_equal(integer_array.is_signed, true); + + random_integer_array(rnd, integer_array, 13, min_value => 0, max_value => 3); + check_equal(integer_array.bit_width, 2); + check_equal(integer_array.is_signed, false); + + random_integer_array(rnd, integer_array, 13, bits_per_word => 13, is_signed => false); + check_equal(integer_array.bit_width, 13); + check_equal(integer_array.is_signed, false); + + end if; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/vhdl/run/run.py b/vunit/vhdl/run/run.py index 77e409121..348c8d7e8 100644 --- a/vunit/vhdl/run/run.py +++ b/vunit/vhdl/run/run.py @@ -1,15 +1,15 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -root = dirname(__file__) -ui = VUnit.from_argv() - -lib = ui.add_library("tb_run_lib") -lib.add_source_files(join(root, 'test', '*.vhd')) -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +root = dirname(__file__) +ui = VUnit.from_argv() + +lib = ui.add_library("tb_run_lib") +lib.add_source_files(join(root, 'test', '*.vhd')) +ui.main() diff --git a/vunit/vhdl/run/src/run.vhd b/vunit/vhdl/run/src/run.vhd index f7f802f54..1892d1932 100644 --- a/vunit/vhdl/run/src/run.vhd +++ b/vunit/vhdl/run/src/run.vhd @@ -1,487 +1,487 @@ --- Run package provides test runner functionality to VHDL 2002+ testbenches --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.logger_pkg.all; -use work.log_levels_pkg.all; -use work.log_handler_pkg.all; -use work.ansi_pkg.enable_colors; -use work.string_ops.all; -use work.dictionary.all; -use work.path.all; -use work.core_pkg; -use std.textio.all; - -package body run_pkg is - procedure notify(signal runner : inout runner_sync_t; - idx : natural := runner_event_idx) is - begin - if runner(idx) /= runner_event then - runner(idx) <= runner_event; - wait until runner(idx) = runner_event; - runner(idx) <= idle_runner; - end if; - end procedure notify; - - procedure test_runner_setup ( - signal runner : inout runner_sync_t; - constant runner_cfg : in string := runner_cfg_default) is - variable test_case_candidates : lines_t; - variable selected_enabled_test_cases : line; - begin - - -- fake active python runner key is only used during testing in tb_run.vhd - -- to avoid creating vunit_results file - set_active_python_runner(runner_state, - (active_python_runner(runner_cfg) and not has_key(runner_cfg, "fake active python runner"))); - - if has_active_python_runner(runner_state) then - core_pkg.setup(output_path(runner_cfg) & "vunit_results"); - end if; - - - if has_active_python_runner(runner_state) then - hide(runner_trace_logger, display_handler, info); - end if; - - if has_key(runner_cfg, "use_color") and boolean'value(get(runner_cfg, "use_color")) then - enable_colors; - end if; - - if not active_python_runner(runner_cfg) then - set_stop_level(failure); - end if; - - set_phase(runner_state, test_runner_setup); - runner(runner_exit_status_idx) <= runner_exit_with_errors; - notify(runner); - - trace(runner_trace_logger, "Entering test runner setup phase."); - entry_gate(runner); - - if selected_enabled_test_cases /= null then - deallocate(selected_enabled_test_cases); - end if; - - if has_key(runner_cfg, "enabled_test_cases") then - write(selected_enabled_test_cases, get(runner_cfg, "enabled_test_cases")); - else - write(selected_enabled_test_cases, string'("__all__")); - end if; - test_case_candidates := split(replace(selected_enabled_test_cases.all, ",,", "__comma__"), ","); - - set_cfg(runner_state, runner_cfg); - - set_run_all(runner_state, strip(test_case_candidates(0).all) = "__all__"); - if get_run_all(runner_state) then - set_num_of_test_cases(runner_state, unknown_num_of_test_cases_c); - else - set_num_of_test_cases(runner_state, 0); - for i in 1 to test_case_candidates'length loop - if strip(test_case_candidates(i - 1).all) /= "" then - inc_num_of_test_cases(runner_state); - - set_test_case_name(runner_state, - get_num_of_test_cases(runner_state), - replace(strip(test_case_candidates(i - 1).all), "__comma__", ",")); - end if; - end loop; - end if; - exit_gate(runner); - set_phase(runner_state, test_suite_setup); - notify(runner); - trace(runner_trace_logger, "Entering test suite setup phase."); - entry_gate(runner); - end test_runner_setup; - - procedure test_runner_cleanup ( - signal runner: inout runner_sync_t; - external_failure : boolean := false; - allow_disabled_errors : boolean := false; - allow_disabled_failures : boolean := false; - fail_on_warning : boolean := false) is - begin - failure_if(runner_trace_logger, external_failure, "External failure."); - - set_phase(runner_state, test_runner_cleanup); - notify(runner); - trace(runner_trace_logger, "Entering test runner cleanup phase."); - entry_gate(runner); - exit_gate(runner); - set_phase(runner_state, test_runner_exit); - notify(runner); - trace(runner_trace_logger, "Entering test runner exit phase."); - - if not final_log_check(allow_disabled_errors => allow_disabled_errors, - allow_disabled_failures => allow_disabled_failures, - fail_on_warning => fail_on_warning) then - return; - end if; - - runner(runner_exit_status_idx) <= runner_exit_without_errors; - notify(runner); - - if has_active_python_runner(runner_state) then - core_pkg.test_suite_done; - end if; - - if not p_simulation_exit_is_disabled(runner_state) then - core_pkg.stop(0); - end if; - - end procedure test_runner_cleanup; - - impure function num_of_enabled_test_cases - return integer is - begin - return get_num_of_test_cases(runner_state); - end; - - impure function enabled ( - constant name : string) - return boolean is - variable i : natural := 1; - begin - if get_run_all(runner_state) then - return true; - end if; - - for i in 1 to get_num_of_test_cases(runner_state) loop - if get_test_case_name(runner_state, i) = name then - return true; - end if; - end loop; - - return false; - end; - - impure function test_suite - return boolean is - variable ret_val : boolean; - begin - init_test_case_iteration(runner_state); - - if get_test_suite_completed(runner_state) then - ret_val := false; - elsif get_run_all(runner_state) then - ret_val := get_has_run_since_last_loop_check(runner_state); - else - if get_test_suite_iteration(runner_state) > 0 then - inc_active_test_case_index(runner_state); - end if; - - ret_val := get_active_test_case_index(runner_state) <= get_num_of_test_cases(runner_state); - end if; - - clear_has_run_since_last_loop_check(runner_state); - - if ret_val then - inc_test_suite_iteration(runner_state); - set_phase(runner_state, test_case_setup); - trace(runner_trace_logger, "Entering test case setup phase."); - else - set_test_suite_completed(runner_state); - set_phase(runner_state, test_suite_cleanup); - trace(runner_trace_logger, "Entering test suite cleanup phase."); - end if; - - return ret_val; - end; - - impure function test_case - return boolean is - begin - if get_test_case_iteration(runner_state) = 0 then - set_phase(runner_state, test_case); - trace(runner_trace_logger, "Entering test case phase."); - inc_test_case_iteration(runner_state); - clear_test_case_exit_after_error(runner_state); - clear_test_suite_exit_after_error(runner_state); - set_running_test_case(runner_state, ""); - return true; - else - set_phase(runner_state, test_case_cleanup); - trace(runner_trace_logger, "Entering test case cleanup phase."); - return false; - end if; - end function test_case; - - impure function run ( - constant name : string) - return boolean is - - impure function has_run ( - constant name : string) - return boolean is - begin - for i in 1 to get_num_of_run_test_cases(runner_state) loop - if get_run_test_case(runner_state, i) = name then - return true; - end if; - end loop; - return false; - end function has_run; - - procedure register_run ( - constant name : in string) is - begin - inc_num_of_run_test_cases(runner_state); - set_has_run_since_last_loop_check(runner_state); - set_run_test_case(runner_state, get_num_of_run_test_cases(runner_state), name); - end procedure register_run; - - begin - if get_test_suite_completed(runner_state) then - set_running_test_case(runner_state, ""); - return false; - elsif get_run_all(runner_state) then - if not has_run(name) then - register_run(name); - info(runner_trace_logger, "Test case: " & name); - if has_active_python_runner(runner_state) then - core_pkg.test_start(name); - end if; - set_running_test_case(runner_state, name); - return true; - end if; - elsif get_test_case_name(runner_state, get_active_test_case_index(runner_state)) = name then - info(runner_trace_logger, "Test case: " & name); - if has_active_python_runner(runner_state) then - core_pkg.test_start(name); - end if; - set_running_test_case(runner_state, name); - return true; - end if; - - set_running_test_case(runner_state, ""); - return false; - end; - - impure function active_test_case - return string is - begin - if get_run_all(runner_state) then - return ""; - end if; - return get_test_case_name(runner_state, get_active_test_case_index(runner_state)); - end; - - impure function running_test_case - return string is - begin - return get_running_test_case(runner_state); - end; - - procedure set_timeout(signal runner : inout runner_sync_t; - constant timeout : in time) is - begin - set_timeout(runner_state, timeout); - notify(runner, runner_timeout_update_idx); - end; - - procedure test_runner_watchdog ( - signal runner : inout runner_sync_t; - constant timeout : in time; - constant do_runner_cleanup : boolean := true) is - - variable current_timeout : time := timeout; - begin - - loop - wait until (runner(runner_exit_status_idx) = runner_exit_without_errors or - runner(runner_timeout_update_idx) = runner_event) for current_timeout; - - if runner(runner_timeout_update_idx) = runner_event then - debug(runner_trace_logger, "Update watchdog timeout " & time'image(current_timeout) & "."); - current_timeout := get_timeout(runner_state); - else - exit; - end if; - end loop; - - if not (runner(runner_exit_status_idx) = runner_exit_without_errors) then - error(runner_trace_logger, "Test runner timeout after " & time'image(current_timeout) & "."); - if do_runner_cleanup then - test_runner_cleanup(runner); - end if; - end if; - end; - - impure function test_suite_error ( - constant err : boolean) - return boolean is - begin - if err then - set_test_suite_completed(runner_state); - set_phase(runner_state, test_case_cleanup); - trace(runner_trace_logger, "Entering test case cleanup phase."); - set_test_suite_exit_after_error(runner_state); - end if; - - return err; - end function test_suite_error; - - impure function test_case_error ( - constant err : boolean) - return boolean is - begin - if err then - set_phase(runner_state, test_case_cleanup); - trace(runner_trace_logger, "Entering test case cleanup phase."); - set_test_case_exit_after_error(runner_state); - end if; - - return err; - end function test_case_error; - - impure function test_suite_exit - return boolean is - begin - return get_test_suite_exit_after_error(runner_state); - end function test_suite_exit; - - impure function test_case_exit - return boolean is - begin - return get_test_case_exit_after_error(runner_state); - end function test_case_exit; - - impure function test_exit - return boolean is - begin - return test_suite_exit or test_case_exit; - end function test_exit; - - procedure lock_entry ( - signal runner : inout runner_sync_t; - constant phase : in runner_legal_phase_t; - constant me : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - lock_entry(runner_state, phase); - log(runner_trace_logger, "Locked " & replace(runner_phase_t'image(phase), "_", " ") & " phase entry gate.", trace, line_num, file_name); - notify(runner); - end; - - procedure unlock_entry ( - signal runner : inout runner_sync_t; - constant phase : in runner_legal_phase_t; - constant me : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - unlock_entry(runner_state, phase); - log(runner_trace_logger, "Unlocked " & replace(runner_phase_t'image(phase), "_", " ") & " phase entry gate.", trace, line_num, file_name); - notify(runner); - end; - - procedure lock_exit ( - signal runner : inout runner_sync_t; - constant phase : in runner_legal_phase_t; - constant me : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - lock_exit(runner_state, phase); - log(runner_trace_logger, "Locked " & replace(runner_phase_t'image(phase), "_", " ") & " phase exit gate.", trace, line_num, file_name); - notify(runner); - end; - - procedure unlock_exit ( - signal runner : inout runner_sync_t; - constant phase : in runner_legal_phase_t; - constant me : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - unlock_exit(runner_state, phase); - log(runner_trace_logger, "Unlocked " & replace(runner_phase_t'image(phase), "_", " ") & " phase exit gate.", trace, line_num, file_name); - notify(runner); - end; - - procedure wait_until ( - signal runner : in runner_sync_t; - constant phase : in runner_legal_phase_t; - constant me : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := "") is - begin - if get_phase(runner_state) /= phase then - log(runner_trace_logger, "Waiting for phase = " & replace(runner_phase_t'image(phase), "_", " ") & ".", trace, line_num, file_name); - wait on runner until get_phase(runner_state) = phase; - log(runner_trace_logger, "Waking up. Phase is " & replace(runner_phase_t'image(phase), "_", " ") & ".", trace, line_num, file_name); - end if; - end; - - procedure entry_gate ( - signal runner : inout runner_sync_t) is - begin - if entry_is_locked(runner_state, get_phase(runner_state)) then - trace(runner_trace_logger, "Halting on " & replace(runner_phase_t'image(get_phase(runner_state)), "_", " ") & " phase entry gate."); - wait on runner until not entry_is_locked(runner_state, get_phase(runner_state)) for max_locked_time; - end if; - notify(runner); - trace(runner_trace_logger, "Passed " & replace(runner_phase_t'image(get_phase(runner_state)), "_", " ") & " phase entry gate."); - end procedure entry_gate; - - procedure exit_gate ( - signal runner : in runner_sync_t) is - begin - if exit_is_locked(runner_state, get_phase(runner_state)) then - trace(runner_trace_logger, "Halting on " & replace(runner_phase_t'image(get_phase(runner_state)), "_", " ") & " phase exit gate."); - wait on runner until not exit_is_locked(runner_state, get_phase(runner_state)) for max_locked_time; - end if; - trace(runner_trace_logger, "Passed " & replace(runner_phase_t'image(get_phase(runner_state)), "_", " ") & " phase exit gate."); - end procedure exit_gate; - - impure function active_python_runner ( - constant runner_cfg : string) - return boolean is - begin - if has_key(runner_cfg, "active python runner") then - return get(runner_cfg, "active python runner") = "true"; - else - return false; - end if; - end; - - impure function output_path ( - constant runner_cfg : string) - return string is - begin - if has_key(runner_cfg, "output path") then - return get(runner_cfg, "output path"); - else - return ""; - end if; - end; - - impure function enabled_test_cases ( - constant runner_cfg : string) - return test_cases_t is - begin - if has_key(runner_cfg, "enabled_test_cases") then - return get(runner_cfg, "enabled_test_cases"); - else - return "__all__"; - end if; - end; - - impure function tb_path ( - constant runner_cfg : string) - return string is - begin - if has_key(runner_cfg, "tb path") then - return get(runner_cfg, "tb path"); - else - return ""; - end if; - end; - - -end package body run_pkg; +-- Run package provides test runner functionality to VHDL 2002+ testbenches +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.logger_pkg.all; +use work.log_levels_pkg.all; +use work.log_handler_pkg.all; +use work.ansi_pkg.enable_colors; +use work.string_ops.all; +use work.dictionary.all; +use work.path.all; +use work.core_pkg; +use std.textio.all; + +package body run_pkg is + procedure notify(signal runner : inout runner_sync_t; + idx : natural := runner_event_idx) is + begin + if runner(idx) /= runner_event then + runner(idx) <= runner_event; + wait until runner(idx) = runner_event; + runner(idx) <= idle_runner; + end if; + end procedure notify; + + procedure test_runner_setup ( + signal runner : inout runner_sync_t; + constant runner_cfg : in string := runner_cfg_default) is + variable test_case_candidates : lines_t; + variable selected_enabled_test_cases : line; + begin + + -- fake active python runner key is only used during testing in tb_run.vhd + -- to avoid creating vunit_results file + set_active_python_runner(runner_state, + (active_python_runner(runner_cfg) and not has_key(runner_cfg, "fake active python runner"))); + + if has_active_python_runner(runner_state) then + core_pkg.setup(output_path(runner_cfg) & "vunit_results"); + end if; + + + if has_active_python_runner(runner_state) then + hide(runner_trace_logger, display_handler, info); + end if; + + if has_key(runner_cfg, "use_color") and boolean'value(get(runner_cfg, "use_color")) then + enable_colors; + end if; + + if not active_python_runner(runner_cfg) then + set_stop_level(failure); + end if; + + set_phase(runner_state, test_runner_setup); + runner(runner_exit_status_idx) <= runner_exit_with_errors; + notify(runner); + + trace(runner_trace_logger, "Entering test runner setup phase."); + entry_gate(runner); + + if selected_enabled_test_cases /= null then + deallocate(selected_enabled_test_cases); + end if; + + if has_key(runner_cfg, "enabled_test_cases") then + write(selected_enabled_test_cases, get(runner_cfg, "enabled_test_cases")); + else + write(selected_enabled_test_cases, string'("__all__")); + end if; + test_case_candidates := split(replace(selected_enabled_test_cases.all, ",,", "__comma__"), ","); + + set_cfg(runner_state, runner_cfg); + + set_run_all(runner_state, strip(test_case_candidates(0).all) = "__all__"); + if get_run_all(runner_state) then + set_num_of_test_cases(runner_state, unknown_num_of_test_cases_c); + else + set_num_of_test_cases(runner_state, 0); + for i in 1 to test_case_candidates'length loop + if strip(test_case_candidates(i - 1).all) /= "" then + inc_num_of_test_cases(runner_state); + + set_test_case_name(runner_state, + get_num_of_test_cases(runner_state), + replace(strip(test_case_candidates(i - 1).all), "__comma__", ",")); + end if; + end loop; + end if; + exit_gate(runner); + set_phase(runner_state, test_suite_setup); + notify(runner); + trace(runner_trace_logger, "Entering test suite setup phase."); + entry_gate(runner); + end test_runner_setup; + + procedure test_runner_cleanup ( + signal runner: inout runner_sync_t; + external_failure : boolean := false; + allow_disabled_errors : boolean := false; + allow_disabled_failures : boolean := false; + fail_on_warning : boolean := false) is + begin + failure_if(runner_trace_logger, external_failure, "External failure."); + + set_phase(runner_state, test_runner_cleanup); + notify(runner); + trace(runner_trace_logger, "Entering test runner cleanup phase."); + entry_gate(runner); + exit_gate(runner); + set_phase(runner_state, test_runner_exit); + notify(runner); + trace(runner_trace_logger, "Entering test runner exit phase."); + + if not final_log_check(allow_disabled_errors => allow_disabled_errors, + allow_disabled_failures => allow_disabled_failures, + fail_on_warning => fail_on_warning) then + return; + end if; + + runner(runner_exit_status_idx) <= runner_exit_without_errors; + notify(runner); + + if has_active_python_runner(runner_state) then + core_pkg.test_suite_done; + end if; + + if not p_simulation_exit_is_disabled(runner_state) then + core_pkg.stop(0); + end if; + + end procedure test_runner_cleanup; + + impure function num_of_enabled_test_cases + return integer is + begin + return get_num_of_test_cases(runner_state); + end; + + impure function enabled ( + constant name : string) + return boolean is + variable i : natural := 1; + begin + if get_run_all(runner_state) then + return true; + end if; + + for i in 1 to get_num_of_test_cases(runner_state) loop + if get_test_case_name(runner_state, i) = name then + return true; + end if; + end loop; + + return false; + end; + + impure function test_suite + return boolean is + variable ret_val : boolean; + begin + init_test_case_iteration(runner_state); + + if get_test_suite_completed(runner_state) then + ret_val := false; + elsif get_run_all(runner_state) then + ret_val := get_has_run_since_last_loop_check(runner_state); + else + if get_test_suite_iteration(runner_state) > 0 then + inc_active_test_case_index(runner_state); + end if; + + ret_val := get_active_test_case_index(runner_state) <= get_num_of_test_cases(runner_state); + end if; + + clear_has_run_since_last_loop_check(runner_state); + + if ret_val then + inc_test_suite_iteration(runner_state); + set_phase(runner_state, test_case_setup); + trace(runner_trace_logger, "Entering test case setup phase."); + else + set_test_suite_completed(runner_state); + set_phase(runner_state, test_suite_cleanup); + trace(runner_trace_logger, "Entering test suite cleanup phase."); + end if; + + return ret_val; + end; + + impure function test_case + return boolean is + begin + if get_test_case_iteration(runner_state) = 0 then + set_phase(runner_state, test_case); + trace(runner_trace_logger, "Entering test case phase."); + inc_test_case_iteration(runner_state); + clear_test_case_exit_after_error(runner_state); + clear_test_suite_exit_after_error(runner_state); + set_running_test_case(runner_state, ""); + return true; + else + set_phase(runner_state, test_case_cleanup); + trace(runner_trace_logger, "Entering test case cleanup phase."); + return false; + end if; + end function test_case; + + impure function run ( + constant name : string) + return boolean is + + impure function has_run ( + constant name : string) + return boolean is + begin + for i in 1 to get_num_of_run_test_cases(runner_state) loop + if get_run_test_case(runner_state, i) = name then + return true; + end if; + end loop; + return false; + end function has_run; + + procedure register_run ( + constant name : in string) is + begin + inc_num_of_run_test_cases(runner_state); + set_has_run_since_last_loop_check(runner_state); + set_run_test_case(runner_state, get_num_of_run_test_cases(runner_state), name); + end procedure register_run; + + begin + if get_test_suite_completed(runner_state) then + set_running_test_case(runner_state, ""); + return false; + elsif get_run_all(runner_state) then + if not has_run(name) then + register_run(name); + info(runner_trace_logger, "Test case: " & name); + if has_active_python_runner(runner_state) then + core_pkg.test_start(name); + end if; + set_running_test_case(runner_state, name); + return true; + end if; + elsif get_test_case_name(runner_state, get_active_test_case_index(runner_state)) = name then + info(runner_trace_logger, "Test case: " & name); + if has_active_python_runner(runner_state) then + core_pkg.test_start(name); + end if; + set_running_test_case(runner_state, name); + return true; + end if; + + set_running_test_case(runner_state, ""); + return false; + end; + + impure function active_test_case + return string is + begin + if get_run_all(runner_state) then + return ""; + end if; + return get_test_case_name(runner_state, get_active_test_case_index(runner_state)); + end; + + impure function running_test_case + return string is + begin + return get_running_test_case(runner_state); + end; + + procedure set_timeout(signal runner : inout runner_sync_t; + constant timeout : in time) is + begin + set_timeout(runner_state, timeout); + notify(runner, runner_timeout_update_idx); + end; + + procedure test_runner_watchdog ( + signal runner : inout runner_sync_t; + constant timeout : in time; + constant do_runner_cleanup : boolean := true) is + + variable current_timeout : time := timeout; + begin + + loop + wait until (runner(runner_exit_status_idx) = runner_exit_without_errors or + runner(runner_timeout_update_idx) = runner_event) for current_timeout; + + if runner(runner_timeout_update_idx) = runner_event then + debug(runner_trace_logger, "Update watchdog timeout " & time'image(current_timeout) & "."); + current_timeout := get_timeout(runner_state); + else + exit; + end if; + end loop; + + if not (runner(runner_exit_status_idx) = runner_exit_without_errors) then + error(runner_trace_logger, "Test runner timeout after " & time'image(current_timeout) & "."); + if do_runner_cleanup then + test_runner_cleanup(runner); + end if; + end if; + end; + + impure function test_suite_error ( + constant err : boolean) + return boolean is + begin + if err then + set_test_suite_completed(runner_state); + set_phase(runner_state, test_case_cleanup); + trace(runner_trace_logger, "Entering test case cleanup phase."); + set_test_suite_exit_after_error(runner_state); + end if; + + return err; + end function test_suite_error; + + impure function test_case_error ( + constant err : boolean) + return boolean is + begin + if err then + set_phase(runner_state, test_case_cleanup); + trace(runner_trace_logger, "Entering test case cleanup phase."); + set_test_case_exit_after_error(runner_state); + end if; + + return err; + end function test_case_error; + + impure function test_suite_exit + return boolean is + begin + return get_test_suite_exit_after_error(runner_state); + end function test_suite_exit; + + impure function test_case_exit + return boolean is + begin + return get_test_case_exit_after_error(runner_state); + end function test_case_exit; + + impure function test_exit + return boolean is + begin + return test_suite_exit or test_case_exit; + end function test_exit; + + procedure lock_entry ( + signal runner : inout runner_sync_t; + constant phase : in runner_legal_phase_t; + constant me : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + lock_entry(runner_state, phase); + log(runner_trace_logger, "Locked " & replace(runner_phase_t'image(phase), "_", " ") & " phase entry gate.", trace, line_num, file_name); + notify(runner); + end; + + procedure unlock_entry ( + signal runner : inout runner_sync_t; + constant phase : in runner_legal_phase_t; + constant me : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + unlock_entry(runner_state, phase); + log(runner_trace_logger, "Unlocked " & replace(runner_phase_t'image(phase), "_", " ") & " phase entry gate.", trace, line_num, file_name); + notify(runner); + end; + + procedure lock_exit ( + signal runner : inout runner_sync_t; + constant phase : in runner_legal_phase_t; + constant me : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + lock_exit(runner_state, phase); + log(runner_trace_logger, "Locked " & replace(runner_phase_t'image(phase), "_", " ") & " phase exit gate.", trace, line_num, file_name); + notify(runner); + end; + + procedure unlock_exit ( + signal runner : inout runner_sync_t; + constant phase : in runner_legal_phase_t; + constant me : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + unlock_exit(runner_state, phase); + log(runner_trace_logger, "Unlocked " & replace(runner_phase_t'image(phase), "_", " ") & " phase exit gate.", trace, line_num, file_name); + notify(runner); + end; + + procedure wait_until ( + signal runner : in runner_sync_t; + constant phase : in runner_legal_phase_t; + constant me : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := "") is + begin + if get_phase(runner_state) /= phase then + log(runner_trace_logger, "Waiting for phase = " & replace(runner_phase_t'image(phase), "_", " ") & ".", trace, line_num, file_name); + wait on runner until get_phase(runner_state) = phase; + log(runner_trace_logger, "Waking up. Phase is " & replace(runner_phase_t'image(phase), "_", " ") & ".", trace, line_num, file_name); + end if; + end; + + procedure entry_gate ( + signal runner : inout runner_sync_t) is + begin + if entry_is_locked(runner_state, get_phase(runner_state)) then + trace(runner_trace_logger, "Halting on " & replace(runner_phase_t'image(get_phase(runner_state)), "_", " ") & " phase entry gate."); + wait on runner until not entry_is_locked(runner_state, get_phase(runner_state)) for max_locked_time; + end if; + notify(runner); + trace(runner_trace_logger, "Passed " & replace(runner_phase_t'image(get_phase(runner_state)), "_", " ") & " phase entry gate."); + end procedure entry_gate; + + procedure exit_gate ( + signal runner : in runner_sync_t) is + begin + if exit_is_locked(runner_state, get_phase(runner_state)) then + trace(runner_trace_logger, "Halting on " & replace(runner_phase_t'image(get_phase(runner_state)), "_", " ") & " phase exit gate."); + wait on runner until not exit_is_locked(runner_state, get_phase(runner_state)) for max_locked_time; + end if; + trace(runner_trace_logger, "Passed " & replace(runner_phase_t'image(get_phase(runner_state)), "_", " ") & " phase exit gate."); + end procedure exit_gate; + + impure function active_python_runner ( + constant runner_cfg : string) + return boolean is + begin + if has_key(runner_cfg, "active python runner") then + return get(runner_cfg, "active python runner") = "true"; + else + return false; + end if; + end; + + impure function output_path ( + constant runner_cfg : string) + return string is + begin + if has_key(runner_cfg, "output path") then + return get(runner_cfg, "output path"); + else + return ""; + end if; + end; + + impure function enabled_test_cases ( + constant runner_cfg : string) + return test_cases_t is + begin + if has_key(runner_cfg, "enabled_test_cases") then + return get(runner_cfg, "enabled_test_cases"); + else + return "__all__"; + end if; + end; + + impure function tb_path ( + constant runner_cfg : string) + return string is + begin + if has_key(runner_cfg, "tb path") then + return get(runner_cfg, "tb path"); + else + return ""; + end if; + end; + + +end package body run_pkg; diff --git a/vunit/vhdl/run/src/run_api.vhd b/vunit/vhdl/run/src/run_api.vhd index 08f3c2ce2..681fb2502 100644 --- a/vunit/vhdl/run/src/run_api.vhd +++ b/vunit/vhdl/run/src/run_api.vhd @@ -1,163 +1,163 @@ --- Run API package provides the common user API for all --- implementations of the runner functionality (VHDL 2002+ and VHDL 1993) --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.logger_pkg.all; -use work.runner_pkg.all; -use work.run_types_pkg.all; - -library ieee; -use ieee.std_logic_1164.all; - -package run_pkg is - signal runner : runner_sync_t := (runner_event_idx => idle_runner, - runner_exit_status_idx => runner_exit_with_errors, - runner_timeout_update_idx => idle_runner); - - constant runner_state : runner_t := new_runner; - - procedure test_runner_setup ( - signal runner : inout runner_sync_t; - constant runner_cfg : in string := runner_cfg_default); - - impure function num_of_enabled_test_cases - return integer; - - impure function enabled ( - constant name : string) - return boolean; - - impure function test_suite - return boolean; - - impure function run ( - constant name : string) - return boolean; - - impure function active_test_case - return string; - - impure function running_test_case - return string; - - procedure test_runner_cleanup ( - signal runner: inout runner_sync_t; - external_failure : boolean := false; - allow_disabled_errors : boolean := false; - allow_disabled_failures : boolean := false; - fail_on_warning : boolean := false); - - impure function test_suite_error ( - constant err : boolean) - return boolean; - - impure function test_case_error ( - constant err : boolean) - return boolean; - - impure function test_suite_exit - return boolean; - - impure function test_case_exit - return boolean; - - impure function test_exit - return boolean; - - impure function test_case - return boolean; - - alias in_test_case is test_case[return boolean]; - - -- Set watchdog timeout dynamically relative to current time - -- Overrides time argument to test_runner_watchdog procedure - procedure set_timeout(signal runner : inout runner_sync_t; - constant timeout : in time); - - procedure test_runner_watchdog ( - signal runner : inout runner_sync_t; - constant timeout : in time; - constant do_runner_cleanup : boolean := true); - - procedure lock_entry ( - signal runner : inout runner_sync_t; - constant phase : in runner_legal_phase_t; - constant me : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure unlock_entry ( - signal runner : inout runner_sync_t; - constant phase : in runner_legal_phase_t; - constant me : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure lock_exit ( - signal runner : inout runner_sync_t; - constant phase : in runner_legal_phase_t; - constant me : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure unlock_exit ( - signal runner : inout runner_sync_t; - constant phase : in runner_legal_phase_t; - constant me : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure wait_until ( - signal runner : in runner_sync_t; - constant phase : in runner_legal_phase_t; - constant me : in string := ""; - constant line_num : in natural := 0; - constant file_name : in string := ""); - - procedure entry_gate ( - signal runner : inout runner_sync_t); - - procedure exit_gate ( - signal runner : in runner_sync_t); - - impure function active_python_runner ( - constant runner_cfg : string) - return boolean; - - impure function output_path ( - constant runner_cfg : string) - return string; - - impure function enabled_test_cases ( - constant runner_cfg : string) - return test_cases_t; - - impure function tb_path ( - constant runner_cfg : string) - return string; - - alias test_runner_setup_entry_gate is entry_gate[runner_sync_t]; - alias test_runner_setup_exit_gate is exit_gate[runner_sync_t]; - alias test_suite_setup_entry_gate is entry_gate[runner_sync_t]; - alias test_suite_setup_exit_gate is exit_gate[runner_sync_t]; - alias test_case_setup_entry_gate is entry_gate[runner_sync_t]; - alias test_case_setup_exit_gate is exit_gate[runner_sync_t]; - alias test_case_entry_gate is entry_gate[runner_sync_t]; - alias test_case_exit_gate is exit_gate[runner_sync_t]; - alias test_case_cleanup_entry_gate is entry_gate[runner_sync_t]; - alias test_case_cleanup_exit_gate is exit_gate[runner_sync_t]; - alias test_suite_cleanup_entry_gate is entry_gate[runner_sync_t]; - alias test_suite_cleanup_exit_gate is exit_gate[runner_sync_t]; - alias test_runner_cleanup_entry_gate is entry_gate[runner_sync_t]; - alias test_runner_cleanup_exit_gate is exit_gate[runner_sync_t]; - - -- Private - procedure notify(signal runner : inout runner_sync_t; - idx : natural := runner_event_idx); - -end package; +-- Run API package provides the common user API for all +-- implementations of the runner functionality (VHDL 2002+ and VHDL 1993) +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.logger_pkg.all; +use work.runner_pkg.all; +use work.run_types_pkg.all; + +library ieee; +use ieee.std_logic_1164.all; + +package run_pkg is + signal runner : runner_sync_t := (runner_event_idx => idle_runner, + runner_exit_status_idx => runner_exit_with_errors, + runner_timeout_update_idx => idle_runner); + + constant runner_state : runner_t := new_runner; + + procedure test_runner_setup ( + signal runner : inout runner_sync_t; + constant runner_cfg : in string := runner_cfg_default); + + impure function num_of_enabled_test_cases + return integer; + + impure function enabled ( + constant name : string) + return boolean; + + impure function test_suite + return boolean; + + impure function run ( + constant name : string) + return boolean; + + impure function active_test_case + return string; + + impure function running_test_case + return string; + + procedure test_runner_cleanup ( + signal runner: inout runner_sync_t; + external_failure : boolean := false; + allow_disabled_errors : boolean := false; + allow_disabled_failures : boolean := false; + fail_on_warning : boolean := false); + + impure function test_suite_error ( + constant err : boolean) + return boolean; + + impure function test_case_error ( + constant err : boolean) + return boolean; + + impure function test_suite_exit + return boolean; + + impure function test_case_exit + return boolean; + + impure function test_exit + return boolean; + + impure function test_case + return boolean; + + alias in_test_case is test_case[return boolean]; + + -- Set watchdog timeout dynamically relative to current time + -- Overrides time argument to test_runner_watchdog procedure + procedure set_timeout(signal runner : inout runner_sync_t; + constant timeout : in time); + + procedure test_runner_watchdog ( + signal runner : inout runner_sync_t; + constant timeout : in time; + constant do_runner_cleanup : boolean := true); + + procedure lock_entry ( + signal runner : inout runner_sync_t; + constant phase : in runner_legal_phase_t; + constant me : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure unlock_entry ( + signal runner : inout runner_sync_t; + constant phase : in runner_legal_phase_t; + constant me : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure lock_exit ( + signal runner : inout runner_sync_t; + constant phase : in runner_legal_phase_t; + constant me : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure unlock_exit ( + signal runner : inout runner_sync_t; + constant phase : in runner_legal_phase_t; + constant me : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure wait_until ( + signal runner : in runner_sync_t; + constant phase : in runner_legal_phase_t; + constant me : in string := ""; + constant line_num : in natural := 0; + constant file_name : in string := ""); + + procedure entry_gate ( + signal runner : inout runner_sync_t); + + procedure exit_gate ( + signal runner : in runner_sync_t); + + impure function active_python_runner ( + constant runner_cfg : string) + return boolean; + + impure function output_path ( + constant runner_cfg : string) + return string; + + impure function enabled_test_cases ( + constant runner_cfg : string) + return test_cases_t; + + impure function tb_path ( + constant runner_cfg : string) + return string; + + alias test_runner_setup_entry_gate is entry_gate[runner_sync_t]; + alias test_runner_setup_exit_gate is exit_gate[runner_sync_t]; + alias test_suite_setup_entry_gate is entry_gate[runner_sync_t]; + alias test_suite_setup_exit_gate is exit_gate[runner_sync_t]; + alias test_case_setup_entry_gate is entry_gate[runner_sync_t]; + alias test_case_setup_exit_gate is exit_gate[runner_sync_t]; + alias test_case_entry_gate is entry_gate[runner_sync_t]; + alias test_case_exit_gate is exit_gate[runner_sync_t]; + alias test_case_cleanup_entry_gate is entry_gate[runner_sync_t]; + alias test_case_cleanup_exit_gate is exit_gate[runner_sync_t]; + alias test_suite_cleanup_entry_gate is entry_gate[runner_sync_t]; + alias test_suite_cleanup_exit_gate is exit_gate[runner_sync_t]; + alias test_runner_cleanup_entry_gate is entry_gate[runner_sync_t]; + alias test_runner_cleanup_exit_gate is exit_gate[runner_sync_t]; + + -- Private + procedure notify(signal runner : inout runner_sync_t; + idx : natural := runner_event_idx); + +end package; diff --git a/vunit/vhdl/run/src/run_deprecated_pkg.vhd b/vunit/vhdl/run/src/run_deprecated_pkg.vhd index 7a143c870..f1ada1258 100644 --- a/vunit/vhdl/run/src/run_deprecated_pkg.vhd +++ b/vunit/vhdl/run/src/run_deprecated_pkg.vhd @@ -1,33 +1,33 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.logger_pkg.all; -use work.checker_pkg.all; -use work.runner_pkg.all; -use work.run_types_pkg.all; -use work.run_pkg.all; -use work.core_pkg; - -package run_deprecated_pkg is - -- Deprecated interface to better support legacy testbenches. - procedure test_runner_cleanup ( - signal runner: inout runner_sync_t; - constant checker_stat : in checker_stat_t); - -end package run_deprecated_pkg; - -package body run_deprecated_pkg is - procedure test_runner_cleanup ( - signal runner: inout runner_sync_t; - constant checker_stat : in checker_stat_t) is - begin - warning("Using deprecated procedure test_runner_cleanup with " & - "checker_stat."); - - failure_if(checker_stat.n_failed > 0, to_string(checker_stat)); - test_runner_cleanup(runner); - end; -end package body run_deprecated_pkg; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.logger_pkg.all; +use work.checker_pkg.all; +use work.runner_pkg.all; +use work.run_types_pkg.all; +use work.run_pkg.all; +use work.core_pkg; + +package run_deprecated_pkg is + -- Deprecated interface to better support legacy testbenches. + procedure test_runner_cleanup ( + signal runner: inout runner_sync_t; + constant checker_stat : in checker_stat_t); + +end package run_deprecated_pkg; + +package body run_deprecated_pkg is + procedure test_runner_cleanup ( + signal runner: inout runner_sync_t; + constant checker_stat : in checker_stat_t) is + begin + warning("Using deprecated procedure test_runner_cleanup with " & + "checker_stat."); + + failure_if(checker_stat.n_failed > 0, to_string(checker_stat)); + test_runner_cleanup(runner); + end; +end package body run_deprecated_pkg; diff --git a/vunit/vhdl/run/src/run_types.vhd b/vunit/vhdl/run/src/run_types.vhd index 8526f4383..f6f212f1d 100644 --- a/vunit/vhdl/run/src/run_types.vhd +++ b/vunit/vhdl/run/src/run_types.vhd @@ -1,53 +1,53 @@ --- Run types package provides common types used by all VHDL implementations. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use std.textio.all; -use work.dictionary.all; - -library ieee; -use ieee.std_logic_1164.all; - -package run_types_pkg is - constant max_locked_time : time := 1 ms; - constant max_n_test_cases : natural := 1024; - - -- Deprecated - constant max_locked_time_c : time := max_locked_time; - constant max_n_test_cases_c : natural := max_n_test_cases; - - subtype runner_cfg_t is string; -- Subtype deprecated, use string instead - constant runner_cfg_default : string := "enabled_test_cases : __all__, output path : , active python runner : false"; - subtype test_cases_t is string; - - type runner_phase_t is (test_runner_entry, test_runner_setup, test_suite_setup, test_case_setup, - test_case, test_case_cleanup, test_suite_cleanup, test_runner_cleanup, - test_runner_exit); - subtype runner_legal_phase_t is runner_phase_t range test_runner_setup to test_runner_cleanup; - - type phase_locks_t is record - entry_is_locked : boolean; - exit_is_locked : boolean; - end record; - - type boolean_array_t is array (integer range <>) of boolean; - - constant runner_event_idx : natural := 0; - constant runner_exit_status_idx : natural := 1; - constant runner_timeout_update_idx : natural := 2; - - constant runner_event : std_logic := '1'; - constant idle_runner : std_logic := 'Z'; - - constant runner_exit_with_errors : std_logic := 'Z'; - constant runner_exit_without_errors : std_logic := '1'; - - subtype runner_sync_t is std_logic_vector(runner_event_idx to runner_timeout_update_idx); -end package; - -package body run_types_pkg is -end package body run_types_pkg; +-- Run types package provides common types used by all VHDL implementations. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use std.textio.all; +use work.dictionary.all; + +library ieee; +use ieee.std_logic_1164.all; + +package run_types_pkg is + constant max_locked_time : time := 1 ms; + constant max_n_test_cases : natural := 1024; + + -- Deprecated + constant max_locked_time_c : time := max_locked_time; + constant max_n_test_cases_c : natural := max_n_test_cases; + + subtype runner_cfg_t is string; -- Subtype deprecated, use string instead + constant runner_cfg_default : string := "enabled_test_cases : __all__, output path : , active python runner : false"; + subtype test_cases_t is string; + + type runner_phase_t is (test_runner_entry, test_runner_setup, test_suite_setup, test_case_setup, + test_case, test_case_cleanup, test_suite_cleanup, test_runner_cleanup, + test_runner_exit); + subtype runner_legal_phase_t is runner_phase_t range test_runner_setup to test_runner_cleanup; + + type phase_locks_t is record + entry_is_locked : boolean; + exit_is_locked : boolean; + end record; + + type boolean_array_t is array (integer range <>) of boolean; + + constant runner_event_idx : natural := 0; + constant runner_exit_status_idx : natural := 1; + constant runner_timeout_update_idx : natural := 2; + + constant runner_event : std_logic := '1'; + constant idle_runner : std_logic := 'Z'; + + constant runner_exit_with_errors : std_logic := 'Z'; + constant runner_exit_without_errors : std_logic := '1'; + + subtype runner_sync_t is std_logic_vector(runner_event_idx to runner_timeout_update_idx); +end package; + +package body run_types_pkg is +end package body run_types_pkg; diff --git a/vunit/vhdl/run/src/runner_pkg.vhd b/vunit/vhdl/run/src/runner_pkg.vhd index 5e0e71b70..fedf7c8a7 100644 --- a/vunit/vhdl/run/src/runner_pkg.vhd +++ b/vunit/vhdl/run/src/runner_pkg.vhd @@ -1,548 +1,548 @@ --- Run base package provides fundamental run functionality. --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.string_ptr_pkg.all; -use work.string_ptr_pool_pkg.all; -use work.integer_vector_ptr_pkg.all; -use work.integer_vector_ptr_pool_pkg.all; -use work.run_types_pkg.all; -use work.logger_pkg.all; -use work.string_ops.all; -use work.codec_pkg.all; - -package runner_pkg is - constant runner_trace_logger : logger_t := get_logger("runner"); - - type runner_t is record - p_data : integer_vector_ptr_t; - end record; - - constant null_runner : runner_t := (p_data => null_ptr); - constant unknown_num_of_test_cases : integer := integer'left; - - -- Deprecated - constant unknown_num_of_test_cases_c : integer := unknown_num_of_test_cases; - - impure function new_runner return runner_t; - - procedure runner_init(runner : runner_t); - procedure set_active_python_runner(runner : runner_t; value : boolean); - impure function has_active_python_runner(runner : runner_t) return boolean; - - procedure lock_entry(runner : runner_t; phase : runner_legal_phase_t); - - procedure unlock_entry(runner : runner_t; phase : runner_legal_phase_t); - - impure function entry_is_locked(runner : runner_t; phase : runner_legal_phase_t) return boolean; - - procedure lock_exit(runner : runner_t; phase : runner_legal_phase_t); - - procedure unlock_exit(runner : runner_t; phase : runner_legal_phase_t); - - impure function exit_is_locked(runner : runner_t; phase : runner_legal_phase_t) return boolean; - - procedure set_phase(runner : runner_t; new_phase : runner_phase_t); - - impure function get_phase(runner : runner_t) return runner_phase_t; - - procedure set_test_case_name(runner : runner_t; index : positive; new_name : string); - - impure function get_test_case_name(runner : runner_t; index : positive) return string ; - - procedure set_num_of_test_cases(runner : runner_t; new_value : integer); - - procedure inc_num_of_test_cases(runner : runner_t); - - impure function get_num_of_test_cases(runner : runner_t) return integer; - - impure function get_active_test_case_index(runner : runner_t) return integer; - - procedure inc_active_test_case_index(runner : runner_t); - - procedure set_test_suite_completed(runner : runner_t); - - impure function get_test_suite_completed(runner : runner_t) return boolean; - - impure function get_test_suite_iteration(runner : runner_t) return natural; - - procedure inc_test_suite_iteration(runner : runner_t); - - procedure set_run_test_case(runner : runner_t; index : positive; new_name : string); - - impure function get_run_test_case(runner : runner_t; index : positive) return string; - - procedure set_running_test_case(runner : runner_t; new_name : string); - - impure function get_running_test_case(runner : runner_t) return string; - - impure function get_num_of_run_test_cases(runner : runner_t) return natural; - - procedure inc_num_of_run_test_cases(runner : runner_t); - - procedure set_has_run_since_last_loop_check(runner : runner_t); - - procedure clear_has_run_since_last_loop_check(runner : runner_t); - - impure function get_has_run_since_last_loop_check(runner : runner_t) return boolean; - - procedure set_run_all(runner : runner_t); - - procedure set_run_all(runner : runner_t; new_value : boolean); - - impure function get_run_all(runner : runner_t) return boolean; - - impure function get_test_case_iteration(runner : runner_t) return natural; - - procedure inc_test_case_iteration(runner : runner_t); - - procedure init_test_case_iteration(runner : runner_t); - - procedure set_test_case_exit_after_error(runner : runner_t); - - procedure clear_test_case_exit_after_error(runner : runner_t); - - impure function get_test_case_exit_after_error(runner : runner_t) return boolean; - - procedure set_test_suite_exit_after_error(runner : runner_t); - - procedure clear_test_suite_exit_after_error(runner : runner_t); - - impure function get_test_suite_exit_after_error(runner : runner_t) return boolean; - - procedure set_cfg(runner : runner_t; new_value : string); - - impure function get_cfg(runner : runner_t) return string; - - procedure set_timeout(runner : runner_t; timeout : time); - impure function get_timeout(runner : runner_t) return time; - - -- Private procedures only use for VUnit internal testing - procedure p_disable_simulation_exit(runner : runner_t); - impure function p_simulation_exit_is_disabled(runner : runner_t) return boolean; -end package; - -package body runner_pkg is - - constant active_python_runner_idx : natural := 0; - constant runner_phase_idx : natural := 1; - constant test_case_names_idx : natural := 2; - constant n_test_cases_idx : natural := 3; - constant active_test_case_index_idx : natural := 4; - constant test_suite_completed_idx : natural := 5; - constant test_suite_iteration_idx : natural := 6; - constant run_test_cases_idx : natural := 7; - constant running_test_case_idx : natural := 8; - constant n_run_test_cases_idx : natural := 9; - constant has_run_since_last_loop_check_idx : natural := 10; - constant run_all_idx : natural := 11; - constant test_case_iteration_idx : natural := 12; - constant test_case_exit_after_error_idx : natural := 13; - constant test_suite_exit_after_error_idx : natural := 14; - constant runner_cfg_idx : natural := 15; - constant disable_simulation_exit_idx : natural := 16; - constant entry_locks_idx : natural := 17; - constant exit_locks_idx : natural := 18; - constant timeout_idx : natural := 19; - constant runner_length : natural := 20; - - function to_integer(value : boolean) return natural is - begin - if value then - return 1; - else - return 0; - end if; - end; - - constant str_pool : string_ptr_pool_t := new_string_ptr_pool; - constant int_pool : integer_vector_ptr_pool_t := new_integer_vector_ptr_pool; - - impure function new_runner return runner_t is - variable runner : runner_t := (p_data => new_integer_vector_ptr(int_pool, runner_length)); - begin - runner_init(runner); - return runner; - end; - - procedure runner_init(runner : runner_t) is - constant n_legal_phases : positive := - runner_legal_phase_t'pos(runner_legal_phase_t'high) - - runner_legal_phase_t'pos(runner_legal_phase_t'low) + 1; - begin - assert runner.p_data /= null_ptr; - reallocate(runner.p_data, runner_length); - - set_phase(runner, runner_phase_t'low); - set_active_python_runner(runner, false); - - set(runner.p_data, test_case_names_idx, to_integer(integer_vector_ptr_t'(new_integer_vector_ptr))); - - set(runner.p_data, n_test_cases_idx, unknown_num_of_test_cases); - set(runner.p_data, active_test_case_index_idx, 1); - set(runner.p_data, test_suite_completed_idx, to_integer(false)); - set(runner.p_data, test_suite_iteration_idx, 0); - - set(runner.p_data, run_test_cases_idx, to_integer(integer_vector_ptr_t'(new_integer_vector_ptr))); - set(runner.p_data, running_test_case_idx, to_integer(null_string_ptr)); - - set(runner.p_data, n_run_test_cases_idx, 0); - set(runner.p_data, has_run_since_last_loop_check_idx, to_integer(true)); - set(runner.p_data, run_all_idx, to_integer(true)); - set(runner.p_data, test_case_iteration_idx, 0); - set(runner.p_data, test_case_exit_after_error_idx, to_integer(false)); - set(runner.p_data, test_suite_exit_after_error_idx, to_integer(false)); - set(runner.p_data, runner_cfg_idx, to_integer(new_string_ptr(str_pool, runner_cfg_default))); - - set(runner.p_data, disable_simulation_exit_idx, to_integer(false)); - set(runner.p_data, entry_locks_idx, to_integer(integer_vector_ptr_t'(new_integer_vector_ptr(n_legal_phases)))); - set(runner.p_data, exit_locks_idx, to_integer(integer_vector_ptr_t'(new_integer_vector_ptr(n_legal_phases)))); - set(runner.p_data, timeout_idx, to_integer(new_string_ptr(str_pool, encode(0 ns)))); - end; - - procedure set_active_python_runner(runner : runner_t; value : boolean) is - begin - set(runner.p_data, active_python_runner_idx, to_integer(value)); - end; - - impure function has_active_python_runner(runner : runner_t) return boolean is - begin - return get(runner.p_data, active_python_runner_idx) = 1; - end function; - - procedure lock_entry(runner : runner_t; phase : runner_legal_phase_t) is - constant entry_locks : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, entry_locks_idx)); - constant idx : natural := runner_legal_phase_t'pos(phase) - runner_legal_phase_t'pos(runner_legal_phase_t'low); - constant n_locks : natural := get(entry_locks, idx); - begin - set(entry_locks, idx, n_locks + 1); - end; - - procedure unlock_entry(runner : runner_t; phase : runner_legal_phase_t) is - constant entry_locks : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, entry_locks_idx)); - constant idx : natural := runner_legal_phase_t'pos(phase) - runner_legal_phase_t'pos(runner_legal_phase_t'low); - constant n_locks : natural := get(entry_locks, idx); - begin - failure_if(runner_trace_logger, n_locks = 0, - "No locks to unlock on " & replace(runner_legal_phase_t'image(phase), "_", " ") & " entry gate."); - set(entry_locks, idx, n_locks - 1); - end; - - impure function entry_is_locked(runner : runner_t; phase : runner_legal_phase_t) return boolean is - constant entry_locks : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, entry_locks_idx)); - constant idx : natural := runner_legal_phase_t'pos(phase) - runner_legal_phase_t'pos(runner_legal_phase_t'low); - constant n_locks : natural := get(entry_locks, idx); - begin - return n_locks > 0; - end; - - procedure lock_exit(runner : runner_t; phase : runner_legal_phase_t) is - constant exit_locks : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, exit_locks_idx)); - constant idx : natural := runner_legal_phase_t'pos(phase) - runner_legal_phase_t'pos(runner_legal_phase_t'low); - constant n_locks : natural := get(exit_locks, idx); - begin - set(exit_locks, idx, n_locks + 1); - end; - - procedure unlock_exit(runner : runner_t; phase : runner_legal_phase_t) is - constant exit_locks : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, exit_locks_idx)); - constant idx : natural := runner_legal_phase_t'pos(phase) - runner_legal_phase_t'pos(runner_legal_phase_t'low); - constant n_locks : natural := get(exit_locks, idx); - begin - failure_if(runner_trace_logger, n_locks = 0, - "No locks to unlock on " & replace(runner_legal_phase_t'image(phase), "_", " ") & " exit gate."); - set(exit_locks, idx, n_locks - 1); - end; - - impure function exit_is_locked(runner : runner_t; phase : runner_legal_phase_t) return boolean is - constant exit_locks : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, exit_locks_idx)); - constant idx : natural := runner_legal_phase_t'pos(phase) - runner_legal_phase_t'pos(runner_legal_phase_t'low); - constant n_locks : natural := get(exit_locks, idx); - begin - return n_locks > 0; - end; - - procedure set_phase(runner : runner_t; new_phase : runner_phase_t) is - begin - set(runner.p_data, runner_phase_idx, runner_phase_t'pos(new_phase)); - end; - - impure function get_phase(runner : runner_t) return runner_phase_t is - begin - return runner_phase_t'val(get(runner.p_data, runner_phase_idx)); - end; - - procedure set_test_case_name(runner : runner_t; index : positive; new_name : string) is - constant test_case_names : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, test_case_names_idx)); - variable test_case_name : string_ptr_t; - begin - if index-1 >= length(test_case_names) then - resize(test_case_names, index, value => to_integer(null_string_ptr)); - end if; - - test_case_name := to_string_ptr(get(test_case_names, index-1)); - - if test_case_name = null_string_ptr then - test_case_name := new_string_ptr(str_pool, new_name); - else - reallocate(test_case_name, new_name); - end if; - - set(test_case_names, index-1, to_integer(test_case_name)); - end; - - impure function get_test_case_name(runner : runner_t; index : positive) return string is - constant test_case_names : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, test_case_names_idx)); - variable test_case_name : string_ptr_t; - begin - -- @TODO fail instead? - if index-1 >= length(test_case_names) then - return ""; - end if; - - test_case_name := to_string_ptr(get(test_case_names, index-1)); - - if test_case_name = null_string_ptr then - return ""; - end if; - - return to_string(test_case_name); - end; - - procedure set_num_of_test_cases(runner : runner_t; new_value : integer) is - begin - set(runner.p_data, n_test_cases_idx, new_value); - end; - - procedure inc_num_of_test_cases(runner : runner_t) is - begin - set(runner.p_data, n_test_cases_idx, get_num_of_test_cases(runner) + 1); - end; - - impure function get_num_of_test_cases(runner : runner_t) return integer is - begin - return get(runner.p_data, n_test_cases_idx); - end; - - impure function get_active_test_case_index(runner : runner_t) return integer is - begin - return get(runner.p_data, active_test_case_index_idx); - end; - - procedure inc_active_test_case_index(runner : runner_t) is - begin - set(runner.p_data, active_test_case_index_idx, get_active_test_case_index(runner) + 1); - end; - - procedure set_test_suite_completed(runner : runner_t) is - begin - set(runner.p_data, test_suite_completed_idx, to_integer(true)); - end; - - impure function get_test_suite_completed(runner : runner_t) return boolean is - begin - return get(runner.p_data, test_suite_completed_idx) = 1; - end; - - impure function get_test_suite_iteration(runner : runner_t) return natural is - begin - return get(runner.p_data, test_suite_iteration_idx); - end; - - procedure inc_test_suite_iteration(runner : runner_t) is - begin - set(runner.p_data, test_suite_iteration_idx, get_test_suite_iteration(runner) + 1); - end; - - procedure set_run_test_case(runner : runner_t; index : positive; new_name : string) is - constant run_test_cases : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, run_test_cases_idx)); - variable run_test_case : string_ptr_t; - begin - if index-1 >= length(run_test_cases) then - resize(run_test_cases, index, value => to_integer(null_string_ptr)); - end if; - - run_test_case := to_string_ptr(get(run_test_cases, index-1)); - - if run_test_case = null_string_ptr then - run_test_case := new_string_ptr(str_pool, new_name); - else - reallocate(run_test_case, new_name); - end if; - - set(run_test_cases, index-1, to_integer(run_test_case)); - end; - - impure function get_run_test_case(runner : runner_t; index : positive) return string is - constant run_test_cases : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, run_test_cases_idx)); - variable run_test_case : string_ptr_t; - begin - -- @TODO fail instead? - if index-1 >= length(run_test_cases) then - return ""; - end if; - - run_test_case := to_string_ptr(get(run_test_cases, index-1)); - - if run_test_case = null_string_ptr then - return ""; - end if; - - return to_string(run_test_case); - end; - - procedure set_running_test_case(runner : runner_t; new_name : string) is - variable running_test_case : string_ptr_t := to_string_ptr(get(runner.p_data, running_test_case_idx)); - begin - if running_test_case = null_string_ptr then - running_test_case := new_string_ptr(str_pool, new_name); - else - reallocate(running_test_case, new_name); - end if; - set(runner.p_data, running_test_case_idx, to_integer(running_test_case)); - end; - - impure function get_running_test_case(runner : runner_t) return string is - constant running_test_case : string_ptr_t := to_string_ptr(get(runner.p_data, running_test_case_idx)); - begin - if running_test_case = null_string_ptr then - return ""; - end if; - return to_string(running_test_case); - end; - - impure function get_num_of_run_test_cases(runner : runner_t) return natural is - begin - return get(runner.p_data, n_run_test_cases_idx); - end; - - procedure inc_num_of_run_test_cases(runner : runner_t) is - begin - set(runner.p_data, n_run_test_cases_idx, get_num_of_run_test_cases(runner) + 1); - end; - - procedure set_has_run_since_last_loop_check(runner : runner_t) is - begin - set(runner.p_data, has_run_since_last_loop_check_idx, to_integer(true)); - end; - - procedure clear_has_run_since_last_loop_check(runner : runner_t) is - begin - set(runner.p_data, has_run_since_last_loop_check_idx, to_integer(false)); - end; - - impure function get_has_run_since_last_loop_check(runner : runner_t) return boolean is - begin - return get(runner.p_data, has_run_since_last_loop_check_idx) = 1; - end; - - procedure set_run_all(runner : runner_t) is - begin - set_run_all(runner, true); - end; - - procedure set_run_all(runner : runner_t; new_value : boolean) is - begin - set(runner.p_data, run_all_idx, to_integer(new_value)); - end; - - impure function get_run_all(runner : runner_t) return boolean is - begin - return get(runner.p_data, run_all_idx) = 1; - end; - - impure function get_test_case_iteration(runner : runner_t) return natural is - begin - return get(runner.p_data, test_case_iteration_idx); - end; - - procedure inc_test_case_iteration(runner : runner_t) is - begin - set(runner.p_data, test_case_iteration_idx, get_test_case_iteration(runner) + 1); - end; - - procedure init_test_case_iteration(runner : runner_t) is - begin - set(runner.p_data, test_case_iteration_idx, 0); - end; - - procedure set_test_case_exit_after_error(runner : runner_t) is - begin - set(runner.p_data, test_case_exit_after_error_idx, to_integer(true)); - end; - - procedure clear_test_case_exit_after_error(runner : runner_t) is - begin - set(runner.p_data, test_case_exit_after_error_idx, to_integer(false)); - end; - - impure function get_test_case_exit_after_error(runner : runner_t) return boolean is - begin - return get(runner.p_data, test_case_exit_after_error_idx) = 1; - end; - - procedure set_test_suite_exit_after_error(runner : runner_t) is - begin - set(runner.p_data, test_suite_exit_after_error_idx, to_integer(true)); - end; - - procedure clear_test_suite_exit_after_error(runner : runner_t) is - begin - set(runner.p_data, test_suite_exit_after_error_idx, to_integer(false)); - end; - - impure function get_test_suite_exit_after_error(runner : runner_t) return boolean is - begin - return get(runner.p_data, test_suite_exit_after_error_idx) = 1; - end; - - procedure set_cfg(runner : runner_t; new_value : string) is - variable runner_cfg : string_ptr_t := to_string_ptr(get(runner.p_data, runner_cfg_idx)); - begin - if runner_cfg = null_string_ptr then - runner_cfg := new_string_ptr(str_pool, new_value); - else - reallocate(runner_cfg, new_value); - end if; - set(runner.p_data, runner_cfg_idx, to_integer(runner_cfg)); - end; - - impure function get_cfg(runner : runner_t) return string is - constant runner_cfg : string_ptr_t := to_string_ptr(get(runner.p_data, runner_cfg_idx)); - begin - return to_string(runner_cfg); - end; - - procedure p_disable_simulation_exit(runner : runner_t) is - begin - set(runner.p_data, disable_simulation_exit_idx, to_integer(true)); - end; - - impure function p_simulation_exit_is_disabled(runner : runner_t) return boolean is - begin - return get(runner.p_data, disable_simulation_exit_idx) = to_integer(true); - end; - - procedure set_timeout(runner : runner_t; timeout : time) is - constant new_value : string := encode(timeout); - variable timeout_ptr : string_ptr_t := to_string_ptr(get(runner.p_data, timeout_idx)); - begin - if timeout_ptr = null_string_ptr then - timeout_ptr := new_string_ptr(str_pool, new_value); - else - reallocate(timeout_ptr, new_value); - end if; - set(runner.p_data, timeout_idx, to_integer(timeout_ptr)); - end; - - impure function get_timeout(runner : runner_t) return time is - constant timeout_ptr : string_ptr_t := to_string_ptr(get(runner.p_data, timeout_idx)); - begin - return decode(to_string(timeout_ptr)); - end; - -end package body; +-- Run base package provides fundamental run functionality. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.string_ptr_pkg.all; +use work.string_ptr_pool_pkg.all; +use work.integer_vector_ptr_pkg.all; +use work.integer_vector_ptr_pool_pkg.all; +use work.run_types_pkg.all; +use work.logger_pkg.all; +use work.string_ops.all; +use work.codec_pkg.all; + +package runner_pkg is + constant runner_trace_logger : logger_t := get_logger("runner"); + + type runner_t is record + p_data : integer_vector_ptr_t; + end record; + + constant null_runner : runner_t := (p_data => null_ptr); + constant unknown_num_of_test_cases : integer := integer'left; + + -- Deprecated + constant unknown_num_of_test_cases_c : integer := unknown_num_of_test_cases; + + impure function new_runner return runner_t; + + procedure runner_init(runner : runner_t); + procedure set_active_python_runner(runner : runner_t; value : boolean); + impure function has_active_python_runner(runner : runner_t) return boolean; + + procedure lock_entry(runner : runner_t; phase : runner_legal_phase_t); + + procedure unlock_entry(runner : runner_t; phase : runner_legal_phase_t); + + impure function entry_is_locked(runner : runner_t; phase : runner_legal_phase_t) return boolean; + + procedure lock_exit(runner : runner_t; phase : runner_legal_phase_t); + + procedure unlock_exit(runner : runner_t; phase : runner_legal_phase_t); + + impure function exit_is_locked(runner : runner_t; phase : runner_legal_phase_t) return boolean; + + procedure set_phase(runner : runner_t; new_phase : runner_phase_t); + + impure function get_phase(runner : runner_t) return runner_phase_t; + + procedure set_test_case_name(runner : runner_t; index : positive; new_name : string); + + impure function get_test_case_name(runner : runner_t; index : positive) return string ; + + procedure set_num_of_test_cases(runner : runner_t; new_value : integer); + + procedure inc_num_of_test_cases(runner : runner_t); + + impure function get_num_of_test_cases(runner : runner_t) return integer; + + impure function get_active_test_case_index(runner : runner_t) return integer; + + procedure inc_active_test_case_index(runner : runner_t); + + procedure set_test_suite_completed(runner : runner_t); + + impure function get_test_suite_completed(runner : runner_t) return boolean; + + impure function get_test_suite_iteration(runner : runner_t) return natural; + + procedure inc_test_suite_iteration(runner : runner_t); + + procedure set_run_test_case(runner : runner_t; index : positive; new_name : string); + + impure function get_run_test_case(runner : runner_t; index : positive) return string; + + procedure set_running_test_case(runner : runner_t; new_name : string); + + impure function get_running_test_case(runner : runner_t) return string; + + impure function get_num_of_run_test_cases(runner : runner_t) return natural; + + procedure inc_num_of_run_test_cases(runner : runner_t); + + procedure set_has_run_since_last_loop_check(runner : runner_t); + + procedure clear_has_run_since_last_loop_check(runner : runner_t); + + impure function get_has_run_since_last_loop_check(runner : runner_t) return boolean; + + procedure set_run_all(runner : runner_t); + + procedure set_run_all(runner : runner_t; new_value : boolean); + + impure function get_run_all(runner : runner_t) return boolean; + + impure function get_test_case_iteration(runner : runner_t) return natural; + + procedure inc_test_case_iteration(runner : runner_t); + + procedure init_test_case_iteration(runner : runner_t); + + procedure set_test_case_exit_after_error(runner : runner_t); + + procedure clear_test_case_exit_after_error(runner : runner_t); + + impure function get_test_case_exit_after_error(runner : runner_t) return boolean; + + procedure set_test_suite_exit_after_error(runner : runner_t); + + procedure clear_test_suite_exit_after_error(runner : runner_t); + + impure function get_test_suite_exit_after_error(runner : runner_t) return boolean; + + procedure set_cfg(runner : runner_t; new_value : string); + + impure function get_cfg(runner : runner_t) return string; + + procedure set_timeout(runner : runner_t; timeout : time); + impure function get_timeout(runner : runner_t) return time; + + -- Private procedures only use for VUnit internal testing + procedure p_disable_simulation_exit(runner : runner_t); + impure function p_simulation_exit_is_disabled(runner : runner_t) return boolean; +end package; + +package body runner_pkg is + + constant active_python_runner_idx : natural := 0; + constant runner_phase_idx : natural := 1; + constant test_case_names_idx : natural := 2; + constant n_test_cases_idx : natural := 3; + constant active_test_case_index_idx : natural := 4; + constant test_suite_completed_idx : natural := 5; + constant test_suite_iteration_idx : natural := 6; + constant run_test_cases_idx : natural := 7; + constant running_test_case_idx : natural := 8; + constant n_run_test_cases_idx : natural := 9; + constant has_run_since_last_loop_check_idx : natural := 10; + constant run_all_idx : natural := 11; + constant test_case_iteration_idx : natural := 12; + constant test_case_exit_after_error_idx : natural := 13; + constant test_suite_exit_after_error_idx : natural := 14; + constant runner_cfg_idx : natural := 15; + constant disable_simulation_exit_idx : natural := 16; + constant entry_locks_idx : natural := 17; + constant exit_locks_idx : natural := 18; + constant timeout_idx : natural := 19; + constant runner_length : natural := 20; + + function to_integer(value : boolean) return natural is + begin + if value then + return 1; + else + return 0; + end if; + end; + + constant str_pool : string_ptr_pool_t := new_string_ptr_pool; + constant int_pool : integer_vector_ptr_pool_t := new_integer_vector_ptr_pool; + + impure function new_runner return runner_t is + variable runner : runner_t := (p_data => new_integer_vector_ptr(int_pool, runner_length)); + begin + runner_init(runner); + return runner; + end; + + procedure runner_init(runner : runner_t) is + constant n_legal_phases : positive := + runner_legal_phase_t'pos(runner_legal_phase_t'high) - + runner_legal_phase_t'pos(runner_legal_phase_t'low) + 1; + begin + assert runner.p_data /= null_ptr; + reallocate(runner.p_data, runner_length); + + set_phase(runner, runner_phase_t'low); + set_active_python_runner(runner, false); + + set(runner.p_data, test_case_names_idx, to_integer(integer_vector_ptr_t'(new_integer_vector_ptr))); + + set(runner.p_data, n_test_cases_idx, unknown_num_of_test_cases); + set(runner.p_data, active_test_case_index_idx, 1); + set(runner.p_data, test_suite_completed_idx, to_integer(false)); + set(runner.p_data, test_suite_iteration_idx, 0); + + set(runner.p_data, run_test_cases_idx, to_integer(integer_vector_ptr_t'(new_integer_vector_ptr))); + set(runner.p_data, running_test_case_idx, to_integer(null_string_ptr)); + + set(runner.p_data, n_run_test_cases_idx, 0); + set(runner.p_data, has_run_since_last_loop_check_idx, to_integer(true)); + set(runner.p_data, run_all_idx, to_integer(true)); + set(runner.p_data, test_case_iteration_idx, 0); + set(runner.p_data, test_case_exit_after_error_idx, to_integer(false)); + set(runner.p_data, test_suite_exit_after_error_idx, to_integer(false)); + set(runner.p_data, runner_cfg_idx, to_integer(new_string_ptr(str_pool, runner_cfg_default))); + + set(runner.p_data, disable_simulation_exit_idx, to_integer(false)); + set(runner.p_data, entry_locks_idx, to_integer(integer_vector_ptr_t'(new_integer_vector_ptr(n_legal_phases)))); + set(runner.p_data, exit_locks_idx, to_integer(integer_vector_ptr_t'(new_integer_vector_ptr(n_legal_phases)))); + set(runner.p_data, timeout_idx, to_integer(new_string_ptr(str_pool, encode(0 ns)))); + end; + + procedure set_active_python_runner(runner : runner_t; value : boolean) is + begin + set(runner.p_data, active_python_runner_idx, to_integer(value)); + end; + + impure function has_active_python_runner(runner : runner_t) return boolean is + begin + return get(runner.p_data, active_python_runner_idx) = 1; + end function; + + procedure lock_entry(runner : runner_t; phase : runner_legal_phase_t) is + constant entry_locks : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, entry_locks_idx)); + constant idx : natural := runner_legal_phase_t'pos(phase) - runner_legal_phase_t'pos(runner_legal_phase_t'low); + constant n_locks : natural := get(entry_locks, idx); + begin + set(entry_locks, idx, n_locks + 1); + end; + + procedure unlock_entry(runner : runner_t; phase : runner_legal_phase_t) is + constant entry_locks : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, entry_locks_idx)); + constant idx : natural := runner_legal_phase_t'pos(phase) - runner_legal_phase_t'pos(runner_legal_phase_t'low); + constant n_locks : natural := get(entry_locks, idx); + begin + failure_if(runner_trace_logger, n_locks = 0, + "No locks to unlock on " & replace(runner_legal_phase_t'image(phase), "_", " ") & " entry gate."); + set(entry_locks, idx, n_locks - 1); + end; + + impure function entry_is_locked(runner : runner_t; phase : runner_legal_phase_t) return boolean is + constant entry_locks : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, entry_locks_idx)); + constant idx : natural := runner_legal_phase_t'pos(phase) - runner_legal_phase_t'pos(runner_legal_phase_t'low); + constant n_locks : natural := get(entry_locks, idx); + begin + return n_locks > 0; + end; + + procedure lock_exit(runner : runner_t; phase : runner_legal_phase_t) is + constant exit_locks : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, exit_locks_idx)); + constant idx : natural := runner_legal_phase_t'pos(phase) - runner_legal_phase_t'pos(runner_legal_phase_t'low); + constant n_locks : natural := get(exit_locks, idx); + begin + set(exit_locks, idx, n_locks + 1); + end; + + procedure unlock_exit(runner : runner_t; phase : runner_legal_phase_t) is + constant exit_locks : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, exit_locks_idx)); + constant idx : natural := runner_legal_phase_t'pos(phase) - runner_legal_phase_t'pos(runner_legal_phase_t'low); + constant n_locks : natural := get(exit_locks, idx); + begin + failure_if(runner_trace_logger, n_locks = 0, + "No locks to unlock on " & replace(runner_legal_phase_t'image(phase), "_", " ") & " exit gate."); + set(exit_locks, idx, n_locks - 1); + end; + + impure function exit_is_locked(runner : runner_t; phase : runner_legal_phase_t) return boolean is + constant exit_locks : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, exit_locks_idx)); + constant idx : natural := runner_legal_phase_t'pos(phase) - runner_legal_phase_t'pos(runner_legal_phase_t'low); + constant n_locks : natural := get(exit_locks, idx); + begin + return n_locks > 0; + end; + + procedure set_phase(runner : runner_t; new_phase : runner_phase_t) is + begin + set(runner.p_data, runner_phase_idx, runner_phase_t'pos(new_phase)); + end; + + impure function get_phase(runner : runner_t) return runner_phase_t is + begin + return runner_phase_t'val(get(runner.p_data, runner_phase_idx)); + end; + + procedure set_test_case_name(runner : runner_t; index : positive; new_name : string) is + constant test_case_names : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, test_case_names_idx)); + variable test_case_name : string_ptr_t; + begin + if index-1 >= length(test_case_names) then + resize(test_case_names, index, value => to_integer(null_string_ptr)); + end if; + + test_case_name := to_string_ptr(get(test_case_names, index-1)); + + if test_case_name = null_string_ptr then + test_case_name := new_string_ptr(str_pool, new_name); + else + reallocate(test_case_name, new_name); + end if; + + set(test_case_names, index-1, to_integer(test_case_name)); + end; + + impure function get_test_case_name(runner : runner_t; index : positive) return string is + constant test_case_names : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, test_case_names_idx)); + variable test_case_name : string_ptr_t; + begin + -- @TODO fail instead? + if index-1 >= length(test_case_names) then + return ""; + end if; + + test_case_name := to_string_ptr(get(test_case_names, index-1)); + + if test_case_name = null_string_ptr then + return ""; + end if; + + return to_string(test_case_name); + end; + + procedure set_num_of_test_cases(runner : runner_t; new_value : integer) is + begin + set(runner.p_data, n_test_cases_idx, new_value); + end; + + procedure inc_num_of_test_cases(runner : runner_t) is + begin + set(runner.p_data, n_test_cases_idx, get_num_of_test_cases(runner) + 1); + end; + + impure function get_num_of_test_cases(runner : runner_t) return integer is + begin + return get(runner.p_data, n_test_cases_idx); + end; + + impure function get_active_test_case_index(runner : runner_t) return integer is + begin + return get(runner.p_data, active_test_case_index_idx); + end; + + procedure inc_active_test_case_index(runner : runner_t) is + begin + set(runner.p_data, active_test_case_index_idx, get_active_test_case_index(runner) + 1); + end; + + procedure set_test_suite_completed(runner : runner_t) is + begin + set(runner.p_data, test_suite_completed_idx, to_integer(true)); + end; + + impure function get_test_suite_completed(runner : runner_t) return boolean is + begin + return get(runner.p_data, test_suite_completed_idx) = 1; + end; + + impure function get_test_suite_iteration(runner : runner_t) return natural is + begin + return get(runner.p_data, test_suite_iteration_idx); + end; + + procedure inc_test_suite_iteration(runner : runner_t) is + begin + set(runner.p_data, test_suite_iteration_idx, get_test_suite_iteration(runner) + 1); + end; + + procedure set_run_test_case(runner : runner_t; index : positive; new_name : string) is + constant run_test_cases : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, run_test_cases_idx)); + variable run_test_case : string_ptr_t; + begin + if index-1 >= length(run_test_cases) then + resize(run_test_cases, index, value => to_integer(null_string_ptr)); + end if; + + run_test_case := to_string_ptr(get(run_test_cases, index-1)); + + if run_test_case = null_string_ptr then + run_test_case := new_string_ptr(str_pool, new_name); + else + reallocate(run_test_case, new_name); + end if; + + set(run_test_cases, index-1, to_integer(run_test_case)); + end; + + impure function get_run_test_case(runner : runner_t; index : positive) return string is + constant run_test_cases : integer_vector_ptr_t := to_integer_vector_ptr(get(runner.p_data, run_test_cases_idx)); + variable run_test_case : string_ptr_t; + begin + -- @TODO fail instead? + if index-1 >= length(run_test_cases) then + return ""; + end if; + + run_test_case := to_string_ptr(get(run_test_cases, index-1)); + + if run_test_case = null_string_ptr then + return ""; + end if; + + return to_string(run_test_case); + end; + + procedure set_running_test_case(runner : runner_t; new_name : string) is + variable running_test_case : string_ptr_t := to_string_ptr(get(runner.p_data, running_test_case_idx)); + begin + if running_test_case = null_string_ptr then + running_test_case := new_string_ptr(str_pool, new_name); + else + reallocate(running_test_case, new_name); + end if; + set(runner.p_data, running_test_case_idx, to_integer(running_test_case)); + end; + + impure function get_running_test_case(runner : runner_t) return string is + constant running_test_case : string_ptr_t := to_string_ptr(get(runner.p_data, running_test_case_idx)); + begin + if running_test_case = null_string_ptr then + return ""; + end if; + return to_string(running_test_case); + end; + + impure function get_num_of_run_test_cases(runner : runner_t) return natural is + begin + return get(runner.p_data, n_run_test_cases_idx); + end; + + procedure inc_num_of_run_test_cases(runner : runner_t) is + begin + set(runner.p_data, n_run_test_cases_idx, get_num_of_run_test_cases(runner) + 1); + end; + + procedure set_has_run_since_last_loop_check(runner : runner_t) is + begin + set(runner.p_data, has_run_since_last_loop_check_idx, to_integer(true)); + end; + + procedure clear_has_run_since_last_loop_check(runner : runner_t) is + begin + set(runner.p_data, has_run_since_last_loop_check_idx, to_integer(false)); + end; + + impure function get_has_run_since_last_loop_check(runner : runner_t) return boolean is + begin + return get(runner.p_data, has_run_since_last_loop_check_idx) = 1; + end; + + procedure set_run_all(runner : runner_t) is + begin + set_run_all(runner, true); + end; + + procedure set_run_all(runner : runner_t; new_value : boolean) is + begin + set(runner.p_data, run_all_idx, to_integer(new_value)); + end; + + impure function get_run_all(runner : runner_t) return boolean is + begin + return get(runner.p_data, run_all_idx) = 1; + end; + + impure function get_test_case_iteration(runner : runner_t) return natural is + begin + return get(runner.p_data, test_case_iteration_idx); + end; + + procedure inc_test_case_iteration(runner : runner_t) is + begin + set(runner.p_data, test_case_iteration_idx, get_test_case_iteration(runner) + 1); + end; + + procedure init_test_case_iteration(runner : runner_t) is + begin + set(runner.p_data, test_case_iteration_idx, 0); + end; + + procedure set_test_case_exit_after_error(runner : runner_t) is + begin + set(runner.p_data, test_case_exit_after_error_idx, to_integer(true)); + end; + + procedure clear_test_case_exit_after_error(runner : runner_t) is + begin + set(runner.p_data, test_case_exit_after_error_idx, to_integer(false)); + end; + + impure function get_test_case_exit_after_error(runner : runner_t) return boolean is + begin + return get(runner.p_data, test_case_exit_after_error_idx) = 1; + end; + + procedure set_test_suite_exit_after_error(runner : runner_t) is + begin + set(runner.p_data, test_suite_exit_after_error_idx, to_integer(true)); + end; + + procedure clear_test_suite_exit_after_error(runner : runner_t) is + begin + set(runner.p_data, test_suite_exit_after_error_idx, to_integer(false)); + end; + + impure function get_test_suite_exit_after_error(runner : runner_t) return boolean is + begin + return get(runner.p_data, test_suite_exit_after_error_idx) = 1; + end; + + procedure set_cfg(runner : runner_t; new_value : string) is + variable runner_cfg : string_ptr_t := to_string_ptr(get(runner.p_data, runner_cfg_idx)); + begin + if runner_cfg = null_string_ptr then + runner_cfg := new_string_ptr(str_pool, new_value); + else + reallocate(runner_cfg, new_value); + end if; + set(runner.p_data, runner_cfg_idx, to_integer(runner_cfg)); + end; + + impure function get_cfg(runner : runner_t) return string is + constant runner_cfg : string_ptr_t := to_string_ptr(get(runner.p_data, runner_cfg_idx)); + begin + return to_string(runner_cfg); + end; + + procedure p_disable_simulation_exit(runner : runner_t) is + begin + set(runner.p_data, disable_simulation_exit_idx, to_integer(true)); + end; + + impure function p_simulation_exit_is_disabled(runner : runner_t) return boolean is + begin + return get(runner.p_data, disable_simulation_exit_idx) = to_integer(true); + end; + + procedure set_timeout(runner : runner_t; timeout : time) is + constant new_value : string := encode(timeout); + variable timeout_ptr : string_ptr_t := to_string_ptr(get(runner.p_data, timeout_idx)); + begin + if timeout_ptr = null_string_ptr then + timeout_ptr := new_string_ptr(str_pool, new_value); + else + reallocate(timeout_ptr, new_value); + end if; + set(runner.p_data, timeout_idx, to_integer(timeout_ptr)); + end; + + impure function get_timeout(runner : runner_t) return time is + constant timeout_ptr : string_ptr_t := to_string_ptr(get(runner.p_data, timeout_idx)); + begin + return decode(to_string(timeout_ptr)); + end; + +end package body; diff --git a/vunit/vhdl/run/test/run_tests.vhd b/vunit/vhdl/run/test/run_tests.vhd index 8700692bb..ba47ee70b 100644 --- a/vunit/vhdl/run/test/run_tests.vhd +++ b/vunit/vhdl/run/test/run_tests.vhd @@ -1,881 +1,881 @@ --- This test suite verifies the VHDL test runner functionality --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -use vunit_lib.string_ops.all; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.checker_pkg.all; -use vunit_lib.check_pkg.all; -use std.textio.all; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use vunit_lib.runner_pkg.all; -use vunit_lib.core_pkg; - -library ieee; -use ieee.std_logic_1164.all; - -entity run_tests is - generic (output_path : string); -end entity; - -architecture test_fixture of run_tests is - signal start_test_process, start_test_process2 : boolean := false; - signal test_process_completed : boolean := false; - signal start_locking_process : boolean := false; - signal start_test_runner_watchdog, test_runner_watchdog_completed : boolean := false; - - impure function get_phase return runner_phase_t is - begin - return get_phase(runner_state); - end; - -begin - test_process : process is - variable t_start : time; - begin - wait until start_test_process; - t_start := now; - if get_phase /= test_suite_setup then - wait on runner until get_phase = test_suite_setup for 20 ns; - end if; - check_equal(now - t_start, 17 ns, "Expected wait on test_suite_setup phase to be 17 ns."); - t_start := now; - if get_phase /= test_case_setup then - wait on runner until get_phase = test_case_setup for 20 ns; - end if; - check_equal(now - t_start, 9 ns, "Expected wait on test_case_setup phase to be 9 ns."); - test_process_completed <= true; - wait; - end process; - - test_process2 : process is - begin - wait until start_test_process2; - lock_entry(runner, test_suite_setup); - lock_exit(runner, test_suite_setup); - wait for 7 ns; - unlock_entry(runner, test_suite_setup); - wait for 4 ns; - unlock_exit(runner, test_suite_setup); - wait; - end process; - - locking_proc1: process is - begin - wait until start_locking_process = true; - lock_entry(runner, test_runner_setup, "locking_proc1"); - lock_exit(runner, test_runner_setup, "locking_proc1"); - lock_entry(runner, test_suite_setup, "locking_proc1"); - lock_exit(runner, test_suite_setup, "locking_proc1"); - wait for 2 ns; - unlock_entry(runner, test_runner_setup, "locking_proc1"); - wait for 1 ns; - unlock_exit(runner, test_runner_setup, "locking_proc1"); - wait for 1 ns; - unlock_entry(runner, test_suite_setup, "locking_proc1"); - wait for 3 ns; - - lock_entry(runner, test_case_setup, "locking_proc1"); - lock_exit(runner, test_case_setup, "locking_proc1"); - lock_entry(runner, test_case, "locking_proc1"); - lock_exit(runner, test_case, "locking_proc1"); - lock_entry(runner, test_case_cleanup, "locking_proc1"); - lock_exit(runner, test_case_cleanup, "locking_proc1"); - lock_entry(runner, test_suite_cleanup, "locking_proc1"); - lock_exit(runner, test_suite_cleanup, "locking_proc1"); - lock_entry(runner, test_runner_cleanup, "locking_proc1"); - lock_exit(runner, test_runner_cleanup, "locking_proc1"); - - wait for 1 ns; - unlock_exit(runner, test_suite_setup, "locking_proc1"); - wait for 1 ns; - unlock_entry(runner, test_case_setup, "locking_proc1"); - wait for 2 ns; - unlock_exit(runner, test_case_setup, "locking_proc1"); - wait for 1 ns; - unlock_entry(runner, test_case, "locking_proc1"); - wait for 2 ns; - unlock_exit(runner, test_case, "locking_proc1"); - wait for 1 ns; - unlock_entry(runner, test_case_cleanup, "locking_proc1"); - wait for 2 ns; - unlock_exit(runner, test_case_cleanup, "locking_proc1"); - wait for 4 ns; - unlock_entry(runner, test_suite_cleanup, "locking_proc1"); - wait for 2 ns; - unlock_exit(runner, test_suite_cleanup, "locking_proc1"); - wait for 1 ns; - unlock_entry(runner, test_runner_cleanup, "locking_proc1"); - wait for 1 ns; - unlock_exit(runner, test_runner_cleanup, "locking_proc1"); - wait; - end process locking_proc1; - - locking_proc2: process is - begin - wait until start_locking_process = true; - wait for 6 ns; - lock_exit(runner, test_runner_cleanup, "locking_proc2"); - wait for 21 ns; - unlock_exit(runner, test_runner_cleanup, "locking_proc2"); - wait; - end process locking_proc2; - - watchdog: process is - begin - wait until start_test_runner_watchdog; - test_runner_watchdog(runner, 10 ns); - test_runner_watchdog_completed <= true; - runner(runner_exit_status_idx) <= runner_exit_with_errors; - end process watchdog; - - test_runner : process - procedure banner ( - constant s : in string) is - variable dashes : string(1 to 256) := (others => '-'); - begin - info(dashes(s'range) & LF & s & LF & dashes(s'range) & LF); - end banner; - - function to_string(value : integer) return string is - begin - return integer'image(value); - end; - - procedure test_case_setup is - begin - runner_init(runner_state); - runner(runner_exit_status_idx) <= runner_exit_with_errors; - if runner(runner_event_idx) /= runner_event then - runner(runner_event_idx) <= runner_event; - wait until runner(runner_event_idx) = runner_event; - runner(runner_event_idx) <= idle_runner; - end if; - end; - - constant c : checker_t := new_checker("checker_t", default_log_level => failure); - - procedure test_case_cleanup is - variable stat : checker_stat_t; - begin - get_checker_stat(c, stat); - reset_checker_stat(c); - - info("Number of checks: " & natural'image(stat.n_checks)); - info("Number of passing checks: " & natural'image(stat.n_passed)); - info("Number of failing checks: " & natural'image(stat.n_failed)); - - assert stat.n_failed = 0 report "Expected no failed checks" severity failure; - end; - - variable i : natural; - variable n_run_a, n_run_b, n_run_c : natural := 0; - variable t_start : time; - constant test_checker : checker_t := new_checker("test_checker"); - variable runner_cfg : line; - variable passed : boolean; - variable level : log_level_t; - variable my_checker : checker_t; - variable error_counter : natural := 0; - - begin - banner("Should extract single enabled test case from input string"); - test_case_setup; - test_runner_setup(runner, "enabled_test_cases : Should foo"); - check(c, num_of_enabled_test_cases = 1, "Expected 1 enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); - check(c, enabled("Should foo"), "Expected ""Should foo"" test case to be enabled"); - check_false(c, enabled("Should bar"), "Didn't expected ""Should bar"" test case to be enabled."); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should extract multiple enabled test cases from input string"); - test_case_setup; - test_runner_setup(runner, "enabled_test_cases : Should bar,,Should zen"); - check(c, num_of_enabled_test_cases = 2, "Expected 2 enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); - check(c, enabled("Should bar"), "Expected ""Should bar"" test case to be enabled"); - check(c, enabled("Should zen"), "Expected ""Should zen"" test case to be enabled"); - check_false(c, enabled("Should toe"), "Didn't expected ""Should zen"" test case to be enabled."); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should strip leading and trailing spaces from test case names"); - test_case_setup; - test_runner_setup(runner, "enabled_test_cases : Should one ,, Should two ,, Should three"); - check(c, num_of_enabled_test_cases = 3, "Expected 3 enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); - check(c, enabled("Should one"), "Expected ""Should one"" test case to be enabled"); - check(c, enabled("Should two"), "Expected ""Should two"" test case to be enabled"); - check(c, enabled("Should three"), "Expected ""Should three"" test case to be enabled"); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should not enable any test cases on empty input string for enabled test cases"); - test_case_setup; - test_runner_setup(runner, "enabled_test_cases:"); - check(c, num_of_enabled_test_cases = 0, "Expected 0 enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should not enable any test cases on space input string for enabled test cases"); - test_case_setup; - test_runner_setup(runner, "enabled_test_cases: "); - check(c, num_of_enabled_test_cases = 0, "Expected 0 enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should ignore test case names with only spaces"); - test_case_setup; - test_runner_setup(runner, "enabled_test_cases : Should one ,, ,, Should three"); - check(c, num_of_enabled_test_cases = 2, "Expected 2 enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); - check(c, enabled("Should one"), "Expected ""Should one"" test case to be enabled"); - check(c, enabled("Should three"), "Expected ""Should three"" test case to be enabled"); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should allow comma in test case name"); - test_case_setup; - test_runner_setup(runner, "enabled_test_cases : Should one ,,,, Should two ,, Should three"); - check(c, num_of_enabled_test_cases = 2, "Expected 2 enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); - check(c, enabled("Should one , Should two"), "Expected ""Should one , Should two"" test case to be enabled"); - check(c, enabled("Should three"), "Expected ""Should three"" test case to be enabled"); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should enable all on __all__ input string"); - test_case_setup; - test_runner_setup(runner, "enabled_test_cases : __all__"); - check(c, num_of_enabled_test_cases = unknown_num_of_test_cases_c, "Expected " & integer'image(unknown_num_of_test_cases_c) & " enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); - check(c, enabled("Should one"), "Expected ""Should one"" test case to be enabled"); - check(c, enabled("Should two"), "Expected ""Should two"" test case to be enabled"); - check(c, enabled("Should three"), "Expected ""Should three"" test case to be enabled"); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should enable all by default"); - test_case_setup; - test_runner_setup(runner); - check(c, num_of_enabled_test_cases = unknown_num_of_test_cases_c, "Expected " & integer'image(unknown_num_of_test_cases_c) & " enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); - check(c, enabled("Should one"), "Expected ""Should one"" test case to be enabled"); - check(c, enabled("Should two"), "Expected ""Should two"" test case to be enabled"); - check(c, enabled("Should three"), "Expected ""Should three"" test case to be enabled"); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should enable all on empty string"); - test_case_setup; - test_runner_setup(runner, ""); - check(c, num_of_enabled_test_cases = unknown_num_of_test_cases_c, "Expected " & integer'image(unknown_num_of_test_cases_c) & " enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); - check(c, enabled("Should one"), "Expected ""Should one"" test case to be enabled"); - check(c, enabled("Should two"), "Expected ""Should two"" test case to be enabled"); - check(c, enabled("Should three"), "Expected ""Should three"" test case to be enabled"); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Mocked logger should cause failure on test_runner_cleanup"); - test_case_setup; - mock(get_logger("parent:unmocked_logger")); - - core_pkg.mock_core_failure; - test_runner_cleanup(runner); - core_pkg.check_and_unmock_core_failure; - - unmock(get_logger("parent:unmocked_logger")); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Error log cause failure on test_runner_cleanup"); - test_case_setup; - error(get_logger("parent:my_logger"), "error message"); - core_pkg.mock_core_failure; - test_runner_cleanup(runner); - core_pkg.check_and_unmock_core_failure; - test_case_cleanup; - reset_log_count(get_logger("parent:my_logger"), error); - - --------------------------------------------------------------------------- - banner("Error log cause failure on test_runner_cleanup"); - test_case_setup; - disable_stop(get_logger("parent:my_logger"), failure); - failure(get_logger("parent:my_logger"), "failure message 1"); - failure(get_logger("parent:my_logger"), "failure message 2"); - set_stop_count(get_logger("parent:my_logger"), failure, 1); - core_pkg.mock_core_failure; - test_runner_cleanup(runner); - core_pkg.check_and_unmock_core_failure; - test_case_cleanup; - reset_log_count(get_logger("parent:my_logger"), failure); - - --------------------------------------------------------------------------- - banner("Should loop over enabled_test_case once and in order unless re-initialized."); - test_case_setup; - test_runner_setup(runner, "enabled_test_cases : Should one ,, Should two ,, Should three"); - i := 0; - while test_suite loop - case i is - when 0 => - check(c, run("Should one"), "Expected ""Should one"" to run."); - check_false(c, run("Should two"), "Didn't expect ""Should two"" to run."); - check_false(c, run("Should three"), "Didn't expect ""Should three"" to run."); - when 1 => - check_false(c, run("Should one"), "Didn't expected ""Should one"" to run."); - check(c, run("Should two"), "Expected ""Should two"" to run."); - check_false(c, run("Should three"), "Didn't expect ""Should three"" to run."); - when 2 => - check_false(c, run("Should one"), "Didn't expected ""Should one"" to run."); - check_false(c, run("Should two"), "Didn't expect ""Should two"" to run."); - check(c, run("Should three"), "Expected ""Should three"" to run."); - when others => - check(c, false, "Should be only three iterations"); - end case; - i := i + 1; - end loop; - check(c, i = 3, "Expected three iterations but got i = " & natural'image(i) & "."); - check_false(c, run("Should one"), "Didn't expect ""Should one"" to run."); - check_false(c, run("Should two"), "Didn't expect ""Should two"" to run."); - check_false(c, run("Should three"), "Didn't expect ""Should three"" to run."); - test_runner_setup(runner, "enabled_test_cases : Should one ,, Should two ,, Should three"); - while test_suite loop - check(c, run("Should one"), "Expected ""Should one"" to run."); - check_false(c, run("Should two"), "Didn't expect ""Should two"" to run."); - check_false(c, run("Should three"), "Didn't expect ""Should three"" to run."); - exit; - end loop; - - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should loop a set of test cases without repetition when all test cases are enabled."); - test_case_setup; - test_runner_setup(runner, "enabled_test_cases : __all__"); - i := 0; - while test_suite and (i < 5) loop - i := i + 1; - if run("Should a") then - n_run_a := n_run_a + 1; - elsif run("Should b") then - n_run_b := n_run_b + 1; - elsif run("Should c") then - n_run_c := n_run_c + 1; - end if; - end loop; - check_false(c, i = 5, "Too many loop iterations. Expected only 4."); - check(c, n_run_a = 1, "Expected ""Should a"" to run once but it was run " & natural'image(n_run_a) & " times."); - check(c, n_run_b = 1, "Expected ""Should b"" to run once but it was run " & natural'image(n_run_b) & " times."); - check(c, n_run_c = 1, "Expected ""Should c"" to run once but it was run " & natural'image(n_run_c) & " times."); - check_false(c, run("Should a"), "Didn't expect ""Should a"" to run."); - check_false(c, run("Should b"), "Didn't expect ""Should b"" to run."); - check_false(c, run("Should c"), "Didn't expect ""Should c"" to run."); - check_false(c, run("Should d"), "Didn't expect ""Should d"" to run."); - test_case_setup; - test_runner_setup(runner); - check(c, run("Should a"), "Expected ""Should a"" to run."); - - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should maintain correct phase when using the full run mode of operation without any early exits"); - test_case_setup; - check(c, get_phase = test_runner_entry, "Phase should be test runner entry"); - test_runner_setup(runner, "enabled_test_cases : test a,, test b"); - check(c, get_phase = test_suite_setup, "Phase should be test suite setup"); - i := 0; - while test_suite loop - check(c, get_phase = test_case_setup, "Phase should be test case setup." & " Got " & runner_phase_t'image(get_phase) & "."); - while in_test_case loop - check(c, get_phase = test_case, "Phase should be test case main." & " Got " & runner_phase_t'image(get_phase) & "."); - if i = 0 then - check_false(c, run("test b"), "Test b should not be enabled at this time."); - check(c, run("test a"), "Test a should be enabled at this time"); - else - check_false(c, run("test a"), "Test a should not be enabled at this time."); - check(c, run("test b"), "Test b should be enabled at this time"); - end if; - i := i + 1; - end loop; - end loop; - check(c, get_phase = test_suite_cleanup, "Phase should be test suite cleanup" & " Got " & runner_phase_t'image(get_phase) & "."); - p_disable_simulation_exit(runner_state); - test_runner_cleanup(runner); - check(c, get_phase = test_runner_exit, "Phase should be test runner exit" & " Got " & runner_phase_t'image(get_phase) & "."); - - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should maintain correct phase when using the full run mode of operation and there is a premature exit of a test case."); - test_case_setup; - check(c, get_phase = test_runner_entry, "Phase should be test runner entry"); - test_runner_setup(runner, "enabled_test_cases : test a,, test b"); - check(c, get_phase = test_suite_setup, "Phase should be test suite setup"); - i := 0; - while test_suite loop - check(c, get_phase = test_case_setup, "Phase should be test case setup." & " Got " & runner_phase_t'image(get_phase) & "."); - while in_test_case loop - check(c, get_phase = test_case, "Phase should be test case main." & " Got " & runner_phase_t'image(get_phase) & "."); - if i = 0 then - check_false(c, run("test b"), "Test b should not be enabled at this time."); - check(c, run("test a"), "Test a should be enabled at this time"); - i := i + 1; - exit when test_case_error(true); - else - check_false(c, run("test a"), "Test a should not be enabled at this time."); - check(c, run("test b"), "Test b should be enabled at this time"); - i := i + 1; - end if; - end loop; - check(c, get_phase = test_case_cleanup, "Phase should be test case cleanup." & " Got " & runner_phase_t'image(get_phase) & "."); - end loop; - check(c, get_phase = test_suite_cleanup, "Phase should be test suite cleanup" & " Got " & runner_phase_t'image(get_phase) & "."); - p_disable_simulation_exit(runner_state); - test_runner_cleanup(runner); - check(c, get_phase = test_runner_exit, "Phase should be test runner exit" & " Got " & runner_phase_t'image(get_phase) & "."); - - - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should maintain correct phase when using the full run mode of operation and there is a premature exit of a test suite."); - test_case_setup; - check(c, get_phase = test_runner_entry, "Phase should be test runner entry"); - test_runner_setup(runner, "enabled_test_cases : test a,, test b"); - check(c, get_phase = test_suite_setup, "Phase should be test suite setup"); - i := 0; - while test_suite loop - check(c, get_phase = test_case_setup, "Phase should be test case setup." & " Got " & runner_phase_t'image(get_phase) & "."); - while in_test_case loop - check(c, get_phase = test_case, "Phase should be test case main." & " Got " & runner_phase_t'image(get_phase) & "."); - check(c, i = 0, "The second test case should never be activated"); - check_false(c, run("test b"), "Test b should not be enabled at this time."); - check(c, run("test a"), "Test a should be enabled at this time"); - i := i + 1; - exit when test_suite_error(true); - end loop; - check(c, get_phase = test_case_cleanup, "Phase should be test case cleanup." & " Got " & runner_phase_t'image(get_phase) & "."); - end loop; - check(c, get_phase = test_suite_cleanup, "Phase should be test suite cleanup." & " Got " & runner_phase_t'image(get_phase) & "."); - p_disable_simulation_exit(runner_state); - test_runner_cleanup(runner); - check(c, get_phase = test_runner_exit, "Phase should be test runner exit" & " Got " & runner_phase_t'image(get_phase) & "."); - - test_case_cleanup; - - --------------------------------------------------------------------------- - --banner("Should be possible to exit a test case or test suite with an error message that can be caught afterwards."); - --test_case_setup; - --test_runner_setup(runner, "test a, test b"); - --check_false(c, test_exit, "Test_exit should be false before error"); - --check_false(c, test_case_exit, "Test_case_exit should be false before error"); - --check_false(c, test_suite_exit, "Test_suite_exit should be false before error"); - --loop - -- exit when test_case_error(true, "Something is wrong"); - --end loop; - --check_false(c, test_exit, "Test_exit should be false before error"); - --check_false(c, test_case_exit, "Test_case_exit should be false before error"); - --check_false(c, test_suite_exit, "Test_suite_exit should be false before error"); - - --------------------------------------------------------------------------- - banner("Should be possible to exit a test suite from the test case/suite from the test case setup code."); - test_case_setup; - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should be possible to exit a test suite from the test case/suite from the test case cleanup code."); - test_case_setup; - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should be possible to stall execution and stall the exit of a phase"); - test_case_setup; - start_test_process2 <= true; - t_start := now; - wait for 1 ns; - test_runner_setup(runner, "enabled_test_cases : test a"); - entry_gate(runner); - check(c, now - t_start = 7 ns, "Expected a 7 ns delay due to phase lock"); - t_start := now; - exit_gate(runner); - while test_suite loop - check(c, now - t_start = 4 ns, "Expected a 4 ns delay due to phase lock"); - while in_test_case loop - end loop; - end loop; - p_disable_simulation_exit(runner_state); - test_runner_cleanup(runner); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should be possible to suspend a process/procedure waiting for a specific phase"); - test_case_setup; - start_test_process <= true; - wait for 17 ns; - test_runner_setup(runner, "enabled_test_cases : test a"); - wait for 9 ns; - while test_suite loop - entry_gate(runner); - wait for 1 ns; - while in_test_case loop - end loop; - end loop; - p_disable_simulation_exit(runner_state); - test_runner_cleanup(runner); - if not test_process_completed then - wait until test_process_completed; - end if; - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Test that unlocking an unlocked phase will trigger a failure"); - test_case_setup; - mock(runner_trace_logger); - unlock_entry(runner, test_runner_setup); - unlock_exit(runner, test_runner_setup); - check_log(runner_trace_logger, "No locks to unlock on test runner setup entry gate.", failure); - check_log(runner_trace_logger, "Unlocked test runner setup phase entry gate.", trace); - check_log(runner_trace_logger, "No locks to unlock on test runner setup exit gate.", failure); - check_log(runner_trace_logger, "Unlocked test runner setup phase exit gate.", trace); - unmock(runner_trace_logger); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should be possible to read current test case name"); - test_case_setup; - test_runner_setup(runner, "enabled_test_cases : test a,, test b"); - i := 0; - while test_suite loop - while in_test_case loop - if i = 0 then - passed := active_test_case = "test a"; - check(c, passed, "Expected active test case to be ""test a"" but got " & active_test_case); - else - passed := active_test_case = "test b"; - check(c, passed, "Expected active test case to be ""test b"" but got " & active_test_case); - end if; - i := i + 1; - end loop; - end loop; - p_disable_simulation_exit(runner_state); - test_runner_cleanup(runner); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should read active test case name = "" when enabled tests are __all__"); - test_case_setup; - test_runner_setup(runner, "enabled_test_cases : __all__"); - while test_suite loop - while in_test_case loop - passed := active_test_case = ""; - check(c, passed, "Expected active test case to be """" but got " & active_test_case); - end loop; - end loop; - p_disable_simulation_exit(runner_state); - test_runner_cleanup(runner); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should run all when the runner hasn't been initialized"); - test_case_setup; - i := 0; - while test_suite loop - if run("Should a") then - i := i + 1; - elsif run("Should b") then - i := i + 1; - elsif run("Should c") then - i := i + 1; - end if; - end loop; - check(c, i = 3, "Not all test cases were run."); - p_disable_simulation_exit(runner_state); - test_runner_cleanup(runner); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should have a trace log where source of locking/unlocking commands can be logged."); - test_case_setup; - - mock(runner_trace_logger); - start_locking_process <= true; - - wait for 1 ns; - check_log(runner_trace_logger, "Locked test runner setup phase entry gate.", trace); - check_log(runner_trace_logger, "Locked test runner setup phase exit gate.", trace); - check_log(runner_trace_logger, "Locked test suite setup phase entry gate.", trace); - check_log(runner_trace_logger, "Locked test suite setup phase exit gate.", trace); - check_no_log; - - test_runner_setup(runner, "enabled_test_cases : test a"); - check_log(runner_trace_logger, "Entering test runner setup phase.", trace); - check_log(runner_trace_logger, "Halting on test runner setup phase entry gate.", trace); - check_log(runner_trace_logger, "Unlocked test runner setup phase entry gate.", trace); - check_log(runner_trace_logger, "Passed test runner setup phase entry gate.", trace); - check_log(runner_trace_logger, "Halting on test runner setup phase exit gate.", trace); - check_log(runner_trace_logger, "Unlocked test runner setup phase exit gate.", trace); - check_log(runner_trace_logger, "Passed test runner setup phase exit gate.", trace); - check_log(runner_trace_logger, "Entering test suite setup phase.", trace); - check_log(runner_trace_logger, "Halting on test suite setup phase entry gate.", trace); - check_log(runner_trace_logger, "Unlocked test suite setup phase entry gate.", trace); - check_log(runner_trace_logger, "Passed test suite setup phase entry gate.", trace); - check_no_log; - - test_suite_setup_entry_gate(runner); - wait for 1 ns; - - test_suite_setup_exit_gate(runner); - check_log(runner_trace_logger, "Passed test suite setup phase entry gate.", trace); - check_log(runner_trace_logger, "Halting on test suite setup phase exit gate.", trace); - check_log(runner_trace_logger, "Locked test runner cleanup phase exit gate.", trace); - check_log(runner_trace_logger, "Locked test case setup phase entry gate.", trace); - check_log(runner_trace_logger, "Locked test case setup phase exit gate.", trace); - check_log(runner_trace_logger, "Locked test case phase entry gate.", trace); - check_log(runner_trace_logger, "Locked test case phase exit gate.", trace); - check_log(runner_trace_logger, "Locked test case cleanup phase entry gate.", trace); - check_log(runner_trace_logger, "Locked test case cleanup phase exit gate.", trace); - check_log(runner_trace_logger, "Locked test suite cleanup phase entry gate.", trace); - check_log(runner_trace_logger, "Locked test suite cleanup phase exit gate.", trace); - check_log(runner_trace_logger, "Locked test runner cleanup phase entry gate.", trace); - check_log(runner_trace_logger, "Locked test runner cleanup phase exit gate.", trace); - check_log(runner_trace_logger, "Unlocked test suite setup phase exit gate.", trace); - check_log(runner_trace_logger, "Passed test suite setup phase exit gate.", trace); - check_no_log; - - while test_suite loop - check_only_log(runner_trace_logger, "Entering test case setup phase.", trace); - - test_case_setup_entry_gate(runner); - check_log(runner_trace_logger, "Halting on test case setup phase entry gate.", trace); - check_log(runner_trace_logger, "Unlocked test case setup phase entry gate.", trace); - check_log(runner_trace_logger, "Passed test case setup phase entry gate.", trace); - check_no_log; - - wait for 1 ns; - test_case_setup_exit_gate(runner); - check_log(runner_trace_logger, "Halting on test case setup phase exit gate.", trace); - check_log(runner_trace_logger, "Unlocked test case setup phase exit gate.", trace); - check_log(runner_trace_logger, "Passed test case setup phase exit gate.", trace); - check_no_log; - - while in_test_case loop - check_only_log(runner_trace_logger, "Entering test case phase.", trace); - - test_case_entry_gate(runner); - check_log(runner_trace_logger, "Halting on test case phase entry gate.", trace); - check_log(runner_trace_logger, "Unlocked test case phase entry gate.", trace); - check_log(runner_trace_logger, "Passed test case phase entry gate.", trace); - check_no_log; - - if run("test a") then - wait for 1 ns; - end if; - check_only_log(runner_trace_logger, "Test case: test a", info); - - test_case_exit_gate(runner); - check_log(runner_trace_logger, "Halting on test case phase exit gate.", trace); - check_log(runner_trace_logger, "Unlocked test case phase exit gate.", trace); - check_log(runner_trace_logger, "Passed test case phase exit gate.", trace); - check_no_log; - end loop; - check_only_log(runner_trace_logger, "Entering test case cleanup phase.", trace); - - test_case_cleanup_entry_gate(runner); - check_log(runner_trace_logger, "Halting on test case cleanup phase entry gate.", trace); - check_log(runner_trace_logger, "Unlocked test case cleanup phase entry gate.", trace); - check_log(runner_trace_logger, "Passed test case cleanup phase entry gate.", trace); - check_no_log; - - wait for 1 ns; - test_case_cleanup_exit_gate(runner); - - check_log(runner_trace_logger, "Halting on test case cleanup phase exit gate.", trace); - check_log(runner_trace_logger, "Unlocked test case cleanup phase exit gate.", trace); - check_log(runner_trace_logger, "Passed test case cleanup phase exit gate.", trace); - check_no_log; - end loop; - check_only_log(runner_trace_logger, "Entering test suite cleanup phase.", trace); - - test_suite_cleanup_entry_gate(runner); - check_log(runner_trace_logger, "Halting on test suite cleanup phase entry gate.", trace); - check_log(runner_trace_logger, "Unlocked test suite cleanup phase entry gate.", trace); - check_log(runner_trace_logger, "Passed test suite cleanup phase entry gate.", trace); - check_no_log; - - wait for 1 ns; - test_suite_cleanup_exit_gate(runner); - check_log(runner_trace_logger, "Halting on test suite cleanup phase exit gate.", trace); - check_log(runner_trace_logger, "Unlocked test suite cleanup phase exit gate.", trace); - check_log(runner_trace_logger, "Passed test suite cleanup phase exit gate.", trace); - check_no_log; - - core_pkg.mock_core_failure; - p_disable_simulation_exit(runner_state); - test_runner_cleanup(runner); - core_pkg.check_core_failure("Final log check failed"); - core_pkg.unmock_core_failure; - - check_log(runner_trace_logger, "Entering test runner cleanup phase.", trace); - check_log(runner_trace_logger, "Halting on test runner cleanup phase entry gate.", trace); - check_log(runner_trace_logger, "Unlocked test runner cleanup phase entry gate.", trace); - check_log(runner_trace_logger, "Passed test runner cleanup phase entry gate.", trace); - check_log(runner_trace_logger, "Halting on test runner cleanup phase exit gate.", trace); - check_log(runner_trace_logger, "Unlocked test runner cleanup phase exit gate.", trace); - check_log(runner_trace_logger, "Unlocked test runner cleanup phase exit gate.", trace); - check_log(runner_trace_logger, "Passed test runner cleanup phase exit gate.", trace); - check_log(runner_trace_logger, "Entering test runner exit phase.", trace); - unmock(runner_trace_logger); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should be possible to track (un)lock commands to file and line number"); - test_case_setup; - - test_runner_setup(runner, "enabled_test_cases : test a"); - mock(runner_trace_logger); - lock_entry(runner, test_case_setup, "me", 17, "foo1.vhd"); - lock_exit(runner, test_case_setup, "me", 18, "foo2.vhd"); - unlock_entry(runner, test_case_setup, "me", 19, "foo3.vhd"); - unlock_exit(runner, test_case_setup, "me", 20, "foo4.vhd"); - check_log(runner_trace_logger, "Locked test case setup phase entry gate.", trace, - line_num => 17, file_name => "foo1.vhd"); - check_log(runner_trace_logger, "Locked test case setup phase exit gate.", trace, - line_num => 18, file_name => "foo2.vhd"); - check_log(runner_trace_logger, "Unlocked test case setup phase entry gate.", trace, - line_num => 19, file_name => "foo3.vhd"); - check_log(runner_trace_logger, "Unlocked test case setup phase exit gate.", trace, - line_num => 20, file_name => "foo4.vhd"); - unmock(runner_trace_logger); - p_disable_simulation_exit(runner_state); - test_runner_cleanup(runner); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should be possible to identify fatal exits in cleanup code"); - test_case_setup; - test_runner_setup(runner, "enabled_test_cases : test a,, test b,, test c,, test d"); - while test_suite loop - while in_test_case loop - if run("test a") then - null; - elsif run("test b") then - exit when test_case_error(true); - elsif run("test c") then - exit when test_suite_error(true); - end if; - end loop; - check_implication(c, active_test_case = "test a", - not test_case_exit and not test_suite_exit and not test_exit, - "Didn't expect (test_case_exit, test_suite_exit, test_exit) = (" - & boolean'image(test_case_exit) & ", " & boolean'image(test_suite_exit) & ", " & boolean'image(test_exit) & ") after " & active_test_case); - check_implication(c, active_test_case = "test b", - test_case_exit and not test_suite_exit and test_exit, - "Didn't expect (test_case_exit, test_suite_exit, test_exit) = (" - & boolean'image(test_case_exit) & ", " & boolean'image(test_suite_exit) & ", " & boolean'image(test_exit) & ") after " & active_test_case); - check_implication(c, active_test_case = "test c", - not test_case_exit and test_suite_exit and test_exit, - "Didn't expect (test_case_exit, test_suite_exit, test_exit) = (" - & boolean'image(test_case_exit) & ", " & boolean'image(test_suite_exit) & ", " & boolean'image(test_exit) & ") after " & active_test_case); - check_false(c, active_test_case = "test d", "Test case d should not be executed"); - end loop; - check(c, active_test_case = "test c", "Expected test suite to end on test c"); - check(c, not test_case_exit and test_suite_exit and test_exit, - "Didn't expect (test_case_exit, test_suite_exit, test_exit) = (" - & boolean'image(test_case_exit) & ", " & boolean'image(test_suite_exit) & ", " & boolean'image(test_exit) & ") after " & active_test_case); - p_disable_simulation_exit(runner_state); - test_runner_cleanup(runner); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should be possible to time-out a test runner that is stuck"); - test_case_setup; - test_runner_setup(runner, "enabled_test_cases : test a,, test b,, test c,, test d"); - start_test_runner_watchdog <= true; - wait for 0 ns; - start_test_runner_watchdog <= false; - t_start := now; - core_pkg.mock_core_failure; - wait until test_runner_watchdog_completed for 11 ns; - check(c, test_runner_watchdog_completed and (now - t_start = 10 ns), "Test runner watchdog failed to time-out"); - core_pkg.check_and_unmock_core_failure; - - assert get_log_count(runner_trace_logger, error) = 1; - assert get_log_count(runner_trace_logger, failure) = 0; - reset_log_count(runner_trace_logger, error); - - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should be possible to externally figure out if the test runner terminated without errors."); - test_case_setup; - test_runner_setup(runner, "enabled_test_cases : test a,, test b,, test c,, test d"); - wait for 0 ns; - check_equal(c, runner(runner_exit_status_idx), runner_exit_with_errors, - "Expected exit with error status after runner setup"); - p_disable_simulation_exit(runner_state); - test_runner_cleanup(runner); - wait for 0 ns; - check_equal(c, runner(runner_exit_status_idx), runner_exit_without_errors, - "Expected exit without error status after runner cleanup"); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should be possible to read running test case when running all"); - test_case_setup; - i := 0; - while test_suite loop - check_implication(c, i = 0, running_test_case = "", "Expected running test case to be """""); - if run("Should a") then - check_equal(c, running_test_case, "Should a", "Expected running test case to be ""Should a"""); - elsif run("Should b") then - check_equal(c, running_test_case, "Should b", "Expected running test case to be ""Should b"""); - elsif run("Should c") then - check_equal(c, running_test_case, "Should c", "Expected running test case to be ""Should c"""); - else - check_equal(c, running_test_case, "", "Expected running test case to be """""); - end if; - i := i + 1; - end loop; - p_disable_simulation_exit(runner_state); - test_runner_cleanup(runner); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should be able to parse runner configuration using convenience functions"); - test_case_setup; - if runner_cfg /= null then - deallocate(runner_cfg); - end if; - write(runner_cfg, string'("active python runner : true, enabled_test_cases : foo,, bar, output path : some_dir/out, tb path : some_other_dir/test")); - check(c, active_python_runner(runner_cfg.all), "Expected active python runner to be true"); - passed := vunit_lib.run_pkg.output_path(runner_cfg.all) = "some_dir/out"; - check(c, passed, "Expected output path to be ""some_dir/out"" but got " & vunit_lib.run_pkg.output_path(runner_cfg.all)); - passed := enabled_test_cases(runner_cfg.all) = "foo, bar"; - check(c, passed, "Expected enabled_test_cases to be ""foo, bar"" but got " & enabled_test_cases(runner_cfg.all)); - passed := vunit_lib.run_pkg.tb_path(runner_cfg.all) = "some_other_dir/test"; - check(c, passed, "Expected tb path to be ""some_other_dir/test"" but got " & vunit_lib.run_pkg.tb_path(runner_cfg.all)); - - check_false(c, active_python_runner(""), "Expected active python runner to be false"); - passed := vunit_lib.run_pkg.output_path("") = ""; - check(c, passed, "Expected output path to be """" but got " & vunit_lib.run_pkg.output_path("")); - passed := enabled_test_cases("") = "__all__"; - check(c, passed, "Expected enabled_test_cases to be ""__all__"" but got " & enabled_test_cases("")); - passed := vunit_lib.run_pkg.tb_path("") = ""; - check(c, passed, "Expected tb path to be """" but got " & vunit_lib.run_pkg.tb_path("")); - test_case_cleanup; - - --------------------------------------------------------------------------- - banner("Should recognize runner_cfg_t for backward compatibility"); - test_case_setup; - check(runner_cfg_t'("foo") = string'("foo")); - test_case_cleanup; - - --------------------------------------------------------------------------- - - core_pkg.setup(output_path & "vunit_results"); - core_pkg.test_suite_done; - core_pkg.stop(0); - wait; - end process; -end test_fixture; +-- This test suite verifies the VHDL test runner functionality +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +use vunit_lib.string_ops.all; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.check_pkg.all; +use std.textio.all; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use vunit_lib.runner_pkg.all; +use vunit_lib.core_pkg; + +library ieee; +use ieee.std_logic_1164.all; + +entity run_tests is + generic (output_path : string); +end entity; + +architecture test_fixture of run_tests is + signal start_test_process, start_test_process2 : boolean := false; + signal test_process_completed : boolean := false; + signal start_locking_process : boolean := false; + signal start_test_runner_watchdog, test_runner_watchdog_completed : boolean := false; + + impure function get_phase return runner_phase_t is + begin + return get_phase(runner_state); + end; + +begin + test_process : process is + variable t_start : time; + begin + wait until start_test_process; + t_start := now; + if get_phase /= test_suite_setup then + wait on runner until get_phase = test_suite_setup for 20 ns; + end if; + check_equal(now - t_start, 17 ns, "Expected wait on test_suite_setup phase to be 17 ns."); + t_start := now; + if get_phase /= test_case_setup then + wait on runner until get_phase = test_case_setup for 20 ns; + end if; + check_equal(now - t_start, 9 ns, "Expected wait on test_case_setup phase to be 9 ns."); + test_process_completed <= true; + wait; + end process; + + test_process2 : process is + begin + wait until start_test_process2; + lock_entry(runner, test_suite_setup); + lock_exit(runner, test_suite_setup); + wait for 7 ns; + unlock_entry(runner, test_suite_setup); + wait for 4 ns; + unlock_exit(runner, test_suite_setup); + wait; + end process; + + locking_proc1: process is + begin + wait until start_locking_process = true; + lock_entry(runner, test_runner_setup, "locking_proc1"); + lock_exit(runner, test_runner_setup, "locking_proc1"); + lock_entry(runner, test_suite_setup, "locking_proc1"); + lock_exit(runner, test_suite_setup, "locking_proc1"); + wait for 2 ns; + unlock_entry(runner, test_runner_setup, "locking_proc1"); + wait for 1 ns; + unlock_exit(runner, test_runner_setup, "locking_proc1"); + wait for 1 ns; + unlock_entry(runner, test_suite_setup, "locking_proc1"); + wait for 3 ns; + + lock_entry(runner, test_case_setup, "locking_proc1"); + lock_exit(runner, test_case_setup, "locking_proc1"); + lock_entry(runner, test_case, "locking_proc1"); + lock_exit(runner, test_case, "locking_proc1"); + lock_entry(runner, test_case_cleanup, "locking_proc1"); + lock_exit(runner, test_case_cleanup, "locking_proc1"); + lock_entry(runner, test_suite_cleanup, "locking_proc1"); + lock_exit(runner, test_suite_cleanup, "locking_proc1"); + lock_entry(runner, test_runner_cleanup, "locking_proc1"); + lock_exit(runner, test_runner_cleanup, "locking_proc1"); + + wait for 1 ns; + unlock_exit(runner, test_suite_setup, "locking_proc1"); + wait for 1 ns; + unlock_entry(runner, test_case_setup, "locking_proc1"); + wait for 2 ns; + unlock_exit(runner, test_case_setup, "locking_proc1"); + wait for 1 ns; + unlock_entry(runner, test_case, "locking_proc1"); + wait for 2 ns; + unlock_exit(runner, test_case, "locking_proc1"); + wait for 1 ns; + unlock_entry(runner, test_case_cleanup, "locking_proc1"); + wait for 2 ns; + unlock_exit(runner, test_case_cleanup, "locking_proc1"); + wait for 4 ns; + unlock_entry(runner, test_suite_cleanup, "locking_proc1"); + wait for 2 ns; + unlock_exit(runner, test_suite_cleanup, "locking_proc1"); + wait for 1 ns; + unlock_entry(runner, test_runner_cleanup, "locking_proc1"); + wait for 1 ns; + unlock_exit(runner, test_runner_cleanup, "locking_proc1"); + wait; + end process locking_proc1; + + locking_proc2: process is + begin + wait until start_locking_process = true; + wait for 6 ns; + lock_exit(runner, test_runner_cleanup, "locking_proc2"); + wait for 21 ns; + unlock_exit(runner, test_runner_cleanup, "locking_proc2"); + wait; + end process locking_proc2; + + watchdog: process is + begin + wait until start_test_runner_watchdog; + test_runner_watchdog(runner, 10 ns); + test_runner_watchdog_completed <= true; + runner(runner_exit_status_idx) <= runner_exit_with_errors; + end process watchdog; + + test_runner : process + procedure banner ( + constant s : in string) is + variable dashes : string(1 to 256) := (others => '-'); + begin + info(dashes(s'range) & LF & s & LF & dashes(s'range) & LF); + end banner; + + function to_string(value : integer) return string is + begin + return integer'image(value); + end; + + procedure test_case_setup is + begin + runner_init(runner_state); + runner(runner_exit_status_idx) <= runner_exit_with_errors; + if runner(runner_event_idx) /= runner_event then + runner(runner_event_idx) <= runner_event; + wait until runner(runner_event_idx) = runner_event; + runner(runner_event_idx) <= idle_runner; + end if; + end; + + constant c : checker_t := new_checker("checker_t", default_log_level => failure); + + procedure test_case_cleanup is + variable stat : checker_stat_t; + begin + get_checker_stat(c, stat); + reset_checker_stat(c); + + info("Number of checks: " & natural'image(stat.n_checks)); + info("Number of passing checks: " & natural'image(stat.n_passed)); + info("Number of failing checks: " & natural'image(stat.n_failed)); + + assert stat.n_failed = 0 report "Expected no failed checks" severity failure; + end; + + variable i : natural; + variable n_run_a, n_run_b, n_run_c : natural := 0; + variable t_start : time; + constant test_checker : checker_t := new_checker("test_checker"); + variable runner_cfg : line; + variable passed : boolean; + variable level : log_level_t; + variable my_checker : checker_t; + variable error_counter : natural := 0; + + begin + banner("Should extract single enabled test case from input string"); + test_case_setup; + test_runner_setup(runner, "enabled_test_cases : Should foo"); + check(c, num_of_enabled_test_cases = 1, "Expected 1 enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); + check(c, enabled("Should foo"), "Expected ""Should foo"" test case to be enabled"); + check_false(c, enabled("Should bar"), "Didn't expected ""Should bar"" test case to be enabled."); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should extract multiple enabled test cases from input string"); + test_case_setup; + test_runner_setup(runner, "enabled_test_cases : Should bar,,Should zen"); + check(c, num_of_enabled_test_cases = 2, "Expected 2 enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); + check(c, enabled("Should bar"), "Expected ""Should bar"" test case to be enabled"); + check(c, enabled("Should zen"), "Expected ""Should zen"" test case to be enabled"); + check_false(c, enabled("Should toe"), "Didn't expected ""Should zen"" test case to be enabled."); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should strip leading and trailing spaces from test case names"); + test_case_setup; + test_runner_setup(runner, "enabled_test_cases : Should one ,, Should two ,, Should three"); + check(c, num_of_enabled_test_cases = 3, "Expected 3 enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); + check(c, enabled("Should one"), "Expected ""Should one"" test case to be enabled"); + check(c, enabled("Should two"), "Expected ""Should two"" test case to be enabled"); + check(c, enabled("Should three"), "Expected ""Should three"" test case to be enabled"); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should not enable any test cases on empty input string for enabled test cases"); + test_case_setup; + test_runner_setup(runner, "enabled_test_cases:"); + check(c, num_of_enabled_test_cases = 0, "Expected 0 enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should not enable any test cases on space input string for enabled test cases"); + test_case_setup; + test_runner_setup(runner, "enabled_test_cases: "); + check(c, num_of_enabled_test_cases = 0, "Expected 0 enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should ignore test case names with only spaces"); + test_case_setup; + test_runner_setup(runner, "enabled_test_cases : Should one ,, ,, Should three"); + check(c, num_of_enabled_test_cases = 2, "Expected 2 enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); + check(c, enabled("Should one"), "Expected ""Should one"" test case to be enabled"); + check(c, enabled("Should three"), "Expected ""Should three"" test case to be enabled"); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should allow comma in test case name"); + test_case_setup; + test_runner_setup(runner, "enabled_test_cases : Should one ,,,, Should two ,, Should three"); + check(c, num_of_enabled_test_cases = 2, "Expected 2 enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); + check(c, enabled("Should one , Should two"), "Expected ""Should one , Should two"" test case to be enabled"); + check(c, enabled("Should three"), "Expected ""Should three"" test case to be enabled"); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should enable all on __all__ input string"); + test_case_setup; + test_runner_setup(runner, "enabled_test_cases : __all__"); + check(c, num_of_enabled_test_cases = unknown_num_of_test_cases_c, "Expected " & integer'image(unknown_num_of_test_cases_c) & " enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); + check(c, enabled("Should one"), "Expected ""Should one"" test case to be enabled"); + check(c, enabled("Should two"), "Expected ""Should two"" test case to be enabled"); + check(c, enabled("Should three"), "Expected ""Should three"" test case to be enabled"); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should enable all by default"); + test_case_setup; + test_runner_setup(runner); + check(c, num_of_enabled_test_cases = unknown_num_of_test_cases_c, "Expected " & integer'image(unknown_num_of_test_cases_c) & " enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); + check(c, enabled("Should one"), "Expected ""Should one"" test case to be enabled"); + check(c, enabled("Should two"), "Expected ""Should two"" test case to be enabled"); + check(c, enabled("Should three"), "Expected ""Should three"" test case to be enabled"); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should enable all on empty string"); + test_case_setup; + test_runner_setup(runner, ""); + check(c, num_of_enabled_test_cases = unknown_num_of_test_cases_c, "Expected " & integer'image(unknown_num_of_test_cases_c) & " enabled test case but got " & integer'image(num_of_enabled_test_cases) & "."); + check(c, enabled("Should one"), "Expected ""Should one"" test case to be enabled"); + check(c, enabled("Should two"), "Expected ""Should two"" test case to be enabled"); + check(c, enabled("Should three"), "Expected ""Should three"" test case to be enabled"); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Mocked logger should cause failure on test_runner_cleanup"); + test_case_setup; + mock(get_logger("parent:unmocked_logger")); + + core_pkg.mock_core_failure; + test_runner_cleanup(runner); + core_pkg.check_and_unmock_core_failure; + + unmock(get_logger("parent:unmocked_logger")); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Error log cause failure on test_runner_cleanup"); + test_case_setup; + error(get_logger("parent:my_logger"), "error message"); + core_pkg.mock_core_failure; + test_runner_cleanup(runner); + core_pkg.check_and_unmock_core_failure; + test_case_cleanup; + reset_log_count(get_logger("parent:my_logger"), error); + + --------------------------------------------------------------------------- + banner("Error log cause failure on test_runner_cleanup"); + test_case_setup; + disable_stop(get_logger("parent:my_logger"), failure); + failure(get_logger("parent:my_logger"), "failure message 1"); + failure(get_logger("parent:my_logger"), "failure message 2"); + set_stop_count(get_logger("parent:my_logger"), failure, 1); + core_pkg.mock_core_failure; + test_runner_cleanup(runner); + core_pkg.check_and_unmock_core_failure; + test_case_cleanup; + reset_log_count(get_logger("parent:my_logger"), failure); + + --------------------------------------------------------------------------- + banner("Should loop over enabled_test_case once and in order unless re-initialized."); + test_case_setup; + test_runner_setup(runner, "enabled_test_cases : Should one ,, Should two ,, Should three"); + i := 0; + while test_suite loop + case i is + when 0 => + check(c, run("Should one"), "Expected ""Should one"" to run."); + check_false(c, run("Should two"), "Didn't expect ""Should two"" to run."); + check_false(c, run("Should three"), "Didn't expect ""Should three"" to run."); + when 1 => + check_false(c, run("Should one"), "Didn't expected ""Should one"" to run."); + check(c, run("Should two"), "Expected ""Should two"" to run."); + check_false(c, run("Should three"), "Didn't expect ""Should three"" to run."); + when 2 => + check_false(c, run("Should one"), "Didn't expected ""Should one"" to run."); + check_false(c, run("Should two"), "Didn't expect ""Should two"" to run."); + check(c, run("Should three"), "Expected ""Should three"" to run."); + when others => + check(c, false, "Should be only three iterations"); + end case; + i := i + 1; + end loop; + check(c, i = 3, "Expected three iterations but got i = " & natural'image(i) & "."); + check_false(c, run("Should one"), "Didn't expect ""Should one"" to run."); + check_false(c, run("Should two"), "Didn't expect ""Should two"" to run."); + check_false(c, run("Should three"), "Didn't expect ""Should three"" to run."); + test_runner_setup(runner, "enabled_test_cases : Should one ,, Should two ,, Should three"); + while test_suite loop + check(c, run("Should one"), "Expected ""Should one"" to run."); + check_false(c, run("Should two"), "Didn't expect ""Should two"" to run."); + check_false(c, run("Should three"), "Didn't expect ""Should three"" to run."); + exit; + end loop; + + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should loop a set of test cases without repetition when all test cases are enabled."); + test_case_setup; + test_runner_setup(runner, "enabled_test_cases : __all__"); + i := 0; + while test_suite and (i < 5) loop + i := i + 1; + if run("Should a") then + n_run_a := n_run_a + 1; + elsif run("Should b") then + n_run_b := n_run_b + 1; + elsif run("Should c") then + n_run_c := n_run_c + 1; + end if; + end loop; + check_false(c, i = 5, "Too many loop iterations. Expected only 4."); + check(c, n_run_a = 1, "Expected ""Should a"" to run once but it was run " & natural'image(n_run_a) & " times."); + check(c, n_run_b = 1, "Expected ""Should b"" to run once but it was run " & natural'image(n_run_b) & " times."); + check(c, n_run_c = 1, "Expected ""Should c"" to run once but it was run " & natural'image(n_run_c) & " times."); + check_false(c, run("Should a"), "Didn't expect ""Should a"" to run."); + check_false(c, run("Should b"), "Didn't expect ""Should b"" to run."); + check_false(c, run("Should c"), "Didn't expect ""Should c"" to run."); + check_false(c, run("Should d"), "Didn't expect ""Should d"" to run."); + test_case_setup; + test_runner_setup(runner); + check(c, run("Should a"), "Expected ""Should a"" to run."); + + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should maintain correct phase when using the full run mode of operation without any early exits"); + test_case_setup; + check(c, get_phase = test_runner_entry, "Phase should be test runner entry"); + test_runner_setup(runner, "enabled_test_cases : test a,, test b"); + check(c, get_phase = test_suite_setup, "Phase should be test suite setup"); + i := 0; + while test_suite loop + check(c, get_phase = test_case_setup, "Phase should be test case setup." & " Got " & runner_phase_t'image(get_phase) & "."); + while in_test_case loop + check(c, get_phase = test_case, "Phase should be test case main." & " Got " & runner_phase_t'image(get_phase) & "."); + if i = 0 then + check_false(c, run("test b"), "Test b should not be enabled at this time."); + check(c, run("test a"), "Test a should be enabled at this time"); + else + check_false(c, run("test a"), "Test a should not be enabled at this time."); + check(c, run("test b"), "Test b should be enabled at this time"); + end if; + i := i + 1; + end loop; + end loop; + check(c, get_phase = test_suite_cleanup, "Phase should be test suite cleanup" & " Got " & runner_phase_t'image(get_phase) & "."); + p_disable_simulation_exit(runner_state); + test_runner_cleanup(runner); + check(c, get_phase = test_runner_exit, "Phase should be test runner exit" & " Got " & runner_phase_t'image(get_phase) & "."); + + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should maintain correct phase when using the full run mode of operation and there is a premature exit of a test case."); + test_case_setup; + check(c, get_phase = test_runner_entry, "Phase should be test runner entry"); + test_runner_setup(runner, "enabled_test_cases : test a,, test b"); + check(c, get_phase = test_suite_setup, "Phase should be test suite setup"); + i := 0; + while test_suite loop + check(c, get_phase = test_case_setup, "Phase should be test case setup." & " Got " & runner_phase_t'image(get_phase) & "."); + while in_test_case loop + check(c, get_phase = test_case, "Phase should be test case main." & " Got " & runner_phase_t'image(get_phase) & "."); + if i = 0 then + check_false(c, run("test b"), "Test b should not be enabled at this time."); + check(c, run("test a"), "Test a should be enabled at this time"); + i := i + 1; + exit when test_case_error(true); + else + check_false(c, run("test a"), "Test a should not be enabled at this time."); + check(c, run("test b"), "Test b should be enabled at this time"); + i := i + 1; + end if; + end loop; + check(c, get_phase = test_case_cleanup, "Phase should be test case cleanup." & " Got " & runner_phase_t'image(get_phase) & "."); + end loop; + check(c, get_phase = test_suite_cleanup, "Phase should be test suite cleanup" & " Got " & runner_phase_t'image(get_phase) & "."); + p_disable_simulation_exit(runner_state); + test_runner_cleanup(runner); + check(c, get_phase = test_runner_exit, "Phase should be test runner exit" & " Got " & runner_phase_t'image(get_phase) & "."); + + + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should maintain correct phase when using the full run mode of operation and there is a premature exit of a test suite."); + test_case_setup; + check(c, get_phase = test_runner_entry, "Phase should be test runner entry"); + test_runner_setup(runner, "enabled_test_cases : test a,, test b"); + check(c, get_phase = test_suite_setup, "Phase should be test suite setup"); + i := 0; + while test_suite loop + check(c, get_phase = test_case_setup, "Phase should be test case setup." & " Got " & runner_phase_t'image(get_phase) & "."); + while in_test_case loop + check(c, get_phase = test_case, "Phase should be test case main." & " Got " & runner_phase_t'image(get_phase) & "."); + check(c, i = 0, "The second test case should never be activated"); + check_false(c, run("test b"), "Test b should not be enabled at this time."); + check(c, run("test a"), "Test a should be enabled at this time"); + i := i + 1; + exit when test_suite_error(true); + end loop; + check(c, get_phase = test_case_cleanup, "Phase should be test case cleanup." & " Got " & runner_phase_t'image(get_phase) & "."); + end loop; + check(c, get_phase = test_suite_cleanup, "Phase should be test suite cleanup." & " Got " & runner_phase_t'image(get_phase) & "."); + p_disable_simulation_exit(runner_state); + test_runner_cleanup(runner); + check(c, get_phase = test_runner_exit, "Phase should be test runner exit" & " Got " & runner_phase_t'image(get_phase) & "."); + + test_case_cleanup; + + --------------------------------------------------------------------------- + --banner("Should be possible to exit a test case or test suite with an error message that can be caught afterwards."); + --test_case_setup; + --test_runner_setup(runner, "test a, test b"); + --check_false(c, test_exit, "Test_exit should be false before error"); + --check_false(c, test_case_exit, "Test_case_exit should be false before error"); + --check_false(c, test_suite_exit, "Test_suite_exit should be false before error"); + --loop + -- exit when test_case_error(true, "Something is wrong"); + --end loop; + --check_false(c, test_exit, "Test_exit should be false before error"); + --check_false(c, test_case_exit, "Test_case_exit should be false before error"); + --check_false(c, test_suite_exit, "Test_suite_exit should be false before error"); + + --------------------------------------------------------------------------- + banner("Should be possible to exit a test suite from the test case/suite from the test case setup code."); + test_case_setup; + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should be possible to exit a test suite from the test case/suite from the test case cleanup code."); + test_case_setup; + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should be possible to stall execution and stall the exit of a phase"); + test_case_setup; + start_test_process2 <= true; + t_start := now; + wait for 1 ns; + test_runner_setup(runner, "enabled_test_cases : test a"); + entry_gate(runner); + check(c, now - t_start = 7 ns, "Expected a 7 ns delay due to phase lock"); + t_start := now; + exit_gate(runner); + while test_suite loop + check(c, now - t_start = 4 ns, "Expected a 4 ns delay due to phase lock"); + while in_test_case loop + end loop; + end loop; + p_disable_simulation_exit(runner_state); + test_runner_cleanup(runner); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should be possible to suspend a process/procedure waiting for a specific phase"); + test_case_setup; + start_test_process <= true; + wait for 17 ns; + test_runner_setup(runner, "enabled_test_cases : test a"); + wait for 9 ns; + while test_suite loop + entry_gate(runner); + wait for 1 ns; + while in_test_case loop + end loop; + end loop; + p_disable_simulation_exit(runner_state); + test_runner_cleanup(runner); + if not test_process_completed then + wait until test_process_completed; + end if; + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Test that unlocking an unlocked phase will trigger a failure"); + test_case_setup; + mock(runner_trace_logger); + unlock_entry(runner, test_runner_setup); + unlock_exit(runner, test_runner_setup); + check_log(runner_trace_logger, "No locks to unlock on test runner setup entry gate.", failure); + check_log(runner_trace_logger, "Unlocked test runner setup phase entry gate.", trace); + check_log(runner_trace_logger, "No locks to unlock on test runner setup exit gate.", failure); + check_log(runner_trace_logger, "Unlocked test runner setup phase exit gate.", trace); + unmock(runner_trace_logger); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should be possible to read current test case name"); + test_case_setup; + test_runner_setup(runner, "enabled_test_cases : test a,, test b"); + i := 0; + while test_suite loop + while in_test_case loop + if i = 0 then + passed := active_test_case = "test a"; + check(c, passed, "Expected active test case to be ""test a"" but got " & active_test_case); + else + passed := active_test_case = "test b"; + check(c, passed, "Expected active test case to be ""test b"" but got " & active_test_case); + end if; + i := i + 1; + end loop; + end loop; + p_disable_simulation_exit(runner_state); + test_runner_cleanup(runner); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should read active test case name = "" when enabled tests are __all__"); + test_case_setup; + test_runner_setup(runner, "enabled_test_cases : __all__"); + while test_suite loop + while in_test_case loop + passed := active_test_case = ""; + check(c, passed, "Expected active test case to be """" but got " & active_test_case); + end loop; + end loop; + p_disable_simulation_exit(runner_state); + test_runner_cleanup(runner); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should run all when the runner hasn't been initialized"); + test_case_setup; + i := 0; + while test_suite loop + if run("Should a") then + i := i + 1; + elsif run("Should b") then + i := i + 1; + elsif run("Should c") then + i := i + 1; + end if; + end loop; + check(c, i = 3, "Not all test cases were run."); + p_disable_simulation_exit(runner_state); + test_runner_cleanup(runner); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should have a trace log where source of locking/unlocking commands can be logged."); + test_case_setup; + + mock(runner_trace_logger); + start_locking_process <= true; + + wait for 1 ns; + check_log(runner_trace_logger, "Locked test runner setup phase entry gate.", trace); + check_log(runner_trace_logger, "Locked test runner setup phase exit gate.", trace); + check_log(runner_trace_logger, "Locked test suite setup phase entry gate.", trace); + check_log(runner_trace_logger, "Locked test suite setup phase exit gate.", trace); + check_no_log; + + test_runner_setup(runner, "enabled_test_cases : test a"); + check_log(runner_trace_logger, "Entering test runner setup phase.", trace); + check_log(runner_trace_logger, "Halting on test runner setup phase entry gate.", trace); + check_log(runner_trace_logger, "Unlocked test runner setup phase entry gate.", trace); + check_log(runner_trace_logger, "Passed test runner setup phase entry gate.", trace); + check_log(runner_trace_logger, "Halting on test runner setup phase exit gate.", trace); + check_log(runner_trace_logger, "Unlocked test runner setup phase exit gate.", trace); + check_log(runner_trace_logger, "Passed test runner setup phase exit gate.", trace); + check_log(runner_trace_logger, "Entering test suite setup phase.", trace); + check_log(runner_trace_logger, "Halting on test suite setup phase entry gate.", trace); + check_log(runner_trace_logger, "Unlocked test suite setup phase entry gate.", trace); + check_log(runner_trace_logger, "Passed test suite setup phase entry gate.", trace); + check_no_log; + + test_suite_setup_entry_gate(runner); + wait for 1 ns; + + test_suite_setup_exit_gate(runner); + check_log(runner_trace_logger, "Passed test suite setup phase entry gate.", trace); + check_log(runner_trace_logger, "Halting on test suite setup phase exit gate.", trace); + check_log(runner_trace_logger, "Locked test runner cleanup phase exit gate.", trace); + check_log(runner_trace_logger, "Locked test case setup phase entry gate.", trace); + check_log(runner_trace_logger, "Locked test case setup phase exit gate.", trace); + check_log(runner_trace_logger, "Locked test case phase entry gate.", trace); + check_log(runner_trace_logger, "Locked test case phase exit gate.", trace); + check_log(runner_trace_logger, "Locked test case cleanup phase entry gate.", trace); + check_log(runner_trace_logger, "Locked test case cleanup phase exit gate.", trace); + check_log(runner_trace_logger, "Locked test suite cleanup phase entry gate.", trace); + check_log(runner_trace_logger, "Locked test suite cleanup phase exit gate.", trace); + check_log(runner_trace_logger, "Locked test runner cleanup phase entry gate.", trace); + check_log(runner_trace_logger, "Locked test runner cleanup phase exit gate.", trace); + check_log(runner_trace_logger, "Unlocked test suite setup phase exit gate.", trace); + check_log(runner_trace_logger, "Passed test suite setup phase exit gate.", trace); + check_no_log; + + while test_suite loop + check_only_log(runner_trace_logger, "Entering test case setup phase.", trace); + + test_case_setup_entry_gate(runner); + check_log(runner_trace_logger, "Halting on test case setup phase entry gate.", trace); + check_log(runner_trace_logger, "Unlocked test case setup phase entry gate.", trace); + check_log(runner_trace_logger, "Passed test case setup phase entry gate.", trace); + check_no_log; + + wait for 1 ns; + test_case_setup_exit_gate(runner); + check_log(runner_trace_logger, "Halting on test case setup phase exit gate.", trace); + check_log(runner_trace_logger, "Unlocked test case setup phase exit gate.", trace); + check_log(runner_trace_logger, "Passed test case setup phase exit gate.", trace); + check_no_log; + + while in_test_case loop + check_only_log(runner_trace_logger, "Entering test case phase.", trace); + + test_case_entry_gate(runner); + check_log(runner_trace_logger, "Halting on test case phase entry gate.", trace); + check_log(runner_trace_logger, "Unlocked test case phase entry gate.", trace); + check_log(runner_trace_logger, "Passed test case phase entry gate.", trace); + check_no_log; + + if run("test a") then + wait for 1 ns; + end if; + check_only_log(runner_trace_logger, "Test case: test a", info); + + test_case_exit_gate(runner); + check_log(runner_trace_logger, "Halting on test case phase exit gate.", trace); + check_log(runner_trace_logger, "Unlocked test case phase exit gate.", trace); + check_log(runner_trace_logger, "Passed test case phase exit gate.", trace); + check_no_log; + end loop; + check_only_log(runner_trace_logger, "Entering test case cleanup phase.", trace); + + test_case_cleanup_entry_gate(runner); + check_log(runner_trace_logger, "Halting on test case cleanup phase entry gate.", trace); + check_log(runner_trace_logger, "Unlocked test case cleanup phase entry gate.", trace); + check_log(runner_trace_logger, "Passed test case cleanup phase entry gate.", trace); + check_no_log; + + wait for 1 ns; + test_case_cleanup_exit_gate(runner); + + check_log(runner_trace_logger, "Halting on test case cleanup phase exit gate.", trace); + check_log(runner_trace_logger, "Unlocked test case cleanup phase exit gate.", trace); + check_log(runner_trace_logger, "Passed test case cleanup phase exit gate.", trace); + check_no_log; + end loop; + check_only_log(runner_trace_logger, "Entering test suite cleanup phase.", trace); + + test_suite_cleanup_entry_gate(runner); + check_log(runner_trace_logger, "Halting on test suite cleanup phase entry gate.", trace); + check_log(runner_trace_logger, "Unlocked test suite cleanup phase entry gate.", trace); + check_log(runner_trace_logger, "Passed test suite cleanup phase entry gate.", trace); + check_no_log; + + wait for 1 ns; + test_suite_cleanup_exit_gate(runner); + check_log(runner_trace_logger, "Halting on test suite cleanup phase exit gate.", trace); + check_log(runner_trace_logger, "Unlocked test suite cleanup phase exit gate.", trace); + check_log(runner_trace_logger, "Passed test suite cleanup phase exit gate.", trace); + check_no_log; + + core_pkg.mock_core_failure; + p_disable_simulation_exit(runner_state); + test_runner_cleanup(runner); + core_pkg.check_core_failure("Final log check failed"); + core_pkg.unmock_core_failure; + + check_log(runner_trace_logger, "Entering test runner cleanup phase.", trace); + check_log(runner_trace_logger, "Halting on test runner cleanup phase entry gate.", trace); + check_log(runner_trace_logger, "Unlocked test runner cleanup phase entry gate.", trace); + check_log(runner_trace_logger, "Passed test runner cleanup phase entry gate.", trace); + check_log(runner_trace_logger, "Halting on test runner cleanup phase exit gate.", trace); + check_log(runner_trace_logger, "Unlocked test runner cleanup phase exit gate.", trace); + check_log(runner_trace_logger, "Unlocked test runner cleanup phase exit gate.", trace); + check_log(runner_trace_logger, "Passed test runner cleanup phase exit gate.", trace); + check_log(runner_trace_logger, "Entering test runner exit phase.", trace); + unmock(runner_trace_logger); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should be possible to track (un)lock commands to file and line number"); + test_case_setup; + + test_runner_setup(runner, "enabled_test_cases : test a"); + mock(runner_trace_logger); + lock_entry(runner, test_case_setup, "me", 17, "foo1.vhd"); + lock_exit(runner, test_case_setup, "me", 18, "foo2.vhd"); + unlock_entry(runner, test_case_setup, "me", 19, "foo3.vhd"); + unlock_exit(runner, test_case_setup, "me", 20, "foo4.vhd"); + check_log(runner_trace_logger, "Locked test case setup phase entry gate.", trace, + line_num => 17, file_name => "foo1.vhd"); + check_log(runner_trace_logger, "Locked test case setup phase exit gate.", trace, + line_num => 18, file_name => "foo2.vhd"); + check_log(runner_trace_logger, "Unlocked test case setup phase entry gate.", trace, + line_num => 19, file_name => "foo3.vhd"); + check_log(runner_trace_logger, "Unlocked test case setup phase exit gate.", trace, + line_num => 20, file_name => "foo4.vhd"); + unmock(runner_trace_logger); + p_disable_simulation_exit(runner_state); + test_runner_cleanup(runner); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should be possible to identify fatal exits in cleanup code"); + test_case_setup; + test_runner_setup(runner, "enabled_test_cases : test a,, test b,, test c,, test d"); + while test_suite loop + while in_test_case loop + if run("test a") then + null; + elsif run("test b") then + exit when test_case_error(true); + elsif run("test c") then + exit when test_suite_error(true); + end if; + end loop; + check_implication(c, active_test_case = "test a", + not test_case_exit and not test_suite_exit and not test_exit, + "Didn't expect (test_case_exit, test_suite_exit, test_exit) = (" + & boolean'image(test_case_exit) & ", " & boolean'image(test_suite_exit) & ", " & boolean'image(test_exit) & ") after " & active_test_case); + check_implication(c, active_test_case = "test b", + test_case_exit and not test_suite_exit and test_exit, + "Didn't expect (test_case_exit, test_suite_exit, test_exit) = (" + & boolean'image(test_case_exit) & ", " & boolean'image(test_suite_exit) & ", " & boolean'image(test_exit) & ") after " & active_test_case); + check_implication(c, active_test_case = "test c", + not test_case_exit and test_suite_exit and test_exit, + "Didn't expect (test_case_exit, test_suite_exit, test_exit) = (" + & boolean'image(test_case_exit) & ", " & boolean'image(test_suite_exit) & ", " & boolean'image(test_exit) & ") after " & active_test_case); + check_false(c, active_test_case = "test d", "Test case d should not be executed"); + end loop; + check(c, active_test_case = "test c", "Expected test suite to end on test c"); + check(c, not test_case_exit and test_suite_exit and test_exit, + "Didn't expect (test_case_exit, test_suite_exit, test_exit) = (" + & boolean'image(test_case_exit) & ", " & boolean'image(test_suite_exit) & ", " & boolean'image(test_exit) & ") after " & active_test_case); + p_disable_simulation_exit(runner_state); + test_runner_cleanup(runner); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should be possible to time-out a test runner that is stuck"); + test_case_setup; + test_runner_setup(runner, "enabled_test_cases : test a,, test b,, test c,, test d"); + start_test_runner_watchdog <= true; + wait for 0 ns; + start_test_runner_watchdog <= false; + t_start := now; + core_pkg.mock_core_failure; + wait until test_runner_watchdog_completed for 11 ns; + check(c, test_runner_watchdog_completed and (now - t_start = 10 ns), "Test runner watchdog failed to time-out"); + core_pkg.check_and_unmock_core_failure; + + assert get_log_count(runner_trace_logger, error) = 1; + assert get_log_count(runner_trace_logger, failure) = 0; + reset_log_count(runner_trace_logger, error); + + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should be possible to externally figure out if the test runner terminated without errors."); + test_case_setup; + test_runner_setup(runner, "enabled_test_cases : test a,, test b,, test c,, test d"); + wait for 0 ns; + check_equal(c, runner(runner_exit_status_idx), runner_exit_with_errors, + "Expected exit with error status after runner setup"); + p_disable_simulation_exit(runner_state); + test_runner_cleanup(runner); + wait for 0 ns; + check_equal(c, runner(runner_exit_status_idx), runner_exit_without_errors, + "Expected exit without error status after runner cleanup"); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should be possible to read running test case when running all"); + test_case_setup; + i := 0; + while test_suite loop + check_implication(c, i = 0, running_test_case = "", "Expected running test case to be """""); + if run("Should a") then + check_equal(c, running_test_case, "Should a", "Expected running test case to be ""Should a"""); + elsif run("Should b") then + check_equal(c, running_test_case, "Should b", "Expected running test case to be ""Should b"""); + elsif run("Should c") then + check_equal(c, running_test_case, "Should c", "Expected running test case to be ""Should c"""); + else + check_equal(c, running_test_case, "", "Expected running test case to be """""); + end if; + i := i + 1; + end loop; + p_disable_simulation_exit(runner_state); + test_runner_cleanup(runner); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should be able to parse runner configuration using convenience functions"); + test_case_setup; + if runner_cfg /= null then + deallocate(runner_cfg); + end if; + write(runner_cfg, string'("active python runner : true, enabled_test_cases : foo,, bar, output path : some_dir/out, tb path : some_other_dir/test")); + check(c, active_python_runner(runner_cfg.all), "Expected active python runner to be true"); + passed := vunit_lib.run_pkg.output_path(runner_cfg.all) = "some_dir/out"; + check(c, passed, "Expected output path to be ""some_dir/out"" but got " & vunit_lib.run_pkg.output_path(runner_cfg.all)); + passed := enabled_test_cases(runner_cfg.all) = "foo, bar"; + check(c, passed, "Expected enabled_test_cases to be ""foo, bar"" but got " & enabled_test_cases(runner_cfg.all)); + passed := vunit_lib.run_pkg.tb_path(runner_cfg.all) = "some_other_dir/test"; + check(c, passed, "Expected tb path to be ""some_other_dir/test"" but got " & vunit_lib.run_pkg.tb_path(runner_cfg.all)); + + check_false(c, active_python_runner(""), "Expected active python runner to be false"); + passed := vunit_lib.run_pkg.output_path("") = ""; + check(c, passed, "Expected output path to be """" but got " & vunit_lib.run_pkg.output_path("")); + passed := enabled_test_cases("") = "__all__"; + check(c, passed, "Expected enabled_test_cases to be ""__all__"" but got " & enabled_test_cases("")); + passed := vunit_lib.run_pkg.tb_path("") = ""; + check(c, passed, "Expected tb path to be """" but got " & vunit_lib.run_pkg.tb_path("")); + test_case_cleanup; + + --------------------------------------------------------------------------- + banner("Should recognize runner_cfg_t for backward compatibility"); + test_case_setup; + check(runner_cfg_t'("foo") = string'("foo")); + test_case_cleanup; + + --------------------------------------------------------------------------- + + core_pkg.setup(output_path & "vunit_results"); + core_pkg.test_suite_done; + core_pkg.stop(0); + wait; + end process; +end test_fixture; diff --git a/vunit/vhdl/run/test/tb_run.vhd b/vunit/vhdl/run/test/tb_run.vhd index fba46f99d..6515f3357 100644 --- a/vunit/vhdl/run/test/tb_run.vhd +++ b/vunit/vhdl/run/test/tb_run.vhd @@ -1,23 +1,23 @@ --- This test suite verifies the VHDL test runner functionality --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -entity tb_run is - generic ( - runner_cfg : string; - output_path : string); -end entity tb_run; - -architecture tb of tb_run is -begin - -- Instantiates tests in submodule to avoid unwanted scanning of if run("") - -- This is since we are actually testing those functions themselves here not - -- using them to test other things. - tests : entity work.run_tests - generic map ( - output_path => output_path); -end architecture; +-- This test suite verifies the VHDL test runner functionality +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +entity tb_run is + generic ( + runner_cfg : string; + output_path : string); +end entity tb_run; + +architecture tb of tb_run is +begin + -- Instantiates tests in submodule to avoid unwanted scanning of if run("") + -- This is since we are actually testing those functions themselves here not + -- using them to test other things. + tests : entity work.run_tests + generic map ( + output_path => output_path); +end architecture; diff --git a/vunit/vhdl/run/test/tb_watchdog.vhd b/vunit/vhdl/run/test/tb_watchdog.vhd index caa776799..d4f1fd6d4 100644 --- a/vunit/vhdl/run/test/tb_watchdog.vhd +++ b/vunit/vhdl/run/test/tb_watchdog.vhd @@ -1,65 +1,65 @@ --- This test suite verifies the VHDL test runner functionality --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library vunit_lib; -use vunit_lib.log_levels_pkg.all; -use vunit_lib.logger_pkg.all; -use vunit_lib.check_pkg.all; -use vunit_lib.run_types_pkg.all; -use vunit_lib.run_pkg.all; -use vunit_lib.runner_pkg.all; - -entity tb_watchdog is - generic ( - runner_cfg : string); -end entity; - -architecture tb of tb_watchdog is -begin - main : process - begin - test_runner_setup(runner, runner_cfg); - if run("test watchdog no timeout") then - wait for 1 ns; - - elsif run("test watchdog timeout") then - mock(runner_trace_logger, error); - wait for 2 ns; - wait for 0 ns; - check_only_log(runner_trace_logger, "Test runner timeout after " & time'image(2 ns) & ".", error); - unmock(runner_trace_logger); - - elsif run("test setting timeout") then - set_timeout(runner, 10 ns); - wait for 9 ns; - mock(runner_trace_logger, error); - wait for 1 ns; - wait for 0 ns; - check_only_log(runner_trace_logger, "Test runner timeout after " & time'image(10 ns) & ".", error); - unmock(runner_trace_logger); - - elsif run("test setting timeout several times") then - set_timeout(runner, 10 ns); - wait for 9 ns; - set_timeout(runner, 100 ns); - wait for 99 ns; - mock(runner_trace_logger, error); - wait for 1 ns; - wait for 0 ns; - check_only_log(runner_trace_logger, "Test runner timeout after " & time'image(100 ns) & ".", error); - unmock(runner_trace_logger); - - - end if; - test_runner_cleanup(runner); - end process; - - test_runner_watchdog(runner, 2 ns, - do_runner_cleanup => false); - -end architecture; +-- This test suite verifies the VHDL test runner functionality +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.run_types_pkg.all; +use vunit_lib.run_pkg.all; +use vunit_lib.runner_pkg.all; + +entity tb_watchdog is + generic ( + runner_cfg : string); +end entity; + +architecture tb of tb_watchdog is +begin + main : process + begin + test_runner_setup(runner, runner_cfg); + if run("test watchdog no timeout") then + wait for 1 ns; + + elsif run("test watchdog timeout") then + mock(runner_trace_logger, error); + wait for 2 ns; + wait for 0 ns; + check_only_log(runner_trace_logger, "Test runner timeout after " & time'image(2 ns) & ".", error); + unmock(runner_trace_logger); + + elsif run("test setting timeout") then + set_timeout(runner, 10 ns); + wait for 9 ns; + mock(runner_trace_logger, error); + wait for 1 ns; + wait for 0 ns; + check_only_log(runner_trace_logger, "Test runner timeout after " & time'image(10 ns) & ".", error); + unmock(runner_trace_logger); + + elsif run("test setting timeout several times") then + set_timeout(runner, 10 ns); + wait for 9 ns; + set_timeout(runner, 100 ns); + wait for 99 ns; + mock(runner_trace_logger, error); + wait for 1 ns; + wait for 0 ns; + check_only_log(runner_trace_logger, "Test runner timeout after " & time'image(100 ns) & ".", error); + unmock(runner_trace_logger); + + + end if; + test_runner_cleanup(runner); + end process; + + test_runner_watchdog(runner, 2 ns, + do_runner_cleanup => false); + +end architecture; diff --git a/vunit/vhdl/string_ops/run.py b/vunit/vhdl/string_ops/run.py index 1e1503aeb..de852e09a 100644 --- a/vunit/vhdl/string_ops/run.py +++ b/vunit/vhdl/string_ops/run.py @@ -1,17 +1,17 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit - -root = dirname(__file__) -common_path = join(root, "..", "common", "test") - -ui = VUnit.from_argv() -lib = ui.add_library("lib") -lib.add_source_files(join(root, "test", "*.vhd")) - -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit + +root = dirname(__file__) +common_path = join(root, "..", "common", "test") + +ui = VUnit.from_argv() +lib = ui.add_library("lib") +lib.add_source_files(join(root, "test", "*.vhd")) + +ui.main() diff --git a/vunit/vhdl/string_ops/src/string_ops.vhd b/vunit/vhdl/string_ops/src/string_ops.vhd index 867df5f02..d79ece037 100644 --- a/vunit/vhdl/string_ops/src/string_ops.vhd +++ b/vunit/vhdl/string_ops/src/string_ops.vhd @@ -1,708 +1,708 @@ --- This package contains useful string operations --- --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use std.textio.all; -use ieee.numeric_std.all; - -package string_ops is - type line_vector is array (natural range <>) of line; - type lines_t is access line_vector; - - function count ( - constant s : string; - constant substring : string; - constant start : natural := 0; - constant stop : natural := 0) - return natural; - function count ( - constant s : string; - constant char : character := ' '; - constant start : natural := 0; - constant stop : natural := 0) - return natural; - function find ( - constant s : string; - constant substring : string; - constant start : natural := 0; - constant stop : natural := 0) - return natural; - function find ( - constant s : string; - constant char : character; - constant start : natural := 0; - constant stop : natural := 0) - return natural; - function strip ( - str : string; - chars : string := " ") - return string; - function rstrip ( - str : string; - chars : string := " ") - return string; - function lstrip ( - str : string; - chars : string := " ") - return string; - function image ( - constant data : std_logic_vector) - return string; - function hex_image ( - constant data : std_logic_vector) - return string; - function replace ( - constant s : string; - constant old_segment : character; - constant new_segment : character; - constant cnt : in natural := natural'high) - return string; - function replace ( - constant s : string; - constant old_segment : string; - constant new_segment : character; - constant cnt : in natural := natural'high) - return string; - function replace ( - constant s : string; - constant old_segment : character; - constant new_segment : string; - constant cnt : in natural := natural'high) - return string; - function replace ( - constant s : string; - constant old_segment : string; - constant new_segment : string; - constant cnt : in natural := natural'high) - return string; - function title ( - constant s : string) - return string; - function upper ( - constant s : string) - return string; - function lower ( - constant s : string) - return string; - impure function split ( - constant s : string; - constant sep : string; - constant max_split : integer := -1) - return lines_t; - function to_integer_string ( - constant value : unsigned) - return string; - function to_integer_string ( - constant value : signed) - return string; - function to_integer_string ( - constant value : std_logic_vector) - return string; - function to_nibble_string ( - constant value : unsigned) - return string; - function to_nibble_string ( - constant value : std_logic_vector) - return string; - function to_nibble_string ( - constant value : signed) - return string; -end package; - -package body string_ops is - function offset ( - constant s : string; - constant index : natural) - return natural is - begin - if s'ascending then - return index - s'left; - else - return s'left - index; - end if; - end function offset; - - function index ( - constant s : string; - constant offset : natural) - return positive is - begin - if s'ascending then - return s'left + offset; - else - return s'left - offset; - end if; - end function index; - - function left_of_range ( - constant s : string; - constant index : natural) - return boolean is - begin - if s'ascending then - return (index < s'left); - else - return (index > s'left); - end if; - end function left_of_range; - - function right_of_range ( - constant s : string; - constant index : natural) - return boolean is - begin - if s'ascending then - return (index > s'right); - else - return (index < s'right); - end if; - end function right_of_range; - - function in_range ( - constant s : string; - constant index : natural) - return boolean is - begin - return not left_of_range(s, index) and not right_of_range(s, index); - end function in_range; - - function slice ( - constant s : string; - constant offset : natural; - constant length : natural) - return string is - begin - if s'ascending then - return s(s'left + offset to s'left + offset + length - 1); - else - return s(s'left - offset downto s'left -offset - length + 1); - end if; - end function slice; - - function image ( - constant data : std_logic_vector) - return string is - variable ret_val : string(1 to data'length); - begin - for i in ret_val'range loop - if data'ascending then - ret_val(i) := std_logic'image(data(data'left + i - 1))(2); - else - ret_val(i) := std_logic'image(data(data'left - i + 1))(2); - end if; - end loop; - return ret_val; - end; - - function hex_image ( - constant data : std_logic_vector) - return string is - type character_array is array (natural range <>) of character; - variable ret_val : string(1 to (data'length + 3)/4 + 3); - variable data_extended : std_logic_vector((((data'length + 3)/4)*4)-1 downto 0) := (others => '0'); - variable j: integer; - variable meta_value_detected : boolean; - constant hex_characters : character_array(0 to 15) := ('0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'); - begin - data_extended(data'length-1 downto 0) := data; - j := 0; - for i in ret_val'right - 1 downto 3 loop - meta_value_detected := false; - for k in j+3 downto j loop - if (data_extended(k) /= '1') and (data_extended(k) /= '0') then - meta_value_detected := true; - end if; - end loop; - if meta_value_detected then - ret_val(i) := 'X'; - else - ret_val(i) := hex_characters(to_integer(unsigned(data_extended(j+3 downto j)))); - end if; - j := j + 4; - end loop; - ret_val(1 to 2) := "x"""; - ret_val(ret_val'right) := '"'; - return ret_val; - end; - - function strip ( - str : string; - chars : string; - lstrip : boolean; - rstrip : boolean) - return string is - variable start : integer := str'left; - variable stop : integer := str'right; - begin - if str = "" then - return ""; - end if; - - if lstrip then - for i in str'range loop - start := i; - exit when count(chars, str(i)) = 0; - end loop; - - if count(chars, str(start)) > 0 then - return ""; - end if; - end if; - - if rstrip then - for i in str'reverse_range loop - stop := i; - exit when count(chars, str(i)) = 0; - end loop; - - if count(chars, str(stop)) > 0 then - return ""; - end if; - end if; - - if str'ascending then - return str(start to stop); - else - return str(start downto stop); - end if; - - end strip; - - function strip ( - str : string; - chars : string := " ") - return string is - begin - return strip(str, chars, true, true); - end strip; - - function rstrip ( - str : string; - chars : string := " ") - return string is - begin - return strip(str, chars, false, true); - end rstrip; - - function lstrip ( - str : string; - chars : string := " ") - return string is - begin - return strip(str, chars, true, false); - end lstrip; - - function replace ( - constant s : string; - constant old_segment : string; - constant new_segment : string; - constant cnt : in natural := natural'high) - return string is - constant n_occurences : natural := count(s, old_segment); - function string_length_after_replace ( - -- Modelsim 10.1a has problem handling n_occurances unless it's - -- passed as a paramter to the fuction. - constant n_occurences : natural) - return natural is - variable n_replacements : natural := n_occurences; - begin - if cnt < n_replacements then - n_replacements := cnt; - end if; - return s'length + n_replacements * (new_segment'length - old_segment'length); - end; - variable ret_val : string(1 to string_length_after_replace(n_occurences)); - variable replaced_substrings : natural := 0; - variable i,j : natural := 1; - constant s_int : string(1 to s'length) := s; - begin - if n_occurences > 0 then - while i <= s_int'right - old_segment'length + 1 loop - if (s_int(i to i + old_segment'length - 1) = old_segment) and - (replaced_substrings < cnt) then - ret_val(j to j + new_segment'length - 1) := new_segment; - replaced_substrings := replaced_substrings + 1; - i := i + old_segment'length; - j := j + new_segment'length; - else - ret_val(j) := s_int(i); - j := j + 1; - i := i + 1; - end if; - end loop; - ret_val(j to j + s_int'right - i) := s_int(i to s_int'right); - else - ret_val := s_int; - end if; - - return ret_val; - end replace; - - function replace ( - constant s : string; - constant old_segment : string; - constant new_segment : character; - constant cnt : in natural := natural'high) - return string is - variable new_segment_str : string(1 to 1); - begin - new_segment_str(1) := new_segment; - return replace(s, old_segment, new_segment_str, cnt); - end replace; - - function replace ( - constant s : string; - constant old_segment : character; - constant new_segment : string; - constant cnt : in natural := natural'high) - return string is - variable old_segment_str : string(1 to 1); - begin - old_segment_str(1) := old_segment; - return replace(s, old_segment_str, new_segment, cnt); - end replace; - - function replace ( - constant s : string; - constant old_segment : character; - constant new_segment : character; - constant cnt : in natural := natural'high) - return string is - variable old_segment_str, new_segment_str : string(1 to 1); - begin - old_segment_str(1) := old_segment; - new_segment_str(1) := new_segment; - return replace(s, old_segment_str, new_segment_str, cnt); - end replace; - - function title ( - constant s : string) - return string is - variable last_char : character := NUL; - variable result : string(s'range); - begin - for i in s'range loop - if (i = s'left) or (last_char = ' ') or (last_char = ht) or (last_char = cr) then - if (character'pos(s(i)) >= character'pos('a')) and (character'pos(s(i)) <= character'pos('z')) then - result(i) := character'val(character'pos(s(i)) + character'pos('A') - character'pos('a')); - else - result(i) := s(i); - end if; - else - result(i) := s(i); - end if; - last_char := s(i); - end loop; - return result; - end title; - - function upper ( - constant s : string) - return string is - variable result : string(s'range); - begin - for i in s'range loop - if (character'pos(s(i)) >= character'pos('a')) and (character'pos(s(i)) <= character'pos('z')) then - result(i) := character'val(character'pos(s(i)) + character'pos('A') - character'pos('a')); - else - result(i) := s(i); - end if; - end loop; - return result; - end upper; - - function lower ( - constant s : string) - return string is - variable result : string(s'range); - begin - for i in s'range loop - if (character'pos(s(i)) >= character'pos('A')) and (character'pos(s(i)) <= character'pos('Z')) then - result(i) := character'val(character'pos(s(i)) + character'pos('a') - character'pos('A')); - else - result(i) := s(i); - end if; - end loop; - return result; - end lower; - - function count ( - constant s : string; - constant char : character := ' '; - constant start : natural := 0; - constant stop : natural := 0) - return natural is - variable substring : string(1 to 1); - begin - substring(1) := char; - return count(s, substring, start, stop); - end; - - function count ( - constant s : string; - constant substring : string; - constant start : natural := 0; - constant stop : natural := 0) - return natural is - variable start_pos, stop_pos : natural; - variable n, o : natural := 0; - begin - if substring = "" then - n := s'length + 1; - elsif s = "" then - n := 0; - else - if start = 0 then - start_pos := s'left; - elsif not in_range(s, start) then - return 0; - else - start_pos := start; - end if; - - if stop = 0 then - stop_pos := s'right; - elsif not in_range(s, stop) then - return 0; - else - stop_pos := stop; - end if; - - if offset(s, start_pos) > offset(s, stop_pos) then - return 0; - end if; - - o := offset(s, start_pos); - while o <= offset(s, stop_pos) - substring'length + 1 loop - if slice(s, o, substring'length) = substring then - n := n + 1; - o := o + substring'length; - else - o := o + 1; - end if; - end loop; - - end if; - - return n; - end count; - - function find ( - constant s : string; - constant char : character; - constant start : natural := 0; - constant stop : natural := 0) - return natural is - variable substring : string(1 to 1); - begin - substring(1) := char; - return find(s, substring, start, stop); - end; - - function find ( - constant s : string; - constant substring : string; - constant start : natural := 0; - constant stop : natural := 0) - return natural is - variable start_pos, stop_pos : natural; - variable o : natural; - begin - if start = 0 or left_of_range(s, start) then - start_pos := s'left; - elsif right_of_range(s, start) then - return 0; - else - start_pos := start; - end if; - - if stop = 0 or right_of_range(s, stop) then - stop_pos := s'right; - elsif left_of_range(s, stop) then - return 0; - else - stop_pos := stop; - end if; - - if substring = "" then - return start_pos; - end if; - - if s = "" then - return 0; - end if; - - o := offset(s, start_pos); - while o <= offset(s, stop_pos) - substring'length + 1 loop - if slice(s, o, substring'length) = substring then - return index(s, o); - end if; - - o := o + 1; - end loop; - - return 0; - - end find; - - impure function split ( - constant s : string; - constant sep : string; - constant max_split : integer := -1) - return lines_t is - variable ret_val : lines_t; - variable ret_val_index : natural := 0; - variable previous_sep_index : natural := 0; - variable i, n_splits : natural := 0; - constant s_int : string(1 to s'length) := s; - begin - if (count(s_int, sep) <= max_split) or (max_split = -1) then - ret_val := new line_vector(0 to count(s_int, sep)); - else - ret_val := new line_vector(0 to max_split); - end if; - - i := 1; - while i <= s_int'length - sep'length + 1 loop - exit when n_splits = max_split; - if s_int(i to i + sep'length - 1) = sep then - n_splits := n_splits + 1; - write(ret_val(ret_val_index), s_int(previous_sep_index + 1 to i - 1)); - ret_val_index := ret_val_index + 1; - previous_sep_index := i + sep'length - 1; - if sep'length = 0 then - i := i + 1; - else - i := i + sep'length; - end if; - else - i := i + 1; - end if; - end loop; - write(ret_val(ret_val_index), s_int(previous_sep_index + 1 to s_int'length)); - return ret_val; - end split; - - -- @TODO: Remove this division function when Aldec issue SPT72991 has been solved - function divide_by_10 ( - constant n : unsigned) - return unsigned is - variable r, q : unsigned(n'length - 1 downto 0); - begin - q := (others => '0'); - r := (others => '0'); - for i in integer(n'length) - 1 downto 0 loop - r := r sll 1; - r(0) := n(i); - if r >= 10 then - r := r - 10; - q(i) := '1'; - end if; - end loop; - return q; - end function divide_by_10; - - function to_integer_string ( - constant value : unsigned) - return string is - variable ret_val : string(1 to integer(0.302*real(value'length) + 1.0)); - variable index : integer := ret_val'right; - variable last_digit, quotient : unsigned(value'length - 1 downto 0); - begin - if is_x(std_logic_vector(value)) then - return "NaN"; - end if; - - if value = (value'range => '0') then - return "0"; - end if; - - if value'length < 32 then - return integer'image(to_integer(value)); - end if; - - quotient := value; - while quotient /= (quotient'range => '0') loop - last_digit := quotient mod 10; - quotient := divide_by_10(quotient); - ret_val(index to index) := integer'image(to_integer(last_digit(3 downto 0))); - index := index - 1; - end loop; - - return ret_val(index + 1 to ret_val'right); - end function to_integer_string; - - function to_integer_string ( - constant value : std_logic_vector) - return string is - begin - return to_integer_string(unsigned(value)); - end; - - function to_integer_string ( - constant value : signed) - return string is - constant value_internal: signed(value'length - 1 downto 0) := value; - variable value_internal_extended: signed(value'length downto 0); - begin - if is_x(std_logic_vector(value)) then - return "NaN"; - end if; - - if value'length <= 32 then - return integer'image(to_integer(value)); - end if; - - if value_internal(value_internal'left) = '0' then - return to_integer_string(unsigned(value_internal(value_internal'left - 1 downto 0))); - end if; - - -- Negate and use the function for unsigned. Extend one bit to ensure the - -- negated value fits. - value_internal_extended(value_internal'range) := value_internal; - value_internal_extended(value_internal_extended'left) := value_internal(value_internal'left); - value_internal_extended := not(value_internal_extended) + 1; - - return "-" & to_integer_string(unsigned(value_internal_extended)); - end function to_integer_string; - - function to_nibble_string ( - constant value : unsigned) - return string is - constant value_i : unsigned(value'length downto 1) := value; - variable ret_val : string(1 to (value'length + (value'length - 1)/4)); - variable index : natural := 1; - begin - for i in value_i'range loop - if (i mod 4 = 0) and (i /= value_i'left) then - ret_val(index) := '_'; - index := index + 1; - end if; - ret_val(index) := std_logic'image(value_i(i))(2); - index := index + 1; - end loop; - - return ret_val; - end function to_nibble_string; - - function to_nibble_string ( - constant value : std_logic_vector) - return string is - begin - return to_nibble_string(unsigned(value)); - end function to_nibble_string; - - function to_nibble_string ( - constant value : signed) - return string is - begin - return to_nibble_string(unsigned(value)); - end function to_nibble_string; - -end package body; +-- This package contains useful string operations +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use std.textio.all; +use ieee.numeric_std.all; + +package string_ops is + type line_vector is array (natural range <>) of line; + type lines_t is access line_vector; + + function count ( + constant s : string; + constant substring : string; + constant start : natural := 0; + constant stop : natural := 0) + return natural; + function count ( + constant s : string; + constant char : character := ' '; + constant start : natural := 0; + constant stop : natural := 0) + return natural; + function find ( + constant s : string; + constant substring : string; + constant start : natural := 0; + constant stop : natural := 0) + return natural; + function find ( + constant s : string; + constant char : character; + constant start : natural := 0; + constant stop : natural := 0) + return natural; + function strip ( + str : string; + chars : string := " ") + return string; + function rstrip ( + str : string; + chars : string := " ") + return string; + function lstrip ( + str : string; + chars : string := " ") + return string; + function image ( + constant data : std_logic_vector) + return string; + function hex_image ( + constant data : std_logic_vector) + return string; + function replace ( + constant s : string; + constant old_segment : character; + constant new_segment : character; + constant cnt : in natural := natural'high) + return string; + function replace ( + constant s : string; + constant old_segment : string; + constant new_segment : character; + constant cnt : in natural := natural'high) + return string; + function replace ( + constant s : string; + constant old_segment : character; + constant new_segment : string; + constant cnt : in natural := natural'high) + return string; + function replace ( + constant s : string; + constant old_segment : string; + constant new_segment : string; + constant cnt : in natural := natural'high) + return string; + function title ( + constant s : string) + return string; + function upper ( + constant s : string) + return string; + function lower ( + constant s : string) + return string; + impure function split ( + constant s : string; + constant sep : string; + constant max_split : integer := -1) + return lines_t; + function to_integer_string ( + constant value : unsigned) + return string; + function to_integer_string ( + constant value : signed) + return string; + function to_integer_string ( + constant value : std_logic_vector) + return string; + function to_nibble_string ( + constant value : unsigned) + return string; + function to_nibble_string ( + constant value : std_logic_vector) + return string; + function to_nibble_string ( + constant value : signed) + return string; +end package; + +package body string_ops is + function offset ( + constant s : string; + constant index : natural) + return natural is + begin + if s'ascending then + return index - s'left; + else + return s'left - index; + end if; + end function offset; + + function index ( + constant s : string; + constant offset : natural) + return positive is + begin + if s'ascending then + return s'left + offset; + else + return s'left - offset; + end if; + end function index; + + function left_of_range ( + constant s : string; + constant index : natural) + return boolean is + begin + if s'ascending then + return (index < s'left); + else + return (index > s'left); + end if; + end function left_of_range; + + function right_of_range ( + constant s : string; + constant index : natural) + return boolean is + begin + if s'ascending then + return (index > s'right); + else + return (index < s'right); + end if; + end function right_of_range; + + function in_range ( + constant s : string; + constant index : natural) + return boolean is + begin + return not left_of_range(s, index) and not right_of_range(s, index); + end function in_range; + + function slice ( + constant s : string; + constant offset : natural; + constant length : natural) + return string is + begin + if s'ascending then + return s(s'left + offset to s'left + offset + length - 1); + else + return s(s'left - offset downto s'left -offset - length + 1); + end if; + end function slice; + + function image ( + constant data : std_logic_vector) + return string is + variable ret_val : string(1 to data'length); + begin + for i in ret_val'range loop + if data'ascending then + ret_val(i) := std_logic'image(data(data'left + i - 1))(2); + else + ret_val(i) := std_logic'image(data(data'left - i + 1))(2); + end if; + end loop; + return ret_val; + end; + + function hex_image ( + constant data : std_logic_vector) + return string is + type character_array is array (natural range <>) of character; + variable ret_val : string(1 to (data'length + 3)/4 + 3); + variable data_extended : std_logic_vector((((data'length + 3)/4)*4)-1 downto 0) := (others => '0'); + variable j: integer; + variable meta_value_detected : boolean; + constant hex_characters : character_array(0 to 15) := ('0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'); + begin + data_extended(data'length-1 downto 0) := data; + j := 0; + for i in ret_val'right - 1 downto 3 loop + meta_value_detected := false; + for k in j+3 downto j loop + if (data_extended(k) /= '1') and (data_extended(k) /= '0') then + meta_value_detected := true; + end if; + end loop; + if meta_value_detected then + ret_val(i) := 'X'; + else + ret_val(i) := hex_characters(to_integer(unsigned(data_extended(j+3 downto j)))); + end if; + j := j + 4; + end loop; + ret_val(1 to 2) := "x"""; + ret_val(ret_val'right) := '"'; + return ret_val; + end; + + function strip ( + str : string; + chars : string; + lstrip : boolean; + rstrip : boolean) + return string is + variable start : integer := str'left; + variable stop : integer := str'right; + begin + if str = "" then + return ""; + end if; + + if lstrip then + for i in str'range loop + start := i; + exit when count(chars, str(i)) = 0; + end loop; + + if count(chars, str(start)) > 0 then + return ""; + end if; + end if; + + if rstrip then + for i in str'reverse_range loop + stop := i; + exit when count(chars, str(i)) = 0; + end loop; + + if count(chars, str(stop)) > 0 then + return ""; + end if; + end if; + + if str'ascending then + return str(start to stop); + else + return str(start downto stop); + end if; + + end strip; + + function strip ( + str : string; + chars : string := " ") + return string is + begin + return strip(str, chars, true, true); + end strip; + + function rstrip ( + str : string; + chars : string := " ") + return string is + begin + return strip(str, chars, false, true); + end rstrip; + + function lstrip ( + str : string; + chars : string := " ") + return string is + begin + return strip(str, chars, true, false); + end lstrip; + + function replace ( + constant s : string; + constant old_segment : string; + constant new_segment : string; + constant cnt : in natural := natural'high) + return string is + constant n_occurences : natural := count(s, old_segment); + function string_length_after_replace ( + -- Modelsim 10.1a has problem handling n_occurances unless it's + -- passed as a paramter to the fuction. + constant n_occurences : natural) + return natural is + variable n_replacements : natural := n_occurences; + begin + if cnt < n_replacements then + n_replacements := cnt; + end if; + return s'length + n_replacements * (new_segment'length - old_segment'length); + end; + variable ret_val : string(1 to string_length_after_replace(n_occurences)); + variable replaced_substrings : natural := 0; + variable i,j : natural := 1; + constant s_int : string(1 to s'length) := s; + begin + if n_occurences > 0 then + while i <= s_int'right - old_segment'length + 1 loop + if (s_int(i to i + old_segment'length - 1) = old_segment) and + (replaced_substrings < cnt) then + ret_val(j to j + new_segment'length - 1) := new_segment; + replaced_substrings := replaced_substrings + 1; + i := i + old_segment'length; + j := j + new_segment'length; + else + ret_val(j) := s_int(i); + j := j + 1; + i := i + 1; + end if; + end loop; + ret_val(j to j + s_int'right - i) := s_int(i to s_int'right); + else + ret_val := s_int; + end if; + + return ret_val; + end replace; + + function replace ( + constant s : string; + constant old_segment : string; + constant new_segment : character; + constant cnt : in natural := natural'high) + return string is + variable new_segment_str : string(1 to 1); + begin + new_segment_str(1) := new_segment; + return replace(s, old_segment, new_segment_str, cnt); + end replace; + + function replace ( + constant s : string; + constant old_segment : character; + constant new_segment : string; + constant cnt : in natural := natural'high) + return string is + variable old_segment_str : string(1 to 1); + begin + old_segment_str(1) := old_segment; + return replace(s, old_segment_str, new_segment, cnt); + end replace; + + function replace ( + constant s : string; + constant old_segment : character; + constant new_segment : character; + constant cnt : in natural := natural'high) + return string is + variable old_segment_str, new_segment_str : string(1 to 1); + begin + old_segment_str(1) := old_segment; + new_segment_str(1) := new_segment; + return replace(s, old_segment_str, new_segment_str, cnt); + end replace; + + function title ( + constant s : string) + return string is + variable last_char : character := NUL; + variable result : string(s'range); + begin + for i in s'range loop + if (i = s'left) or (last_char = ' ') or (last_char = ht) or (last_char = cr) then + if (character'pos(s(i)) >= character'pos('a')) and (character'pos(s(i)) <= character'pos('z')) then + result(i) := character'val(character'pos(s(i)) + character'pos('A') - character'pos('a')); + else + result(i) := s(i); + end if; + else + result(i) := s(i); + end if; + last_char := s(i); + end loop; + return result; + end title; + + function upper ( + constant s : string) + return string is + variable result : string(s'range); + begin + for i in s'range loop + if (character'pos(s(i)) >= character'pos('a')) and (character'pos(s(i)) <= character'pos('z')) then + result(i) := character'val(character'pos(s(i)) + character'pos('A') - character'pos('a')); + else + result(i) := s(i); + end if; + end loop; + return result; + end upper; + + function lower ( + constant s : string) + return string is + variable result : string(s'range); + begin + for i in s'range loop + if (character'pos(s(i)) >= character'pos('A')) and (character'pos(s(i)) <= character'pos('Z')) then + result(i) := character'val(character'pos(s(i)) + character'pos('a') - character'pos('A')); + else + result(i) := s(i); + end if; + end loop; + return result; + end lower; + + function count ( + constant s : string; + constant char : character := ' '; + constant start : natural := 0; + constant stop : natural := 0) + return natural is + variable substring : string(1 to 1); + begin + substring(1) := char; + return count(s, substring, start, stop); + end; + + function count ( + constant s : string; + constant substring : string; + constant start : natural := 0; + constant stop : natural := 0) + return natural is + variable start_pos, stop_pos : natural; + variable n, o : natural := 0; + begin + if substring = "" then + n := s'length + 1; + elsif s = "" then + n := 0; + else + if start = 0 then + start_pos := s'left; + elsif not in_range(s, start) then + return 0; + else + start_pos := start; + end if; + + if stop = 0 then + stop_pos := s'right; + elsif not in_range(s, stop) then + return 0; + else + stop_pos := stop; + end if; + + if offset(s, start_pos) > offset(s, stop_pos) then + return 0; + end if; + + o := offset(s, start_pos); + while o <= offset(s, stop_pos) - substring'length + 1 loop + if slice(s, o, substring'length) = substring then + n := n + 1; + o := o + substring'length; + else + o := o + 1; + end if; + end loop; + + end if; + + return n; + end count; + + function find ( + constant s : string; + constant char : character; + constant start : natural := 0; + constant stop : natural := 0) + return natural is + variable substring : string(1 to 1); + begin + substring(1) := char; + return find(s, substring, start, stop); + end; + + function find ( + constant s : string; + constant substring : string; + constant start : natural := 0; + constant stop : natural := 0) + return natural is + variable start_pos, stop_pos : natural; + variable o : natural; + begin + if start = 0 or left_of_range(s, start) then + start_pos := s'left; + elsif right_of_range(s, start) then + return 0; + else + start_pos := start; + end if; + + if stop = 0 or right_of_range(s, stop) then + stop_pos := s'right; + elsif left_of_range(s, stop) then + return 0; + else + stop_pos := stop; + end if; + + if substring = "" then + return start_pos; + end if; + + if s = "" then + return 0; + end if; + + o := offset(s, start_pos); + while o <= offset(s, stop_pos) - substring'length + 1 loop + if slice(s, o, substring'length) = substring then + return index(s, o); + end if; + + o := o + 1; + end loop; + + return 0; + + end find; + + impure function split ( + constant s : string; + constant sep : string; + constant max_split : integer := -1) + return lines_t is + variable ret_val : lines_t; + variable ret_val_index : natural := 0; + variable previous_sep_index : natural := 0; + variable i, n_splits : natural := 0; + constant s_int : string(1 to s'length) := s; + begin + if (count(s_int, sep) <= max_split) or (max_split = -1) then + ret_val := new line_vector(0 to count(s_int, sep)); + else + ret_val := new line_vector(0 to max_split); + end if; + + i := 1; + while i <= s_int'length - sep'length + 1 loop + exit when n_splits = max_split; + if s_int(i to i + sep'length - 1) = sep then + n_splits := n_splits + 1; + write(ret_val(ret_val_index), s_int(previous_sep_index + 1 to i - 1)); + ret_val_index := ret_val_index + 1; + previous_sep_index := i + sep'length - 1; + if sep'length = 0 then + i := i + 1; + else + i := i + sep'length; + end if; + else + i := i + 1; + end if; + end loop; + write(ret_val(ret_val_index), s_int(previous_sep_index + 1 to s_int'length)); + return ret_val; + end split; + + -- @TODO: Remove this division function when Aldec issue SPT72991 has been solved + function divide_by_10 ( + constant n : unsigned) + return unsigned is + variable r, q : unsigned(n'length - 1 downto 0); + begin + q := (others => '0'); + r := (others => '0'); + for i in integer(n'length) - 1 downto 0 loop + r := r sll 1; + r(0) := n(i); + if r >= 10 then + r := r - 10; + q(i) := '1'; + end if; + end loop; + return q; + end function divide_by_10; + + function to_integer_string ( + constant value : unsigned) + return string is + variable ret_val : string(1 to integer(0.302*real(value'length) + 1.0)); + variable index : integer := ret_val'right; + variable last_digit, quotient : unsigned(value'length - 1 downto 0); + begin + if is_x(std_logic_vector(value)) then + return "NaN"; + end if; + + if value = (value'range => '0') then + return "0"; + end if; + + if value'length < 32 then + return integer'image(to_integer(value)); + end if; + + quotient := value; + while quotient /= (quotient'range => '0') loop + last_digit := quotient mod 10; + quotient := divide_by_10(quotient); + ret_val(index to index) := integer'image(to_integer(last_digit(3 downto 0))); + index := index - 1; + end loop; + + return ret_val(index + 1 to ret_val'right); + end function to_integer_string; + + function to_integer_string ( + constant value : std_logic_vector) + return string is + begin + return to_integer_string(unsigned(value)); + end; + + function to_integer_string ( + constant value : signed) + return string is + constant value_internal: signed(value'length - 1 downto 0) := value; + variable value_internal_extended: signed(value'length downto 0); + begin + if is_x(std_logic_vector(value)) then + return "NaN"; + end if; + + if value'length <= 32 then + return integer'image(to_integer(value)); + end if; + + if value_internal(value_internal'left) = '0' then + return to_integer_string(unsigned(value_internal(value_internal'left - 1 downto 0))); + end if; + + -- Negate and use the function for unsigned. Extend one bit to ensure the + -- negated value fits. + value_internal_extended(value_internal'range) := value_internal; + value_internal_extended(value_internal_extended'left) := value_internal(value_internal'left); + value_internal_extended := not(value_internal_extended) + 1; + + return "-" & to_integer_string(unsigned(value_internal_extended)); + end function to_integer_string; + + function to_nibble_string ( + constant value : unsigned) + return string is + constant value_i : unsigned(value'length downto 1) := value; + variable ret_val : string(1 to (value'length + (value'length - 1)/4)); + variable index : natural := 1; + begin + for i in value_i'range loop + if (i mod 4 = 0) and (i /= value_i'left) then + ret_val(index) := '_'; + index := index + 1; + end if; + ret_val(index) := std_logic'image(value_i(i))(2); + index := index + 1; + end loop; + + return ret_val; + end function to_nibble_string; + + function to_nibble_string ( + constant value : std_logic_vector) + return string is + begin + return to_nibble_string(unsigned(value)); + end function to_nibble_string; + + function to_nibble_string ( + constant value : signed) + return string is + begin + return to_nibble_string(unsigned(value)); + end function to_nibble_string; + +end package body; diff --git a/vunit/vhdl/string_ops/test/tb_string_ops.vhd b/vunit/vhdl/string_ops/test/tb_string_ops.vhd index ff06c477e..18464dedb 100644 --- a/vunit/vhdl/string_ops/test/tb_string_ops.vhd +++ b/vunit/vhdl/string_ops/test/tb_string_ops.vhd @@ -1,373 +1,373 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -library ieee; -use ieee.std_logic_1164.all; -use std.textio.all; -use ieee.numeric_std.all; - -library vunit_lib; -use vunit_lib.run_pkg.all; -use vunit_lib.string_ops.all; - -entity tb_string_ops is - generic (runner_cfg : string); -end entity tb_string_ops; - -architecture test_fixture of tb_string_ops is - procedure check ( - constant expr : in boolean; - constant msg : in string) is - begin - if not expr then - assert false report msg severity failure; - end if; - end procedure check; -begin - test_runner : process - -- Override "=" to make it work in Vivado (doesn't consider two empty - -- strings to be equal) - function "=" ( - constant a : string; - constant b : string) - return boolean is - - function bool_to_sign ( - constant b : boolean) - return integer is - begin - if b then - return 1; - else - return -1; - end if; - end function bool_to_sign; - - variable ret_val : boolean := true; - begin - if a'length /= b'length then - ret_val := false; - else - for i in 1 to a'length loop - ret_val := a(a'left + bool_to_sign(a'ascending) * (i - 1)) = - b(b'left + bool_to_sign(b'ascending) * (i - 1)); - exit when not ret_val; - end loop; - end if; - - return ret_val; - end function "="; - variable ascending_vector : std_logic_vector(3 to 11); - variable descending_vector : std_logic_vector(13 downto 5); - variable l : lines_t; - variable n_asserts_value, n_errors_value : integer; - constant offset_string :string(10 to 16) := "foo bar"; - constant reverse_string :string(16 downto 10) := "foo bar"; - constant reversed_vector :unsigned(16 downto 4) := "1011010101001"; - begin - test_runner_setup(runner, runner_cfg); - while test_suite loop - if run("Test strip") then - check(strip("") = "", "Strip of empty string should return an empty string. Got """ & strip("") & """."); - check(strip(" a ") = "a", "Strip should remove spaces by default. Got """ & strip(" a ") & """."); - check(strip(" ") = "", "Strip of single char string should return an empty string. Got """ & strip(" ") & """."); - check(strip(" b") = "b", "Strip should handle left-sided strip. Got """ & strip(" b") & """."); - check(strip("c ") = "c", "Strip should handle right-sided strip. Got """ & strip("c ") & """."); - check(strip("d") = "d", "Strip should not affect strings without specified chars in the end/begining. Got """ & strip("d") & """."); - check(strip(" e f ") = "e f", "Strip should not affect specified characters within the string. Got """ & strip(" e f ") & """."); - check(strip(" g ") = "g", "Strip should remove multiple instances of specified characters. Got """ & strip(" g ") & """."); - check(strip("-* h-*i-**-", "*-") = " h-*i", "Strip should remove multiple specified characters. Got """ & strip("-* h-*i-**-", "*-") & """."); - check(strip(offset_string, "fo") = " bar", "Should handle offset strings. Got """ & strip(offset_string, "fo") & """."); - check(strip(reverse_string, "fo") = " bar", "Should handle reversed strings. Got """ & strip(reverse_string, "fo") & """."); - elsif run("Test rstrip") then - check(rstrip("") = "", "rstrip of empty string should return an empty string. Got """ & rstrip("") & """."); - check(rstrip(" a ") = " a", "rstrip should remove spaces by default. Got """ & rstrip(" a ") & """."); - check(rstrip(" ") = "", "rstrip of single char string should return an empty string. Got """ & rstrip(" ") & """."); - check(rstrip("d") = "d", "rstrip should not affect strings without specified chars in the end/begining. Got """ & rstrip("d") & """."); - check(rstrip(" e f ") = " e f", "rstrip should not affect specified characters within the string. Got """ & rstrip(" e f ") & """."); - check(rstrip(" g ") = " g", "rstrip should remove multiple instances of specified characters. Got """ & rstrip(" g ") & """."); - check(rstrip("-* h-*i-**-", "*-") = "-* h-*i", "rstrip should remove multiple specified characters. Got """ & rstrip("-* h-*i-**-", "*-") & """."); - check(rstrip(offset_string, "rab") = "foo ", "Should handle offset strings. Got """ & rstrip(offset_string, "rab") & """."); - check(rstrip(reverse_string, "rab") = "foo ", "Should handle reversed strings. Got """ & rstrip(reverse_string, "rab") & """."); - - elsif run("Test lstrip") then - check(lstrip("") = "", "lstrip of empty string should return an empty string. Got """ & lstrip("") & """."); - check(lstrip(" a ") = "a ", "lstrip should remove spaces by default. Got """ & lstrip(" a ") & """."); - check(lstrip(" ") = "", "lstrip of single char string should return an empty string. Got """ & lstrip(" ") & """."); - check(lstrip("d") = "d", "lstrip should not affect strings without specified chars in the end/begining. Got """ & lstrip("d") & """."); - check(lstrip(" e f ") = "e f ", "lstrip should not affect specified characters within the string. Got """ & lstrip(" e f ") & """."); - check(lstrip(" g ") = "g ", "lstrip should remove multiple instances of specified characters. Got """ & lstrip(" g ") & """."); - check(lstrip("-* h-*i-**-", "*-") = " h-*i-**-", "lstrip should remove multiple specified characters. Got """ & lstrip("-* h-*i-**-", "*-") & """."); - check(lstrip(offset_string, "fo") = " bar", "Should handle offset strings. Got """ & lstrip(offset_string, "fo") & """."); - check(lstrip(reverse_string, "fo") = " bar", "Should handle reversed strings. Got """ & lstrip(reverse_string, "fo") & """."); - - elsif run("Test count") then - check(count("","a") = 0, "Empty string should return 0."); - check(count(" a b ") = 3, "Should count spaces by default."); - check(count(" a b ", "") = 6, "Should count an empty string between every character, at the beginning, and at the end."); - check(count("", "") = 1, "Should return 1 when counting empty string in empty string"); - check(count("hello world or hello earth", "or") = 2, "Should handle multi-character substrings"); - check(count("hello world or hello earth", 'o') = 4, "Should handle character type inputs."); - check(count("ababababa", "abab") = 2, "Should count non-overlapping occurences"); - check(count(offset_string, "o") = 2, "Should handle offset strings."); - check(count(reverse_string, "o") = 2, "Should handle reversed strings."); - check(count("ababababa", "a", 2 ,6) = 2, "Should handle parts of input string"); - check(count("aba", "a", 4 ,6) = 0, "Should not find anything outside of the string"); - check(count(offset_string, "a", 4 ,6) = 0, "Should not find anything outside of the string"); - check(count(reverse_string, "a", 12 ,4) = 0, "Should not find anything outside of the string"); - check(count("aba", "a", 3 ,1) = 0, "Should not find anything within a negative range"); - check(count("aba", "abab") = 0, "Should not find anything when substring is longer than the string"); - elsif run("Test find") then - check(find("", "") = 1, "Empty string should be found at the start"); - check(find("foo bar", "") = 1, "Empty string should be found at the start"); - check(find("", "foo") = 0, "Nothing should be found in an empty string"); - check(find("foo bar", "foo") = 1, "Should find string at the start"); - check(find("foo bar", "bar") = 5, "Should find string at the end"); - check(find("foo bar", "o b") = 3, "Should find string in the middle"); - check(find("foo bar", "foo bar") = 1, "Should find full string"); - check(find("foo bar", 'f') = 1, "Should find character at the start"); - check(find("foo bar", 'r') = 7, "Should find character at the end"); - check(find("foo bar", ' ') = 4, "Should find character in the middle"); - check(find("foo bar", "bars") = 0, "Should return 0 when string not found"); - check(find("foo bar", "foo bars") = 0, "Should return 0 when string not found"); - check(find("foo bar", 'q') = 0, "Should return 0 when character not found"); - check(find(offset_string, "") = 10, "Empty string should be found at the start on offset string"); - check(find(offset_string, "foo") = 10, "Should find string at the start on offset string"); - check(find(offset_string, "bar") = 14, "Should find string at the end on offset string"); - check(find(offset_string, "o b") = 12, "Should find string in the middle on offset string"); - check(find(reverse_string, "") = 16, "Empty string should be found at the start on reversed string"); - check(find(reverse_string, "foo") = 16, "Should find string at the start on reversed string"); - check(find(reverse_string, "bar") = 12, "Should find string at the end on reversed string"); - check(find(reverse_string, "o b") = 14, "Should find string in the middle on reversed string"); - check(find("foo bar", "oo", 2, 6) = 2, "Should find string at the start of slice"); - check(find("foo bar", "ba", 2, 6) = 5, "Should find string at the end of slice"); - check(find("foo bar", "o b", 2, 6) = 3, "Should find string in the middle of slice"); - check(find("foo bar", "", 2, 6) = 2, "Empty string should be found at the start of slice"); - check(find("foo bar", 'f', 2, 6) = 0, "Should not find anything before slice"); - check(find("foo bar", "ar", 2, 6) = 0, "Should not find anything after slice"); - check(find(offset_string, 'f', 11, 15) = 0, "Should not find anything before slice in offset string"); - check(find(offset_string, "ar", 11, 15) = 0, "Should not find anything after slice in offset string"); - check(find(reverse_string, 'f', 15, 11) = 0, "Should not find anything before slice in reversed string"); - check(find(reverse_string, "ar", 15, 11) = 0, "Should not find anything after slice in reversed string"); - check(find("foo bar", "o b", 1, 100) = 3, "Should find in ranges wider than input'range"); - check(find("foo bar", "zen", 1, 100) = 0, "Should not find in ranges wider than input'range"); - check(find(offset_string, "o b", 1, 100) = 12, "Should find in ranges wider than input'range"); - check(find(offset_string, "zen", 1, 100) = 0, "Should not find in ranges wider than input'range"); - check(find(reverse_string, "o b", 100, 1) = 14, "Should find in ranges wider than input'range"); - check(find(reverse_string, "zen", 100, 1) = 0, "Should not find in ranges wider than input'range"); - check(find("foo bar", "o b", 3, 2) = 0, "Should not find string in negative range"); - check(find("foo bar", "o b", 30, 0) = 0, "Should not find string in negative range"); - check(find(reverse_string, "o b", 1, 100) = 0, "Should not find string in negative range"); - - elsif run("Test image") then - check(image("") = "", "Should return empty string on empty input vector."); - check(image("UX01ZWLH-") = "UX01ZWLH-", "Should handle every possible bit value"); - ascending_vector := "UX01ZWLH-"; - check(image(ascending_vector) = "UX01ZWLH-", "Should handle ascending vector range"); - descending_vector := "UX01ZWLH-"; - check(image(descending_vector) = "UX01ZWLH-", "Should handle descending vector range"); - - elsif run("Test hex_image") then - check(hex_image("") = "x""""", "Should return empty hex string on empty input vector. Got " & hex_image("") & "."); - check(hex_image("1010") = "x""a""", "Should handle zeros and ones."); - check(hex_image("10101U10") = "x""aX""", "Should X out meta data groups."); - check(hex_image("1010101") = "x""55""", "Should handle vectors without nibble-sized length."); - ascending_vector := "10101U101"; - check(hex_image(ascending_vector) = "x""15X""", "Should handle ascending vector range"); - descending_vector := "10101U101"; - check(hex_image(descending_vector) = "x""15X""", "Should handle descending vector range"); - - elsif run("Test replace") then - check(replace("", 'a', 'b') = "", "Should return empty string on empty input string."); - check(replace("foo bar", 'a', "") = "foo br", "Should be possible to replace segments with nothing"); - check(replace("foo bar ozon", 'o', 'b') = "fbb bar bzbn", "Should replace all specified characters by default."); - check(replace("foo bar ozon", 'o', 'b', 2) = "fbb bar ozon", "Should replace first n specified characters if cnt is specified."); - check(replace("foo bar ozon", 'o', 'b', 0) = "foo bar ozon", "Should not replace if cnt = 0."); - check(replace("foo bar ozon", 'o', 'b', 10) = "fbb bar bzbn", "Should handle cnt > number of old characters."); - check(replace("foo bar ozon", 'o', "ab") = "fabab bar abzabn", "Should be able to replace character with string."); - check(replace("foo bar ozon", "oo", 'b') = "fb bar ozon", "Should be able to replace string with character."); - check(replace("foo bar ozon", "oo", "ab") = "fab bar ozon", "Should be able to replace substring with another."); - check(replace(offset_string, "oo", "ab") = "fab bar", "Should be able to replace offset string."); - check(replace(reverse_string, "oo", "ab") = "fab bar", "Should be able to replace reversed string."); - check(replace("cat anaconda cow", "anaconda", "snake") = "cat snake cow", "Should handle short uneffected endings."); - - elsif run("Test title") then - check(title("") = "", "Should return empty string on empty input string."); - check(title("foo bar 17!") = "Foo Bar 17!", "Should only capitalize the first letter of words."); - check(title("Foo Bar") = "Foo Bar", "Should not affect already capitalized strings."); - check(title("foo" & HT & "bar" & CR & "zoo") = "Foo" & HT & "Bar" & CR & "Zoo", "Should handle tab and return whitespaces."); - check(title("foo bar") = "Foo Bar", "Should handle multiple whitespaces."); - check(title(offset_string) = "Foo Bar", "Should handle offset strings."); - check(title(reverse_string) = "Foo Bar", "Should handle reversed strings."); - - elsif run("Test upper") then - check(upper("") = "", "Should return empty string on empty input string."); - check(upper("foo bar 17!") = "FOO BAR 17!", "Should upper all letters of words."); - check(upper("FOO BAR") = "FOO BAR", "Should not affect already upper strings."); - check(upper("foo" & HT & "bar" & CR & "zoo") = "FOO" & HT & "BAR" & CR & "ZOO", "Should handle tab and return whitespaces."); - check(upper(offset_string) = "FOO BAR", "Should handle offset strings."); - check(upper(reverse_string) = "FOO BAR", "Should handle reversed strings."); - - elsif run("Test lower") then - check(lower("") = "", "Should return empty string on empty input string."); - check(lower("FOO BAR 17!") = "foo bar 17!", "Should lower all letters of words."); - check(lower("foo bar") = "foo bar", "Should not affect already lower strings."); - check(lower("FOO" & HT & "BAR" & CR & "ZOO") = "foo" & HT & "bar" & CR & "zoo", "Should handle tab and return whitespaces."); - check(lower(offset_string) = "foo bar", "Should handle offset strings."); - check(lower(reverse_string) = "foo bar", "Should handle reversed strings."); - - elsif run("Test split") then - l := split("foo",""); - check(l'length = 5, "Should return 5 substrings when splitting ""foo"""); - check(l(0).all = "", "Should return """" as the first substring when splitting ""foo"""); - check(l(1).all = "f", "Should return ""f"" as the 1st substring when splitting ""foo"""); - check(l(2).all = "o", "Should return ""o"" as the 2nd substring when splitting ""foo"""); - check(l(3).all = "o", "Should return ""o"" as the 3rd substring when splitting ""foo"""); - check(l(4).all = "", "Should return """" as the 4th substring when splitting ""foo"""); - deallocate(l); - - l := split("",""); - check(l'length = 2, "Should return 2 substrings when splitting """""); - check(l(0).all = "", "Should return """" as the first substring when splitting """""); - check(l(1).all = "", "Should return """" as the 1st substring when splitting """""); - deallocate(l); - - l := split("foo bar","q"); - check(l'length = 1, "Should return 1 substring when separator is missing"); - check(l(0).all = "foo bar", "Should return input string when separator is missing"); - deallocate(l); - - l := split("","q"); - check(l'length = 1, "Should return 1 substring when separator is missing"); - check(l(0).all = "", "Should return input string when separator is missing"); - deallocate(l); - - l := split("foo bar","b"); - check(l'length = 2, "Should return 2 substrings when separator appears once."); - check(l(0).all = "foo ", "Should return ""foo "" as first substring when splitting ""foo bar"" with ""b"""); - check(l(1).all = "ar", "Should return ""ar"" as second substring when splitting ""foo bar"" with ""b"""); - deallocate(l); - - l := split("foo bar","o"); - check(l'length = 3, "Should return 3 substrings when separator appears twice."); - check(l(0).all = "f", "Should return ""f"" as first substring when splitting ""foo bar"" with ""o"""); - check(l(1).all = "", "Should return """" as second substring when splitting ""foo bar"" with ""o"""); - check(l(2).all = " bar", "Should return "" bar"" as third substring when splitting ""foo bar"" with ""o"""); - deallocate(l); - - l := split("foo bar","f"); - check(l'length = 2, "Should return 2 substrings when separator appears once."); - check(l(0).all = "", "Should return """" as first substring when splitting ""foo bar"" with ""f"""); - check(l(1).all = "oo bar", "Should return ""oo bar"" as second substring when splitting ""foo bar"" with ""f"""); - deallocate(l); - - l := split("foo bar","r"); - check(l'length = 2, "Should return 2 substrings when separator appears once."); - check(l(0).all = "foo ba", "Should return ""foo ba"" as first substring when splitting ""foo bar"" with ""r"""); - check(l(1).all = "", "Should return """" as second substring when splitting ""foo bar"" with ""r"""); - deallocate(l); - - l := split("foo bar","foo"); - check(l'length = 2, "Should return 2 substrings when separator appears once."); - check(l(0).all = "", "Should return """" as first substring when splitting ""foo bar"" with ""foo"""); - check(l(1).all = " bar", "Should return "" bar"" as second substring when splitting ""foo bar"" with ""foo"""); - deallocate(l); - - l := split("fooo bar","oo"); - check(l'length = 2, "Should return 2 substrings when separator appears once."); - check(l(0).all = "f", "Should return ""f"" as first substring when splitting ""fooo bar"" with ""oo"""); - check(l(1).all = "o bar", "Should return ""o bar"" as second substring when splitting ""fooo bar"" with ""oo"""); - deallocate(l); - - l := split("foo bar","foo",0); - check(l'length = 1, "Should return 1 substrings when max count is 0."); - check(l(0).all = "foo bar", "Should return input when max count is zero."); - deallocate(l); - - l := split("foo bar","o",1); - check(l'length = 2, "Should return 2 substrings when max count is 1."); - check(l(0).all = "f", "Should return ""f"" as first substring when splitting ""foo bar"" with ""o"" and max count = 1."); - check(l(1).all = "o bar", "Should return ""o bar"" as second substring when splitting ""foo bar"" with ""o"" and max count = 1."); - deallocate(l); - - l := split(offset_string,"b"); - check(l'length = 2, "Should return 2 substrings when separator appears once."); - check(l(0).all = "foo ", "Should return ""foo "" as first substring when splitting ""foo bar"" with ""b"""); - check(l(1).all = "ar", "Should return ""ar"" as second substring when splitting ""foo bar"" with ""b"""); - deallocate(l); - - l := split(reverse_string,"b"); - check(l'length = 2, "Should return 2 substrings when separator appears once."); - check(l(0).all = "foo ", "Should return ""foo "" as first substring when splitting ""foo bar"" with ""b"""); - check(l(1).all = "ar", "Should return ""ar"" as second substring when splitting ""foo bar"" with ""b"""); - deallocate(l); - - elsif run("Test to_integer_string") then - check(to_integer_string(unsigned'("")) = "0", "Should return 0 on empty input"); - check(to_integer_string(unsigned'("0")) = natural'image(natural'low), "Should return correct value for minimum natural value."); - check(to_integer_string(unsigned'(X"7fffffff")) = natural'image(2147483647), "Should return correct value for maximum natural value in 32-bit integer implementations."); - check(to_integer_string(unsigned'(X"80000000")) = "2147483648", "Should return correct value for minimum natural above what's covered by 32-bit integer implementations."); - check(to_integer_string(unsigned'(X"7b283d038f92b837d92f73a87380a")) = "39966790250241720040714860591790090", "Should handle really large values."); - check(to_integer_string(unsigned'(X"00000000000000000000000000000")) = "0", "Should handle long zeros."); - check(to_integer_string(unsigned'("00LL11HH")) = "15", "Should handle weak bits"); - check(to_integer_string(unsigned'("011110110010100000111101LLLL00111000HHHH1001001010111000001101111101100100101111011100111010100001110011100000001010")) = "39966790250241720040714860591790090", "Should handle really large values containing weak bits."); - check(to_integer_string(unsigned'("1000000-")) = "NaN", "Should return NaN on vectors containing metalogical values"); - check(to_integer_string(unsigned'("1000000---000000000000000000000000000000000")) = "NaN", "Should return NaN on long vectors containing metalogical values"); - check(to_integer_string(unsigned'("1111101000")) = "1000", "Should return correct value for power of 10 values which are close to the maximum value that can be represented by the minimum binary vector for that value."); - - check(to_integer_string(std_logic_vector'("")) = "0", "Should return 0 on empty input"); - check(to_integer_string(std_logic_vector'("0")) = natural'image(natural'low), "Should return correct value for minimum natural value."); - check(to_integer_string(std_logic_vector'(X"7fffffff")) = natural'image(2147483647), "Should return correct value for maximum natural value in 32-bit integer implementations."); - check(to_integer_string(std_logic_vector'(X"80000000")) = "2147483648", "Should return correct value for minimum natural above what's covered by 32-bit integer implementations."); - check(to_integer_string(std_logic_vector'(X"7b283d038f92b837d92f73a87380a")) = "39966790250241720040714860591790090", "Should handle really large values."); - check(to_integer_string(std_logic_vector'(X"00000000000000000000000000000")) = "0", "Should handle long zeros."); - check(to_integer_string(std_logic_vector'("00LL11HH")) = "15", "Should handle weak bits"); - check(to_integer_string(std_logic_vector'("011110110010100000111101LLLL00111000HHHH1001001010111000001101111101100100101111011100111010100001110011100000001010")) = "39966790250241720040714860591790090", "Should handle really large values containing weak bits."); - check(to_integer_string(std_logic_vector'("1000000-")) = "NaN", "Should return NaN on vectors containing metalogical values"); - check(to_integer_string(std_logic_vector'("1000000---000000000000000000000000000000000")) = "NaN", "Should return NaN on long vectors containing metalogical values"); - - - check(to_integer_string(signed'("")) = "0", "Should return 0 on empty input"); - check(to_integer_string(signed'(X"80000000")) = integer'image(-2147483648), "Should return correct value for minimum value in 32-bit integer implementations."); - check(to_integer_string(signed'(X"7fffffff")) = integer'image(2147483647), "Should return correct value for maximum value in 32-bit integer implementations."); - check(to_integer_string(signed'("1" & X"7fffffff")) = "-2147483649", "Should return correct value for maximum integer below what's covered by 32-bit integer implementations."); - check(to_integer_string(signed'(X"080000000")) = "2147483648", "Should return correct value for minimum integer above what's covered by 32-bit integer implementations."); - check(to_integer_string(signed'(X"ab283d038f92b837d92f73a87380a")) = "-27533068910711039130181591688071158", "Should handle really small values."); - check(to_integer_string(signed'(X"7b283d038f92b837d92f73a87380a")) = "39966790250241720040714860591790090", "Should handle really large values."); - check(to_integer_string(signed'(X"00000000000000000000000000000")) = "0", "Should handle long zeros."); - check(to_integer_string(signed'("00LL11HH")) = "15", "Should handle weak bits"); - check(to_integer_string(signed'("011110110010100000111101LLLL00111000HHHH1001001010111000001101111101100100101111011100111010100001110011100000001010")) = "39966790250241720040714860591790090", "Should handle really large values containing weak bits."); - check(to_integer_string(signed'("1000000-")) = "NaN", "Should return NaN on vectors containing metalogical values"); - check(to_integer_string(signed'("1000000---000000000000000000000000000000000")) = "NaN", "Should return NaN on long vectors containing metalogical values"); - - elsif run("Test to_nibble_string") then - check(to_nibble_string(unsigned'("")) = "", "Should return empty string on empty input"); - check(to_nibble_string(std_logic_vector'("")) = "", "Should return empty string on empty input"); - check(to_nibble_string(signed'("")) = "", "Should return empty string on empty input"); - check(to_nibble_string(unsigned'("1")) = "1", "Should handle inputs shorter than a nibble"); - check(to_nibble_string(std_logic_vector'("1")) = "1", "Should handle inputs shorter than a nibble"); - check(to_nibble_string(signed'("1")) = "1", "Should handle inputs shorter than a nibble"); - check(to_nibble_string(unsigned'("1001")) = "1001", "Should handle single nibble"); - check(to_nibble_string(std_logic_vector'("1001")) = "1001", "Should handle single nibble"); - check(to_nibble_string(signed'("1001")) = "1001", "Should handle single nibble"); - check(to_nibble_string(unsigned'("011010101001")) = "0110_1010_1001", "Should handle an input value that is a multiple of nibbles"); - check(to_nibble_string(std_logic_vector'("011010101001")) = "0110_1010_1001", "Should handle an input value that is a multiple of nibbles"); - check(to_nibble_string(signed'("011010101001")) = "0110_1010_1001", "Should handle an input value that is a multiple of nibbles"); - check(to_nibble_string(unsigned'("1011010101001")) = "1_0110_1010_1001", "Should handle an input value that isn't a multiple of nibbles"); - check(to_nibble_string(std_logic_vector'("1011010101001")) = "1_0110_1010_1001", "Should handle an input value that isn't a multiple of nibbles"); - check(to_nibble_string(signed'("1011010101001")) = "1_0110_1010_1001", "Should handle an input value that isn't a multiple of nibbles"); - check(to_nibble_string(reversed_vector) = "1_0110_1010_1001", "Should handle reversed and offset input vectors"); - check(to_nibble_string(std_logic_vector(reversed_vector)) = "1_0110_1010_1001", "Should handle reversed and offset input vectors"); - check(to_nibble_string(signed(reversed_vector)) = "1_0110_1010_1001", "Should handle reversed and offset input vectors"); - end if; - end loop; - - test_runner_cleanup(runner); - wait; - end process; -end test_fixture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +library ieee; +use ieee.std_logic_1164.all; +use std.textio.all; +use ieee.numeric_std.all; + +library vunit_lib; +use vunit_lib.run_pkg.all; +use vunit_lib.string_ops.all; + +entity tb_string_ops is + generic (runner_cfg : string); +end entity tb_string_ops; + +architecture test_fixture of tb_string_ops is + procedure check ( + constant expr : in boolean; + constant msg : in string) is + begin + if not expr then + assert false report msg severity failure; + end if; + end procedure check; +begin + test_runner : process + -- Override "=" to make it work in Vivado (doesn't consider two empty + -- strings to be equal) + function "=" ( + constant a : string; + constant b : string) + return boolean is + + function bool_to_sign ( + constant b : boolean) + return integer is + begin + if b then + return 1; + else + return -1; + end if; + end function bool_to_sign; + + variable ret_val : boolean := true; + begin + if a'length /= b'length then + ret_val := false; + else + for i in 1 to a'length loop + ret_val := a(a'left + bool_to_sign(a'ascending) * (i - 1)) = + b(b'left + bool_to_sign(b'ascending) * (i - 1)); + exit when not ret_val; + end loop; + end if; + + return ret_val; + end function "="; + variable ascending_vector : std_logic_vector(3 to 11); + variable descending_vector : std_logic_vector(13 downto 5); + variable l : lines_t; + variable n_asserts_value, n_errors_value : integer; + constant offset_string :string(10 to 16) := "foo bar"; + constant reverse_string :string(16 downto 10) := "foo bar"; + constant reversed_vector :unsigned(16 downto 4) := "1011010101001"; + begin + test_runner_setup(runner, runner_cfg); + while test_suite loop + if run("Test strip") then + check(strip("") = "", "Strip of empty string should return an empty string. Got """ & strip("") & """."); + check(strip(" a ") = "a", "Strip should remove spaces by default. Got """ & strip(" a ") & """."); + check(strip(" ") = "", "Strip of single char string should return an empty string. Got """ & strip(" ") & """."); + check(strip(" b") = "b", "Strip should handle left-sided strip. Got """ & strip(" b") & """."); + check(strip("c ") = "c", "Strip should handle right-sided strip. Got """ & strip("c ") & """."); + check(strip("d") = "d", "Strip should not affect strings without specified chars in the end/begining. Got """ & strip("d") & """."); + check(strip(" e f ") = "e f", "Strip should not affect specified characters within the string. Got """ & strip(" e f ") & """."); + check(strip(" g ") = "g", "Strip should remove multiple instances of specified characters. Got """ & strip(" g ") & """."); + check(strip("-* h-*i-**-", "*-") = " h-*i", "Strip should remove multiple specified characters. Got """ & strip("-* h-*i-**-", "*-") & """."); + check(strip(offset_string, "fo") = " bar", "Should handle offset strings. Got """ & strip(offset_string, "fo") & """."); + check(strip(reverse_string, "fo") = " bar", "Should handle reversed strings. Got """ & strip(reverse_string, "fo") & """."); + elsif run("Test rstrip") then + check(rstrip("") = "", "rstrip of empty string should return an empty string. Got """ & rstrip("") & """."); + check(rstrip(" a ") = " a", "rstrip should remove spaces by default. Got """ & rstrip(" a ") & """."); + check(rstrip(" ") = "", "rstrip of single char string should return an empty string. Got """ & rstrip(" ") & """."); + check(rstrip("d") = "d", "rstrip should not affect strings without specified chars in the end/begining. Got """ & rstrip("d") & """."); + check(rstrip(" e f ") = " e f", "rstrip should not affect specified characters within the string. Got """ & rstrip(" e f ") & """."); + check(rstrip(" g ") = " g", "rstrip should remove multiple instances of specified characters. Got """ & rstrip(" g ") & """."); + check(rstrip("-* h-*i-**-", "*-") = "-* h-*i", "rstrip should remove multiple specified characters. Got """ & rstrip("-* h-*i-**-", "*-") & """."); + check(rstrip(offset_string, "rab") = "foo ", "Should handle offset strings. Got """ & rstrip(offset_string, "rab") & """."); + check(rstrip(reverse_string, "rab") = "foo ", "Should handle reversed strings. Got """ & rstrip(reverse_string, "rab") & """."); + + elsif run("Test lstrip") then + check(lstrip("") = "", "lstrip of empty string should return an empty string. Got """ & lstrip("") & """."); + check(lstrip(" a ") = "a ", "lstrip should remove spaces by default. Got """ & lstrip(" a ") & """."); + check(lstrip(" ") = "", "lstrip of single char string should return an empty string. Got """ & lstrip(" ") & """."); + check(lstrip("d") = "d", "lstrip should not affect strings without specified chars in the end/begining. Got """ & lstrip("d") & """."); + check(lstrip(" e f ") = "e f ", "lstrip should not affect specified characters within the string. Got """ & lstrip(" e f ") & """."); + check(lstrip(" g ") = "g ", "lstrip should remove multiple instances of specified characters. Got """ & lstrip(" g ") & """."); + check(lstrip("-* h-*i-**-", "*-") = " h-*i-**-", "lstrip should remove multiple specified characters. Got """ & lstrip("-* h-*i-**-", "*-") & """."); + check(lstrip(offset_string, "fo") = " bar", "Should handle offset strings. Got """ & lstrip(offset_string, "fo") & """."); + check(lstrip(reverse_string, "fo") = " bar", "Should handle reversed strings. Got """ & lstrip(reverse_string, "fo") & """."); + + elsif run("Test count") then + check(count("","a") = 0, "Empty string should return 0."); + check(count(" a b ") = 3, "Should count spaces by default."); + check(count(" a b ", "") = 6, "Should count an empty string between every character, at the beginning, and at the end."); + check(count("", "") = 1, "Should return 1 when counting empty string in empty string"); + check(count("hello world or hello earth", "or") = 2, "Should handle multi-character substrings"); + check(count("hello world or hello earth", 'o') = 4, "Should handle character type inputs."); + check(count("ababababa", "abab") = 2, "Should count non-overlapping occurences"); + check(count(offset_string, "o") = 2, "Should handle offset strings."); + check(count(reverse_string, "o") = 2, "Should handle reversed strings."); + check(count("ababababa", "a", 2 ,6) = 2, "Should handle parts of input string"); + check(count("aba", "a", 4 ,6) = 0, "Should not find anything outside of the string"); + check(count(offset_string, "a", 4 ,6) = 0, "Should not find anything outside of the string"); + check(count(reverse_string, "a", 12 ,4) = 0, "Should not find anything outside of the string"); + check(count("aba", "a", 3 ,1) = 0, "Should not find anything within a negative range"); + check(count("aba", "abab") = 0, "Should not find anything when substring is longer than the string"); + elsif run("Test find") then + check(find("", "") = 1, "Empty string should be found at the start"); + check(find("foo bar", "") = 1, "Empty string should be found at the start"); + check(find("", "foo") = 0, "Nothing should be found in an empty string"); + check(find("foo bar", "foo") = 1, "Should find string at the start"); + check(find("foo bar", "bar") = 5, "Should find string at the end"); + check(find("foo bar", "o b") = 3, "Should find string in the middle"); + check(find("foo bar", "foo bar") = 1, "Should find full string"); + check(find("foo bar", 'f') = 1, "Should find character at the start"); + check(find("foo bar", 'r') = 7, "Should find character at the end"); + check(find("foo bar", ' ') = 4, "Should find character in the middle"); + check(find("foo bar", "bars") = 0, "Should return 0 when string not found"); + check(find("foo bar", "foo bars") = 0, "Should return 0 when string not found"); + check(find("foo bar", 'q') = 0, "Should return 0 when character not found"); + check(find(offset_string, "") = 10, "Empty string should be found at the start on offset string"); + check(find(offset_string, "foo") = 10, "Should find string at the start on offset string"); + check(find(offset_string, "bar") = 14, "Should find string at the end on offset string"); + check(find(offset_string, "o b") = 12, "Should find string in the middle on offset string"); + check(find(reverse_string, "") = 16, "Empty string should be found at the start on reversed string"); + check(find(reverse_string, "foo") = 16, "Should find string at the start on reversed string"); + check(find(reverse_string, "bar") = 12, "Should find string at the end on reversed string"); + check(find(reverse_string, "o b") = 14, "Should find string in the middle on reversed string"); + check(find("foo bar", "oo", 2, 6) = 2, "Should find string at the start of slice"); + check(find("foo bar", "ba", 2, 6) = 5, "Should find string at the end of slice"); + check(find("foo bar", "o b", 2, 6) = 3, "Should find string in the middle of slice"); + check(find("foo bar", "", 2, 6) = 2, "Empty string should be found at the start of slice"); + check(find("foo bar", 'f', 2, 6) = 0, "Should not find anything before slice"); + check(find("foo bar", "ar", 2, 6) = 0, "Should not find anything after slice"); + check(find(offset_string, 'f', 11, 15) = 0, "Should not find anything before slice in offset string"); + check(find(offset_string, "ar", 11, 15) = 0, "Should not find anything after slice in offset string"); + check(find(reverse_string, 'f', 15, 11) = 0, "Should not find anything before slice in reversed string"); + check(find(reverse_string, "ar", 15, 11) = 0, "Should not find anything after slice in reversed string"); + check(find("foo bar", "o b", 1, 100) = 3, "Should find in ranges wider than input'range"); + check(find("foo bar", "zen", 1, 100) = 0, "Should not find in ranges wider than input'range"); + check(find(offset_string, "o b", 1, 100) = 12, "Should find in ranges wider than input'range"); + check(find(offset_string, "zen", 1, 100) = 0, "Should not find in ranges wider than input'range"); + check(find(reverse_string, "o b", 100, 1) = 14, "Should find in ranges wider than input'range"); + check(find(reverse_string, "zen", 100, 1) = 0, "Should not find in ranges wider than input'range"); + check(find("foo bar", "o b", 3, 2) = 0, "Should not find string in negative range"); + check(find("foo bar", "o b", 30, 0) = 0, "Should not find string in negative range"); + check(find(reverse_string, "o b", 1, 100) = 0, "Should not find string in negative range"); + + elsif run("Test image") then + check(image("") = "", "Should return empty string on empty input vector."); + check(image("UX01ZWLH-") = "UX01ZWLH-", "Should handle every possible bit value"); + ascending_vector := "UX01ZWLH-"; + check(image(ascending_vector) = "UX01ZWLH-", "Should handle ascending vector range"); + descending_vector := "UX01ZWLH-"; + check(image(descending_vector) = "UX01ZWLH-", "Should handle descending vector range"); + + elsif run("Test hex_image") then + check(hex_image("") = "x""""", "Should return empty hex string on empty input vector. Got " & hex_image("") & "."); + check(hex_image("1010") = "x""a""", "Should handle zeros and ones."); + check(hex_image("10101U10") = "x""aX""", "Should X out meta data groups."); + check(hex_image("1010101") = "x""55""", "Should handle vectors without nibble-sized length."); + ascending_vector := "10101U101"; + check(hex_image(ascending_vector) = "x""15X""", "Should handle ascending vector range"); + descending_vector := "10101U101"; + check(hex_image(descending_vector) = "x""15X""", "Should handle descending vector range"); + + elsif run("Test replace") then + check(replace("", 'a', 'b') = "", "Should return empty string on empty input string."); + check(replace("foo bar", 'a', "") = "foo br", "Should be possible to replace segments with nothing"); + check(replace("foo bar ozon", 'o', 'b') = "fbb bar bzbn", "Should replace all specified characters by default."); + check(replace("foo bar ozon", 'o', 'b', 2) = "fbb bar ozon", "Should replace first n specified characters if cnt is specified."); + check(replace("foo bar ozon", 'o', 'b', 0) = "foo bar ozon", "Should not replace if cnt = 0."); + check(replace("foo bar ozon", 'o', 'b', 10) = "fbb bar bzbn", "Should handle cnt > number of old characters."); + check(replace("foo bar ozon", 'o', "ab") = "fabab bar abzabn", "Should be able to replace character with string."); + check(replace("foo bar ozon", "oo", 'b') = "fb bar ozon", "Should be able to replace string with character."); + check(replace("foo bar ozon", "oo", "ab") = "fab bar ozon", "Should be able to replace substring with another."); + check(replace(offset_string, "oo", "ab") = "fab bar", "Should be able to replace offset string."); + check(replace(reverse_string, "oo", "ab") = "fab bar", "Should be able to replace reversed string."); + check(replace("cat anaconda cow", "anaconda", "snake") = "cat snake cow", "Should handle short uneffected endings."); + + elsif run("Test title") then + check(title("") = "", "Should return empty string on empty input string."); + check(title("foo bar 17!") = "Foo Bar 17!", "Should only capitalize the first letter of words."); + check(title("Foo Bar") = "Foo Bar", "Should not affect already capitalized strings."); + check(title("foo" & HT & "bar" & CR & "zoo") = "Foo" & HT & "Bar" & CR & "Zoo", "Should handle tab and return whitespaces."); + check(title("foo bar") = "Foo Bar", "Should handle multiple whitespaces."); + check(title(offset_string) = "Foo Bar", "Should handle offset strings."); + check(title(reverse_string) = "Foo Bar", "Should handle reversed strings."); + + elsif run("Test upper") then + check(upper("") = "", "Should return empty string on empty input string."); + check(upper("foo bar 17!") = "FOO BAR 17!", "Should upper all letters of words."); + check(upper("FOO BAR") = "FOO BAR", "Should not affect already upper strings."); + check(upper("foo" & HT & "bar" & CR & "zoo") = "FOO" & HT & "BAR" & CR & "ZOO", "Should handle tab and return whitespaces."); + check(upper(offset_string) = "FOO BAR", "Should handle offset strings."); + check(upper(reverse_string) = "FOO BAR", "Should handle reversed strings."); + + elsif run("Test lower") then + check(lower("") = "", "Should return empty string on empty input string."); + check(lower("FOO BAR 17!") = "foo bar 17!", "Should lower all letters of words."); + check(lower("foo bar") = "foo bar", "Should not affect already lower strings."); + check(lower("FOO" & HT & "BAR" & CR & "ZOO") = "foo" & HT & "bar" & CR & "zoo", "Should handle tab and return whitespaces."); + check(lower(offset_string) = "foo bar", "Should handle offset strings."); + check(lower(reverse_string) = "foo bar", "Should handle reversed strings."); + + elsif run("Test split") then + l := split("foo",""); + check(l'length = 5, "Should return 5 substrings when splitting ""foo"""); + check(l(0).all = "", "Should return """" as the first substring when splitting ""foo"""); + check(l(1).all = "f", "Should return ""f"" as the 1st substring when splitting ""foo"""); + check(l(2).all = "o", "Should return ""o"" as the 2nd substring when splitting ""foo"""); + check(l(3).all = "o", "Should return ""o"" as the 3rd substring when splitting ""foo"""); + check(l(4).all = "", "Should return """" as the 4th substring when splitting ""foo"""); + deallocate(l); + + l := split("",""); + check(l'length = 2, "Should return 2 substrings when splitting """""); + check(l(0).all = "", "Should return """" as the first substring when splitting """""); + check(l(1).all = "", "Should return """" as the 1st substring when splitting """""); + deallocate(l); + + l := split("foo bar","q"); + check(l'length = 1, "Should return 1 substring when separator is missing"); + check(l(0).all = "foo bar", "Should return input string when separator is missing"); + deallocate(l); + + l := split("","q"); + check(l'length = 1, "Should return 1 substring when separator is missing"); + check(l(0).all = "", "Should return input string when separator is missing"); + deallocate(l); + + l := split("foo bar","b"); + check(l'length = 2, "Should return 2 substrings when separator appears once."); + check(l(0).all = "foo ", "Should return ""foo "" as first substring when splitting ""foo bar"" with ""b"""); + check(l(1).all = "ar", "Should return ""ar"" as second substring when splitting ""foo bar"" with ""b"""); + deallocate(l); + + l := split("foo bar","o"); + check(l'length = 3, "Should return 3 substrings when separator appears twice."); + check(l(0).all = "f", "Should return ""f"" as first substring when splitting ""foo bar"" with ""o"""); + check(l(1).all = "", "Should return """" as second substring when splitting ""foo bar"" with ""o"""); + check(l(2).all = " bar", "Should return "" bar"" as third substring when splitting ""foo bar"" with ""o"""); + deallocate(l); + + l := split("foo bar","f"); + check(l'length = 2, "Should return 2 substrings when separator appears once."); + check(l(0).all = "", "Should return """" as first substring when splitting ""foo bar"" with ""f"""); + check(l(1).all = "oo bar", "Should return ""oo bar"" as second substring when splitting ""foo bar"" with ""f"""); + deallocate(l); + + l := split("foo bar","r"); + check(l'length = 2, "Should return 2 substrings when separator appears once."); + check(l(0).all = "foo ba", "Should return ""foo ba"" as first substring when splitting ""foo bar"" with ""r"""); + check(l(1).all = "", "Should return """" as second substring when splitting ""foo bar"" with ""r"""); + deallocate(l); + + l := split("foo bar","foo"); + check(l'length = 2, "Should return 2 substrings when separator appears once."); + check(l(0).all = "", "Should return """" as first substring when splitting ""foo bar"" with ""foo"""); + check(l(1).all = " bar", "Should return "" bar"" as second substring when splitting ""foo bar"" with ""foo"""); + deallocate(l); + + l := split("fooo bar","oo"); + check(l'length = 2, "Should return 2 substrings when separator appears once."); + check(l(0).all = "f", "Should return ""f"" as first substring when splitting ""fooo bar"" with ""oo"""); + check(l(1).all = "o bar", "Should return ""o bar"" as second substring when splitting ""fooo bar"" with ""oo"""); + deallocate(l); + + l := split("foo bar","foo",0); + check(l'length = 1, "Should return 1 substrings when max count is 0."); + check(l(0).all = "foo bar", "Should return input when max count is zero."); + deallocate(l); + + l := split("foo bar","o",1); + check(l'length = 2, "Should return 2 substrings when max count is 1."); + check(l(0).all = "f", "Should return ""f"" as first substring when splitting ""foo bar"" with ""o"" and max count = 1."); + check(l(1).all = "o bar", "Should return ""o bar"" as second substring when splitting ""foo bar"" with ""o"" and max count = 1."); + deallocate(l); + + l := split(offset_string,"b"); + check(l'length = 2, "Should return 2 substrings when separator appears once."); + check(l(0).all = "foo ", "Should return ""foo "" as first substring when splitting ""foo bar"" with ""b"""); + check(l(1).all = "ar", "Should return ""ar"" as second substring when splitting ""foo bar"" with ""b"""); + deallocate(l); + + l := split(reverse_string,"b"); + check(l'length = 2, "Should return 2 substrings when separator appears once."); + check(l(0).all = "foo ", "Should return ""foo "" as first substring when splitting ""foo bar"" with ""b"""); + check(l(1).all = "ar", "Should return ""ar"" as second substring when splitting ""foo bar"" with ""b"""); + deallocate(l); + + elsif run("Test to_integer_string") then + check(to_integer_string(unsigned'("")) = "0", "Should return 0 on empty input"); + check(to_integer_string(unsigned'("0")) = natural'image(natural'low), "Should return correct value for minimum natural value."); + check(to_integer_string(unsigned'(X"7fffffff")) = natural'image(2147483647), "Should return correct value for maximum natural value in 32-bit integer implementations."); + check(to_integer_string(unsigned'(X"80000000")) = "2147483648", "Should return correct value for minimum natural above what's covered by 32-bit integer implementations."); + check(to_integer_string(unsigned'(X"7b283d038f92b837d92f73a87380a")) = "39966790250241720040714860591790090", "Should handle really large values."); + check(to_integer_string(unsigned'(X"00000000000000000000000000000")) = "0", "Should handle long zeros."); + check(to_integer_string(unsigned'("00LL11HH")) = "15", "Should handle weak bits"); + check(to_integer_string(unsigned'("011110110010100000111101LLLL00111000HHHH1001001010111000001101111101100100101111011100111010100001110011100000001010")) = "39966790250241720040714860591790090", "Should handle really large values containing weak bits."); + check(to_integer_string(unsigned'("1000000-")) = "NaN", "Should return NaN on vectors containing metalogical values"); + check(to_integer_string(unsigned'("1000000---000000000000000000000000000000000")) = "NaN", "Should return NaN on long vectors containing metalogical values"); + check(to_integer_string(unsigned'("1111101000")) = "1000", "Should return correct value for power of 10 values which are close to the maximum value that can be represented by the minimum binary vector for that value."); + + check(to_integer_string(std_logic_vector'("")) = "0", "Should return 0 on empty input"); + check(to_integer_string(std_logic_vector'("0")) = natural'image(natural'low), "Should return correct value for minimum natural value."); + check(to_integer_string(std_logic_vector'(X"7fffffff")) = natural'image(2147483647), "Should return correct value for maximum natural value in 32-bit integer implementations."); + check(to_integer_string(std_logic_vector'(X"80000000")) = "2147483648", "Should return correct value for minimum natural above what's covered by 32-bit integer implementations."); + check(to_integer_string(std_logic_vector'(X"7b283d038f92b837d92f73a87380a")) = "39966790250241720040714860591790090", "Should handle really large values."); + check(to_integer_string(std_logic_vector'(X"00000000000000000000000000000")) = "0", "Should handle long zeros."); + check(to_integer_string(std_logic_vector'("00LL11HH")) = "15", "Should handle weak bits"); + check(to_integer_string(std_logic_vector'("011110110010100000111101LLLL00111000HHHH1001001010111000001101111101100100101111011100111010100001110011100000001010")) = "39966790250241720040714860591790090", "Should handle really large values containing weak bits."); + check(to_integer_string(std_logic_vector'("1000000-")) = "NaN", "Should return NaN on vectors containing metalogical values"); + check(to_integer_string(std_logic_vector'("1000000---000000000000000000000000000000000")) = "NaN", "Should return NaN on long vectors containing metalogical values"); + + + check(to_integer_string(signed'("")) = "0", "Should return 0 on empty input"); + check(to_integer_string(signed'(X"80000000")) = integer'image(-2147483648), "Should return correct value for minimum value in 32-bit integer implementations."); + check(to_integer_string(signed'(X"7fffffff")) = integer'image(2147483647), "Should return correct value for maximum value in 32-bit integer implementations."); + check(to_integer_string(signed'("1" & X"7fffffff")) = "-2147483649", "Should return correct value for maximum integer below what's covered by 32-bit integer implementations."); + check(to_integer_string(signed'(X"080000000")) = "2147483648", "Should return correct value for minimum integer above what's covered by 32-bit integer implementations."); + check(to_integer_string(signed'(X"ab283d038f92b837d92f73a87380a")) = "-27533068910711039130181591688071158", "Should handle really small values."); + check(to_integer_string(signed'(X"7b283d038f92b837d92f73a87380a")) = "39966790250241720040714860591790090", "Should handle really large values."); + check(to_integer_string(signed'(X"00000000000000000000000000000")) = "0", "Should handle long zeros."); + check(to_integer_string(signed'("00LL11HH")) = "15", "Should handle weak bits"); + check(to_integer_string(signed'("011110110010100000111101LLLL00111000HHHH1001001010111000001101111101100100101111011100111010100001110011100000001010")) = "39966790250241720040714860591790090", "Should handle really large values containing weak bits."); + check(to_integer_string(signed'("1000000-")) = "NaN", "Should return NaN on vectors containing metalogical values"); + check(to_integer_string(signed'("1000000---000000000000000000000000000000000")) = "NaN", "Should return NaN on long vectors containing metalogical values"); + + elsif run("Test to_nibble_string") then + check(to_nibble_string(unsigned'("")) = "", "Should return empty string on empty input"); + check(to_nibble_string(std_logic_vector'("")) = "", "Should return empty string on empty input"); + check(to_nibble_string(signed'("")) = "", "Should return empty string on empty input"); + check(to_nibble_string(unsigned'("1")) = "1", "Should handle inputs shorter than a nibble"); + check(to_nibble_string(std_logic_vector'("1")) = "1", "Should handle inputs shorter than a nibble"); + check(to_nibble_string(signed'("1")) = "1", "Should handle inputs shorter than a nibble"); + check(to_nibble_string(unsigned'("1001")) = "1001", "Should handle single nibble"); + check(to_nibble_string(std_logic_vector'("1001")) = "1001", "Should handle single nibble"); + check(to_nibble_string(signed'("1001")) = "1001", "Should handle single nibble"); + check(to_nibble_string(unsigned'("011010101001")) = "0110_1010_1001", "Should handle an input value that is a multiple of nibbles"); + check(to_nibble_string(std_logic_vector'("011010101001")) = "0110_1010_1001", "Should handle an input value that is a multiple of nibbles"); + check(to_nibble_string(signed'("011010101001")) = "0110_1010_1001", "Should handle an input value that is a multiple of nibbles"); + check(to_nibble_string(unsigned'("1011010101001")) = "1_0110_1010_1001", "Should handle an input value that isn't a multiple of nibbles"); + check(to_nibble_string(std_logic_vector'("1011010101001")) = "1_0110_1010_1001", "Should handle an input value that isn't a multiple of nibbles"); + check(to_nibble_string(signed'("1011010101001")) = "1_0110_1010_1001", "Should handle an input value that isn't a multiple of nibbles"); + check(to_nibble_string(reversed_vector) = "1_0110_1010_1001", "Should handle reversed and offset input vectors"); + check(to_nibble_string(std_logic_vector(reversed_vector)) = "1_0110_1010_1001", "Should handle reversed and offset input vectors"); + check(to_nibble_string(signed(reversed_vector)) = "1_0110_1010_1001", "Should handle reversed and offset input vectors"); + end if; + end loop; + + test_runner_cleanup(runner); + wait; + end process; +end test_fixture; diff --git a/vunit/vhdl/verification_components/run.py b/vunit/vhdl/verification_components/run.py index 73ccd74a6..4521da942 100644 --- a/vunit/vhdl/verification_components/run.py +++ b/vunit/vhdl/verification_components/run.py @@ -1,105 +1,105 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -from os.path import join, dirname -from vunit import VUnit -from itertools import product - -root = dirname(__file__) - -ui = VUnit.from_argv() -ui.add_random() -ui.add_verification_components() -lib = ui.library("vunit_lib") -lib.add_source_files(join(root, "test", "*.vhd")) - - -def encode(tb_cfg): - return ",".join(["%s:%s" % (key, str(tb_cfg[key])) for key in tb_cfg]) - - -def gen_wb_tests(obj, *args): - for dat_width, num_cycles, strobe_prob, ack_prob, stall_prob in product(*args): - tb_cfg = dict( - dat_width=dat_width, - # TODO remove fixed addr - adr_width=32, - strobe_prob=strobe_prob, - ack_prob=ack_prob, - stall_prob=stall_prob, - num_cycles=num_cycles) - config_name = encode(tb_cfg) - obj.add_config(name=config_name, - generics=dict(encoded_tb_cfg=encode(tb_cfg))) - - -def gen_avalon_tests(obj, *args): - for data_width, num_cycles, readdatavalid_prob, waitrequest_prob, in product(*args): - tb_cfg = dict( - data_width=data_width, - readdatavalid_prob=readdatavalid_prob, - waitrequest_prob=waitrequest_prob, - num_cycles=num_cycles) - config_name = encode(tb_cfg) - obj.add_config(name=config_name, - generics=dict(encoded_tb_cfg=encode(tb_cfg))) - - -def gen_avalon_master_tests(obj, *args): - for transfers, readdatavalid_prob, waitrequest_prob, write_prob, read_prob, in product(*args): - tb_cfg = dict( - readdatavalid_prob=readdatavalid_prob, - waitrequest_prob=waitrequest_prob, - write_prob=write_prob, - read_prob=read_prob, - transfers=transfers) - config_name = encode(tb_cfg) - obj.add_config(name=config_name, - generics=dict(encoded_tb_cfg=encode(tb_cfg))) - - -tb_avalon_slave = lib.test_bench("tb_avalon_slave") - -for test in tb_avalon_slave.get_tests(): - gen_avalon_tests(test, [32], [1, 2, 64], [1.0, 0.3], [0.0, 0.4]) - -tb_avalon_master = lib.test_bench("tb_avalon_master") - -for test in tb_avalon_master.get_tests(): - if test.name == "wr single rd single": - gen_avalon_master_tests(test, [1], [1.0], [0.0], [1.0], [1.0]) - else: - gen_avalon_master_tests(test, [64], [1.0, 0.3], [0.0, 0.7], [1.0, 0.3], [1.0, 0.3]) - -tb_wishbone_slave = lib.test_bench("tb_wishbone_slave") - -for test in tb_wishbone_slave.get_tests(): - # TODO strobe_prob not implemented in slave tb - gen_wb_tests(test, [8, 32], [1, 64], [1.0], [0.3, 1.0], [0.4, 0.0]) - - -tb_wishbone_master = lib.test_bench("tb_wishbone_master") - -for test in tb_wishbone_master.get_tests(): - gen_wb_tests(test, [8, 32], [1, 64], [0.3, 1.0], [0.3, 1.0], [0.4, 0.0]) - -tb_axi_stream_protocol_checker = lib.test_bench("tb_axi_stream_protocol_checker") - -for data_length in [0, 8]: - for test in tb_axi_stream_protocol_checker.get_tests("*passing*tdata*"): - test.add_config(name="data_length=%d" % data_length, generics=dict(data_length=data_length)) - -for test in tb_axi_stream_protocol_checker.get_tests("*failing*tid width*"): - test.add_config(name="dest_length=25", generics=dict(dest_length=25)) - test.add_config(name="id_length=8 dest_length=17", generics=dict(id_length=8, dest_length=17)) - -test_failing_max_waits = tb_axi_stream_protocol_checker.test( - "Test failing check of that tready comes within max_waits after valid") -for max_waits in [0, 8]: - test_failing_max_waits.add_config(name="max_waits=%d" % max_waits, generics=dict(max_waits=max_waits)) - - -ui.main() +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +from os.path import join, dirname +from vunit import VUnit +from itertools import product + +root = dirname(__file__) + +ui = VUnit.from_argv() +ui.add_random() +ui.add_verification_components() +lib = ui.library("vunit_lib") +lib.add_source_files(join(root, "test", "*.vhd")) + + +def encode(tb_cfg): + return ",".join(["%s:%s" % (key, str(tb_cfg[key])) for key in tb_cfg]) + + +def gen_wb_tests(obj, *args): + for dat_width, num_cycles, strobe_prob, ack_prob, stall_prob in product(*args): + tb_cfg = dict( + dat_width=dat_width, + # TODO remove fixed addr + adr_width=32, + strobe_prob=strobe_prob, + ack_prob=ack_prob, + stall_prob=stall_prob, + num_cycles=num_cycles) + config_name = encode(tb_cfg) + obj.add_config(name=config_name, + generics=dict(encoded_tb_cfg=encode(tb_cfg))) + + +def gen_avalon_tests(obj, *args): + for data_width, num_cycles, readdatavalid_prob, waitrequest_prob, in product(*args): + tb_cfg = dict( + data_width=data_width, + readdatavalid_prob=readdatavalid_prob, + waitrequest_prob=waitrequest_prob, + num_cycles=num_cycles) + config_name = encode(tb_cfg) + obj.add_config(name=config_name, + generics=dict(encoded_tb_cfg=encode(tb_cfg))) + + +def gen_avalon_master_tests(obj, *args): + for transfers, readdatavalid_prob, waitrequest_prob, write_prob, read_prob, in product(*args): + tb_cfg = dict( + readdatavalid_prob=readdatavalid_prob, + waitrequest_prob=waitrequest_prob, + write_prob=write_prob, + read_prob=read_prob, + transfers=transfers) + config_name = encode(tb_cfg) + obj.add_config(name=config_name, + generics=dict(encoded_tb_cfg=encode(tb_cfg))) + + +tb_avalon_slave = lib.test_bench("tb_avalon_slave") + +for test in tb_avalon_slave.get_tests(): + gen_avalon_tests(test, [32], [1, 2, 64], [1.0, 0.3], [0.0, 0.4]) + +tb_avalon_master = lib.test_bench("tb_avalon_master") + +for test in tb_avalon_master.get_tests(): + if test.name == "wr single rd single": + gen_avalon_master_tests(test, [1], [1.0], [0.0], [1.0], [1.0]) + else: + gen_avalon_master_tests(test, [64], [1.0, 0.3], [0.0, 0.7], [1.0, 0.3], [1.0, 0.3]) + +tb_wishbone_slave = lib.test_bench("tb_wishbone_slave") + +for test in tb_wishbone_slave.get_tests(): + # TODO strobe_prob not implemented in slave tb + gen_wb_tests(test, [8, 32], [1, 64], [1.0], [0.3, 1.0], [0.4, 0.0]) + + +tb_wishbone_master = lib.test_bench("tb_wishbone_master") + +for test in tb_wishbone_master.get_tests(): + gen_wb_tests(test, [8, 32], [1, 64], [0.3, 1.0], [0.3, 1.0], [0.4, 0.0]) + +tb_axi_stream_protocol_checker = lib.test_bench("tb_axi_stream_protocol_checker") + +for data_length in [0, 8]: + for test in tb_axi_stream_protocol_checker.get_tests("*passing*tdata*"): + test.add_config(name="data_length=%d" % data_length, generics=dict(data_length=data_length)) + +for test in tb_axi_stream_protocol_checker.get_tests("*failing*tid width*"): + test.add_config(name="dest_length=25", generics=dict(dest_length=25)) + test.add_config(name="id_length=8 dest_length=17", generics=dict(id_length=8, dest_length=17)) + +test_failing_max_waits = tb_axi_stream_protocol_checker.test( + "Test failing check of that tready comes within max_waits after valid") +for max_waits in [0, 8]: + test_failing_max_waits.add_config(name="max_waits=%d" % max_waits, generics=dict(max_waits=max_waits)) + + +ui.main() diff --git a/vunit/vhdl/verification_components/src/avalon_master.vhd b/vunit/vhdl/verification_components/src/avalon_master.vhd index 1e5820772..16692561f 100644 --- a/vunit/vhdl/verification_components/src/avalon_master.vhd +++ b/vunit/vhdl/verification_components/src/avalon_master.vhd @@ -1,185 +1,185 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com --- Author Slawomir Siluk slaweksiluk@gazeta.pl --- Avalon Memory Mapped Master BFM --- TODO: --- - handle byteenable in bursts -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -use work.queue_pkg.all; -use work.bus_master_pkg.all; -context work.com_context; -use work.com_types_pkg.all; -use work.logger_pkg.all; -use work.check_pkg.all; -use work.sync_pkg.all; - -library osvvm; -use osvvm.RandomPkg.all; - -entity avalon_master is - generic ( - bus_handle : bus_master_t; - use_readdatavalid : boolean := true; - fixed_read_latency : natural := 1; -- (bus cycles). This parameter is ignored when use_readdatavalid is true - write_high_probability : real range 0.0 to 1.0 := 1.0; - read_high_probability : real range 0.0 to 1.0 := 1.0 - ); - port ( - clk : in std_logic; - address : out std_logic_vector; - byteenable : out std_logic_vector; - burstcount : out std_logic_vector; - waitrequest : in std_logic; - write : out std_logic; - writedata : out std_logic_vector; - read : out std_logic; - readdata : in std_logic_vector; - readdatavalid : in std_logic - ); -end entity; - -architecture a of avalon_master is - constant av_master_read_actor : actor_t := new_actor; - constant avmm_burst_rd_actor : actor_t := new_actor; - constant acknowledge_queue : queue_t := new_queue; - constant burst_acknowledge_queue : queue_t := new_queue; - constant burstlen_queue : queue_t := new_queue; - signal burst_read_flag : boolean := false; -begin - - main : process - variable request_msg : msg_t; - variable msg_type : msg_type_t; - variable rnd : RandomPType; - variable msgs : natural; - variable burst : positive; - begin - rnd.InitSeed(rnd'instance_name); - write <= '0'; - read <= '0'; - burstcount <= std_logic_vector(to_unsigned(1, burstcount'length)); - wait until rising_edge(clk); - loop - request_msg := null_msg; - msgs := num_of_messages(bus_handle.p_actor); - if (msgs > 0) then - receive(net, bus_handle.p_actor, request_msg); - msg_type := message_type(request_msg); - if msg_type = bus_read_msg then - while rnd.Uniform(0.0, 1.0) > read_high_probability loop - wait until rising_edge(clk); - end loop; - address <= pop_std_ulogic_vector(request_msg); - byteenable(byteenable'range) <= (others => '1'); - read <= '1'; - wait until rising_edge(clk) and waitrequest = '0'; - read <= '0'; - push(acknowledge_queue, request_msg); - - elsif msg_type = bus_burst_read_msg then - while rnd.Uniform(0.0, 1.0) > read_high_probability loop - wait until rising_edge(clk); - end loop; - address <= pop_std_ulogic_vector(request_msg); - burstcount <= std_logic_vector(to_unsigned(1, burstcount'length)); - burst := pop_integer(request_msg); - burstcount <= std_logic_vector(to_unsigned(burst, burstcount'length)); - byteenable(byteenable'range) <= (others => '1'); - read <= '1'; - wait until rising_edge(clk) and waitrequest = '0'; - read <= '0'; - push(burst_acknowledge_queue, request_msg); - push(burstlen_queue, burst); - - elsif msg_type = bus_write_msg then - while rnd.Uniform(0.0, 1.0) > write_high_probability loop - wait until rising_edge(clk); - end loop; - address <= pop_std_ulogic_vector(request_msg); - burstcount <= std_logic_vector(to_unsigned(1, burstcount'length)); - writedata <= pop_std_ulogic_vector(request_msg); - byteenable <= pop_std_ulogic_vector(request_msg); - write <= '1'; - wait until rising_edge(clk) and waitrequest = '0'; - write <= '0'; - - elsif msg_type = bus_burst_write_msg then - address <= pop_std_ulogic_vector(request_msg); - burst := pop_integer(request_msg); - burstcount <= std_logic_vector(to_unsigned(burst, burstcount'length)); - for i in 0 to burst-1 loop - while rnd.Uniform(0.0, 1.0) > write_high_probability loop - wait until rising_edge(clk); - end loop; - writedata <= pop_std_ulogic_vector(request_msg); - -- TODO handle byteenable - byteenable(byteenable'range) <= (others => '1'); - write <= '1'; - wait until rising_edge(clk) and waitrequest = '0'; - write <= '0'; - address(address'range) <= (others => 'U'); - burstcount(burstcount'range) <= (others => 'U'); - end loop; - - elsif msg_type = wait_until_idle_msg then - wait until not burst_read_flag and is_empty(burst_acknowledge_queue) and rising_edge(clk); - handle_wait_until_idle(net, msg_type, request_msg); - - else - unexpected_msg_type(msg_type); - end if; - else - wait until rising_edge(clk); - end if; - end loop; - end process; - - read_capture : process - variable request_msg, reply_msg : msg_t; - begin - if use_readdatavalid then - wait until readdatavalid = '1' and not is_empty(acknowledge_queue) and rising_edge(clk); - else - -- Non-pipelined case: waits for slave to de-assert waitrequest and sample data after fixed_read_latency cycles. - wait until rising_edge(clk) and waitrequest = '0' and read = '1'; - if fixed_read_latency > 0 then - for i in 0 to fixed_read_latency - 1 loop - wait until rising_edge(clk); - end loop; - end if; - end if; - request_msg := pop(acknowledge_queue); - reply_msg := new_msg(sender => av_master_read_actor); - push_std_ulogic_vector(reply_msg, readdata); - reply(net, request_msg, reply_msg); - delete(request_msg); - end process; - - burst_read_capture : process - variable request_msg, reply_msg : msg_t; - variable burst : positive; - begin - wait until readdatavalid = '1' and not is_empty(burst_acknowledge_queue) and rising_edge(clk); - burst_read_flag <= true; - request_msg := pop(burst_acknowledge_queue); - burst := pop(burstlen_queue); - reply_msg := new_msg(sender => avmm_burst_rd_actor); - push_integer(reply_msg, burst); - push_std_ulogic_vector(reply_msg, readdata); - for i in 1 to burst-1 loop - wait until readdatavalid = '1' and rising_edge(clk) for 1 us; - check_true(readdatavalid = '1', "avalon master burst readdatavalid timeout"); - push_std_ulogic_vector(reply_msg, readdata); - end loop; - reply(net, request_msg, reply_msg); - delete(request_msg); - burst_read_flag <= false; - end process; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +-- Author Slawomir Siluk slaweksiluk@gazeta.pl +-- Avalon Memory Mapped Master BFM +-- TODO: +-- - handle byteenable in bursts +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.queue_pkg.all; +use work.bus_master_pkg.all; +context work.com_context; +use work.com_types_pkg.all; +use work.logger_pkg.all; +use work.check_pkg.all; +use work.sync_pkg.all; + +library osvvm; +use osvvm.RandomPkg.all; + +entity avalon_master is + generic ( + bus_handle : bus_master_t; + use_readdatavalid : boolean := true; + fixed_read_latency : natural := 1; -- (bus cycles). This parameter is ignored when use_readdatavalid is true + write_high_probability : real range 0.0 to 1.0 := 1.0; + read_high_probability : real range 0.0 to 1.0 := 1.0 + ); + port ( + clk : in std_logic; + address : out std_logic_vector; + byteenable : out std_logic_vector; + burstcount : out std_logic_vector; + waitrequest : in std_logic; + write : out std_logic; + writedata : out std_logic_vector; + read : out std_logic; + readdata : in std_logic_vector; + readdatavalid : in std_logic + ); +end entity; + +architecture a of avalon_master is + constant av_master_read_actor : actor_t := new_actor; + constant avmm_burst_rd_actor : actor_t := new_actor; + constant acknowledge_queue : queue_t := new_queue; + constant burst_acknowledge_queue : queue_t := new_queue; + constant burstlen_queue : queue_t := new_queue; + signal burst_read_flag : boolean := false; +begin + + main : process + variable request_msg : msg_t; + variable msg_type : msg_type_t; + variable rnd : RandomPType; + variable msgs : natural; + variable burst : positive; + begin + rnd.InitSeed(rnd'instance_name); + write <= '0'; + read <= '0'; + burstcount <= std_logic_vector(to_unsigned(1, burstcount'length)); + wait until rising_edge(clk); + loop + request_msg := null_msg; + msgs := num_of_messages(bus_handle.p_actor); + if (msgs > 0) then + receive(net, bus_handle.p_actor, request_msg); + msg_type := message_type(request_msg); + if msg_type = bus_read_msg then + while rnd.Uniform(0.0, 1.0) > read_high_probability loop + wait until rising_edge(clk); + end loop; + address <= pop_std_ulogic_vector(request_msg); + byteenable(byteenable'range) <= (others => '1'); + read <= '1'; + wait until rising_edge(clk) and waitrequest = '0'; + read <= '0'; + push(acknowledge_queue, request_msg); + + elsif msg_type = bus_burst_read_msg then + while rnd.Uniform(0.0, 1.0) > read_high_probability loop + wait until rising_edge(clk); + end loop; + address <= pop_std_ulogic_vector(request_msg); + burstcount <= std_logic_vector(to_unsigned(1, burstcount'length)); + burst := pop_integer(request_msg); + burstcount <= std_logic_vector(to_unsigned(burst, burstcount'length)); + byteenable(byteenable'range) <= (others => '1'); + read <= '1'; + wait until rising_edge(clk) and waitrequest = '0'; + read <= '0'; + push(burst_acknowledge_queue, request_msg); + push(burstlen_queue, burst); + + elsif msg_type = bus_write_msg then + while rnd.Uniform(0.0, 1.0) > write_high_probability loop + wait until rising_edge(clk); + end loop; + address <= pop_std_ulogic_vector(request_msg); + burstcount <= std_logic_vector(to_unsigned(1, burstcount'length)); + writedata <= pop_std_ulogic_vector(request_msg); + byteenable <= pop_std_ulogic_vector(request_msg); + write <= '1'; + wait until rising_edge(clk) and waitrequest = '0'; + write <= '0'; + + elsif msg_type = bus_burst_write_msg then + address <= pop_std_ulogic_vector(request_msg); + burst := pop_integer(request_msg); + burstcount <= std_logic_vector(to_unsigned(burst, burstcount'length)); + for i in 0 to burst-1 loop + while rnd.Uniform(0.0, 1.0) > write_high_probability loop + wait until rising_edge(clk); + end loop; + writedata <= pop_std_ulogic_vector(request_msg); + -- TODO handle byteenable + byteenable(byteenable'range) <= (others => '1'); + write <= '1'; + wait until rising_edge(clk) and waitrequest = '0'; + write <= '0'; + address(address'range) <= (others => 'U'); + burstcount(burstcount'range) <= (others => 'U'); + end loop; + + elsif msg_type = wait_until_idle_msg then + wait until not burst_read_flag and is_empty(burst_acknowledge_queue) and rising_edge(clk); + handle_wait_until_idle(net, msg_type, request_msg); + + else + unexpected_msg_type(msg_type); + end if; + else + wait until rising_edge(clk); + end if; + end loop; + end process; + + read_capture : process + variable request_msg, reply_msg : msg_t; + begin + if use_readdatavalid then + wait until readdatavalid = '1' and not is_empty(acknowledge_queue) and rising_edge(clk); + else + -- Non-pipelined case: waits for slave to de-assert waitrequest and sample data after fixed_read_latency cycles. + wait until rising_edge(clk) and waitrequest = '0' and read = '1'; + if fixed_read_latency > 0 then + for i in 0 to fixed_read_latency - 1 loop + wait until rising_edge(clk); + end loop; + end if; + end if; + request_msg := pop(acknowledge_queue); + reply_msg := new_msg(sender => av_master_read_actor); + push_std_ulogic_vector(reply_msg, readdata); + reply(net, request_msg, reply_msg); + delete(request_msg); + end process; + + burst_read_capture : process + variable request_msg, reply_msg : msg_t; + variable burst : positive; + begin + wait until readdatavalid = '1' and not is_empty(burst_acknowledge_queue) and rising_edge(clk); + burst_read_flag <= true; + request_msg := pop(burst_acknowledge_queue); + burst := pop(burstlen_queue); + reply_msg := new_msg(sender => avmm_burst_rd_actor); + push_integer(reply_msg, burst); + push_std_ulogic_vector(reply_msg, readdata); + for i in 1 to burst-1 loop + wait until readdatavalid = '1' and rising_edge(clk) for 1 us; + check_true(readdatavalid = '1', "avalon master burst readdatavalid timeout"); + push_std_ulogic_vector(reply_msg, readdata); + end loop; + reply(net, request_msg, reply_msg); + delete(request_msg); + burst_read_flag <= false; + end process; + +end architecture; diff --git a/vunit/vhdl/verification_components/src/avalon_pkg.vhd b/vunit/vhdl/verification_components/src/avalon_pkg.vhd index ad2a24fb2..68a2283c5 100644 --- a/vunit/vhdl/verification_components/src/avalon_pkg.vhd +++ b/vunit/vhdl/verification_components/src/avalon_pkg.vhd @@ -1,56 +1,56 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com --- Author Slawomir Siluk slaweksiluk@gazeta.pl -library ieee; -use ieee.std_logic_1164.all; - -use work.queue_pkg.all; -use work.logger_pkg.all; -use work.memory_pkg.all; -context work.com_context; - -package avalon_pkg is - - type avalon_slave_t is record - readdatavalid_high_probability : real range 0.0 to 1.0; - waitrequest_high_probability : real range 0.0 to 1.0; - -- Private - p_actor : actor_t; - p_ack_actor : actor_t; - p_memory : memory_t; - p_logger : logger_t; - end record; - - constant avalon_slave_logger : logger_t := get_logger("vunit_lib:avalon_pkg"); - impure function new_avalon_slave( - memory : memory_t; - readdatavalid_high_probability : real := 1.0; - waitrequest_high_probability : real := 0.0; - name : string := ""; - logger : logger_t := avalon_slave_logger) - return avalon_slave_t; - -end package; -package body avalon_pkg is - - impure function new_avalon_slave( - memory : memory_t; - readdatavalid_high_probability : real := 1.0; - waitrequest_high_probability : real := 0.0; - name : string := ""; - logger : logger_t := avalon_slave_logger) - return avalon_slave_t is - begin - return (p_actor => new_actor(name), - p_ack_actor => new_actor(name&" read-ack"), - p_memory => to_vc_interface(memory, logger), - p_logger => logger, - readdatavalid_high_probability => readdatavalid_high_probability, - waitrequest_high_probability => waitrequest_high_probability - ); - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +-- Author Slawomir Siluk slaweksiluk@gazeta.pl +library ieee; +use ieee.std_logic_1164.all; + +use work.queue_pkg.all; +use work.logger_pkg.all; +use work.memory_pkg.all; +context work.com_context; + +package avalon_pkg is + + type avalon_slave_t is record + readdatavalid_high_probability : real range 0.0 to 1.0; + waitrequest_high_probability : real range 0.0 to 1.0; + -- Private + p_actor : actor_t; + p_ack_actor : actor_t; + p_memory : memory_t; + p_logger : logger_t; + end record; + + constant avalon_slave_logger : logger_t := get_logger("vunit_lib:avalon_pkg"); + impure function new_avalon_slave( + memory : memory_t; + readdatavalid_high_probability : real := 1.0; + waitrequest_high_probability : real := 0.0; + name : string := ""; + logger : logger_t := avalon_slave_logger) + return avalon_slave_t; + +end package; +package body avalon_pkg is + + impure function new_avalon_slave( + memory : memory_t; + readdatavalid_high_probability : real := 1.0; + waitrequest_high_probability : real := 0.0; + name : string := ""; + logger : logger_t := avalon_slave_logger) + return avalon_slave_t is + begin + return (p_actor => new_actor(name), + p_ack_actor => new_actor(name&" read-ack"), + p_memory => to_vc_interface(memory, logger), + p_logger => logger, + readdatavalid_high_probability => readdatavalid_high_probability, + waitrequest_high_probability => waitrequest_high_probability + ); + end; + +end package body; diff --git a/vunit/vhdl/verification_components/src/avalon_sink.vhd b/vunit/vhdl/verification_components/src/avalon_sink.vhd index 03e9c5ad5..099d111f1 100644 --- a/vunit/vhdl/verification_components/src/avalon_sink.vhd +++ b/vunit/vhdl/verification_components/src/avalon_sink.vhd @@ -1,85 +1,85 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com --- Author Slawomir Siluk slaweksiluk@gazeta.pl --- Avalon-St Sink Verification Component --- TODO: --- - timeout error - -library ieee; -use ieee.std_logic_1164.all; - -context work.vunit_context; -context work.com_context; -use work.stream_slave_pkg.all; -use work.avalon_stream_pkg.all; - -library osvvm; -use osvvm.RandomPkg.all; - -entity avalon_sink is - generic ( - sink : avalon_sink_t); - port ( - clk : in std_logic; - ready : out std_logic := '0'; - valid : in std_logic; - sop : in std_logic; - eop : in std_logic; - data : in std_logic_vector(data_length(sink)-1 downto 0) - ); -end entity; - -architecture a of avalon_sink is -begin - main : process - variable reply_msg, msg : msg_t; - variable msg_type : msg_type_t; - variable rnd : RandomPType; - variable avalon_stream_transaction : avalon_stream_transaction_t(data(data'range)); - begin - receive(net, sink.p_actor, msg); - msg_type := message_type(msg); - - if msg_type = stream_pop_msg or msg_type = pop_avalon_stream_msg then - -- Loop till got valid data - loop - while rnd.Uniform(0.0, 1.0) > sink.ready_high_probability loop - wait until rising_edge(clk); - end loop; - ready <= '1'; - wait until ready = '1' and rising_edge(clk); - if valid = '1' then - reply_msg := new_msg; - if msg_type = pop_avalon_stream_msg then - avalon_stream_transaction.data := data; - if sop = '1' then - avalon_stream_transaction.sop := true; - else - avalon_stream_transaction.sop := false; - end if; - if eop = '1' then - avalon_stream_transaction.eop := true; - else - avalon_stream_transaction.eop := false; - end if; - push_avalon_stream_transaction(reply_msg, avalon_stream_transaction); - else - push_std_ulogic_vector(reply_msg, data); - end if; - reply(net, msg, reply_msg); - ready <= '0'; - exit; - end if; - ready <= '0'; - end loop; - - else - unexpected_msg_type(msg_type); - end if; - - end process; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +-- Author Slawomir Siluk slaweksiluk@gazeta.pl +-- Avalon-St Sink Verification Component +-- TODO: +-- - timeout error + +library ieee; +use ieee.std_logic_1164.all; + +context work.vunit_context; +context work.com_context; +use work.stream_slave_pkg.all; +use work.avalon_stream_pkg.all; + +library osvvm; +use osvvm.RandomPkg.all; + +entity avalon_sink is + generic ( + sink : avalon_sink_t); + port ( + clk : in std_logic; + ready : out std_logic := '0'; + valid : in std_logic; + sop : in std_logic; + eop : in std_logic; + data : in std_logic_vector(data_length(sink)-1 downto 0) + ); +end entity; + +architecture a of avalon_sink is +begin + main : process + variable reply_msg, msg : msg_t; + variable msg_type : msg_type_t; + variable rnd : RandomPType; + variable avalon_stream_transaction : avalon_stream_transaction_t(data(data'range)); + begin + receive(net, sink.p_actor, msg); + msg_type := message_type(msg); + + if msg_type = stream_pop_msg or msg_type = pop_avalon_stream_msg then + -- Loop till got valid data + loop + while rnd.Uniform(0.0, 1.0) > sink.ready_high_probability loop + wait until rising_edge(clk); + end loop; + ready <= '1'; + wait until ready = '1' and rising_edge(clk); + if valid = '1' then + reply_msg := new_msg; + if msg_type = pop_avalon_stream_msg then + avalon_stream_transaction.data := data; + if sop = '1' then + avalon_stream_transaction.sop := true; + else + avalon_stream_transaction.sop := false; + end if; + if eop = '1' then + avalon_stream_transaction.eop := true; + else + avalon_stream_transaction.eop := false; + end if; + push_avalon_stream_transaction(reply_msg, avalon_stream_transaction); + else + push_std_ulogic_vector(reply_msg, data); + end if; + reply(net, msg, reply_msg); + ready <= '0'; + exit; + end if; + ready <= '0'; + end loop; + + else + unexpected_msg_type(msg_type); + end if; + + end process; + +end architecture; diff --git a/vunit/vhdl/verification_components/src/avalon_slave.vhd b/vunit/vhdl/verification_components/src/avalon_slave.vhd index 8d99ad247..24822ca57 100644 --- a/vunit/vhdl/verification_components/src/avalon_slave.vhd +++ b/vunit/vhdl/verification_components/src/avalon_slave.vhd @@ -1,116 +1,116 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com --- Author Slawomir Siluk slaweksiluk@gazeta.pl --- --- Avalon memory mapped slave wrapper for Vunit memory VC - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -context work.vunit_context; -context work.com_context; -use work.memory_pkg.all; -use work.avalon_pkg.all; - -library osvvm; -use osvvm.RandomPkg.all; - -entity avalon_slave is - generic ( - avalon_slave : avalon_slave_t - ); - port ( - clk : in std_logic; - address : in std_logic_vector; - byteenable : in std_logic_vector; - burstcount : in std_logic_vector; - waitrequest : out std_logic; - write : in std_logic; - writedata : in std_logic_vector; - read : in std_logic; - readdata : out std_logic_vector; - readdatavalid : out std_logic - ); -end entity; - -architecture a of avalon_slave is - - constant slave_read_msg : msg_type_t := new_msg_type("avmm slave read"); - -begin - - write_handler : process - variable pending_writes : positive := 1; - variable addr : natural; - begin - loop - wait until write = '1' and waitrequest = '0' and rising_edge(clk); - -- Burst write in progress - if pending_writes > 1 then - addr := addr + byteenable'length; - pending_writes := pending_writes -1; - write_word(avalon_slave.p_memory, addr, writedata); - -- Burst start or single burst - else - addr := to_integer(unsigned(address)); - pending_writes := to_integer(unsigned(burstcount)); - write_word(avalon_slave.p_memory, addr, writedata); - end if; - end loop; - end process; - - read_request : process - variable rd_request_msg : msg_t; - begin - wait until read = '1' and waitrequest = '0' and rising_edge(clk); - rd_request_msg := new_msg(slave_read_msg, avalon_slave.p_actor); - -- For read, only address is passed to ack proc - push_integer(rd_request_msg, to_integer(unsigned(burstcount))); - push_integer(rd_request_msg, to_integer(unsigned(address))); - send(net, avalon_slave.p_ack_actor, rd_request_msg); - end process; - - read_handler : process - variable request_msg : msg_t; - variable msg_type : msg_type_t; - variable baseaddr : natural; - variable burst : positive; - variable rnd : RandomPType; - begin - readdatavalid <= '0'; - receive(net, avalon_slave.p_ack_actor, request_msg); - msg_type := message_type(request_msg); - - if msg_type = slave_read_msg then - burst := pop_integer(request_msg); - baseaddr := pop_integer(request_msg); - for i in 0 to burst-1 loop - while rnd.Uniform(0.0, 1.0) > avalon_slave.readdatavalid_high_probability loop - wait until rising_edge(clk); - end loop; - readdata <= read_word(avalon_slave.p_memory, baseaddr + byteenable'length*i, byteenable'length); - readdatavalid <= '1'; - wait until rising_edge(clk); - readdatavalid <= '0'; - end loop; - - else - unexpected_msg_type(msg_type); - end if; - end process; - - waitrequest_stim: process - variable rnd : RandomPType; - begin - if rnd.Uniform(0.0, 1.0) < avalon_slave.waitrequest_high_probability then - waitrequest <= '1'; - else - waitrequest <= '0'; - end if; - wait until rising_edge(clk); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +-- Author Slawomir Siluk slaweksiluk@gazeta.pl +-- +-- Avalon memory mapped slave wrapper for Vunit memory VC + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +context work.vunit_context; +context work.com_context; +use work.memory_pkg.all; +use work.avalon_pkg.all; + +library osvvm; +use osvvm.RandomPkg.all; + +entity avalon_slave is + generic ( + avalon_slave : avalon_slave_t + ); + port ( + clk : in std_logic; + address : in std_logic_vector; + byteenable : in std_logic_vector; + burstcount : in std_logic_vector; + waitrequest : out std_logic; + write : in std_logic; + writedata : in std_logic_vector; + read : in std_logic; + readdata : out std_logic_vector; + readdatavalid : out std_logic + ); +end entity; + +architecture a of avalon_slave is + + constant slave_read_msg : msg_type_t := new_msg_type("avmm slave read"); + +begin + + write_handler : process + variable pending_writes : positive := 1; + variable addr : natural; + begin + loop + wait until write = '1' and waitrequest = '0' and rising_edge(clk); + -- Burst write in progress + if pending_writes > 1 then + addr := addr + byteenable'length; + pending_writes := pending_writes -1; + write_word(avalon_slave.p_memory, addr, writedata); + -- Burst start or single burst + else + addr := to_integer(unsigned(address)); + pending_writes := to_integer(unsigned(burstcount)); + write_word(avalon_slave.p_memory, addr, writedata); + end if; + end loop; + end process; + + read_request : process + variable rd_request_msg : msg_t; + begin + wait until read = '1' and waitrequest = '0' and rising_edge(clk); + rd_request_msg := new_msg(slave_read_msg, avalon_slave.p_actor); + -- For read, only address is passed to ack proc + push_integer(rd_request_msg, to_integer(unsigned(burstcount))); + push_integer(rd_request_msg, to_integer(unsigned(address))); + send(net, avalon_slave.p_ack_actor, rd_request_msg); + end process; + + read_handler : process + variable request_msg : msg_t; + variable msg_type : msg_type_t; + variable baseaddr : natural; + variable burst : positive; + variable rnd : RandomPType; + begin + readdatavalid <= '0'; + receive(net, avalon_slave.p_ack_actor, request_msg); + msg_type := message_type(request_msg); + + if msg_type = slave_read_msg then + burst := pop_integer(request_msg); + baseaddr := pop_integer(request_msg); + for i in 0 to burst-1 loop + while rnd.Uniform(0.0, 1.0) > avalon_slave.readdatavalid_high_probability loop + wait until rising_edge(clk); + end loop; + readdata <= read_word(avalon_slave.p_memory, baseaddr + byteenable'length*i, byteenable'length); + readdatavalid <= '1'; + wait until rising_edge(clk); + readdatavalid <= '0'; + end loop; + + else + unexpected_msg_type(msg_type); + end if; + end process; + + waitrequest_stim: process + variable rnd : RandomPType; + begin + if rnd.Uniform(0.0, 1.0) < avalon_slave.waitrequest_high_probability then + waitrequest <= '1'; + else + waitrequest <= '0'; + end if; + wait until rising_edge(clk); + end process; +end architecture; diff --git a/vunit/vhdl/verification_components/src/avalon_source.vhd b/vunit/vhdl/verification_components/src/avalon_source.vhd index df81797cf..3e63c4f15 100644 --- a/vunit/vhdl/verification_components/src/avalon_source.vhd +++ b/vunit/vhdl/verification_components/src/avalon_source.vhd @@ -1,79 +1,79 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com --- Author Slawomir Siluk slaweksiluk@gazeta.pl --- Avalon-St Source Verification Component -library ieee; -use ieee.std_logic_1164.all; - -context work.vunit_context; -context work.com_context; -use work.stream_master_pkg.all; -use work.avalon_stream_pkg.all; -use work.queue_pkg.all; -use work.sync_pkg.all; - -library osvvm; -use osvvm.RandomPkg.all; - -entity avalon_source is - generic ( - source : avalon_source_t); - port ( - clk : in std_logic; - ready : in std_logic; - valid : out std_logic := '0'; - sop : out std_logic := '0'; - eop : out std_logic := '0'; - data : out std_logic_vector(data_length(source)-1 downto 0) := (others => '0') - ); -end entity; - -architecture a of avalon_source is -begin - main : process - variable msg : msg_t; - variable msg_type : msg_type_t; - variable rnd : RandomPType; - variable avalon_stream_transaction : avalon_stream_transaction_t(data(data'range)); - begin - receive(net, source.p_actor, msg); - msg_type := message_type(msg); - - handle_sync_message(net, msg_type, msg); - - if msg_type = stream_push_msg or msg_type = push_avalon_stream_msg then - while rnd.Uniform(0.0, 1.0) > source.valid_high_probability loop - wait until rising_edge(clk); - end loop; - valid <= '1'; - if msg_type = push_avalon_stream_msg then - pop_avalon_stream_transaction(msg, avalon_stream_transaction); - data <= avalon_stream_transaction.data; - if avalon_stream_transaction.sop then - sop <= '1'; - else - sop <= '0'; - end if; - if avalon_stream_transaction.eop then - eop <= '1'; - else - eop <= '0'; - end if; - else - data <= pop_std_ulogic_vector(msg); - sop <= '0'; - eop <= '0'; - end if; - wait until (valid and ready) = '1' and rising_edge(clk); - valid <= '0'; - sop <= '0'; - eop <= '0'; - else - unexpected_msg_type(msg_type); - end if; - end process; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +-- Author Slawomir Siluk slaweksiluk@gazeta.pl +-- Avalon-St Source Verification Component +library ieee; +use ieee.std_logic_1164.all; + +context work.vunit_context; +context work.com_context; +use work.stream_master_pkg.all; +use work.avalon_stream_pkg.all; +use work.queue_pkg.all; +use work.sync_pkg.all; + +library osvvm; +use osvvm.RandomPkg.all; + +entity avalon_source is + generic ( + source : avalon_source_t); + port ( + clk : in std_logic; + ready : in std_logic; + valid : out std_logic := '0'; + sop : out std_logic := '0'; + eop : out std_logic := '0'; + data : out std_logic_vector(data_length(source)-1 downto 0) := (others => '0') + ); +end entity; + +architecture a of avalon_source is +begin + main : process + variable msg : msg_t; + variable msg_type : msg_type_t; + variable rnd : RandomPType; + variable avalon_stream_transaction : avalon_stream_transaction_t(data(data'range)); + begin + receive(net, source.p_actor, msg); + msg_type := message_type(msg); + + handle_sync_message(net, msg_type, msg); + + if msg_type = stream_push_msg or msg_type = push_avalon_stream_msg then + while rnd.Uniform(0.0, 1.0) > source.valid_high_probability loop + wait until rising_edge(clk); + end loop; + valid <= '1'; + if msg_type = push_avalon_stream_msg then + pop_avalon_stream_transaction(msg, avalon_stream_transaction); + data <= avalon_stream_transaction.data; + if avalon_stream_transaction.sop then + sop <= '1'; + else + sop <= '0'; + end if; + if avalon_stream_transaction.eop then + eop <= '1'; + else + eop <= '0'; + end if; + else + data <= pop_std_ulogic_vector(msg); + sop <= '0'; + eop <= '0'; + end if; + wait until (valid and ready) = '1' and rising_edge(clk); + valid <= '0'; + sop <= '0'; + eop <= '0'; + else + unexpected_msg_type(msg_type); + end if; + end process; + +end architecture; diff --git a/vunit/vhdl/verification_components/src/avalon_stream_pkg.vhd b/vunit/vhdl/verification_components/src/avalon_stream_pkg.vhd index 1b7fbe75c..d676c5fba 100644 --- a/vunit/vhdl/verification_components/src/avalon_stream_pkg.vhd +++ b/vunit/vhdl/verification_components/src/avalon_stream_pkg.vhd @@ -1,225 +1,225 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -use work.logger_pkg.all; -use work.stream_master_pkg.all; -use work.stream_slave_pkg.all; -context work.com_context; -context work.data_types_context; - -package avalon_stream_pkg is - - type avalon_source_t is record - valid_high_probability : real range 0.0 to 1.0; - p_actor : actor_t; - p_data_length : natural; - p_logger : logger_t; - end record; - - type avalon_sink_t is record - ready_high_probability : real range 0.0 to 1.0; - -- Private - p_actor : actor_t; - p_data_length : natural; - p_logger : logger_t; - end record; - - constant avalon_stream_logger : logger_t := get_logger("vunit_lib:avalon_stream_pkg"); - impure function new_avalon_source(data_length : natural; - valid_high_probability : real := 1.0; - logger : logger_t := avalon_stream_logger; - actor : actor_t := null_actor) return avalon_source_t; - impure function new_avalon_sink(data_length : natural; - ready_high_probability : real := 1.0; - logger : logger_t := avalon_stream_logger; - actor : actor_t := null_actor) return avalon_sink_t; - impure function data_length(source : avalon_source_t) return natural; - impure function data_length(source : avalon_sink_t) return natural; - impure function as_stream(source : avalon_source_t) return stream_master_t; - impure function as_stream(sink : avalon_sink_t) return stream_slave_t; - - constant push_avalon_stream_msg : msg_type_t := new_msg_type("push avalon stream"); - constant pop_avalon_stream_msg : msg_type_t := new_msg_type("pop avalon stream"); - constant avalon_stream_transaction_msg : msg_type_t := new_msg_type("avalon stream transaction"); - - procedure push_avalon_stream(signal net : inout network_t; - avalon_source : avalon_source_t; - data : std_logic_vector; - sop : std_logic := '0'; - eop : std_logic := '0'); - - procedure pop_avalon_stream(signal net : inout network_t; - avalon_sink : avalon_sink_t; - variable data : inout std_logic_vector; - variable sop : inout std_logic; - variable eop : inout std_logic); - - type avalon_stream_transaction_t is record - data : std_logic_vector; - sop : boolean; - eop : boolean; - end record; - - procedure push_avalon_stream_transaction(msg : msg_t; avalon_stream_transaction : avalon_stream_transaction_t); - procedure pop_avalon_stream_transaction( - constant msg : in msg_t; - variable avalon_stream_transaction : out avalon_stream_transaction_t - ); - - impure function new_avalon_stream_transaction_msg( - avalon_stream_transaction : avalon_stream_transaction_t - ) return msg_t; - - procedure handle_avalon_stream_transaction( - variable msg_type : inout msg_type_t; - variable msg : inout msg_t; - variable avalon_transaction : out avalon_stream_transaction_t - ); -end package; - -package body avalon_stream_pkg is - - impure function new_avalon_source(data_length : natural; - valid_high_probability : real := 1.0; - logger : logger_t := avalon_stream_logger; - actor : actor_t := null_actor) return avalon_source_t is - variable p_actor : actor_t; - begin - p_actor := actor when actor /= null_actor else new_actor; - - return (valid_high_probability => valid_high_probability, - p_actor => p_actor, - p_data_length => data_length, - p_logger => logger); - end; - - impure function new_avalon_sink(data_length : natural; - ready_high_probability : real := 1.0; - logger : logger_t := avalon_stream_logger; - actor : actor_t := null_actor) return avalon_sink_t is - variable p_actor : actor_t; - begin - p_actor := actor when actor /= null_actor else new_actor; - - return (ready_high_probability => ready_high_probability, - p_actor => p_actor, - p_data_length => data_length, - p_logger => logger); - end; - - impure function data_length(source : avalon_source_t) return natural is - begin - return source.p_data_length; - end; - - impure function data_length(source : avalon_sink_t) return natural is - begin - return source.p_data_length; - end; - - impure function as_stream(source : avalon_source_t) return stream_master_t is - begin - return (p_actor => source.p_actor); - end; - - impure function as_stream(sink : avalon_sink_t) return stream_slave_t is - begin - return (p_actor => sink.p_actor); -end; - - procedure push_avalon_stream(signal net : inout network_t; - avalon_source : avalon_source_t; - data : std_logic_vector; - sop : std_logic := '0'; - eop : std_logic := '0') is - variable msg : msg_t := new_msg(push_avalon_stream_msg); - variable avalon_stream_transaction : avalon_stream_transaction_t(data(data'length - 1 downto 0)); - begin - avalon_stream_transaction.data := data; - if sop = '1' then - avalon_stream_transaction.sop := true; - else - avalon_stream_transaction.sop := false; - end if; - if eop = '1' then - avalon_stream_transaction.eop := true; - else - avalon_stream_transaction.eop := false; - end if; - push_avalon_stream_transaction(msg, avalon_stream_transaction); - send(net, avalon_source.p_actor, msg); - end; - - procedure pop_avalon_stream(signal net : inout network_t; - avalon_sink : avalon_sink_t; - variable data : inout std_logic_vector; - variable sop : inout std_logic; - variable eop : inout std_logic) is - variable reference : msg_t := new_msg(pop_avalon_stream_msg); - variable reply_msg : msg_t; - variable avalon_stream_transaction : avalon_stream_transaction_t(data(data'length - 1 downto 0)); -begin - send(net, avalon_sink.p_actor, reference); - receive_reply(net, reference, reply_msg); - pop_avalon_stream_transaction(reply_msg, avalon_stream_transaction); - data := avalon_stream_transaction.data; - if avalon_stream_transaction.sop then - sop := '1'; - else - sop := '0'; - end if; - if avalon_stream_transaction.eop then - eop := '1'; - else - eop := '0'; - end if; - delete(reference); - delete(reply_msg); - end; - - procedure push_avalon_stream_transaction(msg: msg_t; avalon_stream_transaction : avalon_stream_transaction_t) is - begin - push_std_ulogic_vector(msg, avalon_stream_transaction.data); - push_boolean(msg, avalon_stream_transaction.sop); - push_boolean(msg, avalon_stream_transaction.eop); - end; - - procedure pop_avalon_stream_transaction( - constant msg : in msg_t; - variable avalon_stream_transaction : out avalon_stream_transaction_t) is - begin - avalon_stream_transaction.data := pop_std_ulogic_vector(msg); - avalon_stream_transaction.sop := pop_boolean(msg); - avalon_stream_transaction.eop := pop_boolean(msg); - end; - - impure function new_avalon_stream_transaction_msg( - avalon_stream_transaction : avalon_stream_transaction_t - ) return msg_t is - variable msg : msg_t; - begin - msg := new_msg(avalon_stream_transaction_msg); - push_avalon_stream_transaction(msg, avalon_stream_transaction); - - return msg; - end; - - procedure handle_avalon_stream_transaction( - variable msg_type : inout msg_type_t; - variable msg : inout msg_t; - variable avalon_transaction : out avalon_stream_transaction_t) is - begin - if msg_type = avalon_stream_transaction_msg then - handle_message(msg_type); - - pop_avalon_stream_transaction(msg, avalon_transaction); - end if; - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +use work.logger_pkg.all; +use work.stream_master_pkg.all; +use work.stream_slave_pkg.all; +context work.com_context; +context work.data_types_context; + +package avalon_stream_pkg is + + type avalon_source_t is record + valid_high_probability : real range 0.0 to 1.0; + p_actor : actor_t; + p_data_length : natural; + p_logger : logger_t; + end record; + + type avalon_sink_t is record + ready_high_probability : real range 0.0 to 1.0; + -- Private + p_actor : actor_t; + p_data_length : natural; + p_logger : logger_t; + end record; + + constant avalon_stream_logger : logger_t := get_logger("vunit_lib:avalon_stream_pkg"); + impure function new_avalon_source(data_length : natural; + valid_high_probability : real := 1.0; + logger : logger_t := avalon_stream_logger; + actor : actor_t := null_actor) return avalon_source_t; + impure function new_avalon_sink(data_length : natural; + ready_high_probability : real := 1.0; + logger : logger_t := avalon_stream_logger; + actor : actor_t := null_actor) return avalon_sink_t; + impure function data_length(source : avalon_source_t) return natural; + impure function data_length(source : avalon_sink_t) return natural; + impure function as_stream(source : avalon_source_t) return stream_master_t; + impure function as_stream(sink : avalon_sink_t) return stream_slave_t; + + constant push_avalon_stream_msg : msg_type_t := new_msg_type("push avalon stream"); + constant pop_avalon_stream_msg : msg_type_t := new_msg_type("pop avalon stream"); + constant avalon_stream_transaction_msg : msg_type_t := new_msg_type("avalon stream transaction"); + + procedure push_avalon_stream(signal net : inout network_t; + avalon_source : avalon_source_t; + data : std_logic_vector; + sop : std_logic := '0'; + eop : std_logic := '0'); + + procedure pop_avalon_stream(signal net : inout network_t; + avalon_sink : avalon_sink_t; + variable data : inout std_logic_vector; + variable sop : inout std_logic; + variable eop : inout std_logic); + + type avalon_stream_transaction_t is record + data : std_logic_vector; + sop : boolean; + eop : boolean; + end record; + + procedure push_avalon_stream_transaction(msg : msg_t; avalon_stream_transaction : avalon_stream_transaction_t); + procedure pop_avalon_stream_transaction( + constant msg : in msg_t; + variable avalon_stream_transaction : out avalon_stream_transaction_t + ); + + impure function new_avalon_stream_transaction_msg( + avalon_stream_transaction : avalon_stream_transaction_t + ) return msg_t; + + procedure handle_avalon_stream_transaction( + variable msg_type : inout msg_type_t; + variable msg : inout msg_t; + variable avalon_transaction : out avalon_stream_transaction_t + ); +end package; + +package body avalon_stream_pkg is + + impure function new_avalon_source(data_length : natural; + valid_high_probability : real := 1.0; + logger : logger_t := avalon_stream_logger; + actor : actor_t := null_actor) return avalon_source_t is + variable p_actor : actor_t; + begin + p_actor := actor when actor /= null_actor else new_actor; + + return (valid_high_probability => valid_high_probability, + p_actor => p_actor, + p_data_length => data_length, + p_logger => logger); + end; + + impure function new_avalon_sink(data_length : natural; + ready_high_probability : real := 1.0; + logger : logger_t := avalon_stream_logger; + actor : actor_t := null_actor) return avalon_sink_t is + variable p_actor : actor_t; + begin + p_actor := actor when actor /= null_actor else new_actor; + + return (ready_high_probability => ready_high_probability, + p_actor => p_actor, + p_data_length => data_length, + p_logger => logger); + end; + + impure function data_length(source : avalon_source_t) return natural is + begin + return source.p_data_length; + end; + + impure function data_length(source : avalon_sink_t) return natural is + begin + return source.p_data_length; + end; + + impure function as_stream(source : avalon_source_t) return stream_master_t is + begin + return (p_actor => source.p_actor); + end; + + impure function as_stream(sink : avalon_sink_t) return stream_slave_t is + begin + return (p_actor => sink.p_actor); +end; + + procedure push_avalon_stream(signal net : inout network_t; + avalon_source : avalon_source_t; + data : std_logic_vector; + sop : std_logic := '0'; + eop : std_logic := '0') is + variable msg : msg_t := new_msg(push_avalon_stream_msg); + variable avalon_stream_transaction : avalon_stream_transaction_t(data(data'length - 1 downto 0)); + begin + avalon_stream_transaction.data := data; + if sop = '1' then + avalon_stream_transaction.sop := true; + else + avalon_stream_transaction.sop := false; + end if; + if eop = '1' then + avalon_stream_transaction.eop := true; + else + avalon_stream_transaction.eop := false; + end if; + push_avalon_stream_transaction(msg, avalon_stream_transaction); + send(net, avalon_source.p_actor, msg); + end; + + procedure pop_avalon_stream(signal net : inout network_t; + avalon_sink : avalon_sink_t; + variable data : inout std_logic_vector; + variable sop : inout std_logic; + variable eop : inout std_logic) is + variable reference : msg_t := new_msg(pop_avalon_stream_msg); + variable reply_msg : msg_t; + variable avalon_stream_transaction : avalon_stream_transaction_t(data(data'length - 1 downto 0)); +begin + send(net, avalon_sink.p_actor, reference); + receive_reply(net, reference, reply_msg); + pop_avalon_stream_transaction(reply_msg, avalon_stream_transaction); + data := avalon_stream_transaction.data; + if avalon_stream_transaction.sop then + sop := '1'; + else + sop := '0'; + end if; + if avalon_stream_transaction.eop then + eop := '1'; + else + eop := '0'; + end if; + delete(reference); + delete(reply_msg); + end; + + procedure push_avalon_stream_transaction(msg: msg_t; avalon_stream_transaction : avalon_stream_transaction_t) is + begin + push_std_ulogic_vector(msg, avalon_stream_transaction.data); + push_boolean(msg, avalon_stream_transaction.sop); + push_boolean(msg, avalon_stream_transaction.eop); + end; + + procedure pop_avalon_stream_transaction( + constant msg : in msg_t; + variable avalon_stream_transaction : out avalon_stream_transaction_t) is + begin + avalon_stream_transaction.data := pop_std_ulogic_vector(msg); + avalon_stream_transaction.sop := pop_boolean(msg); + avalon_stream_transaction.eop := pop_boolean(msg); + end; + + impure function new_avalon_stream_transaction_msg( + avalon_stream_transaction : avalon_stream_transaction_t + ) return msg_t is + variable msg : msg_t; + begin + msg := new_msg(avalon_stream_transaction_msg); + push_avalon_stream_transaction(msg, avalon_stream_transaction); + + return msg; + end; + + procedure handle_avalon_stream_transaction( + variable msg_type : inout msg_type_t; + variable msg : inout msg_t; + variable avalon_transaction : out avalon_stream_transaction_t) is + begin + if msg_type = avalon_stream_transaction_msg then + handle_message(msg_type); + + pop_avalon_stream_transaction(msg, avalon_transaction); + end if; + end; + +end package body; diff --git a/vunit/vhdl/verification_components/src/axi_lite_master.vhd b/vunit/vhdl/verification_components/src/axi_lite_master.vhd index bbc7c8e4c..9c11e2b27 100644 --- a/vunit/vhdl/verification_components/src/axi_lite_master.vhd +++ b/vunit/vhdl/verification_components/src/axi_lite_master.vhd @@ -1,154 +1,154 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - - -library ieee; -use ieee.std_logic_1164.all; - -use work.queue_pkg.all; -use work.bus_master_pkg.all; -use work.sync_pkg.all; -use work.axi_pkg.all; -use work.axi_slave_pkg.all; -use work.axi_slave_private_pkg.all; -use work.axi_lite_master_pkg.all; -context work.com_context; -context work.vunit_context; - -entity axi_lite_master is - generic( - bus_handle : bus_master_t - ); - port( - aclk : in std_logic; - - arready : in std_logic; - arvalid : out std_logic := '0'; - araddr : out std_logic_vector(address_length(bus_handle) - 1 downto 0) := (others => '0'); - - rready : out std_logic := '0'; - rvalid : in std_logic; - rdata : in std_logic_vector(data_length(bus_handle) - 1 downto 0); - rresp : in axi_resp_t; - - awready : in std_logic; - awvalid : out std_logic := '0'; - awaddr : out std_logic_vector(address_length(bus_handle) - 1 downto 0) := (others => '0'); - - wready : in std_logic; - wvalid : out std_logic := '0'; - wdata : out std_logic_vector(data_length(bus_handle) - 1 downto 0) := (others => '0'); - wstrb : out std_logic_vector(byte_enable_length(bus_handle) - 1 downto 0) := (others => '0'); - - bvalid : in std_logic; - bready : out std_logic := '0'; - bresp : in axi_resp_t := axi_resp_okay); -end entity; - -architecture a of axi_lite_master is - constant reply_queue, message_queue : queue_t := new_queue; -begin - main : process - variable request_msg : msg_t; - variable msg_type : msg_type_t; - begin - receive(net, bus_handle.p_actor, request_msg); - msg_type := message_type(request_msg); - - if is_read(msg_type) or is_write(msg_type) then - push(message_queue, request_msg); - elsif msg_type = wait_until_idle_msg then - wait until ((bvalid and bready) = '1' or (rvalid and rready) = '1') and is_empty(message_queue) and rising_edge(aclk); - handle_wait_until_idle(net, msg_type, request_msg); - else - unexpected_msg_type(msg_type); - end if; - end process; - - -- Use separate process to always align to rising edge of clock - bus_process : process - variable request_msg : msg_t; - variable msg_type : msg_type_t; - variable w_done, aw_done : boolean; - variable expected_resp : axi_resp_t; - begin - wait until rising_edge(aclk) and not is_empty(message_queue); - - request_msg := pop(message_queue); - msg_type := message_type(request_msg); - - if is_read(msg_type) then - araddr <= pop_std_ulogic_vector(request_msg); - expected_resp := pop_std_ulogic_vector(request_msg) when is_axi_lite_msg(msg_type) else axi_resp_okay; - push(reply_queue, request_msg); - - arvalid <= '1'; - wait until (arvalid and arready) = '1' and rising_edge(aclk); - arvalid <= '0'; - - rready <= '1'; - wait until (rvalid and rready) = '1' and rising_edge(aclk); - rready <= '0'; - check_axi_resp(bus_handle, rresp, expected_resp, "rresp"); - - if is_visible(bus_handle.p_logger, debug) then - debug(bus_handle.p_logger, - "Read 0x" & to_hstring(rdata) & - " from address 0x" & to_hstring(araddr)); - end if; - - elsif is_write(msg_type) then - awaddr <= pop_std_ulogic_vector(request_msg); - wdata <= pop_std_ulogic_vector(request_msg); - wstrb <= pop_std_ulogic_vector(request_msg); - expected_resp := pop_std_ulogic_vector(request_msg) when is_axi_lite_msg(msg_type) else axi_resp_okay; - delete(request_msg); - - wvalid <= '1'; - awvalid <= '1'; - - w_done := false; - aw_done := false; - while not (w_done and aw_done) loop - wait until ((awvalid and awready) = '1' or (wvalid and wready) = '1') and rising_edge(aclk); - - if (awvalid and awready) = '1' then - awvalid <= '0'; - aw_done := true; - end if; - - if (wvalid and wready) = '1' then - wvalid <= '0'; - w_done := true; - end if; - end loop; - - bready <= '1'; - wait until (bvalid and bready) = '1' and rising_edge(aclk); - bready <= '0'; - check_axi_resp(bus_handle, bresp, expected_resp, "bresp"); - - if is_visible(bus_handle.p_logger, debug) then - debug(bus_handle.p_logger, - "Wrote 0x" & to_hstring(wdata) & - " to address 0x" & to_hstring(awaddr)); - end if; - end if; - end process; - - -- Reply in separate process do not destroy alignment with the clock - read_reply : process - variable request_msg, reply_msg : msg_t; - begin - wait until (rvalid and rready) = '1' and rising_edge(aclk); - request_msg := pop(reply_queue); - reply_msg := new_msg; - push_std_ulogic_vector(reply_msg, rdata); - reply(net, request_msg, reply_msg); - delete(request_msg); - end process; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + + +library ieee; +use ieee.std_logic_1164.all; + +use work.queue_pkg.all; +use work.bus_master_pkg.all; +use work.sync_pkg.all; +use work.axi_pkg.all; +use work.axi_slave_pkg.all; +use work.axi_slave_private_pkg.all; +use work.axi_lite_master_pkg.all; +context work.com_context; +context work.vunit_context; + +entity axi_lite_master is + generic( + bus_handle : bus_master_t + ); + port( + aclk : in std_logic; + + arready : in std_logic; + arvalid : out std_logic := '0'; + araddr : out std_logic_vector(address_length(bus_handle) - 1 downto 0) := (others => '0'); + + rready : out std_logic := '0'; + rvalid : in std_logic; + rdata : in std_logic_vector(data_length(bus_handle) - 1 downto 0); + rresp : in axi_resp_t; + + awready : in std_logic; + awvalid : out std_logic := '0'; + awaddr : out std_logic_vector(address_length(bus_handle) - 1 downto 0) := (others => '0'); + + wready : in std_logic; + wvalid : out std_logic := '0'; + wdata : out std_logic_vector(data_length(bus_handle) - 1 downto 0) := (others => '0'); + wstrb : out std_logic_vector(byte_enable_length(bus_handle) - 1 downto 0) := (others => '0'); + + bvalid : in std_logic; + bready : out std_logic := '0'; + bresp : in axi_resp_t := axi_resp_okay); +end entity; + +architecture a of axi_lite_master is + constant reply_queue, message_queue : queue_t := new_queue; +begin + main : process + variable request_msg : msg_t; + variable msg_type : msg_type_t; + begin + receive(net, bus_handle.p_actor, request_msg); + msg_type := message_type(request_msg); + + if is_read(msg_type) or is_write(msg_type) then + push(message_queue, request_msg); + elsif msg_type = wait_until_idle_msg then + wait until ((bvalid and bready) = '1' or (rvalid and rready) = '1') and is_empty(message_queue) and rising_edge(aclk); + handle_wait_until_idle(net, msg_type, request_msg); + else + unexpected_msg_type(msg_type); + end if; + end process; + + -- Use separate process to always align to rising edge of clock + bus_process : process + variable request_msg : msg_t; + variable msg_type : msg_type_t; + variable w_done, aw_done : boolean; + variable expected_resp : axi_resp_t; + begin + wait until rising_edge(aclk) and not is_empty(message_queue); + + request_msg := pop(message_queue); + msg_type := message_type(request_msg); + + if is_read(msg_type) then + araddr <= pop_std_ulogic_vector(request_msg); + expected_resp := pop_std_ulogic_vector(request_msg) when is_axi_lite_msg(msg_type) else axi_resp_okay; + push(reply_queue, request_msg); + + arvalid <= '1'; + wait until (arvalid and arready) = '1' and rising_edge(aclk); + arvalid <= '0'; + + rready <= '1'; + wait until (rvalid and rready) = '1' and rising_edge(aclk); + rready <= '0'; + check_axi_resp(bus_handle, rresp, expected_resp, "rresp"); + + if is_visible(bus_handle.p_logger, debug) then + debug(bus_handle.p_logger, + "Read 0x" & to_hstring(rdata) & + " from address 0x" & to_hstring(araddr)); + end if; + + elsif is_write(msg_type) then + awaddr <= pop_std_ulogic_vector(request_msg); + wdata <= pop_std_ulogic_vector(request_msg); + wstrb <= pop_std_ulogic_vector(request_msg); + expected_resp := pop_std_ulogic_vector(request_msg) when is_axi_lite_msg(msg_type) else axi_resp_okay; + delete(request_msg); + + wvalid <= '1'; + awvalid <= '1'; + + w_done := false; + aw_done := false; + while not (w_done and aw_done) loop + wait until ((awvalid and awready) = '1' or (wvalid and wready) = '1') and rising_edge(aclk); + + if (awvalid and awready) = '1' then + awvalid <= '0'; + aw_done := true; + end if; + + if (wvalid and wready) = '1' then + wvalid <= '0'; + w_done := true; + end if; + end loop; + + bready <= '1'; + wait until (bvalid and bready) = '1' and rising_edge(aclk); + bready <= '0'; + check_axi_resp(bus_handle, bresp, expected_resp, "bresp"); + + if is_visible(bus_handle.p_logger, debug) then + debug(bus_handle.p_logger, + "Wrote 0x" & to_hstring(wdata) & + " to address 0x" & to_hstring(awaddr)); + end if; + end if; + end process; + + -- Reply in separate process do not destroy alignment with the clock + read_reply : process + variable request_msg, reply_msg : msg_t; + begin + wait until (rvalid and rready) = '1' and rising_edge(aclk); + request_msg := pop(reply_queue); + reply_msg := new_msg; + push_std_ulogic_vector(reply_msg, rdata); + reply(net, request_msg, reply_msg); + delete(request_msg); + end process; + +end architecture; diff --git a/vunit/vhdl/verification_components/src/axi_lite_master_pkg.vhd b/vunit/vhdl/verification_components/src/axi_lite_master_pkg.vhd index cdac02292..4ef8cc5c3 100644 --- a/vunit/vhdl/verification_components/src/axi_lite_master_pkg.vhd +++ b/vunit/vhdl/verification_components/src/axi_lite_master_pkg.vhd @@ -1,164 +1,164 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -use work.axi_pkg.all; -use work.bus_master_pkg.all; -context work.com_context; -context work.vunit_context; - -package axi_lite_master_pkg is - - constant axi_lite_read_msg : msg_type_t := new_msg_type("read axi lite"); - constant axi_lite_write_msg : msg_type_t := new_msg_type("write axi lite"); - - -- Blocking: Write the bus - procedure write_axi_lite(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant data : std_logic_vector; - constant expected_bresp : axi_resp_t := axi_resp_okay; - -- default byte enable is all bytes - constant byte_enable : std_logic_vector := ""); - - -- Non blocking: Read the bus returning a reference to the future reply - procedure read_axi_lite(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant expected_rresp : axi_resp_t := axi_resp_okay; - variable reference : inout bus_reference_t); - - -- Blocking: read bus with immediate reply - procedure read_axi_lite(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant expected_rresp : axi_resp_t := axi_resp_okay; - variable data : inout std_logic_vector); - - -- Blocking: Read bus and check result against expected data - procedure check_axi_lite(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant expected_rresp : axi_resp_t := axi_resp_okay; - constant expected : std_logic_vector; - constant msg : string := ""); - - function is_read(msg_type : msg_type_t) return boolean; - function is_write(msg_type : msg_type_t) return boolean; - function is_axi_lite_msg(msg_type : msg_type_t) return boolean; - -end package; - -package body axi_lite_master_pkg is - - procedure write_axi_lite(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant data : std_logic_vector; - constant expected_bresp : axi_resp_t := axi_resp_okay; - -- default byte enable is all bytes - constant byte_enable : std_logic_vector := "") is - variable request_msg : msg_t := new_msg(axi_lite_write_msg); - variable full_data : std_logic_vector(bus_handle.p_data_length - 1 downto 0) := (others => '0'); - variable full_address : std_logic_vector(bus_handle.p_address_length - 1 downto 0) := (others => '0'); - variable full_byte_enable : std_logic_vector(byte_enable_length(bus_handle) - 1 downto 0); - begin - full_address(address'length - 1 downto 0) := address; - push_std_ulogic_vector(request_msg, full_address); - - full_data(data'length - 1 downto 0) := data; - push_std_ulogic_vector(request_msg, full_data); - - if byte_enable = "" then - full_byte_enable := (others => '1'); - else - full_byte_enable(byte_enable'length - 1 downto 0) := byte_enable; - end if; - push_std_ulogic_vector(request_msg, full_byte_enable); - - push_std_ulogic_vector(request_msg, expected_bresp); - - send(net, bus_handle.p_actor, request_msg); - end procedure; - - procedure read_axi_lite(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant expected_rresp : axi_resp_t := axi_resp_okay; - variable reference : inout bus_reference_t) is - variable full_address : std_logic_vector(bus_handle.p_address_length - 1 downto 0) := (others => '0'); - alias request_msg : msg_t is reference; - begin - request_msg := new_msg(axi_lite_read_msg); - full_address(address'length - 1 downto 0) := address; - push_std_ulogic_vector(request_msg, full_address); - push_std_ulogic_vector(request_msg, expected_rresp); - send(net, bus_handle.p_actor, request_msg); - end procedure; - - procedure read_axi_lite(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant expected_rresp : axi_resp_t := axi_resp_okay; - variable data : inout std_logic_vector) is - variable reference : bus_reference_t; - begin - read_axi_lite(net, bus_handle, address, expected_rresp, reference); - await_read_bus_reply(net, reference, data); - end procedure; - - procedure check_axi_lite(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant expected_rresp : axi_resp_t := axi_resp_okay; - constant expected : std_logic_vector; - constant msg : string := "") is - variable data : std_logic_vector(bus_handle.p_data_length - 1 downto 0); - variable edata : std_logic_vector(data'range) := (others => '0'); - - impure function error_prefix return string is - begin - if msg = "" then - return "check_bus(x""" & to_hstring(address) & """)"; - else - return msg; - end if; - end; - - impure function base_error return string is - begin - return error_prefix & " - Got x""" & to_hstring(data) & """ expected x""" & to_hstring(edata) & """"; - end; - begin - - edata(expected'length - 1 downto 0) := expected; - - read_axi_lite(net, bus_handle, address, expected_rresp, data); - if not std_match(data, edata) then - failure(bus_handle.p_logger, base_error); - end if; - end procedure; - - function is_read(msg_type : msg_type_t) return boolean is - begin - return msg_type = bus_read_msg or msg_type = axi_lite_read_msg; - end function; - - function is_write(msg_type : msg_type_t) return boolean is - begin - return msg_type = bus_write_msg or msg_type = axi_lite_write_msg; - end function; - - function is_axi_lite_msg(msg_type : msg_type_t) return boolean is - begin - return msg_type = axi_lite_read_msg or msg_type = axi_lite_write_msg; - end function; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.axi_pkg.all; +use work.bus_master_pkg.all; +context work.com_context; +context work.vunit_context; + +package axi_lite_master_pkg is + + constant axi_lite_read_msg : msg_type_t := new_msg_type("read axi lite"); + constant axi_lite_write_msg : msg_type_t := new_msg_type("write axi lite"); + + -- Blocking: Write the bus + procedure write_axi_lite(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant data : std_logic_vector; + constant expected_bresp : axi_resp_t := axi_resp_okay; + -- default byte enable is all bytes + constant byte_enable : std_logic_vector := ""); + + -- Non blocking: Read the bus returning a reference to the future reply + procedure read_axi_lite(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant expected_rresp : axi_resp_t := axi_resp_okay; + variable reference : inout bus_reference_t); + + -- Blocking: read bus with immediate reply + procedure read_axi_lite(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant expected_rresp : axi_resp_t := axi_resp_okay; + variable data : inout std_logic_vector); + + -- Blocking: Read bus and check result against expected data + procedure check_axi_lite(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant expected_rresp : axi_resp_t := axi_resp_okay; + constant expected : std_logic_vector; + constant msg : string := ""); + + function is_read(msg_type : msg_type_t) return boolean; + function is_write(msg_type : msg_type_t) return boolean; + function is_axi_lite_msg(msg_type : msg_type_t) return boolean; + +end package; + +package body axi_lite_master_pkg is + + procedure write_axi_lite(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant data : std_logic_vector; + constant expected_bresp : axi_resp_t := axi_resp_okay; + -- default byte enable is all bytes + constant byte_enable : std_logic_vector := "") is + variable request_msg : msg_t := new_msg(axi_lite_write_msg); + variable full_data : std_logic_vector(bus_handle.p_data_length - 1 downto 0) := (others => '0'); + variable full_address : std_logic_vector(bus_handle.p_address_length - 1 downto 0) := (others => '0'); + variable full_byte_enable : std_logic_vector(byte_enable_length(bus_handle) - 1 downto 0); + begin + full_address(address'length - 1 downto 0) := address; + push_std_ulogic_vector(request_msg, full_address); + + full_data(data'length - 1 downto 0) := data; + push_std_ulogic_vector(request_msg, full_data); + + if byte_enable = "" then + full_byte_enable := (others => '1'); + else + full_byte_enable(byte_enable'length - 1 downto 0) := byte_enable; + end if; + push_std_ulogic_vector(request_msg, full_byte_enable); + + push_std_ulogic_vector(request_msg, expected_bresp); + + send(net, bus_handle.p_actor, request_msg); + end procedure; + + procedure read_axi_lite(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant expected_rresp : axi_resp_t := axi_resp_okay; + variable reference : inout bus_reference_t) is + variable full_address : std_logic_vector(bus_handle.p_address_length - 1 downto 0) := (others => '0'); + alias request_msg : msg_t is reference; + begin + request_msg := new_msg(axi_lite_read_msg); + full_address(address'length - 1 downto 0) := address; + push_std_ulogic_vector(request_msg, full_address); + push_std_ulogic_vector(request_msg, expected_rresp); + send(net, bus_handle.p_actor, request_msg); + end procedure; + + procedure read_axi_lite(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant expected_rresp : axi_resp_t := axi_resp_okay; + variable data : inout std_logic_vector) is + variable reference : bus_reference_t; + begin + read_axi_lite(net, bus_handle, address, expected_rresp, reference); + await_read_bus_reply(net, reference, data); + end procedure; + + procedure check_axi_lite(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant expected_rresp : axi_resp_t := axi_resp_okay; + constant expected : std_logic_vector; + constant msg : string := "") is + variable data : std_logic_vector(bus_handle.p_data_length - 1 downto 0); + variable edata : std_logic_vector(data'range) := (others => '0'); + + impure function error_prefix return string is + begin + if msg = "" then + return "check_bus(x""" & to_hstring(address) & """)"; + else + return msg; + end if; + end; + + impure function base_error return string is + begin + return error_prefix & " - Got x""" & to_hstring(data) & """ expected x""" & to_hstring(edata) & """"; + end; + begin + + edata(expected'length - 1 downto 0) := expected; + + read_axi_lite(net, bus_handle, address, expected_rresp, data); + if not std_match(data, edata) then + failure(bus_handle.p_logger, base_error); + end if; + end procedure; + + function is_read(msg_type : msg_type_t) return boolean is + begin + return msg_type = bus_read_msg or msg_type = axi_lite_read_msg; + end function; + + function is_write(msg_type : msg_type_t) return boolean is + begin + return msg_type = bus_write_msg or msg_type = axi_lite_write_msg; + end function; + + function is_axi_lite_msg(msg_type : msg_type_t) return boolean is + begin + return msg_type = axi_lite_read_msg or msg_type = axi_lite_write_msg; + end function; + +end package body; diff --git a/vunit/vhdl/verification_components/src/axi_pkg.vhd b/vunit/vhdl/verification_components/src/axi_pkg.vhd index 0311317f3..0c1265c8c 100644 --- a/vunit/vhdl/verification_components/src/axi_pkg.vhd +++ b/vunit/vhdl/verification_components/src/axi_pkg.vhd @@ -1,25 +1,25 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -package axi_pkg is - subtype axi_resp_t is std_logic_vector(1 downto 0); - constant axi_resp_okay : axi_resp_t := "00"; - constant axi_resp_exokay : axi_resp_t := "01"; - constant axi_resp_slverr : axi_resp_t := "10"; - constant axi_resp_decerr : axi_resp_t := "11"; - - subtype axi_burst_type_t is std_logic_vector(1 downto 0); - constant axi_burst_type_fixed : axi_burst_type_t := "00"; - constant axi_burst_type_incr : axi_burst_type_t := "01"; - constant axi_burst_type_wrap : axi_burst_type_t := "10"; - - subtype axi4_len_t is std_logic_vector(7 downto 0); - constant max_axi4_burst_length : natural := 2**axi4_len_t'length; - subtype axi4_size_t is std_logic_vector(2 downto 0); -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +package axi_pkg is + subtype axi_resp_t is std_logic_vector(1 downto 0); + constant axi_resp_okay : axi_resp_t := "00"; + constant axi_resp_exokay : axi_resp_t := "01"; + constant axi_resp_slverr : axi_resp_t := "10"; + constant axi_resp_decerr : axi_resp_t := "11"; + + subtype axi_burst_type_t is std_logic_vector(1 downto 0); + constant axi_burst_type_fixed : axi_burst_type_t := "00"; + constant axi_burst_type_incr : axi_burst_type_t := "01"; + constant axi_burst_type_wrap : axi_burst_type_t := "10"; + + subtype axi4_len_t is std_logic_vector(7 downto 0); + constant max_axi4_burst_length : natural := 2**axi4_len_t'length; + subtype axi4_size_t is std_logic_vector(2 downto 0); +end package; diff --git a/vunit/vhdl/verification_components/src/axi_read_slave.vhd b/vunit/vhdl/verification_components/src/axi_read_slave.vhd index 103026330..233d905ee 100644 --- a/vunit/vhdl/verification_components/src/axi_read_slave.vhd +++ b/vunit/vhdl/verification_components/src/axi_read_slave.vhd @@ -1,166 +1,166 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -use work.axi_pkg.all; -use work.axi_slave_private_pkg.all; -use work.queue_pkg.all; -context work.com_context; -context work.vc_context; - -entity axi_read_slave is - generic ( - axi_slave : axi_slave_t); - port ( - aclk : in std_logic; - - arvalid : in std_logic; - arready : out std_logic := '0'; - arid : in std_logic_vector; - araddr : in std_logic_vector; - arlen : in std_logic_vector; - arsize : in std_logic_vector; - arburst : in axi_burst_type_t; - - rvalid : out std_logic := '0'; - rready : in std_logic; - rid : out std_logic_vector; - rdata : out std_logic_vector; - rresp : out axi_resp_t; - rlast : out std_logic - ); -end entity; - -architecture a of axi_read_slave is - shared variable self : axi_slave_private_t; - signal initialized : boolean := false; -begin - - control_process : process - begin - self.init(axi_slave, read_slave, 2**arid'length-1, rdata); - initialized <= true; - main_loop(self, net); - wait; - end process; - - axi_process : process - variable input_burst, burst : axi_burst_t; - variable address : integer; - variable idx : integer; - variable beats : natural := 0; - - variable response_time : time; - variable has_response_time : boolean := false; - begin - assert arid'length = rid'length report "arid vs rid data width mismatch"; - -- Initialization - rid <= (rid'range => '0'); - rdata <= (rdata'range => '0'); - rresp <= (rresp'range => '0'); - rlast <= '0'; - - wait on initialized until initialized; - - loop - if (rready and rvalid) = '1' then - rvalid <= '0'; - beats := beats - 1; - end if; - - if (arvalid and arready) = '1' then - input_burst := self.create_burst(arid, araddr, arlen, arsize, arburst); - self.push_random_response_time; - self.push_burst(input_burst); - end if; - - if not self.burst_queue_empty and beats = 0 then - if not has_response_time then - has_response_time := true; - response_time := self.pop_response_time; - end if; - - if has_response_time and response_time <= now then - has_response_time := false; - burst := self.pop_burst; - beats := burst.length; - rid <= std_logic_vector(to_unsigned(burst.id, rid'length)); - rresp <= axi_resp_okay; - address := burst.address; - end if; - end if; - - if beats > 0 and (rvalid = '0' or rready = '1') and not self.should_stall_data then - rvalid <= '1'; - for j in 0 to burst.size-1 loop - idx := (address + j) mod self.data_size; - rdata(8*idx+7 downto 8*idx) <= std_logic_vector(to_unsigned(read_byte(axi_slave.p_memory, address+j), 8)); - end loop; - - if burst.burst_type = axi_burst_type_incr then - address := address + burst.size; - end if; - - if beats = 1 then - self.finish_burst(burst); - rlast <= '1'; - else - rlast <= '0'; - end if; - end if; - - if self.should_stall_address or self.burst_queue_full then - arready <= '0'; - else - arready <= '1'; - end if; - - wait until rising_edge(aclk); - end loop; - end process; - - well_behaved_check : process - variable size, len : natural; - variable num_beats : integer := 0; - variable num_beats_now : integer; - begin - wait on initialized until initialized; - loop - - num_beats_now := num_beats; - - if arvalid = '1' then - len := to_integer(unsigned(arlen)); - num_beats_now := num_beats + len + 1; - end if; - - -- Always keep track of num_beats such that the well behaved check can be enabled at any time - if (arvalid and arready) = '1' then - size := 2**to_integer(unsigned(arsize)); - num_beats := num_beats_now; - - if self.should_check_well_behaved and size /= self.data_size and len /= 0 then - self.fail("Burst not well behaved, axi size = " & to_string(size) & " but bus data width allows " & to_string(self.data_size)); - end if; - end if; - - if self.should_check_well_behaved and num_beats_now > 0 and rready /= '1' then - self.fail("Burst not well behaved, rready was not high during active burst"); - end if; - - if (rready and rvalid) = '1' then - num_beats := -1; - end if; - - wait until rising_edge(aclk); - end loop; - wait; - end process; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.axi_pkg.all; +use work.axi_slave_private_pkg.all; +use work.queue_pkg.all; +context work.com_context; +context work.vc_context; + +entity axi_read_slave is + generic ( + axi_slave : axi_slave_t); + port ( + aclk : in std_logic; + + arvalid : in std_logic; + arready : out std_logic := '0'; + arid : in std_logic_vector; + araddr : in std_logic_vector; + arlen : in std_logic_vector; + arsize : in std_logic_vector; + arburst : in axi_burst_type_t; + + rvalid : out std_logic := '0'; + rready : in std_logic; + rid : out std_logic_vector; + rdata : out std_logic_vector; + rresp : out axi_resp_t; + rlast : out std_logic + ); +end entity; + +architecture a of axi_read_slave is + shared variable self : axi_slave_private_t; + signal initialized : boolean := false; +begin + + control_process : process + begin + self.init(axi_slave, read_slave, 2**arid'length-1, rdata); + initialized <= true; + main_loop(self, net); + wait; + end process; + + axi_process : process + variable input_burst, burst : axi_burst_t; + variable address : integer; + variable idx : integer; + variable beats : natural := 0; + + variable response_time : time; + variable has_response_time : boolean := false; + begin + assert arid'length = rid'length report "arid vs rid data width mismatch"; + -- Initialization + rid <= (rid'range => '0'); + rdata <= (rdata'range => '0'); + rresp <= (rresp'range => '0'); + rlast <= '0'; + + wait on initialized until initialized; + + loop + if (rready and rvalid) = '1' then + rvalid <= '0'; + beats := beats - 1; + end if; + + if (arvalid and arready) = '1' then + input_burst := self.create_burst(arid, araddr, arlen, arsize, arburst); + self.push_random_response_time; + self.push_burst(input_burst); + end if; + + if not self.burst_queue_empty and beats = 0 then + if not has_response_time then + has_response_time := true; + response_time := self.pop_response_time; + end if; + + if has_response_time and response_time <= now then + has_response_time := false; + burst := self.pop_burst; + beats := burst.length; + rid <= std_logic_vector(to_unsigned(burst.id, rid'length)); + rresp <= axi_resp_okay; + address := burst.address; + end if; + end if; + + if beats > 0 and (rvalid = '0' or rready = '1') and not self.should_stall_data then + rvalid <= '1'; + for j in 0 to burst.size-1 loop + idx := (address + j) mod self.data_size; + rdata(8*idx+7 downto 8*idx) <= std_logic_vector(to_unsigned(read_byte(axi_slave.p_memory, address+j), 8)); + end loop; + + if burst.burst_type = axi_burst_type_incr then + address := address + burst.size; + end if; + + if beats = 1 then + self.finish_burst(burst); + rlast <= '1'; + else + rlast <= '0'; + end if; + end if; + + if self.should_stall_address or self.burst_queue_full then + arready <= '0'; + else + arready <= '1'; + end if; + + wait until rising_edge(aclk); + end loop; + end process; + + well_behaved_check : process + variable size, len : natural; + variable num_beats : integer := 0; + variable num_beats_now : integer; + begin + wait on initialized until initialized; + loop + + num_beats_now := num_beats; + + if arvalid = '1' then + len := to_integer(unsigned(arlen)); + num_beats_now := num_beats + len + 1; + end if; + + -- Always keep track of num_beats such that the well behaved check can be enabled at any time + if (arvalid and arready) = '1' then + size := 2**to_integer(unsigned(arsize)); + num_beats := num_beats_now; + + if self.should_check_well_behaved and size /= self.data_size and len /= 0 then + self.fail("Burst not well behaved, axi size = " & to_string(size) & " but bus data width allows " & to_string(self.data_size)); + end if; + end if; + + if self.should_check_well_behaved and num_beats_now > 0 and rready /= '1' then + self.fail("Burst not well behaved, rready was not high during active burst"); + end if; + + if (rready and rvalid) = '1' then + num_beats := -1; + end if; + + wait until rising_edge(aclk); + end loop; + wait; + end process; + +end architecture; diff --git a/vunit/vhdl/verification_components/src/axi_slave_pkg.vhd b/vunit/vhdl/verification_components/src/axi_slave_pkg.vhd index 149bde6ab..8cdf148a0 100644 --- a/vunit/vhdl/verification_components/src/axi_slave_pkg.vhd +++ b/vunit/vhdl/verification_components/src/axi_slave_pkg.vhd @@ -1,298 +1,298 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -use work.queue_pkg.all; -use work.logger_pkg.all; -use work.memory_pkg.all; -context work.com_context; -use work.axi_statistics_pkg.all; - -package axi_slave_pkg is - subtype probability_t is real range 0.0 to 1.0; - - type axi_slave_t is record - -- Private - p_initial_address_fifo_depth : positive; - p_initial_write_response_fifo_depth : positive; - p_initial_check_4kbyte_boundary : boolean; - p_initial_address_stall_probability : probability_t; - p_initial_data_stall_probability : probability_t; - p_initial_write_response_stall_probability : probability_t; - p_initial_min_response_latency : delay_length; - p_initial_max_response_latency : delay_length; - p_actor : actor_t; - p_memory : memory_t; - p_logger : logger_t; - end record; - - constant axi_slave_logger : logger_t := get_logger("vunit_lib:axi_slave_pkg"); - impure function new_axi_slave(memory : memory_t; - address_fifo_depth : positive := 1; - write_response_fifo_depth : positive := 1; - check_4kbyte_boundary : boolean := true; - address_stall_probability : probability_t := 0.0; - data_stall_probability : probability_t := 0.0; - write_response_stall_probability : probability_t := 0.0; - min_response_latency : delay_length := 0 ns; - max_response_latency : delay_length := 0 ns; - logger : logger_t := axi_slave_logger) return axi_slave_t; - - -- Get the logger used by the axi_slave - function get_logger(axi_slave : axi_slave_t) return logger_t; - - -- Set the maximum number address channel tokens that can be queued - procedure set_address_fifo_depth(signal net : inout network_t; - axi_slave : axi_slave_t; - depth : positive); - - -- Set the maximum number write responses that can be queued - procedure set_write_response_fifo_depth(signal net : inout network_t; - axi_slave : axi_slave_t; - depth : positive); - - -- Set the address channel stall probability - procedure set_address_stall_probability(signal net : inout network_t; - axi_slave : axi_slave_t; - probability : probability_t); - - -- Set the data channel stall probability - procedure set_data_stall_probability(signal net : inout network_t; - axi_slave : axi_slave_t; - probability : probability_t); - - -- Set the write response stall probability - procedure set_write_response_stall_probability(signal net : inout network_t; - axi_slave : axi_slave_t; - probability : probability_t); - - -- Set the response latency - -- - -- For a write slave this is the time between the last write data - -- and providing the write reponse. All write data is written to the - -- memory model right before providing write response. - -- Data address and expected value is still checked as soons as it arrives to - -- the axi slave and is not delayed until the write response time. - -- - -- For a read slave this is the time between the read burst arrival and the - -- first provided read data - -- - -- The response latency is randomly choosen in the uniform interval: - -- [min_latency, max_latency] - procedure set_response_latency(signal net : inout network_t; - axi_slave : axi_slave_t; - min_latency, max_latency : delay_length); - - -- Short hand for set_response_latency when min and max are the same - procedure set_response_latency(signal net : inout network_t; - axi_slave : axi_slave_t; - latency : delay_length); - - procedure enable_4kbyte_boundary_check(signal net : inout network_t; - axi_slave : axi_slave_t); - procedure disable_4kbyte_boundary_check(signal net : inout network_t; - axi_slave : axi_slave_t); - - -- Get statistics object from axi slave - -- Dynamically allocates new statistics object which must he deallocated when - -- used - -- This procedure will automatically deallocate the input statistics object - -- if it is not null - procedure get_statistics(signal net : inout network_t; - axi_slave : axi_slave_t; - variable stat : inout axi_statistics_t; - clear : boolean := false); - - -- Check that bursts are well behaved, that is that data channel traffic is - -- as compact as possible - - -- For write: - -- 1. awvalid never high without wvalid - -- 2. wvalid never goes low during active burst - -- 3. uses max awsize supported by data width - -- 4. bready never low during active burst - - -- For read: - -- 1. rready never low during active burst - -- 2. uses max arsize supported by data width - procedure enable_well_behaved_check(signal net : inout network_t; axi_slave : axi_slave_t); - - -- Private constants - constant axi_slave_set_address_fifo_depth_msg : msg_type_t := new_msg_type("axi slave set address channel fifo depth"); - constant axi_slave_set_write_response_fifo_depth_msg : msg_type_t := new_msg_type("set write response fifo depth"); - constant axi_slave_set_address_stall_probability_msg : msg_type_t := new_msg_type("axi slave set address channel stall probability"); - constant axi_slave_set_data_stall_probability_msg : msg_type_t := new_msg_type("axi slave set data stall probability"); - constant axi_slave_set_write_response_stall_probability_msg : msg_type_t := new_msg_type("axi slave set write response stall probability"); - constant axi_slave_set_response_latency_msg : msg_type_t := new_msg_type("axi slave response latency probability"); - constant axi_slave_configure_4kbyte_boundary_check_msg : msg_type_t := new_msg_type("axi slave configure 4kbyte boundary check"); - constant axi_slave_get_statistics_msg : msg_type_t := new_msg_type("axi slave get statistics"); - constant axi_slave_enable_well_behaved_check_msg : msg_type_t := new_msg_type("axi slave enable well behaved check"); - -end package; - -package body axi_slave_pkg is - impure function new_axi_slave(memory : memory_t; - address_fifo_depth : positive := 1; - write_response_fifo_depth : positive := 1; - check_4kbyte_boundary : boolean := true; - address_stall_probability : probability_t := 0.0; - data_stall_probability : probability_t := 0.0; - write_response_stall_probability : probability_t := 0.0; - min_response_latency : delay_length := 0 ns; - max_response_latency : delay_length := 0 ns; - logger : logger_t := axi_slave_logger) return axi_slave_t is - begin - return (p_actor => new_actor, - p_initial_address_fifo_depth => address_fifo_depth, - p_initial_write_response_fifo_depth => write_response_fifo_depth, - p_initial_check_4kbyte_boundary => check_4kbyte_boundary, - p_initial_address_stall_probability => address_stall_probability, - p_initial_data_stall_probability => data_stall_probability, - p_initial_write_response_stall_probability => write_response_stall_probability, - p_initial_min_response_latency => min_response_latency, - p_initial_max_response_latency => max_response_latency, - p_memory => to_vc_interface(memory, logger), - p_logger => logger); - end; - - function get_logger(axi_slave : axi_slave_t) return logger_t is - begin - return axi_slave.p_logger; - end; - - procedure set_address_fifo_depth(signal net : inout network_t; - axi_slave : axi_slave_t; - depth : positive) is - variable request_msg : msg_t; - variable ack : boolean; - begin - request_msg := new_msg(axi_slave_set_address_fifo_depth_msg); - push(request_msg, depth); - request(net, axi_slave.p_actor, request_msg, ack); - assert ack report "Failed on set_address_fifo_depth command"; - end; - - procedure set_write_response_fifo_depth(signal net : inout network_t; - axi_slave : axi_slave_t; - depth : positive) is - variable request_msg : msg_t; - variable ack : boolean; - begin - request_msg := new_msg(axi_slave_set_write_response_fifo_depth_msg); - push(request_msg, depth); - request(net, axi_slave.p_actor, request_msg, ack); - assert ack report "Failed on set_write_response_fifo_depth command"; - end; - - procedure set_address_stall_probability(signal net : inout network_t; - axi_slave : axi_slave_t; - probability : probability_t) is - variable request_msg : msg_t; - variable ack : boolean; - begin - request_msg := new_msg(axi_slave_set_address_stall_probability_msg); - push_real(request_msg, probability); - request(net, axi_slave.p_actor, request_msg, ack); - assert ack report "Failed on set_address_stall_probability command"; - end; - - procedure set_data_stall_probability(signal net : inout network_t; - axi_slave : axi_slave_t; - probability : probability_t) is - variable request_msg : msg_t; - variable ack : boolean; - begin - request_msg := new_msg(axi_slave_set_data_stall_probability_msg); - push_real(request_msg, probability); - request(net, axi_slave.p_actor, request_msg, ack); - assert ack report "Failed on set_data_stall_probability command"; - end; - - procedure set_write_response_stall_probability(signal net : inout network_t; axi_slave : axi_slave_t; - probability : probability_t) is - variable request_msg : msg_t; - variable ack : boolean; - begin - request_msg := new_msg(axi_slave_set_write_response_stall_probability_msg); - push_real(request_msg, probability); - request(net, axi_slave.p_actor, request_msg, ack); - assert ack report "Failed on set_write_response_stall_probability command"; - end; - - procedure configure_4kbyte_boundary_check(signal net : inout network_t; - axi_slave : axi_slave_t; - value : boolean) is - variable request_msg : msg_t; - variable ack : boolean; - begin - request_msg := new_msg(axi_slave_configure_4kbyte_boundary_check_msg); - push_boolean(request_msg, value); - request(net, axi_slave.p_actor, request_msg, ack); - assert ack report "Failed on configure_4kbyte_boundary_check command"; - end; - - procedure set_response_latency(signal net : inout network_t; - axi_slave : axi_slave_t; - min_latency, max_latency : delay_length) is - variable request_msg : msg_t; - variable ack : boolean; - begin - request_msg := new_msg(axi_slave_set_response_latency_msg); - push_time(request_msg, min_latency); - push_time(request_msg, max_latency); - request(net, axi_slave.p_actor, request_msg, ack); - assert ack report "Failed on set_response_latency command"; - end; - - -- Short hand for set_response_latency when min and max are the same - procedure set_response_latency(signal net : inout network_t; - axi_slave : axi_slave_t; - latency : delay_length) is - begin - set_response_latency(net, axi_slave, latency, latency); - end; - - procedure enable_4kbyte_boundary_check(signal net : inout network_t; - axi_slave : axi_slave_t) is - begin - configure_4kbyte_boundary_check(net, axi_slave, true); - end; - - procedure disable_4kbyte_boundary_check(signal net : inout network_t; - axi_slave : axi_slave_t) is - begin - configure_4kbyte_boundary_check(net, axi_slave, false); - end; - - - procedure get_statistics(signal net : inout network_t; - axi_slave : axi_slave_t; - variable stat : inout axi_statistics_t; - clear : boolean := false) is - variable request_msg, reply_msg : msg_t; - begin - deallocate(stat); - request_msg := new_msg(axi_slave_get_statistics_msg); - push_boolean(request_msg, clear); - send(net, axi_slave.p_actor, request_msg); - receive_reply(net, request_msg, reply_msg); - stat := (p_count_by_burst_length => pop_integer_vector_ptr_ref(reply_msg)); - delete(request_msg); - delete(reply_msg); - end; - - procedure enable_well_behaved_check(signal net : inout network_t; - axi_slave : axi_slave_t) is - variable request_msg : msg_t; - variable ack : boolean; - begin - request_msg := new_msg(axi_slave_enable_well_behaved_check_msg); - request(net, axi_slave.p_actor, request_msg, ack); - assert ack report "Failed on msg_enable_well_behaved_check command"; - end; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +use work.queue_pkg.all; +use work.logger_pkg.all; +use work.memory_pkg.all; +context work.com_context; +use work.axi_statistics_pkg.all; + +package axi_slave_pkg is + subtype probability_t is real range 0.0 to 1.0; + + type axi_slave_t is record + -- Private + p_initial_address_fifo_depth : positive; + p_initial_write_response_fifo_depth : positive; + p_initial_check_4kbyte_boundary : boolean; + p_initial_address_stall_probability : probability_t; + p_initial_data_stall_probability : probability_t; + p_initial_write_response_stall_probability : probability_t; + p_initial_min_response_latency : delay_length; + p_initial_max_response_latency : delay_length; + p_actor : actor_t; + p_memory : memory_t; + p_logger : logger_t; + end record; + + constant axi_slave_logger : logger_t := get_logger("vunit_lib:axi_slave_pkg"); + impure function new_axi_slave(memory : memory_t; + address_fifo_depth : positive := 1; + write_response_fifo_depth : positive := 1; + check_4kbyte_boundary : boolean := true; + address_stall_probability : probability_t := 0.0; + data_stall_probability : probability_t := 0.0; + write_response_stall_probability : probability_t := 0.0; + min_response_latency : delay_length := 0 ns; + max_response_latency : delay_length := 0 ns; + logger : logger_t := axi_slave_logger) return axi_slave_t; + + -- Get the logger used by the axi_slave + function get_logger(axi_slave : axi_slave_t) return logger_t; + + -- Set the maximum number address channel tokens that can be queued + procedure set_address_fifo_depth(signal net : inout network_t; + axi_slave : axi_slave_t; + depth : positive); + + -- Set the maximum number write responses that can be queued + procedure set_write_response_fifo_depth(signal net : inout network_t; + axi_slave : axi_slave_t; + depth : positive); + + -- Set the address channel stall probability + procedure set_address_stall_probability(signal net : inout network_t; + axi_slave : axi_slave_t; + probability : probability_t); + + -- Set the data channel stall probability + procedure set_data_stall_probability(signal net : inout network_t; + axi_slave : axi_slave_t; + probability : probability_t); + + -- Set the write response stall probability + procedure set_write_response_stall_probability(signal net : inout network_t; + axi_slave : axi_slave_t; + probability : probability_t); + + -- Set the response latency + -- + -- For a write slave this is the time between the last write data + -- and providing the write reponse. All write data is written to the + -- memory model right before providing write response. + -- Data address and expected value is still checked as soons as it arrives to + -- the axi slave and is not delayed until the write response time. + -- + -- For a read slave this is the time between the read burst arrival and the + -- first provided read data + -- + -- The response latency is randomly choosen in the uniform interval: + -- [min_latency, max_latency] + procedure set_response_latency(signal net : inout network_t; + axi_slave : axi_slave_t; + min_latency, max_latency : delay_length); + + -- Short hand for set_response_latency when min and max are the same + procedure set_response_latency(signal net : inout network_t; + axi_slave : axi_slave_t; + latency : delay_length); + + procedure enable_4kbyte_boundary_check(signal net : inout network_t; + axi_slave : axi_slave_t); + procedure disable_4kbyte_boundary_check(signal net : inout network_t; + axi_slave : axi_slave_t); + + -- Get statistics object from axi slave + -- Dynamically allocates new statistics object which must he deallocated when + -- used + -- This procedure will automatically deallocate the input statistics object + -- if it is not null + procedure get_statistics(signal net : inout network_t; + axi_slave : axi_slave_t; + variable stat : inout axi_statistics_t; + clear : boolean := false); + + -- Check that bursts are well behaved, that is that data channel traffic is + -- as compact as possible + + -- For write: + -- 1. awvalid never high without wvalid + -- 2. wvalid never goes low during active burst + -- 3. uses max awsize supported by data width + -- 4. bready never low during active burst + + -- For read: + -- 1. rready never low during active burst + -- 2. uses max arsize supported by data width + procedure enable_well_behaved_check(signal net : inout network_t; axi_slave : axi_slave_t); + + -- Private constants + constant axi_slave_set_address_fifo_depth_msg : msg_type_t := new_msg_type("axi slave set address channel fifo depth"); + constant axi_slave_set_write_response_fifo_depth_msg : msg_type_t := new_msg_type("set write response fifo depth"); + constant axi_slave_set_address_stall_probability_msg : msg_type_t := new_msg_type("axi slave set address channel stall probability"); + constant axi_slave_set_data_stall_probability_msg : msg_type_t := new_msg_type("axi slave set data stall probability"); + constant axi_slave_set_write_response_stall_probability_msg : msg_type_t := new_msg_type("axi slave set write response stall probability"); + constant axi_slave_set_response_latency_msg : msg_type_t := new_msg_type("axi slave response latency probability"); + constant axi_slave_configure_4kbyte_boundary_check_msg : msg_type_t := new_msg_type("axi slave configure 4kbyte boundary check"); + constant axi_slave_get_statistics_msg : msg_type_t := new_msg_type("axi slave get statistics"); + constant axi_slave_enable_well_behaved_check_msg : msg_type_t := new_msg_type("axi slave enable well behaved check"); + +end package; + +package body axi_slave_pkg is + impure function new_axi_slave(memory : memory_t; + address_fifo_depth : positive := 1; + write_response_fifo_depth : positive := 1; + check_4kbyte_boundary : boolean := true; + address_stall_probability : probability_t := 0.0; + data_stall_probability : probability_t := 0.0; + write_response_stall_probability : probability_t := 0.0; + min_response_latency : delay_length := 0 ns; + max_response_latency : delay_length := 0 ns; + logger : logger_t := axi_slave_logger) return axi_slave_t is + begin + return (p_actor => new_actor, + p_initial_address_fifo_depth => address_fifo_depth, + p_initial_write_response_fifo_depth => write_response_fifo_depth, + p_initial_check_4kbyte_boundary => check_4kbyte_boundary, + p_initial_address_stall_probability => address_stall_probability, + p_initial_data_stall_probability => data_stall_probability, + p_initial_write_response_stall_probability => write_response_stall_probability, + p_initial_min_response_latency => min_response_latency, + p_initial_max_response_latency => max_response_latency, + p_memory => to_vc_interface(memory, logger), + p_logger => logger); + end; + + function get_logger(axi_slave : axi_slave_t) return logger_t is + begin + return axi_slave.p_logger; + end; + + procedure set_address_fifo_depth(signal net : inout network_t; + axi_slave : axi_slave_t; + depth : positive) is + variable request_msg : msg_t; + variable ack : boolean; + begin + request_msg := new_msg(axi_slave_set_address_fifo_depth_msg); + push(request_msg, depth); + request(net, axi_slave.p_actor, request_msg, ack); + assert ack report "Failed on set_address_fifo_depth command"; + end; + + procedure set_write_response_fifo_depth(signal net : inout network_t; + axi_slave : axi_slave_t; + depth : positive) is + variable request_msg : msg_t; + variable ack : boolean; + begin + request_msg := new_msg(axi_slave_set_write_response_fifo_depth_msg); + push(request_msg, depth); + request(net, axi_slave.p_actor, request_msg, ack); + assert ack report "Failed on set_write_response_fifo_depth command"; + end; + + procedure set_address_stall_probability(signal net : inout network_t; + axi_slave : axi_slave_t; + probability : probability_t) is + variable request_msg : msg_t; + variable ack : boolean; + begin + request_msg := new_msg(axi_slave_set_address_stall_probability_msg); + push_real(request_msg, probability); + request(net, axi_slave.p_actor, request_msg, ack); + assert ack report "Failed on set_address_stall_probability command"; + end; + + procedure set_data_stall_probability(signal net : inout network_t; + axi_slave : axi_slave_t; + probability : probability_t) is + variable request_msg : msg_t; + variable ack : boolean; + begin + request_msg := new_msg(axi_slave_set_data_stall_probability_msg); + push_real(request_msg, probability); + request(net, axi_slave.p_actor, request_msg, ack); + assert ack report "Failed on set_data_stall_probability command"; + end; + + procedure set_write_response_stall_probability(signal net : inout network_t; axi_slave : axi_slave_t; + probability : probability_t) is + variable request_msg : msg_t; + variable ack : boolean; + begin + request_msg := new_msg(axi_slave_set_write_response_stall_probability_msg); + push_real(request_msg, probability); + request(net, axi_slave.p_actor, request_msg, ack); + assert ack report "Failed on set_write_response_stall_probability command"; + end; + + procedure configure_4kbyte_boundary_check(signal net : inout network_t; + axi_slave : axi_slave_t; + value : boolean) is + variable request_msg : msg_t; + variable ack : boolean; + begin + request_msg := new_msg(axi_slave_configure_4kbyte_boundary_check_msg); + push_boolean(request_msg, value); + request(net, axi_slave.p_actor, request_msg, ack); + assert ack report "Failed on configure_4kbyte_boundary_check command"; + end; + + procedure set_response_latency(signal net : inout network_t; + axi_slave : axi_slave_t; + min_latency, max_latency : delay_length) is + variable request_msg : msg_t; + variable ack : boolean; + begin + request_msg := new_msg(axi_slave_set_response_latency_msg); + push_time(request_msg, min_latency); + push_time(request_msg, max_latency); + request(net, axi_slave.p_actor, request_msg, ack); + assert ack report "Failed on set_response_latency command"; + end; + + -- Short hand for set_response_latency when min and max are the same + procedure set_response_latency(signal net : inout network_t; + axi_slave : axi_slave_t; + latency : delay_length) is + begin + set_response_latency(net, axi_slave, latency, latency); + end; + + procedure enable_4kbyte_boundary_check(signal net : inout network_t; + axi_slave : axi_slave_t) is + begin + configure_4kbyte_boundary_check(net, axi_slave, true); + end; + + procedure disable_4kbyte_boundary_check(signal net : inout network_t; + axi_slave : axi_slave_t) is + begin + configure_4kbyte_boundary_check(net, axi_slave, false); + end; + + + procedure get_statistics(signal net : inout network_t; + axi_slave : axi_slave_t; + variable stat : inout axi_statistics_t; + clear : boolean := false) is + variable request_msg, reply_msg : msg_t; + begin + deallocate(stat); + request_msg := new_msg(axi_slave_get_statistics_msg); + push_boolean(request_msg, clear); + send(net, axi_slave.p_actor, request_msg); + receive_reply(net, request_msg, reply_msg); + stat := (p_count_by_burst_length => pop_integer_vector_ptr_ref(reply_msg)); + delete(request_msg); + delete(reply_msg); + end; + + procedure enable_well_behaved_check(signal net : inout network_t; + axi_slave : axi_slave_t) is + variable request_msg : msg_t; + variable ack : boolean; + begin + request_msg := new_msg(axi_slave_enable_well_behaved_check_msg); + request(net, axi_slave.p_actor, request_msg, ack); + assert ack report "Failed on msg_enable_well_behaved_check command"; + end; +end package body; diff --git a/vunit/vhdl/verification_components/src/axi_slave_private_pkg.vhd b/vunit/vhdl/verification_components/src/axi_slave_private_pkg.vhd index 889666b98..934676594 100644 --- a/vunit/vhdl/verification_components/src/axi_slave_private_pkg.vhd +++ b/vunit/vhdl/verification_components/src/axi_slave_private_pkg.vhd @@ -1,580 +1,580 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- Private support package for axi_{read, write}_slave.vhd - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -use std.textio.all; - -use work.axi_pkg.all; -use work.queue_pkg.all; -use work.integer_vector_ptr_pkg.all; -context work.vunit_context; -context work.com_context; -context work.vc_context; - -library osvvm; -use osvvm.RandomPkg.all; - -package axi_slave_private_pkg is - - type axi_burst_t is record - id : integer; - address : integer; - length : integer; - size : integer; - burst_type : axi_burst_type_t; - - -- A running counter for each processed burst - -- Referring to this index for all burst related debug prints - index : natural; - end record; - - impure function describe_burst(burst : axi_burst_t) return string; - - type axi_slave_type_t is (write_slave, - read_slave); - - type axi_slave_private_t is protected - procedure init(axi_slave : axi_slave_t; - axi_slave_type : axi_slave_type_t; - max_id : natural; - data : std_logic_vector); - impure function get_actor return actor_t; - - procedure set_address_fifo_depth(depth : positive); - procedure set_write_response_fifo_depth(depth : positive); - procedure set_address_stall_probability(probability : probability_t); - procedure set_data_stall_probability(probability : probability_t); - procedure set_write_response_stall_probability(probability : probability_t); - procedure set_min_response_latency(latency : delay_length); - procedure set_max_response_latency(latency : delay_length); - procedure set_check_4kbyte_boundary(value : boolean); - procedure enable_well_behaved_check; - impure function should_check_well_behaved return boolean; - impure function should_stall_address return boolean; - impure function should_stall_data return boolean; - impure function should_stall_write_response return boolean; - - impure function create_burst(axid : std_logic_vector; - axaddr : std_logic_vector; - axlen : std_logic_vector; - axsize : std_logic_vector; - axburst : axi_burst_type_t) return axi_burst_t; - - procedure push_burst(burst : axi_burst_t); - impure function pop_burst return axi_burst_t; - impure function burst_queue_full return boolean; - impure function burst_queue_empty return boolean; - impure function burst_queue_length return natural; - - impure function resp_queue_full return boolean; - impure function resp_queue_empty return boolean; - impure function resp_queue_length return natural; - procedure push_resp(burst : axi_burst_t); - impure function pop_resp return axi_burst_t; - - procedure finish_burst(burst : axi_burst_t); - - procedure push_random_response_time; - impure function pop_response_time return time; - - procedure fail(msg : string); - procedure check_4kbyte_boundary(burst : axi_burst_t); - impure function data_size return integer; - - impure function get_statistics return axi_statistics_t; - procedure clear_statistics; - end protected; - - procedure main_loop(variable self : inout axi_slave_private_t; - signal net : inout network_t); - - procedure check_axi_resp(bus_handle : bus_master_t; got, expected : axi_resp_t; msg : string); -end package; - - -package body axi_slave_private_pkg is - - impure function describe_burst(burst : axi_burst_t) return string is - begin - return "#" & to_string(burst.index) & " for id " & to_string(burst.id); - end; - - procedure push_axi_burst(queue : queue_t; burst : axi_burst_t); - impure function pop_axi_burst(queue : queue_t) return axi_burst_t; - - type axi_slave_private_t is protected body - variable p_axi_slave : axi_slave_t; - variable p_axi_slave_type : axi_slave_type_t; - variable p_data_size : integer; - variable p_max_id : natural; - variable p_id_indexes : integer_vector_ptr_t; - variable p_burst_queue_max_length : natural; - variable p_burst_queue : queue_t; - variable p_burst_queue_length : natural; - variable p_resp_queue_max_length : natural; - variable p_resp_queue : queue_t; - variable p_resp_queue_length : natural; - variable p_check_4kbyte_boundary : boolean; - variable p_rnd : RandomPType; - variable p_addr_stall_prob : probability_t; - variable p_data_stall_prob : probability_t; - variable p_wresp_stall_prob : probability_t; - variable p_min_response_latency : delay_length; - variable p_max_response_latency : delay_length; - variable p_response_time_queue : queue_t; - variable p_check_well_behaved : boolean; - variable p_statistics : axi_statistics_t; - - procedure init(axi_slave : axi_slave_t; - axi_slave_type : axi_slave_type_t; - max_id : natural; - data : std_logic_vector) is - begin - p_axi_slave := axi_slave; - p_axi_slave_type := axi_slave_type; - p_data_size := data'length/8; - p_max_id := max_id; - p_id_indexes := new_integer_vector_ptr(len => max_id+1, value => 0); - p_burst_queue_max_length := axi_slave.p_initial_address_fifo_depth; - p_burst_queue := new_queue; - p_burst_queue_length := 0; - p_resp_queue_max_length := axi_slave.p_initial_write_response_fifo_depth; - p_resp_queue := new_queue; - p_resp_queue_length := 0; - p_check_4kbyte_boundary := axi_slave.p_initial_check_4kbyte_boundary; - p_check_well_behaved := false; - set_address_stall_probability(axi_slave.p_initial_address_stall_probability); - set_data_stall_probability(axi_slave.p_initial_data_stall_probability); - set_write_response_stall_probability(axi_slave.p_initial_write_response_stall_probability); - p_response_time_queue := new_queue; - set_min_response_latency(axi_slave.p_initial_min_response_latency); - set_max_response_latency(axi_slave.p_initial_max_response_latency); - p_statistics := new_axi_statistics; - end; - - impure function get_actor return actor_t is - begin - return p_axi_slave.p_actor; - end; - - procedure set_address_fifo_depth(depth : positive) is - begin - if burst_queue_length > depth then - fail("New address fifo depth " & to_string(depth) & - " is smaller than current content size " & to_string(burst_queue_length)); - else - p_burst_queue_max_length := depth; - end if; - end procedure; - - procedure set_write_response_fifo_depth(depth : positive) is - begin - if resp_queue_length > depth then - fail("New write response fifo depth " & to_string(depth) & - " is smaller than current content size " & to_string(resp_queue_length)); - else - p_resp_queue_max_length := depth; - end if; - end procedure; - - procedure set_address_stall_probability(probability : probability_t) is - begin - p_addr_stall_prob := probability; - end; - - procedure set_data_stall_probability(probability : probability_t) is - begin - p_data_stall_prob := probability; - end; - - procedure set_write_response_stall_probability(probability : probability_t) is - begin - p_wresp_stall_prob := probability; - end; - - procedure set_min_response_latency(latency : delay_length) is - begin - p_min_response_latency := latency; - end; - - procedure set_max_response_latency(latency : delay_length) is - begin - p_max_response_latency := latency; - end; - - procedure set_check_4kbyte_boundary(value : boolean) is - begin - p_check_4kbyte_boundary := value; - end; - - procedure enable_well_behaved_check is - begin - p_check_well_behaved := true; - end; - - impure function should_check_well_behaved return boolean is - begin - return p_check_well_behaved; - end; - - impure function should_stall(prob : probability_t) return boolean is - begin - -- Enhance performance when prob = 0.0 - return prob /= 0.0 and p_rnd.Uniform(0.0, 1.0) < prob; - end; - - impure function should_stall_address return boolean is - begin - return should_stall(p_addr_stall_prob); - end; - - impure function should_stall_data return boolean is - begin - return should_stall(p_data_stall_prob); - end; - - impure function should_stall_write_response return boolean is - begin - return should_stall(p_wresp_stall_prob); - end; - - impure function create_burst(axid : std_logic_vector; - axaddr : std_logic_vector; - axlen : std_logic_vector; - axsize : std_logic_vector; - axburst : axi_burst_type_t) return axi_burst_t is - - -- Return the correct prefix ar/aw depending on slave type - impure function ax return string is - begin - case p_axi_slave_type is - when read_slave => return "ar"; - when write_slave => return "aw"; - end case; - end; - - -- Return the correct read/write burst description - impure function description return string is - begin - case p_axi_slave_type is - when read_slave => return "read burst"; - when write_slave => return "write burst"; - end case; - end; - - impure function burst_string return string is - begin - - if axburst = axi_burst_type_fixed then - return "fixed"; - elsif axburst = axi_burst_type_incr then - return "incr"; - elsif axburst = axi_burst_type_wrap then - return "wrap"; - else - return "undefined"; - end if; - end; - - variable burst : axi_burst_t; - begin - burst.id := to_integer(unsigned(axid)); - burst.address := to_integer(unsigned(axaddr)); - burst.length := to_integer(unsigned(axlen)) + 1; - burst.size := 2**to_integer(unsigned(axsize)); - burst.burst_type := axburst; - assert burst.id <= p_max_id report "axi id to large"; - burst.index := get(p_id_indexes, burst.id); - set(p_id_indexes, burst.id, burst.index + 1); - - if is_visible(p_axi_slave.p_logger, debug) then - debug(p_axi_slave.p_logger, - "Got " & description & " " & describe_burst(burst) & - LF & ax & "id = 0x" & to_hstring(axid) & - LF & ax & "addr = 0x" & to_hstring(axaddr) & - LF & ax & "len = " & to_string(to_integer(unsigned(to_01(axlen)))) & - LF & ax & "size = " & to_string(to_integer(unsigned(to_01(axsize)))) & - LF & ax & "burst = " & burst_string & " (" & to_string(axburst) & ")" - ); - end if; - - add_burst_length(p_statistics, burst.length); - - if burst.burst_type = axi_burst_type_wrap then - fail("Wrapping burst type not supported"); - end if; - - if p_check_4kbyte_boundary then - check_4kbyte_boundary(burst); - end if; - - return burst; - end function; - - procedure push_burst(burst : axi_burst_t) is - begin - push_axi_burst(p_burst_queue, burst); - p_burst_queue_length := p_burst_queue_length + 1; - end; - - impure function pop_burst return axi_burst_t is - constant burst : axi_burst_t := pop_axi_burst(p_burst_queue); - begin - if is_visible(p_axi_slave.p_logger, debug) then - case p_axi_slave_type is - when write_slave => - debug(p_axi_slave.p_logger, - "Start accepting data for write burst " & describe_burst(burst)); - when read_slave => - debug(p_axi_slave.p_logger, - "Start providing data for read burst " & describe_burst(burst)); - end case; - end if; - p_burst_queue_length := p_burst_queue_length - 1; - return burst; - end; - - impure function burst_queue_full return boolean is - begin - return burst_queue_length = p_burst_queue_max_length; - end; - - impure function burst_queue_empty return boolean is - begin - return burst_queue_length = 0; - end; - - impure function burst_queue_length return natural is - begin - return p_burst_queue_length; - end; - - procedure push_resp(burst : axi_burst_t) is - begin - push_axi_burst(p_resp_queue, burst); - p_resp_queue_length := p_resp_queue_length + 1; - end; - - impure function pop_resp return axi_burst_t is - constant resp_burst : axi_burst_t := pop_axi_burst(p_resp_queue); - begin - if is_visible(p_axi_slave.p_logger, debug) then - debug(p_axi_slave.p_logger, - "Providing write response for burst " & describe_burst(resp_burst)); - end if; - p_resp_queue_length := p_resp_queue_length - 1; - return resp_burst; - end; - - procedure finish_burst(burst : axi_burst_t) is - begin - if is_visible(p_axi_slave.p_logger, debug) then - case p_axi_slave_type is - when write_slave => - debug(p_axi_slave.p_logger, - "Accepted last data for write burst " & describe_burst(burst)); - when read_slave => - debug(p_axi_slave.p_logger, - "Providing last data for read burst " & describe_burst(burst)); - end case; - end if; - end; - - impure function random_response_latency return delay_length is - begin - if p_min_response_latency = p_max_response_latency then - return p_min_response_latency; - else - return p_rnd.RandTime(p_min_response_latency, p_max_response_latency); - end if; - end; - - procedure push_random_response_time is - begin - push_time(p_response_time_queue, now + random_response_latency); - end; - - impure function pop_response_time return time is - begin - return pop_time(p_response_time_queue); - end; - - impure function resp_queue_full return boolean is - begin - return resp_queue_length = p_resp_queue_max_length; - end; - - impure function resp_queue_empty return boolean is - begin - return resp_queue_length = 0; - end; - - impure function resp_queue_length return natural is - begin - return p_resp_queue_length; - end; - - procedure fail(msg : string) is - begin - failure(p_axi_slave.p_logger, msg); - end; - - procedure check_4kbyte_boundary(burst : axi_burst_t) is - variable first_address, last_address : integer; - variable first_page, last_page : integer; - begin - first_address := burst.address - (burst.address mod data_size); -- Aligned - last_address := burst.address + burst.size*burst.length - 1; - - first_page := first_address / 4096; - last_page := last_address / 4096; - - if first_page /= last_page then - fail("Crossing 4KByte boundary. First page = " - & integer'image(first_page) & " (" & to_string(first_address) & "/4096)" - & ", last page = " - & integer'image(last_page) & " (" & to_string(last_address) & "/4096)"); - end if; - end procedure; - - impure function data_size return integer is - begin - return p_data_size; - end; - - impure function get_statistics return axi_statistics_t is - begin - return copy(p_statistics); - end; - - procedure clear_statistics is - begin - clear(p_statistics); - end; - - end protected body; - - - procedure push_axi_burst(queue : queue_t; burst : axi_burst_t) is - begin - push(queue, burst.id); - push(queue, burst.address); - push(queue, burst.length); - push(queue, burst.size); - push(queue, burst.index); - push_boolean(queue, burst.burst_type = axi_burst_type_fixed); - end; - - impure function pop_axi_burst(queue : queue_t) return axi_burst_t is - variable burst : axi_burst_t; - begin - burst.id := pop(queue); - burst.address := pop(queue); - burst.length := pop(queue); - burst.size := pop(queue); - burst.index := pop(queue); - - if pop_boolean(queue) then - burst.burst_type := axi_burst_type_fixed; - else - burst.burst_type := axi_burst_type_incr; - end if; - - return burst; - end; - - procedure main_loop(variable self : inout axi_slave_private_t; - signal net : inout network_t) is - variable reply_msg, request_msg : msg_t; - variable msg_type : msg_type_t; - - variable clear_stat : boolean; - variable stat : axi_statistics_t; - begin - while true loop - receive(net, self.get_actor, request_msg); - msg_type := message_type(request_msg); - - if msg_type = axi_slave_set_address_fifo_depth_msg then - self.set_address_fifo_depth(pop(request_msg)); - acknowledge(net, request_msg, true); - - elsif msg_type = axi_slave_set_write_response_fifo_depth_msg then - self.set_write_response_fifo_depth(pop(request_msg)); - acknowledge(net, request_msg, true); - - elsif msg_type = axi_slave_set_address_stall_probability_msg then - self.set_address_stall_probability(pop_real(request_msg)); - acknowledge(net, request_msg, true); - - elsif msg_type = axi_slave_set_data_stall_probability_msg then - self.set_data_stall_probability(pop_real(request_msg)); - acknowledge(net, request_msg, true); - - elsif msg_type = axi_slave_set_write_response_stall_probability_msg then - self.set_write_response_stall_probability(pop_real(request_msg)); - acknowledge(net, request_msg, true); - - elsif msg_type = axi_slave_set_response_latency_msg then - self.set_min_response_latency(pop_time(request_msg)); - self.set_max_response_latency(pop_time(request_msg)); - acknowledge(net, request_msg, true); - - elsif msg_type = axi_slave_configure_4kbyte_boundary_check_msg then - self.set_check_4kbyte_boundary(pop_boolean(request_msg)); - acknowledge(net, request_msg, true); - - elsif msg_type = axi_slave_get_statistics_msg then - clear_stat := pop_boolean(request_msg); - stat := self.get_statistics; - - if clear_stat then - self.clear_statistics; - end if; - - reply_msg := new_msg; - push_integer_vector_ptr_ref(reply_msg, stat.p_count_by_burst_length); - reply(net, request_msg, reply_msg); - delete(request_msg); - - elsif msg_type = axi_slave_enable_well_behaved_check_msg then - self.enable_well_behaved_check; - acknowledge(net, request_msg, true); - else - unexpected_msg_type(msg_type); - end if; - - delete(request_msg); - end loop; - end; - - function resp_to_string(resp : axi_resp_t) return string is - begin - case resp is - when axi_resp_okay => return "OKAY"; - when axi_resp_exokay => return "EXOKAY"; - when axi_resp_slverr => return "SLVERR"; - when axi_resp_decerr => return "DECERR"; - when others => return "UNKNOWN"; - end case; - end; - - procedure check_axi_resp(bus_handle : bus_master_t; got, expected : axi_resp_t; msg : string) is - function describe(resp : axi_resp_t) return string is - begin - return resp_to_string(resp) & "(" & to_string(resp) & ")"; - end; - begin - if got /= expected then - failure(bus_handle.p_logger, msg & " - Got AXI response " & describe(got) & " expected " & describe(expected)); - end if; - end; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- Private support package for axi_{read, write}_slave.vhd + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use std.textio.all; + +use work.axi_pkg.all; +use work.queue_pkg.all; +use work.integer_vector_ptr_pkg.all; +context work.vunit_context; +context work.com_context; +context work.vc_context; + +library osvvm; +use osvvm.RandomPkg.all; + +package axi_slave_private_pkg is + + type axi_burst_t is record + id : integer; + address : integer; + length : integer; + size : integer; + burst_type : axi_burst_type_t; + + -- A running counter for each processed burst + -- Referring to this index for all burst related debug prints + index : natural; + end record; + + impure function describe_burst(burst : axi_burst_t) return string; + + type axi_slave_type_t is (write_slave, + read_slave); + + type axi_slave_private_t is protected + procedure init(axi_slave : axi_slave_t; + axi_slave_type : axi_slave_type_t; + max_id : natural; + data : std_logic_vector); + impure function get_actor return actor_t; + + procedure set_address_fifo_depth(depth : positive); + procedure set_write_response_fifo_depth(depth : positive); + procedure set_address_stall_probability(probability : probability_t); + procedure set_data_stall_probability(probability : probability_t); + procedure set_write_response_stall_probability(probability : probability_t); + procedure set_min_response_latency(latency : delay_length); + procedure set_max_response_latency(latency : delay_length); + procedure set_check_4kbyte_boundary(value : boolean); + procedure enable_well_behaved_check; + impure function should_check_well_behaved return boolean; + impure function should_stall_address return boolean; + impure function should_stall_data return boolean; + impure function should_stall_write_response return boolean; + + impure function create_burst(axid : std_logic_vector; + axaddr : std_logic_vector; + axlen : std_logic_vector; + axsize : std_logic_vector; + axburst : axi_burst_type_t) return axi_burst_t; + + procedure push_burst(burst : axi_burst_t); + impure function pop_burst return axi_burst_t; + impure function burst_queue_full return boolean; + impure function burst_queue_empty return boolean; + impure function burst_queue_length return natural; + + impure function resp_queue_full return boolean; + impure function resp_queue_empty return boolean; + impure function resp_queue_length return natural; + procedure push_resp(burst : axi_burst_t); + impure function pop_resp return axi_burst_t; + + procedure finish_burst(burst : axi_burst_t); + + procedure push_random_response_time; + impure function pop_response_time return time; + + procedure fail(msg : string); + procedure check_4kbyte_boundary(burst : axi_burst_t); + impure function data_size return integer; + + impure function get_statistics return axi_statistics_t; + procedure clear_statistics; + end protected; + + procedure main_loop(variable self : inout axi_slave_private_t; + signal net : inout network_t); + + procedure check_axi_resp(bus_handle : bus_master_t; got, expected : axi_resp_t; msg : string); +end package; + + +package body axi_slave_private_pkg is + + impure function describe_burst(burst : axi_burst_t) return string is + begin + return "#" & to_string(burst.index) & " for id " & to_string(burst.id); + end; + + procedure push_axi_burst(queue : queue_t; burst : axi_burst_t); + impure function pop_axi_burst(queue : queue_t) return axi_burst_t; + + type axi_slave_private_t is protected body + variable p_axi_slave : axi_slave_t; + variable p_axi_slave_type : axi_slave_type_t; + variable p_data_size : integer; + variable p_max_id : natural; + variable p_id_indexes : integer_vector_ptr_t; + variable p_burst_queue_max_length : natural; + variable p_burst_queue : queue_t; + variable p_burst_queue_length : natural; + variable p_resp_queue_max_length : natural; + variable p_resp_queue : queue_t; + variable p_resp_queue_length : natural; + variable p_check_4kbyte_boundary : boolean; + variable p_rnd : RandomPType; + variable p_addr_stall_prob : probability_t; + variable p_data_stall_prob : probability_t; + variable p_wresp_stall_prob : probability_t; + variable p_min_response_latency : delay_length; + variable p_max_response_latency : delay_length; + variable p_response_time_queue : queue_t; + variable p_check_well_behaved : boolean; + variable p_statistics : axi_statistics_t; + + procedure init(axi_slave : axi_slave_t; + axi_slave_type : axi_slave_type_t; + max_id : natural; + data : std_logic_vector) is + begin + p_axi_slave := axi_slave; + p_axi_slave_type := axi_slave_type; + p_data_size := data'length/8; + p_max_id := max_id; + p_id_indexes := new_integer_vector_ptr(len => max_id+1, value => 0); + p_burst_queue_max_length := axi_slave.p_initial_address_fifo_depth; + p_burst_queue := new_queue; + p_burst_queue_length := 0; + p_resp_queue_max_length := axi_slave.p_initial_write_response_fifo_depth; + p_resp_queue := new_queue; + p_resp_queue_length := 0; + p_check_4kbyte_boundary := axi_slave.p_initial_check_4kbyte_boundary; + p_check_well_behaved := false; + set_address_stall_probability(axi_slave.p_initial_address_stall_probability); + set_data_stall_probability(axi_slave.p_initial_data_stall_probability); + set_write_response_stall_probability(axi_slave.p_initial_write_response_stall_probability); + p_response_time_queue := new_queue; + set_min_response_latency(axi_slave.p_initial_min_response_latency); + set_max_response_latency(axi_slave.p_initial_max_response_latency); + p_statistics := new_axi_statistics; + end; + + impure function get_actor return actor_t is + begin + return p_axi_slave.p_actor; + end; + + procedure set_address_fifo_depth(depth : positive) is + begin + if burst_queue_length > depth then + fail("New address fifo depth " & to_string(depth) & + " is smaller than current content size " & to_string(burst_queue_length)); + else + p_burst_queue_max_length := depth; + end if; + end procedure; + + procedure set_write_response_fifo_depth(depth : positive) is + begin + if resp_queue_length > depth then + fail("New write response fifo depth " & to_string(depth) & + " is smaller than current content size " & to_string(resp_queue_length)); + else + p_resp_queue_max_length := depth; + end if; + end procedure; + + procedure set_address_stall_probability(probability : probability_t) is + begin + p_addr_stall_prob := probability; + end; + + procedure set_data_stall_probability(probability : probability_t) is + begin + p_data_stall_prob := probability; + end; + + procedure set_write_response_stall_probability(probability : probability_t) is + begin + p_wresp_stall_prob := probability; + end; + + procedure set_min_response_latency(latency : delay_length) is + begin + p_min_response_latency := latency; + end; + + procedure set_max_response_latency(latency : delay_length) is + begin + p_max_response_latency := latency; + end; + + procedure set_check_4kbyte_boundary(value : boolean) is + begin + p_check_4kbyte_boundary := value; + end; + + procedure enable_well_behaved_check is + begin + p_check_well_behaved := true; + end; + + impure function should_check_well_behaved return boolean is + begin + return p_check_well_behaved; + end; + + impure function should_stall(prob : probability_t) return boolean is + begin + -- Enhance performance when prob = 0.0 + return prob /= 0.0 and p_rnd.Uniform(0.0, 1.0) < prob; + end; + + impure function should_stall_address return boolean is + begin + return should_stall(p_addr_stall_prob); + end; + + impure function should_stall_data return boolean is + begin + return should_stall(p_data_stall_prob); + end; + + impure function should_stall_write_response return boolean is + begin + return should_stall(p_wresp_stall_prob); + end; + + impure function create_burst(axid : std_logic_vector; + axaddr : std_logic_vector; + axlen : std_logic_vector; + axsize : std_logic_vector; + axburst : axi_burst_type_t) return axi_burst_t is + + -- Return the correct prefix ar/aw depending on slave type + impure function ax return string is + begin + case p_axi_slave_type is + when read_slave => return "ar"; + when write_slave => return "aw"; + end case; + end; + + -- Return the correct read/write burst description + impure function description return string is + begin + case p_axi_slave_type is + when read_slave => return "read burst"; + when write_slave => return "write burst"; + end case; + end; + + impure function burst_string return string is + begin + + if axburst = axi_burst_type_fixed then + return "fixed"; + elsif axburst = axi_burst_type_incr then + return "incr"; + elsif axburst = axi_burst_type_wrap then + return "wrap"; + else + return "undefined"; + end if; + end; + + variable burst : axi_burst_t; + begin + burst.id := to_integer(unsigned(axid)); + burst.address := to_integer(unsigned(axaddr)); + burst.length := to_integer(unsigned(axlen)) + 1; + burst.size := 2**to_integer(unsigned(axsize)); + burst.burst_type := axburst; + assert burst.id <= p_max_id report "axi id to large"; + burst.index := get(p_id_indexes, burst.id); + set(p_id_indexes, burst.id, burst.index + 1); + + if is_visible(p_axi_slave.p_logger, debug) then + debug(p_axi_slave.p_logger, + "Got " & description & " " & describe_burst(burst) & + LF & ax & "id = 0x" & to_hstring(axid) & + LF & ax & "addr = 0x" & to_hstring(axaddr) & + LF & ax & "len = " & to_string(to_integer(unsigned(to_01(axlen)))) & + LF & ax & "size = " & to_string(to_integer(unsigned(to_01(axsize)))) & + LF & ax & "burst = " & burst_string & " (" & to_string(axburst) & ")" + ); + end if; + + add_burst_length(p_statistics, burst.length); + + if burst.burst_type = axi_burst_type_wrap then + fail("Wrapping burst type not supported"); + end if; + + if p_check_4kbyte_boundary then + check_4kbyte_boundary(burst); + end if; + + return burst; + end function; + + procedure push_burst(burst : axi_burst_t) is + begin + push_axi_burst(p_burst_queue, burst); + p_burst_queue_length := p_burst_queue_length + 1; + end; + + impure function pop_burst return axi_burst_t is + constant burst : axi_burst_t := pop_axi_burst(p_burst_queue); + begin + if is_visible(p_axi_slave.p_logger, debug) then + case p_axi_slave_type is + when write_slave => + debug(p_axi_slave.p_logger, + "Start accepting data for write burst " & describe_burst(burst)); + when read_slave => + debug(p_axi_slave.p_logger, + "Start providing data for read burst " & describe_burst(burst)); + end case; + end if; + p_burst_queue_length := p_burst_queue_length - 1; + return burst; + end; + + impure function burst_queue_full return boolean is + begin + return burst_queue_length = p_burst_queue_max_length; + end; + + impure function burst_queue_empty return boolean is + begin + return burst_queue_length = 0; + end; + + impure function burst_queue_length return natural is + begin + return p_burst_queue_length; + end; + + procedure push_resp(burst : axi_burst_t) is + begin + push_axi_burst(p_resp_queue, burst); + p_resp_queue_length := p_resp_queue_length + 1; + end; + + impure function pop_resp return axi_burst_t is + constant resp_burst : axi_burst_t := pop_axi_burst(p_resp_queue); + begin + if is_visible(p_axi_slave.p_logger, debug) then + debug(p_axi_slave.p_logger, + "Providing write response for burst " & describe_burst(resp_burst)); + end if; + p_resp_queue_length := p_resp_queue_length - 1; + return resp_burst; + end; + + procedure finish_burst(burst : axi_burst_t) is + begin + if is_visible(p_axi_slave.p_logger, debug) then + case p_axi_slave_type is + when write_slave => + debug(p_axi_slave.p_logger, + "Accepted last data for write burst " & describe_burst(burst)); + when read_slave => + debug(p_axi_slave.p_logger, + "Providing last data for read burst " & describe_burst(burst)); + end case; + end if; + end; + + impure function random_response_latency return delay_length is + begin + if p_min_response_latency = p_max_response_latency then + return p_min_response_latency; + else + return p_rnd.RandTime(p_min_response_latency, p_max_response_latency); + end if; + end; + + procedure push_random_response_time is + begin + push_time(p_response_time_queue, now + random_response_latency); + end; + + impure function pop_response_time return time is + begin + return pop_time(p_response_time_queue); + end; + + impure function resp_queue_full return boolean is + begin + return resp_queue_length = p_resp_queue_max_length; + end; + + impure function resp_queue_empty return boolean is + begin + return resp_queue_length = 0; + end; + + impure function resp_queue_length return natural is + begin + return p_resp_queue_length; + end; + + procedure fail(msg : string) is + begin + failure(p_axi_slave.p_logger, msg); + end; + + procedure check_4kbyte_boundary(burst : axi_burst_t) is + variable first_address, last_address : integer; + variable first_page, last_page : integer; + begin + first_address := burst.address - (burst.address mod data_size); -- Aligned + last_address := burst.address + burst.size*burst.length - 1; + + first_page := first_address / 4096; + last_page := last_address / 4096; + + if first_page /= last_page then + fail("Crossing 4KByte boundary. First page = " + & integer'image(first_page) & " (" & to_string(first_address) & "/4096)" + & ", last page = " + & integer'image(last_page) & " (" & to_string(last_address) & "/4096)"); + end if; + end procedure; + + impure function data_size return integer is + begin + return p_data_size; + end; + + impure function get_statistics return axi_statistics_t is + begin + return copy(p_statistics); + end; + + procedure clear_statistics is + begin + clear(p_statistics); + end; + + end protected body; + + + procedure push_axi_burst(queue : queue_t; burst : axi_burst_t) is + begin + push(queue, burst.id); + push(queue, burst.address); + push(queue, burst.length); + push(queue, burst.size); + push(queue, burst.index); + push_boolean(queue, burst.burst_type = axi_burst_type_fixed); + end; + + impure function pop_axi_burst(queue : queue_t) return axi_burst_t is + variable burst : axi_burst_t; + begin + burst.id := pop(queue); + burst.address := pop(queue); + burst.length := pop(queue); + burst.size := pop(queue); + burst.index := pop(queue); + + if pop_boolean(queue) then + burst.burst_type := axi_burst_type_fixed; + else + burst.burst_type := axi_burst_type_incr; + end if; + + return burst; + end; + + procedure main_loop(variable self : inout axi_slave_private_t; + signal net : inout network_t) is + variable reply_msg, request_msg : msg_t; + variable msg_type : msg_type_t; + + variable clear_stat : boolean; + variable stat : axi_statistics_t; + begin + while true loop + receive(net, self.get_actor, request_msg); + msg_type := message_type(request_msg); + + if msg_type = axi_slave_set_address_fifo_depth_msg then + self.set_address_fifo_depth(pop(request_msg)); + acknowledge(net, request_msg, true); + + elsif msg_type = axi_slave_set_write_response_fifo_depth_msg then + self.set_write_response_fifo_depth(pop(request_msg)); + acknowledge(net, request_msg, true); + + elsif msg_type = axi_slave_set_address_stall_probability_msg then + self.set_address_stall_probability(pop_real(request_msg)); + acknowledge(net, request_msg, true); + + elsif msg_type = axi_slave_set_data_stall_probability_msg then + self.set_data_stall_probability(pop_real(request_msg)); + acknowledge(net, request_msg, true); + + elsif msg_type = axi_slave_set_write_response_stall_probability_msg then + self.set_write_response_stall_probability(pop_real(request_msg)); + acknowledge(net, request_msg, true); + + elsif msg_type = axi_slave_set_response_latency_msg then + self.set_min_response_latency(pop_time(request_msg)); + self.set_max_response_latency(pop_time(request_msg)); + acknowledge(net, request_msg, true); + + elsif msg_type = axi_slave_configure_4kbyte_boundary_check_msg then + self.set_check_4kbyte_boundary(pop_boolean(request_msg)); + acknowledge(net, request_msg, true); + + elsif msg_type = axi_slave_get_statistics_msg then + clear_stat := pop_boolean(request_msg); + stat := self.get_statistics; + + if clear_stat then + self.clear_statistics; + end if; + + reply_msg := new_msg; + push_integer_vector_ptr_ref(reply_msg, stat.p_count_by_burst_length); + reply(net, request_msg, reply_msg); + delete(request_msg); + + elsif msg_type = axi_slave_enable_well_behaved_check_msg then + self.enable_well_behaved_check; + acknowledge(net, request_msg, true); + else + unexpected_msg_type(msg_type); + end if; + + delete(request_msg); + end loop; + end; + + function resp_to_string(resp : axi_resp_t) return string is + begin + case resp is + when axi_resp_okay => return "OKAY"; + when axi_resp_exokay => return "EXOKAY"; + when axi_resp_slverr => return "SLVERR"; + when axi_resp_decerr => return "DECERR"; + when others => return "UNKNOWN"; + end case; + end; + + procedure check_axi_resp(bus_handle : bus_master_t; got, expected : axi_resp_t; msg : string) is + function describe(resp : axi_resp_t) return string is + begin + return resp_to_string(resp) & "(" & to_string(resp) & ")"; + end; + begin + if got /= expected then + failure(bus_handle.p_logger, msg & " - Got AXI response " & describe(got) & " expected " & describe(expected)); + end if; + end; +end package body; diff --git a/vunit/vhdl/verification_components/src/axi_statistics_pkg.vhd b/vunit/vhdl/verification_components/src/axi_statistics_pkg.vhd index 0139a641d..d39895601 100644 --- a/vunit/vhdl/verification_components/src/axi_statistics_pkg.vhd +++ b/vunit/vhdl/verification_components/src/axi_statistics_pkg.vhd @@ -1,128 +1,128 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -use work.axi_pkg.all; -use work.integer_vector_ptr_pool_pkg.all; -use work.integer_vector_ptr_pkg.all; - -package axi_statistics_pkg is - type axi_statistics_t is record - -- Private - p_count_by_burst_length : integer_vector_ptr_t; - end record; - constant null_axi_statistics : axi_statistics_t := (p_count_by_burst_length => null_ptr); - - -- Get the maximum burst length that occured - impure function max_burst_length(stat : axi_statistics_t) return natural; - - -- Get the minimum burst length that occured - impure function min_burst_length(stat : axi_statistics_t) return natural; - - -- Get the number of bursts that occured with specific length - impure function get_num_burst_with_length(stat : axi_statistics_t; - burst_length : natural) return natural; - - -- Get the number of bursts - impure function num_bursts(stat : axi_statistics_t) return natural; - - -- Free dynamically allocated memory - procedure deallocate(variable stat : inout axi_statistics_t); - - -- Private - impure function new_axi_statistics return axi_statistics_t; - procedure add_burst_length(stat : axi_statistics_t; - burst_length : natural); - impure function copy(stat : axi_statistics_t) return axi_statistics_t; - procedure clear(stat : axi_statistics_t); -end package; - - -package body axi_statistics_pkg is - constant ptr_pool : integer_vector_ptr_pool_t := new_integer_vector_ptr_pool; - - impure function new_axi_statistics return axi_statistics_t is - variable stat : axi_statistics_t; - begin - stat := (p_count_by_burst_length => new_integer_vector_ptr(ptr_pool, - min_length => max_axi4_burst_length + 1)); - clear(stat); - return stat; - end; - - procedure clear(stat : axi_statistics_t) is - begin - -- Clear re-used integer_vector_ptr - for i in 0 to length(stat.p_count_by_burst_length) - 1 loop - set(stat.p_count_by_burst_length, i, 0); - end loop; - end; - - procedure add_burst_length(stat : axi_statistics_t; - burst_length : natural) is - - begin - set(stat.p_count_by_burst_length, burst_length, - get(stat.p_count_by_burst_length, burst_length) + 1); - end; - - impure function max_burst_length(stat : axi_statistics_t) return natural is - begin - for i in length(stat.p_count_by_burst_length)-1 downto 0 loop - if get_num_burst_with_length(stat, i) > 0 then - return i; - end if; - end loop; - - return 0; - end; - - impure function min_burst_length(stat : axi_statistics_t) return natural is - begin - for i in 0 to length(stat.p_count_by_burst_length)-1 loop - if get_num_burst_with_length(stat, i) > 0 then - return i; - end if; - end loop; - - return 0; - end; - - impure function get_num_burst_with_length(stat : axi_statistics_t; - burst_length : natural) return natural is - begin - if burst_length >= length(stat.p_count_by_burst_length) then - return 0; - else - return get(stat.p_count_by_burst_length, burst_length); - end if; - end; - - impure function num_bursts(stat : axi_statistics_t) return natural is - variable sum : natural := 0; - begin - for i in 0 to max_axi4_burst_length loop - sum := sum + get_num_burst_with_length(stat, i); - end loop; - return sum; - end; - - impure function copy(stat : axi_statistics_t) return axi_statistics_t is - constant stat2 : axi_statistics_t := new_axi_statistics; - begin - for i in 0 to length(stat.p_count_by_burst_length)-1 loop - set(stat2.p_count_by_burst_length, i, get(stat.p_count_by_burst_length, i)); - end loop; - return stat2; - end; - - procedure deallocate(variable stat : inout axi_statistics_t) is - begin - if stat /= null_axi_statistics then - recycle(ptr_pool, stat.p_count_by_burst_length); - stat := null_axi_statistics; - end if; - end; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.axi_pkg.all; +use work.integer_vector_ptr_pool_pkg.all; +use work.integer_vector_ptr_pkg.all; + +package axi_statistics_pkg is + type axi_statistics_t is record + -- Private + p_count_by_burst_length : integer_vector_ptr_t; + end record; + constant null_axi_statistics : axi_statistics_t := (p_count_by_burst_length => null_ptr); + + -- Get the maximum burst length that occured + impure function max_burst_length(stat : axi_statistics_t) return natural; + + -- Get the minimum burst length that occured + impure function min_burst_length(stat : axi_statistics_t) return natural; + + -- Get the number of bursts that occured with specific length + impure function get_num_burst_with_length(stat : axi_statistics_t; + burst_length : natural) return natural; + + -- Get the number of bursts + impure function num_bursts(stat : axi_statistics_t) return natural; + + -- Free dynamically allocated memory + procedure deallocate(variable stat : inout axi_statistics_t); + + -- Private + impure function new_axi_statistics return axi_statistics_t; + procedure add_burst_length(stat : axi_statistics_t; + burst_length : natural); + impure function copy(stat : axi_statistics_t) return axi_statistics_t; + procedure clear(stat : axi_statistics_t); +end package; + + +package body axi_statistics_pkg is + constant ptr_pool : integer_vector_ptr_pool_t := new_integer_vector_ptr_pool; + + impure function new_axi_statistics return axi_statistics_t is + variable stat : axi_statistics_t; + begin + stat := (p_count_by_burst_length => new_integer_vector_ptr(ptr_pool, + min_length => max_axi4_burst_length + 1)); + clear(stat); + return stat; + end; + + procedure clear(stat : axi_statistics_t) is + begin + -- Clear re-used integer_vector_ptr + for i in 0 to length(stat.p_count_by_burst_length) - 1 loop + set(stat.p_count_by_burst_length, i, 0); + end loop; + end; + + procedure add_burst_length(stat : axi_statistics_t; + burst_length : natural) is + + begin + set(stat.p_count_by_burst_length, burst_length, + get(stat.p_count_by_burst_length, burst_length) + 1); + end; + + impure function max_burst_length(stat : axi_statistics_t) return natural is + begin + for i in length(stat.p_count_by_burst_length)-1 downto 0 loop + if get_num_burst_with_length(stat, i) > 0 then + return i; + end if; + end loop; + + return 0; + end; + + impure function min_burst_length(stat : axi_statistics_t) return natural is + begin + for i in 0 to length(stat.p_count_by_burst_length)-1 loop + if get_num_burst_with_length(stat, i) > 0 then + return i; + end if; + end loop; + + return 0; + end; + + impure function get_num_burst_with_length(stat : axi_statistics_t; + burst_length : natural) return natural is + begin + if burst_length >= length(stat.p_count_by_burst_length) then + return 0; + else + return get(stat.p_count_by_burst_length, burst_length); + end if; + end; + + impure function num_bursts(stat : axi_statistics_t) return natural is + variable sum : natural := 0; + begin + for i in 0 to max_axi4_burst_length loop + sum := sum + get_num_burst_with_length(stat, i); + end loop; + return sum; + end; + + impure function copy(stat : axi_statistics_t) return axi_statistics_t is + constant stat2 : axi_statistics_t := new_axi_statistics; + begin + for i in 0 to length(stat.p_count_by_burst_length)-1 loop + set(stat2.p_count_by_burst_length, i, get(stat.p_count_by_burst_length, i)); + end loop; + return stat2; + end; + + procedure deallocate(variable stat : inout axi_statistics_t) is + begin + if stat /= null_axi_statistics then + recycle(ptr_pool, stat.p_count_by_burst_length); + stat := null_axi_statistics; + end if; + end; +end package body; diff --git a/vunit/vhdl/verification_components/src/axi_stream_master.vhd b/vunit/vhdl/verification_components/src/axi_stream_master.vhd index 1b7ba8f4a..228d223c5 100644 --- a/vunit/vhdl/verification_components/src/axi_stream_master.vhd +++ b/vunit/vhdl/verification_components/src/axi_stream_master.vhd @@ -1,174 +1,174 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -context work.vunit_context; -context work.com_context; -use work.stream_master_pkg.all; -use work.axi_stream_pkg.all; -use work.queue_pkg.all; -use work.sync_pkg.all; - -entity axi_stream_master is - generic ( - master : axi_stream_master_t; - drive_invalid : boolean := true; - drive_invalid_val : std_logic := 'X'; - drive_invalid_val_user : std_logic := '0' - ); - port ( - aclk : in std_logic; - areset_n : in std_logic := '1'; - tvalid : out std_logic := '0'; - tready : in std_logic := '1'; - tdata : out std_logic_vector(data_length(master)-1 downto 0) := (others => '0'); - tlast : out std_logic := '0'; - tkeep : out std_logic_vector(data_length(master)/8-1 downto 0) := (others => '0'); - tstrb : out std_logic_vector(data_length(master)/8-1 downto 0) := (others => '0'); - tid : out std_logic_vector(id_length(master)-1 downto 0) := (others => '0'); - tdest : out std_logic_vector(dest_length(master)-1 downto 0) := (others => '0'); - tuser : out std_logic_vector(user_length(master)-1 downto 0) := (others => '0') - ); -end entity; - -architecture a of axi_stream_master is - - constant notify_request_msg : msg_type_t := new_msg_type("notify request"); - constant message_queue : queue_t := new_queue; - signal notify_bus_process_done : std_logic := '0'; - -begin - - main : process - variable request_msg : msg_t; - variable notify_msg : msg_t; - variable msg_type : msg_type_t; - begin - receive(net, master.p_actor, request_msg); - msg_type := message_type(request_msg); - - if msg_type = stream_push_msg or msg_type = push_axi_stream_msg then - push(message_queue, request_msg); - elsif msg_type = wait_for_time_msg then - push(message_queue, request_msg); - elsif msg_type = wait_until_idle_msg then - notify_msg := new_msg(notify_request_msg); - push(message_queue, notify_msg); - wait on notify_bus_process_done until is_empty(message_queue); - handle_wait_until_idle(net, msg_type, request_msg); - else - unexpected_msg_type(msg_type); - end if; - end process; - - bus_process : process - variable msg : msg_t; - variable msg_type : msg_type_t; - begin - if drive_invalid then - tdata <= (others => drive_invalid_val); - tkeep <= (others => drive_invalid_val); - tstrb <= (others => drive_invalid_val); - tid <= (others => drive_invalid_val); - tdest <= (others => drive_invalid_val); - tuser <= (others => drive_invalid_val_user); - end if; - - -- Wait for messages to arrive on the queue, posted by the process above - wait until rising_edge(aclk) and (not is_empty(message_queue) or areset_n = '0'); - - if (areset_n = '0') then - tvalid <= '0'; - else - while not is_empty(message_queue) loop - msg := pop(message_queue); - msg_type := message_type(msg); - - if msg_type = wait_for_time_msg then - handle_sync_message(net, msg_type, msg); - -- Re-align with the clock when a wait for time message was handled, because this breaks edge alignment. - wait until rising_edge(aclk); - elsif msg_type = notify_request_msg then - -- Ignore this message, but expect it - elsif msg_type = stream_push_msg or msg_type = push_axi_stream_msg then - tvalid <= '1'; - tdata <= pop_std_ulogic_vector(msg); - if msg_type = push_axi_stream_msg then - tlast <= pop_std_ulogic(msg); - tkeep <= pop_std_ulogic_vector(msg); - tstrb <= pop_std_ulogic_vector(msg); - tid <= pop_std_ulogic_vector(msg); - tdest <= pop_std_ulogic_vector(msg); - tuser <= pop_std_ulogic_vector(msg); - else - if pop_boolean(msg) then - tlast <= '1'; - else - tlast <= '0'; - end if; - tkeep <= (others => '1'); - tstrb <= (others => '1'); - tid <= (others => '0'); - tdest <= (others => '0'); - tuser <= (others => '0'); - end if; - wait until ((tvalid and tready) = '1' or areset_n = '0') and rising_edge(aclk); - tvalid <= '0'; - tlast <= '0'; - else - unexpected_msg_type(msg_type); - end if; - - delete(msg); - end loop; - - notify_bus_process_done <= '1'; - wait until notify_bus_process_done = '1'; - notify_bus_process_done <= '0'; - end if; - end process; - - axi_stream_monitor_generate : if master.p_monitor /= null_axi_stream_monitor generate - axi_stream_monitor_inst : entity work.axi_stream_monitor - generic map( - monitor => master.p_monitor - ) - port map( - aclk => aclk, - tvalid => tvalid, - tready => tready, - tdata => tdata, - tlast => tlast, - tkeep => tkeep, - tstrb => tstrb, - tid => tid, - tdest => tdest, - tuser => tuser - ); - end generate axi_stream_monitor_generate; - - axi_stream_protocol_checker_generate : if master.p_protocol_checker /= null_axi_stream_protocol_checker generate - axi_stream_protocol_checker_inst: entity work.axi_stream_protocol_checker - generic map ( - protocol_checker => master.p_protocol_checker) - port map ( - aclk => aclk, - areset_n => areset_n, - tvalid => tvalid, - tready => tready, - tdata => tdata, - tlast => tlast, - tkeep => tkeep, - tstrb => tstrb, - tid => tid, - tdest => tdest, - tuser => tuser - ); - end generate axi_stream_protocol_checker_generate; - +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +context work.vunit_context; +context work.com_context; +use work.stream_master_pkg.all; +use work.axi_stream_pkg.all; +use work.queue_pkg.all; +use work.sync_pkg.all; + +entity axi_stream_master is + generic ( + master : axi_stream_master_t; + drive_invalid : boolean := true; + drive_invalid_val : std_logic := 'X'; + drive_invalid_val_user : std_logic := '0' + ); + port ( + aclk : in std_logic; + areset_n : in std_logic := '1'; + tvalid : out std_logic := '0'; + tready : in std_logic := '1'; + tdata : out std_logic_vector(data_length(master)-1 downto 0) := (others => '0'); + tlast : out std_logic := '0'; + tkeep : out std_logic_vector(data_length(master)/8-1 downto 0) := (others => '0'); + tstrb : out std_logic_vector(data_length(master)/8-1 downto 0) := (others => '0'); + tid : out std_logic_vector(id_length(master)-1 downto 0) := (others => '0'); + tdest : out std_logic_vector(dest_length(master)-1 downto 0) := (others => '0'); + tuser : out std_logic_vector(user_length(master)-1 downto 0) := (others => '0') + ); +end entity; + +architecture a of axi_stream_master is + + constant notify_request_msg : msg_type_t := new_msg_type("notify request"); + constant message_queue : queue_t := new_queue; + signal notify_bus_process_done : std_logic := '0'; + +begin + + main : process + variable request_msg : msg_t; + variable notify_msg : msg_t; + variable msg_type : msg_type_t; + begin + receive(net, master.p_actor, request_msg); + msg_type := message_type(request_msg); + + if msg_type = stream_push_msg or msg_type = push_axi_stream_msg then + push(message_queue, request_msg); + elsif msg_type = wait_for_time_msg then + push(message_queue, request_msg); + elsif msg_type = wait_until_idle_msg then + notify_msg := new_msg(notify_request_msg); + push(message_queue, notify_msg); + wait on notify_bus_process_done until is_empty(message_queue); + handle_wait_until_idle(net, msg_type, request_msg); + else + unexpected_msg_type(msg_type); + end if; + end process; + + bus_process : process + variable msg : msg_t; + variable msg_type : msg_type_t; + begin + if drive_invalid then + tdata <= (others => drive_invalid_val); + tkeep <= (others => drive_invalid_val); + tstrb <= (others => drive_invalid_val); + tid <= (others => drive_invalid_val); + tdest <= (others => drive_invalid_val); + tuser <= (others => drive_invalid_val_user); + end if; + + -- Wait for messages to arrive on the queue, posted by the process above + wait until rising_edge(aclk) and (not is_empty(message_queue) or areset_n = '0'); + + if (areset_n = '0') then + tvalid <= '0'; + else + while not is_empty(message_queue) loop + msg := pop(message_queue); + msg_type := message_type(msg); + + if msg_type = wait_for_time_msg then + handle_sync_message(net, msg_type, msg); + -- Re-align with the clock when a wait for time message was handled, because this breaks edge alignment. + wait until rising_edge(aclk); + elsif msg_type = notify_request_msg then + -- Ignore this message, but expect it + elsif msg_type = stream_push_msg or msg_type = push_axi_stream_msg then + tvalid <= '1'; + tdata <= pop_std_ulogic_vector(msg); + if msg_type = push_axi_stream_msg then + tlast <= pop_std_ulogic(msg); + tkeep <= pop_std_ulogic_vector(msg); + tstrb <= pop_std_ulogic_vector(msg); + tid <= pop_std_ulogic_vector(msg); + tdest <= pop_std_ulogic_vector(msg); + tuser <= pop_std_ulogic_vector(msg); + else + if pop_boolean(msg) then + tlast <= '1'; + else + tlast <= '0'; + end if; + tkeep <= (others => '1'); + tstrb <= (others => '1'); + tid <= (others => '0'); + tdest <= (others => '0'); + tuser <= (others => '0'); + end if; + wait until ((tvalid and tready) = '1' or areset_n = '0') and rising_edge(aclk); + tvalid <= '0'; + tlast <= '0'; + else + unexpected_msg_type(msg_type); + end if; + + delete(msg); + end loop; + + notify_bus_process_done <= '1'; + wait until notify_bus_process_done = '1'; + notify_bus_process_done <= '0'; + end if; + end process; + + axi_stream_monitor_generate : if master.p_monitor /= null_axi_stream_monitor generate + axi_stream_monitor_inst : entity work.axi_stream_monitor + generic map( + monitor => master.p_monitor + ) + port map( + aclk => aclk, + tvalid => tvalid, + tready => tready, + tdata => tdata, + tlast => tlast, + tkeep => tkeep, + tstrb => tstrb, + tid => tid, + tdest => tdest, + tuser => tuser + ); + end generate axi_stream_monitor_generate; + + axi_stream_protocol_checker_generate : if master.p_protocol_checker /= null_axi_stream_protocol_checker generate + axi_stream_protocol_checker_inst: entity work.axi_stream_protocol_checker + generic map ( + protocol_checker => master.p_protocol_checker) + port map ( + aclk => aclk, + areset_n => areset_n, + tvalid => tvalid, + tready => tready, + tdata => tdata, + tlast => tlast, + tkeep => tkeep, + tstrb => tstrb, + tid => tid, + tdest => tdest, + tuser => tuser + ); + end generate axi_stream_protocol_checker_generate; + end architecture; \ No newline at end of file diff --git a/vunit/vhdl/verification_components/src/axi_stream_monitor.vhd b/vunit/vhdl/verification_components/src/axi_stream_monitor.vhd index 48567515d..35e9622bc 100644 --- a/vunit/vhdl/verification_components/src/axi_stream_monitor.vhd +++ b/vunit/vhdl/verification_components/src/axi_stream_monitor.vhd @@ -1,83 +1,83 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -context work.vunit_context; -context work.com_context; -use work.axi_stream_pkg.all; - -entity axi_stream_monitor is - generic ( - monitor : axi_stream_monitor_t); - port ( - aclk : in std_logic; - tvalid : in std_logic; - tready : in std_logic := '1'; - tdata : in std_logic_vector(data_length(monitor) - 1 downto 0); - tlast : in std_logic := '1'; - tkeep : in std_logic_vector(data_length(monitor)/8-1 downto 0) := (others => '0'); - tstrb : in std_logic_vector(data_length(monitor)/8-1 downto 0) := (others => '0'); - tid : in std_logic_vector(id_length(monitor)-1 downto 0) := (others => '0'); - tdest : in std_logic_vector(dest_length(monitor)-1 downto 0) := (others => '0'); - tuser : in std_logic_vector(user_length(monitor)-1 downto 0) := (others => '0') - ); -end entity; - -architecture a of axi_stream_monitor is -begin - main : process - variable msg : msg_t; - variable axi_stream_transaction : axi_stream_transaction_t( - tdata(tdata'range), - tkeep(tkeep'range), - tstrb(tstrb'range), - tid(tid'range), - tdest(tdest'range), - tuser(tuser'range) - ); - begin - wait until (tvalid and tready) = '1' and rising_edge(aclk); - - if is_visible(monitor.p_logger, debug) then - debug(monitor.p_logger, "tdata: " & to_nibble_string(tdata) & " (" & to_integer_string(tdata) & ")" & ", tlast: " & to_string(tlast)); - end if; - - axi_stream_transaction := ( - tdata => tdata, - tlast => tlast = '1', - tkeep => tkeep, - tstrb => tstrb, - tid => tid, - tdest => tdest, - tuser => tuser - ); - - msg := new_axi_stream_transaction_msg(axi_stream_transaction); - publish(net, monitor.p_actor, msg); - end process; - - axi_stream_protocol_checker_generate : if monitor.p_protocol_checker /= null_axi_stream_protocol_checker generate - axi_stream_protocol_checker_inst: entity work.axi_stream_protocol_checker - generic map ( - protocol_checker => monitor.p_protocol_checker) - port map ( - aclk => aclk, - areset_n => open, - tvalid => tvalid, - tready => tready, - tdata => tdata, - tlast => tlast, - tkeep => tkeep, - tstrb => tstrb, - tid => tid, - tdest => tdest, - tuser => tuser - ); - end generate axi_stream_protocol_checker_generate; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +context work.vunit_context; +context work.com_context; +use work.axi_stream_pkg.all; + +entity axi_stream_monitor is + generic ( + monitor : axi_stream_monitor_t); + port ( + aclk : in std_logic; + tvalid : in std_logic; + tready : in std_logic := '1'; + tdata : in std_logic_vector(data_length(monitor) - 1 downto 0); + tlast : in std_logic := '1'; + tkeep : in std_logic_vector(data_length(monitor)/8-1 downto 0) := (others => '0'); + tstrb : in std_logic_vector(data_length(monitor)/8-1 downto 0) := (others => '0'); + tid : in std_logic_vector(id_length(monitor)-1 downto 0) := (others => '0'); + tdest : in std_logic_vector(dest_length(monitor)-1 downto 0) := (others => '0'); + tuser : in std_logic_vector(user_length(monitor)-1 downto 0) := (others => '0') + ); +end entity; + +architecture a of axi_stream_monitor is +begin + main : process + variable msg : msg_t; + variable axi_stream_transaction : axi_stream_transaction_t( + tdata(tdata'range), + tkeep(tkeep'range), + tstrb(tstrb'range), + tid(tid'range), + tdest(tdest'range), + tuser(tuser'range) + ); + begin + wait until (tvalid and tready) = '1' and rising_edge(aclk); + + if is_visible(monitor.p_logger, debug) then + debug(monitor.p_logger, "tdata: " & to_nibble_string(tdata) & " (" & to_integer_string(tdata) & ")" & ", tlast: " & to_string(tlast)); + end if; + + axi_stream_transaction := ( + tdata => tdata, + tlast => tlast = '1', + tkeep => tkeep, + tstrb => tstrb, + tid => tid, + tdest => tdest, + tuser => tuser + ); + + msg := new_axi_stream_transaction_msg(axi_stream_transaction); + publish(net, monitor.p_actor, msg); + end process; + + axi_stream_protocol_checker_generate : if monitor.p_protocol_checker /= null_axi_stream_protocol_checker generate + axi_stream_protocol_checker_inst: entity work.axi_stream_protocol_checker + generic map ( + protocol_checker => monitor.p_protocol_checker) + port map ( + aclk => aclk, + areset_n => open, + tvalid => tvalid, + tready => tready, + tdata => tdata, + tlast => tlast, + tkeep => tkeep, + tstrb => tstrb, + tid => tid, + tdest => tdest, + tuser => tuser + ); + end generate axi_stream_protocol_checker_generate; + +end architecture; diff --git a/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd b/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd index 40f0ddc85..d03f8ca0b 100644 --- a/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd +++ b/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd @@ -1,775 +1,775 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -use work.logger_pkg.all; -use work.checker_pkg.all; -use work.check_pkg.all; -use work.stream_master_pkg.all; -use work.stream_slave_pkg.all; -use work.sync_pkg.all; -context work.vunit_context; -context work.com_context; -context work.data_types_context; - -package axi_stream_pkg is - - type axi_stream_component_type_t is (null_component, default_component, custom_component); - - type axi_stream_protocol_checker_t is record - p_type : axi_stream_component_type_t; - p_actor : actor_t; - p_data_length : natural; - p_id_length : natural; - p_dest_length : natural; - p_user_length : natural; - p_logger : logger_t; - p_max_waits : natural; - end record; - - constant null_axi_stream_protocol_checker : axi_stream_protocol_checker_t := ( - p_type => null_component, - p_actor => null_actor, - p_data_length => 0, - p_id_length => 0, - p_dest_length => 0, - p_user_length => 0, - p_logger => null_logger, - p_max_waits => 0 - ); - - -- The default protocol checker is used to specify that the checker - -- configuration is defined by the parent component into which the checker is - -- instantiated. - constant default_axi_stream_protocol_checker : axi_stream_protocol_checker_t := ( - p_type => default_component, - p_actor => null_actor, - p_data_length => 0, - p_id_length => 0, - p_dest_length => 0, - p_user_length => 0, - p_logger => null_logger, - p_max_waits => 0 - ); - - type axi_stream_monitor_t is record - p_type : axi_stream_component_type_t; - p_actor : actor_t; - p_data_length : natural; - p_id_length : natural; - p_dest_length : natural; - p_user_length : natural; - p_logger : logger_t; - p_protocol_checker : axi_stream_protocol_checker_t; - end record; - - constant null_axi_stream_monitor : axi_stream_monitor_t := ( - p_type => null_component, - p_actor => null_actor, - p_data_length => 0, - p_id_length => 0, - p_dest_length => 0, - p_user_length => 0, - p_logger => null_logger, - p_protocol_checker => null_axi_stream_protocol_checker - ); - - -- The default monitor is used to specify that the monitor - -- configuration is defined by the parent component into which the monitor is - -- instantiated. - constant default_axi_stream_monitor : axi_stream_monitor_t := ( - p_type => default_component, - p_actor => null_actor, - p_data_length => 0, - p_id_length => 0, - p_dest_length => 0, - p_user_length => 0, - p_logger => null_logger, - p_protocol_checker => null_axi_stream_protocol_checker - ); - - type axi_stream_master_t is record - p_actor : actor_t; - p_data_length : natural; - p_id_length : natural; - p_dest_length : natural; - p_user_length : natural; - p_logger : logger_t; - p_monitor : axi_stream_monitor_t; - p_protocol_checker : axi_stream_protocol_checker_t; - end record; - - type axi_stream_slave_t is record - p_actor : actor_t; - p_data_length : natural; - p_id_length : natural; - p_dest_length : natural; - p_user_length : natural; - p_logger : logger_t; - p_monitor : axi_stream_monitor_t; - p_protocol_checker : axi_stream_protocol_checker_t; - end record; - - constant axi_stream_logger : logger_t := get_logger("vunit_lib:axi_stream_pkg"); - constant axi_stream_checker : checker_t := new_checker(axi_stream_logger); - - impure function new_axi_stream_master( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - logger : logger_t := axi_stream_logger; - actor : actor_t := null_actor; - monitor : axi_stream_monitor_t := null_axi_stream_monitor; - protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker - ) return axi_stream_master_t; - - impure function new_axi_stream_slave( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - logger : logger_t := axi_stream_logger; - actor : actor_t := null_actor; - monitor : axi_stream_monitor_t := null_axi_stream_monitor; - protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker - ) return axi_stream_slave_t; - - impure function new_axi_stream_monitor( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - logger : logger_t := axi_stream_logger; - actor : actor_t; - protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker - ) return axi_stream_monitor_t; - - impure function new_axi_stream_protocol_checker( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - logger : logger_t := axi_stream_logger; - actor : actor_t := null_actor; - max_waits : natural := 16 - ) return axi_stream_protocol_checker_t; - - impure function data_length(master : axi_stream_master_t) return natural; - impure function data_length(slave : axi_stream_slave_t) return natural; - impure function data_length(monitor : axi_stream_monitor_t) return natural; - impure function data_length(protocol_checker : axi_stream_protocol_checker_t) return natural; - impure function id_length(master : axi_stream_master_t) return natural; - impure function id_length(slave : axi_stream_slave_t) return natural; - impure function id_length(monitor : axi_stream_monitor_t) return natural; - impure function id_length(protocol_checker : axi_stream_protocol_checker_t) return natural; - impure function dest_length(master : axi_stream_master_t) return natural; - impure function dest_length(slave : axi_stream_slave_t) return natural; - impure function dest_length(monitor : axi_stream_monitor_t) return natural; - impure function dest_length(protocol_checker : axi_stream_protocol_checker_t) return natural; - impure function user_length(master : axi_stream_master_t) return natural; - impure function user_length(slave : axi_stream_slave_t) return natural; - impure function user_length(monitor : axi_stream_monitor_t) return natural; - impure function user_length(protocol_checker : axi_stream_protocol_checker_t) return natural; - impure function as_stream(master : axi_stream_master_t) return stream_master_t; - impure function as_stream(slave : axi_stream_slave_t) return stream_slave_t; - impure function as_sync(master : axi_stream_master_t) return sync_handle_t; - impure function as_sync(slave : axi_stream_slave_t) return sync_handle_t; - - constant push_axi_stream_msg : msg_type_t := new_msg_type("push axi stream"); - constant pop_axi_stream_msg : msg_type_t := new_msg_type("pop axi stream"); - constant check_axi_stream_msg : msg_type_t := new_msg_type("check axi stream"); - constant axi_stream_transaction_msg : msg_type_t := new_msg_type("axi stream transaction"); - - alias axi_stream_reference_t is msg_t; - - procedure push_axi_stream( - signal net : inout network_t; - axi_stream : axi_stream_master_t; - tdata : std_logic_vector; - tlast : std_logic := '1'; - tkeep : std_logic_vector := ""; - tstrb : std_logic_vector := ""; - tid : std_logic_vector := ""; - tdest : std_logic_vector := ""; - tuser : std_logic_vector := "" - ); - - -- Blocking: pop a value from the axi stream - procedure pop_axi_stream( - signal net : inout network_t; - axi_stream : axi_stream_slave_t; - variable tdata : out std_logic_vector; - variable tlast : out std_logic; - variable tkeep : out std_logic_vector; - variable tstrb : out std_logic_vector; - variable tid : out std_logic_vector; - variable tdest : out std_logic_vector; - variable tuser : out std_logic_vector - ); - - procedure pop_axi_stream( - signal net : inout network_t; - axi_stream : axi_stream_slave_t; - variable tdata : out std_logic_vector; - variable tlast : out std_logic - ); - - -- Non-blocking: pop a value from the axi stream to be read in the future - procedure pop_axi_stream(signal net : inout network_t; - axi_stream : axi_stream_slave_t; - variable reference : inout axi_stream_reference_t); - - -- Blocking: Wait for reply to non-blocking pop - procedure await_pop_axi_stream_reply( - signal net : inout network_t; - variable reference : inout axi_stream_reference_t; - variable tdata : out std_logic_vector; - variable tlast : out std_logic; - variable tkeep : out std_logic_vector; - variable tstrb : out std_logic_vector; - variable tid : out std_logic_vector; - variable tdest : out std_logic_vector; - variable tuser : out std_logic_vector - ); - - procedure await_pop_axi_stream_reply( - signal net : inout network_t; - variable reference : inout axi_stream_reference_t; - variable tdata : out std_logic_vector; - variable tlast : out std_logic - ); - - -- Blocking: read axi stream and check result against expected value - procedure check_axi_stream( - signal net : inout network_t; - axi_stream : axi_stream_slave_t; - expected : std_logic_vector; - tlast : std_logic := '1'; - tkeep : std_logic_vector := ""; - tstrb : std_logic_vector := ""; - tid : std_logic_vector := ""; - tdest : std_logic_vector := ""; - tuser : std_logic_vector := ""; - msg : string := ""; - blocking : boolean := true - ); - - type axi_stream_transaction_t is record - tdata : std_logic_vector; - tlast : boolean; - tkeep : std_logic_vector; - tstrb : std_logic_vector; - tid : std_logic_vector; - tdest : std_logic_vector; - tuser : std_logic_vector; - end record; - - procedure push_axi_stream_transaction(msg : msg_t; axi_stream_transaction : axi_stream_transaction_t); - procedure pop_axi_stream_transaction( - constant msg : in msg_t; - variable axi_stream_transaction : out axi_stream_transaction_t - ); - - impure function new_axi_stream_transaction_msg( - axi_stream_transaction : axi_stream_transaction_t - ) return msg_t; - - procedure handle_axi_stream_transaction( - variable msg_type : inout msg_type_t; - variable msg : inout msg_t; - variable axi_transaction : out axi_stream_transaction_t); - -end package; - -package body axi_stream_pkg is - - impure function get_valid_monitor( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - logger : logger_t := axi_stream_logger; - actor : actor_t; - monitor : axi_stream_monitor_t; - parent_component : string - ) return axi_stream_monitor_t is - begin - if monitor = null_axi_stream_monitor then - return monitor; - elsif monitor = default_axi_stream_monitor then - check(actor /= null_actor, "A valid actor is needed to create a default monitor"); - return new_axi_stream_monitor(data_length, id_length, dest_length, user_length, logger, actor); - else - check_equal(axi_stream_checker, monitor.p_data_length, data_length, "Data length of monitor doesn't match that of the " & parent_component); - check_equal(axi_stream_checker, monitor.p_id_length, id_length, "ID length of monitor doesn't match that of the " & parent_component); - check_equal(axi_stream_checker, monitor.p_dest_length, dest_length, "Dest length of monitor doesn't match that of the " & parent_component); - check_equal(axi_stream_checker, monitor.p_user_length, user_length, "User length of monitor doesn't match that of the " & parent_component); - return monitor; - end if; - end; - - impure function get_valid_protocol_checker( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - logger : logger_t; - actor : actor_t; - protocol_checker : axi_stream_protocol_checker_t; - parent_component : string - ) return axi_stream_protocol_checker_t is - begin - if protocol_checker = null_axi_stream_protocol_checker then - return protocol_checker; - elsif protocol_checker = default_axi_stream_protocol_checker then - return new_axi_stream_protocol_checker(data_length, id_length, dest_length, user_length, logger, actor); - else - check_equal(axi_stream_checker, protocol_checker.p_data_length, data_length, "Data length of protocol checker doesn't match that of the " & parent_component); - check_equal(axi_stream_checker, protocol_checker.p_id_length, id_length, "ID length of monitor doesn't match that of the " & parent_component); - check_equal(axi_stream_checker, protocol_checker.p_dest_length, dest_length, "Dest length of monitor doesn't match that of the " & parent_component); - check_equal(axi_stream_checker, protocol_checker.p_user_length, user_length, "User length of monitor doesn't match that of the " & parent_component); - return protocol_checker; - end if; - end; - - impure function new_axi_stream_master( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - logger : logger_t := axi_stream_logger; - actor : actor_t := null_actor; - monitor : axi_stream_monitor_t := null_axi_stream_monitor; - protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker - ) return axi_stream_master_t is - variable p_actor : actor_t; - variable p_monitor : axi_stream_monitor_t; - variable p_protocol_checker : axi_stream_protocol_checker_t; - begin - p_monitor := get_valid_monitor(data_length, id_length, dest_length, user_length, logger, actor, monitor, "master"); - p_actor := actor when actor /= null_actor else new_actor; - p_protocol_checker := get_valid_protocol_checker(data_length, id_length, dest_length, user_length, logger, actor, protocol_checker, "master"); - - return (p_actor => p_actor, - p_data_length => data_length, - p_id_length => id_length, - p_dest_length => dest_length, - p_user_length => user_length, - p_logger => logger, - p_monitor => p_monitor, - p_protocol_checker => p_protocol_checker); - end; - - impure function new_axi_stream_slave( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - logger : logger_t := axi_stream_logger; - actor : actor_t := null_actor; - monitor : axi_stream_monitor_t := null_axi_stream_monitor; - protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker - ) return axi_stream_slave_t is - variable p_actor : actor_t; - variable p_monitor : axi_stream_monitor_t; - variable p_protocol_checker : axi_stream_protocol_checker_t; - begin - p_monitor := get_valid_monitor(data_length, id_length, dest_length, user_length, logger, actor, monitor, "slave"); - p_actor := actor when actor /= null_actor else new_actor; - p_protocol_checker := get_valid_protocol_checker(data_length, id_length, dest_length, user_length, logger, actor, protocol_checker, "slave"); - - return (p_actor => new_actor, - p_data_length => data_length, - p_id_length => id_length, - p_dest_length => dest_length, - p_user_length => user_length, - p_logger => logger, - p_monitor => p_monitor, - p_protocol_checker => p_protocol_checker); - end; - - impure function new_axi_stream_monitor( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - logger : logger_t := axi_stream_logger; - actor : actor_t; - protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker - ) return axi_stream_monitor_t is - constant p_protocol_checker : axi_stream_protocol_checker_t := get_valid_protocol_checker( - data_length, id_length, dest_length, user_length, logger, actor, protocol_checker, "monitor" - ); - begin - return ( - p_type => custom_component, - p_actor => actor, - p_data_length => data_length, - p_id_length => id_length, - p_dest_length => dest_length, - p_user_length => user_length, - p_logger => logger, - p_protocol_checker => p_protocol_checker); - end; - - impure function new_axi_stream_protocol_checker( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - logger : logger_t := axi_stream_logger; - actor : actor_t := null_actor; - max_waits : natural := 16 - ) return axi_stream_protocol_checker_t is - begin - return ( - p_type => custom_component, - p_actor => actor, - p_data_length => data_length, - p_id_length => id_length, - p_dest_length => dest_length, - p_user_length => user_length, - p_logger => logger, - p_max_waits => max_waits); - end; - - impure function data_length(master : axi_stream_master_t) return natural is - begin - return master.p_data_length; - end; - - impure function data_length(slave : axi_stream_slave_t) return natural is - begin - return slave.p_data_length; - end; - - impure function data_length(monitor : axi_stream_monitor_t) return natural is - begin - return monitor.p_data_length; - end; - - impure function data_length(protocol_checker : axi_stream_protocol_checker_t) return natural is - begin - return protocol_checker.p_data_length; - end; - - impure function id_length(master : axi_stream_master_t) return natural is - begin - return master.p_id_length; - end; - - impure function id_length(slave : axi_stream_slave_t) return natural is - begin - return slave.p_id_length; - end; - - impure function id_length(monitor : axi_stream_monitor_t) return natural is - begin - return monitor.p_id_length; - end; - - impure function id_length(protocol_checker: axi_stream_protocol_checker_t) return natural is - begin - return protocol_checker.p_id_length; - end; - - impure function dest_length(master : axi_stream_master_t) return natural is - begin - return master.p_dest_length; - end; - - impure function dest_length(slave: axi_stream_slave_t) return natural is - begin - return slave.p_dest_length; - end; - - impure function dest_length(monitor : axi_stream_monitor_t) return natural is - begin - return monitor.p_dest_length; - end; - - impure function dest_length(protocol_checker : axi_stream_protocol_checker_t) return natural is - begin - return protocol_checker.p_dest_length; - end; - - impure function user_length(master : axi_stream_master_t) return natural is - begin - return master.p_user_length; - end; - - impure function user_length(slave : axi_stream_slave_t) return natural is - begin - return slave.p_user_length; - end; - - impure function user_length(monitor : axi_stream_monitor_t) return natural is - begin - return monitor.p_user_length; - end; - - impure function user_length(protocol_checker : axi_stream_protocol_checker_t) return natural is - begin - return protocol_checker.p_user_length; - end; - - impure function as_stream(master : axi_stream_master_t) return stream_master_t is - begin - return (p_actor => master.p_actor); - end; - - impure function as_stream(slave : axi_stream_slave_t) return stream_slave_t is - begin - return (p_actor => slave.p_actor); - end; - - impure function as_sync(master : axi_stream_master_t) return sync_handle_t is - begin - return master.p_actor; - end; - - impure function as_sync(slave : axi_stream_slave_t) return sync_handle_t is - begin - return slave.p_actor; - end; - - procedure push_axi_stream( - signal net : inout network_t; - axi_stream : axi_stream_master_t; - tdata : std_logic_vector; - tlast : std_logic := '1'; - tkeep : std_logic_vector := ""; - tstrb : std_logic_vector := ""; - tid : std_logic_vector := ""; - tdest : std_logic_vector := ""; - tuser : std_logic_vector := "" - ) is - variable msg : msg_t := new_msg(push_axi_stream_msg); - variable normalized_data : std_logic_vector(data_length(axi_stream)-1 downto 0) := (others => '0'); - variable normalized_keep : std_logic_vector(data_length(axi_stream)/8-1 downto 0) := (others => '0'); - variable normalized_strb : std_logic_vector(data_length(axi_stream)/8-1 downto 0) := (others => '0'); - variable normalized_id : std_logic_vector(id_length(axi_stream)-1 downto 0) := (others => '0'); - variable normalized_dest : std_logic_vector(dest_length(axi_stream)-1 downto 0) := (others => '0'); - variable normalized_user : std_logic_vector(user_length(axi_stream)-1 downto 0) := (others => '0'); - begin - normalized_data(tdata'length-1 downto 0) := tdata; - push_std_ulogic_vector(msg, normalized_data); - push_std_ulogic(msg, tlast); - normalized_keep(tkeep'length-1 downto 0) := tkeep; - push_std_ulogic_vector(msg, normalized_keep); - normalized_strb(tstrb'length-1 downto 0) := tstrb; - push_std_ulogic_vector(msg, normalized_strb); - normalized_id(tid'length-1 downto 0) := tid; - push_std_ulogic_vector(msg, normalized_id); - normalized_dest(tdest'length-1 downto 0) := tdest; - push_std_ulogic_vector(msg, normalized_dest); - normalized_user(tuser'length-1 downto 0) := tuser; - push_std_ulogic_vector(msg, normalized_user); - send(net, axi_stream.p_actor, msg); - end; - - procedure pop_axi_stream(signal net : inout network_t; - axi_stream : axi_stream_slave_t; - variable reference : inout axi_stream_reference_t) is - begin - reference := new_msg(pop_axi_stream_msg); - send(net, axi_stream.p_actor, reference); - end; - - procedure await_pop_axi_stream_reply( - signal net : inout network_t; - variable reference : inout axi_stream_reference_t; - variable tdata : out std_logic_vector; - variable tlast : out std_logic; - variable tkeep : out std_logic_vector; - variable tstrb : out std_logic_vector; - variable tid : out std_logic_vector; - variable tdest : out std_logic_vector; - variable tuser : out std_logic_vector - ) is - variable reply_msg : msg_t; - begin - receive_reply(net, reference, reply_msg); - tdata := pop_std_ulogic_vector(reply_msg); - if pop_boolean(reply_msg) then - tlast := '1'; - else - tlast := '0'; - end if; - tkeep := pop_std_ulogic_vector(reply_msg); - tstrb := pop_std_ulogic_vector(reply_msg); - tid := pop_std_ulogic_vector(reply_msg); - tdest := pop_std_ulogic_vector(reply_msg); - tuser := pop_std_ulogic_vector(reply_msg); - delete(reference); - delete(reply_msg); - end; - - procedure await_pop_axi_stream_reply( - signal net : inout network_t; - variable reference : inout axi_stream_reference_t; - variable tdata : out std_logic_vector; - variable tlast : out std_logic - ) is - variable reply_msg : msg_t; - begin - receive_reply(net, reference, reply_msg); - tdata := pop_std_ulogic_vector(reply_msg); - if pop_boolean(reply_msg) then - tlast := '1'; - else - tlast := '0'; - end if; - delete(reference); - delete(reply_msg); - end; - - procedure pop_axi_stream( - signal net : inout network_t; - axi_stream : axi_stream_slave_t; - variable tdata : out std_logic_vector; - variable tlast : out std_logic; - variable tkeep : out std_logic_vector; - variable tstrb : out std_logic_vector; - variable tid : out std_logic_vector; - variable tdest : out std_logic_vector; - variable tuser : out std_logic_vector - ) is - variable reference : axi_stream_reference_t; - begin - pop_axi_stream(net, axi_stream, reference); - await_pop_axi_stream_reply(net, reference, tdata, tlast, tkeep, tstrb, tid, tdest, tuser); - end; - - procedure pop_axi_stream( - signal net : inout network_t; - axi_stream : axi_stream_slave_t; - variable tdata : out std_logic_vector; - variable tlast : out std_logic - ) is - variable reference : axi_stream_reference_t; - begin - pop_axi_stream(net, axi_stream, reference); - await_pop_axi_stream_reply(net, reference, tdata, tlast); - end; - - procedure check_axi_stream( - signal net : inout network_t; - axi_stream : axi_stream_slave_t; - expected : std_logic_vector; - tlast : std_logic := '1'; - tkeep : std_logic_vector := ""; - tstrb : std_logic_vector := ""; - tid : std_logic_vector := ""; - tdest : std_logic_vector := ""; - tuser : std_logic_vector := ""; - msg : string := ""; - blocking : boolean := true - ) is - variable got_tdata : std_logic_vector(data_length(axi_stream)-1 downto 0); - variable got_tlast : std_logic; - variable got_tkeep : std_logic_vector(data_length(axi_stream)/8-1 downto 0); - variable got_tstrb : std_logic_vector(data_length(axi_stream)/8-1 downto 0); - variable got_tid : std_logic_vector(id_length(axi_stream)-1 downto 0); - variable got_tdest : std_logic_vector(dest_length(axi_stream)-1 downto 0); - variable got_tuser : std_logic_vector(user_length(axi_stream)-1 downto 0); - variable check_msg : msg_t := new_msg(check_axi_stream_msg); - variable normalized_data : std_logic_vector(data_length(axi_stream)-1 downto 0) := (others => '0'); - variable normalized_keep : std_logic_vector(data_length(axi_stream)/8-1 downto 0) := (others => '0'); - variable normalized_strb : std_logic_vector(data_length(axi_stream)/8-1 downto 0) := (others => '0'); - variable normalized_id : std_logic_vector(id_length(axi_stream)-1 downto 0) := (others => '0'); - variable normalized_dest : std_logic_vector(dest_length(axi_stream)-1 downto 0) := (others => '0'); - variable normalized_user : std_logic_vector(user_length(axi_stream)-1 downto 0) := (others => '0'); - begin - if blocking then - pop_axi_stream(net, axi_stream, got_tdata, got_tlast, got_tkeep, got_tstrb, got_tid, got_tdest, got_tuser); - check_equal(got_tdata, expected, "TDATA mismatch, " & msg); - check_equal(got_tlast, tlast, "TLAST mismatch, " & msg); - if tkeep'length > 0 then - check_equal(got_tkeep, tkeep, "TKEEP mismatch, " & msg); - end if; - if tstrb'length > 0 then - check_equal(got_tstrb, tstrb, "TSTRB mismatch, " & msg); - end if; - if tid'length > 0 then - check_equal(got_tid, tid, "TID mismatch, " & msg); - end if; - if tdest'length > 0 then - check_equal(got_tdest, tdest, "TDEST mismatch, " & msg); - end if; - if tuser'length > 0 then - check_equal(got_tuser, tuser, "TUSER mismatch, " & msg); - end if; - else - push_string(check_msg, msg); - normalized_data(expected'length-1 downto 0) := expected; - push_std_ulogic_vector(check_msg, normalized_data); - push_std_ulogic(check_msg, tlast); - normalized_keep(tkeep'length-1 downto 0) := tkeep; - push_std_ulogic_vector(check_msg, normalized_keep); - normalized_strb(tstrb'length-1 downto 0) := tstrb; - push_std_ulogic_vector(check_msg, normalized_strb); - normalized_id(tid'length-1 downto 0) := tid; - push_std_ulogic_vector(check_msg, normalized_id); - normalized_dest(tdest'length-1 downto 0) := tdest; - push_std_ulogic_vector(check_msg, normalized_dest); - normalized_user(tuser'length-1 downto 0) := tuser; - push_std_ulogic_vector(check_msg, normalized_user); - send(net, axi_stream.p_actor, check_msg); - end if; - end procedure; - - procedure push_axi_stream_transaction(msg : msg_t; axi_stream_transaction : axi_stream_transaction_t) is - begin - push_std_ulogic_vector(msg, axi_stream_transaction.tdata); - push_boolean(msg, axi_stream_transaction.tlast); - push_std_ulogic_vector(msg, axi_stream_transaction.tkeep); - push_std_ulogic_vector(msg, axi_stream_transaction.tstrb); - push_std_ulogic_vector(msg, axi_stream_transaction.tid); - push_std_ulogic_vector(msg, axi_stream_transaction.tdest); - push_std_ulogic_vector(msg, axi_stream_transaction.tuser); - end; - - procedure pop_axi_stream_transaction( - constant msg : in msg_t; - variable axi_stream_transaction : out axi_stream_transaction_t - ) is - begin - axi_stream_transaction.tdata := pop_std_ulogic_vector(msg); - axi_stream_transaction.tlast := pop_boolean(msg); - axi_stream_transaction.tkeep := pop_std_ulogic_vector(msg); - axi_stream_transaction.tstrb := pop_std_ulogic_vector(msg); - axi_stream_transaction.tid := pop_std_ulogic_vector(msg); - axi_stream_transaction.tdest := pop_std_ulogic_vector(msg); - axi_stream_transaction.tuser := pop_std_ulogic_vector(msg); - end; - - impure function new_axi_stream_transaction_msg( - axi_stream_transaction : axi_stream_transaction_t - ) return msg_t is - variable msg : msg_t; - begin - msg := new_msg(axi_stream_transaction_msg); - push_axi_stream_transaction(msg, axi_stream_transaction); - return msg; - end; - - procedure handle_axi_stream_transaction( - variable msg_type : inout msg_type_t; - variable msg : inout msg_t; - variable axi_transaction : out axi_stream_transaction_t) is - begin - if msg_type = axi_stream_transaction_msg then - handle_message(msg_type); - - pop_axi_stream_transaction(msg, axi_transaction); - end if; - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +use work.logger_pkg.all; +use work.checker_pkg.all; +use work.check_pkg.all; +use work.stream_master_pkg.all; +use work.stream_slave_pkg.all; +use work.sync_pkg.all; +context work.vunit_context; +context work.com_context; +context work.data_types_context; + +package axi_stream_pkg is + + type axi_stream_component_type_t is (null_component, default_component, custom_component); + + type axi_stream_protocol_checker_t is record + p_type : axi_stream_component_type_t; + p_actor : actor_t; + p_data_length : natural; + p_id_length : natural; + p_dest_length : natural; + p_user_length : natural; + p_logger : logger_t; + p_max_waits : natural; + end record; + + constant null_axi_stream_protocol_checker : axi_stream_protocol_checker_t := ( + p_type => null_component, + p_actor => null_actor, + p_data_length => 0, + p_id_length => 0, + p_dest_length => 0, + p_user_length => 0, + p_logger => null_logger, + p_max_waits => 0 + ); + + -- The default protocol checker is used to specify that the checker + -- configuration is defined by the parent component into which the checker is + -- instantiated. + constant default_axi_stream_protocol_checker : axi_stream_protocol_checker_t := ( + p_type => default_component, + p_actor => null_actor, + p_data_length => 0, + p_id_length => 0, + p_dest_length => 0, + p_user_length => 0, + p_logger => null_logger, + p_max_waits => 0 + ); + + type axi_stream_monitor_t is record + p_type : axi_stream_component_type_t; + p_actor : actor_t; + p_data_length : natural; + p_id_length : natural; + p_dest_length : natural; + p_user_length : natural; + p_logger : logger_t; + p_protocol_checker : axi_stream_protocol_checker_t; + end record; + + constant null_axi_stream_monitor : axi_stream_monitor_t := ( + p_type => null_component, + p_actor => null_actor, + p_data_length => 0, + p_id_length => 0, + p_dest_length => 0, + p_user_length => 0, + p_logger => null_logger, + p_protocol_checker => null_axi_stream_protocol_checker + ); + + -- The default monitor is used to specify that the monitor + -- configuration is defined by the parent component into which the monitor is + -- instantiated. + constant default_axi_stream_monitor : axi_stream_monitor_t := ( + p_type => default_component, + p_actor => null_actor, + p_data_length => 0, + p_id_length => 0, + p_dest_length => 0, + p_user_length => 0, + p_logger => null_logger, + p_protocol_checker => null_axi_stream_protocol_checker + ); + + type axi_stream_master_t is record + p_actor : actor_t; + p_data_length : natural; + p_id_length : natural; + p_dest_length : natural; + p_user_length : natural; + p_logger : logger_t; + p_monitor : axi_stream_monitor_t; + p_protocol_checker : axi_stream_protocol_checker_t; + end record; + + type axi_stream_slave_t is record + p_actor : actor_t; + p_data_length : natural; + p_id_length : natural; + p_dest_length : natural; + p_user_length : natural; + p_logger : logger_t; + p_monitor : axi_stream_monitor_t; + p_protocol_checker : axi_stream_protocol_checker_t; + end record; + + constant axi_stream_logger : logger_t := get_logger("vunit_lib:axi_stream_pkg"); + constant axi_stream_checker : checker_t := new_checker(axi_stream_logger); + + impure function new_axi_stream_master( + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + logger : logger_t := axi_stream_logger; + actor : actor_t := null_actor; + monitor : axi_stream_monitor_t := null_axi_stream_monitor; + protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker + ) return axi_stream_master_t; + + impure function new_axi_stream_slave( + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + logger : logger_t := axi_stream_logger; + actor : actor_t := null_actor; + monitor : axi_stream_monitor_t := null_axi_stream_monitor; + protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker + ) return axi_stream_slave_t; + + impure function new_axi_stream_monitor( + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + logger : logger_t := axi_stream_logger; + actor : actor_t; + protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker + ) return axi_stream_monitor_t; + + impure function new_axi_stream_protocol_checker( + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + logger : logger_t := axi_stream_logger; + actor : actor_t := null_actor; + max_waits : natural := 16 + ) return axi_stream_protocol_checker_t; + + impure function data_length(master : axi_stream_master_t) return natural; + impure function data_length(slave : axi_stream_slave_t) return natural; + impure function data_length(monitor : axi_stream_monitor_t) return natural; + impure function data_length(protocol_checker : axi_stream_protocol_checker_t) return natural; + impure function id_length(master : axi_stream_master_t) return natural; + impure function id_length(slave : axi_stream_slave_t) return natural; + impure function id_length(monitor : axi_stream_monitor_t) return natural; + impure function id_length(protocol_checker : axi_stream_protocol_checker_t) return natural; + impure function dest_length(master : axi_stream_master_t) return natural; + impure function dest_length(slave : axi_stream_slave_t) return natural; + impure function dest_length(monitor : axi_stream_monitor_t) return natural; + impure function dest_length(protocol_checker : axi_stream_protocol_checker_t) return natural; + impure function user_length(master : axi_stream_master_t) return natural; + impure function user_length(slave : axi_stream_slave_t) return natural; + impure function user_length(monitor : axi_stream_monitor_t) return natural; + impure function user_length(protocol_checker : axi_stream_protocol_checker_t) return natural; + impure function as_stream(master : axi_stream_master_t) return stream_master_t; + impure function as_stream(slave : axi_stream_slave_t) return stream_slave_t; + impure function as_sync(master : axi_stream_master_t) return sync_handle_t; + impure function as_sync(slave : axi_stream_slave_t) return sync_handle_t; + + constant push_axi_stream_msg : msg_type_t := new_msg_type("push axi stream"); + constant pop_axi_stream_msg : msg_type_t := new_msg_type("pop axi stream"); + constant check_axi_stream_msg : msg_type_t := new_msg_type("check axi stream"); + constant axi_stream_transaction_msg : msg_type_t := new_msg_type("axi stream transaction"); + + alias axi_stream_reference_t is msg_t; + + procedure push_axi_stream( + signal net : inout network_t; + axi_stream : axi_stream_master_t; + tdata : std_logic_vector; + tlast : std_logic := '1'; + tkeep : std_logic_vector := ""; + tstrb : std_logic_vector := ""; + tid : std_logic_vector := ""; + tdest : std_logic_vector := ""; + tuser : std_logic_vector := "" + ); + + -- Blocking: pop a value from the axi stream + procedure pop_axi_stream( + signal net : inout network_t; + axi_stream : axi_stream_slave_t; + variable tdata : out std_logic_vector; + variable tlast : out std_logic; + variable tkeep : out std_logic_vector; + variable tstrb : out std_logic_vector; + variable tid : out std_logic_vector; + variable tdest : out std_logic_vector; + variable tuser : out std_logic_vector + ); + + procedure pop_axi_stream( + signal net : inout network_t; + axi_stream : axi_stream_slave_t; + variable tdata : out std_logic_vector; + variable tlast : out std_logic + ); + + -- Non-blocking: pop a value from the axi stream to be read in the future + procedure pop_axi_stream(signal net : inout network_t; + axi_stream : axi_stream_slave_t; + variable reference : inout axi_stream_reference_t); + + -- Blocking: Wait for reply to non-blocking pop + procedure await_pop_axi_stream_reply( + signal net : inout network_t; + variable reference : inout axi_stream_reference_t; + variable tdata : out std_logic_vector; + variable tlast : out std_logic; + variable tkeep : out std_logic_vector; + variable tstrb : out std_logic_vector; + variable tid : out std_logic_vector; + variable tdest : out std_logic_vector; + variable tuser : out std_logic_vector + ); + + procedure await_pop_axi_stream_reply( + signal net : inout network_t; + variable reference : inout axi_stream_reference_t; + variable tdata : out std_logic_vector; + variable tlast : out std_logic + ); + + -- Blocking: read axi stream and check result against expected value + procedure check_axi_stream( + signal net : inout network_t; + axi_stream : axi_stream_slave_t; + expected : std_logic_vector; + tlast : std_logic := '1'; + tkeep : std_logic_vector := ""; + tstrb : std_logic_vector := ""; + tid : std_logic_vector := ""; + tdest : std_logic_vector := ""; + tuser : std_logic_vector := ""; + msg : string := ""; + blocking : boolean := true + ); + + type axi_stream_transaction_t is record + tdata : std_logic_vector; + tlast : boolean; + tkeep : std_logic_vector; + tstrb : std_logic_vector; + tid : std_logic_vector; + tdest : std_logic_vector; + tuser : std_logic_vector; + end record; + + procedure push_axi_stream_transaction(msg : msg_t; axi_stream_transaction : axi_stream_transaction_t); + procedure pop_axi_stream_transaction( + constant msg : in msg_t; + variable axi_stream_transaction : out axi_stream_transaction_t + ); + + impure function new_axi_stream_transaction_msg( + axi_stream_transaction : axi_stream_transaction_t + ) return msg_t; + + procedure handle_axi_stream_transaction( + variable msg_type : inout msg_type_t; + variable msg : inout msg_t; + variable axi_transaction : out axi_stream_transaction_t); + +end package; + +package body axi_stream_pkg is + + impure function get_valid_monitor( + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + logger : logger_t := axi_stream_logger; + actor : actor_t; + monitor : axi_stream_monitor_t; + parent_component : string + ) return axi_stream_monitor_t is + begin + if monitor = null_axi_stream_monitor then + return monitor; + elsif monitor = default_axi_stream_monitor then + check(actor /= null_actor, "A valid actor is needed to create a default monitor"); + return new_axi_stream_monitor(data_length, id_length, dest_length, user_length, logger, actor); + else + check_equal(axi_stream_checker, monitor.p_data_length, data_length, "Data length of monitor doesn't match that of the " & parent_component); + check_equal(axi_stream_checker, monitor.p_id_length, id_length, "ID length of monitor doesn't match that of the " & parent_component); + check_equal(axi_stream_checker, monitor.p_dest_length, dest_length, "Dest length of monitor doesn't match that of the " & parent_component); + check_equal(axi_stream_checker, monitor.p_user_length, user_length, "User length of monitor doesn't match that of the " & parent_component); + return monitor; + end if; + end; + + impure function get_valid_protocol_checker( + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + logger : logger_t; + actor : actor_t; + protocol_checker : axi_stream_protocol_checker_t; + parent_component : string + ) return axi_stream_protocol_checker_t is + begin + if protocol_checker = null_axi_stream_protocol_checker then + return protocol_checker; + elsif protocol_checker = default_axi_stream_protocol_checker then + return new_axi_stream_protocol_checker(data_length, id_length, dest_length, user_length, logger, actor); + else + check_equal(axi_stream_checker, protocol_checker.p_data_length, data_length, "Data length of protocol checker doesn't match that of the " & parent_component); + check_equal(axi_stream_checker, protocol_checker.p_id_length, id_length, "ID length of monitor doesn't match that of the " & parent_component); + check_equal(axi_stream_checker, protocol_checker.p_dest_length, dest_length, "Dest length of monitor doesn't match that of the " & parent_component); + check_equal(axi_stream_checker, protocol_checker.p_user_length, user_length, "User length of monitor doesn't match that of the " & parent_component); + return protocol_checker; + end if; + end; + + impure function new_axi_stream_master( + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + logger : logger_t := axi_stream_logger; + actor : actor_t := null_actor; + monitor : axi_stream_monitor_t := null_axi_stream_monitor; + protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker + ) return axi_stream_master_t is + variable p_actor : actor_t; + variable p_monitor : axi_stream_monitor_t; + variable p_protocol_checker : axi_stream_protocol_checker_t; + begin + p_monitor := get_valid_monitor(data_length, id_length, dest_length, user_length, logger, actor, monitor, "master"); + p_actor := actor when actor /= null_actor else new_actor; + p_protocol_checker := get_valid_protocol_checker(data_length, id_length, dest_length, user_length, logger, actor, protocol_checker, "master"); + + return (p_actor => p_actor, + p_data_length => data_length, + p_id_length => id_length, + p_dest_length => dest_length, + p_user_length => user_length, + p_logger => logger, + p_monitor => p_monitor, + p_protocol_checker => p_protocol_checker); + end; + + impure function new_axi_stream_slave( + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + logger : logger_t := axi_stream_logger; + actor : actor_t := null_actor; + monitor : axi_stream_monitor_t := null_axi_stream_monitor; + protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker + ) return axi_stream_slave_t is + variable p_actor : actor_t; + variable p_monitor : axi_stream_monitor_t; + variable p_protocol_checker : axi_stream_protocol_checker_t; + begin + p_monitor := get_valid_monitor(data_length, id_length, dest_length, user_length, logger, actor, monitor, "slave"); + p_actor := actor when actor /= null_actor else new_actor; + p_protocol_checker := get_valid_protocol_checker(data_length, id_length, dest_length, user_length, logger, actor, protocol_checker, "slave"); + + return (p_actor => new_actor, + p_data_length => data_length, + p_id_length => id_length, + p_dest_length => dest_length, + p_user_length => user_length, + p_logger => logger, + p_monitor => p_monitor, + p_protocol_checker => p_protocol_checker); + end; + + impure function new_axi_stream_monitor( + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + logger : logger_t := axi_stream_logger; + actor : actor_t; + protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker + ) return axi_stream_monitor_t is + constant p_protocol_checker : axi_stream_protocol_checker_t := get_valid_protocol_checker( + data_length, id_length, dest_length, user_length, logger, actor, protocol_checker, "monitor" + ); + begin + return ( + p_type => custom_component, + p_actor => actor, + p_data_length => data_length, + p_id_length => id_length, + p_dest_length => dest_length, + p_user_length => user_length, + p_logger => logger, + p_protocol_checker => p_protocol_checker); + end; + + impure function new_axi_stream_protocol_checker( + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + logger : logger_t := axi_stream_logger; + actor : actor_t := null_actor; + max_waits : natural := 16 + ) return axi_stream_protocol_checker_t is + begin + return ( + p_type => custom_component, + p_actor => actor, + p_data_length => data_length, + p_id_length => id_length, + p_dest_length => dest_length, + p_user_length => user_length, + p_logger => logger, + p_max_waits => max_waits); + end; + + impure function data_length(master : axi_stream_master_t) return natural is + begin + return master.p_data_length; + end; + + impure function data_length(slave : axi_stream_slave_t) return natural is + begin + return slave.p_data_length; + end; + + impure function data_length(monitor : axi_stream_monitor_t) return natural is + begin + return monitor.p_data_length; + end; + + impure function data_length(protocol_checker : axi_stream_protocol_checker_t) return natural is + begin + return protocol_checker.p_data_length; + end; + + impure function id_length(master : axi_stream_master_t) return natural is + begin + return master.p_id_length; + end; + + impure function id_length(slave : axi_stream_slave_t) return natural is + begin + return slave.p_id_length; + end; + + impure function id_length(monitor : axi_stream_monitor_t) return natural is + begin + return monitor.p_id_length; + end; + + impure function id_length(protocol_checker: axi_stream_protocol_checker_t) return natural is + begin + return protocol_checker.p_id_length; + end; + + impure function dest_length(master : axi_stream_master_t) return natural is + begin + return master.p_dest_length; + end; + + impure function dest_length(slave: axi_stream_slave_t) return natural is + begin + return slave.p_dest_length; + end; + + impure function dest_length(monitor : axi_stream_monitor_t) return natural is + begin + return monitor.p_dest_length; + end; + + impure function dest_length(protocol_checker : axi_stream_protocol_checker_t) return natural is + begin + return protocol_checker.p_dest_length; + end; + + impure function user_length(master : axi_stream_master_t) return natural is + begin + return master.p_user_length; + end; + + impure function user_length(slave : axi_stream_slave_t) return natural is + begin + return slave.p_user_length; + end; + + impure function user_length(monitor : axi_stream_monitor_t) return natural is + begin + return monitor.p_user_length; + end; + + impure function user_length(protocol_checker : axi_stream_protocol_checker_t) return natural is + begin + return protocol_checker.p_user_length; + end; + + impure function as_stream(master : axi_stream_master_t) return stream_master_t is + begin + return (p_actor => master.p_actor); + end; + + impure function as_stream(slave : axi_stream_slave_t) return stream_slave_t is + begin + return (p_actor => slave.p_actor); + end; + + impure function as_sync(master : axi_stream_master_t) return sync_handle_t is + begin + return master.p_actor; + end; + + impure function as_sync(slave : axi_stream_slave_t) return sync_handle_t is + begin + return slave.p_actor; + end; + + procedure push_axi_stream( + signal net : inout network_t; + axi_stream : axi_stream_master_t; + tdata : std_logic_vector; + tlast : std_logic := '1'; + tkeep : std_logic_vector := ""; + tstrb : std_logic_vector := ""; + tid : std_logic_vector := ""; + tdest : std_logic_vector := ""; + tuser : std_logic_vector := "" + ) is + variable msg : msg_t := new_msg(push_axi_stream_msg); + variable normalized_data : std_logic_vector(data_length(axi_stream)-1 downto 0) := (others => '0'); + variable normalized_keep : std_logic_vector(data_length(axi_stream)/8-1 downto 0) := (others => '0'); + variable normalized_strb : std_logic_vector(data_length(axi_stream)/8-1 downto 0) := (others => '0'); + variable normalized_id : std_logic_vector(id_length(axi_stream)-1 downto 0) := (others => '0'); + variable normalized_dest : std_logic_vector(dest_length(axi_stream)-1 downto 0) := (others => '0'); + variable normalized_user : std_logic_vector(user_length(axi_stream)-1 downto 0) := (others => '0'); + begin + normalized_data(tdata'length-1 downto 0) := tdata; + push_std_ulogic_vector(msg, normalized_data); + push_std_ulogic(msg, tlast); + normalized_keep(tkeep'length-1 downto 0) := tkeep; + push_std_ulogic_vector(msg, normalized_keep); + normalized_strb(tstrb'length-1 downto 0) := tstrb; + push_std_ulogic_vector(msg, normalized_strb); + normalized_id(tid'length-1 downto 0) := tid; + push_std_ulogic_vector(msg, normalized_id); + normalized_dest(tdest'length-1 downto 0) := tdest; + push_std_ulogic_vector(msg, normalized_dest); + normalized_user(tuser'length-1 downto 0) := tuser; + push_std_ulogic_vector(msg, normalized_user); + send(net, axi_stream.p_actor, msg); + end; + + procedure pop_axi_stream(signal net : inout network_t; + axi_stream : axi_stream_slave_t; + variable reference : inout axi_stream_reference_t) is + begin + reference := new_msg(pop_axi_stream_msg); + send(net, axi_stream.p_actor, reference); + end; + + procedure await_pop_axi_stream_reply( + signal net : inout network_t; + variable reference : inout axi_stream_reference_t; + variable tdata : out std_logic_vector; + variable tlast : out std_logic; + variable tkeep : out std_logic_vector; + variable tstrb : out std_logic_vector; + variable tid : out std_logic_vector; + variable tdest : out std_logic_vector; + variable tuser : out std_logic_vector + ) is + variable reply_msg : msg_t; + begin + receive_reply(net, reference, reply_msg); + tdata := pop_std_ulogic_vector(reply_msg); + if pop_boolean(reply_msg) then + tlast := '1'; + else + tlast := '0'; + end if; + tkeep := pop_std_ulogic_vector(reply_msg); + tstrb := pop_std_ulogic_vector(reply_msg); + tid := pop_std_ulogic_vector(reply_msg); + tdest := pop_std_ulogic_vector(reply_msg); + tuser := pop_std_ulogic_vector(reply_msg); + delete(reference); + delete(reply_msg); + end; + + procedure await_pop_axi_stream_reply( + signal net : inout network_t; + variable reference : inout axi_stream_reference_t; + variable tdata : out std_logic_vector; + variable tlast : out std_logic + ) is + variable reply_msg : msg_t; + begin + receive_reply(net, reference, reply_msg); + tdata := pop_std_ulogic_vector(reply_msg); + if pop_boolean(reply_msg) then + tlast := '1'; + else + tlast := '0'; + end if; + delete(reference); + delete(reply_msg); + end; + + procedure pop_axi_stream( + signal net : inout network_t; + axi_stream : axi_stream_slave_t; + variable tdata : out std_logic_vector; + variable tlast : out std_logic; + variable tkeep : out std_logic_vector; + variable tstrb : out std_logic_vector; + variable tid : out std_logic_vector; + variable tdest : out std_logic_vector; + variable tuser : out std_logic_vector + ) is + variable reference : axi_stream_reference_t; + begin + pop_axi_stream(net, axi_stream, reference); + await_pop_axi_stream_reply(net, reference, tdata, tlast, tkeep, tstrb, tid, tdest, tuser); + end; + + procedure pop_axi_stream( + signal net : inout network_t; + axi_stream : axi_stream_slave_t; + variable tdata : out std_logic_vector; + variable tlast : out std_logic + ) is + variable reference : axi_stream_reference_t; + begin + pop_axi_stream(net, axi_stream, reference); + await_pop_axi_stream_reply(net, reference, tdata, tlast); + end; + + procedure check_axi_stream( + signal net : inout network_t; + axi_stream : axi_stream_slave_t; + expected : std_logic_vector; + tlast : std_logic := '1'; + tkeep : std_logic_vector := ""; + tstrb : std_logic_vector := ""; + tid : std_logic_vector := ""; + tdest : std_logic_vector := ""; + tuser : std_logic_vector := ""; + msg : string := ""; + blocking : boolean := true + ) is + variable got_tdata : std_logic_vector(data_length(axi_stream)-1 downto 0); + variable got_tlast : std_logic; + variable got_tkeep : std_logic_vector(data_length(axi_stream)/8-1 downto 0); + variable got_tstrb : std_logic_vector(data_length(axi_stream)/8-1 downto 0); + variable got_tid : std_logic_vector(id_length(axi_stream)-1 downto 0); + variable got_tdest : std_logic_vector(dest_length(axi_stream)-1 downto 0); + variable got_tuser : std_logic_vector(user_length(axi_stream)-1 downto 0); + variable check_msg : msg_t := new_msg(check_axi_stream_msg); + variable normalized_data : std_logic_vector(data_length(axi_stream)-1 downto 0) := (others => '0'); + variable normalized_keep : std_logic_vector(data_length(axi_stream)/8-1 downto 0) := (others => '0'); + variable normalized_strb : std_logic_vector(data_length(axi_stream)/8-1 downto 0) := (others => '0'); + variable normalized_id : std_logic_vector(id_length(axi_stream)-1 downto 0) := (others => '0'); + variable normalized_dest : std_logic_vector(dest_length(axi_stream)-1 downto 0) := (others => '0'); + variable normalized_user : std_logic_vector(user_length(axi_stream)-1 downto 0) := (others => '0'); + begin + if blocking then + pop_axi_stream(net, axi_stream, got_tdata, got_tlast, got_tkeep, got_tstrb, got_tid, got_tdest, got_tuser); + check_equal(got_tdata, expected, "TDATA mismatch, " & msg); + check_equal(got_tlast, tlast, "TLAST mismatch, " & msg); + if tkeep'length > 0 then + check_equal(got_tkeep, tkeep, "TKEEP mismatch, " & msg); + end if; + if tstrb'length > 0 then + check_equal(got_tstrb, tstrb, "TSTRB mismatch, " & msg); + end if; + if tid'length > 0 then + check_equal(got_tid, tid, "TID mismatch, " & msg); + end if; + if tdest'length > 0 then + check_equal(got_tdest, tdest, "TDEST mismatch, " & msg); + end if; + if tuser'length > 0 then + check_equal(got_tuser, tuser, "TUSER mismatch, " & msg); + end if; + else + push_string(check_msg, msg); + normalized_data(expected'length-1 downto 0) := expected; + push_std_ulogic_vector(check_msg, normalized_data); + push_std_ulogic(check_msg, tlast); + normalized_keep(tkeep'length-1 downto 0) := tkeep; + push_std_ulogic_vector(check_msg, normalized_keep); + normalized_strb(tstrb'length-1 downto 0) := tstrb; + push_std_ulogic_vector(check_msg, normalized_strb); + normalized_id(tid'length-1 downto 0) := tid; + push_std_ulogic_vector(check_msg, normalized_id); + normalized_dest(tdest'length-1 downto 0) := tdest; + push_std_ulogic_vector(check_msg, normalized_dest); + normalized_user(tuser'length-1 downto 0) := tuser; + push_std_ulogic_vector(check_msg, normalized_user); + send(net, axi_stream.p_actor, check_msg); + end if; + end procedure; + + procedure push_axi_stream_transaction(msg : msg_t; axi_stream_transaction : axi_stream_transaction_t) is + begin + push_std_ulogic_vector(msg, axi_stream_transaction.tdata); + push_boolean(msg, axi_stream_transaction.tlast); + push_std_ulogic_vector(msg, axi_stream_transaction.tkeep); + push_std_ulogic_vector(msg, axi_stream_transaction.tstrb); + push_std_ulogic_vector(msg, axi_stream_transaction.tid); + push_std_ulogic_vector(msg, axi_stream_transaction.tdest); + push_std_ulogic_vector(msg, axi_stream_transaction.tuser); + end; + + procedure pop_axi_stream_transaction( + constant msg : in msg_t; + variable axi_stream_transaction : out axi_stream_transaction_t + ) is + begin + axi_stream_transaction.tdata := pop_std_ulogic_vector(msg); + axi_stream_transaction.tlast := pop_boolean(msg); + axi_stream_transaction.tkeep := pop_std_ulogic_vector(msg); + axi_stream_transaction.tstrb := pop_std_ulogic_vector(msg); + axi_stream_transaction.tid := pop_std_ulogic_vector(msg); + axi_stream_transaction.tdest := pop_std_ulogic_vector(msg); + axi_stream_transaction.tuser := pop_std_ulogic_vector(msg); + end; + + impure function new_axi_stream_transaction_msg( + axi_stream_transaction : axi_stream_transaction_t + ) return msg_t is + variable msg : msg_t; + begin + msg := new_msg(axi_stream_transaction_msg); + push_axi_stream_transaction(msg, axi_stream_transaction); + return msg; + end; + + procedure handle_axi_stream_transaction( + variable msg_type : inout msg_type_t; + variable msg : inout msg_t; + variable axi_transaction : out axi_stream_transaction_t) is + begin + if msg_type = axi_stream_transaction_msg then + handle_message(msg_type); + + pop_axi_stream_transaction(msg, axi_transaction); + end if; + end; + +end package body; diff --git a/vunit/vhdl/verification_components/src/axi_stream_protocol_checker.vhd b/vunit/vhdl/verification_components/src/axi_stream_protocol_checker.vhd index 49e1579e5..7b237cf43 100644 --- a/vunit/vhdl/verification_components/src/axi_stream_protocol_checker.vhd +++ b/vunit/vhdl/verification_components/src/axi_stream_protocol_checker.vhd @@ -1,263 +1,263 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std_unsigned.all; - -use std.textio.all; - -context work.vunit_context; -context work.com_context; -use work.axi_stream_pkg.all; - -entity axi_stream_protocol_checker is - generic ( - protocol_checker : axi_stream_protocol_checker_t); - port ( - aclk : in std_logic; - areset_n : in std_logic := '1'; - tvalid : in std_logic; - tready : in std_logic := '1'; - tdata : in std_logic_vector(data_length(protocol_checker) - 1 downto 0); - tlast : in std_logic := '1'; - tkeep : in std_logic_vector(data_length(protocol_checker)/8-1 downto 0) := (others => '0'); - tstrb : in std_logic_vector(data_length(protocol_checker)/8-1 downto 0) := (others => '0'); - tid : in std_logic_vector(id_length(protocol_checker)-1 downto 0) := (others => '0'); - tdest : in std_logic_vector(dest_length(protocol_checker)-1 downto 0) := (others => '0'); - tuser : in std_logic_vector(user_length(protocol_checker)-1 downto 0) := (others => '0') - ); -end entity; - -architecture a of axi_stream_protocol_checker is - constant rule1_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 1"); - constant rule2_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 2"); - constant rule3_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 3"); - constant rule4_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 4"); - constant rule5_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 5"); - constant rule6_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 6"); - constant rule7_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 7"); - constant rule8_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 8"); - constant rule9_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 9"); - constant rule10_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 10"); - constant rule11_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 11"); - constant rule12_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 12"); - constant rule13_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 13"); - constant rule14_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 14"); - constant rule15_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 15"); - constant rule16_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 16"); - constant rule17_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 17"); - constant rule18_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 18"); - constant rule19_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 19"); - constant rule20_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 20"); - constant rule21_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 21"); - constant rule22_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 22"); - - signal handshake_is_not_x : std_logic; - signal enable_rule1_check : std_logic; - signal enable_rule2_check : std_logic; - signal enable_rule11_check : std_logic; - signal enable_rule12_check : std_logic; - signal enable_rule13_check : std_logic; - signal enable_rule14_check : std_logic; - signal enable_rule15_check : std_logic; - signal rule20_check_value : std_logic; - - signal areset_n_d : std_logic := '0'; - signal areset_rose : std_logic; - signal not_tvalid : std_logic; -begin - handshake_is_not_x <= '1' when not is_x(tvalid) and not is_x(tready) else '0'; - - -- AXI4STREAM_ERRM_TDATA_STABLE TDATA remains stable when TVALID is asserted, - -- and TREADY is LOW - enable_rule1_check <= '1' when (handshake_is_not_x = '1') and not is_x(tdata) else '0'; - check_stable( - rule1_checker, aclk, enable_rule1_check, tvalid, tready, tdata, - result("for tdata while waiting for tready")); - - -- AXI4STREAM_ERRM_TLAST_STABLE TLAST remains stable when TVALID is asserted, - -- and TREADY is LOW - enable_rule2_check <= '1' when (handshake_is_not_x = '1') and not is_x(tlast) else '0'; - check_stable( - rule2_checker, aclk, enable_rule2_check, tvalid, tready, tlast, - result("for tlast while waiting for tready")); - - -- AXI4STREAM_ERRM_TVALID_STABLE When TVALID is asserted, then it must remain - -- asserted until TREADY is HIGH - check_stable( - rule3_checker, aclk, handshake_is_not_x, tvalid, tready, tvalid, - result("for tvalid while waiting for tready")); - - -- AXI4STREAM_RECS_TREADY_MAX_WAIT Recommended that TREADY is asserted within - -- MAXWAITS cycles of TVALID being asserted - process - variable n_clock_cycles : natural; - begin - wait until rising_edge(aclk) and (to_x01(tvalid) = '1'); - while not tready loop - wait until rising_edge(aclk); - n_clock_cycles := n_clock_cycles + 1; - end loop; - check(rule4_checker, - n_clock_cycles <= protocol_checker.p_max_waits, - result("for performance - tready active " & to_string(n_clock_cycles) & - " clock cycles after tvalid. Expected <= " & to_string(protocol_checker.p_max_waits) & " clock cycles."), - level => warning); - end process; - - -- AXI4STREAM_ERRM_TDATA_X A value of X on TDATA is not permitted when TVALID - -- is HIGH - check_not_unknown(rule5_checker, aclk, tvalid, tdata, result("for tdata when tvalid is high")); - - -- AXI4STREAM_ERRM_TLAST_X A value of X on TLAST is not permitted when TVALID - -- is HIGH - check_not_unknown(rule6_checker, aclk, tvalid, tlast, result("for tlast when tvalid is high")); - - -- AXI4STREAM_ERRM_TVALID_X A value of X on TVALID is not permitted when not - -- in reset - check_not_unknown(rule7_checker, aclk, areset_n, tvalid, result("for tvalid when not in reset")); - - -- AXI4STREAM_ERRS_TREADY_X A value of X on TREADY is not permitted when not - -- in reset - check_not_unknown(rule8_checker, aclk, areset_n, tready, result("for tready when not in reset")); - - -- AXI4STREAM_ERRM_STREAM_ALL_DONE_EOS At the end of simulation, all streams have had - -- their corresponding TLAST transfer - check_complete_packets : block is - constant active_streams : integer_array_t := new_1d(length => 2 ** tid'length); - begin - assert tid'length <= 8 report "tid must not be more than 8 bits (maximum recommendation)" severity failure; - - track_streams : process - variable value : natural; - begin - wait until rising_edge(aclk) and (to_x01(tvalid) = '1'); - if tid'length = 0 then - value := 1 when to_x01(tlast) = '0' else 0; - set(active_streams, 0, value); - elsif not is_x(tid) then - value := 1 when to_x01(tlast) = '0' else 0; - set(active_streams, to_integer(tid), value); - end if; - end process; - - check_that_streams_have_ended : process - variable incomplete_streams : line; - begin - lock_entry(runner, test_runner_cleanup); - wait_until(runner, test_runner_cleanup); - - if tid'length = 0 then - check(rule9_checker, get(active_streams, 0) = 0, result("for packet completion.")); - else - for i in 0 to 2 * tid'length - 1 loop - if get(active_streams, i) /= 0 then - if incomplete_streams = null then - write(incomplete_streams, to_string(i)); - else - write(incomplete_streams, ", " & to_string(i)); - end if; - end if; - end loop; - - if incomplete_streams /= null then - check_failed(rule9_checker, result("for packet completion for the following streams: " & - incomplete_streams.all & ".")); - else - check_passed(rule9_checker, result("for packet completion.")); - end if; - end if; - - unlock_entry(runner, test_runner_cleanup); - wait; - end process; - end block; - - -- AXI4STREAM_ERRM_TUSER_X A value of X on TUSER is not permitted when not in reset - -- is HIGH - check_not_unknown(rule10_checker, aclk, areset_n, tuser, result("for tuser when areset_n is high")); - - -- AXI4STREAM_ERRM_TUSER_STABLE TUSER payload signals must remain constant while TVALID is asserted, - -- and TREADY is de-asserted - enable_rule11_check <= '1' when (handshake_is_not_x = '1') and not is_x(tuser) else '0'; - check_stable( - rule11_checker, aclk, enable_rule11_check, tvalid, tready, tuser, - result("for tuser while waiting for tready")); - - -- AXI4STREAM_ERRM_TID_STABLE TID remains stable when TVALID is asserted, - -- and TREADY is LOW - enable_rule12_check <= '1' when (handshake_is_not_x = '1') and not is_x(tid) else '0'; - check_stable( - rule12_checker, aclk, enable_rule12_check, tvalid, tready, tid, - result("for tid while waiting for tready")); - - -- AXI4STREAM_ERRM_TDEST_STABLE TDEST remains stable when TVALID is asserted, - -- and TREADY is LOW - enable_rule13_check <= '1' when (handshake_is_not_x = '1') and not is_x(tdest) else '0'; - check_stable( - rule13_checker, aclk, enable_rule13_check, tvalid, tready, tdest, - result("for tdest while waiting for tready")); - - -- AXI4STREAM_ERRM_TSTRB_STABLE TSTRB remains stable when TVALID is asserted, - -- and TREADY is LOW - enable_rule14_check <= '1' when (handshake_is_not_x = '1') and not is_x(tstrb) else '0'; - check_stable( - rule14_checker, aclk, enable_rule14_check, tvalid, tready, tstrb, - result("for tstrb while waiting for tready")); - - -- AXI4STREAM_ERRM_TKEEP_STABLE TKEEP remains stable when TVALID is asserted, - -- and TREADY is LOW - enable_rule15_check <= '1' when (handshake_is_not_x = '1') and not is_x(tkeep) else '0'; - check_stable( - rule15_checker, aclk, enable_rule15_check, tvalid, tready, tkeep, - result("for tkeep while waiting for tready")); - - -- AXI4STREAM_ERRM_TID_X A value of X on TID is not permitted when TVALID - -- is HIGH - check_not_unknown(rule16_checker, aclk, tvalid, tid, result("for tid when tvalid is high")); - - -- AXI4STREAM_ERRM_TDEST_X A value of X on TDEST is not permitted when TVALID - -- is HIGH - check_not_unknown(rule17_checker, aclk, tvalid, tdest, result("for tdest when tvalid is high")); - - -- AXI4STREAM_ERRM_TSTRB_X A value of X on TSTRB is not permitted when TVALID - -- is HIGH - check_not_unknown(rule18_checker, aclk, tvalid, tstrb, result("for tstrb when tvalid is high")); - - -- AXI4STREAM_ERRM_TKEEP_X A value of X on TKEEP is not permitted when TVALID - -- is HIGH - check_not_unknown(rule19_checker, aclk, tvalid, tkeep, result("for tkeep when tvalid is high")); - - -- AXI4STREAM_ERRM_TKEEP_TSTRB If TKEEP is de-asserted, then TSTRB must also be de-asserted - -- eschmidscs: Binding this to tvalid. ARM does not include that, but makes more sense this way? - rule20_check_value <= not(or(((not tkeep) and tstrb))); - check_true(rule20_checker, aclk, tvalid, rule20_check_value, result("for tstrb de-asserted when tkeep de-asserted")); - - -- AXI4STREAM_AUXM_TID_TDTEST_WIDTH The value of ID_WIDTH + DEST_WIDTH must not exceed 24 - -- eschmidscs: Must wait a short while to allow testing of the rule. - process - begin - wait for 1 ps; - check_true(rule21_checker, tid'length + tdest'length <= 24, result("for tid width and tdest width together must be less than 25")); - wait; - end process; - - -- AXI4STREAM_ERRM_TVALID_RESET TVALID is LOW for the first cycle after ARESETn goes HIGH - process (aclk) is - begin - if rising_edge(aclk) then - areset_n_d <= areset_n; - end if; - end process; - areset_rose <= areset_n and not areset_n_d; - not_tvalid <= not tvalid; - check_implication(rule22_checker, aclk, areset_n, areset_rose, not_tvalid, result("for tvalid de-asserted after reset release")); - - -- for * being DATA, KEEP, STRB, ID, DEST or USER - -- AXI4STREAM_ERRM_T*_TIEOFF T* must be stable while *_WIDTH has been set to zero - -- cannot be checked, vector has negative range -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std_unsigned.all; + +use std.textio.all; + +context work.vunit_context; +context work.com_context; +use work.axi_stream_pkg.all; + +entity axi_stream_protocol_checker is + generic ( + protocol_checker : axi_stream_protocol_checker_t); + port ( + aclk : in std_logic; + areset_n : in std_logic := '1'; + tvalid : in std_logic; + tready : in std_logic := '1'; + tdata : in std_logic_vector(data_length(protocol_checker) - 1 downto 0); + tlast : in std_logic := '1'; + tkeep : in std_logic_vector(data_length(protocol_checker)/8-1 downto 0) := (others => '0'); + tstrb : in std_logic_vector(data_length(protocol_checker)/8-1 downto 0) := (others => '0'); + tid : in std_logic_vector(id_length(protocol_checker)-1 downto 0) := (others => '0'); + tdest : in std_logic_vector(dest_length(protocol_checker)-1 downto 0) := (others => '0'); + tuser : in std_logic_vector(user_length(protocol_checker)-1 downto 0) := (others => '0') + ); +end entity; + +architecture a of axi_stream_protocol_checker is + constant rule1_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 1"); + constant rule2_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 2"); + constant rule3_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 3"); + constant rule4_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 4"); + constant rule5_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 5"); + constant rule6_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 6"); + constant rule7_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 7"); + constant rule8_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 8"); + constant rule9_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 9"); + constant rule10_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 10"); + constant rule11_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 11"); + constant rule12_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 12"); + constant rule13_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 13"); + constant rule14_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 14"); + constant rule15_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 15"); + constant rule16_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 16"); + constant rule17_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 17"); + constant rule18_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 18"); + constant rule19_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 19"); + constant rule20_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 20"); + constant rule21_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 21"); + constant rule22_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 22"); + + signal handshake_is_not_x : std_logic; + signal enable_rule1_check : std_logic; + signal enable_rule2_check : std_logic; + signal enable_rule11_check : std_logic; + signal enable_rule12_check : std_logic; + signal enable_rule13_check : std_logic; + signal enable_rule14_check : std_logic; + signal enable_rule15_check : std_logic; + signal rule20_check_value : std_logic; + + signal areset_n_d : std_logic := '0'; + signal areset_rose : std_logic; + signal not_tvalid : std_logic; +begin + handshake_is_not_x <= '1' when not is_x(tvalid) and not is_x(tready) else '0'; + + -- AXI4STREAM_ERRM_TDATA_STABLE TDATA remains stable when TVALID is asserted, + -- and TREADY is LOW + enable_rule1_check <= '1' when (handshake_is_not_x = '1') and not is_x(tdata) else '0'; + check_stable( + rule1_checker, aclk, enable_rule1_check, tvalid, tready, tdata, + result("for tdata while waiting for tready")); + + -- AXI4STREAM_ERRM_TLAST_STABLE TLAST remains stable when TVALID is asserted, + -- and TREADY is LOW + enable_rule2_check <= '1' when (handshake_is_not_x = '1') and not is_x(tlast) else '0'; + check_stable( + rule2_checker, aclk, enable_rule2_check, tvalid, tready, tlast, + result("for tlast while waiting for tready")); + + -- AXI4STREAM_ERRM_TVALID_STABLE When TVALID is asserted, then it must remain + -- asserted until TREADY is HIGH + check_stable( + rule3_checker, aclk, handshake_is_not_x, tvalid, tready, tvalid, + result("for tvalid while waiting for tready")); + + -- AXI4STREAM_RECS_TREADY_MAX_WAIT Recommended that TREADY is asserted within + -- MAXWAITS cycles of TVALID being asserted + process + variable n_clock_cycles : natural; + begin + wait until rising_edge(aclk) and (to_x01(tvalid) = '1'); + while not tready loop + wait until rising_edge(aclk); + n_clock_cycles := n_clock_cycles + 1; + end loop; + check(rule4_checker, + n_clock_cycles <= protocol_checker.p_max_waits, + result("for performance - tready active " & to_string(n_clock_cycles) & + " clock cycles after tvalid. Expected <= " & to_string(protocol_checker.p_max_waits) & " clock cycles."), + level => warning); + end process; + + -- AXI4STREAM_ERRM_TDATA_X A value of X on TDATA is not permitted when TVALID + -- is HIGH + check_not_unknown(rule5_checker, aclk, tvalid, tdata, result("for tdata when tvalid is high")); + + -- AXI4STREAM_ERRM_TLAST_X A value of X on TLAST is not permitted when TVALID + -- is HIGH + check_not_unknown(rule6_checker, aclk, tvalid, tlast, result("for tlast when tvalid is high")); + + -- AXI4STREAM_ERRM_TVALID_X A value of X on TVALID is not permitted when not + -- in reset + check_not_unknown(rule7_checker, aclk, areset_n, tvalid, result("for tvalid when not in reset")); + + -- AXI4STREAM_ERRS_TREADY_X A value of X on TREADY is not permitted when not + -- in reset + check_not_unknown(rule8_checker, aclk, areset_n, tready, result("for tready when not in reset")); + + -- AXI4STREAM_ERRM_STREAM_ALL_DONE_EOS At the end of simulation, all streams have had + -- their corresponding TLAST transfer + check_complete_packets : block is + constant active_streams : integer_array_t := new_1d(length => 2 ** tid'length); + begin + assert tid'length <= 8 report "tid must not be more than 8 bits (maximum recommendation)" severity failure; + + track_streams : process + variable value : natural; + begin + wait until rising_edge(aclk) and (to_x01(tvalid) = '1'); + if tid'length = 0 then + value := 1 when to_x01(tlast) = '0' else 0; + set(active_streams, 0, value); + elsif not is_x(tid) then + value := 1 when to_x01(tlast) = '0' else 0; + set(active_streams, to_integer(tid), value); + end if; + end process; + + check_that_streams_have_ended : process + variable incomplete_streams : line; + begin + lock_entry(runner, test_runner_cleanup); + wait_until(runner, test_runner_cleanup); + + if tid'length = 0 then + check(rule9_checker, get(active_streams, 0) = 0, result("for packet completion.")); + else + for i in 0 to 2 * tid'length - 1 loop + if get(active_streams, i) /= 0 then + if incomplete_streams = null then + write(incomplete_streams, to_string(i)); + else + write(incomplete_streams, ", " & to_string(i)); + end if; + end if; + end loop; + + if incomplete_streams /= null then + check_failed(rule9_checker, result("for packet completion for the following streams: " & + incomplete_streams.all & ".")); + else + check_passed(rule9_checker, result("for packet completion.")); + end if; + end if; + + unlock_entry(runner, test_runner_cleanup); + wait; + end process; + end block; + + -- AXI4STREAM_ERRM_TUSER_X A value of X on TUSER is not permitted when not in reset + -- is HIGH + check_not_unknown(rule10_checker, aclk, areset_n, tuser, result("for tuser when areset_n is high")); + + -- AXI4STREAM_ERRM_TUSER_STABLE TUSER payload signals must remain constant while TVALID is asserted, + -- and TREADY is de-asserted + enable_rule11_check <= '1' when (handshake_is_not_x = '1') and not is_x(tuser) else '0'; + check_stable( + rule11_checker, aclk, enable_rule11_check, tvalid, tready, tuser, + result("for tuser while waiting for tready")); + + -- AXI4STREAM_ERRM_TID_STABLE TID remains stable when TVALID is asserted, + -- and TREADY is LOW + enable_rule12_check <= '1' when (handshake_is_not_x = '1') and not is_x(tid) else '0'; + check_stable( + rule12_checker, aclk, enable_rule12_check, tvalid, tready, tid, + result("for tid while waiting for tready")); + + -- AXI4STREAM_ERRM_TDEST_STABLE TDEST remains stable when TVALID is asserted, + -- and TREADY is LOW + enable_rule13_check <= '1' when (handshake_is_not_x = '1') and not is_x(tdest) else '0'; + check_stable( + rule13_checker, aclk, enable_rule13_check, tvalid, tready, tdest, + result("for tdest while waiting for tready")); + + -- AXI4STREAM_ERRM_TSTRB_STABLE TSTRB remains stable when TVALID is asserted, + -- and TREADY is LOW + enable_rule14_check <= '1' when (handshake_is_not_x = '1') and not is_x(tstrb) else '0'; + check_stable( + rule14_checker, aclk, enable_rule14_check, tvalid, tready, tstrb, + result("for tstrb while waiting for tready")); + + -- AXI4STREAM_ERRM_TKEEP_STABLE TKEEP remains stable when TVALID is asserted, + -- and TREADY is LOW + enable_rule15_check <= '1' when (handshake_is_not_x = '1') and not is_x(tkeep) else '0'; + check_stable( + rule15_checker, aclk, enable_rule15_check, tvalid, tready, tkeep, + result("for tkeep while waiting for tready")); + + -- AXI4STREAM_ERRM_TID_X A value of X on TID is not permitted when TVALID + -- is HIGH + check_not_unknown(rule16_checker, aclk, tvalid, tid, result("for tid when tvalid is high")); + + -- AXI4STREAM_ERRM_TDEST_X A value of X on TDEST is not permitted when TVALID + -- is HIGH + check_not_unknown(rule17_checker, aclk, tvalid, tdest, result("for tdest when tvalid is high")); + + -- AXI4STREAM_ERRM_TSTRB_X A value of X on TSTRB is not permitted when TVALID + -- is HIGH + check_not_unknown(rule18_checker, aclk, tvalid, tstrb, result("for tstrb when tvalid is high")); + + -- AXI4STREAM_ERRM_TKEEP_X A value of X on TKEEP is not permitted when TVALID + -- is HIGH + check_not_unknown(rule19_checker, aclk, tvalid, tkeep, result("for tkeep when tvalid is high")); + + -- AXI4STREAM_ERRM_TKEEP_TSTRB If TKEEP is de-asserted, then TSTRB must also be de-asserted + -- eschmidscs: Binding this to tvalid. ARM does not include that, but makes more sense this way? + rule20_check_value <= not(or(((not tkeep) and tstrb))); + check_true(rule20_checker, aclk, tvalid, rule20_check_value, result("for tstrb de-asserted when tkeep de-asserted")); + + -- AXI4STREAM_AUXM_TID_TDTEST_WIDTH The value of ID_WIDTH + DEST_WIDTH must not exceed 24 + -- eschmidscs: Must wait a short while to allow testing of the rule. + process + begin + wait for 1 ps; + check_true(rule21_checker, tid'length + tdest'length <= 24, result("for tid width and tdest width together must be less than 25")); + wait; + end process; + + -- AXI4STREAM_ERRM_TVALID_RESET TVALID is LOW for the first cycle after ARESETn goes HIGH + process (aclk) is + begin + if rising_edge(aclk) then + areset_n_d <= areset_n; + end if; + end process; + areset_rose <= areset_n and not areset_n_d; + not_tvalid <= not tvalid; + check_implication(rule22_checker, aclk, areset_n, areset_rose, not_tvalid, result("for tvalid de-asserted after reset release")); + + -- for * being DATA, KEEP, STRB, ID, DEST or USER + -- AXI4STREAM_ERRM_T*_TIEOFF T* must be stable while *_WIDTH has been set to zero + -- cannot be checked, vector has negative range +end architecture; diff --git a/vunit/vhdl/verification_components/src/axi_stream_slave.vhd b/vunit/vhdl/verification_components/src/axi_stream_slave.vhd index bc36796b7..69a33a7ff 100644 --- a/vunit/vhdl/verification_components/src/axi_stream_slave.vhd +++ b/vunit/vhdl/verification_components/src/axi_stream_slave.vhd @@ -1,181 +1,181 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -context work.vunit_context; -context work.com_context; -use work.stream_slave_pkg.all; -use work.axi_stream_pkg.all; -use work.sync_pkg.all; -use work.string_ptr_pkg.all; - -entity axi_stream_slave is - generic ( - slave : axi_stream_slave_t); - port ( - aclk : in std_logic; - areset_n : in std_logic := '1'; - tvalid : in std_logic; - tready : out std_logic := '0'; - tdata : in std_logic_vector(data_length(slave)-1 downto 0); - tlast : in std_logic := '1'; - tkeep : in std_logic_vector(data_length(slave)/8-1 downto 0) := (others => '0'); - tstrb : in std_logic_vector(data_length(slave)/8-1 downto 0) := (others => '0'); - tid : in std_logic_vector(id_length(slave)-1 downto 0) := (others => '0'); - tdest : in std_logic_vector(dest_length(slave)-1 downto 0) := (others => '0'); - tuser : in std_logic_vector(user_length(slave)-1 downto 0) := (others => '0') - ); -end entity; - -architecture a of axi_stream_slave is - - constant notify_request_msg : msg_type_t := new_msg_type("notify request"); - constant message_queue : queue_t := new_queue; - signal notify_bus_process_done : std_logic := '0'; - -begin - - main : process - variable request_msg : msg_t; - variable notify_msg : msg_t; - variable msg_type : msg_type_t; - begin - receive(net, slave.p_actor, request_msg); - msg_type := message_type(request_msg); - - if msg_type = stream_pop_msg or msg_type = pop_axi_stream_msg then - push(message_queue, request_msg); - elsif msg_type = check_axi_stream_msg then - push(message_queue, request_msg); - elsif msg_type = wait_for_time_msg then - push(message_queue, request_msg); - elsif msg_type = wait_until_idle_msg then - notify_msg := new_msg(notify_request_msg); - push(message_queue, notify_msg); - wait on notify_bus_process_done until is_empty(message_queue); - handle_wait_until_idle(net, msg_type, request_msg); - else - unexpected_msg_type(msg_type); - end if; - end process; - - bus_process : process - variable reply_msg, msg : msg_t; - variable msg_type : msg_type_t; - variable report_msg : string_ptr_t; - variable axi_stream_transaction : axi_stream_transaction_t( - tdata(tdata'range), - tkeep(tkeep'range), - tstrb(tstrb'range), - tid(tid'range), - tdest(tdest'range), - tuser(tuser'range) - ); - begin - -- Wait for messages to arrive on the queue, posted by the process above - wait until rising_edge(aclk) and (not is_empty(message_queue)); - - while not is_empty(message_queue) loop - msg := pop(message_queue); - msg_type := message_type(msg); - - if msg_type = wait_for_time_msg then - handle_sync_message(net, msg_type, msg); - wait until rising_edge(aclk); - elsif msg_type = notify_request_msg then - -- Ignore this message, but expect it - elsif msg_type = stream_pop_msg or msg_type = pop_axi_stream_msg then - tready <= '1'; - wait until (tvalid and tready) = '1' and rising_edge(aclk); - tready <= '0'; - - axi_stream_transaction := ( - tdata => tdata, - tlast => tlast = '1', - tkeep => tkeep, - tstrb => tstrb, - tid => tid, - tdest => tdest, - tuser => tuser - ); - - reply_msg := new_axi_stream_transaction_msg(axi_stream_transaction); - reply(net, msg, reply_msg); - elsif msg_type = check_axi_stream_msg then - tready <= '1'; - wait until (tvalid and tready) = '1' and rising_edge(aclk); - tready <= '0'; - - report_msg := new_string_ptr(pop_string(msg)); - check_equal(tdata, pop_std_ulogic_vector(msg), "TDATA mismatch, " & to_string(report_msg)); - check_equal(tlast, pop_std_ulogic(msg), "TLAST mismatch, " & to_string(report_msg)); - if tkeep'length > 0 then - check_equal(tkeep, pop_std_ulogic_vector(msg), "TKEEP mismatch, " & to_string(report_msg)); - end if; - if tstrb'length > 0 then - check_equal(tstrb, pop_std_ulogic_vector(msg), "TSTRB mismatch, " & to_string(report_msg)); - end if; - if tid'length > 0 then - check_equal(tid, pop_std_ulogic_vector(msg), "TID mismatch, " & to_string(report_msg)); - end if; - if tdest'length > 0 then - check_equal(tdest, pop_std_ulogic_vector(msg), "TDEST mismatch, " & to_string(report_msg)); - end if; - if tuser'length > 0 then - check_equal(tuser, pop_std_ulogic_vector(msg), "TUSER mismatch, " & to_string(report_msg)); - end if; - else - unexpected_msg_type(msg_type); - end if; - end loop; - - notify_bus_process_done <= '1'; - wait until notify_bus_process_done = '1'; - notify_bus_process_done <= '0'; - - end process; - - axi_stream_monitor_generate : if slave.p_monitor /= null_axi_stream_monitor generate - axi_stream_monitor_inst : entity work.axi_stream_monitor - generic map( - monitor => slave.p_monitor - ) - port map( - aclk => aclk, - tvalid => tvalid, - tready => tready, - tdata => tdata, - tlast => tlast, - tkeep => tkeep, - tstrb => tstrb, - tid => tid, - tdest => tdest, - tuser => tuser - ); - end generate axi_stream_monitor_generate; - - axi_stream_protocol_checker_generate : if slave.p_protocol_checker /= null_axi_stream_protocol_checker generate - axi_stream_protocol_checker_inst: entity work.axi_stream_protocol_checker - generic map ( - protocol_checker => slave.p_protocol_checker) - port map ( - aclk => aclk, - areset_n => areset_n, - tvalid => tvalid, - tready => tready, - tdata => tdata, - tlast => tlast, - tkeep => tkeep, - tstrb => tstrb, - tid => tid, - tdest => tdest, - tuser => tuser - ); - end generate axi_stream_protocol_checker_generate; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +context work.vunit_context; +context work.com_context; +use work.stream_slave_pkg.all; +use work.axi_stream_pkg.all; +use work.sync_pkg.all; +use work.string_ptr_pkg.all; + +entity axi_stream_slave is + generic ( + slave : axi_stream_slave_t); + port ( + aclk : in std_logic; + areset_n : in std_logic := '1'; + tvalid : in std_logic; + tready : out std_logic := '0'; + tdata : in std_logic_vector(data_length(slave)-1 downto 0); + tlast : in std_logic := '1'; + tkeep : in std_logic_vector(data_length(slave)/8-1 downto 0) := (others => '0'); + tstrb : in std_logic_vector(data_length(slave)/8-1 downto 0) := (others => '0'); + tid : in std_logic_vector(id_length(slave)-1 downto 0) := (others => '0'); + tdest : in std_logic_vector(dest_length(slave)-1 downto 0) := (others => '0'); + tuser : in std_logic_vector(user_length(slave)-1 downto 0) := (others => '0') + ); +end entity; + +architecture a of axi_stream_slave is + + constant notify_request_msg : msg_type_t := new_msg_type("notify request"); + constant message_queue : queue_t := new_queue; + signal notify_bus_process_done : std_logic := '0'; + +begin + + main : process + variable request_msg : msg_t; + variable notify_msg : msg_t; + variable msg_type : msg_type_t; + begin + receive(net, slave.p_actor, request_msg); + msg_type := message_type(request_msg); + + if msg_type = stream_pop_msg or msg_type = pop_axi_stream_msg then + push(message_queue, request_msg); + elsif msg_type = check_axi_stream_msg then + push(message_queue, request_msg); + elsif msg_type = wait_for_time_msg then + push(message_queue, request_msg); + elsif msg_type = wait_until_idle_msg then + notify_msg := new_msg(notify_request_msg); + push(message_queue, notify_msg); + wait on notify_bus_process_done until is_empty(message_queue); + handle_wait_until_idle(net, msg_type, request_msg); + else + unexpected_msg_type(msg_type); + end if; + end process; + + bus_process : process + variable reply_msg, msg : msg_t; + variable msg_type : msg_type_t; + variable report_msg : string_ptr_t; + variable axi_stream_transaction : axi_stream_transaction_t( + tdata(tdata'range), + tkeep(tkeep'range), + tstrb(tstrb'range), + tid(tid'range), + tdest(tdest'range), + tuser(tuser'range) + ); + begin + -- Wait for messages to arrive on the queue, posted by the process above + wait until rising_edge(aclk) and (not is_empty(message_queue)); + + while not is_empty(message_queue) loop + msg := pop(message_queue); + msg_type := message_type(msg); + + if msg_type = wait_for_time_msg then + handle_sync_message(net, msg_type, msg); + wait until rising_edge(aclk); + elsif msg_type = notify_request_msg then + -- Ignore this message, but expect it + elsif msg_type = stream_pop_msg or msg_type = pop_axi_stream_msg then + tready <= '1'; + wait until (tvalid and tready) = '1' and rising_edge(aclk); + tready <= '0'; + + axi_stream_transaction := ( + tdata => tdata, + tlast => tlast = '1', + tkeep => tkeep, + tstrb => tstrb, + tid => tid, + tdest => tdest, + tuser => tuser + ); + + reply_msg := new_axi_stream_transaction_msg(axi_stream_transaction); + reply(net, msg, reply_msg); + elsif msg_type = check_axi_stream_msg then + tready <= '1'; + wait until (tvalid and tready) = '1' and rising_edge(aclk); + tready <= '0'; + + report_msg := new_string_ptr(pop_string(msg)); + check_equal(tdata, pop_std_ulogic_vector(msg), "TDATA mismatch, " & to_string(report_msg)); + check_equal(tlast, pop_std_ulogic(msg), "TLAST mismatch, " & to_string(report_msg)); + if tkeep'length > 0 then + check_equal(tkeep, pop_std_ulogic_vector(msg), "TKEEP mismatch, " & to_string(report_msg)); + end if; + if tstrb'length > 0 then + check_equal(tstrb, pop_std_ulogic_vector(msg), "TSTRB mismatch, " & to_string(report_msg)); + end if; + if tid'length > 0 then + check_equal(tid, pop_std_ulogic_vector(msg), "TID mismatch, " & to_string(report_msg)); + end if; + if tdest'length > 0 then + check_equal(tdest, pop_std_ulogic_vector(msg), "TDEST mismatch, " & to_string(report_msg)); + end if; + if tuser'length > 0 then + check_equal(tuser, pop_std_ulogic_vector(msg), "TUSER mismatch, " & to_string(report_msg)); + end if; + else + unexpected_msg_type(msg_type); + end if; + end loop; + + notify_bus_process_done <= '1'; + wait until notify_bus_process_done = '1'; + notify_bus_process_done <= '0'; + + end process; + + axi_stream_monitor_generate : if slave.p_monitor /= null_axi_stream_monitor generate + axi_stream_monitor_inst : entity work.axi_stream_monitor + generic map( + monitor => slave.p_monitor + ) + port map( + aclk => aclk, + tvalid => tvalid, + tready => tready, + tdata => tdata, + tlast => tlast, + tkeep => tkeep, + tstrb => tstrb, + tid => tid, + tdest => tdest, + tuser => tuser + ); + end generate axi_stream_monitor_generate; + + axi_stream_protocol_checker_generate : if slave.p_protocol_checker /= null_axi_stream_protocol_checker generate + axi_stream_protocol_checker_inst: entity work.axi_stream_protocol_checker + generic map ( + protocol_checker => slave.p_protocol_checker) + port map ( + aclk => aclk, + areset_n => areset_n, + tvalid => tvalid, + tready => tready, + tdata => tdata, + tlast => tlast, + tkeep => tkeep, + tstrb => tstrb, + tid => tid, + tdest => tdest, + tuser => tuser + ); + end generate axi_stream_protocol_checker_generate; + +end architecture; diff --git a/vunit/vhdl/verification_components/src/axi_write_slave.vhd b/vunit/vhdl/verification_components/src/axi_write_slave.vhd index 748e309be..94a44f8f3 100644 --- a/vunit/vhdl/verification_components/src/axi_write_slave.vhd +++ b/vunit/vhdl/verification_components/src/axi_write_slave.vhd @@ -1,264 +1,264 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -use work.axi_pkg.all; -use work.axi_slave_pkg.all; -use work.axi_slave_private_pkg.all; -use work.queue_pkg.all; -use work.memory_pkg.all; -use work.integer_vector_ptr_pkg.all; -use work.integer_vector_ptr_pool_pkg.all; -context work.com_context; - -entity axi_write_slave is - generic ( - axi_slave : axi_slave_t); - port ( - aclk : in std_logic; - - awvalid : in std_logic; - awready : out std_logic := '0'; - awid : in std_logic_vector; - awaddr : in std_logic_vector; - awlen : in std_logic_vector; - awsize : in std_logic_vector; - awburst : in axi_burst_type_t; - - wvalid : in std_logic; - wready : out std_logic := '0'; - wdata : in std_logic_vector; - wstrb : in std_logic_vector; - wlast : in std_logic; - - bvalid : out std_logic := '0'; - bready : in std_logic; - bid : out std_logic_vector; - bresp : out axi_resp_t - ); -end entity; - -architecture a of axi_write_slave is - shared variable self : axi_slave_private_t; - signal initialized : boolean := false; - - constant data_vector_length : natural := max_axi4_burst_length * wdata'length; - constant data_pool : integer_vector_ptr_pool_t := new_integer_vector_ptr_pool; - - type burst_data_t is record - length : natural; - address : integer_vector_ptr_t; - data : integer_vector_ptr_t; - end record; - - procedure push_burst_data(queue : queue_t; variable burst_data : inout burst_data_t) is - begin - push_integer(queue, burst_data.length); - push_integer_vector_ptr_ref(queue, burst_data.address); - push_integer_vector_ptr_ref(queue, burst_data.data); - end; - - impure function pop_burst_data(queue : queue_t) return burst_data_t is - variable burst_data : burst_data_t; - begin - burst_data.length := pop_integer(queue); - burst_data.address := pop_integer_vector_ptr_ref(queue); - burst_data.data := pop_integer_vector_ptr_ref(queue); - return burst_data; - end; - - impure function new_burst_data return burst_data_t is - begin - return (length => 0, - address => new_integer_vector_ptr(data_pool, min_length => data_vector_length), - data => new_integer_vector_ptr(data_pool, min_length => data_vector_length)); - end; - - procedure recycle(variable burst_data : inout burst_data_t) is - begin - recycle(data_pool, burst_data.address); - recycle(data_pool, burst_data.data); - end; - -begin - - control_process : process - begin - self.init(axi_slave, write_slave, 2**awid'length-1, wdata); - initialized <= true; - main_loop(self, net); - wait; - end process; - - axi_process : process - - procedure record_input_data(variable input_data : inout burst_data_t; - address : natural; byte : natural) is - variable ignored : boolean; - begin - if not check_address(axi_slave.p_memory, address, reading => false, check_permissions => true) then - return; - end if; - - set(input_data.address, input_data.length, address); - set(input_data.data, input_data.length, byte); - input_data.length := input_data.length + 1; - - ignored := check_write_data(axi_slave.p_memory, address, byte); - end; - - procedure write_data_to_memory(input_data_queue : queue_t) is - variable burst_data : burst_data_t; - begin - burst_data := pop_burst_data(input_data_queue); - for i in 0 to burst_data.length-1 loop - write_byte_unchecked(axi_slave.p_memory, get(burst_data.address, i), get(burst_data.data, i)); - end loop; - recycle(burst_data); - end; - - variable resp_burst, input_burst, burst : axi_burst_t; - variable address, aligned_address : integer; - variable beats : natural := 0; - variable input_data : burst_data_t; - constant input_data_queue : queue_t := new_queue; - - variable response_time : time; - variable has_response_time : boolean := false; - begin - -- Initialization - bid <= (bid'range => '0'); - bresp <= (bresp'range => '0'); - - assert awid'length = bid'length report "arwid vs wid data width mismatch"; - assert (awlen'length = 4 or - awlen'length = 8) report "awlen must be either 4 (AXI3) or 8 (AXI4)"; - - wait on initialized until initialized; - - loop - if bready = '1' then - bvalid <= '0'; - end if; - - if (awvalid and awready) = '1' then - input_burst := self.create_burst(awid, awaddr, awlen, awsize, awburst); - self.push_burst(input_burst); - end if; - - if (wvalid and wready) = '1' then - if (wlast = '1') /= (beats = 1) then - self.fail("Expected wlast='1' on last beat of burst " & describe_burst(burst) & - " with length " & to_string(burst.length) & - " starting at address " & to_string(burst.address)); - end if; - - aligned_address := address - (address mod self.data_size); - for j in 0 to self.data_size-1 loop - if wstrb(j) = '1' then - record_input_data(input_data, aligned_address+j, to_integer(unsigned(wdata(8*j+7 downto 8*j)))); - end if; - end loop; - - if burst.burst_type = axi_burst_type_incr then - address := address + burst.size; - end if; - - beats := beats - 1; - if beats = 0 then - self.push_random_response_time; - self.finish_burst(burst); - self.push_resp(burst); - push_burst_data(input_data_queue, input_data); - end if; - end if; - - if not (self.burst_queue_empty or beats > 0) then - input_data := new_burst_data; - burst := self.pop_burst; - address := burst.address; - beats := burst.length; - end if; - - if not self.resp_queue_empty and (bvalid = '0' or bready = '1') and not self.should_stall_write_response then - - if not has_response_time then - has_response_time := true; - response_time := self.pop_response_time; - end if; - - if has_response_time and response_time <= now then - has_response_time := false; - resp_burst := self.pop_resp; - write_data_to_memory(input_data_queue); - bvalid <= '1'; - bid <= std_logic_vector(to_unsigned(resp_burst.id, bid'length)); - bresp <= axi_resp_okay; - end if; - end if; - - if beats > 0 and not (beats = 1 and self.resp_queue_full) and not self.should_stall_data then - wready <= '1'; - else - wready <= '0'; - end if; - - if self.should_stall_address or self.burst_queue_full then - awready <= '0'; - else - awready <= '1'; - end if; - - wait until rising_edge(aclk); - end loop; - end process; - - well_behaved_check : process - variable size, len : natural; - variable num_beats : integer := 0; - variable num_beats_now : integer; - begin - wait on initialized until initialized; - loop - - num_beats_now := num_beats; - - if awvalid = '1' then - len := to_integer(unsigned(awlen)); - num_beats_now := num_beats + len + 1; - end if; - - -- Always keep track of num_beats such that the well behaved check can be enabled at any time - if (awvalid and awready) = '1' then - size := 2**to_integer(unsigned(awsize)); - num_beats := num_beats_now; - - if self.should_check_well_behaved and size /= self.data_size and len /= 0 then - self.fail("Burst not well behaved, axi size = " & to_string(size) & " but bus data width allows " & to_string(self.data_size)); - end if; - end if; - - if self.should_check_well_behaved and num_beats_now > 0 and wvalid /= '1' then - self.fail("Burst not well behaved, vwalid was not high during active burst"); - end if; - - if self.should_check_well_behaved and num_beats_now > 0 and bready /= '1' then - self.fail("Burst not well behaved, bready was not high during active burst"); - end if; - - if (wvalid and wready) = '1' then - num_beats := -1; - end if; - - wait until rising_edge(aclk); - end loop; - wait; - end process; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.axi_pkg.all; +use work.axi_slave_pkg.all; +use work.axi_slave_private_pkg.all; +use work.queue_pkg.all; +use work.memory_pkg.all; +use work.integer_vector_ptr_pkg.all; +use work.integer_vector_ptr_pool_pkg.all; +context work.com_context; + +entity axi_write_slave is + generic ( + axi_slave : axi_slave_t); + port ( + aclk : in std_logic; + + awvalid : in std_logic; + awready : out std_logic := '0'; + awid : in std_logic_vector; + awaddr : in std_logic_vector; + awlen : in std_logic_vector; + awsize : in std_logic_vector; + awburst : in axi_burst_type_t; + + wvalid : in std_logic; + wready : out std_logic := '0'; + wdata : in std_logic_vector; + wstrb : in std_logic_vector; + wlast : in std_logic; + + bvalid : out std_logic := '0'; + bready : in std_logic; + bid : out std_logic_vector; + bresp : out axi_resp_t + ); +end entity; + +architecture a of axi_write_slave is + shared variable self : axi_slave_private_t; + signal initialized : boolean := false; + + constant data_vector_length : natural := max_axi4_burst_length * wdata'length; + constant data_pool : integer_vector_ptr_pool_t := new_integer_vector_ptr_pool; + + type burst_data_t is record + length : natural; + address : integer_vector_ptr_t; + data : integer_vector_ptr_t; + end record; + + procedure push_burst_data(queue : queue_t; variable burst_data : inout burst_data_t) is + begin + push_integer(queue, burst_data.length); + push_integer_vector_ptr_ref(queue, burst_data.address); + push_integer_vector_ptr_ref(queue, burst_data.data); + end; + + impure function pop_burst_data(queue : queue_t) return burst_data_t is + variable burst_data : burst_data_t; + begin + burst_data.length := pop_integer(queue); + burst_data.address := pop_integer_vector_ptr_ref(queue); + burst_data.data := pop_integer_vector_ptr_ref(queue); + return burst_data; + end; + + impure function new_burst_data return burst_data_t is + begin + return (length => 0, + address => new_integer_vector_ptr(data_pool, min_length => data_vector_length), + data => new_integer_vector_ptr(data_pool, min_length => data_vector_length)); + end; + + procedure recycle(variable burst_data : inout burst_data_t) is + begin + recycle(data_pool, burst_data.address); + recycle(data_pool, burst_data.data); + end; + +begin + + control_process : process + begin + self.init(axi_slave, write_slave, 2**awid'length-1, wdata); + initialized <= true; + main_loop(self, net); + wait; + end process; + + axi_process : process + + procedure record_input_data(variable input_data : inout burst_data_t; + address : natural; byte : natural) is + variable ignored : boolean; + begin + if not check_address(axi_slave.p_memory, address, reading => false, check_permissions => true) then + return; + end if; + + set(input_data.address, input_data.length, address); + set(input_data.data, input_data.length, byte); + input_data.length := input_data.length + 1; + + ignored := check_write_data(axi_slave.p_memory, address, byte); + end; + + procedure write_data_to_memory(input_data_queue : queue_t) is + variable burst_data : burst_data_t; + begin + burst_data := pop_burst_data(input_data_queue); + for i in 0 to burst_data.length-1 loop + write_byte_unchecked(axi_slave.p_memory, get(burst_data.address, i), get(burst_data.data, i)); + end loop; + recycle(burst_data); + end; + + variable resp_burst, input_burst, burst : axi_burst_t; + variable address, aligned_address : integer; + variable beats : natural := 0; + variable input_data : burst_data_t; + constant input_data_queue : queue_t := new_queue; + + variable response_time : time; + variable has_response_time : boolean := false; + begin + -- Initialization + bid <= (bid'range => '0'); + bresp <= (bresp'range => '0'); + + assert awid'length = bid'length report "arwid vs wid data width mismatch"; + assert (awlen'length = 4 or + awlen'length = 8) report "awlen must be either 4 (AXI3) or 8 (AXI4)"; + + wait on initialized until initialized; + + loop + if bready = '1' then + bvalid <= '0'; + end if; + + if (awvalid and awready) = '1' then + input_burst := self.create_burst(awid, awaddr, awlen, awsize, awburst); + self.push_burst(input_burst); + end if; + + if (wvalid and wready) = '1' then + if (wlast = '1') /= (beats = 1) then + self.fail("Expected wlast='1' on last beat of burst " & describe_burst(burst) & + " with length " & to_string(burst.length) & + " starting at address " & to_string(burst.address)); + end if; + + aligned_address := address - (address mod self.data_size); + for j in 0 to self.data_size-1 loop + if wstrb(j) = '1' then + record_input_data(input_data, aligned_address+j, to_integer(unsigned(wdata(8*j+7 downto 8*j)))); + end if; + end loop; + + if burst.burst_type = axi_burst_type_incr then + address := address + burst.size; + end if; + + beats := beats - 1; + if beats = 0 then + self.push_random_response_time; + self.finish_burst(burst); + self.push_resp(burst); + push_burst_data(input_data_queue, input_data); + end if; + end if; + + if not (self.burst_queue_empty or beats > 0) then + input_data := new_burst_data; + burst := self.pop_burst; + address := burst.address; + beats := burst.length; + end if; + + if not self.resp_queue_empty and (bvalid = '0' or bready = '1') and not self.should_stall_write_response then + + if not has_response_time then + has_response_time := true; + response_time := self.pop_response_time; + end if; + + if has_response_time and response_time <= now then + has_response_time := false; + resp_burst := self.pop_resp; + write_data_to_memory(input_data_queue); + bvalid <= '1'; + bid <= std_logic_vector(to_unsigned(resp_burst.id, bid'length)); + bresp <= axi_resp_okay; + end if; + end if; + + if beats > 0 and not (beats = 1 and self.resp_queue_full) and not self.should_stall_data then + wready <= '1'; + else + wready <= '0'; + end if; + + if self.should_stall_address or self.burst_queue_full then + awready <= '0'; + else + awready <= '1'; + end if; + + wait until rising_edge(aclk); + end loop; + end process; + + well_behaved_check : process + variable size, len : natural; + variable num_beats : integer := 0; + variable num_beats_now : integer; + begin + wait on initialized until initialized; + loop + + num_beats_now := num_beats; + + if awvalid = '1' then + len := to_integer(unsigned(awlen)); + num_beats_now := num_beats + len + 1; + end if; + + -- Always keep track of num_beats such that the well behaved check can be enabled at any time + if (awvalid and awready) = '1' then + size := 2**to_integer(unsigned(awsize)); + num_beats := num_beats_now; + + if self.should_check_well_behaved and size /= self.data_size and len /= 0 then + self.fail("Burst not well behaved, axi size = " & to_string(size) & " but bus data width allows " & to_string(self.data_size)); + end if; + end if; + + if self.should_check_well_behaved and num_beats_now > 0 and wvalid /= '1' then + self.fail("Burst not well behaved, vwalid was not high during active burst"); + end if; + + if self.should_check_well_behaved and num_beats_now > 0 and bready /= '1' then + self.fail("Burst not well behaved, bready was not high during active burst"); + end if; + + if (wvalid and wready) = '1' then + num_beats := -1; + end if; + + wait until rising_edge(aclk); + end loop; + wait; + end process; + +end architecture; diff --git a/vunit/vhdl/verification_components/src/bus2memory.vhd b/vunit/vhdl/verification_components/src/bus2memory.vhd index db1c3aea5..057b649f4 100644 --- a/vunit/vhdl/verification_components/src/bus2memory.vhd +++ b/vunit/vhdl/verification_components/src/bus2memory.vhd @@ -1,61 +1,61 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -context work.com_context; -use work.queue_pkg.all; -use work.bus_master_pkg.all; -use work.memory_pkg.all; - -entity bus2memory is - generic ( - bus_handle : bus_master_t; - memory : memory_t); -end entity; - -architecture a of bus2memory is - constant my_memory : memory_t := to_vc_interface(memory); -begin - main : process - variable request_msg, reply_msg : msg_t; - variable msg_type : msg_type_t; - variable address : std_logic_vector(address_length(bus_handle)-1 downto 0); - variable byte_enable : std_logic_vector(byte_enable_length(bus_handle)-1 downto 0); - variable data : std_logic_vector(data_length(bus_handle)-1 downto 0); - constant blen : natural := byte_length(bus_handle); - begin - while true loop - receive(net, bus_handle.p_actor, request_msg); - msg_type := message_type(request_msg); - - if msg_type = bus_read_msg then - address := pop_std_ulogic_vector(request_msg); - data := read_word(my_memory, to_integer(unsigned(address)), bytes_per_word => data'length/8); - reply_msg := new_msg; - push_std_ulogic_vector(reply_msg, data); - reply(net, request_msg, reply_msg); - - elsif msg_type = bus_write_msg then - address := pop_std_ulogic_vector(request_msg); - data := pop_std_ulogic_vector(request_msg); - byte_enable := pop_std_ulogic_vector(request_msg); - - for i in byte_enable'range loop - -- @TODO byte_enable on memory_t? - if byte_enable(i) = '1' then - write_word(my_memory, to_integer(unsigned(address))+i, data(blen*(i+1)-1 downto blen*i)); - end if; - end loop; - else - unexpected_msg_type(msg_type); - end if; - end loop; - end process; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +context work.com_context; +use work.queue_pkg.all; +use work.bus_master_pkg.all; +use work.memory_pkg.all; + +entity bus2memory is + generic ( + bus_handle : bus_master_t; + memory : memory_t); +end entity; + +architecture a of bus2memory is + constant my_memory : memory_t := to_vc_interface(memory); +begin + main : process + variable request_msg, reply_msg : msg_t; + variable msg_type : msg_type_t; + variable address : std_logic_vector(address_length(bus_handle)-1 downto 0); + variable byte_enable : std_logic_vector(byte_enable_length(bus_handle)-1 downto 0); + variable data : std_logic_vector(data_length(bus_handle)-1 downto 0); + constant blen : natural := byte_length(bus_handle); + begin + while true loop + receive(net, bus_handle.p_actor, request_msg); + msg_type := message_type(request_msg); + + if msg_type = bus_read_msg then + address := pop_std_ulogic_vector(request_msg); + data := read_word(my_memory, to_integer(unsigned(address)), bytes_per_word => data'length/8); + reply_msg := new_msg; + push_std_ulogic_vector(reply_msg, data); + reply(net, request_msg, reply_msg); + + elsif msg_type = bus_write_msg then + address := pop_std_ulogic_vector(request_msg); + data := pop_std_ulogic_vector(request_msg); + byte_enable := pop_std_ulogic_vector(request_msg); + + for i in byte_enable'range loop + -- @TODO byte_enable on memory_t? + if byte_enable(i) = '1' then + write_word(my_memory, to_integer(unsigned(address))+i, data(blen*(i+1)-1 downto blen*i)); + end if; + end loop; + else + unexpected_msg_type(msg_type); + end if; + end loop; + end process; + +end architecture; diff --git a/vunit/vhdl/verification_components/src/bus_master_pkg-body.vhd b/vunit/vhdl/verification_components/src/bus_master_pkg-body.vhd index 3f268f7cf..ad806ea06 100644 --- a/vunit/vhdl/verification_components/src/bus_master_pkg-body.vhd +++ b/vunit/vhdl/verification_components/src/bus_master_pkg-body.vhd @@ -1,341 +1,341 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -use work.queue_pkg.all; -use work.sync_pkg.all; -use work.queue_pkg.all; -use work.check_pkg.all; - -package body bus_master_pkg is - - impure function new_bus(data_length : natural; - address_length : natural; - byte_length : natural := 8; - logger : logger_t := bus_logger; - actor : actor_t := null_actor) return bus_master_t is - variable p_actor : actor_t; - begin - p_actor := actor when actor /= null_actor else new_actor; - - return (p_actor => p_actor, - p_data_length => data_length, - p_address_length => address_length, - p_byte_length => byte_length, - p_logger => logger); - end; - - function get_logger(bus_handle : bus_master_t) return logger_t is - begin - return bus_handle.p_logger; - end; - - impure function data_length(bus_handle : bus_master_t) return natural is - begin - return bus_handle.p_data_length; - end; - - impure function address_length(bus_handle : bus_master_t) return natural is - begin - return bus_handle.p_address_length; - end; - - impure function byte_length(bus_handle : bus_master_t) return natural is - begin - return bus_handle.p_byte_length; - end; - - impure function byte_enable_length(bus_handle : bus_master_t) return natural is - begin - return (bus_handle.p_data_length + bus_handle.p_byte_length - 1) / bus_handle.p_byte_length; - end; - - impure function to_address(constant bus_handle : bus_master_t; address : natural) return std_logic_vector is - begin - return std_logic_vector(to_unsigned(address, address_length(bus_handle))); - end; - - procedure write_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant data : std_logic_vector; - -- default byte enable is all bytes - constant byte_enable : std_logic_vector := "") is - variable request_msg : msg_t := new_msg(bus_write_msg); - variable full_data : std_logic_vector(bus_handle.p_data_length-1 downto 0) := (others => '0'); - variable full_address : std_logic_vector(bus_handle.p_address_length-1 downto 0) := (others => '0'); - variable full_byte_enable : std_logic_vector(byte_enable_length(bus_handle)-1 downto 0); - begin - full_address(address'length-1 downto 0) := address; - push_std_ulogic_vector(request_msg, full_address); - - full_data(data'length-1 downto 0) := data; - push_std_ulogic_vector(request_msg, full_data); - - if byte_enable = "" then - full_byte_enable := (others => '1'); - else - full_byte_enable(byte_enable'length-1 downto 0) := byte_enable; - end if; - push_std_ulogic_vector(request_msg, full_byte_enable); - - send(net, bus_handle.p_actor, request_msg); - end procedure; - - procedure write_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : natural; - constant data : std_logic_vector; - -- default byte enable is all bytes - constant byte_enable : std_logic_vector := "") is - begin - write_bus(net, bus_handle, to_address(bus_handle, address), data, byte_enable); - end; - - procedure burst_write_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant burst_length : positive; - constant data : queue_t) is - variable request_msg : msg_t := new_msg(bus_burst_write_msg); - variable full_address : std_logic_vector(bus_handle.p_address_length-1 downto 0) := (others => '0'); - variable full_data : std_logic_vector(bus_handle.p_data_length-1 downto 0) := (others => '0'); - begin - full_address(address'length-1 downto 0) := address; - push_std_ulogic_vector(request_msg, full_address); - push_integer(request_msg, burst_length); - for i in 0 to burst_length-1 loop - full_data(bus_handle.p_data_length-1 downto 0) := pop(data); - push_std_ulogic_vector(request_msg, full_data); - end loop; - send(net, bus_handle.p_actor, request_msg); - end procedure; - - procedure burst_write_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : natural; - constant burst_length : positive; - constant data : queue_t) is - begin - burst_write_bus(net, bus_handle, to_address(bus_handle, address), burst_length, data); - end procedure; - - procedure check_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant expected : std_logic_vector; - constant msg : string := "") is - variable data : std_logic_vector(bus_handle.p_data_length-1 downto 0); - variable edata : std_logic_vector(data'range) := (others => '0'); - - impure function error_prefix return string is - begin - if msg = "" then - return "check_bus(x""" & to_hstring(address) & """)"; - else - return msg; - end if; - end; - - impure function base_error return string is - begin - return error_prefix & " - Got x""" & to_hstring(data) & """ expected x""" & to_hstring(edata) & """"; - end; - begin - - edata(expected'length-1 downto 0) := expected; - - read_bus(net, bus_handle, address, data); - if not std_match(data, edata) then - failure(bus_handle.p_logger, base_error); - end if; - end procedure; - - procedure check_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : natural; - constant expected : std_logic_vector; - constant msg : string := "") is - begin - check_bus(net, bus_handle, to_address(bus_handle, address), expected, msg); - end; - - -- Non blocking read with delayed reply - procedure read_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - variable reference : inout bus_reference_t) is - variable full_address : std_logic_vector(bus_handle.p_address_length-1 downto 0) := (others => '0'); - alias request_msg : msg_t is reference; - begin - request_msg := new_msg(bus_read_msg); - full_address(address'length-1 downto 0) := address; - push_std_ulogic_vector(request_msg, full_address); - send(net, bus_handle.p_actor, request_msg); - end procedure; - - procedure read_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : natural; - variable reference : inout bus_reference_t) is - begin - read_bus(net, bus_handle, to_address(bus_handle, address), reference); - end; - - procedure burst_read_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant burst_length : positive; - variable reference : inout bus_reference_t) is - variable full_address : std_logic_vector(bus_handle.p_address_length-1 downto 0) := (others => '0'); - alias request_msg : msg_t is reference; - begin - request_msg := new_msg(bus_burst_read_msg); - full_address(address'length-1 downto 0) := address; - push_std_ulogic_vector(request_msg, full_address); - push_integer(request_msg, burst_length); - send(net, bus_handle.p_actor, request_msg); - end procedure; - - procedure burst_read_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : natural; - constant burst_length : positive; - variable reference : inout bus_reference_t) is - begin - burst_read_bus(net, bus_handle, to_address(bus_handle, address), burst_length, reference); - end procedure; - - -- Await read bus reply - procedure await_read_bus_reply(signal net : inout network_t; - variable reference : inout bus_reference_t; - variable data : inout std_logic_vector) is - variable reply_msg : msg_t; - alias request_msg : msg_t is reference; - begin - receive_reply(net, request_msg, reply_msg); - data := pop_std_ulogic_vector(reply_msg)(data'range); - delete(request_msg); - delete(reply_msg); - end procedure; - - procedure await_burst_read_bus_reply(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant data : queue_t; - variable reference : inout bus_reference_t) is - variable reply_msg : msg_t; - alias request_msg : msg_t is reference; - variable d : std_logic_vector(bus_handle.p_data_length-1 downto 0); - variable burst_length : positive; - begin - receive_reply(net, request_msg, reply_msg); - burst_length := pop_integer(reply_msg); - for i in 0 to burst_length-1 loop - d := pop_std_ulogic_vector(reply_msg)(d'range); - push(data, d); - end loop; - delete(request_msg); - delete(reply_msg); - end procedure; - - -- Blocking read with immediate reply - procedure read_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - variable data : inout std_logic_vector) is - variable reference : bus_reference_t; - begin - read_bus(net, bus_handle, address, reference); - await_read_bus_reply(net, reference, data); - end procedure; - - - procedure read_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : natural; - variable data : inout std_logic_vector) is - begin - read_bus(net, bus_handle, to_address(bus_handle, address), data); - end; - - procedure burst_read_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant burst_length : positive; - constant data : queue_t) is - variable reference : bus_reference_t; - begin - burst_read_bus(net, bus_handle, address, burst_length, reference); - await_burst_read_bus_reply(net, bus_handle, data, reference); - end procedure; - - procedure burst_read_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : natural; - constant burst_length : positive; - constant data : queue_t) is - begin - burst_read_bus(net, bus_handle, to_address(bus_handle, address), burst_length, data); - end procedure; - - procedure wait_until_read_equals( - signal net : inout network_t; - bus_handle : bus_master_t; - addr : std_logic_vector; - value : std_logic_vector; - timeout : delay_length := delay_length'high; - msg : string := "") is - constant start_time : time := now; - variable waited : delay_length := delay_length'low; - variable data : std_logic_vector(bus_handle.p_data_length-1 downto 0); - begin - while waited <= timeout loop - -- Do the waited calculation here so that a read delay is allowed when - -- timeout is set to zero. - waited := now - start_time; - read_bus(net, bus_handle, addr, data); - if std_match(data(value'length-1 downto 0), value) then - return; - end if; - end loop; - - if msg = "" then - failure(bus_handle.p_logger, "Timeout"); - else - failure(bus_handle.p_logger, msg); - end if; - end; - - procedure wait_until_read_bit_equals( - signal net : inout network_t; - bus_handle : bus_master_t; - addr : std_logic_vector; - idx : natural; - value : std_logic; - timeout : delay_length := delay_length'high; - msg : string := "") is - variable data : std_logic_vector(bus_handle.p_data_length-1 downto 0); - begin - data := (others => '-'); - data(idx) := value; - wait_until_read_equals(net, bus_handle, addr, data, timeout, msg); - end; - - impure function as_sync(bus_master : bus_master_t) return sync_handle_t is - begin - return bus_master.p_actor; - end; - - procedure wait_until_idle(signal net : inout network_t; - bus_handle : bus_master_t) is - begin - wait_until_idle(net, bus_handle.p_actor); - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.queue_pkg.all; +use work.sync_pkg.all; +use work.queue_pkg.all; +use work.check_pkg.all; + +package body bus_master_pkg is + + impure function new_bus(data_length : natural; + address_length : natural; + byte_length : natural := 8; + logger : logger_t := bus_logger; + actor : actor_t := null_actor) return bus_master_t is + variable p_actor : actor_t; + begin + p_actor := actor when actor /= null_actor else new_actor; + + return (p_actor => p_actor, + p_data_length => data_length, + p_address_length => address_length, + p_byte_length => byte_length, + p_logger => logger); + end; + + function get_logger(bus_handle : bus_master_t) return logger_t is + begin + return bus_handle.p_logger; + end; + + impure function data_length(bus_handle : bus_master_t) return natural is + begin + return bus_handle.p_data_length; + end; + + impure function address_length(bus_handle : bus_master_t) return natural is + begin + return bus_handle.p_address_length; + end; + + impure function byte_length(bus_handle : bus_master_t) return natural is + begin + return bus_handle.p_byte_length; + end; + + impure function byte_enable_length(bus_handle : bus_master_t) return natural is + begin + return (bus_handle.p_data_length + bus_handle.p_byte_length - 1) / bus_handle.p_byte_length; + end; + + impure function to_address(constant bus_handle : bus_master_t; address : natural) return std_logic_vector is + begin + return std_logic_vector(to_unsigned(address, address_length(bus_handle))); + end; + + procedure write_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant data : std_logic_vector; + -- default byte enable is all bytes + constant byte_enable : std_logic_vector := "") is + variable request_msg : msg_t := new_msg(bus_write_msg); + variable full_data : std_logic_vector(bus_handle.p_data_length-1 downto 0) := (others => '0'); + variable full_address : std_logic_vector(bus_handle.p_address_length-1 downto 0) := (others => '0'); + variable full_byte_enable : std_logic_vector(byte_enable_length(bus_handle)-1 downto 0); + begin + full_address(address'length-1 downto 0) := address; + push_std_ulogic_vector(request_msg, full_address); + + full_data(data'length-1 downto 0) := data; + push_std_ulogic_vector(request_msg, full_data); + + if byte_enable = "" then + full_byte_enable := (others => '1'); + else + full_byte_enable(byte_enable'length-1 downto 0) := byte_enable; + end if; + push_std_ulogic_vector(request_msg, full_byte_enable); + + send(net, bus_handle.p_actor, request_msg); + end procedure; + + procedure write_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : natural; + constant data : std_logic_vector; + -- default byte enable is all bytes + constant byte_enable : std_logic_vector := "") is + begin + write_bus(net, bus_handle, to_address(bus_handle, address), data, byte_enable); + end; + + procedure burst_write_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant burst_length : positive; + constant data : queue_t) is + variable request_msg : msg_t := new_msg(bus_burst_write_msg); + variable full_address : std_logic_vector(bus_handle.p_address_length-1 downto 0) := (others => '0'); + variable full_data : std_logic_vector(bus_handle.p_data_length-1 downto 0) := (others => '0'); + begin + full_address(address'length-1 downto 0) := address; + push_std_ulogic_vector(request_msg, full_address); + push_integer(request_msg, burst_length); + for i in 0 to burst_length-1 loop + full_data(bus_handle.p_data_length-1 downto 0) := pop(data); + push_std_ulogic_vector(request_msg, full_data); + end loop; + send(net, bus_handle.p_actor, request_msg); + end procedure; + + procedure burst_write_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : natural; + constant burst_length : positive; + constant data : queue_t) is + begin + burst_write_bus(net, bus_handle, to_address(bus_handle, address), burst_length, data); + end procedure; + + procedure check_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant expected : std_logic_vector; + constant msg : string := "") is + variable data : std_logic_vector(bus_handle.p_data_length-1 downto 0); + variable edata : std_logic_vector(data'range) := (others => '0'); + + impure function error_prefix return string is + begin + if msg = "" then + return "check_bus(x""" & to_hstring(address) & """)"; + else + return msg; + end if; + end; + + impure function base_error return string is + begin + return error_prefix & " - Got x""" & to_hstring(data) & """ expected x""" & to_hstring(edata) & """"; + end; + begin + + edata(expected'length-1 downto 0) := expected; + + read_bus(net, bus_handle, address, data); + if not std_match(data, edata) then + failure(bus_handle.p_logger, base_error); + end if; + end procedure; + + procedure check_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : natural; + constant expected : std_logic_vector; + constant msg : string := "") is + begin + check_bus(net, bus_handle, to_address(bus_handle, address), expected, msg); + end; + + -- Non blocking read with delayed reply + procedure read_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + variable reference : inout bus_reference_t) is + variable full_address : std_logic_vector(bus_handle.p_address_length-1 downto 0) := (others => '0'); + alias request_msg : msg_t is reference; + begin + request_msg := new_msg(bus_read_msg); + full_address(address'length-1 downto 0) := address; + push_std_ulogic_vector(request_msg, full_address); + send(net, bus_handle.p_actor, request_msg); + end procedure; + + procedure read_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : natural; + variable reference : inout bus_reference_t) is + begin + read_bus(net, bus_handle, to_address(bus_handle, address), reference); + end; + + procedure burst_read_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant burst_length : positive; + variable reference : inout bus_reference_t) is + variable full_address : std_logic_vector(bus_handle.p_address_length-1 downto 0) := (others => '0'); + alias request_msg : msg_t is reference; + begin + request_msg := new_msg(bus_burst_read_msg); + full_address(address'length-1 downto 0) := address; + push_std_ulogic_vector(request_msg, full_address); + push_integer(request_msg, burst_length); + send(net, bus_handle.p_actor, request_msg); + end procedure; + + procedure burst_read_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : natural; + constant burst_length : positive; + variable reference : inout bus_reference_t) is + begin + burst_read_bus(net, bus_handle, to_address(bus_handle, address), burst_length, reference); + end procedure; + + -- Await read bus reply + procedure await_read_bus_reply(signal net : inout network_t; + variable reference : inout bus_reference_t; + variable data : inout std_logic_vector) is + variable reply_msg : msg_t; + alias request_msg : msg_t is reference; + begin + receive_reply(net, request_msg, reply_msg); + data := pop_std_ulogic_vector(reply_msg)(data'range); + delete(request_msg); + delete(reply_msg); + end procedure; + + procedure await_burst_read_bus_reply(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant data : queue_t; + variable reference : inout bus_reference_t) is + variable reply_msg : msg_t; + alias request_msg : msg_t is reference; + variable d : std_logic_vector(bus_handle.p_data_length-1 downto 0); + variable burst_length : positive; + begin + receive_reply(net, request_msg, reply_msg); + burst_length := pop_integer(reply_msg); + for i in 0 to burst_length-1 loop + d := pop_std_ulogic_vector(reply_msg)(d'range); + push(data, d); + end loop; + delete(request_msg); + delete(reply_msg); + end procedure; + + -- Blocking read with immediate reply + procedure read_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + variable data : inout std_logic_vector) is + variable reference : bus_reference_t; + begin + read_bus(net, bus_handle, address, reference); + await_read_bus_reply(net, reference, data); + end procedure; + + + procedure read_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : natural; + variable data : inout std_logic_vector) is + begin + read_bus(net, bus_handle, to_address(bus_handle, address), data); + end; + + procedure burst_read_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant burst_length : positive; + constant data : queue_t) is + variable reference : bus_reference_t; + begin + burst_read_bus(net, bus_handle, address, burst_length, reference); + await_burst_read_bus_reply(net, bus_handle, data, reference); + end procedure; + + procedure burst_read_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : natural; + constant burst_length : positive; + constant data : queue_t) is + begin + burst_read_bus(net, bus_handle, to_address(bus_handle, address), burst_length, data); + end procedure; + + procedure wait_until_read_equals( + signal net : inout network_t; + bus_handle : bus_master_t; + addr : std_logic_vector; + value : std_logic_vector; + timeout : delay_length := delay_length'high; + msg : string := "") is + constant start_time : time := now; + variable waited : delay_length := delay_length'low; + variable data : std_logic_vector(bus_handle.p_data_length-1 downto 0); + begin + while waited <= timeout loop + -- Do the waited calculation here so that a read delay is allowed when + -- timeout is set to zero. + waited := now - start_time; + read_bus(net, bus_handle, addr, data); + if std_match(data(value'length-1 downto 0), value) then + return; + end if; + end loop; + + if msg = "" then + failure(bus_handle.p_logger, "Timeout"); + else + failure(bus_handle.p_logger, msg); + end if; + end; + + procedure wait_until_read_bit_equals( + signal net : inout network_t; + bus_handle : bus_master_t; + addr : std_logic_vector; + idx : natural; + value : std_logic; + timeout : delay_length := delay_length'high; + msg : string := "") is + variable data : std_logic_vector(bus_handle.p_data_length-1 downto 0); + begin + data := (others => '-'); + data(idx) := value; + wait_until_read_equals(net, bus_handle, addr, data, timeout, msg); + end; + + impure function as_sync(bus_master : bus_master_t) return sync_handle_t is + begin + return bus_master.p_actor; + end; + + procedure wait_until_idle(signal net : inout network_t; + bus_handle : bus_master_t) is + begin + wait_until_idle(net, bus_handle.p_actor); + end; + +end package body; diff --git a/vunit/vhdl/verification_components/src/bus_master_pkg.vhd b/vunit/vhdl/verification_components/src/bus_master_pkg.vhd index 25a9a1ae0..a14e97f23 100644 --- a/vunit/vhdl/verification_components/src/bus_master_pkg.vhd +++ b/vunit/vhdl/verification_components/src/bus_master_pkg.vhd @@ -1,189 +1,189 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- Defines bus master verification component interface - -library ieee; -use ieee.std_logic_1164.all; - -use work.logger_pkg.all; -context work.com_context; -use work.sync_pkg.all; -use work.queue_pkg.all; - -package bus_master_pkg is - - -- Handle to VC instance with bus master VCI - type bus_master_t is record - -- These fields are private, do not use directly - p_actor : actor_t; - p_data_length : natural; - p_address_length : natural; - p_byte_length : natural; - p_logger : logger_t; - end record; - - -- Reference to non-blocking bus command - alias bus_reference_t is msg_t; - - -- Default logger object for bus master instances - constant bus_logger : logger_t := get_logger("vunit_lib:bus_master_pkg"); - - -- Create new handle for bus master VC - impure function new_bus(data_length : natural; - address_length : natural; - byte_length : natural := 8; - logger : logger_t := bus_logger; - actor : actor_t := null_actor) return bus_master_t; - - -- Return the logger used by the bus master - function get_logger(bus_handle : bus_master_t) return logger_t; - - -- Return the length of the data on this bus - impure function data_length(bus_handle : bus_master_t) return natural; - - -- Return the length of the address on this bus - impure function address_length(bus_handle : bus_master_t) return natural; - - -- Return the length of a byte on this bus - impure function byte_length(bus_handle : bus_master_t) return natural; - - -- Return the length of the byte enable signal on this bus - impure function byte_enable_length(bus_handle : bus_master_t) return natural; - - -- Convert natural address to std_logic_vector using the correct number of bits - impure function to_address(constant bus_handle : - bus_master_t; address : natural) return std_logic_vector; - - -- Blocking: Write the bus - procedure write_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant data : std_logic_vector; - -- default byte enable is all bytes - constant byte_enable : std_logic_vector := ""); - procedure write_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : natural; - constant data : std_logic_vector; - -- default byte enable is all bytes - constant byte_enable : std_logic_vector := ""); - -- Procedures for burst bus write: Caller is responsible for allocation and - -- deallocation of data queue. Procedure cunsumes burst_length data words - -- from data queue. If data queue has less data words, all data - -- words are consumed and pop from empty queue error is raised. - procedure burst_write_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant burst_length : positive; - constant data : queue_t); - procedure burst_write_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : natural; - constant burst_length : positive; - constant data : queue_t); - - -- Non blocking: Read the bus returning a reference to the future reply - procedure read_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - variable reference : inout bus_reference_t); - procedure read_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : natural; - variable reference : inout bus_reference_t); - procedure burst_read_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant burst_length : positive; - variable reference : inout bus_reference_t); - procedure burst_read_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : natural; - constant burst_length : positive; - variable reference : inout bus_reference_t); - - -- Blocking: Await read bus reply data - procedure await_read_bus_reply(signal net : inout network_t; - variable reference : inout bus_reference_t; - variable data : inout std_logic_vector); - -- Procedure for burst read reply: Caller is responsible for allocation and - -- deallocation of data queue. Procedure pushes burst_length data words - -- into data queue. - procedure await_burst_read_bus_reply(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant data : queue_t; - variable reference : inout bus_reference_t); - - -- Blocking: Read bus and check result against expected data - procedure check_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant expected : std_logic_vector; - constant msg : string := ""); - procedure check_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : natural; - constant expected : std_logic_vector; - constant msg : string := ""); - - -- Blocking: read bus with immediate reply - procedure read_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - variable data : inout std_logic_vector); - procedure read_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : natural; - variable data : inout std_logic_vector); - -- Procedure for burst bus read: Caller is responsible for allocation and - -- deallocation of data queue. Procedure pushes burst_length data words - -- into data queue. - procedure burst_read_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : std_logic_vector; - constant burst_length : positive; - constant data : queue_t); - procedure burst_read_bus(signal net : inout network_t; - constant bus_handle : bus_master_t; - constant address : natural; - constant burst_length : positive; - constant data : queue_t); - - -- Blocking: Wait until a read from address equals the value using - -- std_match If timeout is reached error with msg - procedure wait_until_read_equals( - signal net : inout network_t; - bus_handle : bus_master_t; - addr : std_logic_vector; - value : std_logic_vector; - timeout : delay_length := delay_length'high; - msg : string := ""); - - -- Blocking: Wait until a read from address has the bit with this - -- index set to value If timeout is reached error with msg - procedure wait_until_read_bit_equals( - signal net : inout network_t; - bus_handle : bus_master_t; - addr : std_logic_vector; - idx : natural; - value : std_logic; - timeout : delay_length := delay_length'high; - msg : string := ""); - - -- Convert a bus master to a sync handle - impure function as_sync(bus_master : bus_master_t) return sync_handle_t; - - -- Wait until all operations scheduled before this command has finished - procedure wait_until_idle(signal net : inout network_t; - bus_handle : bus_master_t); - - -- Message type definitions, used by VC-instances - constant bus_write_msg : msg_type_t := new_msg_type("write bus"); - constant bus_read_msg : msg_type_t := new_msg_type("read bus"); - constant bus_burst_write_msg : msg_type_t := new_msg_type("burst write bus"); - constant bus_burst_read_msg : msg_type_t := new_msg_type("burst read bus"); -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- Defines bus master verification component interface + +library ieee; +use ieee.std_logic_1164.all; + +use work.logger_pkg.all; +context work.com_context; +use work.sync_pkg.all; +use work.queue_pkg.all; + +package bus_master_pkg is + + -- Handle to VC instance with bus master VCI + type bus_master_t is record + -- These fields are private, do not use directly + p_actor : actor_t; + p_data_length : natural; + p_address_length : natural; + p_byte_length : natural; + p_logger : logger_t; + end record; + + -- Reference to non-blocking bus command + alias bus_reference_t is msg_t; + + -- Default logger object for bus master instances + constant bus_logger : logger_t := get_logger("vunit_lib:bus_master_pkg"); + + -- Create new handle for bus master VC + impure function new_bus(data_length : natural; + address_length : natural; + byte_length : natural := 8; + logger : logger_t := bus_logger; + actor : actor_t := null_actor) return bus_master_t; + + -- Return the logger used by the bus master + function get_logger(bus_handle : bus_master_t) return logger_t; + + -- Return the length of the data on this bus + impure function data_length(bus_handle : bus_master_t) return natural; + + -- Return the length of the address on this bus + impure function address_length(bus_handle : bus_master_t) return natural; + + -- Return the length of a byte on this bus + impure function byte_length(bus_handle : bus_master_t) return natural; + + -- Return the length of the byte enable signal on this bus + impure function byte_enable_length(bus_handle : bus_master_t) return natural; + + -- Convert natural address to std_logic_vector using the correct number of bits + impure function to_address(constant bus_handle : + bus_master_t; address : natural) return std_logic_vector; + + -- Blocking: Write the bus + procedure write_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant data : std_logic_vector; + -- default byte enable is all bytes + constant byte_enable : std_logic_vector := ""); + procedure write_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : natural; + constant data : std_logic_vector; + -- default byte enable is all bytes + constant byte_enable : std_logic_vector := ""); + -- Procedures for burst bus write: Caller is responsible for allocation and + -- deallocation of data queue. Procedure cunsumes burst_length data words + -- from data queue. If data queue has less data words, all data + -- words are consumed and pop from empty queue error is raised. + procedure burst_write_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant burst_length : positive; + constant data : queue_t); + procedure burst_write_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : natural; + constant burst_length : positive; + constant data : queue_t); + + -- Non blocking: Read the bus returning a reference to the future reply + procedure read_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + variable reference : inout bus_reference_t); + procedure read_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : natural; + variable reference : inout bus_reference_t); + procedure burst_read_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant burst_length : positive; + variable reference : inout bus_reference_t); + procedure burst_read_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : natural; + constant burst_length : positive; + variable reference : inout bus_reference_t); + + -- Blocking: Await read bus reply data + procedure await_read_bus_reply(signal net : inout network_t; + variable reference : inout bus_reference_t; + variable data : inout std_logic_vector); + -- Procedure for burst read reply: Caller is responsible for allocation and + -- deallocation of data queue. Procedure pushes burst_length data words + -- into data queue. + procedure await_burst_read_bus_reply(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant data : queue_t; + variable reference : inout bus_reference_t); + + -- Blocking: Read bus and check result against expected data + procedure check_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant expected : std_logic_vector; + constant msg : string := ""); + procedure check_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : natural; + constant expected : std_logic_vector; + constant msg : string := ""); + + -- Blocking: read bus with immediate reply + procedure read_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + variable data : inout std_logic_vector); + procedure read_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : natural; + variable data : inout std_logic_vector); + -- Procedure for burst bus read: Caller is responsible for allocation and + -- deallocation of data queue. Procedure pushes burst_length data words + -- into data queue. + procedure burst_read_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : std_logic_vector; + constant burst_length : positive; + constant data : queue_t); + procedure burst_read_bus(signal net : inout network_t; + constant bus_handle : bus_master_t; + constant address : natural; + constant burst_length : positive; + constant data : queue_t); + + -- Blocking: Wait until a read from address equals the value using + -- std_match If timeout is reached error with msg + procedure wait_until_read_equals( + signal net : inout network_t; + bus_handle : bus_master_t; + addr : std_logic_vector; + value : std_logic_vector; + timeout : delay_length := delay_length'high; + msg : string := ""); + + -- Blocking: Wait until a read from address has the bit with this + -- index set to value If timeout is reached error with msg + procedure wait_until_read_bit_equals( + signal net : inout network_t; + bus_handle : bus_master_t; + addr : std_logic_vector; + idx : natural; + value : std_logic; + timeout : delay_length := delay_length'high; + msg : string := ""); + + -- Convert a bus master to a sync handle + impure function as_sync(bus_master : bus_master_t) return sync_handle_t; + + -- Wait until all operations scheduled before this command has finished + procedure wait_until_idle(signal net : inout network_t; + bus_handle : bus_master_t); + + -- Message type definitions, used by VC-instances + constant bus_write_msg : msg_type_t := new_msg_type("write bus"); + constant bus_read_msg : msg_type_t := new_msg_type("read bus"); + constant bus_burst_write_msg : msg_type_t := new_msg_type("burst write bus"); + constant bus_burst_read_msg : msg_type_t := new_msg_type("burst read bus"); +end package; diff --git a/vunit/vhdl/verification_components/src/memory_pkg-body.vhd b/vunit/vhdl/verification_components/src/memory_pkg-body.vhd index 260f3be91..d9b73e3d7 100644 --- a/vunit/vhdl/verification_components/src/memory_pkg-body.vhd +++ b/vunit/vhdl/verification_components/src/memory_pkg-body.vhd @@ -1,491 +1,491 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.numeric_std.all; - -package body memory_pkg is - - constant num_bytes_idx : natural := 0; - constant num_buffers_idx : natural := 1; - constant num_meta : natural := num_buffers_idx + 1; - - type memory_data_t is record - byte : byte_t; - exp : byte_t; - has_exp : boolean; - perm : permissions_t; - end record; - - impure function new_memory(logger : logger_t := memory_logger; - endian : endianness_t := little_endian) return memory_t is - constant p_meta : integer_vector_ptr_t := new_integer_vector_ptr(num_meta); - begin - set(p_meta, num_bytes_idx, 0); - set(p_meta, num_buffers_idx, 0); - - return (p_meta => p_meta, - p_default_endian => endian, - p_check_permissions => false, - p_data => new_integer_vector_ptr(0), - p_buffers => new_integer_vector_ptr(0), - p_logger => logger); - end; - - procedure clear(memory : memory_t) is - begin - assert memory /= null_memory; - set(memory.p_meta, num_bytes_idx, 0); - set(memory.p_meta, num_buffers_idx, 0); - reallocate(memory.p_data, 0); - reallocate(memory.p_buffers, 0); - end procedure; - - impure function evaluate_endian(memory : memory_t; endian : endianness_arg_t) return endianness_t is - begin - if endian = default_endian then - return memory.p_default_endian; - else - return endian; - end if; - end; - - impure function decode(value : integer) return memory_data_t is - begin - return (byte => value mod 256, - exp => (value/256) mod 256, - has_exp => (value/256**2) mod 2 = 1, - perm => permissions_t'val((value/(2*256**2)) mod 256)); - end; - - impure function encode(memory_data : memory_data_t) return integer is - variable result : integer; - begin - result := (memory_data.byte + - memory_data.exp*256 + - permissions_t'pos(memory_data.perm)*(2*256**2)); - if memory_data.has_exp then - result := result + 256**2; - end if; - return result; - end; - - impure function allocate(memory : memory_t; - num_bytes : natural; - name : string := ""; - alignment : positive := 1; - permissions : permissions_t := read_and_write) return buffer_t is - variable buf : buffer_t; - variable num_buffers : natural; - begin - buf.p_memory_ref := memory; - buf.p_name := new_string_ptr(name); - buf.p_address := work.memory_pkg.num_bytes(memory); - buf.p_address := buf.p_address + ((-buf.p_address) mod alignment); - buf.p_num_bytes := num_bytes; - set(memory.p_meta, num_bytes_idx, last_address(buf)+1); - - if length(memory.p_data) < last_address(buf) + 1 then - -- Allocate exponentially more memory to avoid to much copying - resize(memory.p_data, 2*last_address(buf) + 1, value => encode((byte => 0, exp => 0, has_exp => false, perm => no_access))); - end if; - - num_buffers := get(memory.p_meta, num_buffers_idx) + 1; - - set(memory.p_meta, num_buffers_idx, num_buffers); - if length(memory.p_buffers) < num_buffers*3 then - -- Allocate exponentially more memory to avoid to much copying - resize(memory.p_buffers, 2*num_buffers*3); - end if; - - set(memory.p_buffers, 3*num_buffers-3, to_integer(buf.p_name)); - set(memory.p_buffers, 3*num_buffers-2, buf.p_address); - set(memory.p_buffers, 3*num_buffers-1, buf.p_num_bytes); - - -- Set default access type - for i in 0 to num_bytes-1 loop - set(memory.p_data, buf.p_address + i, encode((byte => 0, exp => 0, has_exp => false, perm => permissions))); - end loop; - return buf; - end function; - - impure function name(buf : buffer_t) return string is - begin - return to_string(buf.p_name); - end function; - - impure function base_address(buf : buffer_t) return natural is - begin - return buf.p_address; - end function; - - impure function last_address(buf : buffer_t) return natural is - begin - return buf.p_address + num_bytes(buf) - 1; - end function; - - impure function num_bytes(buf : buffer_t) return natural is - begin - return buf.p_num_bytes; - end function; - - impure function address_to_allocation(memory : memory_t; address : natural) return buffer_t is - variable buf : buffer_t; - begin - -- @TODO use bisection for speedup - for i in 0 to get(memory.p_meta, num_buffers_idx)-1 loop - buf.p_address := get(memory.p_buffers, 3*i+1); - - if address >= buf.p_address then - buf.p_num_bytes := get(memory.p_buffers, 3*i+2); - - if address < buf.p_address + buf.p_num_bytes then - buf.p_name := to_string_ptr(get(memory.p_buffers, 3*i)); - return buf; - end if; - end if; - end loop; - - return null_buffer; - end; - - impure function check_write_data(memory : memory_t; - address : natural; - byte : byte_t) return boolean is - constant memory_data : memory_data_t := decode(get(memory.p_data, address)); - begin - if memory_data.has_exp and byte /= memory_data.exp then - failure(memory.p_logger, "Writing to " & describe_address(memory, address) & - ". Got " & to_string(byte) & " expected " & to_string(memory_data.exp)); - return false; - end if; - - return true; - end; - - impure function check_address(memory : memory_t; address : natural; - reading : boolean; - check_permissions : boolean := false) return boolean is - impure function verb return string is - begin - if reading then - return "Reading from"; - else - return "Writing to"; - end if; - end function; - - begin - if length(memory.p_data) = 0 then - failure(memory.p_logger, verb & " empty memory"); - return false; - elsif address >= length(memory.p_data) then - failure(memory.p_logger, verb & " address " & to_string(address) & " out of range 0 to " & to_string(length(memory.p_data)-1)); - return false; - elsif check_permissions and get_permissions(memory, address) = no_access then - failure(memory.p_logger, verb & " " & describe_address(memory, address) & " without permission (no_access)"); - return false; - elsif check_permissions and reading and get_permissions(memory, address) = write_only then - failure(memory.p_logger, verb & " " & describe_address(memory, address) & " without permission (write_only)"); - return false; - elsif check_permissions and not reading and get_permissions(memory, address) = read_only then - failure(memory.p_logger, verb & " " & describe_address(memory, address) & " without permission (read_only)"); - return false; - end if; - return true; - end; - - impure function get(memory : memory_t; - address : natural; - reading : boolean; - check_permissions : boolean := false) return memory_data_t is - begin - if not check_address(memory, address, reading, check_permissions) then - return decode(0); - end if; - return decode(get(memory.p_data, address)); - end; - - impure function num_bytes(memory : memory_t) return natural is - begin - return get(memory.p_meta, num_bytes_idx); - end; - - procedure write_byte_unchecked(memory : memory_t; address : natural; byte : byte_t) is - variable old : memory_data_t; - begin - old := decode(get(memory.p_data, address)); - set(memory.p_data, address, encode((byte => byte, exp => old.exp, has_exp => old.has_exp, perm => old.perm))); - end; - - procedure write_byte(memory : memory_t; address : natural; byte : byte_t) is - begin - if not check_address(memory, address, false, memory.p_check_permissions) then - return; - end if; - - if not check_write_data(memory, address, byte) then - return; - end if; - write_byte_unchecked(memory, address, byte); - end; - - impure function read_byte(memory : memory_t; address : natural) return byte_t is - begin - return get(memory, address, true, memory.p_check_permissions).byte; - end; - - procedure check_expected_was_written(memory : memory_t; address : natural; num_bytes : natural) is - variable memory_data : memory_data_t; - begin - for addr in address to address + num_bytes - 1 loop - memory_data := decode(get(memory.p_data, addr)); - if memory_data.has_exp and memory_data.byte /= memory_data.exp then - failure(memory.p_logger, "The " & describe_address(memory, addr) & - " was never written with expected byte " & to_string(memory_data.exp)); - end if; - end loop; - end procedure; - - impure function expected_was_written(memory : memory_t; - address : natural; - num_bytes : natural) return boolean is - variable memory_data : memory_data_t; - begin - for addr in address to address + num_bytes - 1 loop - memory_data := decode(get(memory.p_data, addr)); - if memory_data.has_exp and memory_data.byte /= memory_data.exp then - return false; - end if; - end loop; - - return true; - end; - - procedure check_expected_was_written(buf : buffer_t) is - begin - check_expected_was_written(buf.p_memory_ref, base_address(buf), num_bytes(buf)); - end procedure; - - impure function expected_was_written(buf : buffer_t) return boolean is - begin - return expected_was_written(buf.p_memory_ref, base_address(buf), num_bytes(buf)); - end; - - procedure check_expected_was_written(memory : memory_t) is - begin - check_expected_was_written(memory, 0, num_bytes(memory)); - end procedure; - - impure function expected_was_written(memory : memory_t) return boolean is - begin - return expected_was_written(memory, 0, num_bytes(memory)); - end; - - impure function get_permissions(memory : memory_t; address : natural) return permissions_t is - begin - return get(memory, address, true).perm; - end; - - procedure set_permissions(memory : memory_t; address : natural; permissions : permissions_t) is - variable old : memory_data_t; - begin - if not check_address(memory, address, false) then - return; - end if; - old := decode(get(memory.p_data, address)); - set(memory.p_data, address, encode((byte => old.byte, exp => old.exp, has_exp => old.has_exp, perm => permissions))); - end procedure; - - impure function has_expected_byte(memory : memory_t; address : natural) return boolean is - begin - return get(memory, address, true).has_exp; - end; - - procedure clear_expected_byte(memory : memory_t; address : natural) is - variable old : memory_data_t; - begin - if not check_address(memory, address, false) then - return; - end if; - old := decode(get(memory.p_data, address)); - set(memory.p_data, address, encode((byte => old.byte, exp => 0, has_exp => false, perm => old.perm))); - end procedure; - - procedure set_expected_byte(memory : memory_t; address : natural; expected : byte_t) is - variable old : memory_data_t; - begin - if not check_address(memory, address, false) then - return; - end if; - old := decode(get(memory.p_data, address)); - set(memory.p_data, address, encode((byte => old.byte, exp => expected, has_exp => true, perm => old.perm))); - end procedure; - - impure function get_expected_byte(memory : memory_t; address : natural) return byte_t is - begin - return get(memory, address, true).exp; - end; - - procedure set_expected_word(memory : memory_t; - address : natural; - expected : std_logic_vector; - endian : endianness_arg_t := default_endian) is - -- Normalize to downto range to enable std_logic_vector literals which are - -- 1 to N - constant word_i : std_logic_vector(expected'length-1 downto 0) := expected; - constant endianness : endianness_t := evaluate_endian(memory, endian); - begin - case endianness is - when big_endian => - for idx in 0 to word_i'length/8-1 loop - set_expected_byte(memory, address + word_i'length/8 - 1 - idx, - to_integer(unsigned(word_i(8*idx+7 downto 8*idx)))); - end loop; - when little_endian => - for idx in 0 to word_i'length/8-1 loop - set_expected_byte(memory, address + idx, - to_integer(unsigned(word_i(8*idx+7 downto 8*idx)))); - end loop; - end case; - end; - - impure function serialize(word : integer; - bytes_per_word : natural range 1 to 4; - endian : endianness_t) return integer_vector is - - variable result : integer_vector(0 to bytes_per_word-1); - variable byte : byte_t; - variable word_i : integer := word; - begin - case endian is - when big_endian => - for byte_idx in 0 to bytes_per_word-1 loop - byte := word_i mod 256; - word_i := (word_i - byte)/256; - result(bytes_per_word-1-byte_idx) := byte; - end loop; - when little_endian => - for byte_idx in 0 to bytes_per_word-1 loop - byte := word_i mod 256; - word_i := (word_i - byte)/256; - result(byte_idx) := byte; - end loop; - end case; - return result; - end function; - - procedure set_expected_integer(memory : memory_t; - address : natural; - expected : integer; - bytes_per_word : natural range 1 to 4 := 4; - endian : endianness_arg_t := default_endian) is - constant bytes : integer_vector(0 to bytes_per_word-1) := serialize(expected, - bytes_per_word, - evaluate_endian(memory, endian)); - begin - for byte_idx in 0 to bytes_per_word-1 loop - set_expected_byte(memory, address + byte_idx, bytes(byte_idx)); - end loop; - end; - - impure function describe_address(memory : memory_t; address : natural) return string is - constant buf : buffer_t := address_to_allocation(memory, address); - - impure function describe_buffer return string is - begin - if to_string(buf.p_name) = "" then - return "anonymous buffer"; - else - return "buffer '" & to_string(buf.p_name) & "'"; - end if; - end; - begin - if buf = null_buffer then - return "address " & to_string(address) & " at unallocated location"; - end if; - - return ("address " & to_string(address) & " at offset " & to_string(address - base_address(buf)) & - " within " & describe_buffer & " at range " & - "(" & to_string(base_address(buf)) & " to " & to_string(last_address(buf)) & ")"); - end; - - procedure write_word(memory : memory_t; - address : natural; - word : std_logic_vector; - endian : endianness_arg_t := default_endian) is - constant endianness : endianness_t := evaluate_endian(memory, endian); - -- Normalize to downto range to enable std_logic_vector literals which are - -- 1 to N - constant word_i : std_logic_vector(word'length-1 downto 0) := word; - begin - case endianness is - when big_endian => - for idx in 0 to word_i'length/8-1 loop - write_byte(memory, address + word_i'length/8 - 1 - idx, - to_integer(unsigned(word_i(8*idx+7 downto 8*idx)))); - end loop; - when little_endian => - for idx in 0 to word_i'length/8-1 loop - write_byte(memory, address + idx, - to_integer(unsigned(word_i(8*idx+7 downto 8*idx)))); - end loop; - end case; - end procedure; - - - impure function read_word(memory : memory_t; - address : natural; - bytes_per_word : positive; - endian : endianness_arg_t := default_endian) return std_logic_vector is - constant endianness : endianness_t := evaluate_endian(memory, endian); - variable result : std_logic_vector(8*bytes_per_word-1 downto 0); - variable bidx : natural; - begin - for idx in 0 to bytes_per_word-1 loop - case endianness is - when big_endian => - bidx := bytes_per_word - 1 - idx; - when little_endian => - bidx := idx; - end case; - - result(8*bidx+7 downto 8*bidx) := std_logic_vector( - to_unsigned(read_byte(memory, address + idx), 8)); - - end loop; - return result; - end; - - procedure write_integer(memory : memory_t; - address : natural; - word : integer; - bytes_per_word : natural range 1 to 4 := 4; - endian : endianness_arg_t := default_endian) is - - constant bytes : integer_vector := serialize(word, - bytes_per_word, - evaluate_endian(memory, endian)); - begin - for byte_idx in 0 to bytes_per_word-1 loop - write_byte(memory, address + byte_idx, - bytes(byte_idx)); - end loop; - end procedure; - - impure function to_vc_interface(memory : memory_t; - - -- Override logger, null_logger means no override - logger : logger_t := null_logger) return memory_t is - variable result : memory_t := memory; - begin - if logger /= null_logger then - result.p_logger := logger; - end if; - result.p_check_permissions := true; - return result; - end; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.numeric_std.all; + +package body memory_pkg is + + constant num_bytes_idx : natural := 0; + constant num_buffers_idx : natural := 1; + constant num_meta : natural := num_buffers_idx + 1; + + type memory_data_t is record + byte : byte_t; + exp : byte_t; + has_exp : boolean; + perm : permissions_t; + end record; + + impure function new_memory(logger : logger_t := memory_logger; + endian : endianness_t := little_endian) return memory_t is + constant p_meta : integer_vector_ptr_t := new_integer_vector_ptr(num_meta); + begin + set(p_meta, num_bytes_idx, 0); + set(p_meta, num_buffers_idx, 0); + + return (p_meta => p_meta, + p_default_endian => endian, + p_check_permissions => false, + p_data => new_integer_vector_ptr(0), + p_buffers => new_integer_vector_ptr(0), + p_logger => logger); + end; + + procedure clear(memory : memory_t) is + begin + assert memory /= null_memory; + set(memory.p_meta, num_bytes_idx, 0); + set(memory.p_meta, num_buffers_idx, 0); + reallocate(memory.p_data, 0); + reallocate(memory.p_buffers, 0); + end procedure; + + impure function evaluate_endian(memory : memory_t; endian : endianness_arg_t) return endianness_t is + begin + if endian = default_endian then + return memory.p_default_endian; + else + return endian; + end if; + end; + + impure function decode(value : integer) return memory_data_t is + begin + return (byte => value mod 256, + exp => (value/256) mod 256, + has_exp => (value/256**2) mod 2 = 1, + perm => permissions_t'val((value/(2*256**2)) mod 256)); + end; + + impure function encode(memory_data : memory_data_t) return integer is + variable result : integer; + begin + result := (memory_data.byte + + memory_data.exp*256 + + permissions_t'pos(memory_data.perm)*(2*256**2)); + if memory_data.has_exp then + result := result + 256**2; + end if; + return result; + end; + + impure function allocate(memory : memory_t; + num_bytes : natural; + name : string := ""; + alignment : positive := 1; + permissions : permissions_t := read_and_write) return buffer_t is + variable buf : buffer_t; + variable num_buffers : natural; + begin + buf.p_memory_ref := memory; + buf.p_name := new_string_ptr(name); + buf.p_address := work.memory_pkg.num_bytes(memory); + buf.p_address := buf.p_address + ((-buf.p_address) mod alignment); + buf.p_num_bytes := num_bytes; + set(memory.p_meta, num_bytes_idx, last_address(buf)+1); + + if length(memory.p_data) < last_address(buf) + 1 then + -- Allocate exponentially more memory to avoid to much copying + resize(memory.p_data, 2*last_address(buf) + 1, value => encode((byte => 0, exp => 0, has_exp => false, perm => no_access))); + end if; + + num_buffers := get(memory.p_meta, num_buffers_idx) + 1; + + set(memory.p_meta, num_buffers_idx, num_buffers); + if length(memory.p_buffers) < num_buffers*3 then + -- Allocate exponentially more memory to avoid to much copying + resize(memory.p_buffers, 2*num_buffers*3); + end if; + + set(memory.p_buffers, 3*num_buffers-3, to_integer(buf.p_name)); + set(memory.p_buffers, 3*num_buffers-2, buf.p_address); + set(memory.p_buffers, 3*num_buffers-1, buf.p_num_bytes); + + -- Set default access type + for i in 0 to num_bytes-1 loop + set(memory.p_data, buf.p_address + i, encode((byte => 0, exp => 0, has_exp => false, perm => permissions))); + end loop; + return buf; + end function; + + impure function name(buf : buffer_t) return string is + begin + return to_string(buf.p_name); + end function; + + impure function base_address(buf : buffer_t) return natural is + begin + return buf.p_address; + end function; + + impure function last_address(buf : buffer_t) return natural is + begin + return buf.p_address + num_bytes(buf) - 1; + end function; + + impure function num_bytes(buf : buffer_t) return natural is + begin + return buf.p_num_bytes; + end function; + + impure function address_to_allocation(memory : memory_t; address : natural) return buffer_t is + variable buf : buffer_t; + begin + -- @TODO use bisection for speedup + for i in 0 to get(memory.p_meta, num_buffers_idx)-1 loop + buf.p_address := get(memory.p_buffers, 3*i+1); + + if address >= buf.p_address then + buf.p_num_bytes := get(memory.p_buffers, 3*i+2); + + if address < buf.p_address + buf.p_num_bytes then + buf.p_name := to_string_ptr(get(memory.p_buffers, 3*i)); + return buf; + end if; + end if; + end loop; + + return null_buffer; + end; + + impure function check_write_data(memory : memory_t; + address : natural; + byte : byte_t) return boolean is + constant memory_data : memory_data_t := decode(get(memory.p_data, address)); + begin + if memory_data.has_exp and byte /= memory_data.exp then + failure(memory.p_logger, "Writing to " & describe_address(memory, address) & + ". Got " & to_string(byte) & " expected " & to_string(memory_data.exp)); + return false; + end if; + + return true; + end; + + impure function check_address(memory : memory_t; address : natural; + reading : boolean; + check_permissions : boolean := false) return boolean is + impure function verb return string is + begin + if reading then + return "Reading from"; + else + return "Writing to"; + end if; + end function; + + begin + if length(memory.p_data) = 0 then + failure(memory.p_logger, verb & " empty memory"); + return false; + elsif address >= length(memory.p_data) then + failure(memory.p_logger, verb & " address " & to_string(address) & " out of range 0 to " & to_string(length(memory.p_data)-1)); + return false; + elsif check_permissions and get_permissions(memory, address) = no_access then + failure(memory.p_logger, verb & " " & describe_address(memory, address) & " without permission (no_access)"); + return false; + elsif check_permissions and reading and get_permissions(memory, address) = write_only then + failure(memory.p_logger, verb & " " & describe_address(memory, address) & " without permission (write_only)"); + return false; + elsif check_permissions and not reading and get_permissions(memory, address) = read_only then + failure(memory.p_logger, verb & " " & describe_address(memory, address) & " without permission (read_only)"); + return false; + end if; + return true; + end; + + impure function get(memory : memory_t; + address : natural; + reading : boolean; + check_permissions : boolean := false) return memory_data_t is + begin + if not check_address(memory, address, reading, check_permissions) then + return decode(0); + end if; + return decode(get(memory.p_data, address)); + end; + + impure function num_bytes(memory : memory_t) return natural is + begin + return get(memory.p_meta, num_bytes_idx); + end; + + procedure write_byte_unchecked(memory : memory_t; address : natural; byte : byte_t) is + variable old : memory_data_t; + begin + old := decode(get(memory.p_data, address)); + set(memory.p_data, address, encode((byte => byte, exp => old.exp, has_exp => old.has_exp, perm => old.perm))); + end; + + procedure write_byte(memory : memory_t; address : natural; byte : byte_t) is + begin + if not check_address(memory, address, false, memory.p_check_permissions) then + return; + end if; + + if not check_write_data(memory, address, byte) then + return; + end if; + write_byte_unchecked(memory, address, byte); + end; + + impure function read_byte(memory : memory_t; address : natural) return byte_t is + begin + return get(memory, address, true, memory.p_check_permissions).byte; + end; + + procedure check_expected_was_written(memory : memory_t; address : natural; num_bytes : natural) is + variable memory_data : memory_data_t; + begin + for addr in address to address + num_bytes - 1 loop + memory_data := decode(get(memory.p_data, addr)); + if memory_data.has_exp and memory_data.byte /= memory_data.exp then + failure(memory.p_logger, "The " & describe_address(memory, addr) & + " was never written with expected byte " & to_string(memory_data.exp)); + end if; + end loop; + end procedure; + + impure function expected_was_written(memory : memory_t; + address : natural; + num_bytes : natural) return boolean is + variable memory_data : memory_data_t; + begin + for addr in address to address + num_bytes - 1 loop + memory_data := decode(get(memory.p_data, addr)); + if memory_data.has_exp and memory_data.byte /= memory_data.exp then + return false; + end if; + end loop; + + return true; + end; + + procedure check_expected_was_written(buf : buffer_t) is + begin + check_expected_was_written(buf.p_memory_ref, base_address(buf), num_bytes(buf)); + end procedure; + + impure function expected_was_written(buf : buffer_t) return boolean is + begin + return expected_was_written(buf.p_memory_ref, base_address(buf), num_bytes(buf)); + end; + + procedure check_expected_was_written(memory : memory_t) is + begin + check_expected_was_written(memory, 0, num_bytes(memory)); + end procedure; + + impure function expected_was_written(memory : memory_t) return boolean is + begin + return expected_was_written(memory, 0, num_bytes(memory)); + end; + + impure function get_permissions(memory : memory_t; address : natural) return permissions_t is + begin + return get(memory, address, true).perm; + end; + + procedure set_permissions(memory : memory_t; address : natural; permissions : permissions_t) is + variable old : memory_data_t; + begin + if not check_address(memory, address, false) then + return; + end if; + old := decode(get(memory.p_data, address)); + set(memory.p_data, address, encode((byte => old.byte, exp => old.exp, has_exp => old.has_exp, perm => permissions))); + end procedure; + + impure function has_expected_byte(memory : memory_t; address : natural) return boolean is + begin + return get(memory, address, true).has_exp; + end; + + procedure clear_expected_byte(memory : memory_t; address : natural) is + variable old : memory_data_t; + begin + if not check_address(memory, address, false) then + return; + end if; + old := decode(get(memory.p_data, address)); + set(memory.p_data, address, encode((byte => old.byte, exp => 0, has_exp => false, perm => old.perm))); + end procedure; + + procedure set_expected_byte(memory : memory_t; address : natural; expected : byte_t) is + variable old : memory_data_t; + begin + if not check_address(memory, address, false) then + return; + end if; + old := decode(get(memory.p_data, address)); + set(memory.p_data, address, encode((byte => old.byte, exp => expected, has_exp => true, perm => old.perm))); + end procedure; + + impure function get_expected_byte(memory : memory_t; address : natural) return byte_t is + begin + return get(memory, address, true).exp; + end; + + procedure set_expected_word(memory : memory_t; + address : natural; + expected : std_logic_vector; + endian : endianness_arg_t := default_endian) is + -- Normalize to downto range to enable std_logic_vector literals which are + -- 1 to N + constant word_i : std_logic_vector(expected'length-1 downto 0) := expected; + constant endianness : endianness_t := evaluate_endian(memory, endian); + begin + case endianness is + when big_endian => + for idx in 0 to word_i'length/8-1 loop + set_expected_byte(memory, address + word_i'length/8 - 1 - idx, + to_integer(unsigned(word_i(8*idx+7 downto 8*idx)))); + end loop; + when little_endian => + for idx in 0 to word_i'length/8-1 loop + set_expected_byte(memory, address + idx, + to_integer(unsigned(word_i(8*idx+7 downto 8*idx)))); + end loop; + end case; + end; + + impure function serialize(word : integer; + bytes_per_word : natural range 1 to 4; + endian : endianness_t) return integer_vector is + + variable result : integer_vector(0 to bytes_per_word-1); + variable byte : byte_t; + variable word_i : integer := word; + begin + case endian is + when big_endian => + for byte_idx in 0 to bytes_per_word-1 loop + byte := word_i mod 256; + word_i := (word_i - byte)/256; + result(bytes_per_word-1-byte_idx) := byte; + end loop; + when little_endian => + for byte_idx in 0 to bytes_per_word-1 loop + byte := word_i mod 256; + word_i := (word_i - byte)/256; + result(byte_idx) := byte; + end loop; + end case; + return result; + end function; + + procedure set_expected_integer(memory : memory_t; + address : natural; + expected : integer; + bytes_per_word : natural range 1 to 4 := 4; + endian : endianness_arg_t := default_endian) is + constant bytes : integer_vector(0 to bytes_per_word-1) := serialize(expected, + bytes_per_word, + evaluate_endian(memory, endian)); + begin + for byte_idx in 0 to bytes_per_word-1 loop + set_expected_byte(memory, address + byte_idx, bytes(byte_idx)); + end loop; + end; + + impure function describe_address(memory : memory_t; address : natural) return string is + constant buf : buffer_t := address_to_allocation(memory, address); + + impure function describe_buffer return string is + begin + if to_string(buf.p_name) = "" then + return "anonymous buffer"; + else + return "buffer '" & to_string(buf.p_name) & "'"; + end if; + end; + begin + if buf = null_buffer then + return "address " & to_string(address) & " at unallocated location"; + end if; + + return ("address " & to_string(address) & " at offset " & to_string(address - base_address(buf)) & + " within " & describe_buffer & " at range " & + "(" & to_string(base_address(buf)) & " to " & to_string(last_address(buf)) & ")"); + end; + + procedure write_word(memory : memory_t; + address : natural; + word : std_logic_vector; + endian : endianness_arg_t := default_endian) is + constant endianness : endianness_t := evaluate_endian(memory, endian); + -- Normalize to downto range to enable std_logic_vector literals which are + -- 1 to N + constant word_i : std_logic_vector(word'length-1 downto 0) := word; + begin + case endianness is + when big_endian => + for idx in 0 to word_i'length/8-1 loop + write_byte(memory, address + word_i'length/8 - 1 - idx, + to_integer(unsigned(word_i(8*idx+7 downto 8*idx)))); + end loop; + when little_endian => + for idx in 0 to word_i'length/8-1 loop + write_byte(memory, address + idx, + to_integer(unsigned(word_i(8*idx+7 downto 8*idx)))); + end loop; + end case; + end procedure; + + + impure function read_word(memory : memory_t; + address : natural; + bytes_per_word : positive; + endian : endianness_arg_t := default_endian) return std_logic_vector is + constant endianness : endianness_t := evaluate_endian(memory, endian); + variable result : std_logic_vector(8*bytes_per_word-1 downto 0); + variable bidx : natural; + begin + for idx in 0 to bytes_per_word-1 loop + case endianness is + when big_endian => + bidx := bytes_per_word - 1 - idx; + when little_endian => + bidx := idx; + end case; + + result(8*bidx+7 downto 8*bidx) := std_logic_vector( + to_unsigned(read_byte(memory, address + idx), 8)); + + end loop; + return result; + end; + + procedure write_integer(memory : memory_t; + address : natural; + word : integer; + bytes_per_word : natural range 1 to 4 := 4; + endian : endianness_arg_t := default_endian) is + + constant bytes : integer_vector := serialize(word, + bytes_per_word, + evaluate_endian(memory, endian)); + begin + for byte_idx in 0 to bytes_per_word-1 loop + write_byte(memory, address + byte_idx, + bytes(byte_idx)); + end loop; + end procedure; + + impure function to_vc_interface(memory : memory_t; + + -- Override logger, null_logger means no override + logger : logger_t := null_logger) return memory_t is + variable result : memory_t := memory; + begin + if logger /= null_logger then + result.p_logger := logger; + end if; + result.p_check_permissions := true; + return result; + end; +end package body; diff --git a/vunit/vhdl/verification_components/src/memory_pkg.vhd b/vunit/vhdl/verification_components/src/memory_pkg.vhd index 6a3c2d37d..eb4675be4 100644 --- a/vunit/vhdl/verification_components/src/memory_pkg.vhd +++ b/vunit/vhdl/verification_components/src/memory_pkg.vhd @@ -1,195 +1,195 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- Model of a memory address space - -library ieee; -use ieee.std_logic_1164.all; - -use work.integer_vector_ptr_pkg.all; -use work.string_ptr_pkg.all; -use work.logger_pkg.all; - -package memory_pkg is - - type endianness_arg_t is (little_endian, - big_endian, - default_endian); - subtype endianness_t is endianness_arg_t range little_endian to big_endian; - - -- Memory model object - type memory_t is record - -- Private - p_meta : integer_vector_ptr_t; - p_default_endian : endianness_t; - p_check_permissions : boolean; - p_data : integer_vector_ptr_t; - p_buffers : integer_vector_ptr_t; - p_logger : logger_t; - end record; - constant null_memory : memory_t := (p_logger => null_logger, - p_check_permissions => boolean'low, - p_default_endian => endianness_t'low, - others => null_ptr); - - -- Default memory logger - constant memory_logger : logger_t := get_logger("vunit_lib:memory_pkg"); - - -- Create a new memory object - impure function new_memory(logger : logger_t := memory_logger; - endian : endianness_t := little_endian) return memory_t; - - -- Empties the memory by removing all data and permissions - procedure clear(memory : memory_t); - - -- Return the number of allocated bytes in the memory - impure function num_bytes(memory : memory_t) return natural; - - ----------------------------------------------------- - -- Memory data read and write functions - ----------------------------------------------------- - subtype byte_t is integer range 0 to 255; - procedure write_byte(memory : memory_t; - address : natural; - byte : byte_t); - impure function read_byte(memory : memory_t; - address : natural) return byte_t; - - procedure write_word(memory : memory_t; - address : natural; - word : std_logic_vector; - endian : endianness_arg_t := default_endian); - - impure function read_word(memory : memory_t; - address : natural; - bytes_per_word : positive; - endian : endianness_arg_t := default_endian) return std_logic_vector; - - -- Write integer - procedure write_integer(memory : memory_t; - address : natural; - word : integer; - bytes_per_word : natural range 1 to 4 := 4; - endian : endianness_arg_t := default_endian); - - ----------------------------------------------------- - -- Memory access permission control functions - ----------------------------------------------------- - type permissions_t is (no_access, - write_only, - read_only, - read_and_write); - impure function get_permissions(memory : memory_t; - address : natural) return permissions_t; - procedure set_permissions(memory : memory_t; - address : natural; - permissions : permissions_t); - - ----------------------------------------------------- - -- Functions to set memory expected data - ----------------------------------------------------- - impure function has_expected_byte(memory : memory_t; - address : natural) return boolean; - procedure clear_expected_byte(memory : memory_t; - address : natural); - procedure set_expected_byte(memory : memory_t; - address : natural; - expected : byte_t); - procedure set_expected_word(memory : memory_t; - address : natural; - expected : std_logic_vector; - endian : endianness_arg_t := default_endian); - procedure set_expected_integer(memory : memory_t; - address : natural; - expected : integer; - bytes_per_word : natural range 1 to 4 := 4; - endian : endianness_arg_t := default_endian); - impure function get_expected_byte(memory : memory_t; - address : natural) return byte_t; - - -- Check that all expected bytes within address range was written - -- with correct value - procedure check_expected_was_written(memory : memory_t; - address : natural; - num_bytes : natural); - - -- Returns true if all expected bytes within address range was written - -- with correct value - impure function expected_was_written(memory : memory_t; - address : natural; - num_bytes : natural) return boolean; - - -- Check that all expected bytes within the entire memory was written - -- with correct value - procedure check_expected_was_written(memory : memory_t); - - -- Returns true if all expected bytes within the entire memory was written - -- with correct value - impure function expected_was_written(memory : memory_t) return boolean; - - ----------------------------------------------------- - -- Memory buffer allocation - ----------------------------------------------------- - - -- Reference to an allocated buffer with the memory - type buffer_t is record - -- Private - p_memory_ref : memory_t; - p_name : string_ptr_t; - p_address : natural; - p_num_bytes : natural; - end record; - constant null_buffer : buffer_t := (p_memory_ref => null_memory, - p_name => null_string_ptr, - p_address => natural'low, - p_num_bytes => natural'low); - - -- Allocate a buffer - impure function allocate(memory : memory_t; - num_bytes : natural; - name : string := ""; - alignment : positive := 1; - permissions : permissions_t := read_and_write) return buffer_t; - impure function name(buf : buffer_t) return string; - impure function num_bytes(buf : buffer_t) return natural; - impure function base_address(buf : buffer_t) return natural; - impure function last_address(buf: buffer_t) return natural; - - -- Check that all expected bytes was written with correct value in buffer - procedure check_expected_was_written(buf : buffer_t); - - -- Returns true if all expected bytes was written with correct value in buffer - impure function expected_was_written(buf : buffer_t) return boolean; - - -- Return a string describing the address with name of allocation and - -- permission settings - impure function describe_address(memory : memory_t; - address : natural) return string; - - -- Return a reference to the memory object that can be used in a verification - -- component. The verification component can use its own logger and - -- permissions should be checked. - impure function to_vc_interface(memory : memory_t; - - -- Override logger, null_logger means no override - logger : logger_t := null_logger) return memory_t; - - -- Only perform checks related to address - -- Check for access permissions and address out of range - impure function check_address(memory : memory_t; address : natural; - reading : boolean; - check_permissions : boolean := false) return boolean; - - -- Only perform checks related to write_byte data without performing the write - -- Does not check address - impure function check_write_data(memory : memory_t; - address : natural; - byte : byte_t) return boolean; - - -- Perform write of one byte without running any address or data checks - procedure write_byte_unchecked(memory : memory_t; address : natural; byte : byte_t); - -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- Model of a memory address space + +library ieee; +use ieee.std_logic_1164.all; + +use work.integer_vector_ptr_pkg.all; +use work.string_ptr_pkg.all; +use work.logger_pkg.all; + +package memory_pkg is + + type endianness_arg_t is (little_endian, + big_endian, + default_endian); + subtype endianness_t is endianness_arg_t range little_endian to big_endian; + + -- Memory model object + type memory_t is record + -- Private + p_meta : integer_vector_ptr_t; + p_default_endian : endianness_t; + p_check_permissions : boolean; + p_data : integer_vector_ptr_t; + p_buffers : integer_vector_ptr_t; + p_logger : logger_t; + end record; + constant null_memory : memory_t := (p_logger => null_logger, + p_check_permissions => boolean'low, + p_default_endian => endianness_t'low, + others => null_ptr); + + -- Default memory logger + constant memory_logger : logger_t := get_logger("vunit_lib:memory_pkg"); + + -- Create a new memory object + impure function new_memory(logger : logger_t := memory_logger; + endian : endianness_t := little_endian) return memory_t; + + -- Empties the memory by removing all data and permissions + procedure clear(memory : memory_t); + + -- Return the number of allocated bytes in the memory + impure function num_bytes(memory : memory_t) return natural; + + ----------------------------------------------------- + -- Memory data read and write functions + ----------------------------------------------------- + subtype byte_t is integer range 0 to 255; + procedure write_byte(memory : memory_t; + address : natural; + byte : byte_t); + impure function read_byte(memory : memory_t; + address : natural) return byte_t; + + procedure write_word(memory : memory_t; + address : natural; + word : std_logic_vector; + endian : endianness_arg_t := default_endian); + + impure function read_word(memory : memory_t; + address : natural; + bytes_per_word : positive; + endian : endianness_arg_t := default_endian) return std_logic_vector; + + -- Write integer + procedure write_integer(memory : memory_t; + address : natural; + word : integer; + bytes_per_word : natural range 1 to 4 := 4; + endian : endianness_arg_t := default_endian); + + ----------------------------------------------------- + -- Memory access permission control functions + ----------------------------------------------------- + type permissions_t is (no_access, + write_only, + read_only, + read_and_write); + impure function get_permissions(memory : memory_t; + address : natural) return permissions_t; + procedure set_permissions(memory : memory_t; + address : natural; + permissions : permissions_t); + + ----------------------------------------------------- + -- Functions to set memory expected data + ----------------------------------------------------- + impure function has_expected_byte(memory : memory_t; + address : natural) return boolean; + procedure clear_expected_byte(memory : memory_t; + address : natural); + procedure set_expected_byte(memory : memory_t; + address : natural; + expected : byte_t); + procedure set_expected_word(memory : memory_t; + address : natural; + expected : std_logic_vector; + endian : endianness_arg_t := default_endian); + procedure set_expected_integer(memory : memory_t; + address : natural; + expected : integer; + bytes_per_word : natural range 1 to 4 := 4; + endian : endianness_arg_t := default_endian); + impure function get_expected_byte(memory : memory_t; + address : natural) return byte_t; + + -- Check that all expected bytes within address range was written + -- with correct value + procedure check_expected_was_written(memory : memory_t; + address : natural; + num_bytes : natural); + + -- Returns true if all expected bytes within address range was written + -- with correct value + impure function expected_was_written(memory : memory_t; + address : natural; + num_bytes : natural) return boolean; + + -- Check that all expected bytes within the entire memory was written + -- with correct value + procedure check_expected_was_written(memory : memory_t); + + -- Returns true if all expected bytes within the entire memory was written + -- with correct value + impure function expected_was_written(memory : memory_t) return boolean; + + ----------------------------------------------------- + -- Memory buffer allocation + ----------------------------------------------------- + + -- Reference to an allocated buffer with the memory + type buffer_t is record + -- Private + p_memory_ref : memory_t; + p_name : string_ptr_t; + p_address : natural; + p_num_bytes : natural; + end record; + constant null_buffer : buffer_t := (p_memory_ref => null_memory, + p_name => null_string_ptr, + p_address => natural'low, + p_num_bytes => natural'low); + + -- Allocate a buffer + impure function allocate(memory : memory_t; + num_bytes : natural; + name : string := ""; + alignment : positive := 1; + permissions : permissions_t := read_and_write) return buffer_t; + impure function name(buf : buffer_t) return string; + impure function num_bytes(buf : buffer_t) return natural; + impure function base_address(buf : buffer_t) return natural; + impure function last_address(buf: buffer_t) return natural; + + -- Check that all expected bytes was written with correct value in buffer + procedure check_expected_was_written(buf : buffer_t); + + -- Returns true if all expected bytes was written with correct value in buffer + impure function expected_was_written(buf : buffer_t) return boolean; + + -- Return a string describing the address with name of allocation and + -- permission settings + impure function describe_address(memory : memory_t; + address : natural) return string; + + -- Return a reference to the memory object that can be used in a verification + -- component. The verification component can use its own logger and + -- permissions should be checked. + impure function to_vc_interface(memory : memory_t; + + -- Override logger, null_logger means no override + logger : logger_t := null_logger) return memory_t; + + -- Only perform checks related to address + -- Check for access permissions and address out of range + impure function check_address(memory : memory_t; address : natural; + reading : boolean; + check_permissions : boolean := false) return boolean; + + -- Only perform checks related to write_byte data without performing the write + -- Does not check address + impure function check_write_data(memory : memory_t; + address : natural; + byte : byte_t) return boolean; + + -- Perform write of one byte without running any address or data checks + procedure write_byte_unchecked(memory : memory_t; address : natural; byte : byte_t); + +end package; diff --git a/vunit/vhdl/verification_components/src/memory_utils_pkg.vhd b/vunit/vhdl/verification_components/src/memory_utils_pkg.vhd index 0ff97e6bf..d25bebaaf 100644 --- a/vunit/vhdl/verification_components/src/memory_utils_pkg.vhd +++ b/vunit/vhdl/verification_components/src/memory_utils_pkg.vhd @@ -1,267 +1,267 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- Utility functions to write data types to memory model - -library ieee; -use ieee.std_logic_1164.all; - -use work.integer_vector_ptr_pkg.all; -use work.integer_array_pkg.all; -use work.memory_pkg.all; - -package memory_utils_pkg is - - -- Allocate memory for the integer_vector_ptr - impure function allocate_integer_vector_ptr(memory : memory_t; - integer_vector_ptr : integer_vector_ptr_t; - name : string := ""; - alignment : positive := 1; - bytes_per_word : natural range 1 to 4 := 4; - permissions : permissions_t := write_only) return buffer_t; - - -- Write integer vector pointer to memory address - procedure write_integer_vector_ptr(memory : memory_t; - base_address : natural; - integer_vector_ptr : integer_vector_ptr_t; - bytes_per_word : natural range 1 to 4 := 4; - endian : endianness_arg_t := default_endian); - - -- Allocate memory for the integer_vector_ptr and write it there - impure function write_integer_vector_ptr(memory : memory_t; - integer_vector_ptr : integer_vector_ptr_t; - name : string := ""; - alignment : positive := 1; - bytes_per_word : natural range 1 to 4 := 4; - endian : endianness_arg_t := default_endian; - permissions : permissions_t := read_only) return buffer_t; - - -- Set expected integer vector pointer data at memory address - procedure set_expected_integer_vector_ptr(memory : memory_t; - base_address : natural; - integer_vector_ptr : integer_vector_ptr_t; - bytes_per_word : natural range 1 to 4 := 4; - endian : endianness_arg_t := default_endian); - - -- Allocate memory and set expected integer vector pointer data at memory address - impure function set_expected_integer_vector_ptr(memory : memory_t; - integer_vector_ptr : integer_vector_ptr_t; - name : string := ""; - alignment : positive := 1; - bytes_per_word : natural range 1 to 4 := 4; - endian : endianness_arg_t := default_endian; - permissions : permissions_t := write_only) return buffer_t; - - -- Allocate memory for the integer_array - impure function allocate_integer_array(memory : memory_t; - integer_array : integer_array_t; - name : string := ""; - alignment : positive := 1; - stride_in_bytes : natural := 0; -- 0 stride means use image width - permissions : permissions_t := read_only) return buffer_t; - - -- Write integer array to memory address - procedure write_integer_array(memory : memory_t; - base_address : natural; - integer_array : integer_array_t; - stride_in_bytes : natural := 0; -- 0 stride means use image width - endian : endianness_arg_t := default_endian); - - -- Allocate memory for the integer_array and write the data there - impure function write_integer_array(memory : memory_t; - integer_array : integer_array_t; - name : string := ""; - alignment : positive := 1; - stride_in_bytes : natural := 0; -- 0 stride means use image width - endian : endianness_arg_t := default_endian; - permissions : permissions_t := read_only) return buffer_t; - - -- Set integer_array as expected data - procedure set_expected_integer_array(memory : memory_t; - base_address : natural; - integer_array : integer_array_t; - stride_in_bytes : natural := 0; -- 0 stride means use image width - endian : endianness_arg_t := default_endian); - - -- Allocate memory for the integer_array and set it as expected data - impure function set_expected_integer_array(memory : memory_t; - integer_array : integer_array_t; - name : string := ""; - alignment : positive := 1; - stride_in_bytes : natural := 0; -- 0 stride means use image width - endian : endianness_arg_t := default_endian; - permissions : permissions_t := write_only) return buffer_t; - -end package; - -package body memory_utils_pkg is - - -- Allocate memory for the integer_vector_ptr and set read_only permission - impure function allocate_integer_vector_ptr(memory : memory_t; - integer_vector_ptr : integer_vector_ptr_t; - name : string := ""; - alignment : positive := 1; - bytes_per_word : natural range 1 to 4 := 4; - permissions : permissions_t := write_only) return buffer_t is - begin - return allocate(memory, length(integer_vector_ptr) * bytes_per_word, name => name, - alignment => alignment, permissions => permissions); - end; - - procedure write_integer_vector_ptr(memory : memory_t; - base_address : natural; - integer_vector_ptr : integer_vector_ptr_t; - bytes_per_word : natural range 1 to 4 := 4; - endian : endianness_arg_t := default_endian) is - begin - for i in 0 to length(integer_vector_ptr)-1 loop - write_integer(memory, base_address + bytes_per_word*i, get(integer_vector_ptr, i), - bytes_per_word => bytes_per_word, - endian => endian); - end loop; - end; - - impure function write_integer_vector_ptr(memory : memory_t; - integer_vector_ptr : integer_vector_ptr_t; - name : string := ""; - alignment : positive := 1; - bytes_per_word : natural range 1 to 4 := 4; - endian : endianness_arg_t := default_endian; - permissions : permissions_t := read_only) return buffer_t is - variable buf : buffer_t; - begin - buf := allocate_integer_vector_ptr(memory, integer_vector_ptr, name, alignment, bytes_per_word, permissions); - write_integer_vector_ptr(memory, base_address(buf), integer_vector_ptr, bytes_per_word, endian); - return buf; - end; - - procedure set_expected_integer_vector_ptr(memory : memory_t; - base_address : natural; - integer_vector_ptr : integer_vector_ptr_t; - bytes_per_word : natural range 1 to 4 := 4; - endian : endianness_arg_t := default_endian) is - begin - for i in 0 to length(integer_vector_ptr)-1 loop - set_expected_integer(memory, - base_address + bytes_per_word*i, - get(integer_vector_ptr, i), bytes_per_word, endian); - end loop; - end; - - impure function set_expected_integer_vector_ptr(memory : memory_t; - integer_vector_ptr : integer_vector_ptr_t; - name : string := ""; - alignment : positive := 1; - bytes_per_word : natural range 1 to 4 := 4; - endian : endianness_arg_t := default_endian; - permissions : permissions_t := write_only) return buffer_t is - variable buf : buffer_t; - begin - buf := allocate_integer_vector_ptr(memory, integer_vector_ptr, name, alignment, bytes_per_word, permissions); - set_expected_integer_vector_ptr(memory, base_address(buf), integer_vector_ptr, bytes_per_word, endian); - return buf; - end function; - - impure function get_stride_in_bytes(integer_array : integer_array_t; - -- 0 stride means integer_array.width * bytes_per_word - stride_in_bytes : natural := 0) return natural is - begin - if stride_in_bytes = 0 then - return integer_array.width * bytes_per_word(integer_array); - else - return stride_in_bytes; - end if; - end; - - impure function allocate_integer_array(memory : memory_t; - integer_array : integer_array_t; - name : string := ""; - alignment : positive := 1; - stride_in_bytes : natural := 0; -- 0 stride means use image width - permissions : permissions_t := read_only) return buffer_t is - begin - return allocate(memory, - integer_array.depth * integer_array.height * get_stride_in_bytes(integer_array, stride_in_bytes), - name => name, alignment => alignment, permissions => permissions); - end; - - procedure write_integer_array(memory : memory_t; - base_address : natural; - integer_array : integer_array_t; - stride_in_bytes : natural := 0; -- 0 stride means use image width - endian : endianness_arg_t := default_endian) is - constant bytes_per_word : natural := work.integer_array_pkg.bytes_per_word(integer_array); - variable stride : natural; - variable addr : natural; - begin - stride := get_stride_in_bytes(integer_array, stride_in_bytes); - - for z in 0 to integer_array.depth-1 loop - for y in 0 to integer_array.height-1 loop - addr := base_address + stride*(y + z*integer_array.height); - for x in 0 to integer_array.width-1 loop - write_integer(memory, - addr, - get(integer_array, x, y, z), - bytes_per_word => bytes_per_word, - endian => endian); - addr := addr + bytes_per_word; - end loop; - end loop; - end loop; - end; - - impure function write_integer_array(memory : memory_t; - integer_array : integer_array_t; - name : string := ""; - alignment : positive := 1; - stride_in_bytes : natural := 0; -- 0 stride means use image width - endian : endianness_arg_t := default_endian; - permissions : permissions_t := read_only) return buffer_t is - variable buf : buffer_t; - begin - buf := allocate_integer_array(memory, integer_array, name, alignment, stride_in_bytes, permissions); - write_integer_array(memory, base_address(buf), integer_array, stride_in_bytes, endian); - return buf; - end; - - procedure set_expected_integer_array(memory : memory_t; - base_address : natural; - integer_array : integer_array_t; - stride_in_bytes : natural := 0; -- 0 stride means use image width - endian : endianness_arg_t := default_endian) is - constant bytes_per_word : natural := work.integer_array_pkg.bytes_per_word(integer_array); - constant stride : natural := get_stride_in_bytes(integer_array, stride_in_bytes); - variable addr : natural; - begin - for z in 0 to integer_array.depth-1 loop - for y in 0 to integer_array.height-1 loop - addr := base_address + stride*(y + z*integer_array.height); - - for x in 0 to integer_array.width-1 loop - set_expected_integer(memory, addr, get(integer_array, x, y, z), bytes_per_word, endian); - addr := addr + bytes_per_word; - end loop; - - end loop; - end loop; - end; - - impure function set_expected_integer_array(memory : memory_t; - integer_array : integer_array_t; - name : string := ""; - alignment : positive := 1; - stride_in_bytes : natural := 0; -- 0 stride means use image width - endian : endianness_arg_t := default_endian; - permissions : permissions_t := write_only) return buffer_t is - - variable buf : buffer_t; - begin - buf := allocate_integer_array(memory, integer_array, name, alignment, stride_in_bytes, permissions); - set_expected_integer_array(memory, base_address(buf), integer_array, stride_in_bytes, endian); - return buf; - end; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- Utility functions to write data types to memory model + +library ieee; +use ieee.std_logic_1164.all; + +use work.integer_vector_ptr_pkg.all; +use work.integer_array_pkg.all; +use work.memory_pkg.all; + +package memory_utils_pkg is + + -- Allocate memory for the integer_vector_ptr + impure function allocate_integer_vector_ptr(memory : memory_t; + integer_vector_ptr : integer_vector_ptr_t; + name : string := ""; + alignment : positive := 1; + bytes_per_word : natural range 1 to 4 := 4; + permissions : permissions_t := write_only) return buffer_t; + + -- Write integer vector pointer to memory address + procedure write_integer_vector_ptr(memory : memory_t; + base_address : natural; + integer_vector_ptr : integer_vector_ptr_t; + bytes_per_word : natural range 1 to 4 := 4; + endian : endianness_arg_t := default_endian); + + -- Allocate memory for the integer_vector_ptr and write it there + impure function write_integer_vector_ptr(memory : memory_t; + integer_vector_ptr : integer_vector_ptr_t; + name : string := ""; + alignment : positive := 1; + bytes_per_word : natural range 1 to 4 := 4; + endian : endianness_arg_t := default_endian; + permissions : permissions_t := read_only) return buffer_t; + + -- Set expected integer vector pointer data at memory address + procedure set_expected_integer_vector_ptr(memory : memory_t; + base_address : natural; + integer_vector_ptr : integer_vector_ptr_t; + bytes_per_word : natural range 1 to 4 := 4; + endian : endianness_arg_t := default_endian); + + -- Allocate memory and set expected integer vector pointer data at memory address + impure function set_expected_integer_vector_ptr(memory : memory_t; + integer_vector_ptr : integer_vector_ptr_t; + name : string := ""; + alignment : positive := 1; + bytes_per_word : natural range 1 to 4 := 4; + endian : endianness_arg_t := default_endian; + permissions : permissions_t := write_only) return buffer_t; + + -- Allocate memory for the integer_array + impure function allocate_integer_array(memory : memory_t; + integer_array : integer_array_t; + name : string := ""; + alignment : positive := 1; + stride_in_bytes : natural := 0; -- 0 stride means use image width + permissions : permissions_t := read_only) return buffer_t; + + -- Write integer array to memory address + procedure write_integer_array(memory : memory_t; + base_address : natural; + integer_array : integer_array_t; + stride_in_bytes : natural := 0; -- 0 stride means use image width + endian : endianness_arg_t := default_endian); + + -- Allocate memory for the integer_array and write the data there + impure function write_integer_array(memory : memory_t; + integer_array : integer_array_t; + name : string := ""; + alignment : positive := 1; + stride_in_bytes : natural := 0; -- 0 stride means use image width + endian : endianness_arg_t := default_endian; + permissions : permissions_t := read_only) return buffer_t; + + -- Set integer_array as expected data + procedure set_expected_integer_array(memory : memory_t; + base_address : natural; + integer_array : integer_array_t; + stride_in_bytes : natural := 0; -- 0 stride means use image width + endian : endianness_arg_t := default_endian); + + -- Allocate memory for the integer_array and set it as expected data + impure function set_expected_integer_array(memory : memory_t; + integer_array : integer_array_t; + name : string := ""; + alignment : positive := 1; + stride_in_bytes : natural := 0; -- 0 stride means use image width + endian : endianness_arg_t := default_endian; + permissions : permissions_t := write_only) return buffer_t; + +end package; + +package body memory_utils_pkg is + + -- Allocate memory for the integer_vector_ptr and set read_only permission + impure function allocate_integer_vector_ptr(memory : memory_t; + integer_vector_ptr : integer_vector_ptr_t; + name : string := ""; + alignment : positive := 1; + bytes_per_word : natural range 1 to 4 := 4; + permissions : permissions_t := write_only) return buffer_t is + begin + return allocate(memory, length(integer_vector_ptr) * bytes_per_word, name => name, + alignment => alignment, permissions => permissions); + end; + + procedure write_integer_vector_ptr(memory : memory_t; + base_address : natural; + integer_vector_ptr : integer_vector_ptr_t; + bytes_per_word : natural range 1 to 4 := 4; + endian : endianness_arg_t := default_endian) is + begin + for i in 0 to length(integer_vector_ptr)-1 loop + write_integer(memory, base_address + bytes_per_word*i, get(integer_vector_ptr, i), + bytes_per_word => bytes_per_word, + endian => endian); + end loop; + end; + + impure function write_integer_vector_ptr(memory : memory_t; + integer_vector_ptr : integer_vector_ptr_t; + name : string := ""; + alignment : positive := 1; + bytes_per_word : natural range 1 to 4 := 4; + endian : endianness_arg_t := default_endian; + permissions : permissions_t := read_only) return buffer_t is + variable buf : buffer_t; + begin + buf := allocate_integer_vector_ptr(memory, integer_vector_ptr, name, alignment, bytes_per_word, permissions); + write_integer_vector_ptr(memory, base_address(buf), integer_vector_ptr, bytes_per_word, endian); + return buf; + end; + + procedure set_expected_integer_vector_ptr(memory : memory_t; + base_address : natural; + integer_vector_ptr : integer_vector_ptr_t; + bytes_per_word : natural range 1 to 4 := 4; + endian : endianness_arg_t := default_endian) is + begin + for i in 0 to length(integer_vector_ptr)-1 loop + set_expected_integer(memory, + base_address + bytes_per_word*i, + get(integer_vector_ptr, i), bytes_per_word, endian); + end loop; + end; + + impure function set_expected_integer_vector_ptr(memory : memory_t; + integer_vector_ptr : integer_vector_ptr_t; + name : string := ""; + alignment : positive := 1; + bytes_per_word : natural range 1 to 4 := 4; + endian : endianness_arg_t := default_endian; + permissions : permissions_t := write_only) return buffer_t is + variable buf : buffer_t; + begin + buf := allocate_integer_vector_ptr(memory, integer_vector_ptr, name, alignment, bytes_per_word, permissions); + set_expected_integer_vector_ptr(memory, base_address(buf), integer_vector_ptr, bytes_per_word, endian); + return buf; + end function; + + impure function get_stride_in_bytes(integer_array : integer_array_t; + -- 0 stride means integer_array.width * bytes_per_word + stride_in_bytes : natural := 0) return natural is + begin + if stride_in_bytes = 0 then + return integer_array.width * bytes_per_word(integer_array); + else + return stride_in_bytes; + end if; + end; + + impure function allocate_integer_array(memory : memory_t; + integer_array : integer_array_t; + name : string := ""; + alignment : positive := 1; + stride_in_bytes : natural := 0; -- 0 stride means use image width + permissions : permissions_t := read_only) return buffer_t is + begin + return allocate(memory, + integer_array.depth * integer_array.height * get_stride_in_bytes(integer_array, stride_in_bytes), + name => name, alignment => alignment, permissions => permissions); + end; + + procedure write_integer_array(memory : memory_t; + base_address : natural; + integer_array : integer_array_t; + stride_in_bytes : natural := 0; -- 0 stride means use image width + endian : endianness_arg_t := default_endian) is + constant bytes_per_word : natural := work.integer_array_pkg.bytes_per_word(integer_array); + variable stride : natural; + variable addr : natural; + begin + stride := get_stride_in_bytes(integer_array, stride_in_bytes); + + for z in 0 to integer_array.depth-1 loop + for y in 0 to integer_array.height-1 loop + addr := base_address + stride*(y + z*integer_array.height); + for x in 0 to integer_array.width-1 loop + write_integer(memory, + addr, + get(integer_array, x, y, z), + bytes_per_word => bytes_per_word, + endian => endian); + addr := addr + bytes_per_word; + end loop; + end loop; + end loop; + end; + + impure function write_integer_array(memory : memory_t; + integer_array : integer_array_t; + name : string := ""; + alignment : positive := 1; + stride_in_bytes : natural := 0; -- 0 stride means use image width + endian : endianness_arg_t := default_endian; + permissions : permissions_t := read_only) return buffer_t is + variable buf : buffer_t; + begin + buf := allocate_integer_array(memory, integer_array, name, alignment, stride_in_bytes, permissions); + write_integer_array(memory, base_address(buf), integer_array, stride_in_bytes, endian); + return buf; + end; + + procedure set_expected_integer_array(memory : memory_t; + base_address : natural; + integer_array : integer_array_t; + stride_in_bytes : natural := 0; -- 0 stride means use image width + endian : endianness_arg_t := default_endian) is + constant bytes_per_word : natural := work.integer_array_pkg.bytes_per_word(integer_array); + constant stride : natural := get_stride_in_bytes(integer_array, stride_in_bytes); + variable addr : natural; + begin + for z in 0 to integer_array.depth-1 loop + for y in 0 to integer_array.height-1 loop + addr := base_address + stride*(y + z*integer_array.height); + + for x in 0 to integer_array.width-1 loop + set_expected_integer(memory, addr, get(integer_array, x, y, z), bytes_per_word, endian); + addr := addr + bytes_per_word; + end loop; + + end loop; + end loop; + end; + + impure function set_expected_integer_array(memory : memory_t; + integer_array : integer_array_t; + name : string := ""; + alignment : positive := 1; + stride_in_bytes : natural := 0; -- 0 stride means use image width + endian : endianness_arg_t := default_endian; + permissions : permissions_t := write_only) return buffer_t is + + variable buf : buffer_t; + begin + buf := allocate_integer_array(memory, integer_array, name, alignment, stride_in_bytes, permissions); + set_expected_integer_array(memory, base_address(buf), integer_array, stride_in_bytes, endian); + return buf; + end; +end package body; diff --git a/vunit/vhdl/verification_components/src/ram_master.vhd b/vunit/vhdl/verification_components/src/ram_master.vhd index d19928504..b6f8e7faa 100644 --- a/vunit/vhdl/verification_components/src/ram_master.vhd +++ b/vunit/vhdl/verification_components/src/ram_master.vhd @@ -1,90 +1,90 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -use work.queue_pkg.all; -use work.bus_master_pkg.all; -use work.sync_pkg.all; -context work.com_context; - -entity ram_master is - generic ( - bus_handle : bus_master_t; - latency : positive - ); - port ( - clk : in std_logic; - en : out std_logic := '0'; - we : out std_logic_vector(byte_enable_length(bus_handle)-1 downto 0); - addr : out std_logic_vector(address_length(bus_handle)-1 downto 0); - wdata : out std_logic_vector(data_length(bus_handle)-1 downto 0); - rdata : in std_logic_vector(data_length(bus_handle)-1 downto 0) - ); -end entity; - -architecture a of ram_master is - signal rd : std_logic := '0'; - signal rd_pipe : std_logic_vector(0 to latency-1); - constant request_queue : queue_t := new_queue; -begin - main : process - variable request_msg : msg_t; - variable msg_type : msg_type_t; - begin - receive(net, bus_handle.p_actor, request_msg); - msg_type := message_type(request_msg); - - if msg_type = bus_read_msg then - en <= '1'; - addr <= pop_std_ulogic_vector(request_msg); - rd <= '1'; - we <= (we'range => '0'); - push(request_queue, request_msg); - wait until en = '1' and rising_edge(clk); - en <= '0'; - rd <= '0'; - - elsif msg_type = bus_write_msg then - en <= '1'; - addr <= pop_std_ulogic_vector(request_msg); - wdata <= pop_std_ulogic_vector(request_msg); - we <= pop_std_ulogic_vector(request_msg); - wait until en = '1' and rising_edge(clk); - en <= '0'; - - elsif msg_type = wait_until_idle_msg then - while not is_empty(request_queue) loop - wait until rising_edge(clk); - end loop; - handle_wait_until_idle(net, msg_type, request_msg); - else - unexpected_msg_type(msg_type); - end if; - - end process; - - read_return : process - variable request_msg, reply_msg : msg_t; - begin - wait until rising_edge(clk); - rd_pipe(rd_pipe'high) <= rd; - for i in 0 to rd_pipe'high-1 loop - rd_pipe(i) <= rd_pipe(i+1); - end loop; - - if rd_pipe(0) = '1' then - request_msg := pop(request_queue); - reply_msg := new_msg; - push_std_ulogic_vector(reply_msg, rdata); - reply(net, request_msg, reply_msg); - delete(request_msg); - end if; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.queue_pkg.all; +use work.bus_master_pkg.all; +use work.sync_pkg.all; +context work.com_context; + +entity ram_master is + generic ( + bus_handle : bus_master_t; + latency : positive + ); + port ( + clk : in std_logic; + en : out std_logic := '0'; + we : out std_logic_vector(byte_enable_length(bus_handle)-1 downto 0); + addr : out std_logic_vector(address_length(bus_handle)-1 downto 0); + wdata : out std_logic_vector(data_length(bus_handle)-1 downto 0); + rdata : in std_logic_vector(data_length(bus_handle)-1 downto 0) + ); +end entity; + +architecture a of ram_master is + signal rd : std_logic := '0'; + signal rd_pipe : std_logic_vector(0 to latency-1); + constant request_queue : queue_t := new_queue; +begin + main : process + variable request_msg : msg_t; + variable msg_type : msg_type_t; + begin + receive(net, bus_handle.p_actor, request_msg); + msg_type := message_type(request_msg); + + if msg_type = bus_read_msg then + en <= '1'; + addr <= pop_std_ulogic_vector(request_msg); + rd <= '1'; + we <= (we'range => '0'); + push(request_queue, request_msg); + wait until en = '1' and rising_edge(clk); + en <= '0'; + rd <= '0'; + + elsif msg_type = bus_write_msg then + en <= '1'; + addr <= pop_std_ulogic_vector(request_msg); + wdata <= pop_std_ulogic_vector(request_msg); + we <= pop_std_ulogic_vector(request_msg); + wait until en = '1' and rising_edge(clk); + en <= '0'; + + elsif msg_type = wait_until_idle_msg then + while not is_empty(request_queue) loop + wait until rising_edge(clk); + end loop; + handle_wait_until_idle(net, msg_type, request_msg); + else + unexpected_msg_type(msg_type); + end if; + + end process; + + read_return : process + variable request_msg, reply_msg : msg_t; + begin + wait until rising_edge(clk); + rd_pipe(rd_pipe'high) <= rd; + for i in 0 to rd_pipe'high-1 loop + rd_pipe(i) <= rd_pipe(i+1); + end loop; + + if rd_pipe(0) = '1' then + request_msg := pop(request_queue); + reply_msg := new_msg; + push_std_ulogic_vector(reply_msg, rdata); + reply(net, request_msg, reply_msg); + delete(request_msg); + end if; + end process; +end architecture; diff --git a/vunit/vhdl/verification_components/src/signal_checker_pkg.vhd b/vunit/vhdl/verification_components/src/signal_checker_pkg.vhd index 53659680b..f44409094 100644 --- a/vunit/vhdl/verification_components/src/signal_checker_pkg.vhd +++ b/vunit/vhdl/verification_components/src/signal_checker_pkg.vhd @@ -1,75 +1,75 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -context work.vunit_context; -context work.com_context; - -use work.sync_pkg.all; - -package signal_checker_pkg is - type signal_checker_t is record - -- Private - p_actor : actor_t; - p_logger : logger_t; - end record; - - impure function new_signal_checker( - logger : logger_t := null_logger) - return signal_checker_t; - - -- Add one value to the expect queue - -- Allow event to occur within event_time += margin including end points - procedure expect(signal net : inout network_t; - signal_checker : signal_checker_t; - value : std_logic_vector; - event_time : delay_length; - margin : delay_length := 0 ns); - - -- Wait until all expected values have been checked - procedure wait_until_idle(signal net : inout network_t; - signal_checker : signal_checker_t); - - -- Private message type definitions - constant expect_msg : msg_type_t := new_msg_type("expect"); - -end package; - - -package body signal_checker_pkg is - impure function new_signal_checker(logger : logger_t := null_logger) return signal_checker_t is - variable result : signal_checker_t; - begin - result := (p_actor => new_actor, - p_logger => logger); - if logger = null_logger then - result.p_logger := default_logger; - end if; - return result; - end; - - procedure expect(signal net : inout network_t; - signal_checker : signal_checker_t; - value : std_logic_vector; - event_time : delay_length; - margin : delay_length := 0 ns) is - variable request_msg : msg_t := new_msg(expect_msg); - begin - push_std_ulogic_vector(request_msg, value); - push_time(request_msg, event_time); - push_time(request_msg, margin); - send(net, signal_checker.p_actor, request_msg); - end; - - procedure wait_until_idle(signal net : inout network_t; - signal_checker : signal_checker_t) is - begin - wait_until_idle(net, signal_checker.p_actor); - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +context work.vunit_context; +context work.com_context; + +use work.sync_pkg.all; + +package signal_checker_pkg is + type signal_checker_t is record + -- Private + p_actor : actor_t; + p_logger : logger_t; + end record; + + impure function new_signal_checker( + logger : logger_t := null_logger) + return signal_checker_t; + + -- Add one value to the expect queue + -- Allow event to occur within event_time += margin including end points + procedure expect(signal net : inout network_t; + signal_checker : signal_checker_t; + value : std_logic_vector; + event_time : delay_length; + margin : delay_length := 0 ns); + + -- Wait until all expected values have been checked + procedure wait_until_idle(signal net : inout network_t; + signal_checker : signal_checker_t); + + -- Private message type definitions + constant expect_msg : msg_type_t := new_msg_type("expect"); + +end package; + + +package body signal_checker_pkg is + impure function new_signal_checker(logger : logger_t := null_logger) return signal_checker_t is + variable result : signal_checker_t; + begin + result := (p_actor => new_actor, + p_logger => logger); + if logger = null_logger then + result.p_logger := default_logger; + end if; + return result; + end; + + procedure expect(signal net : inout network_t; + signal_checker : signal_checker_t; + value : std_logic_vector; + event_time : delay_length; + margin : delay_length := 0 ns) is + variable request_msg : msg_t := new_msg(expect_msg); + begin + push_std_ulogic_vector(request_msg, value); + push_time(request_msg, event_time); + push_time(request_msg, margin); + send(net, signal_checker.p_actor, request_msg); + end; + + procedure wait_until_idle(signal net : inout network_t; + signal_checker : signal_checker_t) is + begin + wait_until_idle(net, signal_checker.p_actor); + end; + +end package body; diff --git a/vunit/vhdl/verification_components/src/std_logic_checker.vhd b/vunit/vhdl/verification_components/src/std_logic_checker.vhd index f55601c7a..8d259bf1f 100644 --- a/vunit/vhdl/verification_components/src/std_logic_checker.vhd +++ b/vunit/vhdl/verification_components/src/std_logic_checker.vhd @@ -1,94 +1,94 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -context work.vunit_context; -context work.data_types_context; -context work.com_context; -use work.signal_checker_pkg.all; -use work.sync_pkg.all; - -entity std_logic_checker is - generic ( - signal_checker : signal_checker_t); - port ( - value : in std_logic_vector); -end entity; - - -architecture a of std_logic_checker is - constant expect_queue : queue_t := new_queue; -begin - - main : process - variable request_msg : msg_t; - variable reply_msg : msg_t; - variable msg_type : msg_type_t; - begin - receive(net, signal_checker.p_actor, request_msg); - msg_type := message_type(request_msg); - - if msg_type = expect_msg then - push_std_ulogic_vector(expect_queue, pop_std_ulogic_vector(request_msg)); - push_time(expect_queue, pop_time(request_msg)); - push_time(expect_queue, pop_time(request_msg)); - - elsif msg_type = wait_until_idle_msg then - - while not is_empty(expect_queue) loop - if value'event then - wait for 0 ns; - else - wait on value; - end if; - end loop; - - reply_msg := new_msg(wait_until_idle_reply_msg); - reply(net, request_msg, reply_msg); - else - unexpected_msg_type(msg_type); - end if; - - delete(request_msg); - end process; - - monitor : process - variable expected_value : std_logic_vector(value'range); - variable event_time, margin : delay_length; - - impure function margin_suffix return string is - begin - if margin = 0 ns then - return ""; - else - return " +- " & time'image(margin); - end if; - end; - begin - wait on value; - if is_empty(expect_queue) then - error(signal_checker.p_logger, "Unexpected event with value = " & to_string(value)); - else - expected_value := pop_std_ulogic_vector(expect_queue); - event_time := pop_time(expect_queue); - margin := pop_time(expect_queue); - - if value /= expected_value then - error(signal_checker.p_logger, "Got event with wrong value, got " & to_string(value) & - " expected " & to_string(expected_value)); - - elsif now < event_time - margin or now > event_time + margin then - error(signal_checker.p_logger, "Got event at wrong time, occured at " & time'image(now) & - " expected at " & time'image(event_time) & margin_suffix); - - else - pass(signal_checker.p_logger, "Got expected event with value = " & to_string(value)); - end if; - end if; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +context work.vunit_context; +context work.data_types_context; +context work.com_context; +use work.signal_checker_pkg.all; +use work.sync_pkg.all; + +entity std_logic_checker is + generic ( + signal_checker : signal_checker_t); + port ( + value : in std_logic_vector); +end entity; + + +architecture a of std_logic_checker is + constant expect_queue : queue_t := new_queue; +begin + + main : process + variable request_msg : msg_t; + variable reply_msg : msg_t; + variable msg_type : msg_type_t; + begin + receive(net, signal_checker.p_actor, request_msg); + msg_type := message_type(request_msg); + + if msg_type = expect_msg then + push_std_ulogic_vector(expect_queue, pop_std_ulogic_vector(request_msg)); + push_time(expect_queue, pop_time(request_msg)); + push_time(expect_queue, pop_time(request_msg)); + + elsif msg_type = wait_until_idle_msg then + + while not is_empty(expect_queue) loop + if value'event then + wait for 0 ns; + else + wait on value; + end if; + end loop; + + reply_msg := new_msg(wait_until_idle_reply_msg); + reply(net, request_msg, reply_msg); + else + unexpected_msg_type(msg_type); + end if; + + delete(request_msg); + end process; + + monitor : process + variable expected_value : std_logic_vector(value'range); + variable event_time, margin : delay_length; + + impure function margin_suffix return string is + begin + if margin = 0 ns then + return ""; + else + return " +- " & time'image(margin); + end if; + end; + begin + wait on value; + if is_empty(expect_queue) then + error(signal_checker.p_logger, "Unexpected event with value = " & to_string(value)); + else + expected_value := pop_std_ulogic_vector(expect_queue); + event_time := pop_time(expect_queue); + margin := pop_time(expect_queue); + + if value /= expected_value then + error(signal_checker.p_logger, "Got event with wrong value, got " & to_string(value) & + " expected " & to_string(expected_value)); + + elsif now < event_time - margin or now > event_time + margin then + error(signal_checker.p_logger, "Got event at wrong time, occured at " & time'image(now) & + " expected at " & time'image(event_time) & margin_suffix); + + else + pass(signal_checker.p_logger, "Got expected event with value = " & to_string(value)); + end if; + end if; + end process; +end architecture; diff --git a/vunit/vhdl/verification_components/src/stream_master_pkg-body.vhd b/vunit/vhdl/verification_components/src/stream_master_pkg-body.vhd index d4e515e18..5de0f2eca 100644 --- a/vunit/vhdl/verification_components/src/stream_master_pkg-body.vhd +++ b/vunit/vhdl/verification_components/src/stream_master_pkg-body.vhd @@ -1,31 +1,31 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -context work.vunit_context; -context work.com_context; - -package body stream_master_pkg is - impure function new_stream_master return stream_master_t is - begin - return (p_actor => new_actor); - end; - - procedure push_stream(signal net : inout network_t; - stream : stream_master_t; - data : std_logic_vector; - last : boolean := false) is - variable msg : msg_t := new_msg(stream_push_msg); - constant normalized_data : std_logic_vector(data'length-1 downto 0) := data; - begin - push_std_ulogic_vector(msg, normalized_data); - push_boolean(msg, last); - send(net, stream.p_actor, msg); - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +context work.vunit_context; +context work.com_context; + +package body stream_master_pkg is + impure function new_stream_master return stream_master_t is + begin + return (p_actor => new_actor); + end; + + procedure push_stream(signal net : inout network_t; + stream : stream_master_t; + data : std_logic_vector; + last : boolean := false) is + variable msg : msg_t := new_msg(stream_push_msg); + constant normalized_data : std_logic_vector(data'length-1 downto 0) := data; + begin + push_std_ulogic_vector(msg, normalized_data); + push_boolean(msg, last); + send(net, stream.p_actor, msg); + end; + +end package body; diff --git a/vunit/vhdl/verification_components/src/stream_master_pkg.vhd b/vunit/vhdl/verification_components/src/stream_master_pkg.vhd index c24594b8c..9a5d6c15e 100644 --- a/vunit/vhdl/verification_components/src/stream_master_pkg.vhd +++ b/vunit/vhdl/verification_components/src/stream_master_pkg.vhd @@ -1,32 +1,32 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- Stream master verification component interface - -library ieee; -use ieee.std_logic_1164.all; - -context work.vunit_context; -context work.com_context; - -package stream_master_pkg is - -- Stream master handle - type stream_master_t is record - p_actor : actor_t; - end record; - - -- Create a new stream master object - impure function new_stream_master return stream_master_t; - - -- Push a data value to the stream - procedure push_stream(signal net : inout network_t; - stream : stream_master_t; - data : std_logic_vector; - last : boolean := false); - - -- Message type definitions used by VC implementing stream master VCI - constant stream_push_msg : msg_type_t := new_msg_type("stream push"); -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- Stream master verification component interface + +library ieee; +use ieee.std_logic_1164.all; + +context work.vunit_context; +context work.com_context; + +package stream_master_pkg is + -- Stream master handle + type stream_master_t is record + p_actor : actor_t; + end record; + + -- Create a new stream master object + impure function new_stream_master return stream_master_t; + + -- Push a data value to the stream + procedure push_stream(signal net : inout network_t; + stream : stream_master_t; + data : std_logic_vector; + last : boolean := false); + + -- Message type definitions used by VC implementing stream master VCI + constant stream_push_msg : msg_type_t := new_msg_type("stream push"); +end package; diff --git a/vunit/vhdl/verification_components/src/stream_slave_pkg-body.vhd b/vunit/vhdl/verification_components/src/stream_slave_pkg-body.vhd index ca3c043f0..10f0862ad 100644 --- a/vunit/vhdl/verification_components/src/stream_slave_pkg-body.vhd +++ b/vunit/vhdl/verification_components/src/stream_slave_pkg-body.vhd @@ -1,76 +1,76 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -package body stream_slave_pkg is - impure function new_stream_slave return stream_slave_t is - begin - return (p_actor => new_actor); - end; - - procedure pop_stream(signal net : inout network_t; - stream : stream_slave_t; - variable reference : inout stream_reference_t) is - begin - reference := new_msg(stream_pop_msg); - send(net, stream.p_actor, reference); - end; - - procedure await_pop_stream_reply(signal net : inout network_t; - variable reference : inout stream_reference_t; - variable data : out std_logic_vector; - variable last : out boolean) is - variable reply_msg : msg_t; - begin - receive_reply(net, reference, reply_msg); - data := pop_std_ulogic_vector(reply_msg); - last := pop_boolean(reply_msg); - delete(reference); - delete(reply_msg); - end; - - procedure await_pop_stream_reply(signal net : inout network_t; - variable reference : inout stream_reference_t; - variable data : out std_logic_vector) is - variable reply_msg : msg_t; - begin - receive_reply(net, reference, reply_msg); - data := pop_std_ulogic_vector(reply_msg); - delete(reference); - delete(reply_msg); - end; - - procedure pop_stream(signal net : inout network_t; - stream : stream_slave_t; - variable data : out std_logic_vector; - variable last : out boolean) is - variable reference : stream_reference_t; - begin - pop_stream(net, stream, reference); - await_pop_stream_reply(net, reference, data, last); - end; - - procedure pop_stream(signal net : inout network_t; - stream : stream_slave_t; - variable data : out std_logic_vector) is - variable reference : stream_reference_t; - begin - pop_stream(net, stream, reference); - await_pop_stream_reply(net, reference, data); - end; - - procedure check_stream(signal net : inout network_t; - stream : stream_slave_t; - expected : std_logic_vector; - last : boolean := false; - msg : string := "") is - variable got_data : std_logic_vector(expected'range); - variable got_last : boolean; - begin - pop_stream(net, stream, got_data, got_last); - check_equal(got_data, expected, msg); - check_equal(got_last, last, msg); - end procedure; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +package body stream_slave_pkg is + impure function new_stream_slave return stream_slave_t is + begin + return (p_actor => new_actor); + end; + + procedure pop_stream(signal net : inout network_t; + stream : stream_slave_t; + variable reference : inout stream_reference_t) is + begin + reference := new_msg(stream_pop_msg); + send(net, stream.p_actor, reference); + end; + + procedure await_pop_stream_reply(signal net : inout network_t; + variable reference : inout stream_reference_t; + variable data : out std_logic_vector; + variable last : out boolean) is + variable reply_msg : msg_t; + begin + receive_reply(net, reference, reply_msg); + data := pop_std_ulogic_vector(reply_msg); + last := pop_boolean(reply_msg); + delete(reference); + delete(reply_msg); + end; + + procedure await_pop_stream_reply(signal net : inout network_t; + variable reference : inout stream_reference_t; + variable data : out std_logic_vector) is + variable reply_msg : msg_t; + begin + receive_reply(net, reference, reply_msg); + data := pop_std_ulogic_vector(reply_msg); + delete(reference); + delete(reply_msg); + end; + + procedure pop_stream(signal net : inout network_t; + stream : stream_slave_t; + variable data : out std_logic_vector; + variable last : out boolean) is + variable reference : stream_reference_t; + begin + pop_stream(net, stream, reference); + await_pop_stream_reply(net, reference, data, last); + end; + + procedure pop_stream(signal net : inout network_t; + stream : stream_slave_t; + variable data : out std_logic_vector) is + variable reference : stream_reference_t; + begin + pop_stream(net, stream, reference); + await_pop_stream_reply(net, reference, data); + end; + + procedure check_stream(signal net : inout network_t; + stream : stream_slave_t; + expected : std_logic_vector; + last : boolean := false; + msg : string := "") is + variable got_data : std_logic_vector(expected'range); + variable got_last : boolean; + begin + pop_stream(net, stream, got_data, got_last); + check_equal(got_data, expected, msg); + check_equal(got_last, last, msg); + end procedure; +end package body; diff --git a/vunit/vhdl/verification_components/src/stream_slave_pkg.vhd b/vunit/vhdl/verification_components/src/stream_slave_pkg.vhd index d6b99b6ca..66f001fcb 100644 --- a/vunit/vhdl/verification_components/src/stream_slave_pkg.vhd +++ b/vunit/vhdl/verification_components/src/stream_slave_pkg.vhd @@ -1,61 +1,61 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- Stream slave verification component interface - -library ieee; -use ieee.std_logic_1164.all; - -context work.vunit_context; -context work.com_context; - -package stream_slave_pkg is - -- Stream slave handle - type stream_slave_t is record - p_actor : actor_t; - end record; - - -- Create a new stream slave object - impure function new_stream_slave return stream_slave_t; - - -- Reference to future stream result - alias stream_reference_t is msg_t; - - -- Blocking: pop a value from the stream - procedure pop_stream(signal net : inout network_t; - stream : stream_slave_t; - variable data : out std_logic_vector; - variable last : out boolean); - - procedure pop_stream(signal net : inout network_t; - stream : stream_slave_t; - variable data : out std_logic_vector); - - -- Non-blocking: pop a value from the stream to be read in the future - procedure pop_stream(signal net : inout network_t; - stream : stream_slave_t; - variable reference : inout stream_reference_t); - - -- Blocking: Wait for reply to non-blocking pop - procedure await_pop_stream_reply(signal net : inout network_t; - variable reference : inout stream_reference_t; - variable data : out std_logic_vector; - variable last : out boolean); - - procedure await_pop_stream_reply(signal net : inout network_t; - variable reference : inout stream_reference_t; - variable data : out std_logic_vector); - - -- Blocking: read stream and check result against expected value - procedure check_stream(signal net : inout network_t; - stream : stream_slave_t; - expected : std_logic_vector; - last : boolean := false; - msg : string := ""); - - -- Message type definitions used by VC implementing stream slave VCI - constant stream_pop_msg : msg_type_t := new_msg_type("stream pop"); -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- Stream slave verification component interface + +library ieee; +use ieee.std_logic_1164.all; + +context work.vunit_context; +context work.com_context; + +package stream_slave_pkg is + -- Stream slave handle + type stream_slave_t is record + p_actor : actor_t; + end record; + + -- Create a new stream slave object + impure function new_stream_slave return stream_slave_t; + + -- Reference to future stream result + alias stream_reference_t is msg_t; + + -- Blocking: pop a value from the stream + procedure pop_stream(signal net : inout network_t; + stream : stream_slave_t; + variable data : out std_logic_vector; + variable last : out boolean); + + procedure pop_stream(signal net : inout network_t; + stream : stream_slave_t; + variable data : out std_logic_vector); + + -- Non-blocking: pop a value from the stream to be read in the future + procedure pop_stream(signal net : inout network_t; + stream : stream_slave_t; + variable reference : inout stream_reference_t); + + -- Blocking: Wait for reply to non-blocking pop + procedure await_pop_stream_reply(signal net : inout network_t; + variable reference : inout stream_reference_t; + variable data : out std_logic_vector; + variable last : out boolean); + + procedure await_pop_stream_reply(signal net : inout network_t; + variable reference : inout stream_reference_t; + variable data : out std_logic_vector); + + -- Blocking: read stream and check result against expected value + procedure check_stream(signal net : inout network_t; + stream : stream_slave_t; + expected : std_logic_vector; + last : boolean := false; + msg : string := ""); + + -- Message type definitions used by VC implementing stream slave VCI + constant stream_pop_msg : msg_type_t := new_msg_type("stream pop"); +end package; diff --git a/vunit/vhdl/verification_components/src/sync_pkg-body.vhd b/vunit/vhdl/verification_components/src/sync_pkg-body.vhd index a32372520..d32e4256a 100644 --- a/vunit/vhdl/verification_components/src/sync_pkg-body.vhd +++ b/vunit/vhdl/verification_components/src/sync_pkg-body.vhd @@ -1,63 +1,63 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -context work.vunit_context; -use work.queue_pkg.all; -context work.com_context; - -package body sync_pkg is - procedure wait_until_idle(signal net : inout network_t; - handle : sync_handle_t) is - variable msg, reply_msg : msg_t; - begin - msg := new_msg(wait_until_idle_msg); - request(net, handle, msg, reply_msg); - delete(reply_msg); - end; - - procedure wait_for_time(signal net : inout network_t; - handle : sync_handle_t; - delay : delay_length) is - variable msg : msg_t; - begin - msg := new_msg(wait_for_time_msg); - push_time(msg, delay); - send(net, handle, msg); - end; - - procedure handle_wait_until_idle(signal net : inout network_t; - variable msg_type : inout msg_type_t; - variable msg : inout msg_t) is - variable reply_msg : msg_t; - begin - if msg_type = wait_until_idle_msg then - handle_message(msg_type); - reply_msg := new_msg(wait_until_idle_reply_msg); - reply(net, msg, reply_msg); - end if; - end; - - procedure handle_wait_for_time(signal net : inout network_t; - variable msg_type : inout msg_type_t; - variable msg : inout msg_t) is - variable delay : delay_length; - begin - if msg_type = wait_for_time_msg then - handle_message(msg_type); - delay := pop_time(msg); - wait for delay; - end if; - end; - - procedure handle_sync_message(signal net : inout network_t; - variable msg_type : inout msg_type_t; - variable msg : inout msg_t) is - begin - handle_wait_until_idle(net, msg_type, msg); - handle_wait_for_time(net, msg_type, msg); - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +context work.vunit_context; +use work.queue_pkg.all; +context work.com_context; + +package body sync_pkg is + procedure wait_until_idle(signal net : inout network_t; + handle : sync_handle_t) is + variable msg, reply_msg : msg_t; + begin + msg := new_msg(wait_until_idle_msg); + request(net, handle, msg, reply_msg); + delete(reply_msg); + end; + + procedure wait_for_time(signal net : inout network_t; + handle : sync_handle_t; + delay : delay_length) is + variable msg : msg_t; + begin + msg := new_msg(wait_for_time_msg); + push_time(msg, delay); + send(net, handle, msg); + end; + + procedure handle_wait_until_idle(signal net : inout network_t; + variable msg_type : inout msg_type_t; + variable msg : inout msg_t) is + variable reply_msg : msg_t; + begin + if msg_type = wait_until_idle_msg then + handle_message(msg_type); + reply_msg := new_msg(wait_until_idle_reply_msg); + reply(net, msg, reply_msg); + end if; + end; + + procedure handle_wait_for_time(signal net : inout network_t; + variable msg_type : inout msg_type_t; + variable msg : inout msg_t) is + variable delay : delay_length; + begin + if msg_type = wait_for_time_msg then + handle_message(msg_type); + delay := pop_time(msg); + wait for delay; + end if; + end; + + procedure handle_sync_message(signal net : inout network_t; + variable msg_type : inout msg_type_t; + variable msg : inout msg_t) is + begin + handle_wait_until_idle(net, msg_type, msg); + handle_wait_for_time(net, msg_type, msg); + end; + +end package body; diff --git a/vunit/vhdl/verification_components/src/sync_pkg.vhd b/vunit/vhdl/verification_components/src/sync_pkg.vhd index 9f858c466..651d1aa94 100644 --- a/vunit/vhdl/verification_components/src/sync_pkg.vhd +++ b/vunit/vhdl/verification_components/src/sync_pkg.vhd @@ -1,45 +1,45 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- Defines synchronization verification component interface VCI - -context work.vunit_context; -context work.com_context; - -package sync_pkg is - - -- Handle to talk to a VC implementing the sync VCI - alias sync_handle_t is actor_t; - - -- Blocking: Wait until all operations requested from the VC have been finished - procedure wait_until_idle(signal net : inout network_t; - handle : sync_handle_t); - - -- Non-blocking: Make VC wait for a delay before starting the next operation - procedure wait_for_time(signal net : inout network_t; - handle : sync_handle_t; - delay : delay_length); - - -- Message type definitions used by VC implementing the synchronization VCI - constant wait_until_idle_msg : msg_type_t := new_msg_type("wait until idle"); - constant wait_until_idle_reply_msg : msg_type_t := new_msg_type("wait until idle reply"); - constant wait_for_time_msg : msg_type_t := new_msg_type("wait for time"); - - -- Standard implementation of wait_until_idle VCI which may be used by VC - procedure handle_wait_until_idle(signal net : inout network_t; - variable msg_type : inout msg_type_t; - variable msg : inout msg_t); - - -- Standard implementation of wait_for_time VCI which may be used by VC - procedure handle_wait_for_time(signal net : inout network_t; - variable msg_type : inout msg_type_t; - variable msg : inout msg_t); - - -- Standard implementation of synchronization VCI which may be used by VC - procedure handle_sync_message(signal net : inout network_t; - variable msg_type : inout msg_type_t; - variable msg : inout msg_t); -end package; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- Defines synchronization verification component interface VCI + +context work.vunit_context; +context work.com_context; + +package sync_pkg is + + -- Handle to talk to a VC implementing the sync VCI + alias sync_handle_t is actor_t; + + -- Blocking: Wait until all operations requested from the VC have been finished + procedure wait_until_idle(signal net : inout network_t; + handle : sync_handle_t); + + -- Non-blocking: Make VC wait for a delay before starting the next operation + procedure wait_for_time(signal net : inout network_t; + handle : sync_handle_t; + delay : delay_length); + + -- Message type definitions used by VC implementing the synchronization VCI + constant wait_until_idle_msg : msg_type_t := new_msg_type("wait until idle"); + constant wait_until_idle_reply_msg : msg_type_t := new_msg_type("wait until idle reply"); + constant wait_for_time_msg : msg_type_t := new_msg_type("wait for time"); + + -- Standard implementation of wait_until_idle VCI which may be used by VC + procedure handle_wait_until_idle(signal net : inout network_t; + variable msg_type : inout msg_type_t; + variable msg : inout msg_t); + + -- Standard implementation of wait_for_time VCI which may be used by VC + procedure handle_wait_for_time(signal net : inout network_t; + variable msg_type : inout msg_type_t; + variable msg : inout msg_t); + + -- Standard implementation of synchronization VCI which may be used by VC + procedure handle_sync_message(signal net : inout network_t; + variable msg_type : inout msg_type_t; + variable msg : inout msg_t); +end package; diff --git a/vunit/vhdl/verification_components/src/uart_master.vhd b/vunit/vhdl/verification_components/src/uart_master.vhd index bf40bca39..ef9da5e09 100644 --- a/vunit/vhdl/verification_components/src/uart_master.vhd +++ b/vunit/vhdl/verification_components/src/uart_master.vhd @@ -1,67 +1,67 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -library vunit_lib; -context vunit_lib.vunit_context; -context vunit_lib.com_context; -use vunit_lib.stream_master_pkg.all; -use vunit_lib.uart_pkg.all; -use vunit_lib.queue_pkg.all; -use vunit_lib.sync_pkg.all; - -entity uart_master is - generic ( - uart : uart_master_t); - port ( - tx : out std_logic := uart.p_idle_state); -end entity; - -architecture a of uart_master is -begin - - main : process - procedure uart_send(data : std_logic_vector; - signal tx : out std_logic; - baud_rate : integer) is - constant time_per_bit : time := (10**9 / baud_rate) * 1 ns; - - procedure send_bit(value : std_logic) is - begin - tx <= value; - wait for time_per_bit; - end procedure; - - begin - debug("Sending " & to_string(data)); - send_bit(not uart.p_idle_state); - for i in 0 to data'length-1 loop - send_bit(data(i)); - end loop; - send_bit(uart.p_idle_state); - end procedure; - - variable msg : msg_t; - variable baud_rate : natural := uart.p_baud_rate; - variable msg_type : msg_type_t; - begin - receive(net, uart.p_actor, msg); - msg_type := message_type(msg); - - handle_sync_message(net, msg_type, msg); - - if msg_type = stream_push_msg then - uart_send(pop_std_ulogic_vector(msg), tx, baud_rate); - elsif msg_type = uart_set_baud_rate_msg then - baud_rate := pop(msg); - else - unexpected_msg_type(msg_type); - end if; - end process; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +use vunit_lib.stream_master_pkg.all; +use vunit_lib.uart_pkg.all; +use vunit_lib.queue_pkg.all; +use vunit_lib.sync_pkg.all; + +entity uart_master is + generic ( + uart : uart_master_t); + port ( + tx : out std_logic := uart.p_idle_state); +end entity; + +architecture a of uart_master is +begin + + main : process + procedure uart_send(data : std_logic_vector; + signal tx : out std_logic; + baud_rate : integer) is + constant time_per_bit : time := (10**9 / baud_rate) * 1 ns; + + procedure send_bit(value : std_logic) is + begin + tx <= value; + wait for time_per_bit; + end procedure; + + begin + debug("Sending " & to_string(data)); + send_bit(not uart.p_idle_state); + for i in 0 to data'length-1 loop + send_bit(data(i)); + end loop; + send_bit(uart.p_idle_state); + end procedure; + + variable msg : msg_t; + variable baud_rate : natural := uart.p_baud_rate; + variable msg_type : msg_type_t; + begin + receive(net, uart.p_actor, msg); + msg_type := message_type(msg); + + handle_sync_message(net, msg_type, msg); + + if msg_type = stream_push_msg then + uart_send(pop_std_ulogic_vector(msg), tx, baud_rate); + elsif msg_type = uart_set_baud_rate_msg then + baud_rate := pop(msg); + else + unexpected_msg_type(msg_type); + end if; + end process; + +end architecture; diff --git a/vunit/vhdl/verification_components/src/uart_pkg.vhd b/vunit/vhdl/verification_components/src/uart_pkg.vhd index e548d23ea..3cb944115 100644 --- a/vunit/vhdl/verification_components/src/uart_pkg.vhd +++ b/vunit/vhdl/verification_components/src/uart_pkg.vhd @@ -1,119 +1,119 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -context work.com_context; -use work.stream_master_pkg.all; -use work.stream_slave_pkg.all; -use work.sync_pkg.all; -use work.integer_vector_ptr_pkg.all; -use work.queue_pkg.all; - -package uart_pkg is - type uart_master_t is record - p_actor : actor_t; - p_baud_rate : natural; - p_idle_state : std_logic; - end record; - - type uart_slave_t is record - p_actor : actor_t; - p_baud_rate : natural; - p_idle_state : std_logic; - p_data_length : positive; - end record; - - -- Set the baud rate [bits/s] - procedure set_baud_rate(signal net : inout network_t; - uart_master : uart_master_t; - baud_rate : natural); - - procedure set_baud_rate(signal net : inout network_t; - uart_slave : uart_slave_t; - baud_rate : natural); - - constant default_baud_rate : natural := 115200; - constant default_idle_state : std_logic := '1'; - constant default_data_length : positive := 8; - impure function new_uart_master(initial_baud_rate : natural := default_baud_rate; - idle_state : std_logic := default_idle_state) return uart_master_t; - impure function new_uart_slave(initial_baud_rate : natural := default_baud_rate; - idle_state : std_logic := default_idle_state; - data_length : positive := default_data_length) return uart_slave_t; - - impure function as_stream(uart_master : uart_master_t) return stream_master_t; - impure function as_stream(uart_slave : uart_slave_t) return stream_slave_t; - impure function as_sync(uart_master : uart_master_t) return sync_handle_t; - impure function as_sync(uart_slave : uart_slave_t) return sync_handle_t; - - constant uart_set_baud_rate_msg : msg_type_t := new_msg_type("uart set baud rate"); -end package; - -package body uart_pkg is - - impure function new_uart_master(initial_baud_rate : natural := default_baud_rate; - idle_state : std_logic := default_idle_state) return uart_master_t is - begin - return (p_actor => new_actor, - p_baud_rate => initial_baud_rate, - p_idle_state => idle_state); - end; - - impure function new_uart_slave(initial_baud_rate : natural := default_baud_rate; - idle_state : std_logic := default_idle_state; - data_length : positive := default_data_length) return uart_slave_t is - begin - return (p_actor => new_actor, - p_baud_rate => initial_baud_rate, - p_idle_state => idle_state, - p_data_length => data_length); - end; - - impure function as_stream(uart_master : uart_master_t) return stream_master_t is - begin - return stream_master_t'(p_actor => uart_master.p_actor); - end; - - impure function as_stream(uart_slave : uart_slave_t) return stream_slave_t is - begin - return stream_slave_t'(p_actor => uart_slave.p_actor); - end; - - impure function as_sync(uart_master : uart_master_t) return sync_handle_t is - begin - return uart_master.p_actor; - end; - - impure function as_sync(uart_slave : uart_slave_t) return sync_handle_t is - begin - return uart_slave.p_actor; - end; - - procedure set_baud_rate(signal net : inout network_t; - actor : actor_t; - baud_rate : natural) is - variable msg : msg_t := new_msg(uart_set_baud_rate_msg); - begin - push(msg, baud_rate); - send(net, actor, msg); - end; - - procedure set_baud_rate(signal net : inout network_t; - uart_master : uart_master_t; - baud_rate : natural) is - begin - set_baud_rate(net, uart_master.p_actor, baud_rate); - end; - - procedure set_baud_rate(signal net : inout network_t; - uart_slave : uart_slave_t; - baud_rate : natural) is - begin - set_baud_rate(net, uart_slave.p_actor, baud_rate); - end; -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +context work.com_context; +use work.stream_master_pkg.all; +use work.stream_slave_pkg.all; +use work.sync_pkg.all; +use work.integer_vector_ptr_pkg.all; +use work.queue_pkg.all; + +package uart_pkg is + type uart_master_t is record + p_actor : actor_t; + p_baud_rate : natural; + p_idle_state : std_logic; + end record; + + type uart_slave_t is record + p_actor : actor_t; + p_baud_rate : natural; + p_idle_state : std_logic; + p_data_length : positive; + end record; + + -- Set the baud rate [bits/s] + procedure set_baud_rate(signal net : inout network_t; + uart_master : uart_master_t; + baud_rate : natural); + + procedure set_baud_rate(signal net : inout network_t; + uart_slave : uart_slave_t; + baud_rate : natural); + + constant default_baud_rate : natural := 115200; + constant default_idle_state : std_logic := '1'; + constant default_data_length : positive := 8; + impure function new_uart_master(initial_baud_rate : natural := default_baud_rate; + idle_state : std_logic := default_idle_state) return uart_master_t; + impure function new_uart_slave(initial_baud_rate : natural := default_baud_rate; + idle_state : std_logic := default_idle_state; + data_length : positive := default_data_length) return uart_slave_t; + + impure function as_stream(uart_master : uart_master_t) return stream_master_t; + impure function as_stream(uart_slave : uart_slave_t) return stream_slave_t; + impure function as_sync(uart_master : uart_master_t) return sync_handle_t; + impure function as_sync(uart_slave : uart_slave_t) return sync_handle_t; + + constant uart_set_baud_rate_msg : msg_type_t := new_msg_type("uart set baud rate"); +end package; + +package body uart_pkg is + + impure function new_uart_master(initial_baud_rate : natural := default_baud_rate; + idle_state : std_logic := default_idle_state) return uart_master_t is + begin + return (p_actor => new_actor, + p_baud_rate => initial_baud_rate, + p_idle_state => idle_state); + end; + + impure function new_uart_slave(initial_baud_rate : natural := default_baud_rate; + idle_state : std_logic := default_idle_state; + data_length : positive := default_data_length) return uart_slave_t is + begin + return (p_actor => new_actor, + p_baud_rate => initial_baud_rate, + p_idle_state => idle_state, + p_data_length => data_length); + end; + + impure function as_stream(uart_master : uart_master_t) return stream_master_t is + begin + return stream_master_t'(p_actor => uart_master.p_actor); + end; + + impure function as_stream(uart_slave : uart_slave_t) return stream_slave_t is + begin + return stream_slave_t'(p_actor => uart_slave.p_actor); + end; + + impure function as_sync(uart_master : uart_master_t) return sync_handle_t is + begin + return uart_master.p_actor; + end; + + impure function as_sync(uart_slave : uart_slave_t) return sync_handle_t is + begin + return uart_slave.p_actor; + end; + + procedure set_baud_rate(signal net : inout network_t; + actor : actor_t; + baud_rate : natural) is + variable msg : msg_t := new_msg(uart_set_baud_rate_msg); + begin + push(msg, baud_rate); + send(net, actor, msg); + end; + + procedure set_baud_rate(signal net : inout network_t; + uart_master : uart_master_t; + baud_rate : natural) is + begin + set_baud_rate(net, uart_master.p_actor, baud_rate); + end; + + procedure set_baud_rate(signal net : inout network_t; + uart_slave : uart_slave_t; + baud_rate : natural) is + begin + set_baud_rate(net, uart_slave.p_actor, baud_rate); + end; +end package body; diff --git a/vunit/vhdl/verification_components/src/uart_slave.vhd b/vunit/vhdl/verification_components/src/uart_slave.vhd index 5444743aa..062c61fbf 100644 --- a/vunit/vhdl/verification_components/src/uart_slave.vhd +++ b/vunit/vhdl/verification_components/src/uart_slave.vhd @@ -1,85 +1,85 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -library vunit_lib; -context vunit_lib.vunit_context; -context vunit_lib.com_context; -use vunit_lib.stream_slave_pkg.all; -use vunit_lib.uart_pkg.all; -use vunit_lib.queue_pkg.all; - -entity uart_slave is - generic ( - uart : uart_slave_t); - port ( - rx : in std_logic); -end entity; - -architecture a of uart_slave is - signal baud_rate : natural := uart.p_baud_rate; - signal local_event : std_logic := '0'; - constant data_queue : queue_t := new_queue; -begin - - main : process - variable reply_msg, msg : msg_t; - variable msg_type : msg_type_t; - begin - receive(net, uart.p_actor, msg); - msg_type := message_type(msg); - - if msg_type = uart_set_baud_rate_msg then - baud_rate <= pop(msg); - - elsif msg_type = stream_pop_msg then - reply_msg := new_msg; - if not (length(data_queue) > 0) then - wait on local_event until length(data_queue) > 0; - end if; - push_std_ulogic_vector(reply_msg, pop_std_ulogic_vector(data_queue)); - push_boolean(reply_msg, false); - reply(net, msg, reply_msg); - - else - unexpected_msg_type(msg_type); - end if; - - end process; - - recv : process - procedure uart_recv(variable data : out std_logic_vector; - signal rx : in std_logic; - baud_rate : integer) is - constant time_per_bit : time := (10**9 / baud_rate) * 1 ns; - constant time_per_half_bit : time := (10**9 / (2*baud_rate)) * 1 ns; - begin - wait for time_per_half_bit; -- middle of start bit - assert rx = not uart.p_idle_state; - wait for time_per_bit; -- skip start bit - - for i in 0 to data'length-1 loop - data(i) := rx; - wait for time_per_bit; - end loop; - - assert rx = uart.p_idle_state; - end procedure; - - variable data : std_logic_vector(uart.p_data_length-1 downto 0); - begin - wait on rx until rx = not uart.p_idle_state; - uart_recv(data, rx, baud_rate); - push_std_ulogic_vector(data_queue, data); - local_event <= '1'; - wait for 0 ns; - local_event <= '0'; - wait for 0 ns; - end process; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +use vunit_lib.stream_slave_pkg.all; +use vunit_lib.uart_pkg.all; +use vunit_lib.queue_pkg.all; + +entity uart_slave is + generic ( + uart : uart_slave_t); + port ( + rx : in std_logic); +end entity; + +architecture a of uart_slave is + signal baud_rate : natural := uart.p_baud_rate; + signal local_event : std_logic := '0'; + constant data_queue : queue_t := new_queue; +begin + + main : process + variable reply_msg, msg : msg_t; + variable msg_type : msg_type_t; + begin + receive(net, uart.p_actor, msg); + msg_type := message_type(msg); + + if msg_type = uart_set_baud_rate_msg then + baud_rate <= pop(msg); + + elsif msg_type = stream_pop_msg then + reply_msg := new_msg; + if not (length(data_queue) > 0) then + wait on local_event until length(data_queue) > 0; + end if; + push_std_ulogic_vector(reply_msg, pop_std_ulogic_vector(data_queue)); + push_boolean(reply_msg, false); + reply(net, msg, reply_msg); + + else + unexpected_msg_type(msg_type); + end if; + + end process; + + recv : process + procedure uart_recv(variable data : out std_logic_vector; + signal rx : in std_logic; + baud_rate : integer) is + constant time_per_bit : time := (10**9 / baud_rate) * 1 ns; + constant time_per_half_bit : time := (10**9 / (2*baud_rate)) * 1 ns; + begin + wait for time_per_half_bit; -- middle of start bit + assert rx = not uart.p_idle_state; + wait for time_per_bit; -- skip start bit + + for i in 0 to data'length-1 loop + data(i) := rx; + wait for time_per_bit; + end loop; + + assert rx = uart.p_idle_state; + end procedure; + + variable data : std_logic_vector(uart.p_data_length-1 downto 0); + begin + wait on rx until rx = not uart.p_idle_state; + uart_recv(data, rx, baud_rate); + push_std_ulogic_vector(data_queue, data); + local_event <= '1'; + wait for 0 ns; + local_event <= '0'; + wait for 0 ns; + end process; + +end architecture; diff --git a/vunit/vhdl/verification_components/src/vc_context.vhd b/vunit/vhdl/verification_components/src/vc_context.vhd index 07b979ca7..3ea436584 100644 --- a/vunit/vhdl/verification_components/src/vc_context.vhd +++ b/vunit/vhdl/verification_components/src/vc_context.vhd @@ -1,24 +1,24 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -context vc_context is - library vunit_lib; - use vunit_lib.avalon_pkg.all; - use vunit_lib.avalon_stream_pkg.all; - use vunit_lib.bus_master_pkg.all; - use vunit_lib.axi_pkg.all; - use vunit_lib.axi_slave_pkg.all; - use vunit_lib.axi_statistics_pkg.all; - use vunit_lib.axi_stream_pkg.all; - use vunit_lib.memory_pkg.all; - use vunit_lib.memory_utils_pkg.all; - use vunit_lib.stream_master_pkg.all; - use vunit_lib.stream_slave_pkg.all; - use vunit_lib.sync_pkg.all; - use vunit_lib.uart_pkg.all; - use vunit_lib.wishbone_pkg.all; - context vunit_lib.com_context; -end context; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +context vc_context is + library vunit_lib; + use vunit_lib.avalon_pkg.all; + use vunit_lib.avalon_stream_pkg.all; + use vunit_lib.bus_master_pkg.all; + use vunit_lib.axi_pkg.all; + use vunit_lib.axi_slave_pkg.all; + use vunit_lib.axi_statistics_pkg.all; + use vunit_lib.axi_stream_pkg.all; + use vunit_lib.memory_pkg.all; + use vunit_lib.memory_utils_pkg.all; + use vunit_lib.stream_master_pkg.all; + use vunit_lib.stream_slave_pkg.all; + use vunit_lib.sync_pkg.all; + use vunit_lib.uart_pkg.all; + use vunit_lib.wishbone_pkg.all; + context vunit_lib.com_context; +end context; diff --git a/vunit/vhdl/verification_components/src/wishbone_master.vhd b/vunit/vhdl/verification_components/src/wishbone_master.vhd index 429837d0f..bd9a63595 100644 --- a/vunit/vhdl/verification_components/src/wishbone_master.vhd +++ b/vunit/vhdl/verification_components/src/wishbone_master.vhd @@ -1,157 +1,157 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com --- Author Slawomir Siluk slaweksiluk@gazeta.pl --- Wishbome Master BFM for pipelined block transfers - -library ieee; -use ieee.std_logic_1164.all; - -use work.queue_pkg.all; -use work.bus_master_pkg.all; -context work.com_context; -use work.com_types_pkg.all; -use work.logger_pkg.all; -use work.check_pkg.all; -use work.log_levels_pkg.all; -use work.sync_pkg.all; - -library osvvm; -use osvvm.RandomPkg.all; - -entity wishbone_master is - generic ( - bus_handle : bus_master_t; - strobe_high_probability : real range 0.0 to 1.0 := 1.0 - ); - port ( - clk : in std_logic; - adr : out std_logic_vector; - dat_i : in std_logic_vector; - dat_o : out std_logic_vector; - sel : out std_logic_vector; - cyc : out std_logic; - stb : out std_logic; - we : out std_logic; - stall : in std_logic; - ack : in std_logic - ); -end entity; - -architecture a of wishbone_master is - constant rd_request_queue : queue_t := new_queue; - constant wr_request_queue : queue_t := new_queue; - constant acknowledge_queue : queue_t := new_queue; - constant bus_ack_msg : msg_type_t := new_msg_type("wb master ack msg"); - constant wb_master_ack_actor : actor_t := new_actor; - signal start_cycle : std_logic := '0'; - signal end_cycle : std_logic := '0'; - signal cycle : boolean; -begin - - main : process - variable request_msg : msg_t; - variable msg_type : msg_type_t; - variable cycle_type : msg_type_t; - variable rnd : RandomPType; - begin - rnd.InitSeed(rnd'instance_name); - report rnd'instance_name; - request_msg := null_msg; - cycle_type := bus_read_msg; - stb <= '0'; - - wait until rising_edge(clk); - loop - receive(net, bus_handle.p_actor, request_msg); - msg_type := message_type(request_msg); - - if msg_type = bus_read_msg or msg_type = bus_write_msg then - if msg_type /= cycle_type and cycle then - wait until not cycle; -- todo: is this necessary? the wb spec v4 does not explicitly forbid mixed cycles - wait until rising_edge(clk); - end if; - - start_cycle <= not start_cycle; - cycle_type := msg_type; - - while rnd.Uniform(0.0, 1.0) > strobe_high_probability loop - wait until rising_edge(clk); - end loop; - adr <= pop_std_ulogic_vector(request_msg); - stb <= '1'; - if msg_type = bus_write_msg then - we <= '1'; - dat_o <= pop_std_ulogic_vector(request_msg); - sel <= pop_std_ulogic_vector(request_msg); - else - we <= '0'; - -- TODO why sel is not passed in msg for reading (present for writing)? - --sel <= pop_std_ulogic_vector(request_msg); - end if; - wait until rising_edge(clk) and stall = '0'; - stb <= '0'; - - push(acknowledge_queue, request_msg); - - elsif msg_type = wait_until_idle_msg then - if cycle then - wait until not cycle; - end if; - handle_wait_until_idle(net, msg_type, request_msg); - - else - unexpected_msg_type(msg_type); - - end if; - end loop; - end process; - - p_cycle : process - variable request_msg : msg_t; - variable ack_msg : msg_t; - variable pending : natural := 0; - variable received_acks : natural := 0; - begin - cyc <= '0'; - cycle <= false; - - loop - wait until start_cycle'event or end_cycle'event; - - if start_cycle'event then - pending := pending+1; - end if; - if end_cycle'event then - pending := pending-1; - end if; - check_true(pending >= 0, "Pending transactions became negative - internal error", failure); - - if pending > 0 then - cyc <= '1'; - cycle <= true; - else - cyc <= '0'; - cycle <= false; - end if; - end loop; - end process; - - acknowledge : process - variable request_msg, reply_msg, ack_msg : msg_t; - begin - wait until ack = '1' and rising_edge(clk); - request_msg := pop(acknowledge_queue); - -- Reply only on read - if we = '0' then - reply_msg := new_msg(sender => wb_master_ack_actor); - push_std_ulogic_vector(reply_msg, dat_i); - reply(net, request_msg, reply_msg); - end if; - delete(request_msg); - -- Response main sequencer that ack is received - end_cycle <= not end_cycle; - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +-- Author Slawomir Siluk slaweksiluk@gazeta.pl +-- Wishbome Master BFM for pipelined block transfers + +library ieee; +use ieee.std_logic_1164.all; + +use work.queue_pkg.all; +use work.bus_master_pkg.all; +context work.com_context; +use work.com_types_pkg.all; +use work.logger_pkg.all; +use work.check_pkg.all; +use work.log_levels_pkg.all; +use work.sync_pkg.all; + +library osvvm; +use osvvm.RandomPkg.all; + +entity wishbone_master is + generic ( + bus_handle : bus_master_t; + strobe_high_probability : real range 0.0 to 1.0 := 1.0 + ); + port ( + clk : in std_logic; + adr : out std_logic_vector; + dat_i : in std_logic_vector; + dat_o : out std_logic_vector; + sel : out std_logic_vector; + cyc : out std_logic; + stb : out std_logic; + we : out std_logic; + stall : in std_logic; + ack : in std_logic + ); +end entity; + +architecture a of wishbone_master is + constant rd_request_queue : queue_t := new_queue; + constant wr_request_queue : queue_t := new_queue; + constant acknowledge_queue : queue_t := new_queue; + constant bus_ack_msg : msg_type_t := new_msg_type("wb master ack msg"); + constant wb_master_ack_actor : actor_t := new_actor; + signal start_cycle : std_logic := '0'; + signal end_cycle : std_logic := '0'; + signal cycle : boolean; +begin + + main : process + variable request_msg : msg_t; + variable msg_type : msg_type_t; + variable cycle_type : msg_type_t; + variable rnd : RandomPType; + begin + rnd.InitSeed(rnd'instance_name); + report rnd'instance_name; + request_msg := null_msg; + cycle_type := bus_read_msg; + stb <= '0'; + + wait until rising_edge(clk); + loop + receive(net, bus_handle.p_actor, request_msg); + msg_type := message_type(request_msg); + + if msg_type = bus_read_msg or msg_type = bus_write_msg then + if msg_type /= cycle_type and cycle then + wait until not cycle; -- todo: is this necessary? the wb spec v4 does not explicitly forbid mixed cycles + wait until rising_edge(clk); + end if; + + start_cycle <= not start_cycle; + cycle_type := msg_type; + + while rnd.Uniform(0.0, 1.0) > strobe_high_probability loop + wait until rising_edge(clk); + end loop; + adr <= pop_std_ulogic_vector(request_msg); + stb <= '1'; + if msg_type = bus_write_msg then + we <= '1'; + dat_o <= pop_std_ulogic_vector(request_msg); + sel <= pop_std_ulogic_vector(request_msg); + else + we <= '0'; + -- TODO why sel is not passed in msg for reading (present for writing)? + --sel <= pop_std_ulogic_vector(request_msg); + end if; + wait until rising_edge(clk) and stall = '0'; + stb <= '0'; + + push(acknowledge_queue, request_msg); + + elsif msg_type = wait_until_idle_msg then + if cycle then + wait until not cycle; + end if; + handle_wait_until_idle(net, msg_type, request_msg); + + else + unexpected_msg_type(msg_type); + + end if; + end loop; + end process; + + p_cycle : process + variable request_msg : msg_t; + variable ack_msg : msg_t; + variable pending : natural := 0; + variable received_acks : natural := 0; + begin + cyc <= '0'; + cycle <= false; + + loop + wait until start_cycle'event or end_cycle'event; + + if start_cycle'event then + pending := pending+1; + end if; + if end_cycle'event then + pending := pending-1; + end if; + check_true(pending >= 0, "Pending transactions became negative - internal error", failure); + + if pending > 0 then + cyc <= '1'; + cycle <= true; + else + cyc <= '0'; + cycle <= false; + end if; + end loop; + end process; + + acknowledge : process + variable request_msg, reply_msg, ack_msg : msg_t; + begin + wait until ack = '1' and rising_edge(clk); + request_msg := pop(acknowledge_queue); + -- Reply only on read + if we = '0' then + reply_msg := new_msg(sender => wb_master_ack_actor); + push_std_ulogic_vector(reply_msg, dat_i); + reply(net, request_msg, reply_msg); + end if; + delete(request_msg); + -- Response main sequencer that ack is received + end_cycle <= not end_cycle; + end process; +end architecture; diff --git a/vunit/vhdl/verification_components/src/wishbone_pkg.vhd b/vunit/vhdl/verification_components/src/wishbone_pkg.vhd index bec098669..069f0610a 100644 --- a/vunit/vhdl/verification_components/src/wishbone_pkg.vhd +++ b/vunit/vhdl/verification_components/src/wishbone_pkg.vhd @@ -1,54 +1,54 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com --- Author Slawomir Siluk slaweksiluk@gazeta.pl -library ieee; -use ieee.std_logic_1164.all; - -use work.queue_pkg.all; -use work.logger_pkg.all; -use work.memory_pkg.all; -context work.com_context; - -package wishbone_pkg is - - type wishbone_slave_t is record - ack_high_probability : real range 0.0 to 1.0; - stall_high_probability : real range 0.0 to 1.0; - -- Private - p_actor : actor_t; - p_ack_actor : actor_t; - p_memory : memory_t; - p_logger : logger_t; - end record; - - constant wishbone_slave_logger : logger_t := get_logger("vunit_lib:wishbone_slave_pkg"); - impure function new_wishbone_slave( - memory : memory_t; - ack_high_probability : real := 1.0; - stall_high_probability : real := 0.0; - logger : logger_t := wishbone_slave_logger) - return wishbone_slave_t; - -end package; -package body wishbone_pkg is - - impure function new_wishbone_slave( - memory : memory_t; - ack_high_probability : real := 1.0; - stall_high_probability : real := 0.0; - logger : logger_t := wishbone_slave_logger) - return wishbone_slave_t is - begin - return (p_actor => new_actor, - p_ack_actor => new_actor, - p_memory => to_vc_interface(memory, logger), - p_logger => logger, - ack_high_probability => ack_high_probability, - stall_high_probability => stall_high_probability - ); - end; - -end package body; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +-- Author Slawomir Siluk slaweksiluk@gazeta.pl +library ieee; +use ieee.std_logic_1164.all; + +use work.queue_pkg.all; +use work.logger_pkg.all; +use work.memory_pkg.all; +context work.com_context; + +package wishbone_pkg is + + type wishbone_slave_t is record + ack_high_probability : real range 0.0 to 1.0; + stall_high_probability : real range 0.0 to 1.0; + -- Private + p_actor : actor_t; + p_ack_actor : actor_t; + p_memory : memory_t; + p_logger : logger_t; + end record; + + constant wishbone_slave_logger : logger_t := get_logger("vunit_lib:wishbone_slave_pkg"); + impure function new_wishbone_slave( + memory : memory_t; + ack_high_probability : real := 1.0; + stall_high_probability : real := 0.0; + logger : logger_t := wishbone_slave_logger) + return wishbone_slave_t; + +end package; +package body wishbone_pkg is + + impure function new_wishbone_slave( + memory : memory_t; + ack_high_probability : real := 1.0; + stall_high_probability : real := 0.0; + logger : logger_t := wishbone_slave_logger) + return wishbone_slave_t is + begin + return (p_actor => new_actor, + p_ack_actor => new_actor, + p_memory => to_vc_interface(memory, logger), + p_logger => logger, + ack_high_probability => ack_high_probability, + stall_high_probability => stall_high_probability + ); + end; + +end package body; diff --git a/vunit/vhdl/verification_components/src/wishbone_slave.vhd b/vunit/vhdl/verification_components/src/wishbone_slave.vhd index 0c1e880fe..3aeb08e02 100644 --- a/vunit/vhdl/verification_components/src/wishbone_slave.vhd +++ b/vunit/vhdl/verification_components/src/wishbone_slave.vhd @@ -1,116 +1,116 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com --- Author Slawomir Siluk slaweksiluk@gazeta.pl --- Wishbone slave wrapper for Vunit memory VC --- TODO: --- * wb sel --- * err and rty responses - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -context work.vunit_context; -context work.com_context; -use work.memory_pkg.all; -use work.wishbone_pkg.all; - -library osvvm; -use osvvm.RandomPkg.all; - -entity wishbone_slave is - generic ( - wishbone_slave : wishbone_slave_t - ); - port ( - clk : in std_logic; - adr : in std_logic_vector; - dat_i : in std_logic_vector; - dat_o : out std_logic_vector; - sel : in std_logic_vector; - cyc : in std_logic; - stb : in std_logic; - we : in std_logic; - stall : out std_logic; - ack : out std_logic - ); -end entity; - -architecture a of wishbone_slave is - - constant slave_write_msg : msg_type_t := new_msg_type("wb slave write"); - constant slave_read_msg : msg_type_t := new_msg_type("wb slave read"); -begin - - request : process - variable wr_request_msg : msg_t; - variable rd_request_msg : msg_t; - begin - wait until (cyc and stb) = '1' and stall = '0' and rising_edge(clk); - if we = '1' then - wr_request_msg := new_msg(slave_write_msg); - -- For write address and data is passed to ack proc - push_integer(wr_request_msg, to_integer(unsigned(adr))); - push_std_ulogic_vector(wr_request_msg, dat_i); - send(net, wishbone_slave.p_ack_actor, wr_request_msg); - elsif we = '0' then - rd_request_msg := new_msg(slave_read_msg); - -- For read, only address is passed to ack proc - push_integer(rd_request_msg, to_integer(unsigned(adr))); - send(net, wishbone_slave.p_ack_actor, rd_request_msg); - end if; - end process; - - acknowledge : process - variable request_msg : msg_t; - variable msg_type : msg_type_t; - variable data : std_logic_vector(dat_i'range); - variable addr : natural; - variable rnd : RandomPType; - begin - ack <= '0'; - receive(net, wishbone_slave.p_ack_actor, request_msg); - msg_type := message_type(request_msg); - - if msg_type = slave_write_msg then - addr := pop_integer(request_msg); - data := pop_std_ulogic_vector(request_msg); - write_word(wishbone_slave.p_memory, addr, data); - while rnd.Uniform(0.0, 1.0) > wishbone_slave.ack_high_probability loop - wait until rising_edge(clk); - end loop; - ack <= '1'; - wait until rising_edge(clk); - ack <= '0'; - - elsif msg_type = slave_read_msg then - data := (others => '0'); - addr := pop_integer(request_msg); - data := read_word(wishbone_slave.p_memory, addr, sel'length); - while rnd.Uniform(0.0, 1.0) > wishbone_slave.ack_high_probability loop - wait until rising_edge(clk); - end loop; - dat_o <= data; - ack <= '1'; - wait until rising_edge(clk); - ack <= '0'; - - else - unexpected_msg_type(msg_type); - end if; - end process; - - stall_stim: process - variable rnd : RandomPType; - begin - if rnd.Uniform(0.0, 1.0) < wishbone_slave.stall_high_probability then - stall <= '1'; - else - stall <= '0'; - end if; - wait until rising_edge(clk); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +-- Author Slawomir Siluk slaweksiluk@gazeta.pl +-- Wishbone slave wrapper for Vunit memory VC +-- TODO: +-- * wb sel +-- * err and rty responses + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +context work.vunit_context; +context work.com_context; +use work.memory_pkg.all; +use work.wishbone_pkg.all; + +library osvvm; +use osvvm.RandomPkg.all; + +entity wishbone_slave is + generic ( + wishbone_slave : wishbone_slave_t + ); + port ( + clk : in std_logic; + adr : in std_logic_vector; + dat_i : in std_logic_vector; + dat_o : out std_logic_vector; + sel : in std_logic_vector; + cyc : in std_logic; + stb : in std_logic; + we : in std_logic; + stall : out std_logic; + ack : out std_logic + ); +end entity; + +architecture a of wishbone_slave is + + constant slave_write_msg : msg_type_t := new_msg_type("wb slave write"); + constant slave_read_msg : msg_type_t := new_msg_type("wb slave read"); +begin + + request : process + variable wr_request_msg : msg_t; + variable rd_request_msg : msg_t; + begin + wait until (cyc and stb) = '1' and stall = '0' and rising_edge(clk); + if we = '1' then + wr_request_msg := new_msg(slave_write_msg); + -- For write address and data is passed to ack proc + push_integer(wr_request_msg, to_integer(unsigned(adr))); + push_std_ulogic_vector(wr_request_msg, dat_i); + send(net, wishbone_slave.p_ack_actor, wr_request_msg); + elsif we = '0' then + rd_request_msg := new_msg(slave_read_msg); + -- For read, only address is passed to ack proc + push_integer(rd_request_msg, to_integer(unsigned(adr))); + send(net, wishbone_slave.p_ack_actor, rd_request_msg); + end if; + end process; + + acknowledge : process + variable request_msg : msg_t; + variable msg_type : msg_type_t; + variable data : std_logic_vector(dat_i'range); + variable addr : natural; + variable rnd : RandomPType; + begin + ack <= '0'; + receive(net, wishbone_slave.p_ack_actor, request_msg); + msg_type := message_type(request_msg); + + if msg_type = slave_write_msg then + addr := pop_integer(request_msg); + data := pop_std_ulogic_vector(request_msg); + write_word(wishbone_slave.p_memory, addr, data); + while rnd.Uniform(0.0, 1.0) > wishbone_slave.ack_high_probability loop + wait until rising_edge(clk); + end loop; + ack <= '1'; + wait until rising_edge(clk); + ack <= '0'; + + elsif msg_type = slave_read_msg then + data := (others => '0'); + addr := pop_integer(request_msg); + data := read_word(wishbone_slave.p_memory, addr, sel'length); + while rnd.Uniform(0.0, 1.0) > wishbone_slave.ack_high_probability loop + wait until rising_edge(clk); + end loop; + dat_o <= data; + ack <= '1'; + wait until rising_edge(clk); + ack <= '0'; + + else + unexpected_msg_type(msg_type); + end if; + end process; + + stall_stim: process + variable rnd : RandomPType; + begin + if rnd.Uniform(0.0, 1.0) < wishbone_slave.stall_high_probability then + stall <= '1'; + else + stall <= '0'; + end if; + wait until rising_edge(clk); + end process; +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_avalon_master.vhd b/vunit/vhdl/verification_components/test/tb_avalon_master.vhd index 921c03b8b..437993152 100644 --- a/vunit/vhdl/verification_components/test/tb_avalon_master.vhd +++ b/vunit/vhdl/verification_components/test/tb_avalon_master.vhd @@ -1,329 +1,329 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com --- Author Slawomir Siluk slaweksiluk@gazeta.pl - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -context work.vunit_context; -context work.com_context; -use work.memory_pkg.all; -use work.avalon_pkg.all; -use work.bus_master_pkg.all; - -library osvvm; -use osvvm.RandomPkg.all; - -entity tb_avalon_master is - generic ( - runner_cfg : string; - encoded_tb_cfg : string - ); -end entity; - -architecture a of tb_avalon_master is - - type tb_cfg_t is record - data_width : positive; - addr_width : positive; - burst_width : positive; - write_prob : real; - read_prob : real; - waitrequest_prob : real; - readdatavalid_prob : real; - transfers : positive; - rndburst_max : positive; - end record tb_cfg_t; - - impure function decode(encoded_tb_cfg : string) return tb_cfg_t is - begin - return (data_width => 32, - addr_width => 32, - burst_width => 8, - rndburst_max => 3, - write_prob => real'value(get(encoded_tb_cfg, "write_prob")), - read_prob => real'value(get(encoded_tb_cfg, "read_prob")), - waitrequest_prob => real'value(get(encoded_tb_cfg, "waitrequest_prob")), - readdatavalid_prob => real'value(get(encoded_tb_cfg, "readdatavalid_prob")), - transfers => positive'value(get(encoded_tb_cfg, "transfers")) - ); - end function decode; - - constant tb_cfg : tb_cfg_t := decode(encoded_tb_cfg); - - signal clk : std_logic := '0'; - signal address : std_logic_vector(tb_cfg.addr_width-1 downto 0); - signal writedata : std_logic_vector(tb_cfg.data_width-1 downto 0); - signal readdata : std_logic_vector(tb_cfg.data_width-1 downto 0); - signal byteenable : std_logic_vector(tb_cfg.data_width/8 -1 downto 0); - signal burstcount : std_logic_vector(tb_cfg.burst_width -1 downto 0); - signal write : std_logic := '0'; - signal read : std_logic := '0'; - signal readdatavalid : std_logic := '0'; - signal waitrequest : std_logic := '0'; - - constant master_logger : logger_t := get_logger("master"); - constant tb_logger : logger_t := get_logger("tb"); - constant master_actor : actor_t := new_actor("Avalon-MM Master"); - constant bus_handle : bus_master_t := new_bus(data_length => tb_cfg.data_width, - address_length => tb_cfg.addr_width, logger => master_logger, - actor => master_actor); - - constant memory : memory_t := new_memory; - constant buf : buffer_t := allocate(memory, tb_cfg.transfers * byteenable'length); - constant avalon_slave : avalon_slave_t := new_avalon_slave( - memory => memory, - readdatavalid_high_probability => tb_cfg.readdatavalid_prob, - waitrequest_high_probability => tb_cfg.waitrequest_prob, - name => "Avalon-MM Slave" - ); - - procedure gen_rndburst( - variable rnd : inout RandomPType; - variable rndburst : inout positive; - variable transfers : inout natural - ) is - begin - rndburst := rnd.RandInt(1, tb_cfg.rndburst_max); - if transfers >= rndburst then - transfers := transfers - rndburst; - else - rndburst := transfers; - transfers := 0; - end if; - end procedure; - -begin - - main_stim : process - variable tmp : std_logic_vector(writedata'range); - variable value : std_logic_vector(writedata'range) := (others => '1'); - variable burst_rd_ref : bus_reference_t; - variable bus_rd_ref1 : bus_reference_t; - variable bus_rd_ref2 : bus_reference_t; - type bus_reference_arr_t is array (0 to tb_cfg.transfers-1) of bus_reference_t; - variable rd_ref : bus_reference_arr_t; - constant data_queue : queue_t := new_queue; - constant rd_ref_queue : queue_t := new_queue; - variable rnd : RandomPType; - variable transfers : natural; - variable rndburst : positive; - variable i : natural; - variable addr : natural range 0 to tb_cfg.transfers*byteenable'length; - begin - rnd.InitSeed(rnd'instance_name); - test_runner_setup(runner, runner_cfg); - set_format(display_handler, verbose, true); - show(tb_logger, display_handler, verbose); - show(default_logger, display_handler, verbose); - show(master_logger, display_handler, verbose); - show(com_logger, display_handler, verbose); - - wait until rising_edge(clk); - - if run("wr single rd single") then - info(tb_logger, "Writing..."); - write_bus(net, bus_handle, 0, value); - wait until rising_edge(clk); - wait for 100 ns; - info(tb_logger, "Reading..."); - read_bus(net, bus_handle, 0, tmp); - check_equal(tmp, value, "read data"); - - elsif run("wr block rd block") then - info(tb_logger, "Writing..."); - for i in 0 to tb_cfg.transfers -1 loop - write_bus(net, bus_handle, i*(byteenable'length), - std_logic_vector(to_unsigned(i, writedata'length))); - end loop; - - info(tb_logger, "Reading..."); - for i in 0 to tb_cfg.transfers-1 loop - read_bus(net, bus_handle, i*(byteenable'length), rd_ref(i)); - end loop; - - info(tb_logger, "Get reads by references..."); - for i in 0 to tb_cfg.transfers-1 loop - await_read_bus_reply(net, rd_ref(i), tmp); - check_equal(tmp, std_logic_vector(to_unsigned(i, readdata'length)), "read data"); - end loop; - - elsif run("wr rd interleaved") then - info(tb_logger, "Writing and reading..."); - for i in 0 to tb_cfg.transfers -1 loop - write_bus(net, bus_handle, i*(byteenable'length), - std_logic_vector(to_unsigned(i, writedata'length))); - read_bus(net, bus_handle, i*(byteenable'length), rd_ref(i)); - end loop; - - info(tb_logger, "Get reads by references..."); - for i in 0 to tb_cfg.transfers-1 loop - await_read_bus_reply(net, rd_ref(i), tmp); - check_equal(tmp, std_logic_vector(to_unsigned(i, readdata'length)), "read data"); - end loop; - - - elsif run("burst wr and burst rd non-blocking") then - info(tb_logger, "Writing..."); - for i in 0 to tb_cfg.transfers -1 loop - push(data_queue, std_logic_vector(to_unsigned(i, writedata'length))); - end loop; - burst_write_bus(net, bus_handle, 0, tb_cfg.transfers, data_queue); - check_true(is_empty(data_queue), "wr queue not flushed by master"); - - info(tb_logger, "Reading..."); - burst_read_bus(net, bus_handle, 0, tb_cfg.transfers, burst_rd_ref); - info(tb_logger, "Get reads by references..."); - await_burst_read_bus_reply(net, bus_handle, data_queue, burst_rd_ref); - - info(tb_logger, "Compare..."); - for i in 0 to tb_cfg.transfers-1 loop - tmp := pop(data_queue); - check_equal(tmp, std_logic_vector(to_unsigned(i, readdata'length)), "read data"); - end loop; - check_true(is_empty(data_queue), "rd queue not flushed by master"); - - - elsif run("burst wr and burst rd blocking") then - info(tb_logger, "Writing..."); - for i in 0 to tb_cfg.transfers -1 loop - push(data_queue, std_logic_vector(to_unsigned(i, writedata'length))); - end loop; - burst_write_bus(net, bus_handle, 0, tb_cfg.transfers, data_queue); - check_true(is_empty(data_queue), "wr queue not flushed by master"); - - info(tb_logger, "Reading..."); - burst_read_bus(net, bus_handle, 0, tb_cfg.transfers, data_queue); - - info(tb_logger, "Compare..."); - for i in 0 to tb_cfg.transfers-1 loop - tmp := pop(data_queue); - check_equal(tmp, std_logic_vector(to_unsigned(i, readdata'length)), "read data"); - end loop; - check_true(is_empty(data_queue), "rd queue not flushed by master"); - - - elsif run("random burstcount") then - for i in 0 to tb_cfg.transfers -1 loop - push(data_queue, std_logic_vector(to_unsigned(i, writedata'length))); - end loop; - - info(tb_logger, "Writing..."); - transfers := tb_cfg.transfers; - addr := 0; - while transfers > 0 loop - gen_rndburst(rnd, rndburst, transfers); - burst_write_bus(net, bus_handle, addr, rndburst, data_queue); - addr := addr + rndburst * byteenable'length; - end loop; - check_true(is_empty(data_queue), "wr queue not flushed by master"); - - info(tb_logger, "Reading..."); - transfers := tb_cfg.transfers; - addr := 0; - while transfers > 0 loop - gen_rndburst(rnd, rndburst, transfers); - burst_read_bus(net, bus_handle, addr, rndburst, burst_rd_ref); - push(rd_ref_queue, burst_rd_ref); - addr := addr + rndburst * byteenable'length; - end loop; - - info(tb_logger, "Get reads by references and compre..."); - while not is_empty(rd_ref_queue) loop - burst_rd_ref := pop(rd_ref_queue); - await_burst_read_bus_reply(net, bus_handle, data_queue, burst_rd_ref); - while not is_empty(data_queue) loop - tmp := pop(data_queue); - check_equal(tmp, std_logic_vector(to_unsigned(i, readdata'length)), "read data"); - i := i + 1; - end loop; - end loop; - - elsif run("wait until idle") then - wait_until_idle(net, bus_handle); - write_bus(net, bus_handle, 0, value); - value := std_logic_vector(to_unsigned(456, value'length)); - write_bus(net, bus_handle, 0, value); - read_bus(net, bus_handle, 4, bus_rd_ref1); - read_bus(net, bus_handle, 0, bus_rd_ref2); - wait_until_idle(net, bus_handle); - await_read_bus_reply(net, bus_rd_ref1, tmp); - await_read_bus_reply(net, bus_rd_ref2, tmp); - check_equal(tmp, value, "invalid data"); - wait_until_idle(net, bus_handle); - write_bus(net, bus_handle, 0, value); - wait_until_idle(net, bus_handle); - - -- Wait till idle during bursts - for i in 1 to tb_cfg.transfers loop - push(data_queue, std_logic_vector(to_unsigned(i, writedata'length))); - end loop; - burst_write_bus(net, bus_handle, 0, tb_cfg.transfers, data_queue); - wait_until_idle(net, bus_handle); - wait until rising_edge(clk); - check_equal(write, '0', "unexpected write after wail till idle"); - - burst_read_bus(net, bus_handle, 0, tb_cfg.transfers, data_queue); - wait_until_idle(net, bus_handle); - wait until rising_edge(clk); - check_equal(readdatavalid, '0', "unexpected readdatavalid after wail till idle"); - - burst_read_bus(net, bus_handle, 0, tb_cfg.transfers, burst_rd_ref); - wait_until_idle(net, bus_handle); - wait until rising_edge(clk); - check_equal(readdatavalid, '0', "unexpected readdatavalid after wail till idle"); - await_burst_read_bus_reply(net, bus_handle, data_queue, burst_rd_ref); - wait_until_idle(net, bus_handle); - - end if; - - info(tb_logger, "Done, quit..."); - wait for 50 ns; - test_runner_cleanup(runner); - wait; - end process; - test_runner_watchdog(runner, 100 us); - - dut : entity work.avalon_master - generic map ( - bus_handle => bus_handle, - write_high_probability => tb_cfg.write_prob, - read_high_probability => tb_cfg.read_prob - ) - port map ( - clk => clk, - address => address, - byteenable => byteenable, - burstcount => burstcount, - write => write, - writedata => writedata, - read => read, - readdata => readdata, - readdatavalid => readdatavalid, - waitrequest => waitrequest - ); - - slave : entity work.avalon_slave - generic map ( - avalon_slave => avalon_slave - ) - port map ( - clk => clk, - address => address, - byteenable => byteenable, - burstcount => burstcount, - write => write, - writedata => writedata, - read => read, - readdata => readdata, - readdatavalid => readdatavalid, - waitrequest => waitrequest - ); - - clk <= not clk after 5 ns; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +-- Author Slawomir Siluk slaweksiluk@gazeta.pl + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +context work.vunit_context; +context work.com_context; +use work.memory_pkg.all; +use work.avalon_pkg.all; +use work.bus_master_pkg.all; + +library osvvm; +use osvvm.RandomPkg.all; + +entity tb_avalon_master is + generic ( + runner_cfg : string; + encoded_tb_cfg : string + ); +end entity; + +architecture a of tb_avalon_master is + + type tb_cfg_t is record + data_width : positive; + addr_width : positive; + burst_width : positive; + write_prob : real; + read_prob : real; + waitrequest_prob : real; + readdatavalid_prob : real; + transfers : positive; + rndburst_max : positive; + end record tb_cfg_t; + + impure function decode(encoded_tb_cfg : string) return tb_cfg_t is + begin + return (data_width => 32, + addr_width => 32, + burst_width => 8, + rndburst_max => 3, + write_prob => real'value(get(encoded_tb_cfg, "write_prob")), + read_prob => real'value(get(encoded_tb_cfg, "read_prob")), + waitrequest_prob => real'value(get(encoded_tb_cfg, "waitrequest_prob")), + readdatavalid_prob => real'value(get(encoded_tb_cfg, "readdatavalid_prob")), + transfers => positive'value(get(encoded_tb_cfg, "transfers")) + ); + end function decode; + + constant tb_cfg : tb_cfg_t := decode(encoded_tb_cfg); + + signal clk : std_logic := '0'; + signal address : std_logic_vector(tb_cfg.addr_width-1 downto 0); + signal writedata : std_logic_vector(tb_cfg.data_width-1 downto 0); + signal readdata : std_logic_vector(tb_cfg.data_width-1 downto 0); + signal byteenable : std_logic_vector(tb_cfg.data_width/8 -1 downto 0); + signal burstcount : std_logic_vector(tb_cfg.burst_width -1 downto 0); + signal write : std_logic := '0'; + signal read : std_logic := '0'; + signal readdatavalid : std_logic := '0'; + signal waitrequest : std_logic := '0'; + + constant master_logger : logger_t := get_logger("master"); + constant tb_logger : logger_t := get_logger("tb"); + constant master_actor : actor_t := new_actor("Avalon-MM Master"); + constant bus_handle : bus_master_t := new_bus(data_length => tb_cfg.data_width, + address_length => tb_cfg.addr_width, logger => master_logger, + actor => master_actor); + + constant memory : memory_t := new_memory; + constant buf : buffer_t := allocate(memory, tb_cfg.transfers * byteenable'length); + constant avalon_slave : avalon_slave_t := new_avalon_slave( + memory => memory, + readdatavalid_high_probability => tb_cfg.readdatavalid_prob, + waitrequest_high_probability => tb_cfg.waitrequest_prob, + name => "Avalon-MM Slave" + ); + + procedure gen_rndburst( + variable rnd : inout RandomPType; + variable rndburst : inout positive; + variable transfers : inout natural + ) is + begin + rndburst := rnd.RandInt(1, tb_cfg.rndburst_max); + if transfers >= rndburst then + transfers := transfers - rndburst; + else + rndburst := transfers; + transfers := 0; + end if; + end procedure; + +begin + + main_stim : process + variable tmp : std_logic_vector(writedata'range); + variable value : std_logic_vector(writedata'range) := (others => '1'); + variable burst_rd_ref : bus_reference_t; + variable bus_rd_ref1 : bus_reference_t; + variable bus_rd_ref2 : bus_reference_t; + type bus_reference_arr_t is array (0 to tb_cfg.transfers-1) of bus_reference_t; + variable rd_ref : bus_reference_arr_t; + constant data_queue : queue_t := new_queue; + constant rd_ref_queue : queue_t := new_queue; + variable rnd : RandomPType; + variable transfers : natural; + variable rndburst : positive; + variable i : natural; + variable addr : natural range 0 to tb_cfg.transfers*byteenable'length; + begin + rnd.InitSeed(rnd'instance_name); + test_runner_setup(runner, runner_cfg); + set_format(display_handler, verbose, true); + show(tb_logger, display_handler, verbose); + show(default_logger, display_handler, verbose); + show(master_logger, display_handler, verbose); + show(com_logger, display_handler, verbose); + + wait until rising_edge(clk); + + if run("wr single rd single") then + info(tb_logger, "Writing..."); + write_bus(net, bus_handle, 0, value); + wait until rising_edge(clk); + wait for 100 ns; + info(tb_logger, "Reading..."); + read_bus(net, bus_handle, 0, tmp); + check_equal(tmp, value, "read data"); + + elsif run("wr block rd block") then + info(tb_logger, "Writing..."); + for i in 0 to tb_cfg.transfers -1 loop + write_bus(net, bus_handle, i*(byteenable'length), + std_logic_vector(to_unsigned(i, writedata'length))); + end loop; + + info(tb_logger, "Reading..."); + for i in 0 to tb_cfg.transfers-1 loop + read_bus(net, bus_handle, i*(byteenable'length), rd_ref(i)); + end loop; + + info(tb_logger, "Get reads by references..."); + for i in 0 to tb_cfg.transfers-1 loop + await_read_bus_reply(net, rd_ref(i), tmp); + check_equal(tmp, std_logic_vector(to_unsigned(i, readdata'length)), "read data"); + end loop; + + elsif run("wr rd interleaved") then + info(tb_logger, "Writing and reading..."); + for i in 0 to tb_cfg.transfers -1 loop + write_bus(net, bus_handle, i*(byteenable'length), + std_logic_vector(to_unsigned(i, writedata'length))); + read_bus(net, bus_handle, i*(byteenable'length), rd_ref(i)); + end loop; + + info(tb_logger, "Get reads by references..."); + for i in 0 to tb_cfg.transfers-1 loop + await_read_bus_reply(net, rd_ref(i), tmp); + check_equal(tmp, std_logic_vector(to_unsigned(i, readdata'length)), "read data"); + end loop; + + + elsif run("burst wr and burst rd non-blocking") then + info(tb_logger, "Writing..."); + for i in 0 to tb_cfg.transfers -1 loop + push(data_queue, std_logic_vector(to_unsigned(i, writedata'length))); + end loop; + burst_write_bus(net, bus_handle, 0, tb_cfg.transfers, data_queue); + check_true(is_empty(data_queue), "wr queue not flushed by master"); + + info(tb_logger, "Reading..."); + burst_read_bus(net, bus_handle, 0, tb_cfg.transfers, burst_rd_ref); + info(tb_logger, "Get reads by references..."); + await_burst_read_bus_reply(net, bus_handle, data_queue, burst_rd_ref); + + info(tb_logger, "Compare..."); + for i in 0 to tb_cfg.transfers-1 loop + tmp := pop(data_queue); + check_equal(tmp, std_logic_vector(to_unsigned(i, readdata'length)), "read data"); + end loop; + check_true(is_empty(data_queue), "rd queue not flushed by master"); + + + elsif run("burst wr and burst rd blocking") then + info(tb_logger, "Writing..."); + for i in 0 to tb_cfg.transfers -1 loop + push(data_queue, std_logic_vector(to_unsigned(i, writedata'length))); + end loop; + burst_write_bus(net, bus_handle, 0, tb_cfg.transfers, data_queue); + check_true(is_empty(data_queue), "wr queue not flushed by master"); + + info(tb_logger, "Reading..."); + burst_read_bus(net, bus_handle, 0, tb_cfg.transfers, data_queue); + + info(tb_logger, "Compare..."); + for i in 0 to tb_cfg.transfers-1 loop + tmp := pop(data_queue); + check_equal(tmp, std_logic_vector(to_unsigned(i, readdata'length)), "read data"); + end loop; + check_true(is_empty(data_queue), "rd queue not flushed by master"); + + + elsif run("random burstcount") then + for i in 0 to tb_cfg.transfers -1 loop + push(data_queue, std_logic_vector(to_unsigned(i, writedata'length))); + end loop; + + info(tb_logger, "Writing..."); + transfers := tb_cfg.transfers; + addr := 0; + while transfers > 0 loop + gen_rndburst(rnd, rndburst, transfers); + burst_write_bus(net, bus_handle, addr, rndburst, data_queue); + addr := addr + rndburst * byteenable'length; + end loop; + check_true(is_empty(data_queue), "wr queue not flushed by master"); + + info(tb_logger, "Reading..."); + transfers := tb_cfg.transfers; + addr := 0; + while transfers > 0 loop + gen_rndburst(rnd, rndburst, transfers); + burst_read_bus(net, bus_handle, addr, rndburst, burst_rd_ref); + push(rd_ref_queue, burst_rd_ref); + addr := addr + rndburst * byteenable'length; + end loop; + + info(tb_logger, "Get reads by references and compre..."); + while not is_empty(rd_ref_queue) loop + burst_rd_ref := pop(rd_ref_queue); + await_burst_read_bus_reply(net, bus_handle, data_queue, burst_rd_ref); + while not is_empty(data_queue) loop + tmp := pop(data_queue); + check_equal(tmp, std_logic_vector(to_unsigned(i, readdata'length)), "read data"); + i := i + 1; + end loop; + end loop; + + elsif run("wait until idle") then + wait_until_idle(net, bus_handle); + write_bus(net, bus_handle, 0, value); + value := std_logic_vector(to_unsigned(456, value'length)); + write_bus(net, bus_handle, 0, value); + read_bus(net, bus_handle, 4, bus_rd_ref1); + read_bus(net, bus_handle, 0, bus_rd_ref2); + wait_until_idle(net, bus_handle); + await_read_bus_reply(net, bus_rd_ref1, tmp); + await_read_bus_reply(net, bus_rd_ref2, tmp); + check_equal(tmp, value, "invalid data"); + wait_until_idle(net, bus_handle); + write_bus(net, bus_handle, 0, value); + wait_until_idle(net, bus_handle); + + -- Wait till idle during bursts + for i in 1 to tb_cfg.transfers loop + push(data_queue, std_logic_vector(to_unsigned(i, writedata'length))); + end loop; + burst_write_bus(net, bus_handle, 0, tb_cfg.transfers, data_queue); + wait_until_idle(net, bus_handle); + wait until rising_edge(clk); + check_equal(write, '0', "unexpected write after wail till idle"); + + burst_read_bus(net, bus_handle, 0, tb_cfg.transfers, data_queue); + wait_until_idle(net, bus_handle); + wait until rising_edge(clk); + check_equal(readdatavalid, '0', "unexpected readdatavalid after wail till idle"); + + burst_read_bus(net, bus_handle, 0, tb_cfg.transfers, burst_rd_ref); + wait_until_idle(net, bus_handle); + wait until rising_edge(clk); + check_equal(readdatavalid, '0', "unexpected readdatavalid after wail till idle"); + await_burst_read_bus_reply(net, bus_handle, data_queue, burst_rd_ref); + wait_until_idle(net, bus_handle); + + end if; + + info(tb_logger, "Done, quit..."); + wait for 50 ns; + test_runner_cleanup(runner); + wait; + end process; + test_runner_watchdog(runner, 100 us); + + dut : entity work.avalon_master + generic map ( + bus_handle => bus_handle, + write_high_probability => tb_cfg.write_prob, + read_high_probability => tb_cfg.read_prob + ) + port map ( + clk => clk, + address => address, + byteenable => byteenable, + burstcount => burstcount, + write => write, + writedata => writedata, + read => read, + readdata => readdata, + readdatavalid => readdatavalid, + waitrequest => waitrequest + ); + + slave : entity work.avalon_slave + generic map ( + avalon_slave => avalon_slave + ) + port map ( + clk => clk, + address => address, + byteenable => byteenable, + burstcount => burstcount, + write => write, + writedata => writedata, + read => read, + readdata => readdata, + readdatavalid => readdatavalid, + waitrequest => waitrequest + ); + + clk <= not clk after 5 ns; + +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_avalon_slave.vhd b/vunit/vhdl/verification_components/test/tb_avalon_slave.vhd index 78a0052f6..d843d5c91 100644 --- a/vunit/vhdl/verification_components/test/tb_avalon_slave.vhd +++ b/vunit/vhdl/verification_components/test/tb_avalon_slave.vhd @@ -1,174 +1,174 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com --- Author Slawomir Siluk slaweksiluk@gazeta.pl - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -context work.vunit_context; -context work.com_context; -use work.memory_pkg.all; -use work.avalon_pkg.all; - -entity tb_avalon_slave is - generic ( - runner_cfg : string; - encoded_tb_cfg : string - ); -end entity; - -architecture a of tb_avalon_slave is - - type tb_cfg_t is record - data_width : positive; - address_width : positive; - burstcount_width : positive; - num_cycles : positive; - readdatavalid_prob : real; - waitrequest_prob : real; - end record tb_cfg_t; - - impure function decode(encoded_tb_cfg : string) return tb_cfg_t is - begin - return (data_width => positive'value(get(encoded_tb_cfg, "data_width")), - address_width => 32, - burstcount_width => 8, - num_cycles => positive'value(get(encoded_tb_cfg, "num_cycles")), - readdatavalid_prob => real'value(get(encoded_tb_cfg, "readdatavalid_prob")), - waitrequest_prob => real'value(get(encoded_tb_cfg, "waitrequest_prob"))); - end function decode; - - constant tb_cfg : tb_cfg_t := decode(encoded_tb_cfg); - - signal clk : std_logic := '0'; - signal address : std_logic_vector(tb_cfg.address_width-1 downto 0) := (others => '0'); - signal writedata : std_logic_vector(tb_cfg.data_width-1 downto 0) := (others => '0'); - signal readdata : std_logic_vector(tb_cfg.data_width-1 downto 0) := (others => '0'); - signal byteenable : std_logic_vector(tb_cfg.data_width/8 -1 downto 0) := (others => '1'); - signal burstcount : std_logic_vector(tb_cfg.burstcount_width -1 downto 0) := (others => '0'); - signal write : std_logic := '0'; - signal read : std_logic := '0'; - signal waitrequest : std_logic := '0'; - signal readdatavalid : std_logic := '0'; - - - constant tb_logger : logger_t := get_logger("tb"); - - signal wr_ack_cnt : natural range 0 to tb_cfg.num_cycles; - signal rd_ack_cnt : natural range 0 to tb_cfg.num_cycles; - - constant memory : memory_t := new_memory; - constant buf : buffer_t := allocate(memory, tb_cfg.num_cycles * byteenable'length); - constant avalon_slave : avalon_slave_t := - new_avalon_slave(memory => memory, - name => "avmm_vc", - readdatavalid_high_probability => tb_cfg.readdatavalid_prob, - waitrequest_high_probability => tb_cfg.waitrequest_prob - ); -begin - - main_stim : process - variable tmp : std_logic_vector(writedata'range); - variable value : std_logic_vector(writedata'range) := (others => '1'); - begin - test_runner_setup(runner, runner_cfg); - set_format(display_handler, verbose, true); - show(tb_logger, display_handler, verbose); - show(default_logger, display_handler, verbose); - show(com_logger, display_handler, verbose); - burstcount <= std_logic_vector(to_unsigned(1, burstcount'length)); - wait until rising_edge(clk); - - - if run("wr block rd block") then - info(tb_logger, "Writing..."); - for i in 0 to tb_cfg.num_cycles-1 loop - write <= '1'; - address <= std_logic_vector(to_unsigned(i*(byteenable'length), address'length)); - writedata <= std_logic_vector(to_unsigned(i, writedata'length)); - wait until rising_edge(clk) and waitrequest = '0'; - end loop; - write <= '0'; - - wait until rising_edge(clk); - - info(tb_logger, "Reading..."); - for i in 0 to tb_cfg.num_cycles-1 loop - read <= '1'; - address <= std_logic_vector(to_unsigned(i*(byteenable'length), address'length)); - wait until rising_edge(clk) and waitrequest = '0'; - end loop; - read <= '0'; - - wait until rising_edge(clk) and rd_ack_cnt = tb_cfg.num_cycles-1; - - elsif run("burst wr block rd block") then - info(tb_logger, "Writing..."); - address <= (others => 'U'); - burstcount(0) <= '1'; - - for i in 0 to tb_cfg.num_cycles-1 loop - if i = 0 then - address <= std_logic_vector(to_unsigned(0, address'length)); - burstcount <= std_logic_vector(to_unsigned(tb_cfg.num_cycles, burstcount'length)); - end if; - write <= '1'; - writedata <= std_logic_vector(to_unsigned(i, writedata'length)); - wait until rising_edge(clk) and waitrequest = '0'; - end loop; - write <= '0'; - address <= (others => 'U'); - burstcount <= (others => 'U'); - writedata <= (others => 'U'); - - wait until rising_edge(clk); - - info(tb_logger, "Reading..."); - wait until rising_edge(clk); - read <= '1'; - burstcount <= std_logic_vector(to_unsigned(tb_cfg.num_cycles, burstcount'length)); - address <= std_logic_vector(to_unsigned(0, address'length)); - wait until rising_edge(clk) and waitrequest = '0'; - read <= '0'; - - wait until rising_edge(clk) and rd_ack_cnt = tb_cfg.num_cycles-1; - end if; - - wait for 50 ns; - test_runner_cleanup(runner); - wait; - end process; - test_runner_watchdog(runner, 100 us); - - rd_ack: process - begin - wait until rising_edge(clk) and readdatavalid = '1'; - check_equal(readdata, std_logic_vector(to_unsigned(rd_ack_cnt, - readdata'length)), "readdata"); - rd_ack_cnt <= rd_ack_cnt +1; - end process; - - dut_slave : entity work.avalon_slave - generic map ( - avalon_slave => avalon_slave - ) - port map ( - clk => clk, - address => address, - byteenable => byteenable, - burstcount => burstcount, - write => write, - writedata => writedata, - read => read, - readdata => readdata, - readdatavalid => readdatavalid, - waitrequest => waitrequest - ); - - clk <= not clk after 5 ns; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +-- Author Slawomir Siluk slaweksiluk@gazeta.pl + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +context work.vunit_context; +context work.com_context; +use work.memory_pkg.all; +use work.avalon_pkg.all; + +entity tb_avalon_slave is + generic ( + runner_cfg : string; + encoded_tb_cfg : string + ); +end entity; + +architecture a of tb_avalon_slave is + + type tb_cfg_t is record + data_width : positive; + address_width : positive; + burstcount_width : positive; + num_cycles : positive; + readdatavalid_prob : real; + waitrequest_prob : real; + end record tb_cfg_t; + + impure function decode(encoded_tb_cfg : string) return tb_cfg_t is + begin + return (data_width => positive'value(get(encoded_tb_cfg, "data_width")), + address_width => 32, + burstcount_width => 8, + num_cycles => positive'value(get(encoded_tb_cfg, "num_cycles")), + readdatavalid_prob => real'value(get(encoded_tb_cfg, "readdatavalid_prob")), + waitrequest_prob => real'value(get(encoded_tb_cfg, "waitrequest_prob"))); + end function decode; + + constant tb_cfg : tb_cfg_t := decode(encoded_tb_cfg); + + signal clk : std_logic := '0'; + signal address : std_logic_vector(tb_cfg.address_width-1 downto 0) := (others => '0'); + signal writedata : std_logic_vector(tb_cfg.data_width-1 downto 0) := (others => '0'); + signal readdata : std_logic_vector(tb_cfg.data_width-1 downto 0) := (others => '0'); + signal byteenable : std_logic_vector(tb_cfg.data_width/8 -1 downto 0) := (others => '1'); + signal burstcount : std_logic_vector(tb_cfg.burstcount_width -1 downto 0) := (others => '0'); + signal write : std_logic := '0'; + signal read : std_logic := '0'; + signal waitrequest : std_logic := '0'; + signal readdatavalid : std_logic := '0'; + + + constant tb_logger : logger_t := get_logger("tb"); + + signal wr_ack_cnt : natural range 0 to tb_cfg.num_cycles; + signal rd_ack_cnt : natural range 0 to tb_cfg.num_cycles; + + constant memory : memory_t := new_memory; + constant buf : buffer_t := allocate(memory, tb_cfg.num_cycles * byteenable'length); + constant avalon_slave : avalon_slave_t := + new_avalon_slave(memory => memory, + name => "avmm_vc", + readdatavalid_high_probability => tb_cfg.readdatavalid_prob, + waitrequest_high_probability => tb_cfg.waitrequest_prob + ); +begin + + main_stim : process + variable tmp : std_logic_vector(writedata'range); + variable value : std_logic_vector(writedata'range) := (others => '1'); + begin + test_runner_setup(runner, runner_cfg); + set_format(display_handler, verbose, true); + show(tb_logger, display_handler, verbose); + show(default_logger, display_handler, verbose); + show(com_logger, display_handler, verbose); + burstcount <= std_logic_vector(to_unsigned(1, burstcount'length)); + wait until rising_edge(clk); + + + if run("wr block rd block") then + info(tb_logger, "Writing..."); + for i in 0 to tb_cfg.num_cycles-1 loop + write <= '1'; + address <= std_logic_vector(to_unsigned(i*(byteenable'length), address'length)); + writedata <= std_logic_vector(to_unsigned(i, writedata'length)); + wait until rising_edge(clk) and waitrequest = '0'; + end loop; + write <= '0'; + + wait until rising_edge(clk); + + info(tb_logger, "Reading..."); + for i in 0 to tb_cfg.num_cycles-1 loop + read <= '1'; + address <= std_logic_vector(to_unsigned(i*(byteenable'length), address'length)); + wait until rising_edge(clk) and waitrequest = '0'; + end loop; + read <= '0'; + + wait until rising_edge(clk) and rd_ack_cnt = tb_cfg.num_cycles-1; + + elsif run("burst wr block rd block") then + info(tb_logger, "Writing..."); + address <= (others => 'U'); + burstcount(0) <= '1'; + + for i in 0 to tb_cfg.num_cycles-1 loop + if i = 0 then + address <= std_logic_vector(to_unsigned(0, address'length)); + burstcount <= std_logic_vector(to_unsigned(tb_cfg.num_cycles, burstcount'length)); + end if; + write <= '1'; + writedata <= std_logic_vector(to_unsigned(i, writedata'length)); + wait until rising_edge(clk) and waitrequest = '0'; + end loop; + write <= '0'; + address <= (others => 'U'); + burstcount <= (others => 'U'); + writedata <= (others => 'U'); + + wait until rising_edge(clk); + + info(tb_logger, "Reading..."); + wait until rising_edge(clk); + read <= '1'; + burstcount <= std_logic_vector(to_unsigned(tb_cfg.num_cycles, burstcount'length)); + address <= std_logic_vector(to_unsigned(0, address'length)); + wait until rising_edge(clk) and waitrequest = '0'; + read <= '0'; + + wait until rising_edge(clk) and rd_ack_cnt = tb_cfg.num_cycles-1; + end if; + + wait for 50 ns; + test_runner_cleanup(runner); + wait; + end process; + test_runner_watchdog(runner, 100 us); + + rd_ack: process + begin + wait until rising_edge(clk) and readdatavalid = '1'; + check_equal(readdata, std_logic_vector(to_unsigned(rd_ack_cnt, + readdata'length)), "readdata"); + rd_ack_cnt <= rd_ack_cnt +1; + end process; + + dut_slave : entity work.avalon_slave + generic map ( + avalon_slave => avalon_slave + ) + port map ( + clk => clk, + address => address, + byteenable => byteenable, + burstcount => burstcount, + write => write, + writedata => writedata, + read => read, + readdata => readdata, + readdatavalid => readdatavalid, + waitrequest => waitrequest + ); + + clk <= not clk after 5 ns; + +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_avalon_stream.vhd b/vunit/vhdl/verification_components/test/tb_avalon_stream.vhd index 33556834a..66878c864 100644 --- a/vunit/vhdl/verification_components/test/tb_avalon_stream.vhd +++ b/vunit/vhdl/verification_components/test/tb_avalon_stream.vhd @@ -1,131 +1,131 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com --- Author Slawomir Siluk slaweksiluk@gazeta.pl - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -context work.vunit_context; -context work.com_context; -context work.data_types_context; -use work.avalon_stream_pkg.all; -use work.stream_master_pkg.all; -use work.stream_slave_pkg.all; - -entity tb_avalon_stream is - generic (runner_cfg : string); -end entity; - -architecture a of tb_avalon_stream is - constant avalon_source_stream : avalon_source_t := - new_avalon_source(data_length => 8, valid_high_probability => 0.1); - constant master_stream : stream_master_t := as_stream(avalon_source_stream); - - constant avalon_sink_stream : avalon_sink_t := - new_avalon_sink(data_length => 8, ready_high_probability => 0.3); - constant slave_stream : stream_slave_t := as_stream(avalon_sink_stream); - - signal clk : std_logic := '0'; - signal valid : std_logic; - signal ready : std_logic; - signal sop : std_logic; - signal eop : std_logic; - signal data : std_logic_vector(data_length(avalon_source_stream)-1 downto 0); -begin - - main : process - variable tmp : std_logic_vector(data'range); - variable is_sop : std_logic; - variable is_eop : std_logic; - variable sop_tmp : std_logic; - variable eop_tmp : std_logic; - begin - test_runner_setup(runner, runner_cfg); - set_format(display_handler, verbose, true); - show(avalon_sink_stream.p_logger, display_handler, verbose); - - wait until rising_edge(clk); - if run("test single push and pop") then - push_stream(net, master_stream, x"77"); - pop_stream(net, slave_stream, tmp); - check_equal(tmp, std_logic_vector'(x"77"), "pop stream data"); - - elsif run("test double push and pop") then - push_stream(net, master_stream, x"66"); - pop_stream(net, slave_stream, tmp); - check_equal(tmp, std_logic_vector'(x"66"), "pop stream first data"); - - push_stream(net, master_stream, x"55"); - pop_stream(net, slave_stream, tmp); - check_equal(tmp, std_logic_vector'(x"55"), "pop stream second data"); - - elsif run("push delay pop") then - push_stream(net, master_stream, x"de"); - wait until rising_edge(clk); - wait until rising_edge(clk); - wait until rising_edge(clk); - pop_stream(net, slave_stream, tmp); - check_equal(tmp, std_logic_vector'(x"de"), "pop stream data"); - - elsif run("block push and pop") then - for i in 0 to 7 loop - push_stream(net, master_stream, std_logic_vector(to_unsigned(i, 8))); - pop_stream(net, slave_stream, tmp); - check_equal(tmp, std_logic_vector(to_unsigned(i, 8)), "pop stream data"&natural'image(i)); - end loop; - - elsif run("test sop and eop") then - for i in 0 to 7 loop - if i = 0 then - is_sop := '1'; - is_eop := '0'; - elsif i = 7 then - is_sop := '0'; - is_eop := '1'; - else - is_sop := '0'; - is_eop := '0'; - end if; - push_avalon_stream(net, avalon_source_stream, std_logic_vector(to_unsigned(i, 8)), is_sop, is_eop); - pop_avalon_stream(net, avalon_sink_stream, tmp, sop_tmp, eop_tmp); - check_equal(tmp, std_logic_vector(to_unsigned(i, 8)), "pop stream data"&natural'image(i)); - check_equal(sop_tmp, is_sop, "pop stream sop"); - check_equal(eop_tmp, is_eop, "pop stream eop"); - end loop; - - end if; - wait until rising_edge(clk); - test_runner_cleanup(runner); - end process; - test_runner_watchdog(runner, 10 ms); - - avalon_source_vc : entity work.avalon_source - generic map ( - source => avalon_source_stream) - port map ( - clk => clk, - valid => valid, - ready => ready, - sop => sop, - eop => eop, - data => data - ); - - avalon_sink_vc : entity work.avalon_sink - generic map ( - sink => avalon_sink_stream) - port map ( - clk => clk, - valid => valid, - ready => ready, - sop => sop, - eop => eop, - data => data - ); - - clk <= not clk after 5 ns; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +-- Author Slawomir Siluk slaweksiluk@gazeta.pl + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +context work.vunit_context; +context work.com_context; +context work.data_types_context; +use work.avalon_stream_pkg.all; +use work.stream_master_pkg.all; +use work.stream_slave_pkg.all; + +entity tb_avalon_stream is + generic (runner_cfg : string); +end entity; + +architecture a of tb_avalon_stream is + constant avalon_source_stream : avalon_source_t := + new_avalon_source(data_length => 8, valid_high_probability => 0.1); + constant master_stream : stream_master_t := as_stream(avalon_source_stream); + + constant avalon_sink_stream : avalon_sink_t := + new_avalon_sink(data_length => 8, ready_high_probability => 0.3); + constant slave_stream : stream_slave_t := as_stream(avalon_sink_stream); + + signal clk : std_logic := '0'; + signal valid : std_logic; + signal ready : std_logic; + signal sop : std_logic; + signal eop : std_logic; + signal data : std_logic_vector(data_length(avalon_source_stream)-1 downto 0); +begin + + main : process + variable tmp : std_logic_vector(data'range); + variable is_sop : std_logic; + variable is_eop : std_logic; + variable sop_tmp : std_logic; + variable eop_tmp : std_logic; + begin + test_runner_setup(runner, runner_cfg); + set_format(display_handler, verbose, true); + show(avalon_sink_stream.p_logger, display_handler, verbose); + + wait until rising_edge(clk); + if run("test single push and pop") then + push_stream(net, master_stream, x"77"); + pop_stream(net, slave_stream, tmp); + check_equal(tmp, std_logic_vector'(x"77"), "pop stream data"); + + elsif run("test double push and pop") then + push_stream(net, master_stream, x"66"); + pop_stream(net, slave_stream, tmp); + check_equal(tmp, std_logic_vector'(x"66"), "pop stream first data"); + + push_stream(net, master_stream, x"55"); + pop_stream(net, slave_stream, tmp); + check_equal(tmp, std_logic_vector'(x"55"), "pop stream second data"); + + elsif run("push delay pop") then + push_stream(net, master_stream, x"de"); + wait until rising_edge(clk); + wait until rising_edge(clk); + wait until rising_edge(clk); + pop_stream(net, slave_stream, tmp); + check_equal(tmp, std_logic_vector'(x"de"), "pop stream data"); + + elsif run("block push and pop") then + for i in 0 to 7 loop + push_stream(net, master_stream, std_logic_vector(to_unsigned(i, 8))); + pop_stream(net, slave_stream, tmp); + check_equal(tmp, std_logic_vector(to_unsigned(i, 8)), "pop stream data"&natural'image(i)); + end loop; + + elsif run("test sop and eop") then + for i in 0 to 7 loop + if i = 0 then + is_sop := '1'; + is_eop := '0'; + elsif i = 7 then + is_sop := '0'; + is_eop := '1'; + else + is_sop := '0'; + is_eop := '0'; + end if; + push_avalon_stream(net, avalon_source_stream, std_logic_vector(to_unsigned(i, 8)), is_sop, is_eop); + pop_avalon_stream(net, avalon_sink_stream, tmp, sop_tmp, eop_tmp); + check_equal(tmp, std_logic_vector(to_unsigned(i, 8)), "pop stream data"&natural'image(i)); + check_equal(sop_tmp, is_sop, "pop stream sop"); + check_equal(eop_tmp, is_eop, "pop stream eop"); + end loop; + + end if; + wait until rising_edge(clk); + test_runner_cleanup(runner); + end process; + test_runner_watchdog(runner, 10 ms); + + avalon_source_vc : entity work.avalon_source + generic map ( + source => avalon_source_stream) + port map ( + clk => clk, + valid => valid, + ready => ready, + sop => sop, + eop => eop, + data => data + ); + + avalon_sink_vc : entity work.avalon_sink + generic map ( + sink => avalon_sink_stream) + port map ( + clk => clk, + valid => valid, + ready => ready, + sop => sop, + eop => eop, + data => data + ); + + clk <= not clk after 5 ns; +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_avalon_stream_pkg.vhd b/vunit/vhdl/verification_components/test/tb_avalon_stream_pkg.vhd index 2efc95e73..382e45ae8 100644 --- a/vunit/vhdl/verification_components/test/tb_avalon_stream_pkg.vhd +++ b/vunit/vhdl/verification_components/test/tb_avalon_stream_pkg.vhd @@ -1,87 +1,87 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com --- Author Slawomir Siluk slaweksiluk@gazeta.pl - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -context work.vunit_context; -context work.com_context; -context work.data_types_context; -use work.avalon_stream_pkg.all; -use work.stream_master_pkg.all; -use work.stream_slave_pkg.all; - -entity tb_avalon_stream_pkg is - generic (runner_cfg : string); -end entity; - -architecture a of tb_avalon_stream_pkg is - signal clk : std_logic := '0'; -begin - - main : process - variable msg : msg_t; - variable msg_type : msg_type_t; - variable avalon_stream_transaction : avalon_stream_transaction_t(data(7 downto 0)); - variable avalon_stream_transaction_tmp : avalon_stream_transaction_t(data(7 downto 0)); - begin - test_runner_setup(runner, runner_cfg); - set_format(display_handler, verbose, true); - - wait until rising_edge(clk); - if run("test single transaction push and pop") then - avalon_stream_transaction.data := x"ab"; - msg := new_avalon_stream_transaction_msg(avalon_stream_transaction); - msg_type := message_type(msg); - handle_avalon_stream_transaction(msg_type, msg, avalon_stream_transaction_tmp); - check_equal(avalon_stream_transaction_tmp.data, avalon_stream_transaction.data, "pop stream transaction data"); - - elsif run("test double transaction push and pop") then - avalon_stream_transaction.data := x"a5"; - msg := new_avalon_stream_transaction_msg(avalon_stream_transaction); - msg_type := message_type(msg); - handle_avalon_stream_transaction(msg_type, msg, avalon_stream_transaction_tmp); - check_equal(avalon_stream_transaction_tmp.data, avalon_stream_transaction.data, "pop stream transaction data"); - - avalon_stream_transaction.data := x"9e"; - msg := new_avalon_stream_transaction_msg(avalon_stream_transaction); - msg_type := message_type(msg); - handle_avalon_stream_transaction(msg_type, msg, avalon_stream_transaction_tmp); - check_equal(avalon_stream_transaction_tmp.data, avalon_stream_transaction.data, "pop stream transaction data"); - - elsif run("test transaction push delay pop") then - avalon_stream_transaction.data := x"f1"; - msg := new_avalon_stream_transaction_msg(avalon_stream_transaction); - msg_type := message_type(msg); - wait until rising_edge(clk); - wait until rising_edge(clk); - wait until rising_edge(clk); - handle_avalon_stream_transaction(msg_type, msg, avalon_stream_transaction_tmp); - check_equal(avalon_stream_transaction_tmp.data, avalon_stream_transaction.data, "pop stream transaction data"); - - elsif run("test transaction sop and eop") then - for i in 0 to 7 loop - avalon_stream_transaction.data := std_logic_vector(to_unsigned(i, 8)); - avalon_stream_transaction.sop := (i = 0); - avalon_stream_transaction.eop := (i = 7); - msg := new_avalon_stream_transaction_msg(avalon_stream_transaction); - msg_type := message_type(msg); - handle_avalon_stream_transaction(msg_type, msg, avalon_stream_transaction_tmp); - check_equal(avalon_stream_transaction_tmp.data, std_logic_vector(to_unsigned(i, 8)), "pop stream data"&natural'image(i)); - check_equal(avalon_stream_transaction_tmp.sop, i = 0, "pop stream sop"); - check_equal(avalon_stream_transaction_tmp.eop, i = 7, "pop stream eop"); - end loop; - - end if; - wait until rising_edge(clk); - test_runner_cleanup(runner); - end process; - test_runner_watchdog(runner, 10 ms); - - clk <= not clk after 5 ns; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +-- Author Slawomir Siluk slaweksiluk@gazeta.pl + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +context work.vunit_context; +context work.com_context; +context work.data_types_context; +use work.avalon_stream_pkg.all; +use work.stream_master_pkg.all; +use work.stream_slave_pkg.all; + +entity tb_avalon_stream_pkg is + generic (runner_cfg : string); +end entity; + +architecture a of tb_avalon_stream_pkg is + signal clk : std_logic := '0'; +begin + + main : process + variable msg : msg_t; + variable msg_type : msg_type_t; + variable avalon_stream_transaction : avalon_stream_transaction_t(data(7 downto 0)); + variable avalon_stream_transaction_tmp : avalon_stream_transaction_t(data(7 downto 0)); + begin + test_runner_setup(runner, runner_cfg); + set_format(display_handler, verbose, true); + + wait until rising_edge(clk); + if run("test single transaction push and pop") then + avalon_stream_transaction.data := x"ab"; + msg := new_avalon_stream_transaction_msg(avalon_stream_transaction); + msg_type := message_type(msg); + handle_avalon_stream_transaction(msg_type, msg, avalon_stream_transaction_tmp); + check_equal(avalon_stream_transaction_tmp.data, avalon_stream_transaction.data, "pop stream transaction data"); + + elsif run("test double transaction push and pop") then + avalon_stream_transaction.data := x"a5"; + msg := new_avalon_stream_transaction_msg(avalon_stream_transaction); + msg_type := message_type(msg); + handle_avalon_stream_transaction(msg_type, msg, avalon_stream_transaction_tmp); + check_equal(avalon_stream_transaction_tmp.data, avalon_stream_transaction.data, "pop stream transaction data"); + + avalon_stream_transaction.data := x"9e"; + msg := new_avalon_stream_transaction_msg(avalon_stream_transaction); + msg_type := message_type(msg); + handle_avalon_stream_transaction(msg_type, msg, avalon_stream_transaction_tmp); + check_equal(avalon_stream_transaction_tmp.data, avalon_stream_transaction.data, "pop stream transaction data"); + + elsif run("test transaction push delay pop") then + avalon_stream_transaction.data := x"f1"; + msg := new_avalon_stream_transaction_msg(avalon_stream_transaction); + msg_type := message_type(msg); + wait until rising_edge(clk); + wait until rising_edge(clk); + wait until rising_edge(clk); + handle_avalon_stream_transaction(msg_type, msg, avalon_stream_transaction_tmp); + check_equal(avalon_stream_transaction_tmp.data, avalon_stream_transaction.data, "pop stream transaction data"); + + elsif run("test transaction sop and eop") then + for i in 0 to 7 loop + avalon_stream_transaction.data := std_logic_vector(to_unsigned(i, 8)); + avalon_stream_transaction.sop := (i = 0); + avalon_stream_transaction.eop := (i = 7); + msg := new_avalon_stream_transaction_msg(avalon_stream_transaction); + msg_type := message_type(msg); + handle_avalon_stream_transaction(msg_type, msg, avalon_stream_transaction_tmp); + check_equal(avalon_stream_transaction_tmp.data, std_logic_vector(to_unsigned(i, 8)), "pop stream data"&natural'image(i)); + check_equal(avalon_stream_transaction_tmp.sop, i = 0, "pop stream sop"); + check_equal(avalon_stream_transaction_tmp.eop, i = 7, "pop stream eop"); + end loop; + + end if; + wait until rising_edge(clk); + test_runner_cleanup(runner); + end process; + test_runner_watchdog(runner, 10 ms); + + clk <= not clk after 5 ns; +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_axi_lite_master.vhd b/vunit/vhdl/verification_components/test/tb_axi_lite_master.vhd index 14f946afd..605fdcc9f 100644 --- a/vunit/vhdl/verification_components/test/tb_axi_lite_master.vhd +++ b/vunit/vhdl/verification_components/test/tb_axi_lite_master.vhd @@ -1,398 +1,398 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library vunit_lib; -context vunit_lib.vunit_context; -context work.com_context; - -use work.axi_pkg.all; -use work.bus_master_pkg.all; -use work.axi_lite_master_pkg.all; - -library osvvm; -use osvvm.RandomPkg.all; - -entity tb_axi_lite_master is - generic (runner_cfg : string); -end entity; - -architecture a of tb_axi_lite_master is - constant num_random_tests : integer := 128; - - signal clk : std_logic := '0'; - signal arready : std_logic := '0'; - signal arvalid : std_logic; - signal araddr : std_logic_vector(31 downto 0); - - signal rready : std_logic := '0'; - signal rvalid : std_logic; - signal rdata : std_logic_vector(15 downto 0); - signal rresp : std_logic_vector(1 downto 0); - - signal awready : std_logic := '0'; - signal awvalid : std_logic; - signal awaddr : std_logic_vector(31 downto 0); - - signal wready : std_logic := '0'; - signal wvalid : std_logic; - signal wdata : std_logic_vector(15 downto 0); - signal wstrb : std_logic_vector(1 downto 0); - - signal bvalid : std_logic := '0'; - signal bready : std_logic; - signal bresp : std_logic_vector(1 downto 0); - - constant bus_handle : bus_master_t := new_bus(data_length => wdata'length, - address_length => awaddr'length); - - signal start, done : boolean := false; -begin - - main : process - variable tmp : std_logic_vector(rdata'range); - variable rnd : RandomPType; - begin - test_runner_setup(runner, runner_cfg); - rnd.InitSeed("common_seed"); - start <= true; - wait for 0 ns; - - if run("Test single write") then - mock(get_logger(bus_handle), debug); - write_bus(net, bus_handle, x"01234567", x"1122"); - wait_until_idle(net, bus_handle); - check_only_log(get_logger(bus_handle), "Wrote 0x1122 to address 0x01234567", debug); - unmock(get_logger(bus_handle)); - - elsif run("Test single write with byte enable") then - write_bus(net, bus_handle, x"01234567", x"1122", byte_enable => "10"); - - elsif run("Test write not okay") then - write_bus(net, bus_handle, x"01234567", x"1122"); - - elsif run("Test write with axi resp") then - write_axi_lite(net, bus_handle, x"01234567", x"1122", axi_resp_slverr); - - elsif run("Test write with wrong axi resp") then - write_axi_lite(net, bus_handle, x"01234567", x"1122", axi_resp_decerr); - - elsif run("Test single read") then - mock(get_logger(bus_handle), debug); - read_bus(net, bus_handle, x"01234567", tmp); - check_equal(tmp, std_logic_vector'(x"5566"), "read data"); - check_only_log(get_logger(bus_handle), "Read 0x5566 from address 0x01234567", debug); - unmock(get_logger(bus_handle)); - - elsif run("Test read not okay") then - read_bus(net, bus_handle, x"01234567", tmp); - - elsif run("Test read with axi resp") then - read_axi_lite(net, bus_handle, x"01234567", axi_resp_slverr, tmp); - - elsif run("Test read with wrong axi resp") then - read_axi_lite(net, bus_handle, x"01234567", axi_resp_exokay, tmp); - - elsif run("Test random") then - for i in 0 to num_random_tests-1 loop - if rnd.RandInt(0, 1) = 0 then - read_bus(net, bus_handle, rnd.RandSlv(araddr'length), tmp); - check_equal(tmp, rnd.RandSlv(rdata'length), "read data"); - else - write_bus(net, bus_handle, rnd.RandSlv(awaddr'length), rnd.RandSlv(wdata'length)); - end if; - end loop; - - elsif run("Test random axi resp") then - for i in 0 to num_random_tests-1 loop - if rnd.RandInt(0, 1) = 0 then - read_axi_lite(net, bus_handle, rnd.RandSlv(araddr'length), rnd.RandSlv(axi_resp_t'length), tmp); - check_equal(tmp, rnd.RandSlv(rdata'length), "read data"); - else - write_axi_lite(net, bus_handle, rnd.RandSlv(awaddr'length), rnd.RandSlv(wdata'length), - rnd.RandSlv(axi_resp_t'length)); - end if; - end loop; - end if; - - wait for 100 ns; - - if not done then - wait until done; - end if; - - test_runner_cleanup(runner); - end process; - test_runner_watchdog(runner, 100 us); - - - support : process - variable rnd : RandomPType; - begin - rnd.InitSeed("common_seed"); - - wait until start; - - if enabled("Test single write") then - awready <= '1'; - wait until (awready and awvalid) = '1' and rising_edge(clk); - awready <= '0'; - check_equal(awaddr, std_logic_vector'(x"01234567"), "awaddr"); - - wready <= '1'; - wait until (wready and wvalid) = '1' and rising_edge(clk); - wready <= '0'; - check_equal(wdata, std_logic_vector'(x"1122"), "wdata"); - check_equal(wstrb, std_logic_vector'("11"), "wstrb"); - - bvalid <= '1'; - bresp <= axi_resp_okay; - wait until (bready and bvalid) = '1' and rising_edge(clk); - bvalid <= '0'; - - done <= true; - - elsif enabled("Test single write with byte enable") then - awready <= '1'; - wait until (awready and awvalid) = '1' and rising_edge(clk); - awready <= '0'; - check_equal(awaddr, std_logic_vector'(x"01234567"), "awaddr"); - - wready <= '1'; - wait until (wready and wvalid) = '1' and rising_edge(clk); - wready <= '0'; - check_equal(wdata, std_logic_vector'(x"1122"), "wdata"); - check_equal(wstrb, std_logic_vector'("10"), "wstrb"); - - bvalid <= '1'; - bresp <= axi_resp_okay; - wait until (bready and bvalid) = '1' and rising_edge(clk); - bvalid <= '0'; - - done <= true; - - elsif enabled("Test write not okay") then - awready <= '1'; - wait until (awready and awvalid) = '1' and rising_edge(clk); - awready <= '0'; - - wready <= '1'; - wait until (wready and wvalid) = '1' and rising_edge(clk); - wready <= '0'; - - bvalid <= '1'; - bresp <= axi_resp_slverr; - mock(bus_logger, failure); - wait until (bready and bvalid) = '1' and rising_edge(clk); - bvalid <= '0'; - wait until mock_queue_length > 0 for 0 ns; - check_only_log(bus_logger, "bresp - Got AXI response SLVERR(10) expected OKAY(00)", failure); - unmock(bus_logger); - - done <= true; - - elsif enabled("Test write with axi resp") then - awready <= '1'; - wait until (awready and awvalid) = '1' and rising_edge(clk); - awready <= '0'; - check_equal(awaddr, std_logic_vector'(x"01234567"), "awaddr"); - - wready <= '1'; - wait until (wready and wvalid) = '1' and rising_edge(clk); - wready <= '0'; - check_equal(wdata, std_logic_vector'(x"1122"), "wdata"); - check_equal(wstrb, std_logic_vector'("11"), "wstrb"); - - bvalid <= '1'; - bresp <= axi_resp_slverr; - wait until (bready and bvalid) = '1' and rising_edge(clk); - bvalid <= '0'; - - done <= true; - - elsif enabled("Test write with wrong axi resp") then - awready <= '1'; - wait until (awready and awvalid) = '1' and rising_edge(clk); - awready <= '0'; - check_equal(awaddr, std_logic_vector'(x"01234567"), "awaddr"); - - wready <= '1'; - wait until (wready and wvalid) = '1' and rising_edge(clk); - wready <= '0'; - check_equal(wdata, std_logic_vector'(x"1122"), "wdata"); - check_equal(wstrb, std_logic_vector'("11"), "wstrb"); - - bvalid <= '1'; - bresp <= axi_resp_exokay; - mock(bus_logger, failure); - wait until (bready and bvalid) = '1' and rising_edge(clk); - bvalid <= '0'; - wait until mock_queue_length > 0 for 0 ns; - check_only_log(bus_logger, "bresp - Got AXI response EXOKAY(01) expected DECERR(11)", failure); - unmock(bus_logger); - - done <= true; - - elsif enabled("Test single read") then - arready <= '1'; - wait until (arready and arvalid) = '1' and rising_edge(clk); - arready <= '0'; - check_equal(araddr, std_logic_vector'(x"01234567"), "araddr"); - - rvalid <= '1'; - rresp <= axi_resp_okay; - rdata <= x"5566"; - wait until (rready and rvalid) = '1' and rising_edge(clk); - rvalid <= '0'; - - done <= true; - - elsif enabled("Test read not okay") then - arready <= '1'; - wait until (arready and arvalid) = '1' and rising_edge(clk); - arready <= '0'; - - rvalid <= '1'; - rresp <= axi_resp_decerr; - rdata <= x"5566"; - mock(bus_logger, failure); - wait until (rready and rvalid) = '1' and rising_edge(clk); - rvalid <= '0'; - wait until mock_queue_length > 0 for 0 ns; - check_only_log(bus_logger, "rresp - Got AXI response DECERR(11) expected OKAY(00)", failure); - unmock(bus_logger); - - done <= true; - - elsif enabled("Test read with axi resp") then - arready <= '1'; - wait until (arready and arvalid) = '1' and rising_edge(clk); - arready <= '0'; - check_equal(araddr, std_logic_vector'(x"01234567"), "araddr"); - - rvalid <= '1'; - rresp <= axi_resp_slverr; - rdata <= x"0000"; - wait until (rready and rvalid) = '1' and rising_edge(clk); - rvalid <= '0'; - - done <= true; - - elsif enabled("Test read with wrong axi resp") then - arready <= '1'; - wait until (arready and arvalid) = '1' and rising_edge(clk); - arready <= '0'; - check_equal(araddr, std_logic_vector'(x"01234567"), "araddr"); - - rvalid <= '1'; - rresp <= axi_resp_decerr; - rdata <= x"0000"; - mock(bus_logger, failure); - wait until (rready and rvalid) = '1' and rising_edge(clk); - rvalid <= '0'; - wait until mock_queue_length > 0 for 0 ns; - check_only_log(bus_logger, "rresp - Got AXI response DECERR(11) expected EXOKAY(01)", failure); - unmock(bus_logger); - - done <= true; - - elsif enabled("Test random") then - for i in 0 to num_random_tests-1 loop - if rnd.RandInt(0, 1) = 0 then - arready <= '1'; - wait until (arready and arvalid) = '1' and rising_edge(clk); - arready <= '0'; - check_equal(araddr, rnd.RandSlv(araddr'length), "araddr"); - - rvalid <= '1'; - rresp <= axi_resp_okay; - rdata <= rnd.RandSlv(rdata'length); - wait until (rready and rvalid) = '1' and rising_edge(clk); - rvalid <= '0'; - else - awready <= '1'; - wait until (awready and awvalid) = '1' and rising_edge(clk); - awready <= '0'; - check_equal(awaddr, rnd.RandSlv(awaddr'length), "awaddr"); - - wready <= '1'; - wait until (wready and wvalid) = '1' and rising_edge(clk); - wready <= '0'; - check_equal(wdata, rnd.RandSlv(wdata'length), "wdata"); - check_equal(wstrb, std_logic_vector'("11"), "wstrb"); - - bvalid <= '1'; - bresp <= axi_resp_okay; - wait until (bready and bvalid) = '1' and rising_edge(clk); - bvalid <= '0'; - end if; - end loop; - done <= true; - - elsif enabled("Test random axi resp") then - for i in 0 to num_random_tests-1 loop - if rnd.RandInt(0, 1) = 0 then - arready <= '1'; - wait until (arready and arvalid) = '1' and rising_edge(clk); - arready <= '0'; - check_equal(araddr, rnd.RandSlv(araddr'length), "araddr"); - - rvalid <= '1'; - rresp <= rnd.RandSlv(axi_resp_t'length); - rdata <= rnd.RandSlv(rdata'length); - wait until (rready and rvalid) = '1' and rising_edge(clk); - rvalid <= '0'; - else - awready <= '1'; - wait until (awready and awvalid) = '1' and rising_edge(clk); - awready <= '0'; - check_equal(awaddr, rnd.RandSlv(awaddr'length), "awaddr"); - - wready <= '1'; - wait until (wready and wvalid) = '1' and rising_edge(clk); - wready <= '0'; - check_equal(wdata, rnd.RandSlv(wdata'length), "wdata"); - check_equal(wstrb, std_logic_vector'("11"), "wstrb"); - - bvalid <= '1'; - bresp <= rnd.RandSlv(axi_resp_t'length); - wait until (bready and bvalid) = '1' and rising_edge(clk); - bvalid <= '0'; - end if; - end loop; - done <= true; - end if; - end process; - - dut : entity work.axi_lite_master - generic map ( - bus_handle => bus_handle) - port map ( - aclk => clk, - arready => arready, - arvalid => arvalid, - araddr => araddr, - rready => rready, - rvalid => rvalid, - rdata => rdata, - rresp => rresp, - awready => awready, - awvalid => awvalid, - awaddr => awaddr, - wready => wready, - wvalid => wvalid, - wdata => wdata, - wstrb => wstrb, - bvalid => bvalid, - bready => bready, - bresp => bresp); - - clk <= not clk after 5 ns; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library vunit_lib; +context vunit_lib.vunit_context; +context work.com_context; + +use work.axi_pkg.all; +use work.bus_master_pkg.all; +use work.axi_lite_master_pkg.all; + +library osvvm; +use osvvm.RandomPkg.all; + +entity tb_axi_lite_master is + generic (runner_cfg : string); +end entity; + +architecture a of tb_axi_lite_master is + constant num_random_tests : integer := 128; + + signal clk : std_logic := '0'; + signal arready : std_logic := '0'; + signal arvalid : std_logic; + signal araddr : std_logic_vector(31 downto 0); + + signal rready : std_logic := '0'; + signal rvalid : std_logic; + signal rdata : std_logic_vector(15 downto 0); + signal rresp : std_logic_vector(1 downto 0); + + signal awready : std_logic := '0'; + signal awvalid : std_logic; + signal awaddr : std_logic_vector(31 downto 0); + + signal wready : std_logic := '0'; + signal wvalid : std_logic; + signal wdata : std_logic_vector(15 downto 0); + signal wstrb : std_logic_vector(1 downto 0); + + signal bvalid : std_logic := '0'; + signal bready : std_logic; + signal bresp : std_logic_vector(1 downto 0); + + constant bus_handle : bus_master_t := new_bus(data_length => wdata'length, + address_length => awaddr'length); + + signal start, done : boolean := false; +begin + + main : process + variable tmp : std_logic_vector(rdata'range); + variable rnd : RandomPType; + begin + test_runner_setup(runner, runner_cfg); + rnd.InitSeed("common_seed"); + start <= true; + wait for 0 ns; + + if run("Test single write") then + mock(get_logger(bus_handle), debug); + write_bus(net, bus_handle, x"01234567", x"1122"); + wait_until_idle(net, bus_handle); + check_only_log(get_logger(bus_handle), "Wrote 0x1122 to address 0x01234567", debug); + unmock(get_logger(bus_handle)); + + elsif run("Test single write with byte enable") then + write_bus(net, bus_handle, x"01234567", x"1122", byte_enable => "10"); + + elsif run("Test write not okay") then + write_bus(net, bus_handle, x"01234567", x"1122"); + + elsif run("Test write with axi resp") then + write_axi_lite(net, bus_handle, x"01234567", x"1122", axi_resp_slverr); + + elsif run("Test write with wrong axi resp") then + write_axi_lite(net, bus_handle, x"01234567", x"1122", axi_resp_decerr); + + elsif run("Test single read") then + mock(get_logger(bus_handle), debug); + read_bus(net, bus_handle, x"01234567", tmp); + check_equal(tmp, std_logic_vector'(x"5566"), "read data"); + check_only_log(get_logger(bus_handle), "Read 0x5566 from address 0x01234567", debug); + unmock(get_logger(bus_handle)); + + elsif run("Test read not okay") then + read_bus(net, bus_handle, x"01234567", tmp); + + elsif run("Test read with axi resp") then + read_axi_lite(net, bus_handle, x"01234567", axi_resp_slverr, tmp); + + elsif run("Test read with wrong axi resp") then + read_axi_lite(net, bus_handle, x"01234567", axi_resp_exokay, tmp); + + elsif run("Test random") then + for i in 0 to num_random_tests-1 loop + if rnd.RandInt(0, 1) = 0 then + read_bus(net, bus_handle, rnd.RandSlv(araddr'length), tmp); + check_equal(tmp, rnd.RandSlv(rdata'length), "read data"); + else + write_bus(net, bus_handle, rnd.RandSlv(awaddr'length), rnd.RandSlv(wdata'length)); + end if; + end loop; + + elsif run("Test random axi resp") then + for i in 0 to num_random_tests-1 loop + if rnd.RandInt(0, 1) = 0 then + read_axi_lite(net, bus_handle, rnd.RandSlv(araddr'length), rnd.RandSlv(axi_resp_t'length), tmp); + check_equal(tmp, rnd.RandSlv(rdata'length), "read data"); + else + write_axi_lite(net, bus_handle, rnd.RandSlv(awaddr'length), rnd.RandSlv(wdata'length), + rnd.RandSlv(axi_resp_t'length)); + end if; + end loop; + end if; + + wait for 100 ns; + + if not done then + wait until done; + end if; + + test_runner_cleanup(runner); + end process; + test_runner_watchdog(runner, 100 us); + + + support : process + variable rnd : RandomPType; + begin + rnd.InitSeed("common_seed"); + + wait until start; + + if enabled("Test single write") then + awready <= '1'; + wait until (awready and awvalid) = '1' and rising_edge(clk); + awready <= '0'; + check_equal(awaddr, std_logic_vector'(x"01234567"), "awaddr"); + + wready <= '1'; + wait until (wready and wvalid) = '1' and rising_edge(clk); + wready <= '0'; + check_equal(wdata, std_logic_vector'(x"1122"), "wdata"); + check_equal(wstrb, std_logic_vector'("11"), "wstrb"); + + bvalid <= '1'; + bresp <= axi_resp_okay; + wait until (bready and bvalid) = '1' and rising_edge(clk); + bvalid <= '0'; + + done <= true; + + elsif enabled("Test single write with byte enable") then + awready <= '1'; + wait until (awready and awvalid) = '1' and rising_edge(clk); + awready <= '0'; + check_equal(awaddr, std_logic_vector'(x"01234567"), "awaddr"); + + wready <= '1'; + wait until (wready and wvalid) = '1' and rising_edge(clk); + wready <= '0'; + check_equal(wdata, std_logic_vector'(x"1122"), "wdata"); + check_equal(wstrb, std_logic_vector'("10"), "wstrb"); + + bvalid <= '1'; + bresp <= axi_resp_okay; + wait until (bready and bvalid) = '1' and rising_edge(clk); + bvalid <= '0'; + + done <= true; + + elsif enabled("Test write not okay") then + awready <= '1'; + wait until (awready and awvalid) = '1' and rising_edge(clk); + awready <= '0'; + + wready <= '1'; + wait until (wready and wvalid) = '1' and rising_edge(clk); + wready <= '0'; + + bvalid <= '1'; + bresp <= axi_resp_slverr; + mock(bus_logger, failure); + wait until (bready and bvalid) = '1' and rising_edge(clk); + bvalid <= '0'; + wait until mock_queue_length > 0 for 0 ns; + check_only_log(bus_logger, "bresp - Got AXI response SLVERR(10) expected OKAY(00)", failure); + unmock(bus_logger); + + done <= true; + + elsif enabled("Test write with axi resp") then + awready <= '1'; + wait until (awready and awvalid) = '1' and rising_edge(clk); + awready <= '0'; + check_equal(awaddr, std_logic_vector'(x"01234567"), "awaddr"); + + wready <= '1'; + wait until (wready and wvalid) = '1' and rising_edge(clk); + wready <= '0'; + check_equal(wdata, std_logic_vector'(x"1122"), "wdata"); + check_equal(wstrb, std_logic_vector'("11"), "wstrb"); + + bvalid <= '1'; + bresp <= axi_resp_slverr; + wait until (bready and bvalid) = '1' and rising_edge(clk); + bvalid <= '0'; + + done <= true; + + elsif enabled("Test write with wrong axi resp") then + awready <= '1'; + wait until (awready and awvalid) = '1' and rising_edge(clk); + awready <= '0'; + check_equal(awaddr, std_logic_vector'(x"01234567"), "awaddr"); + + wready <= '1'; + wait until (wready and wvalid) = '1' and rising_edge(clk); + wready <= '0'; + check_equal(wdata, std_logic_vector'(x"1122"), "wdata"); + check_equal(wstrb, std_logic_vector'("11"), "wstrb"); + + bvalid <= '1'; + bresp <= axi_resp_exokay; + mock(bus_logger, failure); + wait until (bready and bvalid) = '1' and rising_edge(clk); + bvalid <= '0'; + wait until mock_queue_length > 0 for 0 ns; + check_only_log(bus_logger, "bresp - Got AXI response EXOKAY(01) expected DECERR(11)", failure); + unmock(bus_logger); + + done <= true; + + elsif enabled("Test single read") then + arready <= '1'; + wait until (arready and arvalid) = '1' and rising_edge(clk); + arready <= '0'; + check_equal(araddr, std_logic_vector'(x"01234567"), "araddr"); + + rvalid <= '1'; + rresp <= axi_resp_okay; + rdata <= x"5566"; + wait until (rready and rvalid) = '1' and rising_edge(clk); + rvalid <= '0'; + + done <= true; + + elsif enabled("Test read not okay") then + arready <= '1'; + wait until (arready and arvalid) = '1' and rising_edge(clk); + arready <= '0'; + + rvalid <= '1'; + rresp <= axi_resp_decerr; + rdata <= x"5566"; + mock(bus_logger, failure); + wait until (rready and rvalid) = '1' and rising_edge(clk); + rvalid <= '0'; + wait until mock_queue_length > 0 for 0 ns; + check_only_log(bus_logger, "rresp - Got AXI response DECERR(11) expected OKAY(00)", failure); + unmock(bus_logger); + + done <= true; + + elsif enabled("Test read with axi resp") then + arready <= '1'; + wait until (arready and arvalid) = '1' and rising_edge(clk); + arready <= '0'; + check_equal(araddr, std_logic_vector'(x"01234567"), "araddr"); + + rvalid <= '1'; + rresp <= axi_resp_slverr; + rdata <= x"0000"; + wait until (rready and rvalid) = '1' and rising_edge(clk); + rvalid <= '0'; + + done <= true; + + elsif enabled("Test read with wrong axi resp") then + arready <= '1'; + wait until (arready and arvalid) = '1' and rising_edge(clk); + arready <= '0'; + check_equal(araddr, std_logic_vector'(x"01234567"), "araddr"); + + rvalid <= '1'; + rresp <= axi_resp_decerr; + rdata <= x"0000"; + mock(bus_logger, failure); + wait until (rready and rvalid) = '1' and rising_edge(clk); + rvalid <= '0'; + wait until mock_queue_length > 0 for 0 ns; + check_only_log(bus_logger, "rresp - Got AXI response DECERR(11) expected EXOKAY(01)", failure); + unmock(bus_logger); + + done <= true; + + elsif enabled("Test random") then + for i in 0 to num_random_tests-1 loop + if rnd.RandInt(0, 1) = 0 then + arready <= '1'; + wait until (arready and arvalid) = '1' and rising_edge(clk); + arready <= '0'; + check_equal(araddr, rnd.RandSlv(araddr'length), "araddr"); + + rvalid <= '1'; + rresp <= axi_resp_okay; + rdata <= rnd.RandSlv(rdata'length); + wait until (rready and rvalid) = '1' and rising_edge(clk); + rvalid <= '0'; + else + awready <= '1'; + wait until (awready and awvalid) = '1' and rising_edge(clk); + awready <= '0'; + check_equal(awaddr, rnd.RandSlv(awaddr'length), "awaddr"); + + wready <= '1'; + wait until (wready and wvalid) = '1' and rising_edge(clk); + wready <= '0'; + check_equal(wdata, rnd.RandSlv(wdata'length), "wdata"); + check_equal(wstrb, std_logic_vector'("11"), "wstrb"); + + bvalid <= '1'; + bresp <= axi_resp_okay; + wait until (bready and bvalid) = '1' and rising_edge(clk); + bvalid <= '0'; + end if; + end loop; + done <= true; + + elsif enabled("Test random axi resp") then + for i in 0 to num_random_tests-1 loop + if rnd.RandInt(0, 1) = 0 then + arready <= '1'; + wait until (arready and arvalid) = '1' and rising_edge(clk); + arready <= '0'; + check_equal(araddr, rnd.RandSlv(araddr'length), "araddr"); + + rvalid <= '1'; + rresp <= rnd.RandSlv(axi_resp_t'length); + rdata <= rnd.RandSlv(rdata'length); + wait until (rready and rvalid) = '1' and rising_edge(clk); + rvalid <= '0'; + else + awready <= '1'; + wait until (awready and awvalid) = '1' and rising_edge(clk); + awready <= '0'; + check_equal(awaddr, rnd.RandSlv(awaddr'length), "awaddr"); + + wready <= '1'; + wait until (wready and wvalid) = '1' and rising_edge(clk); + wready <= '0'; + check_equal(wdata, rnd.RandSlv(wdata'length), "wdata"); + check_equal(wstrb, std_logic_vector'("11"), "wstrb"); + + bvalid <= '1'; + bresp <= rnd.RandSlv(axi_resp_t'length); + wait until (bready and bvalid) = '1' and rising_edge(clk); + bvalid <= '0'; + end if; + end loop; + done <= true; + end if; + end process; + + dut : entity work.axi_lite_master + generic map ( + bus_handle => bus_handle) + port map ( + aclk => clk, + arready => arready, + arvalid => arvalid, + araddr => araddr, + rready => rready, + rvalid => rvalid, + rdata => rdata, + rresp => rresp, + awready => awready, + awvalid => awvalid, + awaddr => awaddr, + wready => wready, + wvalid => wvalid, + wdata => wdata, + wstrb => wstrb, + bvalid => bvalid, + bready => bready, + bresp => bresp); + + clk <= not clk after 5 ns; + +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_axi_read_slave.vhd b/vunit/vhdl/verification_components/test/tb_axi_read_slave.vhd index a4e062890..606969e83 100644 --- a/vunit/vhdl/verification_components/test/tb_axi_read_slave.vhd +++ b/vunit/vhdl/verification_components/test/tb_axi_read_slave.vhd @@ -1,418 +1,418 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -context work.vunit_context; -context work.com_context; -context work.vc_context; - -use work.axi_pkg.all; -use work.integer_vector_ptr_pkg.all; -use work.random_pkg.all; - -library osvvm; -use osvvm.RandomPkg.all; - -entity tb_axi_read_slave is - generic (runner_cfg : string); -end entity; - -architecture a of tb_axi_read_slave is - signal clk : std_logic := '1'; - - constant log_data_size : integer := 4; - constant data_size : integer := 2**log_data_size; - - signal arvalid : std_logic := '0'; - signal arready : std_logic; - signal arid : std_logic_vector(3 downto 0); - signal araddr : std_logic_vector(31 downto 0); - signal arlen : axi4_len_t; - signal arsize : axi4_size_t; - signal arburst : axi_burst_type_t; - - signal rvalid : std_logic; - signal rready : std_logic := '0'; - signal rid : std_logic_vector(arid'range); - signal rdata : std_logic_vector(8*data_size-1 downto 0); - signal rresp : axi_resp_t; - signal rlast : std_logic; - - constant memory : memory_t := new_memory; - constant axi_slave : axi_slave_t := new_axi_slave(address_fifo_depth => 1, - memory => memory); - -begin - main : process - variable rnd : RandomPType; - - procedure write_addr(id : std_logic_vector; - addr : natural; - len : natural; - log_size : natural; - burst : axi_burst_type_t) is - begin - arvalid <= '1'; - arid <= id; - araddr <= std_logic_vector(to_unsigned(addr, araddr'length)); - arlen <= std_logic_vector(to_unsigned(len-1, arlen'length)); - arsize <= std_logic_vector(to_unsigned(log_size, arsize'length)); - arburst <= burst; - - wait until (arvalid and arready) = '1' and rising_edge(clk); - arvalid <= '0'; - end procedure; - - procedure read_data(id : std_logic_vector; address : natural; size : natural; resp : axi_resp_t; last : boolean) is - variable idx : integer; - begin - rready <= '1'; - wait until (rvalid and rready) = '1' and rising_edge(clk); - rready <= '0'; - for i in 0 to size-1 loop - idx := (address + i) mod data_size; -- Align data bus - check_equal(rdata(8*idx+7 downto 8*idx), read_byte(memory, address+i)); - end loop; - check_equal(rid, id, "rid"); - check_equal(rresp, resp, "rresp"); - check_equal(rlast, last, "rlast"); - end procedure; - - procedure transfer(log_size, len : natural; - id : std_logic_vector; - burst : std_logic_vector) is - variable buf : buffer_t; - variable size : natural; - variable data : integer_vector_ptr_t; - begin - size := 2**log_size; - random_integer_vector_ptr(rnd, data, size * len, 0, 255); - - buf := allocate(memory, 8 * len, alignment => 4096); - for i in 0 to length(data)-1 loop - write_byte(memory, base_address(buf)+i, get(data, i)); - end loop; - - write_addr(id, base_address(buf), len, log_size, burst); - - for i in 0 to len-1 loop - read_data(id, base_address(buf)+size*i, size, axi_resp_okay, i=len-1); - end loop; - end; - - variable log_size : natural; - variable buf : buffer_t; - variable id : std_logic_vector(arid'range); - variable len : natural; - variable burst : axi_burst_type_t; - variable start_time, diff_time : time; - begin - test_runner_setup(runner, runner_cfg); - rnd.InitSeed(rnd'instance_name); - - if run("Test random read") then - for test_idx in 0 to 32-1 loop - - id := rnd.RandSlv(arid'length); - case rnd.RandInt(1) is - when 0 => - burst := axi_burst_type_fixed; - len := 1; - when 1 => - burst := axi_burst_type_incr; - len := rnd.RandInt(1, 2**arlen'length); - when others => - assert false; - end case; - - log_size := rnd.RandInt(0, 3); - transfer(log_size, len, id, burst); - end loop; - - elsif run("Test random data stall") then - log_size := 3; - len := 128; - id := (arid'range => '0'); - burst := axi_burst_type_incr; - - for i in 0 to 4 loop - if i = 2 then - set_data_stall_probability(net, axi_slave, 0.9); - else - set_data_stall_probability(net, axi_slave, 0.0); - end if; - - start_time := now; - - transfer(log_size, len, id, burst); - info("diff_time := " & to_string(now - start_time)); - - if i = 1 or i = 4 then - -- First two and last two runs should have the same time with 0.0 - -- stall probability - check_equal(diff_time, now - start_time); - elsif i = 2 then - -- Middle run should have larger time - check(5*diff_time < now - start_time); - end if; - - diff_time := now - start_time; - end loop; - - elsif run("Test response latency") then - log_size := 3; - len := 128; - id := (arid'range => '0'); - burst := axi_burst_type_incr; - - for i in 0 to 1 loop - if i = 1 then - set_response_latency(net, axi_slave, 1 us); - end if; - - start_time := now; - - transfer(log_size, len, id, burst); - info("diff_time := " & to_string(now - start_time)); - - if i = 1 then - check_equal(diff_time + 1 us, now - start_time); - end if; - - diff_time := now - start_time; - end loop; - - elsif run("Test that permissions are checked") then - -- Also checks that the axi slave logger is used for memory errors - buf := allocate(memory, data_size, permissions => no_access); - mock(axi_slave_logger, failure); - write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_fixed); - wait until mock_queue_length > 0 and rising_edge(clk); - check_only_log(axi_slave_logger, - "Reading from address 0 at offset 0 within anonymous buffer at range (0 to 15) without permission (no_access)", - failure); - unmock(axi_slave_logger); - - elsif run("Test error on unsupported wrap burst") then - mock(axi_slave_logger, failure); - - buf := allocate(memory, 8); - write_addr(x"2", base_address(buf), 2, 0, axi_burst_type_wrap); - wait until mock_queue_length > 0 and rising_edge(clk); - check_only_log(axi_slave_logger, "Wrapping burst type not supported", failure); - unmock(axi_slave_logger); - - elsif run("Test error 4KByte boundary crossing") then - buf := allocate(memory, 4096+32, alignment => 4096); - mock(axi_slave_logger, failure); - write_addr(x"2", base_address(buf)+4000, 256, 0, axi_burst_type_incr); - wait until mock_queue_length > 0 and rising_edge(clk); - check_only_log(axi_slave_logger, "Crossing 4KByte boundary. First page = 0 (4000/4096), last page = 1 (4255/4096)", failure); - unmock(axi_slave_logger); - - elsif run("Test no error on 4KByte boundary crossing with disabled check") then - buf := allocate(memory, 4096+32, alignment => 4096); - disable_4kbyte_boundary_check(net, axi_slave); - write_addr(x"2", base_address(buf)+4000, 256, 0, axi_burst_type_incr); - wait until arvalid = '0' and rising_edge(clk); - - elsif run("Test default address fifo depth is 1") then - buf := allocate(memory, 1024); - write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- Taken data process - write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- In the queue - for i in 0 to 127 loop - wait until rising_edge(clk); - assert arready = '0' report "Can only have one address in the queue"; - end loop; - - elsif run("Test set address fifo depth") then - buf := allocate(memory, 1024); - set_address_fifo_depth(net, axi_slave, 16); - - write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- Taken data process - for i in 1 to 16 loop - write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- In the queue - end loop; - - for i in 0 to 127 loop - wait until rising_edge(clk); - assert arready = '0' report "Address queue should be full"; - end loop; - - elsif run("Test changing address depth to smaller than content gives error") then - buf := allocate(memory, 1024); - set_address_fifo_depth(net, axi_slave, 16); - - write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- Taken data process - for i in 1 to 16 loop - write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- In the queue - end loop; - - set_address_fifo_depth(net, axi_slave, 17); - set_address_fifo_depth(net, axi_slave, 16); - - mock(axi_slave_logger, failure); - set_address_fifo_depth(net, axi_slave, 1); - check_only_log(axi_slave_logger, "New address fifo depth 1 is smaller than current content size 16", failure); - unmock(axi_slave_logger); - - elsif run("Test address stall probability") then - buf := allocate(memory, 1024); - set_address_fifo_depth(net, axi_slave, 128); - - start_time := now; - for i in 1 to 16 loop - write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); - end loop; - diff_time := now - start_time; - - set_address_stall_probability(net, axi_slave, 0.9); - start_time := now; - for i in 1 to 16 loop - write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); - end loop; - assert (now - start_time) > 5.0 * diff_time report "Should take about longer with stall probability"; - - elsif run("Test well behaved check does not fail for well behaved bursts") then - buf := allocate(memory, 128); - enable_well_behaved_check(net, axi_slave); - set_address_fifo_depth(net, axi_slave, 3); - set_write_response_fifo_depth(net, axi_slave, 3); - - wait until rising_edge(clk); - rready <= '1'; - assert rvalid = '0'; - -- Only allow non max size for single beat bursts - write_addr(x"0", base_address(buf), len => 1, log_size => log_data_size, burst => axi_burst_type_incr); - rready <= '1'; - assert rvalid = '0'; - write_addr(x"0", base_address(buf), len => 2, log_size => log_data_size, burst => axi_burst_type_incr); - rready <= '1'; - assert rvalid = '1'; - write_addr(x"0", base_address(buf), len => 1, log_size => 0, burst => axi_burst_type_incr); - rready <= '1'; - assert rvalid = '1'; - wait until rising_edge(clk); - rready <= '1'; - assert rvalid = '1'; - wait until rising_edge(clk); - rready <= '0'; - assert rvalid = '1'; - wait until rising_edge(clk); - rready <= '0'; - assert rvalid = '0'; - wait until rising_edge(clk); - assert rvalid = '0'; - wait until rising_edge(clk); - assert rvalid = '0'; - wait until rising_edge(clk); - assert rvalid = '0'; - - elsif run("Test well behaved check does not fail after well behaved burst finished") then - buf := allocate(memory, 128); - enable_well_behaved_check(net, axi_slave); - - wait until rising_edge(clk); - rready <= '1'; - assert rvalid = '0'; - -- Only allow non max size for single beat bursts - write_addr(x"0", base_address(buf), len => 3, log_size => log_data_size, burst => axi_burst_type_incr); - rready <= '1'; - assert rvalid = '0'; - wait until rising_edge(clk); - rready <= '1'; - assert rvalid = '1'; - wait until rising_edge(clk); - rready <= '1'; - assert rvalid = '1'; - wait until rising_edge(clk); - rready <= '0'; - assert rvalid = '1'; - wait until rising_edge(clk); - rready <= '0'; - assert rvalid = '0'; - wait until rising_edge(clk); - rready <= '0'; - wait until rising_edge(clk); - rready <= '0'; - assert rvalid = '0'; - - elsif run("Test well behaved check fails for ill behaved awsize") then - buf := allocate(memory, 8); - enable_well_behaved_check(net, axi_slave); - mock(axi_slave_logger, failure); - rready <= '1'; - wait until rising_edge(clk); - write_addr(x"0", base_address(buf), len => 2, log_size => 0, burst => axi_burst_type_incr); - check_only_log(axi_slave_logger, "Burst not well behaved, axi size = 1 but bus data width allows " & to_string(data_size), failure); - unmock(axi_slave_logger); - - elsif run("Test well behaved check fails when rready not high during active burst") then - buf := allocate(memory, 128); - enable_well_behaved_check(net, axi_slave); - mock(axi_slave_logger, failure); - wait until rising_edge(clk); - write_addr(x"0", base_address(buf), len => 2, log_size => log_data_size, burst => axi_burst_type_incr); - check_only_log(axi_slave_logger, "Burst not well behaved, rready was not high during active burst", failure); - unmock(axi_slave_logger); - - elsif run("Test well behaved check fails when wvalid not high during active burst and arready is low") then - buf := allocate(memory, 8); - enable_well_behaved_check(net, axi_slave); - mock(axi_slave_logger, failure); - set_address_stall_probability(net, axi_slave, 1.0); - - wait until rising_edge(clk); - wait until rising_edge(clk); - assert arready = '0'; - - arvalid <= '1'; - arid <= x"0"; - araddr <= std_logic_vector(to_unsigned(base_address(buf), araddr'length)); - arlen <= std_logic_vector(to_unsigned(0, arlen'length)); - arsize <= std_logic_vector(to_unsigned(log_size, arsize'length)); - arburst <= axi_burst_type_incr; - - wait until rising_edge(clk); - assert arready = '0'; - wait until mock_queue_length > 0 for 0 ns; - - check_only_log(axi_slave_logger, "Burst not well behaved, rready was not high during active burst", failure); - unmock(axi_slave_logger); - - - end if; - - test_runner_cleanup(runner); - end process; - test_runner_watchdog(runner, 1 ms); - - dut : entity work.axi_read_slave - generic map ( - axi_slave => axi_slave) - port map ( - aclk => clk, - - arvalid => arvalid, - arready => arready, - arid => arid, - araddr => araddr, - arlen => arlen, - arsize => arsize, - arburst => arburst, - - rvalid => rvalid, - rready => rready, - rid => rid, - rdata => rdata, - rresp => rresp, - rlast => rlast); - - clk <= not clk after 5 ns; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +context work.vunit_context; +context work.com_context; +context work.vc_context; + +use work.axi_pkg.all; +use work.integer_vector_ptr_pkg.all; +use work.random_pkg.all; + +library osvvm; +use osvvm.RandomPkg.all; + +entity tb_axi_read_slave is + generic (runner_cfg : string); +end entity; + +architecture a of tb_axi_read_slave is + signal clk : std_logic := '1'; + + constant log_data_size : integer := 4; + constant data_size : integer := 2**log_data_size; + + signal arvalid : std_logic := '0'; + signal arready : std_logic; + signal arid : std_logic_vector(3 downto 0); + signal araddr : std_logic_vector(31 downto 0); + signal arlen : axi4_len_t; + signal arsize : axi4_size_t; + signal arburst : axi_burst_type_t; + + signal rvalid : std_logic; + signal rready : std_logic := '0'; + signal rid : std_logic_vector(arid'range); + signal rdata : std_logic_vector(8*data_size-1 downto 0); + signal rresp : axi_resp_t; + signal rlast : std_logic; + + constant memory : memory_t := new_memory; + constant axi_slave : axi_slave_t := new_axi_slave(address_fifo_depth => 1, + memory => memory); + +begin + main : process + variable rnd : RandomPType; + + procedure write_addr(id : std_logic_vector; + addr : natural; + len : natural; + log_size : natural; + burst : axi_burst_type_t) is + begin + arvalid <= '1'; + arid <= id; + araddr <= std_logic_vector(to_unsigned(addr, araddr'length)); + arlen <= std_logic_vector(to_unsigned(len-1, arlen'length)); + arsize <= std_logic_vector(to_unsigned(log_size, arsize'length)); + arburst <= burst; + + wait until (arvalid and arready) = '1' and rising_edge(clk); + arvalid <= '0'; + end procedure; + + procedure read_data(id : std_logic_vector; address : natural; size : natural; resp : axi_resp_t; last : boolean) is + variable idx : integer; + begin + rready <= '1'; + wait until (rvalid and rready) = '1' and rising_edge(clk); + rready <= '0'; + for i in 0 to size-1 loop + idx := (address + i) mod data_size; -- Align data bus + check_equal(rdata(8*idx+7 downto 8*idx), read_byte(memory, address+i)); + end loop; + check_equal(rid, id, "rid"); + check_equal(rresp, resp, "rresp"); + check_equal(rlast, last, "rlast"); + end procedure; + + procedure transfer(log_size, len : natural; + id : std_logic_vector; + burst : std_logic_vector) is + variable buf : buffer_t; + variable size : natural; + variable data : integer_vector_ptr_t; + begin + size := 2**log_size; + random_integer_vector_ptr(rnd, data, size * len, 0, 255); + + buf := allocate(memory, 8 * len, alignment => 4096); + for i in 0 to length(data)-1 loop + write_byte(memory, base_address(buf)+i, get(data, i)); + end loop; + + write_addr(id, base_address(buf), len, log_size, burst); + + for i in 0 to len-1 loop + read_data(id, base_address(buf)+size*i, size, axi_resp_okay, i=len-1); + end loop; + end; + + variable log_size : natural; + variable buf : buffer_t; + variable id : std_logic_vector(arid'range); + variable len : natural; + variable burst : axi_burst_type_t; + variable start_time, diff_time : time; + begin + test_runner_setup(runner, runner_cfg); + rnd.InitSeed(rnd'instance_name); + + if run("Test random read") then + for test_idx in 0 to 32-1 loop + + id := rnd.RandSlv(arid'length); + case rnd.RandInt(1) is + when 0 => + burst := axi_burst_type_fixed; + len := 1; + when 1 => + burst := axi_burst_type_incr; + len := rnd.RandInt(1, 2**arlen'length); + when others => + assert false; + end case; + + log_size := rnd.RandInt(0, 3); + transfer(log_size, len, id, burst); + end loop; + + elsif run("Test random data stall") then + log_size := 3; + len := 128; + id := (arid'range => '0'); + burst := axi_burst_type_incr; + + for i in 0 to 4 loop + if i = 2 then + set_data_stall_probability(net, axi_slave, 0.9); + else + set_data_stall_probability(net, axi_slave, 0.0); + end if; + + start_time := now; + + transfer(log_size, len, id, burst); + info("diff_time := " & to_string(now - start_time)); + + if i = 1 or i = 4 then + -- First two and last two runs should have the same time with 0.0 + -- stall probability + check_equal(diff_time, now - start_time); + elsif i = 2 then + -- Middle run should have larger time + check(5*diff_time < now - start_time); + end if; + + diff_time := now - start_time; + end loop; + + elsif run("Test response latency") then + log_size := 3; + len := 128; + id := (arid'range => '0'); + burst := axi_burst_type_incr; + + for i in 0 to 1 loop + if i = 1 then + set_response_latency(net, axi_slave, 1 us); + end if; + + start_time := now; + + transfer(log_size, len, id, burst); + info("diff_time := " & to_string(now - start_time)); + + if i = 1 then + check_equal(diff_time + 1 us, now - start_time); + end if; + + diff_time := now - start_time; + end loop; + + elsif run("Test that permissions are checked") then + -- Also checks that the axi slave logger is used for memory errors + buf := allocate(memory, data_size, permissions => no_access); + mock(axi_slave_logger, failure); + write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_fixed); + wait until mock_queue_length > 0 and rising_edge(clk); + check_only_log(axi_slave_logger, + "Reading from address 0 at offset 0 within anonymous buffer at range (0 to 15) without permission (no_access)", + failure); + unmock(axi_slave_logger); + + elsif run("Test error on unsupported wrap burst") then + mock(axi_slave_logger, failure); + + buf := allocate(memory, 8); + write_addr(x"2", base_address(buf), 2, 0, axi_burst_type_wrap); + wait until mock_queue_length > 0 and rising_edge(clk); + check_only_log(axi_slave_logger, "Wrapping burst type not supported", failure); + unmock(axi_slave_logger); + + elsif run("Test error 4KByte boundary crossing") then + buf := allocate(memory, 4096+32, alignment => 4096); + mock(axi_slave_logger, failure); + write_addr(x"2", base_address(buf)+4000, 256, 0, axi_burst_type_incr); + wait until mock_queue_length > 0 and rising_edge(clk); + check_only_log(axi_slave_logger, "Crossing 4KByte boundary. First page = 0 (4000/4096), last page = 1 (4255/4096)", failure); + unmock(axi_slave_logger); + + elsif run("Test no error on 4KByte boundary crossing with disabled check") then + buf := allocate(memory, 4096+32, alignment => 4096); + disable_4kbyte_boundary_check(net, axi_slave); + write_addr(x"2", base_address(buf)+4000, 256, 0, axi_burst_type_incr); + wait until arvalid = '0' and rising_edge(clk); + + elsif run("Test default address fifo depth is 1") then + buf := allocate(memory, 1024); + write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- Taken data process + write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- In the queue + for i in 0 to 127 loop + wait until rising_edge(clk); + assert arready = '0' report "Can only have one address in the queue"; + end loop; + + elsif run("Test set address fifo depth") then + buf := allocate(memory, 1024); + set_address_fifo_depth(net, axi_slave, 16); + + write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- Taken data process + for i in 1 to 16 loop + write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- In the queue + end loop; + + for i in 0 to 127 loop + wait until rising_edge(clk); + assert arready = '0' report "Address queue should be full"; + end loop; + + elsif run("Test changing address depth to smaller than content gives error") then + buf := allocate(memory, 1024); + set_address_fifo_depth(net, axi_slave, 16); + + write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- Taken data process + for i in 1 to 16 loop + write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- In the queue + end loop; + + set_address_fifo_depth(net, axi_slave, 17); + set_address_fifo_depth(net, axi_slave, 16); + + mock(axi_slave_logger, failure); + set_address_fifo_depth(net, axi_slave, 1); + check_only_log(axi_slave_logger, "New address fifo depth 1 is smaller than current content size 16", failure); + unmock(axi_slave_logger); + + elsif run("Test address stall probability") then + buf := allocate(memory, 1024); + set_address_fifo_depth(net, axi_slave, 128); + + start_time := now; + for i in 1 to 16 loop + write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); + end loop; + diff_time := now - start_time; + + set_address_stall_probability(net, axi_slave, 0.9); + start_time := now; + for i in 1 to 16 loop + write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); + end loop; + assert (now - start_time) > 5.0 * diff_time report "Should take about longer with stall probability"; + + elsif run("Test well behaved check does not fail for well behaved bursts") then + buf := allocate(memory, 128); + enable_well_behaved_check(net, axi_slave); + set_address_fifo_depth(net, axi_slave, 3); + set_write_response_fifo_depth(net, axi_slave, 3); + + wait until rising_edge(clk); + rready <= '1'; + assert rvalid = '0'; + -- Only allow non max size for single beat bursts + write_addr(x"0", base_address(buf), len => 1, log_size => log_data_size, burst => axi_burst_type_incr); + rready <= '1'; + assert rvalid = '0'; + write_addr(x"0", base_address(buf), len => 2, log_size => log_data_size, burst => axi_burst_type_incr); + rready <= '1'; + assert rvalid = '1'; + write_addr(x"0", base_address(buf), len => 1, log_size => 0, burst => axi_burst_type_incr); + rready <= '1'; + assert rvalid = '1'; + wait until rising_edge(clk); + rready <= '1'; + assert rvalid = '1'; + wait until rising_edge(clk); + rready <= '0'; + assert rvalid = '1'; + wait until rising_edge(clk); + rready <= '0'; + assert rvalid = '0'; + wait until rising_edge(clk); + assert rvalid = '0'; + wait until rising_edge(clk); + assert rvalid = '0'; + wait until rising_edge(clk); + assert rvalid = '0'; + + elsif run("Test well behaved check does not fail after well behaved burst finished") then + buf := allocate(memory, 128); + enable_well_behaved_check(net, axi_slave); + + wait until rising_edge(clk); + rready <= '1'; + assert rvalid = '0'; + -- Only allow non max size for single beat bursts + write_addr(x"0", base_address(buf), len => 3, log_size => log_data_size, burst => axi_burst_type_incr); + rready <= '1'; + assert rvalid = '0'; + wait until rising_edge(clk); + rready <= '1'; + assert rvalid = '1'; + wait until rising_edge(clk); + rready <= '1'; + assert rvalid = '1'; + wait until rising_edge(clk); + rready <= '0'; + assert rvalid = '1'; + wait until rising_edge(clk); + rready <= '0'; + assert rvalid = '0'; + wait until rising_edge(clk); + rready <= '0'; + wait until rising_edge(clk); + rready <= '0'; + assert rvalid = '0'; + + elsif run("Test well behaved check fails for ill behaved awsize") then + buf := allocate(memory, 8); + enable_well_behaved_check(net, axi_slave); + mock(axi_slave_logger, failure); + rready <= '1'; + wait until rising_edge(clk); + write_addr(x"0", base_address(buf), len => 2, log_size => 0, burst => axi_burst_type_incr); + check_only_log(axi_slave_logger, "Burst not well behaved, axi size = 1 but bus data width allows " & to_string(data_size), failure); + unmock(axi_slave_logger); + + elsif run("Test well behaved check fails when rready not high during active burst") then + buf := allocate(memory, 128); + enable_well_behaved_check(net, axi_slave); + mock(axi_slave_logger, failure); + wait until rising_edge(clk); + write_addr(x"0", base_address(buf), len => 2, log_size => log_data_size, burst => axi_burst_type_incr); + check_only_log(axi_slave_logger, "Burst not well behaved, rready was not high during active burst", failure); + unmock(axi_slave_logger); + + elsif run("Test well behaved check fails when wvalid not high during active burst and arready is low") then + buf := allocate(memory, 8); + enable_well_behaved_check(net, axi_slave); + mock(axi_slave_logger, failure); + set_address_stall_probability(net, axi_slave, 1.0); + + wait until rising_edge(clk); + wait until rising_edge(clk); + assert arready = '0'; + + arvalid <= '1'; + arid <= x"0"; + araddr <= std_logic_vector(to_unsigned(base_address(buf), araddr'length)); + arlen <= std_logic_vector(to_unsigned(0, arlen'length)); + arsize <= std_logic_vector(to_unsigned(log_size, arsize'length)); + arburst <= axi_burst_type_incr; + + wait until rising_edge(clk); + assert arready = '0'; + wait until mock_queue_length > 0 for 0 ns; + + check_only_log(axi_slave_logger, "Burst not well behaved, rready was not high during active burst", failure); + unmock(axi_slave_logger); + + + end if; + + test_runner_cleanup(runner); + end process; + test_runner_watchdog(runner, 1 ms); + + dut : entity work.axi_read_slave + generic map ( + axi_slave => axi_slave) + port map ( + aclk => clk, + + arvalid => arvalid, + arready => arready, + arid => arid, + araddr => araddr, + arlen => arlen, + arsize => arsize, + arburst => arburst, + + rvalid => rvalid, + rready => rready, + rid => rid, + rdata => rdata, + rresp => rresp, + rlast => rlast); + + clk <= not clk after 5 ns; +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_axi_slave_private_pkg.vhd b/vunit/vhdl/verification_components/test/tb_axi_slave_private_pkg.vhd index 30d6a8b56..1e107c83c 100644 --- a/vunit/vhdl/verification_components/test/tb_axi_slave_private_pkg.vhd +++ b/vunit/vhdl/verification_components/test/tb_axi_slave_private_pkg.vhd @@ -1,252 +1,252 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -context work.vunit_context; -context work.vc_context; -use work.axi_pkg.all; -use work.axi_slave_private_pkg.all; - -entity tb_axi_slave_private_pkg is - generic (runner_cfg : string); -end entity; - -architecture tb of tb_axi_slave_private_pkg is - signal init : boolean := false; - shared variable axi_slave : axi_slave_private_t; -begin - main : process - variable data : std_logic_vector(127 downto 0); - - variable axid : std_logic_vector(3 downto 0); - constant max_id : natural := 2**axid'length-1; - variable axaddr : std_logic_vector(31 downto 0); - variable axlen : axi4_len_t; - variable axsize : axi4_size_t; - variable axburst : axi_burst_type_t; - - variable burst : axi_burst_t; - variable logger : logger_t := get_logger("axi_slave"); - constant axi_public_slave : axi_slave_t := new_axi_slave(memory => new_memory, - logger => logger); - variable stat : axi_statistics_t; - - procedure slave_init(axi_slave_type : axi_slave_type_t) is - begin - axi_slave.init(axi_public_slave, - axi_slave_type, - max_id, - data); - end; - - begin - test_runner_setup(runner, runner_cfg); - - if run("test create_burst updates statistics") then - slave_init(read_slave); - init <= true; - wait for 0 ns; - axid := x"2"; - axaddr := x"00100000"; - axlen := x"ff"; - axsize := "100"; - axburst := axi_burst_type_fixed; - - get_statistics(net, axi_public_slave, stat); - check_equal(get_num_burst_with_length(stat, 256), 0, - "No burst before create"); - burst := axi_slave.create_burst(axid, axaddr, axlen, axsize, axburst); - check_equal(get_num_burst_with_length(stat, 256), 0, - "Statistics was copied before create"); - - get_statistics(net, axi_public_slave, stat); - check_equal(get_num_burst_with_length(stat, 256), 1, - "Newly read statistics has one entry"); - - burst := axi_slave.create_burst(axid, axaddr, axlen, axsize, axburst); - - get_statistics(net, axi_public_slave, stat, clear => true); - check_equal(get_num_burst_with_length(stat, 256), 2, - "One more burst"); - - check_equal(min_burst_length(stat), 256, - "min_burst_length"); - - check_equal(max_burst_length(stat), 256, - "max_burst_length"); - - get_statistics(net, axi_public_slave, stat); - - check_equal(get_num_burst_with_length(stat, 256), 0, - "Cleared"); - - elsif run("test create_burst read debug messages") then - slave_init(read_slave); - mock(logger, debug); - axid := x"2"; - axaddr := x"00100000"; - axlen := x"ff"; - axsize := "100"; - axburst := axi_burst_type_fixed; - - burst := axi_slave.create_burst(axid, axaddr, axlen, axsize, axburst); - check_only_log(logger, - "Got read burst #0 for id 2" & LF & - "arid = 0x2" & LF & - "araddr = 0x00100000" & LF & - "arlen = 255" & LF & - "arsize = 4" & LF & - "arburst = fixed (00)", - debug); - - axburst := axi_burst_type_incr; - burst := axi_slave.create_burst(axid, axaddr, axlen, axsize, axburst); - check_only_log(logger, - "Got read burst #1 for id 2" & LF & - "arid = 0x2" & LF & - "araddr = 0x00100000" & LF & - "arlen = 255" & LF & - "arsize = 4" & LF & - "arburst = incr (01)", - debug); - - -- Each id has a separate burst index - axid := x"0"; - axburst := axi_burst_type_incr; - burst := axi_slave.create_burst(axid, axaddr, axlen, axsize, axburst); - check_only_log(logger, - "Got read burst #0 for id 0" & LF & - "arid = 0x0" & LF & - "araddr = 0x00100000" & LF & - "arlen = 255" & LF & - "arsize = 4" & LF & - "arburst = incr (01)", - debug); - - unmock(logger); - - elsif run("test create_burst write debug messages") then - slave_init(write_slave); - - mock(logger, debug); - axid := x"2"; - axaddr := x"00100000"; - axlen := x"ff"; - axsize := "100"; - axburst := axi_burst_type_fixed; - - burst := axi_slave.create_burst(axid, axaddr, axlen, axsize, axburst); - check_only_log(logger, - "Got write burst #0 for id 2" & LF & - "awid = 0x2" & LF & - "awaddr = 0x00100000" & LF & - "awlen = 255" & LF & - "awsize = 4" & LF & - "awburst = fixed (00)", - debug); - - axburst := axi_burst_type_incr; - burst := axi_slave.create_burst(axid, axaddr, axlen, axsize, axburst); - check_only_log(logger, - "Got write burst #1 for id 2" & LF & - "awid = 0x2" & LF & - "awaddr = 0x00100000" & LF & - "awlen = 255" & LF & - "awsize = 4" & LF & - "awburst = incr (01)", - debug); - - -- Each id has a separate burst index - axid := x"0"; - axburst := axi_burst_type_incr; - burst := axi_slave.create_burst(axid, axaddr, axlen, axsize, axburst); - check_only_log(logger, - "Got write burst #0 for id 0" & LF & - "awid = 0x0" & LF & - "awaddr = 0x00100000" & LF & - "awlen = 255" & LF & - "awsize = 4" & LF & - "awburst = incr (01)", - debug); - - unmock(logger); - - elsif run("test pop_resp debug messages") then - slave_init(write_slave); - - mock(logger, debug); - - burst.index := 77; - burst.id := 13; - axi_slave.push_resp(burst); - - check_no_log; - - burst := axi_slave.pop_resp; - - check_only_log(logger, "Providing write response for burst #77 for id 13", debug); - - unmock(logger); - - elsif run("test pop_burst write slave debug messages") then - slave_init(write_slave); - - burst.index := 77; - burst.id := 13; - axi_slave.push_resp(burst); - axi_slave.push_burst(burst); - mock(logger, debug); - burst := axi_slave.pop_burst; - check_only_log(logger, "Start accepting data for write burst #77 for id 13", debug); - unmock(logger); - - elsif run("test pop_burst read slave debug messages") then - slave_init(read_slave); - - burst.index := 77; - burst.id := 13; - axi_slave.push_resp(burst); - axi_slave.push_burst(burst); - mock(logger, debug); - burst := axi_slave.pop_burst; - check_only_log(logger, "Start providing data for read burst #77 for id 13", debug); - unmock(logger); - - elsif run("test finish_burst read slave debug messages") then - slave_init(read_slave); - mock(logger, debug); - - burst.index := 77; - burst.id := 13; - axi_slave.push_resp(burst); - axi_slave.finish_burst(burst); - check_only_log(logger, "Providing last data for read burst #77 for id 13", debug); - - unmock(logger); - - elsif run("test finish_burst write slave debug messages") then - slave_init(write_slave); - mock(logger, debug); - - burst.index := 77; - burst.id := 13; - axi_slave.push_resp(burst); - axi_slave.finish_burst(burst); - check_only_log(logger, "Accepted last data for write burst #77 for id 13", debug); - - unmock(logger); - end if; - test_runner_cleanup(runner); - end process; - - slave_main : process - begin - wait until init; - main_loop(axi_slave, net); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +context work.vunit_context; +context work.vc_context; +use work.axi_pkg.all; +use work.axi_slave_private_pkg.all; + +entity tb_axi_slave_private_pkg is + generic (runner_cfg : string); +end entity; + +architecture tb of tb_axi_slave_private_pkg is + signal init : boolean := false; + shared variable axi_slave : axi_slave_private_t; +begin + main : process + variable data : std_logic_vector(127 downto 0); + + variable axid : std_logic_vector(3 downto 0); + constant max_id : natural := 2**axid'length-1; + variable axaddr : std_logic_vector(31 downto 0); + variable axlen : axi4_len_t; + variable axsize : axi4_size_t; + variable axburst : axi_burst_type_t; + + variable burst : axi_burst_t; + variable logger : logger_t := get_logger("axi_slave"); + constant axi_public_slave : axi_slave_t := new_axi_slave(memory => new_memory, + logger => logger); + variable stat : axi_statistics_t; + + procedure slave_init(axi_slave_type : axi_slave_type_t) is + begin + axi_slave.init(axi_public_slave, + axi_slave_type, + max_id, + data); + end; + + begin + test_runner_setup(runner, runner_cfg); + + if run("test create_burst updates statistics") then + slave_init(read_slave); + init <= true; + wait for 0 ns; + axid := x"2"; + axaddr := x"00100000"; + axlen := x"ff"; + axsize := "100"; + axburst := axi_burst_type_fixed; + + get_statistics(net, axi_public_slave, stat); + check_equal(get_num_burst_with_length(stat, 256), 0, + "No burst before create"); + burst := axi_slave.create_burst(axid, axaddr, axlen, axsize, axburst); + check_equal(get_num_burst_with_length(stat, 256), 0, + "Statistics was copied before create"); + + get_statistics(net, axi_public_slave, stat); + check_equal(get_num_burst_with_length(stat, 256), 1, + "Newly read statistics has one entry"); + + burst := axi_slave.create_burst(axid, axaddr, axlen, axsize, axburst); + + get_statistics(net, axi_public_slave, stat, clear => true); + check_equal(get_num_burst_with_length(stat, 256), 2, + "One more burst"); + + check_equal(min_burst_length(stat), 256, + "min_burst_length"); + + check_equal(max_burst_length(stat), 256, + "max_burst_length"); + + get_statistics(net, axi_public_slave, stat); + + check_equal(get_num_burst_with_length(stat, 256), 0, + "Cleared"); + + elsif run("test create_burst read debug messages") then + slave_init(read_slave); + mock(logger, debug); + axid := x"2"; + axaddr := x"00100000"; + axlen := x"ff"; + axsize := "100"; + axburst := axi_burst_type_fixed; + + burst := axi_slave.create_burst(axid, axaddr, axlen, axsize, axburst); + check_only_log(logger, + "Got read burst #0 for id 2" & LF & + "arid = 0x2" & LF & + "araddr = 0x00100000" & LF & + "arlen = 255" & LF & + "arsize = 4" & LF & + "arburst = fixed (00)", + debug); + + axburst := axi_burst_type_incr; + burst := axi_slave.create_burst(axid, axaddr, axlen, axsize, axburst); + check_only_log(logger, + "Got read burst #1 for id 2" & LF & + "arid = 0x2" & LF & + "araddr = 0x00100000" & LF & + "arlen = 255" & LF & + "arsize = 4" & LF & + "arburst = incr (01)", + debug); + + -- Each id has a separate burst index + axid := x"0"; + axburst := axi_burst_type_incr; + burst := axi_slave.create_burst(axid, axaddr, axlen, axsize, axburst); + check_only_log(logger, + "Got read burst #0 for id 0" & LF & + "arid = 0x0" & LF & + "araddr = 0x00100000" & LF & + "arlen = 255" & LF & + "arsize = 4" & LF & + "arburst = incr (01)", + debug); + + unmock(logger); + + elsif run("test create_burst write debug messages") then + slave_init(write_slave); + + mock(logger, debug); + axid := x"2"; + axaddr := x"00100000"; + axlen := x"ff"; + axsize := "100"; + axburst := axi_burst_type_fixed; + + burst := axi_slave.create_burst(axid, axaddr, axlen, axsize, axburst); + check_only_log(logger, + "Got write burst #0 for id 2" & LF & + "awid = 0x2" & LF & + "awaddr = 0x00100000" & LF & + "awlen = 255" & LF & + "awsize = 4" & LF & + "awburst = fixed (00)", + debug); + + axburst := axi_burst_type_incr; + burst := axi_slave.create_burst(axid, axaddr, axlen, axsize, axburst); + check_only_log(logger, + "Got write burst #1 for id 2" & LF & + "awid = 0x2" & LF & + "awaddr = 0x00100000" & LF & + "awlen = 255" & LF & + "awsize = 4" & LF & + "awburst = incr (01)", + debug); + + -- Each id has a separate burst index + axid := x"0"; + axburst := axi_burst_type_incr; + burst := axi_slave.create_burst(axid, axaddr, axlen, axsize, axburst); + check_only_log(logger, + "Got write burst #0 for id 0" & LF & + "awid = 0x0" & LF & + "awaddr = 0x00100000" & LF & + "awlen = 255" & LF & + "awsize = 4" & LF & + "awburst = incr (01)", + debug); + + unmock(logger); + + elsif run("test pop_resp debug messages") then + slave_init(write_slave); + + mock(logger, debug); + + burst.index := 77; + burst.id := 13; + axi_slave.push_resp(burst); + + check_no_log; + + burst := axi_slave.pop_resp; + + check_only_log(logger, "Providing write response for burst #77 for id 13", debug); + + unmock(logger); + + elsif run("test pop_burst write slave debug messages") then + slave_init(write_slave); + + burst.index := 77; + burst.id := 13; + axi_slave.push_resp(burst); + axi_slave.push_burst(burst); + mock(logger, debug); + burst := axi_slave.pop_burst; + check_only_log(logger, "Start accepting data for write burst #77 for id 13", debug); + unmock(logger); + + elsif run("test pop_burst read slave debug messages") then + slave_init(read_slave); + + burst.index := 77; + burst.id := 13; + axi_slave.push_resp(burst); + axi_slave.push_burst(burst); + mock(logger, debug); + burst := axi_slave.pop_burst; + check_only_log(logger, "Start providing data for read burst #77 for id 13", debug); + unmock(logger); + + elsif run("test finish_burst read slave debug messages") then + slave_init(read_slave); + mock(logger, debug); + + burst.index := 77; + burst.id := 13; + axi_slave.push_resp(burst); + axi_slave.finish_burst(burst); + check_only_log(logger, "Providing last data for read burst #77 for id 13", debug); + + unmock(logger); + + elsif run("test finish_burst write slave debug messages") then + slave_init(write_slave); + mock(logger, debug); + + burst.index := 77; + burst.id := 13; + axi_slave.push_resp(burst); + axi_slave.finish_burst(burst); + check_only_log(logger, "Accepted last data for write burst #77 for id 13", debug); + + unmock(logger); + end if; + test_runner_cleanup(runner); + end process; + + slave_main : process + begin + wait until init; + main_loop(axi_slave, net); + end process; +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_axi_statistics_pkg.vhd b/vunit/vhdl/verification_components/test/tb_axi_statistics_pkg.vhd index e76a5c978..c42781ab2 100644 --- a/vunit/vhdl/verification_components/test/tb_axi_statistics_pkg.vhd +++ b/vunit/vhdl/verification_components/test/tb_axi_statistics_pkg.vhd @@ -1,87 +1,87 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - --- vunit: run_all_in_same_sim - -use work.axi_pkg.all; -context work.vunit_context; -context work.vc_context; - -entity tb_axi_statistics_pkg is - generic (runner_cfg : string); -end entity; - -architecture tb of tb_axi_statistics_pkg is -begin - main : process - variable stat, stat2 : axi_statistics_t; - begin - test_runner_setup(runner, runner_cfg); - check(stat = null_axi_statistics); - - while test_suite loop - deallocate(stat); - check(stat = null_axi_statistics); - - if run("test new_axi_statistics") then - stat := new_axi_statistics; - check_equal(num_bursts(stat), 0); - check_equal(min_burst_length(stat), 0); - check_equal(max_burst_length(stat), 0); - - for i in 0 to max_axi4_burst_length loop - check_equal(get_num_burst_with_length(stat, i), 0); - end loop; - check_equal(get_num_burst_with_length(stat, max_axi4_burst_length+1), 0); - - deallocate(stat); - check(stat = null_axi_statistics); - - elsif run("test add_burst_length") then - stat := new_axi_statistics; - - add_burst_length(stat, 7); - add_burst_length(stat, 7); - add_burst_length(stat, 17); - - check_equal(num_bursts(stat), 3); - check_equal(min_burst_length(stat), 7); - check_equal(max_burst_length(stat), 17); - - for i in 0 to max_axi4_burst_length loop - if i = 7 then - check_equal(get_num_burst_with_length(stat, i), 2); - elsif i = 17 then - check_equal(get_num_burst_with_length(stat, i), 1); - else - check_equal(get_num_burst_with_length(stat, i), 0); - end if; - end loop; - - elsif run("test copy") then - stat := new_axi_statistics; - add_burst_length(stat, 7); - stat2 := copy(stat); - add_burst_length(stat, 17); - check_equal(num_bursts(stat), 2); - clear(stat); - check_equal(num_bursts(stat2), 1); - check_equal(get_num_burst_with_length(stat2, 7), 1); - check_equal(get_num_burst_with_length(stat2, 17), 0); - - elsif run("test clear") then - stat := new_axi_statistics; - add_burst_length(stat, 7); - add_burst_length(stat, 17); - clear(stat); - check_equal(num_bursts(stat), 0); - check_equal(get_num_burst_with_length(stat, 7), 0); - check_equal(get_num_burst_with_length(stat, 17), 0); - end if; - end loop; - test_runner_cleanup(runner); - end process; -end; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +-- vunit: run_all_in_same_sim + +use work.axi_pkg.all; +context work.vunit_context; +context work.vc_context; + +entity tb_axi_statistics_pkg is + generic (runner_cfg : string); +end entity; + +architecture tb of tb_axi_statistics_pkg is +begin + main : process + variable stat, stat2 : axi_statistics_t; + begin + test_runner_setup(runner, runner_cfg); + check(stat = null_axi_statistics); + + while test_suite loop + deallocate(stat); + check(stat = null_axi_statistics); + + if run("test new_axi_statistics") then + stat := new_axi_statistics; + check_equal(num_bursts(stat), 0); + check_equal(min_burst_length(stat), 0); + check_equal(max_burst_length(stat), 0); + + for i in 0 to max_axi4_burst_length loop + check_equal(get_num_burst_with_length(stat, i), 0); + end loop; + check_equal(get_num_burst_with_length(stat, max_axi4_burst_length+1), 0); + + deallocate(stat); + check(stat = null_axi_statistics); + + elsif run("test add_burst_length") then + stat := new_axi_statistics; + + add_burst_length(stat, 7); + add_burst_length(stat, 7); + add_burst_length(stat, 17); + + check_equal(num_bursts(stat), 3); + check_equal(min_burst_length(stat), 7); + check_equal(max_burst_length(stat), 17); + + for i in 0 to max_axi4_burst_length loop + if i = 7 then + check_equal(get_num_burst_with_length(stat, i), 2); + elsif i = 17 then + check_equal(get_num_burst_with_length(stat, i), 1); + else + check_equal(get_num_burst_with_length(stat, i), 0); + end if; + end loop; + + elsif run("test copy") then + stat := new_axi_statistics; + add_burst_length(stat, 7); + stat2 := copy(stat); + add_burst_length(stat, 17); + check_equal(num_bursts(stat), 2); + clear(stat); + check_equal(num_bursts(stat2), 1); + check_equal(get_num_burst_with_length(stat2, 7), 1); + check_equal(get_num_burst_with_length(stat2, 17), 0); + + elsif run("test clear") then + stat := new_axi_statistics; + add_burst_length(stat, 7); + add_burst_length(stat, 17); + clear(stat); + check_equal(num_bursts(stat), 0); + check_equal(get_num_burst_with_length(stat, 7), 0); + check_equal(get_num_burst_with_length(stat, 17), 0); + end if; + end loop; + test_runner_cleanup(runner); + end process; +end; diff --git a/vunit/vhdl/verification_components/test/tb_axi_stream.vhd b/vunit/vhdl/verification_components/test/tb_axi_stream.vhd index 0b28f82a9..a52de31d0 100644 --- a/vunit/vhdl/verification_components/test/tb_axi_stream.vhd +++ b/vunit/vhdl/verification_components/test/tb_axi_stream.vhd @@ -1,501 +1,501 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -context work.vunit_context; -context work.com_context; -context work.data_types_context; -use work.axi_stream_pkg.all; -use work.stream_master_pkg.all; -use work.stream_slave_pkg.all; -use work.sync_pkg.all; - -entity tb_axi_stream is - generic(runner_cfg : string); -end entity; - -architecture a of tb_axi_stream is - constant master_axi_stream : axi_stream_master_t := new_axi_stream_master( - data_length => 8, id_length => 8, dest_length => 8, user_length => 8, - logger => get_logger("master"), actor => new_actor("master"), - monitor => default_axi_stream_monitor, protocol_checker => default_axi_stream_protocol_checker - ); - constant master_stream : stream_master_t := as_stream(master_axi_stream); - constant master_sync : sync_handle_t := as_sync(master_axi_stream); - - constant slave_axi_stream : axi_stream_slave_t := new_axi_stream_slave( - data_length => 8, id_length => 8, dest_length => 8, user_length => 8, - logger => get_logger("slave"), actor => new_actor("slave"), - monitor => default_axi_stream_monitor, protocol_checker => default_axi_stream_protocol_checker - ); - constant slave_stream : stream_slave_t := as_stream(slave_axi_stream); - constant slave_sync : sync_handle_t := as_sync(slave_axi_stream); - - constant monitor : axi_stream_monitor_t := new_axi_stream_monitor( - data_length => 8, id_length => 8, dest_length => 8, user_length => 8, - logger => get_logger("monitor"), actor => new_actor("monitor"), - protocol_checker => default_axi_stream_protocol_checker - ); - - constant protocol_checker : axi_stream_protocol_checker_t := new_axi_stream_protocol_checker( - data_length => 8, id_length => 8, dest_length => 8, user_length => 8, - logger => get_logger("protocol_checker"), - max_waits => 8 - ); - - constant n_monitors : natural := 3; - - signal aclk : std_logic := '0'; - signal areset_n : std_logic := '1'; - signal tvalid : std_logic; - signal tready : std_logic; - signal tdata : std_logic_vector(data_length(slave_axi_stream)-1 downto 0); - signal tlast : std_logic; - signal tkeep : std_logic_vector(data_length(slave_axi_stream)/8-1 downto 0); - signal tstrb : std_logic_vector(data_length(slave_axi_stream)/8-1 downto 0); - signal tid : std_logic_vector(id_length(slave_axi_stream)-1 downto 0); - signal tdest : std_logic_vector(dest_length(slave_axi_stream)-1 downto 0); - signal tuser : std_logic_vector(user_length(slave_axi_stream)-1 downto 0); - - signal not_valid : std_logic; - signal not_valid_data : std_logic; - signal not_valid_keep : std_logic; - signal not_valid_strb : std_logic; - signal not_valid_id : std_logic; - signal not_valid_dest : std_logic; - signal not_valid_user : std_logic; -begin - - main : process - constant subscriber : actor_t := new_actor("main"); - - variable data : std_logic_vector(tdata'range); - variable last_bool : boolean; - variable last : std_logic; - variable keep : std_logic_vector(tkeep'range); - variable strb : std_logic_vector(tstrb'range); - variable id : std_logic_vector(tid'range); - variable dest : std_logic_vector(tdest'range); - variable user : std_logic_vector(tuser'range); - - variable axi_stream_transaction : axi_stream_transaction_t( - tdata(tdata'range), - tkeep(tkeep'range), - tstrb(tstrb'range), - tid(tid'range), - tdest(tdest'range), - tuser(tuser'range) - ); - variable reference_queue : queue_t := new_queue; - variable reference : stream_reference_t; - variable msg : msg_t; - variable msg_type : msg_type_t; - variable timestamp : time := 0 ns; - - variable mocklogger : logger_t; - - procedure get_axi_stream_transaction(variable axi_stream_transaction : out axi_stream_transaction_t) is - begin - receive(net, subscriber, msg); - msg_type := message_type(msg); - handle_axi_stream_transaction(msg_type, msg, axi_stream_transaction); - check(is_already_handled(msg_type)); - end; - - begin - test_runner_setup(runner, runner_cfg); - subscribe(subscriber, find("monitor")); - subscribe(subscriber, find("master")); - subscribe(subscriber, find("slave")); - show(get_logger("monitor"), display_handler, debug); - show(get_logger("master"), display_handler, debug); - show(get_logger("slave"), display_handler, debug); - - wait for 20 ns; - - if run("test single push and pop") then - push_stream(net, master_stream, x"77", true); - pop_stream(net, slave_stream, data, last_bool); - check_equal(data, std_logic_vector'(x"77"), result("for pop stream data")); - check_true(last_bool, result("for pop stream last")); - - for i in 1 to n_monitors loop - get_axi_stream_transaction(axi_stream_transaction); - check_equal( - axi_stream_transaction.tdata, - std_logic_vector'(x"77"), - result("for axi_stream_transaction.tdata") - ); - check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); - end loop; - elsif run("test reset") then - wait until rising_edge(aclk); - areset_n <= '0'; - wait until rising_edge(aclk); - check_equal(tvalid, '0', result("for valid low check while in reset")); - areset_n <= '1'; - wait until rising_edge(aclk); - - elsif run("test single push and pop with tlast") then - push_stream(net, master_stream, x"88", true); - pop_stream(net, slave_stream, data, last_bool); - check_equal(data, std_logic_vector'(x"88"), result("for pop stream data")); - check_true(last_bool, result("for pop stream last")); - - for i in 1 to n_monitors loop - get_axi_stream_transaction(axi_stream_transaction); - check_equal( - axi_stream_transaction.tdata, - std_logic_vector'(x"88"), - result("for axi_stream_transaction.tdata") - ); - check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); - end loop; - - elsif run("test single axi push and pop") then - push_axi_stream(net, master_axi_stream, x"99", tlast => '1'); - pop_stream(net, slave_stream, data, last_bool); - check_equal(data, std_logic_vector'(x"99"), result("pop stream data")); - check_equal(last_bool, true, result("pop stream last")); - - elsif run("test single push and axi pop") then - push_stream(net, master_stream, x"AA", last => true); - pop_axi_stream(net, slave_axi_stream, data, last); - check_equal(data, std_logic_vector'(x"AA"), result("pop stream data")); - check_equal(last, '1', result("pop stream last")); - - elsif run("test single axi push and axi pop") then - push_axi_stream(net, master_axi_stream, x"99", tlast => '1', tkeep => "1", tstrb => "1", tid => x"11", tdest => x"22", tuser => x"33" ); - pop_axi_stream(net, slave_axi_stream, data, last, keep, strb, id, dest, user); - check_equal(data, std_logic_vector'(x"99"), result("pop axi stream data")); - check_equal(last, std_logic'('1'), result("pop stream last")); - check_equal(keep, std_logic_vector'("1"), result("pop stream keep")); - check_equal(strb, std_logic_vector'("1"), result("pop stream strb")); - check_equal(id, std_logic_vector'(x"11"), result("pop stream id")); - check_equal(dest, std_logic_vector'(x"22"), result("pop stream dest")); - check_equal(user, std_logic_vector'(x"33"), result("pop stream user")); - - for i in 1 to n_monitors loop - get_axi_stream_transaction(axi_stream_transaction); - check_equal(axi_stream_transaction.tdata, std_logic_vector'(x"99"), result("for axi_stream_transaction.tdata")); - check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); - check_equal(axi_stream_transaction.tkeep, std_logic_vector'("1"), result("pop stream keep")); - check_equal(axi_stream_transaction.tstrb, std_logic_vector'("1"), result("pop stream strb")); - check_equal(axi_stream_transaction.tid, std_logic_vector'(x"11"), result("pop stream id")); - check_equal(axi_stream_transaction.tdest, std_logic_vector'(x"22"), result("pop stream dest")); - check_equal(axi_stream_transaction.tuser, std_logic_vector'(x"33"), result("pop stream user")); - end loop; - - elsif run("test single stalled push and pop") then - wait until rising_edge(aclk); - wait_for_time(net, master_sync, 30 ns); - timestamp := now; - push_stream(net, master_stream, x"77", true); - pop_stream(net, slave_stream, data, last_bool); - check_equal(data, std_logic_vector'(x"77"), result("for pop stream data")); - check_true(last_bool, result("for pop stream last")); - check_equal(now - 10 ns, timestamp + 50 ns, result("for push wait time")); -- two extra cycles inserted by alignment - for i in 1 to n_monitors loop - get_axi_stream_transaction(axi_stream_transaction); - check_equal( - axi_stream_transaction.tdata, - std_logic_vector'(x"77"), - result("for axi_stream_transaction.tdata") - ); - check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); - end loop; - - elsif run("test single push and stalled pop") then - wait until rising_edge(aclk); - wait_for_time(net, slave_sync, 30 ns); - timestamp := now; - push_stream(net, master_stream, x"77", true); - pop_stream(net, slave_stream, data, last_bool); - check_equal(data, std_logic_vector'(x"77"), result("for pop stream data")); - check_true(last_bool, result("for pop stream last")); - check_equal(now - 10 ns, timestamp + 50 ns, result("for push wait time")); - - for i in 1 to n_monitors loop - get_axi_stream_transaction(axi_stream_transaction); - check_equal( - axi_stream_transaction.tdata, - std_logic_vector'(x"77"), - result("for axi_stream_transaction.tdata") - ); - check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); - end loop; - - elsif run("test single push and stalled pop with non-multiple of clock period") then - wait until rising_edge(aclk); - wait_for_time(net, slave_sync, 29 ns); - timestamp := now; - push_stream(net, master_stream, x"77", true); - pop_stream(net, slave_stream, data, last_bool); - check_equal(data, std_logic_vector'(x"77"), result("for pop stream data")); - check_true(last_bool, result("for pop stream last")); - check_equal(now - 10 ns, timestamp + 40 ns, result("for push wait time")); - - for i in 1 to n_monitors loop - get_axi_stream_transaction(axi_stream_transaction); - check_equal( - axi_stream_transaction.tdata, - std_logic_vector'(x"77"), - result("for axi_stream_transaction.tdata") - ); - check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); - end loop; - - elsif run("test single stalled push and pop with non-multiple of clock period") then - wait until rising_edge(aclk); - wait_for_time(net, master_sync, 29 ns); - timestamp := now; - push_stream(net, master_stream, x"77", true); - pop_stream(net, slave_stream, data, last_bool); - check_equal(data, std_logic_vector'(x"77"), result("for pop stream data")); - check_true(last_bool, result("for pop stream last")); - check_equal(now - 10 ns, timestamp + 40 ns, result("for push wait time")); -- Aligned to clock edge again - for i in 1 to n_monitors loop - get_axi_stream_transaction(axi_stream_transaction); - check_equal( - axi_stream_transaction.tdata, - std_logic_vector'(x"77"), - result("for axi_stream_transaction.tdata") - ); - check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); - end loop; - - elsif run("test pop before push") then - for i in 0 to 7 loop - pop_stream(net, slave_stream, reference); - push(reference_queue, reference); - end loop; - - for i in 0 to 7 loop - push_stream(net, master_stream, std_logic_vector(to_unsigned(i + 1, data'length)), true); - end loop; - - wait_until_idle(net, master_sync); -- wait until all transfers are done before checking them - wait_until_idle(net, slave_sync); - - for i in 0 to 7 loop - reference := pop(reference_queue); - await_pop_stream_reply(net, reference, data); - check_equal(data, to_unsigned(i + 1, data'length), result("for await pop stream data")); - - for j in 1 to n_monitors loop - get_axi_stream_transaction(axi_stream_transaction); - check_equal( - axi_stream_transaction.tdata, - to_unsigned(i + 1, data'length), - result("for axi_stream_transaction.tdata") - ); - end loop; - end loop; - - elsif run("test passing check") then - push_axi_stream(net, master_axi_stream, x"12", tlast => '1', tkeep => "1", tstrb => "1", tid => x"23", tdest => x"34", tuser => x"45"); - check_axi_stream(net, slave_axi_stream, x"12", '1', "1", "1", x"23", x"34", x"45", "checking axi stream"); - - elsif run("test passing reduced check") then - push_axi_stream(net, master_axi_stream, x"12", tlast => '1', tkeep => "1", tstrb => "1", tid => x"23", tdest => x"34", tuser => x"45"); - check_axi_stream(net, slave_axi_stream, x"12", '1', msg => "reduced checking axi stream"); - - elsif run("test failing check") then - - push_axi_stream(net, master_axi_stream, x"11", tlast => '0', tkeep => "0", tstrb => "0", tid => x"22", tdest => x"33", tuser => x"44"); - -- Delay mocking the logger to prevent 'invalid checks' from failing the checks below - wait until rising_edge (aclk) and tvalid = '1'; - - mocklogger := get_logger("check"); - mock(mocklogger); - - check_axi_stream(net, slave_axi_stream, x"12", '1', "1", "1", x"23", x"34", x"45", "checking axi stream"); - check_log(mocklogger, "TDATA mismatch, checking axi stream - Got 0001_0001 (17). Expected 0001_0010 (18).", error); - check_log(mocklogger, "TLAST mismatch, checking axi stream - Got 0. Expected 1.", error); - check_log(mocklogger, "TKEEP mismatch, checking axi stream - Got 0 (0). Expected 1 (1).", error); - check_log(mocklogger, "TSTRB mismatch, checking axi stream - Got 0 (0). Expected 1 (1).", error); - check_log(mocklogger, "TID mismatch, checking axi stream - Got 0010_0010 (34). Expected 0010_0011 (35).", error); - check_log(mocklogger, "TDEST mismatch, checking axi stream - Got 0011_0011 (51). Expected 0011_0100 (52).", error); - check_log(mocklogger, "TUSER mismatch, checking axi stream - Got 0100_0100 (68). Expected 0100_0101 (69).", error); - - unmock(mocklogger); - - elsif run("test back-to-back passing check") then - wait until rising_edge(aclk); - timestamp := now; - - last := '0'; - for i in 3 to 14 loop - if i = 14 then - last := '1'; - end if; - push_axi_stream(net, master_axi_stream, - tdata => std_logic_vector(to_unsigned(i, 8)), - tlast => last, - tkeep => "1", - tstrb => "1", - tid => std_logic_vector(to_unsigned(42, 8)), - tdest => std_logic_vector(to_unsigned(i+1, 8)), - tuser => std_logic_vector(to_unsigned(i*2, 8))); - end loop; - - last := '0'; - for i in 3 to 14 loop - if i = 14 then - last := '1'; - end if; - check_axi_stream(net, slave_axi_stream, - expected => std_logic_vector(to_unsigned(i, 8)), - tlast => last, - tkeep => "1", - tstrb => "1", - tid => std_logic_vector(to_unsigned(42, 8)), - tdest => std_logic_vector(to_unsigned(i+1, 8)), - tuser => std_logic_vector(to_unsigned(i*2, 8)), - msg => "check blocking", - blocking => false); - end loop; - - check_equal(now, timestamp, result(" setting up transaction stalled")); - - wait_until_idle(net, as_sync(slave_axi_stream)); - check_equal(now, timestamp + (12+1)*10 ns, " transaction time incorrect"); - - elsif run("test back-to-back failing check") then - wait until rising_edge(aclk); - timestamp := now; - - push_axi_stream(net, master_axi_stream, - tdata => std_logic_vector(to_unsigned(3, 8)), - tlast => '1', - tkeep => "0", - tstrb => "0", - tid => std_logic_vector(to_unsigned(42, 8)), - tdest => std_logic_vector(to_unsigned(4, 8)), - tuser => std_logic_vector(to_unsigned(7, 8))); - - check_axi_stream(net, slave_axi_stream, - expected => std_logic_vector(to_unsigned(6, 8)), - tlast => '0', - tkeep => "1", - tstrb => "1", - tid => std_logic_vector(to_unsigned(44, 8)), - tdest => std_logic_vector(to_unsigned(5, 8)), - tuser => std_logic_vector(to_unsigned(8, 8)), - msg => "check non-blocking", - blocking => false); - - check_equal(now, timestamp, result(" setting up transaction stalled")); - - wait until rising_edge(aclk); - mocklogger := get_logger("check"); - mock(mocklogger); - - wait until rising_edge(aclk) and tvalid = '1'; - - check_log(mocklogger, "TDATA mismatch, check non-blocking - Got 0000_0011 (3). Expected 0000_0110 (6).", error); - check_log(mocklogger, "TLAST mismatch, check non-blocking - Got 1. Expected 0.", error); - check_log(mocklogger, "TKEEP mismatch, check non-blocking - Got 0 (0). Expected 1 (1).", error); - check_log(mocklogger, "TSTRB mismatch, check non-blocking - Got 0 (0). Expected 1 (1).", error); - check_log(mocklogger, "TID mismatch, check non-blocking - Got 0010_1010 (42). Expected 0010_1100 (44).", error); - check_log(mocklogger, "TDEST mismatch, check non-blocking - Got 0000_0100 (4). Expected 0000_0101 (5).", error); - check_log(mocklogger, "TUSER mismatch, check non-blocking - Got 0000_0111 (7). Expected 0000_1000 (8).", error); - - unmock(mocklogger); - - check_equal(now, timestamp + 20 ns, " transaction time incorrect"); - - end if; - test_runner_cleanup(runner); - end process; - test_runner_watchdog(runner, 10 ms); - - axi_stream_master_inst : entity work.axi_stream_master - generic map( - master => master_axi_stream) - port map( - aclk => aclk, - areset_n => areset_n, - tvalid => tvalid, - tready => tready, - tdata => tdata, - tlast => tlast, - tkeep => tkeep, - tstrb => tstrb, - tid => tid, - tuser => tuser, - tdest => tdest); - - not_valid <= not tvalid; - - not_valid_data <= '1' when tdata = std_logic_vector'("XXXXXXXX") else '0'; - check_true(aclk, not_valid, not_valid_data, "Invalid data not X"); - not_valid_keep <= '1' when tkeep = std_logic_vector'("X") else '0'; - check_true(aclk, not_valid, not_valid_keep, "Invalid keep not X"); - not_valid_strb <= '1' when tstrb = std_logic_vector'("X") else '0'; - check_true(aclk, not_valid, not_valid_strb, "Invalid strb not X"); - not_valid_id <= '1' when tid = std_logic_vector'("XXXXXXXX") else '0'; - check_true(aclk, not_valid, not_valid_id, "Invalid id not X"); - not_valid_dest <= '1' when tdest = std_logic_vector'("XXXXXXXX") else '0'; - check_true(aclk, not_valid, not_valid_dest, "Invalid dest not X"); - not_valid_user <= '1' when tuser = std_logic_vector'("00000000") else '0'; - check_true(aclk, not_valid, not_valid_user, "Invalid user not 0"); - - axi_stream_slave_inst : entity work.axi_stream_slave - generic map( - slave => slave_axi_stream) - port map( - aclk => aclk, - areset_n => areset_n, - tvalid => tvalid, - tready => tready, - tdata => tdata, - tlast => tlast, - tkeep => tkeep, - tstrb => tstrb, - tid => tid, - tuser => tuser, - tdest => tdest); - - axi_stream_monitor_inst : entity work.axi_stream_monitor - generic map( - monitor => monitor - ) - port map( - aclk => aclk, - tvalid => tvalid, - tready => tready, - tdata => tdata, - tlast => tlast, - tkeep => tkeep, - tstrb => tstrb, - tid => tid, - tdest => tdest, - tuser => tuser - ); - - axi_stream_protocol_checker_inst : entity work.axi_stream_protocol_checker - generic map( - protocol_checker => protocol_checker) - port map( - aclk => aclk, - areset_n => areset_n, - tvalid => tvalid, - tready => tready, - tdata => tdata, - tlast => tlast, - tkeep => tkeep, - tstrb => tstrb, - tid => tid, - tdest => tdest, - tuser => tuser - ); - - aclk <= not aclk after 5 ns; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +context work.vunit_context; +context work.com_context; +context work.data_types_context; +use work.axi_stream_pkg.all; +use work.stream_master_pkg.all; +use work.stream_slave_pkg.all; +use work.sync_pkg.all; + +entity tb_axi_stream is + generic(runner_cfg : string); +end entity; + +architecture a of tb_axi_stream is + constant master_axi_stream : axi_stream_master_t := new_axi_stream_master( + data_length => 8, id_length => 8, dest_length => 8, user_length => 8, + logger => get_logger("master"), actor => new_actor("master"), + monitor => default_axi_stream_monitor, protocol_checker => default_axi_stream_protocol_checker + ); + constant master_stream : stream_master_t := as_stream(master_axi_stream); + constant master_sync : sync_handle_t := as_sync(master_axi_stream); + + constant slave_axi_stream : axi_stream_slave_t := new_axi_stream_slave( + data_length => 8, id_length => 8, dest_length => 8, user_length => 8, + logger => get_logger("slave"), actor => new_actor("slave"), + monitor => default_axi_stream_monitor, protocol_checker => default_axi_stream_protocol_checker + ); + constant slave_stream : stream_slave_t := as_stream(slave_axi_stream); + constant slave_sync : sync_handle_t := as_sync(slave_axi_stream); + + constant monitor : axi_stream_monitor_t := new_axi_stream_monitor( + data_length => 8, id_length => 8, dest_length => 8, user_length => 8, + logger => get_logger("monitor"), actor => new_actor("monitor"), + protocol_checker => default_axi_stream_protocol_checker + ); + + constant protocol_checker : axi_stream_protocol_checker_t := new_axi_stream_protocol_checker( + data_length => 8, id_length => 8, dest_length => 8, user_length => 8, + logger => get_logger("protocol_checker"), + max_waits => 8 + ); + + constant n_monitors : natural := 3; + + signal aclk : std_logic := '0'; + signal areset_n : std_logic := '1'; + signal tvalid : std_logic; + signal tready : std_logic; + signal tdata : std_logic_vector(data_length(slave_axi_stream)-1 downto 0); + signal tlast : std_logic; + signal tkeep : std_logic_vector(data_length(slave_axi_stream)/8-1 downto 0); + signal tstrb : std_logic_vector(data_length(slave_axi_stream)/8-1 downto 0); + signal tid : std_logic_vector(id_length(slave_axi_stream)-1 downto 0); + signal tdest : std_logic_vector(dest_length(slave_axi_stream)-1 downto 0); + signal tuser : std_logic_vector(user_length(slave_axi_stream)-1 downto 0); + + signal not_valid : std_logic; + signal not_valid_data : std_logic; + signal not_valid_keep : std_logic; + signal not_valid_strb : std_logic; + signal not_valid_id : std_logic; + signal not_valid_dest : std_logic; + signal not_valid_user : std_logic; +begin + + main : process + constant subscriber : actor_t := new_actor("main"); + + variable data : std_logic_vector(tdata'range); + variable last_bool : boolean; + variable last : std_logic; + variable keep : std_logic_vector(tkeep'range); + variable strb : std_logic_vector(tstrb'range); + variable id : std_logic_vector(tid'range); + variable dest : std_logic_vector(tdest'range); + variable user : std_logic_vector(tuser'range); + + variable axi_stream_transaction : axi_stream_transaction_t( + tdata(tdata'range), + tkeep(tkeep'range), + tstrb(tstrb'range), + tid(tid'range), + tdest(tdest'range), + tuser(tuser'range) + ); + variable reference_queue : queue_t := new_queue; + variable reference : stream_reference_t; + variable msg : msg_t; + variable msg_type : msg_type_t; + variable timestamp : time := 0 ns; + + variable mocklogger : logger_t; + + procedure get_axi_stream_transaction(variable axi_stream_transaction : out axi_stream_transaction_t) is + begin + receive(net, subscriber, msg); + msg_type := message_type(msg); + handle_axi_stream_transaction(msg_type, msg, axi_stream_transaction); + check(is_already_handled(msg_type)); + end; + + begin + test_runner_setup(runner, runner_cfg); + subscribe(subscriber, find("monitor")); + subscribe(subscriber, find("master")); + subscribe(subscriber, find("slave")); + show(get_logger("monitor"), display_handler, debug); + show(get_logger("master"), display_handler, debug); + show(get_logger("slave"), display_handler, debug); + + wait for 20 ns; + + if run("test single push and pop") then + push_stream(net, master_stream, x"77", true); + pop_stream(net, slave_stream, data, last_bool); + check_equal(data, std_logic_vector'(x"77"), result("for pop stream data")); + check_true(last_bool, result("for pop stream last")); + + for i in 1 to n_monitors loop + get_axi_stream_transaction(axi_stream_transaction); + check_equal( + axi_stream_transaction.tdata, + std_logic_vector'(x"77"), + result("for axi_stream_transaction.tdata") + ); + check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); + end loop; + elsif run("test reset") then + wait until rising_edge(aclk); + areset_n <= '0'; + wait until rising_edge(aclk); + check_equal(tvalid, '0', result("for valid low check while in reset")); + areset_n <= '1'; + wait until rising_edge(aclk); + + elsif run("test single push and pop with tlast") then + push_stream(net, master_stream, x"88", true); + pop_stream(net, slave_stream, data, last_bool); + check_equal(data, std_logic_vector'(x"88"), result("for pop stream data")); + check_true(last_bool, result("for pop stream last")); + + for i in 1 to n_monitors loop + get_axi_stream_transaction(axi_stream_transaction); + check_equal( + axi_stream_transaction.tdata, + std_logic_vector'(x"88"), + result("for axi_stream_transaction.tdata") + ); + check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); + end loop; + + elsif run("test single axi push and pop") then + push_axi_stream(net, master_axi_stream, x"99", tlast => '1'); + pop_stream(net, slave_stream, data, last_bool); + check_equal(data, std_logic_vector'(x"99"), result("pop stream data")); + check_equal(last_bool, true, result("pop stream last")); + + elsif run("test single push and axi pop") then + push_stream(net, master_stream, x"AA", last => true); + pop_axi_stream(net, slave_axi_stream, data, last); + check_equal(data, std_logic_vector'(x"AA"), result("pop stream data")); + check_equal(last, '1', result("pop stream last")); + + elsif run("test single axi push and axi pop") then + push_axi_stream(net, master_axi_stream, x"99", tlast => '1', tkeep => "1", tstrb => "1", tid => x"11", tdest => x"22", tuser => x"33" ); + pop_axi_stream(net, slave_axi_stream, data, last, keep, strb, id, dest, user); + check_equal(data, std_logic_vector'(x"99"), result("pop axi stream data")); + check_equal(last, std_logic'('1'), result("pop stream last")); + check_equal(keep, std_logic_vector'("1"), result("pop stream keep")); + check_equal(strb, std_logic_vector'("1"), result("pop stream strb")); + check_equal(id, std_logic_vector'(x"11"), result("pop stream id")); + check_equal(dest, std_logic_vector'(x"22"), result("pop stream dest")); + check_equal(user, std_logic_vector'(x"33"), result("pop stream user")); + + for i in 1 to n_monitors loop + get_axi_stream_transaction(axi_stream_transaction); + check_equal(axi_stream_transaction.tdata, std_logic_vector'(x"99"), result("for axi_stream_transaction.tdata")); + check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); + check_equal(axi_stream_transaction.tkeep, std_logic_vector'("1"), result("pop stream keep")); + check_equal(axi_stream_transaction.tstrb, std_logic_vector'("1"), result("pop stream strb")); + check_equal(axi_stream_transaction.tid, std_logic_vector'(x"11"), result("pop stream id")); + check_equal(axi_stream_transaction.tdest, std_logic_vector'(x"22"), result("pop stream dest")); + check_equal(axi_stream_transaction.tuser, std_logic_vector'(x"33"), result("pop stream user")); + end loop; + + elsif run("test single stalled push and pop") then + wait until rising_edge(aclk); + wait_for_time(net, master_sync, 30 ns); + timestamp := now; + push_stream(net, master_stream, x"77", true); + pop_stream(net, slave_stream, data, last_bool); + check_equal(data, std_logic_vector'(x"77"), result("for pop stream data")); + check_true(last_bool, result("for pop stream last")); + check_equal(now - 10 ns, timestamp + 50 ns, result("for push wait time")); -- two extra cycles inserted by alignment + for i in 1 to n_monitors loop + get_axi_stream_transaction(axi_stream_transaction); + check_equal( + axi_stream_transaction.tdata, + std_logic_vector'(x"77"), + result("for axi_stream_transaction.tdata") + ); + check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); + end loop; + + elsif run("test single push and stalled pop") then + wait until rising_edge(aclk); + wait_for_time(net, slave_sync, 30 ns); + timestamp := now; + push_stream(net, master_stream, x"77", true); + pop_stream(net, slave_stream, data, last_bool); + check_equal(data, std_logic_vector'(x"77"), result("for pop stream data")); + check_true(last_bool, result("for pop stream last")); + check_equal(now - 10 ns, timestamp + 50 ns, result("for push wait time")); + + for i in 1 to n_monitors loop + get_axi_stream_transaction(axi_stream_transaction); + check_equal( + axi_stream_transaction.tdata, + std_logic_vector'(x"77"), + result("for axi_stream_transaction.tdata") + ); + check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); + end loop; + + elsif run("test single push and stalled pop with non-multiple of clock period") then + wait until rising_edge(aclk); + wait_for_time(net, slave_sync, 29 ns); + timestamp := now; + push_stream(net, master_stream, x"77", true); + pop_stream(net, slave_stream, data, last_bool); + check_equal(data, std_logic_vector'(x"77"), result("for pop stream data")); + check_true(last_bool, result("for pop stream last")); + check_equal(now - 10 ns, timestamp + 40 ns, result("for push wait time")); + + for i in 1 to n_monitors loop + get_axi_stream_transaction(axi_stream_transaction); + check_equal( + axi_stream_transaction.tdata, + std_logic_vector'(x"77"), + result("for axi_stream_transaction.tdata") + ); + check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); + end loop; + + elsif run("test single stalled push and pop with non-multiple of clock period") then + wait until rising_edge(aclk); + wait_for_time(net, master_sync, 29 ns); + timestamp := now; + push_stream(net, master_stream, x"77", true); + pop_stream(net, slave_stream, data, last_bool); + check_equal(data, std_logic_vector'(x"77"), result("for pop stream data")); + check_true(last_bool, result("for pop stream last")); + check_equal(now - 10 ns, timestamp + 40 ns, result("for push wait time")); -- Aligned to clock edge again + for i in 1 to n_monitors loop + get_axi_stream_transaction(axi_stream_transaction); + check_equal( + axi_stream_transaction.tdata, + std_logic_vector'(x"77"), + result("for axi_stream_transaction.tdata") + ); + check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); + end loop; + + elsif run("test pop before push") then + for i in 0 to 7 loop + pop_stream(net, slave_stream, reference); + push(reference_queue, reference); + end loop; + + for i in 0 to 7 loop + push_stream(net, master_stream, std_logic_vector(to_unsigned(i + 1, data'length)), true); + end loop; + + wait_until_idle(net, master_sync); -- wait until all transfers are done before checking them + wait_until_idle(net, slave_sync); + + for i in 0 to 7 loop + reference := pop(reference_queue); + await_pop_stream_reply(net, reference, data); + check_equal(data, to_unsigned(i + 1, data'length), result("for await pop stream data")); + + for j in 1 to n_monitors loop + get_axi_stream_transaction(axi_stream_transaction); + check_equal( + axi_stream_transaction.tdata, + to_unsigned(i + 1, data'length), + result("for axi_stream_transaction.tdata") + ); + end loop; + end loop; + + elsif run("test passing check") then + push_axi_stream(net, master_axi_stream, x"12", tlast => '1', tkeep => "1", tstrb => "1", tid => x"23", tdest => x"34", tuser => x"45"); + check_axi_stream(net, slave_axi_stream, x"12", '1', "1", "1", x"23", x"34", x"45", "checking axi stream"); + + elsif run("test passing reduced check") then + push_axi_stream(net, master_axi_stream, x"12", tlast => '1', tkeep => "1", tstrb => "1", tid => x"23", tdest => x"34", tuser => x"45"); + check_axi_stream(net, slave_axi_stream, x"12", '1', msg => "reduced checking axi stream"); + + elsif run("test failing check") then + + push_axi_stream(net, master_axi_stream, x"11", tlast => '0', tkeep => "0", tstrb => "0", tid => x"22", tdest => x"33", tuser => x"44"); + -- Delay mocking the logger to prevent 'invalid checks' from failing the checks below + wait until rising_edge (aclk) and tvalid = '1'; + + mocklogger := get_logger("check"); + mock(mocklogger); + + check_axi_stream(net, slave_axi_stream, x"12", '1', "1", "1", x"23", x"34", x"45", "checking axi stream"); + check_log(mocklogger, "TDATA mismatch, checking axi stream - Got 0001_0001 (17). Expected 0001_0010 (18).", error); + check_log(mocklogger, "TLAST mismatch, checking axi stream - Got 0. Expected 1.", error); + check_log(mocklogger, "TKEEP mismatch, checking axi stream - Got 0 (0). Expected 1 (1).", error); + check_log(mocklogger, "TSTRB mismatch, checking axi stream - Got 0 (0). Expected 1 (1).", error); + check_log(mocklogger, "TID mismatch, checking axi stream - Got 0010_0010 (34). Expected 0010_0011 (35).", error); + check_log(mocklogger, "TDEST mismatch, checking axi stream - Got 0011_0011 (51). Expected 0011_0100 (52).", error); + check_log(mocklogger, "TUSER mismatch, checking axi stream - Got 0100_0100 (68). Expected 0100_0101 (69).", error); + + unmock(mocklogger); + + elsif run("test back-to-back passing check") then + wait until rising_edge(aclk); + timestamp := now; + + last := '0'; + for i in 3 to 14 loop + if i = 14 then + last := '1'; + end if; + push_axi_stream(net, master_axi_stream, + tdata => std_logic_vector(to_unsigned(i, 8)), + tlast => last, + tkeep => "1", + tstrb => "1", + tid => std_logic_vector(to_unsigned(42, 8)), + tdest => std_logic_vector(to_unsigned(i+1, 8)), + tuser => std_logic_vector(to_unsigned(i*2, 8))); + end loop; + + last := '0'; + for i in 3 to 14 loop + if i = 14 then + last := '1'; + end if; + check_axi_stream(net, slave_axi_stream, + expected => std_logic_vector(to_unsigned(i, 8)), + tlast => last, + tkeep => "1", + tstrb => "1", + tid => std_logic_vector(to_unsigned(42, 8)), + tdest => std_logic_vector(to_unsigned(i+1, 8)), + tuser => std_logic_vector(to_unsigned(i*2, 8)), + msg => "check blocking", + blocking => false); + end loop; + + check_equal(now, timestamp, result(" setting up transaction stalled")); + + wait_until_idle(net, as_sync(slave_axi_stream)); + check_equal(now, timestamp + (12+1)*10 ns, " transaction time incorrect"); + + elsif run("test back-to-back failing check") then + wait until rising_edge(aclk); + timestamp := now; + + push_axi_stream(net, master_axi_stream, + tdata => std_logic_vector(to_unsigned(3, 8)), + tlast => '1', + tkeep => "0", + tstrb => "0", + tid => std_logic_vector(to_unsigned(42, 8)), + tdest => std_logic_vector(to_unsigned(4, 8)), + tuser => std_logic_vector(to_unsigned(7, 8))); + + check_axi_stream(net, slave_axi_stream, + expected => std_logic_vector(to_unsigned(6, 8)), + tlast => '0', + tkeep => "1", + tstrb => "1", + tid => std_logic_vector(to_unsigned(44, 8)), + tdest => std_logic_vector(to_unsigned(5, 8)), + tuser => std_logic_vector(to_unsigned(8, 8)), + msg => "check non-blocking", + blocking => false); + + check_equal(now, timestamp, result(" setting up transaction stalled")); + + wait until rising_edge(aclk); + mocklogger := get_logger("check"); + mock(mocklogger); + + wait until rising_edge(aclk) and tvalid = '1'; + + check_log(mocklogger, "TDATA mismatch, check non-blocking - Got 0000_0011 (3). Expected 0000_0110 (6).", error); + check_log(mocklogger, "TLAST mismatch, check non-blocking - Got 1. Expected 0.", error); + check_log(mocklogger, "TKEEP mismatch, check non-blocking - Got 0 (0). Expected 1 (1).", error); + check_log(mocklogger, "TSTRB mismatch, check non-blocking - Got 0 (0). Expected 1 (1).", error); + check_log(mocklogger, "TID mismatch, check non-blocking - Got 0010_1010 (42). Expected 0010_1100 (44).", error); + check_log(mocklogger, "TDEST mismatch, check non-blocking - Got 0000_0100 (4). Expected 0000_0101 (5).", error); + check_log(mocklogger, "TUSER mismatch, check non-blocking - Got 0000_0111 (7). Expected 0000_1000 (8).", error); + + unmock(mocklogger); + + check_equal(now, timestamp + 20 ns, " transaction time incorrect"); + + end if; + test_runner_cleanup(runner); + end process; + test_runner_watchdog(runner, 10 ms); + + axi_stream_master_inst : entity work.axi_stream_master + generic map( + master => master_axi_stream) + port map( + aclk => aclk, + areset_n => areset_n, + tvalid => tvalid, + tready => tready, + tdata => tdata, + tlast => tlast, + tkeep => tkeep, + tstrb => tstrb, + tid => tid, + tuser => tuser, + tdest => tdest); + + not_valid <= not tvalid; + + not_valid_data <= '1' when tdata = std_logic_vector'("XXXXXXXX") else '0'; + check_true(aclk, not_valid, not_valid_data, "Invalid data not X"); + not_valid_keep <= '1' when tkeep = std_logic_vector'("X") else '0'; + check_true(aclk, not_valid, not_valid_keep, "Invalid keep not X"); + not_valid_strb <= '1' when tstrb = std_logic_vector'("X") else '0'; + check_true(aclk, not_valid, not_valid_strb, "Invalid strb not X"); + not_valid_id <= '1' when tid = std_logic_vector'("XXXXXXXX") else '0'; + check_true(aclk, not_valid, not_valid_id, "Invalid id not X"); + not_valid_dest <= '1' when tdest = std_logic_vector'("XXXXXXXX") else '0'; + check_true(aclk, not_valid, not_valid_dest, "Invalid dest not X"); + not_valid_user <= '1' when tuser = std_logic_vector'("00000000") else '0'; + check_true(aclk, not_valid, not_valid_user, "Invalid user not 0"); + + axi_stream_slave_inst : entity work.axi_stream_slave + generic map( + slave => slave_axi_stream) + port map( + aclk => aclk, + areset_n => areset_n, + tvalid => tvalid, + tready => tready, + tdata => tdata, + tlast => tlast, + tkeep => tkeep, + tstrb => tstrb, + tid => tid, + tuser => tuser, + tdest => tdest); + + axi_stream_monitor_inst : entity work.axi_stream_monitor + generic map( + monitor => monitor + ) + port map( + aclk => aclk, + tvalid => tvalid, + tready => tready, + tdata => tdata, + tlast => tlast, + tkeep => tkeep, + tstrb => tstrb, + tid => tid, + tdest => tdest, + tuser => tuser + ); + + axi_stream_protocol_checker_inst : entity work.axi_stream_protocol_checker + generic map( + protocol_checker => protocol_checker) + port map( + aclk => aclk, + areset_n => areset_n, + tvalid => tvalid, + tready => tready, + tdata => tdata, + tlast => tlast, + tkeep => tkeep, + tstrb => tstrb, + tid => tid, + tdest => tdest, + tuser => tuser + ); + + aclk <= not aclk after 5 ns; +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_axi_stream_protocol_checker.vhd b/vunit/vhdl/verification_components/test/tb_axi_stream_protocol_checker.vhd index 23263251c..bfd3fe8c7 100644 --- a/vunit/vhdl/verification_components/test/tb_axi_stream_protocol_checker.vhd +++ b/vunit/vhdl/verification_components/test/tb_axi_stream_protocol_checker.vhd @@ -1,619 +1,619 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -context work.vunit_context; -context work.com_context; -context work.data_types_context; -use work.axi_stream_pkg.all; -use work.stream_master_pkg.all; -use work.stream_slave_pkg.all; -use work.runner_pkg.all; - -entity tb_axi_stream_protocol_checker is - generic( - runner_cfg : string; - data_length : natural := 8; - id_length : natural := 4; - dest_length : natural := 4; - user_length : natural := 8; - max_waits : natural := 16); -end entity; - -architecture a of tb_axi_stream_protocol_checker is - signal aclk : std_logic := '0'; - signal areset_n : std_logic := '1'; - signal tvalid : std_logic := '0'; - signal tready : std_logic := '0'; - signal tdata : std_logic_vector(data_length - 1 downto 0) := (others => '0'); - signal tlast : std_logic := '1'; - signal tdest : std_logic_vector(dest_length - 1 downto 0) := (others => '0'); - signal tid : std_logic_vector(id_length - 1 downto 0) := (others => '0'); - signal tstrb : std_logic_vector(data_length/8 - 1 downto 0) := (others => '0'); - signal tkeep : std_logic_vector(data_length/8 - 1 downto 0) := (others => '0'); - signal tuser : std_logic_vector(user_length - 1 downto 0) := (others => '0'); - - constant logger : logger_t := get_logger("protocol_checker"); - constant protocol_checker : axi_stream_protocol_checker_t := new_axi_stream_protocol_checker( - data_length => tdata'length, id_length => tid'length, dest_length => tdest'length, user_length => tuser'length, - logger => logger, actor => new_actor("protocol_checker"), max_waits => max_waits - ); - constant meta_values : std_logic_vector(1 to 5) := "-XWZU"; - constant valid_values : std_logic_vector(1 to 4) := "01LH"; - -begin - - main : process - variable rule_logger : logger_t; - - procedure pass_stable_test (signal d : out std_logic_vector) is - constant zeros : std_logic_vector(d'range) := (others => '0'); - constant ones : std_logic_vector(d'range) := (others => '1'); - constant highs: std_logic_vector(d'range) := (others => 'H'); - begin - wait until rising_edge(aclk); - d <= ones; - tready <= '1'; - wait until rising_edge(aclk); - d <= zeros; - wait until rising_edge(aclk); - d <= ones; - tready <= '0'; - wait until rising_edge(aclk); - d <= zeros; - wait until rising_edge(aclk); - - tvalid <= '1'; - d <= ones; - wait until rising_edge(aclk); - d <= highs; - wait until rising_edge(aclk); - tready <= '1'; - wait until rising_edge(aclk); - tvalid <= '0'; - end; - - procedure fail_stable_test ( - signal d : out std_logic_vector; - constant rname : string; constant sname : string; - constant szero : string; constant sone : string; - constant vzero : std_logic := '0'; constant vone : std_logic := '1' - ) is - constant zeros : std_logic_vector(d'range) := (others => vzero); - constant ones : std_logic_vector(d'range) := (others => vone); - variable rule_logger : logger_t; - begin - rule_logger := get_logger(get_name(logger) & rname); - mock(rule_logger); - - wait until rising_edge(aclk); - tvalid <= '1'; - wait until rising_edge(aclk); - d <= ones; - wait until rising_edge(aclk); - d <= zeros; - tready <= '1'; - wait until rising_edge(aclk); - tready <= '0'; - tvalid <= '0'; - wait until rising_edge(aclk); - - check_only_log( - rule_logger, - "Stability check failed for " & sname & " while waiting for tready - Got " & sone & " at 2nd active and enabled clock edge. Expected " & szero & ".", - error); - - wait until rising_edge(aclk); - tvalid <= '1'; - wait until rising_edge(aclk); - d <= ones; - tready <= '1'; - wait until rising_edge(aclk); - tready <= '0'; - tvalid <= '0'; - wait until rising_edge(aclk); - - check_only_log( - rule_logger, - "Stability check failed for " & sname & " while waiting for tready - Got " & sone & " at 2nd active and enabled clock edge. Expected " & szero & ".", - error); - - tvalid <= '1'; - wait until rising_edge(aclk); - tready <= '1'; - wait until rising_edge(aclk); - tready <= '0'; - wait until rising_edge(aclk); - d <= zeros; - tready <= '1'; - wait until rising_edge(aclk); - tready <= '0'; - tvalid <= '0'; - wait until rising_edge(aclk); - - check_log( - rule_logger, - "Stability check passed for " & sname & " while waiting for tready - Got " & sone & " for 2 active and enabled clock edges.", - pass); - check_only_log( - rule_logger, - "Stability check failed for " & sname & " while waiting for tready - Got " & szero & " at 2nd active and enabled clock edge. Expected " & sone & ".", - error); - - unmock(rule_logger); - end; - - procedure pass_unknown_test( - signal d : out std_logic_vector; - signal e1, e2 : out std_logic) is - begin - wait until rising_edge(aclk); - e1 <= '0'; - e2 <= '0'; - for i in meta_values'range loop - d <= (d'range => meta_values(i)); - wait until rising_edge(aclk); - end loop; - e1 <= '1'; - e2 <= '1'; - for i in valid_values'range loop - d <= (d'range => valid_values(i)); - wait until rising_edge(aclk); - end loop; - end; - - procedure fail_unknown_test( - signal d: out std_logic_vector; - signal e1, e2: out std_logic; - constant rname : string; constant sname : string; constant e1name : string - ) is - variable rule_logger : logger_t; - begin - rule_logger := get_logger(get_name(logger) & rname); - mock(rule_logger); - - -- need to disable these signals first, because there would be a log message for the first clock edge if enabled (happens with areset_n) - e1 <= '0'; - e2 <= '0'; - wait until rising_edge(aclk); - e1 <= '1'; - e2 <= '1'; - for i in meta_values'range loop - d <= (d'range => meta_values(i)); - wait until rising_edge(aclk); - wait for 1 ns; - check_only_log( - rule_logger, - "Not unknown check failed for " & sname & " when " & e1name & " is high - Got " & to_nibble_string(std_logic_vector'(d'range => meta_values(i))) & ".", - error); - end loop; - - unmock(rule_logger); - end; - - begin - test_runner_setup(runner, runner_cfg); - - if run("Test passing check of that tdata remains stable when tvalid is asserted and tready is low") then - pass_stable_test(tdata); - - elsif run("Test failing check of that tdata remains stable when tvalid is asserted and tready is low") then - fail_stable_test(tdata, ":rule 1", "tdata", "0000_0000 (0)", "1111_1111 (255)"); - - elsif run("Test passing check of that tlast remains stable when tvalid is asserted and tready is low") then - pass_stable_test(d(0) => tlast); - - elsif run("Test failing check of that tlast remains stable when tvalid is asserted and tready is low") then - fail_stable_test(d(0) => tlast, rname => ":rule 2", sname => "tlast", szero => "1", sone => "0", vzero => '1', vone => '0'); - - elsif run("Test passing check of that tvalid remains asserted until tready is high") then - wait until rising_edge(aclk); - tvalid <= '1'; - wait until rising_edge(aclk); - tvalid <= 'H'; - wait until rising_edge(aclk); - tready <= '1'; - wait until rising_edge(aclk); - tvalid <= '0'; - tready <= '0'; - wait until rising_edge(aclk); - - tvalid <= '1'; - tready <= 'H'; - wait until rising_edge(aclk); - tvalid <= '0'; - tready <= '0'; - - elsif run("Test failing check of that tvalid remains asserted until tready is high") then - rule_logger := get_logger(get_name(logger) & ":rule 3"); - mock(rule_logger); - - wait until rising_edge(aclk); - tvalid <= '1'; - wait until rising_edge(aclk); - tvalid <= '0'; - wait until rising_edge(aclk); - tready <= '1'; - wait until rising_edge(aclk); - tready <= '0'; - tvalid <= '0'; - wait until rising_edge(aclk); - - check_log( - rule_logger, - "Stability check failed for tvalid while waiting for tready - Got 0 " & "at 2nd active and enabled clock edge. Expected 1.", - error); - check_only_log( - rule_logger, - "Stability check failed for tvalid while waiting for tready - Got 0 " & "at 3rd active and enabled clock edge. Expected 1.", - error); - - wait until rising_edge(aclk); - tvalid <= '1'; - wait until rising_edge(aclk); - tvalid <= 'L'; - tready <= 'H'; - wait until rising_edge(aclk); - tready <= '0'; - tvalid <= '0'; - wait until rising_edge(aclk); - - check_only_log( - rule_logger, - "Stability check failed for tvalid while waiting for tready - Got L " & "at 2nd active and enabled clock edge. Expected 1.", - error); - - unmock(rule_logger); - - elsif run("Test passing check of that tready comes within max_waits after valid") then - wait until rising_edge(aclk); - tvalid <= '1'; - for i in 1 to max_waits loop - wait until rising_edge(aclk); - end loop; - tready <= '1'; - wait until rising_edge(aclk); - tvalid <= '0'; - tready <= '0'; - - elsif run("Test failing check of that tready comes within max_waits after valid") then - rule_logger := get_logger(get_name(logger) & ":rule 4"); - mock(rule_logger); - - wait until rising_edge(aclk); - tvalid <= 'H'; - for i in 1 to max_waits + 1 loop - wait until rising_edge(aclk); - end loop; - tready <= 'H'; - wait until rising_edge(aclk); - tvalid <= '0'; - tready <= '0'; - - check_only_log( - rule_logger, - "Check failed for performance - tready active " & to_string(max_waits + 1) & " clock cycles after tvalid. Expected <= " & to_string(max_waits) & " clock cycles.", - warning); - - unmock(rule_logger); - - elsif run("Test passing check of that tdata must not be unknown when tvalid is high") then - pass_unknown_test(tdata, tvalid, tready); - - elsif run("Test failing check of that tdata must not be unknown when tvalid is high") then - fail_unknown_test(tdata, tvalid, tready, ":rule 5", "tdata", "tvalid"); - - elsif run("Test passing check of that tlast must not be unknown when tvalid is high") then - wait until rising_edge(aclk); - for i in meta_values'range loop - tlast <= meta_values(i); - wait until rising_edge(aclk); - end loop; - - tvalid <= '1'; - tready <= '1'; - for i in valid_values'range loop - tlast <= valid_values(i); - wait until rising_edge(aclk); - end loop; - - elsif run("Test failing check of that tlast must not be unknown when tvalid is high") then - rule_logger := get_logger(get_name(logger) & ":rule 6"); - mock(rule_logger); - - wait until rising_edge(aclk); - tvalid <= '1'; - tready <= '1'; - for i in meta_values'range loop - tlast <= meta_values(i); - wait until rising_edge(aclk); - wait for 1 ns; - check_only_log( - rule_logger, - "Not unknown check failed for tlast when tvalid is high - Got " & to_string(meta_values(i)) & ".", - error); - end loop; - - unmock(rule_logger); - - elsif run("Test passing check of that tvalid must not be unknown unless in reset") then - wait until rising_edge(aclk); - areset_n <= '0'; - tready <= '1'; - for i in meta_values'range loop - tvalid <= meta_values(i); - wait until rising_edge(aclk); - end loop; - areset_n <= '1'; - tready <= '1'; - for i in valid_values'range loop - tvalid <= valid_values(i); - wait until rising_edge(aclk); - end loop; - - elsif run("Test failing check of that tvalid must not be unknown unless in reset") then - wait until rising_edge(aclk); - wait for 1 ns; - rule_logger := get_logger(get_name(logger) & ":rule 7"); - mock(rule_logger); - - for i in meta_values'range loop - tvalid <= meta_values(i); - wait until rising_edge(aclk); - wait for 1 ns; - check_only_log( - rule_logger, - "Not unknown check failed for tvalid when not in reset - Got " & to_string(meta_values(i)) & ".", - error); - end loop; - - unmock(rule_logger); - - elsif run("Test passing check of that tready must not be unknown unless in reset") then - wait until rising_edge(aclk); - areset_n <= '0'; - tvalid <= '1'; - for i in meta_values'range loop - tready <= meta_values(i); - wait until rising_edge(aclk); - end loop; - areset_n <= '1'; - tvalid <= '0'; - tready <= valid_values(1); - wait until rising_edge(aclk); - tvalid <= '1'; - for i in valid_values'range loop - tready <= valid_values(i); - wait until rising_edge(aclk); - end loop; - - elsif run("Test failing check of that tready must not be unknown unless in reset") then - rule_logger := get_logger(get_name(logger) & ":rule 8"); - mock(rule_logger); - - for i in meta_values'range loop - tready <= meta_values(i); - wait until rising_edge(aclk); - wait for 1 ns; - check_only_log( - rule_logger, - "Not unknown check failed for tready when not in reset - Got " & to_string(meta_values(i)) & ".", - error); - end loop; - - unmock(rule_logger); - - elsif run("Test passing check of that all packets are complete when the simulation ends") then - wait until rising_edge(aclk); - tvalid <= '1'; - tlast <= '0'; - tready <= '1'; - wait until rising_edge(aclk); - tready <= '0'; - wait until rising_edge(aclk); - tready <= '1'; - wait until rising_edge(aclk); - tvalid <= '0'; - wait until rising_edge(aclk); - tvalid <= '1'; - tlast <= '1'; - tready <= '1'; - wait until rising_edge(aclk); - - elsif run("Test failing check of that all packets are complete when the simulation ends") then - wait until rising_edge(aclk); - rule_logger := get_logger(get_name(logger) & ":rule 9"); - mock(rule_logger); - - wait until rising_edge(aclk); - tvalid <= '1'; - tlast <= '0'; - tready <= '1'; - wait until rising_edge(aclk); - tlast <= '0'; - wait until rising_edge(aclk); - - set_phase(runner_state, test_runner_cleanup); - notify(runner); - entry_gate(runner); - - check_only_log(rule_logger, "Unconditional check failed for packet completion for the following streams: 0.", error); - - unmock(rule_logger); - - elsif run("Test passing check of that tuser must not be unknown unless in reset") then - pass_unknown_test(tuser, areset_n, areset_n); - - elsif run("Test failing check of that tuser must not be unknown unless in reset") then - fail_unknown_test(tuser, areset_n, areset_n, ":rule 10", "tuser", "areset_n"); - - elsif run("Test passing check of that tuser remains stable when tvalid is asserted and tready is low") then - pass_stable_test(tuser); - - elsif run("Test failing check of that tuser remains stable when tvalid is asserted and tready is low") then - fail_stable_test(tuser, ":rule 11", "tuser", "0000_0000 (0)", "1111_1111 (255)"); - - elsif run("Test passing check of that tid remains stable when tvalid is asserted and tready is low") then - pass_stable_test(tid); - - elsif run("Test failing check of that tid remains stable when tvalid is asserted and tready is low") then - fail_stable_test(tid, ":rule 12", "tid", "0000 (0)", "1111 (15)"); - - elsif run("Test passing check of that tdest remains stable when tvalid is asserted and tready is low") then - pass_stable_test(tdest); - - elsif run("Test failing check of that tdest remains stable when tvalid is asserted and tready is low") then - fail_stable_test(tdest, ":rule 13", "tdest", "0000 (0)", "1111 (15)"); - - elsif run("Test passing check of that tstrb remains stable when tvalid is asserted and tready is low") then - tkeep <= (others => '1'); - pass_stable_test(tstrb); - - elsif run("Test failing check of that tstrb remains stable when tvalid is asserted and tready is low") then - tkeep <= (others => '1'); - fail_stable_test(tstrb, ":rule 14", "tstrb", "0", "1"); - - elsif run("Test passing check of that tkeep remains stable when tvalid is asserted and tready is low") then - pass_stable_test(tkeep); - - elsif run("Test failing check of that tkeep remains stable when tvalid is asserted and tready is low") then - fail_stable_test(tkeep, ":rule 15", "tkeep", "0", "1"); - - elsif run("Test passing check of that tid must not be unknown when tvalid is high") then - pass_unknown_test(tid, tvalid, tready); - - elsif run("Test failing check of that tid must not be unknown when tvalid is high") then - fail_unknown_test(tid, tvalid, tready, ":rule 16", "tid", "tvalid"); - - elsif run("Test passing check of that tdest must not be unknown when tvalid is high") then - pass_unknown_test(tdest, tvalid, tready); - - elsif run("Test failing check of that tdest must not be unknown when tvalid is high") then - fail_unknown_test(tdest, tvalid, tready, ":rule 17", "tdest", "tvalid"); - - elsif run("Test passing check of that tstrb must not be unknown when tvalid is high") then - tkeep <= (others => '1'); - pass_unknown_test(tstrb, tvalid, tready); - - elsif run("Test failing check of that tstrb must not be unknown when tvalid is high") then - tkeep <= (others => '1'); - fail_unknown_test(tstrb, tvalid, tready, ":rule 18", "tstrb", "tvalid"); - - elsif run("Test passing check of that tkeep must not be unknown when tvalid is high") then - pass_unknown_test(tkeep, tvalid, tready); - - elsif run("Test failing check of that tkeep must not be unknown when tvalid is high") then - fail_unknown_test(tkeep, tvalid, tready, ":rule 19", "tkeep", "tvalid"); - - elsif run("Test passing check of that tstrb must be de-asserted when tkeep is de-asserted") then - wait until rising_edge(aclk); - tvalid <= '1'; - tready <= '1'; - for b in tstrb'range loop - tkeep <= (others => '0'); - tkeep(b) <= '1'; - tstrb <= (others => '0'); - wait until rising_edge(aclk); - tstrb(b) <= '1'; - wait until rising_edge(aclk); - end loop; - - elsif run("Test failing check of that tstrb must be de-asserted when tkeep is de-asserted") then - rule_logger := get_logger(get_name(logger) & ":rule 20"); - mock(rule_logger); - - wait until rising_edge(aclk); - tvalid <= '1'; - tready <= '1'; - for b in tstrb'range loop - tkeep <= (others => '1'); - tkeep(b) <= '0'; - tstrb <= (others => '0'); - wait until rising_edge(aclk); - tstrb(b) <= '1'; - wait until rising_edge(aclk); - wait for 1 ns; - check_log( - rule_logger, - "True check passed for tstrb de-asserted when tkeep de-asserted", - pass); - check_only_log( - rule_logger, - "True check failed for tstrb de-asserted when tkeep de-asserted", - error); - end loop; - - unmock(rule_logger); - - elsif run("Test failing check of that the sum of tid width and tdest width must be less than 25") then - rule_logger := get_logger(get_name(logger) & ":rule 21"); - mock(rule_logger); - - wait for 1 ns; - check_only_log( - rule_logger, - "True check failed for tid width and tdest width together must be less than 25", - error); - - unmock(rule_logger); - - elsif run("Test passing check of that tvalid must be low just after areset_n goes high") then - areset_n <= '0'; - tvalid <= '0'; - wait until rising_edge(aclk); - areset_n <= '1'; - wait until rising_edge(aclk); - tvalid <= '1'; - wait until rising_edge(aclk); - - elsif run("Test failing check of that tvalid must be low just after areset_n goes high") then - rule_logger := get_logger(get_name(logger) & ":rule 22"); - mock(rule_logger); - - areset_n <= '0'; - tvalid <= '0'; - wait until rising_edge(aclk); - areset_n <= '1'; - tvalid <= '1'; - wait until rising_edge(aclk); - wait for 1 ns; - check_only_log( - rule_logger, - "Implication check failed for tvalid de-asserted after reset release", - error); - - unmock(rule_logger); - - end if; - - test_runner_cleanup(runner); - end process; - - test_runner_watchdog(runner, 10 ms); - - axi_stream_protocol_checker_inst : entity work.axi_stream_protocol_checker - generic map( - protocol_checker => protocol_checker - ) - port map( - aclk => aclk, - areset_n => areset_n, - tvalid => tvalid, - tready => tready, - tdata => tdata, - tlast => tlast, - tkeep => tkeep, - tstrb => tstrb, - tid => tid, - tdest => tdest, - tuser => tuser - ); - - aclk <= not aclk after 5 ns; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +context work.vunit_context; +context work.com_context; +context work.data_types_context; +use work.axi_stream_pkg.all; +use work.stream_master_pkg.all; +use work.stream_slave_pkg.all; +use work.runner_pkg.all; + +entity tb_axi_stream_protocol_checker is + generic( + runner_cfg : string; + data_length : natural := 8; + id_length : natural := 4; + dest_length : natural := 4; + user_length : natural := 8; + max_waits : natural := 16); +end entity; + +architecture a of tb_axi_stream_protocol_checker is + signal aclk : std_logic := '0'; + signal areset_n : std_logic := '1'; + signal tvalid : std_logic := '0'; + signal tready : std_logic := '0'; + signal tdata : std_logic_vector(data_length - 1 downto 0) := (others => '0'); + signal tlast : std_logic := '1'; + signal tdest : std_logic_vector(dest_length - 1 downto 0) := (others => '0'); + signal tid : std_logic_vector(id_length - 1 downto 0) := (others => '0'); + signal tstrb : std_logic_vector(data_length/8 - 1 downto 0) := (others => '0'); + signal tkeep : std_logic_vector(data_length/8 - 1 downto 0) := (others => '0'); + signal tuser : std_logic_vector(user_length - 1 downto 0) := (others => '0'); + + constant logger : logger_t := get_logger("protocol_checker"); + constant protocol_checker : axi_stream_protocol_checker_t := new_axi_stream_protocol_checker( + data_length => tdata'length, id_length => tid'length, dest_length => tdest'length, user_length => tuser'length, + logger => logger, actor => new_actor("protocol_checker"), max_waits => max_waits + ); + constant meta_values : std_logic_vector(1 to 5) := "-XWZU"; + constant valid_values : std_logic_vector(1 to 4) := "01LH"; + +begin + + main : process + variable rule_logger : logger_t; + + procedure pass_stable_test (signal d : out std_logic_vector) is + constant zeros : std_logic_vector(d'range) := (others => '0'); + constant ones : std_logic_vector(d'range) := (others => '1'); + constant highs: std_logic_vector(d'range) := (others => 'H'); + begin + wait until rising_edge(aclk); + d <= ones; + tready <= '1'; + wait until rising_edge(aclk); + d <= zeros; + wait until rising_edge(aclk); + d <= ones; + tready <= '0'; + wait until rising_edge(aclk); + d <= zeros; + wait until rising_edge(aclk); + + tvalid <= '1'; + d <= ones; + wait until rising_edge(aclk); + d <= highs; + wait until rising_edge(aclk); + tready <= '1'; + wait until rising_edge(aclk); + tvalid <= '0'; + end; + + procedure fail_stable_test ( + signal d : out std_logic_vector; + constant rname : string; constant sname : string; + constant szero : string; constant sone : string; + constant vzero : std_logic := '0'; constant vone : std_logic := '1' + ) is + constant zeros : std_logic_vector(d'range) := (others => vzero); + constant ones : std_logic_vector(d'range) := (others => vone); + variable rule_logger : logger_t; + begin + rule_logger := get_logger(get_name(logger) & rname); + mock(rule_logger); + + wait until rising_edge(aclk); + tvalid <= '1'; + wait until rising_edge(aclk); + d <= ones; + wait until rising_edge(aclk); + d <= zeros; + tready <= '1'; + wait until rising_edge(aclk); + tready <= '0'; + tvalid <= '0'; + wait until rising_edge(aclk); + + check_only_log( + rule_logger, + "Stability check failed for " & sname & " while waiting for tready - Got " & sone & " at 2nd active and enabled clock edge. Expected " & szero & ".", + error); + + wait until rising_edge(aclk); + tvalid <= '1'; + wait until rising_edge(aclk); + d <= ones; + tready <= '1'; + wait until rising_edge(aclk); + tready <= '0'; + tvalid <= '0'; + wait until rising_edge(aclk); + + check_only_log( + rule_logger, + "Stability check failed for " & sname & " while waiting for tready - Got " & sone & " at 2nd active and enabled clock edge. Expected " & szero & ".", + error); + + tvalid <= '1'; + wait until rising_edge(aclk); + tready <= '1'; + wait until rising_edge(aclk); + tready <= '0'; + wait until rising_edge(aclk); + d <= zeros; + tready <= '1'; + wait until rising_edge(aclk); + tready <= '0'; + tvalid <= '0'; + wait until rising_edge(aclk); + + check_log( + rule_logger, + "Stability check passed for " & sname & " while waiting for tready - Got " & sone & " for 2 active and enabled clock edges.", + pass); + check_only_log( + rule_logger, + "Stability check failed for " & sname & " while waiting for tready - Got " & szero & " at 2nd active and enabled clock edge. Expected " & sone & ".", + error); + + unmock(rule_logger); + end; + + procedure pass_unknown_test( + signal d : out std_logic_vector; + signal e1, e2 : out std_logic) is + begin + wait until rising_edge(aclk); + e1 <= '0'; + e2 <= '0'; + for i in meta_values'range loop + d <= (d'range => meta_values(i)); + wait until rising_edge(aclk); + end loop; + e1 <= '1'; + e2 <= '1'; + for i in valid_values'range loop + d <= (d'range => valid_values(i)); + wait until rising_edge(aclk); + end loop; + end; + + procedure fail_unknown_test( + signal d: out std_logic_vector; + signal e1, e2: out std_logic; + constant rname : string; constant sname : string; constant e1name : string + ) is + variable rule_logger : logger_t; + begin + rule_logger := get_logger(get_name(logger) & rname); + mock(rule_logger); + + -- need to disable these signals first, because there would be a log message for the first clock edge if enabled (happens with areset_n) + e1 <= '0'; + e2 <= '0'; + wait until rising_edge(aclk); + e1 <= '1'; + e2 <= '1'; + for i in meta_values'range loop + d <= (d'range => meta_values(i)); + wait until rising_edge(aclk); + wait for 1 ns; + check_only_log( + rule_logger, + "Not unknown check failed for " & sname & " when " & e1name & " is high - Got " & to_nibble_string(std_logic_vector'(d'range => meta_values(i))) & ".", + error); + end loop; + + unmock(rule_logger); + end; + + begin + test_runner_setup(runner, runner_cfg); + + if run("Test passing check of that tdata remains stable when tvalid is asserted and tready is low") then + pass_stable_test(tdata); + + elsif run("Test failing check of that tdata remains stable when tvalid is asserted and tready is low") then + fail_stable_test(tdata, ":rule 1", "tdata", "0000_0000 (0)", "1111_1111 (255)"); + + elsif run("Test passing check of that tlast remains stable when tvalid is asserted and tready is low") then + pass_stable_test(d(0) => tlast); + + elsif run("Test failing check of that tlast remains stable when tvalid is asserted and tready is low") then + fail_stable_test(d(0) => tlast, rname => ":rule 2", sname => "tlast", szero => "1", sone => "0", vzero => '1', vone => '0'); + + elsif run("Test passing check of that tvalid remains asserted until tready is high") then + wait until rising_edge(aclk); + tvalid <= '1'; + wait until rising_edge(aclk); + tvalid <= 'H'; + wait until rising_edge(aclk); + tready <= '1'; + wait until rising_edge(aclk); + tvalid <= '0'; + tready <= '0'; + wait until rising_edge(aclk); + + tvalid <= '1'; + tready <= 'H'; + wait until rising_edge(aclk); + tvalid <= '0'; + tready <= '0'; + + elsif run("Test failing check of that tvalid remains asserted until tready is high") then + rule_logger := get_logger(get_name(logger) & ":rule 3"); + mock(rule_logger); + + wait until rising_edge(aclk); + tvalid <= '1'; + wait until rising_edge(aclk); + tvalid <= '0'; + wait until rising_edge(aclk); + tready <= '1'; + wait until rising_edge(aclk); + tready <= '0'; + tvalid <= '0'; + wait until rising_edge(aclk); + + check_log( + rule_logger, + "Stability check failed for tvalid while waiting for tready - Got 0 " & "at 2nd active and enabled clock edge. Expected 1.", + error); + check_only_log( + rule_logger, + "Stability check failed for tvalid while waiting for tready - Got 0 " & "at 3rd active and enabled clock edge. Expected 1.", + error); + + wait until rising_edge(aclk); + tvalid <= '1'; + wait until rising_edge(aclk); + tvalid <= 'L'; + tready <= 'H'; + wait until rising_edge(aclk); + tready <= '0'; + tvalid <= '0'; + wait until rising_edge(aclk); + + check_only_log( + rule_logger, + "Stability check failed for tvalid while waiting for tready - Got L " & "at 2nd active and enabled clock edge. Expected 1.", + error); + + unmock(rule_logger); + + elsif run("Test passing check of that tready comes within max_waits after valid") then + wait until rising_edge(aclk); + tvalid <= '1'; + for i in 1 to max_waits loop + wait until rising_edge(aclk); + end loop; + tready <= '1'; + wait until rising_edge(aclk); + tvalid <= '0'; + tready <= '0'; + + elsif run("Test failing check of that tready comes within max_waits after valid") then + rule_logger := get_logger(get_name(logger) & ":rule 4"); + mock(rule_logger); + + wait until rising_edge(aclk); + tvalid <= 'H'; + for i in 1 to max_waits + 1 loop + wait until rising_edge(aclk); + end loop; + tready <= 'H'; + wait until rising_edge(aclk); + tvalid <= '0'; + tready <= '0'; + + check_only_log( + rule_logger, + "Check failed for performance - tready active " & to_string(max_waits + 1) & " clock cycles after tvalid. Expected <= " & to_string(max_waits) & " clock cycles.", + warning); + + unmock(rule_logger); + + elsif run("Test passing check of that tdata must not be unknown when tvalid is high") then + pass_unknown_test(tdata, tvalid, tready); + + elsif run("Test failing check of that tdata must not be unknown when tvalid is high") then + fail_unknown_test(tdata, tvalid, tready, ":rule 5", "tdata", "tvalid"); + + elsif run("Test passing check of that tlast must not be unknown when tvalid is high") then + wait until rising_edge(aclk); + for i in meta_values'range loop + tlast <= meta_values(i); + wait until rising_edge(aclk); + end loop; + + tvalid <= '1'; + tready <= '1'; + for i in valid_values'range loop + tlast <= valid_values(i); + wait until rising_edge(aclk); + end loop; + + elsif run("Test failing check of that tlast must not be unknown when tvalid is high") then + rule_logger := get_logger(get_name(logger) & ":rule 6"); + mock(rule_logger); + + wait until rising_edge(aclk); + tvalid <= '1'; + tready <= '1'; + for i in meta_values'range loop + tlast <= meta_values(i); + wait until rising_edge(aclk); + wait for 1 ns; + check_only_log( + rule_logger, + "Not unknown check failed for tlast when tvalid is high - Got " & to_string(meta_values(i)) & ".", + error); + end loop; + + unmock(rule_logger); + + elsif run("Test passing check of that tvalid must not be unknown unless in reset") then + wait until rising_edge(aclk); + areset_n <= '0'; + tready <= '1'; + for i in meta_values'range loop + tvalid <= meta_values(i); + wait until rising_edge(aclk); + end loop; + areset_n <= '1'; + tready <= '1'; + for i in valid_values'range loop + tvalid <= valid_values(i); + wait until rising_edge(aclk); + end loop; + + elsif run("Test failing check of that tvalid must not be unknown unless in reset") then + wait until rising_edge(aclk); + wait for 1 ns; + rule_logger := get_logger(get_name(logger) & ":rule 7"); + mock(rule_logger); + + for i in meta_values'range loop + tvalid <= meta_values(i); + wait until rising_edge(aclk); + wait for 1 ns; + check_only_log( + rule_logger, + "Not unknown check failed for tvalid when not in reset - Got " & to_string(meta_values(i)) & ".", + error); + end loop; + + unmock(rule_logger); + + elsif run("Test passing check of that tready must not be unknown unless in reset") then + wait until rising_edge(aclk); + areset_n <= '0'; + tvalid <= '1'; + for i in meta_values'range loop + tready <= meta_values(i); + wait until rising_edge(aclk); + end loop; + areset_n <= '1'; + tvalid <= '0'; + tready <= valid_values(1); + wait until rising_edge(aclk); + tvalid <= '1'; + for i in valid_values'range loop + tready <= valid_values(i); + wait until rising_edge(aclk); + end loop; + + elsif run("Test failing check of that tready must not be unknown unless in reset") then + rule_logger := get_logger(get_name(logger) & ":rule 8"); + mock(rule_logger); + + for i in meta_values'range loop + tready <= meta_values(i); + wait until rising_edge(aclk); + wait for 1 ns; + check_only_log( + rule_logger, + "Not unknown check failed for tready when not in reset - Got " & to_string(meta_values(i)) & ".", + error); + end loop; + + unmock(rule_logger); + + elsif run("Test passing check of that all packets are complete when the simulation ends") then + wait until rising_edge(aclk); + tvalid <= '1'; + tlast <= '0'; + tready <= '1'; + wait until rising_edge(aclk); + tready <= '0'; + wait until rising_edge(aclk); + tready <= '1'; + wait until rising_edge(aclk); + tvalid <= '0'; + wait until rising_edge(aclk); + tvalid <= '1'; + tlast <= '1'; + tready <= '1'; + wait until rising_edge(aclk); + + elsif run("Test failing check of that all packets are complete when the simulation ends") then + wait until rising_edge(aclk); + rule_logger := get_logger(get_name(logger) & ":rule 9"); + mock(rule_logger); + + wait until rising_edge(aclk); + tvalid <= '1'; + tlast <= '0'; + tready <= '1'; + wait until rising_edge(aclk); + tlast <= '0'; + wait until rising_edge(aclk); + + set_phase(runner_state, test_runner_cleanup); + notify(runner); + entry_gate(runner); + + check_only_log(rule_logger, "Unconditional check failed for packet completion for the following streams: 0.", error); + + unmock(rule_logger); + + elsif run("Test passing check of that tuser must not be unknown unless in reset") then + pass_unknown_test(tuser, areset_n, areset_n); + + elsif run("Test failing check of that tuser must not be unknown unless in reset") then + fail_unknown_test(tuser, areset_n, areset_n, ":rule 10", "tuser", "areset_n"); + + elsif run("Test passing check of that tuser remains stable when tvalid is asserted and tready is low") then + pass_stable_test(tuser); + + elsif run("Test failing check of that tuser remains stable when tvalid is asserted and tready is low") then + fail_stable_test(tuser, ":rule 11", "tuser", "0000_0000 (0)", "1111_1111 (255)"); + + elsif run("Test passing check of that tid remains stable when tvalid is asserted and tready is low") then + pass_stable_test(tid); + + elsif run("Test failing check of that tid remains stable when tvalid is asserted and tready is low") then + fail_stable_test(tid, ":rule 12", "tid", "0000 (0)", "1111 (15)"); + + elsif run("Test passing check of that tdest remains stable when tvalid is asserted and tready is low") then + pass_stable_test(tdest); + + elsif run("Test failing check of that tdest remains stable when tvalid is asserted and tready is low") then + fail_stable_test(tdest, ":rule 13", "tdest", "0000 (0)", "1111 (15)"); + + elsif run("Test passing check of that tstrb remains stable when tvalid is asserted and tready is low") then + tkeep <= (others => '1'); + pass_stable_test(tstrb); + + elsif run("Test failing check of that tstrb remains stable when tvalid is asserted and tready is low") then + tkeep <= (others => '1'); + fail_stable_test(tstrb, ":rule 14", "tstrb", "0", "1"); + + elsif run("Test passing check of that tkeep remains stable when tvalid is asserted and tready is low") then + pass_stable_test(tkeep); + + elsif run("Test failing check of that tkeep remains stable when tvalid is asserted and tready is low") then + fail_stable_test(tkeep, ":rule 15", "tkeep", "0", "1"); + + elsif run("Test passing check of that tid must not be unknown when tvalid is high") then + pass_unknown_test(tid, tvalid, tready); + + elsif run("Test failing check of that tid must not be unknown when tvalid is high") then + fail_unknown_test(tid, tvalid, tready, ":rule 16", "tid", "tvalid"); + + elsif run("Test passing check of that tdest must not be unknown when tvalid is high") then + pass_unknown_test(tdest, tvalid, tready); + + elsif run("Test failing check of that tdest must not be unknown when tvalid is high") then + fail_unknown_test(tdest, tvalid, tready, ":rule 17", "tdest", "tvalid"); + + elsif run("Test passing check of that tstrb must not be unknown when tvalid is high") then + tkeep <= (others => '1'); + pass_unknown_test(tstrb, tvalid, tready); + + elsif run("Test failing check of that tstrb must not be unknown when tvalid is high") then + tkeep <= (others => '1'); + fail_unknown_test(tstrb, tvalid, tready, ":rule 18", "tstrb", "tvalid"); + + elsif run("Test passing check of that tkeep must not be unknown when tvalid is high") then + pass_unknown_test(tkeep, tvalid, tready); + + elsif run("Test failing check of that tkeep must not be unknown when tvalid is high") then + fail_unknown_test(tkeep, tvalid, tready, ":rule 19", "tkeep", "tvalid"); + + elsif run("Test passing check of that tstrb must be de-asserted when tkeep is de-asserted") then + wait until rising_edge(aclk); + tvalid <= '1'; + tready <= '1'; + for b in tstrb'range loop + tkeep <= (others => '0'); + tkeep(b) <= '1'; + tstrb <= (others => '0'); + wait until rising_edge(aclk); + tstrb(b) <= '1'; + wait until rising_edge(aclk); + end loop; + + elsif run("Test failing check of that tstrb must be de-asserted when tkeep is de-asserted") then + rule_logger := get_logger(get_name(logger) & ":rule 20"); + mock(rule_logger); + + wait until rising_edge(aclk); + tvalid <= '1'; + tready <= '1'; + for b in tstrb'range loop + tkeep <= (others => '1'); + tkeep(b) <= '0'; + tstrb <= (others => '0'); + wait until rising_edge(aclk); + tstrb(b) <= '1'; + wait until rising_edge(aclk); + wait for 1 ns; + check_log( + rule_logger, + "True check passed for tstrb de-asserted when tkeep de-asserted", + pass); + check_only_log( + rule_logger, + "True check failed for tstrb de-asserted when tkeep de-asserted", + error); + end loop; + + unmock(rule_logger); + + elsif run("Test failing check of that the sum of tid width and tdest width must be less than 25") then + rule_logger := get_logger(get_name(logger) & ":rule 21"); + mock(rule_logger); + + wait for 1 ns; + check_only_log( + rule_logger, + "True check failed for tid width and tdest width together must be less than 25", + error); + + unmock(rule_logger); + + elsif run("Test passing check of that tvalid must be low just after areset_n goes high") then + areset_n <= '0'; + tvalid <= '0'; + wait until rising_edge(aclk); + areset_n <= '1'; + wait until rising_edge(aclk); + tvalid <= '1'; + wait until rising_edge(aclk); + + elsif run("Test failing check of that tvalid must be low just after areset_n goes high") then + rule_logger := get_logger(get_name(logger) & ":rule 22"); + mock(rule_logger); + + areset_n <= '0'; + tvalid <= '0'; + wait until rising_edge(aclk); + areset_n <= '1'; + tvalid <= '1'; + wait until rising_edge(aclk); + wait for 1 ns; + check_only_log( + rule_logger, + "Implication check failed for tvalid de-asserted after reset release", + error); + + unmock(rule_logger); + + end if; + + test_runner_cleanup(runner); + end process; + + test_runner_watchdog(runner, 10 ms); + + axi_stream_protocol_checker_inst : entity work.axi_stream_protocol_checker + generic map( + protocol_checker => protocol_checker + ) + port map( + aclk => aclk, + areset_n => areset_n, + tvalid => tvalid, + tready => tready, + tdata => tdata, + tlast => tlast, + tkeep => tkeep, + tstrb => tstrb, + tid => tid, + tdest => tdest, + tuser => tuser + ); + + aclk <= not aclk after 5 ns; +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_axi_write_slave.vhd b/vunit/vhdl/verification_components/test/tb_axi_write_slave.vhd index 33642b7fd..09e4ba528 100644 --- a/vunit/vhdl/verification_components/test/tb_axi_write_slave.vhd +++ b/vunit/vhdl/verification_components/test/tb_axi_write_slave.vhd @@ -1,639 +1,639 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library vunit_lib; -context vunit_lib.vunit_context; -context work.com_context; -use work.axi_pkg.all; -context work.vc_context; - -use work.integer_vector_ptr_pkg.all; -use work.random_pkg.all; - -library osvvm; -use osvvm.RandomPkg.all; - -entity tb_axi_write_slave is - generic (runner_cfg : string); -end entity; - -architecture a of tb_axi_write_slave is - signal clk : std_logic := '1'; - - constant log_data_size : integer := 4; - constant data_size : integer := 2**log_data_size; - - signal awvalid : std_logic := '0'; - signal awready : std_logic; - signal awid : std_logic_vector(3 downto 0); - signal awaddr : std_logic_vector(31 downto 0); - signal awlen : axi4_len_t; - signal awsize : axi4_size_t; - signal awburst : axi_burst_type_t; - - signal wvalid : std_logic; - signal wready : std_logic := '0'; - signal wdata : std_logic_vector(8*data_size-1 downto 0); - signal wstrb : std_logic_vector(data_size downto 0); - signal wlast : std_logic; - - signal bvalid : std_logic := '0'; - signal bready : std_logic; - signal bid : std_logic_vector(awid'range); - signal bresp : axi_resp_t; - - constant memory : memory_t := new_memory; - constant axi_slave : axi_slave_t := new_axi_slave(memory => memory); - -begin - main : process - variable buf : buffer_t; - variable rnd : RandomPType; - - procedure read_response(id : std_logic_vector; - resp : axi_resp_t := axi_resp_okay) is - begin - bready <= '1'; - wait until (bvalid and bready) = '1' and rising_edge(clk); - check_equal(bresp, resp, "bresp"); - check_equal(bid, id, "bid"); - bready <= '0'; - end procedure; - - - procedure write_addr(id : std_logic_vector; - addr : natural; - len : natural; - log_size : natural; - burst : axi_burst_type_t) is - begin - awvalid <= '1'; - awid <= id; - awaddr <= std_logic_vector(to_unsigned(addr, awaddr'length)); - awlen <= std_logic_vector(to_unsigned(len-1, awlen'length)); - awsize <= std_logic_vector(to_unsigned(log_size, awsize'length)); - awburst <= burst; - - wait until (awvalid and awready) = '1' and rising_edge(clk); - awvalid <= '0'; - end procedure; - - procedure transfer_data(id : std_logic_vector; - buf : buffer_t; - log_size : natural; - data : integer_vector_ptr_t) is - variable size, len, address, idx : natural; - begin - size := 2**log_size; - len := (length(data) + (base_address(buf) mod size)) / data_size; - write_addr(id, base_address(buf), len, log_size, axi_burst_type_incr); - - address := base_address(buf); - for j in 0 to len-1 loop - wstrb <= (others => '0'); - for i in 0 to size-1-(address mod data_size) loop - idx := address mod data_size; - wstrb(idx) <= '1'; - wdata(8*idx+7 downto 8*idx) <= std_logic_vector(to_unsigned(get(data, address - base_address(buf)), 8)); - set_permissions(memory, address, write_only); - set_expected_byte(memory, address, get(data, address - base_address(buf))); - address := address + 1; - end loop; - - if j = len-1 then - wlast <= '1'; - else - wlast <= '0'; - end if; - - wvalid <= '1'; - wait until (wvalid and wready) = '1' and rising_edge(clk); - wvalid <= '0'; - wstrb <= (others => '0'); - wdata <= (others => '0'); - end loop; - end procedure; - - procedure transfer(id : std_logic_vector; - buf : buffer_t; - log_size : natural; - data : integer_vector_ptr_t) is - begin - transfer_data(id, buf, log_size, data); - read_response(id, axi_resp_okay); - check_expected_was_written(buf); - end procedure; - - variable data : integer_vector_ptr_t; - variable strb : integer_vector_ptr_t; - variable size, log_size : natural; - variable id : std_logic_vector(awid'range); - variable len : natural; - variable burst : axi_burst_type_t; - variable idx : integer; - variable num_ops : integer; - variable start_time, diff_time : time; - - constant dummy_byte : natural := 13; - constant large_latency : time := 1 us; - begin - test_runner_setup(runner, runner_cfg); - rnd.InitSeed(rnd'instance_name); - - if run("Test random writes") then - num_ops := 0; - for test_idx in 0 to 32-1 loop - - id := rnd.RandSlv(awid'length); - case rnd.RandInt(1) is - when 0 => - burst := axi_burst_type_fixed; - len := 1; - when 1 => - burst := axi_burst_type_incr; - len := rnd.RandInt(1, 2**awlen'length); - when others => - assert false; - end case; - - log_size := rnd.RandInt(0, 3); - size := 2**log_size; - random_integer_vector_ptr(rnd, data, size * len, 0, 255); - random_integer_vector_ptr(rnd, strb, length(data), 0, 1); - - buf := allocate(memory, 8 * len, alignment => 4096); - for i in 0 to length(data)-1 loop - if get(strb, i) = 1 then - set_expected_byte(memory, base_address(buf)+i, get(data, i)); - num_ops := num_ops + 1; - else - set_permissions(memory, base_address(buf)+i, no_access); - end if; - end loop; - - write_addr(id, base_address(buf), len, log_size, burst); - - - for j in 0 to len-1 loop - for i in 0 to size-1 loop - idx := (base_address(buf) + j*size + i) mod data_size; - wdata(8*idx+7 downto 8*idx) <= std_logic_vector(to_unsigned(get(data, j*size + i), 8)); - wstrb(idx downto idx) <= std_logic_vector(to_unsigned(get(strb, j*size + i), 1)); - end loop; - - if j = len-1 then - wlast <= '1'; - else - wlast <= '0'; - end if; - - wvalid <= '1'; - wait until (wvalid and wready) = '1' and rising_edge(clk); - wvalid <= '0'; - wstrb <= (others => '0'); - wdata <= (others => '0'); - end loop; - - read_response(id, axi_resp_okay); - check_expected_was_written(buf); - end loop; - - assert num_ops > 5000; - - elsif run("Test that permissions are checked") then - -- Also check that memory errors are made to the axi slave logger - buf := allocate(memory, data_size, permissions => no_access); - write_addr(x"0", base_address(buf), 1, log_data_size, axi_burst_type_fixed); - - wvalid <= '1'; - wlast <= '1'; - wstrb <= (0 => '1', others => '0'); - wdata <= (others => '0'); - mock(axi_slave_logger, failure); - wait until (wvalid and wready) = '1' and rising_edge(clk); - wait until mock_queue_length > 0 and rising_edge(clk); - check_only_log(axi_slave_logger, - "Writing to address 0 at offset 0 within anonymous buffer at range (0 to 15) without permission (no_access)", - failure); - unmock(axi_slave_logger); - wvalid <= '0'; - wlast <= '0'; - wstrb <= (others => '0'); - wdata <= (others => '0'); - - elsif run("Test data stall probability") then - for i in 0 to 4 loop - if i = 2 then - set_data_stall_probability(net, axi_slave, 0.9); - else - set_data_stall_probability(net, axi_slave, 0.0); - end if; - - log_size := log_data_size; - size := 2**log_size; - random_integer_vector_ptr(rnd, data, size * 128, 0, 255); - buf := allocate(memory, length(data), permissions => no_access); - start_time := now; - transfer(x"2", buf, log_size, data); - info("diff_time := " & to_string(now - start_time)); - - if i = 1 or i = 4 then - -- First two and last two runs should have the same time with 0.0 - -- stall probability - check_equal(diff_time, now - start_time); - elsif i = 2 then - -- Middle run should have larger time - check(5*diff_time < now - start_time); - end if; - - diff_time := now - start_time; - end loop; - - elsif run("Test response latency") then - for i in 0 to 1 loop - if i = 1 then - set_response_latency(net, axi_slave, large_latency); - end if; - - log_size := log_data_size; - size := 2**log_size; - random_integer_vector_ptr(rnd, data, size * 128, 0, 255); - buf := allocate(memory, length(data), permissions => no_access); - - -- Write known value to memory so that we can check that it has not - -- been changed to early when response latency is high - for addr in base_address(buf) to last_address(buf) loop - write_byte(memory, addr, dummy_byte); - end loop; - - start_time := now; - transfer_data(x"2", buf, log_size, data); - - if i = 1 then - wait for (large_latency - 10 ns); - -- Check that data was not set yet - for addr in base_address(buf) to last_address(buf) loop - check_equal(read_byte(memory, addr), dummy_byte, "Data should not be set yet"); - end loop; - end if; - read_response(x"2", axi_resp_okay); - check_expected_was_written(buf); - info("diff_time := " & to_string(now - start_time)); - - if i = 1 then - check_equal(diff_time + large_latency, now - start_time); - end if; - - diff_time := now - start_time; - end loop; - - elsif run("Test write response stall probability") then - for i in 0 to 4 loop - if i = 2 then - set_write_response_stall_probability(net, axi_slave, 0.95); - else - set_write_response_stall_probability(net, axi_slave, 0.0); - end if; - - log_size := log_data_size; - size := 2**log_size; - random_integer_vector_ptr(rnd, data, size, 0, 255); - buf := allocate(memory, length(data), permissions => no_access); - start_time := now; - for j in 0 to 128 loop - transfer(x"2", buf, log_size, data); - end loop; - - info("diff_time := " & to_string(now - start_time)); - - if i = 1 or i = 4 then - -- First two and last two runs should have the same time with 0.0 - -- stall probability - check_equal(diff_time, now - start_time); - elsif i = 2 then - -- Middle run should have larger time - check(5*diff_time < now - start_time); - end if; - - diff_time := now - start_time; - end loop; - - elsif run("Test narrow write") then - -- Half bus width starting at aligned address - len := 2; - log_size := log_data_size - 1; - size := 2**log_size; - random_integer_vector_ptr(rnd, data, size * 2, 0, 255); - buf := allocate(memory, length(data), permissions => no_access); - transfer(x"2", buf, log_size, data); - - elsif run("Test unaligned narrow write") then - -- Half bus width starting at unaligned address - log_size := log_data_size - 1; - size := 2**log_size; - buf := allocate(memory, 1); -- Unaligned address - random_integer_vector_ptr(rnd, data, size * 2, 0, 255); - buf := allocate(memory, length(data), permissions => no_access); - transfer(x"2", buf, log_size, data); - - elsif run("Test unaligned write") then - -- Full bus width starting at unaligned address - len := 2; - log_size := log_data_size; - size := 2**log_size; - buf := allocate(memory, 1); -- Unaligned address - random_integer_vector_ptr(rnd, data, size * 2, 0, 255); - buf := allocate(memory, length(data), permissions => no_access); - transfer(x"2", buf, log_size, data); - - elsif run("Test error on missing tlast fixed") then - mock(axi_slave_logger, failure); - - buf := allocate(memory, 8); - write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_fixed); - wvalid <= '1'; - wait until (wvalid and wready) = '1' and rising_edge(clk); - wvalid <= '0'; - - wait until mock_queue_length > 0 and rising_edge(clk); - check_only_log(axi_slave_logger, - "Expected wlast='1' on last beat of burst #0 for id 2 with length 1 starting at address 0", - failure); - unmock(axi_slave_logger); - read_response(x"2", axi_resp_okay); - - elsif run("Test error on missing tlast incr") then - buf := allocate(memory, 8); - write_addr(x"3", base_address(buf), 2, 0, axi_burst_type_incr); - - wvalid <= '1'; - wait until (wvalid and wready) = '1' and rising_edge(clk); - wvalid <= '0'; - wait until wvalid = '0' and rising_edge(clk); - - mock(axi_slave_logger, failure); - - wvalid <= '1'; - wait until (wvalid and wready) = '1' and rising_edge(clk); - wvalid <= '0'; - wait until mock_queue_length > 0 and rising_edge(clk); - - check_only_log(axi_slave_logger, - "Expected wlast='1' on last beat of burst #0 for id 3 with length 2 starting at address 0", - failure); - unmock(axi_slave_logger); - read_response(x"3", axi_resp_okay); - - elsif run("Test error on unsupported wrap burst") then - mock(axi_slave_logger, failure); - buf := allocate(memory, 8); - write_addr(x"2", base_address(buf), 2, 0, axi_burst_type_wrap); - wait until mock_queue_length > 0 and rising_edge(clk); - check_only_log(axi_slave_logger, "Wrapping burst type not supported", failure); - unmock(axi_slave_logger); - - elsif run("Test error 4KByte boundary crossing") then - buf := allocate(memory, 4096+32, alignment => 4096); - mock(axi_slave_logger, failure); - write_addr(x"2", base_address(buf)+4000, 256, 0, axi_burst_type_incr); - wait until mock_queue_length > 0 and rising_edge(clk); - check_only_log(axi_slave_logger, "Crossing 4KByte boundary. First page = 0 (4000/4096), last page = 1 (4255/4096)", failure); - unmock(axi_slave_logger); - - elsif run("Test no error on 4KByte boundary crossing with disabled check") then - buf := allocate(memory, 4096+32, alignment => 4096); - disable_4kbyte_boundary_check(net, axi_slave); - write_addr(x"2", base_address(buf)+4000, 256, 0, axi_burst_type_incr); - wait until awvalid = '0' and rising_edge(clk); - - elsif run("Test default address depth is 1") then - write_addr(x"2", 0, 1, 0, axi_burst_type_incr); -- Taken data process - write_addr(x"2", 0, 1, 0, axi_burst_type_incr); -- In the queue - for i in 0 to 127 loop - wait until rising_edge(clk); - assert awready = '0' report "Can only have one address in the queue"; - end loop; - - elsif run("Test set address fifo depth") then - set_address_fifo_depth(net, axi_slave, 16); - - write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- Taken data process - for i in 1 to 16 loop - write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- In the queue - end loop; - - for i in 0 to 127 loop - wait until rising_edge(clk); - assert awready = '0' report "Address queue should be full"; - end loop; - - elsif run("Test changing address depth to smaller than content gives error") then - set_address_fifo_depth(net, axi_slave, 16); - - write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- Taken data process - for i in 1 to 16 loop - write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- In the queue - end loop; - - set_address_fifo_depth(net, axi_slave, 17); - set_address_fifo_depth(net, axi_slave, 16); - - mock(axi_slave_logger, failure); - - set_address_fifo_depth(net, axi_slave, 1); - check_only_log(axi_slave_logger, "New address fifo depth 1 is smaller than current content size 16", failure); - unmock(axi_slave_logger); - - elsif run("Test address stall probability") then - set_address_fifo_depth(net, axi_slave, 128); - - start_time := now; - for i in 1 to 16 loop - write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); - end loop; - diff_time := now - start_time; - - set_address_stall_probability(net, axi_slave, 0.9); - start_time := now; - for i in 1 to 16 loop - write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); - end loop; - assert (now - start_time) > 5.0 * diff_time report "Should take about longer with stall probability"; - - elsif run("Test well behaved check does not fail for well behaved bursts") then - buf := allocate(memory, 8); - enable_well_behaved_check(net, axi_slave); - set_address_fifo_depth(net, axi_slave, 3); - set_write_response_fifo_depth(net, axi_slave, 3); - - bready <= '1'; - - wait until rising_edge(clk); - wvalid <= '1'; - wlast <= '1'; - assert wready = '0'; - -- Only allow non max size for single beat bursts - write_addr(x"0", base_address(buf), len => 1, log_size => log_data_size, burst => axi_burst_type_incr); - wvalid <= '1'; - wlast <= '1'; - assert wready = '0'; - write_addr(x"0", base_address(buf), len => 2, log_size => log_data_size, burst => axi_burst_type_incr); - wvalid <= '1'; - wlast <= '0'; - assert wready = '1'; - write_addr(x"0", base_address(buf), len => 1, log_size => 0, burst => axi_burst_type_incr); - wvalid <= '1'; - wlast <= '1'; - assert wready = '1'; - wait until rising_edge(clk); - wvalid <= '1'; - wlast <= '1'; - assert wready = '1'; - wait until rising_edge(clk); - wvalid <= '0'; - wlast <= '0'; - assert wready = '1'; - wait until rising_edge(clk); - wvalid <= '0'; - wlast <= '0'; - assert wready = '0'; - wait until rising_edge(clk); - assert wready = '0'; - wait until rising_edge(clk); - assert wready = '0'; - wait until rising_edge(clk); - assert wready = '0'; - - elsif run("Test well behaved check does not fail after well behaved burst finished") then - buf := allocate(memory, 8); - enable_well_behaved_check(net, axi_slave); - bready <= '1'; - - wait until rising_edge(clk); - wvalid <= '1'; - wlast <= '0'; - assert wready = '0'; - -- Only allow non max size for single beat bursts - write_addr(x"0", base_address(buf), len => 3, log_size => log_data_size, burst => axi_burst_type_incr); - wvalid <= '1'; - wlast <= '0'; - assert wready = '0'; - wait until rising_edge(clk); - wvalid <= '1'; - wlast <= '0'; - assert wready = '1'; - wait until rising_edge(clk); - wvalid <= '1'; - wlast <= '1'; - assert wready = '1'; - wait until rising_edge(clk); - wvalid <= '0'; - wlast <= '0'; - assert wready = '1'; - wait until rising_edge(clk); - wvalid <= '0'; - wlast <= '0'; - assert wready = '0'; - wait until rising_edge(clk); - wvalid <= '0'; - wlast <= '0'; - wait until rising_edge(clk); - wvalid <= '0'; - wlast <= '0'; - assert wready = '0'; - - elsif run("Test well behaved check fails for ill behaved awsize") then - buf := allocate(memory, 8); - enable_well_behaved_check(net, axi_slave); - mock(axi_slave_logger, failure); - bready <= '1'; - - wait until rising_edge(clk); - wvalid <= '1'; - wlast <= '0'; - write_addr(x"0", base_address(buf), len => 2, log_size => 0, burst => axi_burst_type_incr); - check_only_log(axi_slave_logger, "Burst not well behaved, axi size = 1 but bus data width allows " & to_string(data_size), failure); - unmock(axi_slave_logger); - - elsif run("Test well behaved check fails when wvalid not high during active burst") then - buf := allocate(memory, 8); - enable_well_behaved_check(net, axi_slave); - mock(axi_slave_logger, failure); - bready <= '1'; - wait until rising_edge(clk); - write_addr(x"0", base_address(buf), len => 2, log_size => log_data_size, burst => axi_burst_type_incr); - check_only_log(axi_slave_logger, "Burst not well behaved, vwalid was not high during active burst", failure); - unmock(axi_slave_logger); - - elsif run("Test well behaved check fails when bready not high during active burst") then - buf := allocate(memory, 8); - enable_well_behaved_check(net, axi_slave); - mock(axi_slave_logger, failure); - wvalid <= '1'; - wait until rising_edge(clk); - write_addr(x"0", base_address(buf), len => 2, log_size => log_data_size, burst => axi_burst_type_incr); - check_only_log(axi_slave_logger, "Burst not well behaved, bready was not high during active burst", failure); - unmock(axi_slave_logger); - - elsif run("Test well behaved check fails when wvalid not high during active burst and awready is low") then - buf := allocate(memory, 8); - enable_well_behaved_check(net, axi_slave); - mock(axi_slave_logger, failure); - set_address_stall_probability(net, axi_slave, 1.0); - bready <= '1'; - - wait until rising_edge(clk); - wait until rising_edge(clk); - assert awready = '0'; - - awvalid <= '1'; - awid <= x"0"; - awaddr <= std_logic_vector(to_unsigned(base_address(buf), awaddr'length)); - awlen <= std_logic_vector(to_unsigned(0, awlen'length)); - awsize <= std_logic_vector(to_unsigned(log_size, awsize'length)); - awburst <= axi_burst_type_incr; - - wait until rising_edge(clk); - assert awready = '0'; - wait until mock_queue_length > 0 for 0 ns; - - check_only_log(axi_slave_logger, "Burst not well behaved, vwalid was not high during active burst", failure); - unmock(axi_slave_logger); - - end if; - - test_runner_cleanup(runner); - end process; - test_runner_watchdog(runner, 1 ms); - - dut : entity work.axi_write_slave - generic map ( - axi_slave => axi_slave) - port map ( - aclk => clk, - awvalid => awvalid, - awready => awready, - awid => awid, - awaddr => awaddr, - awlen => awlen, - awsize => awsize, - awburst => awburst, - wvalid => wvalid, - wready => wready, - wdata => wdata, - wstrb => wstrb, - wlast => wlast, - bvalid => bvalid, - bready => bready, - bid => bid, - bresp => bresp); - - clk <= not clk after 5 ns; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library vunit_lib; +context vunit_lib.vunit_context; +context work.com_context; +use work.axi_pkg.all; +context work.vc_context; + +use work.integer_vector_ptr_pkg.all; +use work.random_pkg.all; + +library osvvm; +use osvvm.RandomPkg.all; + +entity tb_axi_write_slave is + generic (runner_cfg : string); +end entity; + +architecture a of tb_axi_write_slave is + signal clk : std_logic := '1'; + + constant log_data_size : integer := 4; + constant data_size : integer := 2**log_data_size; + + signal awvalid : std_logic := '0'; + signal awready : std_logic; + signal awid : std_logic_vector(3 downto 0); + signal awaddr : std_logic_vector(31 downto 0); + signal awlen : axi4_len_t; + signal awsize : axi4_size_t; + signal awburst : axi_burst_type_t; + + signal wvalid : std_logic; + signal wready : std_logic := '0'; + signal wdata : std_logic_vector(8*data_size-1 downto 0); + signal wstrb : std_logic_vector(data_size downto 0); + signal wlast : std_logic; + + signal bvalid : std_logic := '0'; + signal bready : std_logic; + signal bid : std_logic_vector(awid'range); + signal bresp : axi_resp_t; + + constant memory : memory_t := new_memory; + constant axi_slave : axi_slave_t := new_axi_slave(memory => memory); + +begin + main : process + variable buf : buffer_t; + variable rnd : RandomPType; + + procedure read_response(id : std_logic_vector; + resp : axi_resp_t := axi_resp_okay) is + begin + bready <= '1'; + wait until (bvalid and bready) = '1' and rising_edge(clk); + check_equal(bresp, resp, "bresp"); + check_equal(bid, id, "bid"); + bready <= '0'; + end procedure; + + + procedure write_addr(id : std_logic_vector; + addr : natural; + len : natural; + log_size : natural; + burst : axi_burst_type_t) is + begin + awvalid <= '1'; + awid <= id; + awaddr <= std_logic_vector(to_unsigned(addr, awaddr'length)); + awlen <= std_logic_vector(to_unsigned(len-1, awlen'length)); + awsize <= std_logic_vector(to_unsigned(log_size, awsize'length)); + awburst <= burst; + + wait until (awvalid and awready) = '1' and rising_edge(clk); + awvalid <= '0'; + end procedure; + + procedure transfer_data(id : std_logic_vector; + buf : buffer_t; + log_size : natural; + data : integer_vector_ptr_t) is + variable size, len, address, idx : natural; + begin + size := 2**log_size; + len := (length(data) + (base_address(buf) mod size)) / data_size; + write_addr(id, base_address(buf), len, log_size, axi_burst_type_incr); + + address := base_address(buf); + for j in 0 to len-1 loop + wstrb <= (others => '0'); + for i in 0 to size-1-(address mod data_size) loop + idx := address mod data_size; + wstrb(idx) <= '1'; + wdata(8*idx+7 downto 8*idx) <= std_logic_vector(to_unsigned(get(data, address - base_address(buf)), 8)); + set_permissions(memory, address, write_only); + set_expected_byte(memory, address, get(data, address - base_address(buf))); + address := address + 1; + end loop; + + if j = len-1 then + wlast <= '1'; + else + wlast <= '0'; + end if; + + wvalid <= '1'; + wait until (wvalid and wready) = '1' and rising_edge(clk); + wvalid <= '0'; + wstrb <= (others => '0'); + wdata <= (others => '0'); + end loop; + end procedure; + + procedure transfer(id : std_logic_vector; + buf : buffer_t; + log_size : natural; + data : integer_vector_ptr_t) is + begin + transfer_data(id, buf, log_size, data); + read_response(id, axi_resp_okay); + check_expected_was_written(buf); + end procedure; + + variable data : integer_vector_ptr_t; + variable strb : integer_vector_ptr_t; + variable size, log_size : natural; + variable id : std_logic_vector(awid'range); + variable len : natural; + variable burst : axi_burst_type_t; + variable idx : integer; + variable num_ops : integer; + variable start_time, diff_time : time; + + constant dummy_byte : natural := 13; + constant large_latency : time := 1 us; + begin + test_runner_setup(runner, runner_cfg); + rnd.InitSeed(rnd'instance_name); + + if run("Test random writes") then + num_ops := 0; + for test_idx in 0 to 32-1 loop + + id := rnd.RandSlv(awid'length); + case rnd.RandInt(1) is + when 0 => + burst := axi_burst_type_fixed; + len := 1; + when 1 => + burst := axi_burst_type_incr; + len := rnd.RandInt(1, 2**awlen'length); + when others => + assert false; + end case; + + log_size := rnd.RandInt(0, 3); + size := 2**log_size; + random_integer_vector_ptr(rnd, data, size * len, 0, 255); + random_integer_vector_ptr(rnd, strb, length(data), 0, 1); + + buf := allocate(memory, 8 * len, alignment => 4096); + for i in 0 to length(data)-1 loop + if get(strb, i) = 1 then + set_expected_byte(memory, base_address(buf)+i, get(data, i)); + num_ops := num_ops + 1; + else + set_permissions(memory, base_address(buf)+i, no_access); + end if; + end loop; + + write_addr(id, base_address(buf), len, log_size, burst); + + + for j in 0 to len-1 loop + for i in 0 to size-1 loop + idx := (base_address(buf) + j*size + i) mod data_size; + wdata(8*idx+7 downto 8*idx) <= std_logic_vector(to_unsigned(get(data, j*size + i), 8)); + wstrb(idx downto idx) <= std_logic_vector(to_unsigned(get(strb, j*size + i), 1)); + end loop; + + if j = len-1 then + wlast <= '1'; + else + wlast <= '0'; + end if; + + wvalid <= '1'; + wait until (wvalid and wready) = '1' and rising_edge(clk); + wvalid <= '0'; + wstrb <= (others => '0'); + wdata <= (others => '0'); + end loop; + + read_response(id, axi_resp_okay); + check_expected_was_written(buf); + end loop; + + assert num_ops > 5000; + + elsif run("Test that permissions are checked") then + -- Also check that memory errors are made to the axi slave logger + buf := allocate(memory, data_size, permissions => no_access); + write_addr(x"0", base_address(buf), 1, log_data_size, axi_burst_type_fixed); + + wvalid <= '1'; + wlast <= '1'; + wstrb <= (0 => '1', others => '0'); + wdata <= (others => '0'); + mock(axi_slave_logger, failure); + wait until (wvalid and wready) = '1' and rising_edge(clk); + wait until mock_queue_length > 0 and rising_edge(clk); + check_only_log(axi_slave_logger, + "Writing to address 0 at offset 0 within anonymous buffer at range (0 to 15) without permission (no_access)", + failure); + unmock(axi_slave_logger); + wvalid <= '0'; + wlast <= '0'; + wstrb <= (others => '0'); + wdata <= (others => '0'); + + elsif run("Test data stall probability") then + for i in 0 to 4 loop + if i = 2 then + set_data_stall_probability(net, axi_slave, 0.9); + else + set_data_stall_probability(net, axi_slave, 0.0); + end if; + + log_size := log_data_size; + size := 2**log_size; + random_integer_vector_ptr(rnd, data, size * 128, 0, 255); + buf := allocate(memory, length(data), permissions => no_access); + start_time := now; + transfer(x"2", buf, log_size, data); + info("diff_time := " & to_string(now - start_time)); + + if i = 1 or i = 4 then + -- First two and last two runs should have the same time with 0.0 + -- stall probability + check_equal(diff_time, now - start_time); + elsif i = 2 then + -- Middle run should have larger time + check(5*diff_time < now - start_time); + end if; + + diff_time := now - start_time; + end loop; + + elsif run("Test response latency") then + for i in 0 to 1 loop + if i = 1 then + set_response_latency(net, axi_slave, large_latency); + end if; + + log_size := log_data_size; + size := 2**log_size; + random_integer_vector_ptr(rnd, data, size * 128, 0, 255); + buf := allocate(memory, length(data), permissions => no_access); + + -- Write known value to memory so that we can check that it has not + -- been changed to early when response latency is high + for addr in base_address(buf) to last_address(buf) loop + write_byte(memory, addr, dummy_byte); + end loop; + + start_time := now; + transfer_data(x"2", buf, log_size, data); + + if i = 1 then + wait for (large_latency - 10 ns); + -- Check that data was not set yet + for addr in base_address(buf) to last_address(buf) loop + check_equal(read_byte(memory, addr), dummy_byte, "Data should not be set yet"); + end loop; + end if; + read_response(x"2", axi_resp_okay); + check_expected_was_written(buf); + info("diff_time := " & to_string(now - start_time)); + + if i = 1 then + check_equal(diff_time + large_latency, now - start_time); + end if; + + diff_time := now - start_time; + end loop; + + elsif run("Test write response stall probability") then + for i in 0 to 4 loop + if i = 2 then + set_write_response_stall_probability(net, axi_slave, 0.95); + else + set_write_response_stall_probability(net, axi_slave, 0.0); + end if; + + log_size := log_data_size; + size := 2**log_size; + random_integer_vector_ptr(rnd, data, size, 0, 255); + buf := allocate(memory, length(data), permissions => no_access); + start_time := now; + for j in 0 to 128 loop + transfer(x"2", buf, log_size, data); + end loop; + + info("diff_time := " & to_string(now - start_time)); + + if i = 1 or i = 4 then + -- First two and last two runs should have the same time with 0.0 + -- stall probability + check_equal(diff_time, now - start_time); + elsif i = 2 then + -- Middle run should have larger time + check(5*diff_time < now - start_time); + end if; + + diff_time := now - start_time; + end loop; + + elsif run("Test narrow write") then + -- Half bus width starting at aligned address + len := 2; + log_size := log_data_size - 1; + size := 2**log_size; + random_integer_vector_ptr(rnd, data, size * 2, 0, 255); + buf := allocate(memory, length(data), permissions => no_access); + transfer(x"2", buf, log_size, data); + + elsif run("Test unaligned narrow write") then + -- Half bus width starting at unaligned address + log_size := log_data_size - 1; + size := 2**log_size; + buf := allocate(memory, 1); -- Unaligned address + random_integer_vector_ptr(rnd, data, size * 2, 0, 255); + buf := allocate(memory, length(data), permissions => no_access); + transfer(x"2", buf, log_size, data); + + elsif run("Test unaligned write") then + -- Full bus width starting at unaligned address + len := 2; + log_size := log_data_size; + size := 2**log_size; + buf := allocate(memory, 1); -- Unaligned address + random_integer_vector_ptr(rnd, data, size * 2, 0, 255); + buf := allocate(memory, length(data), permissions => no_access); + transfer(x"2", buf, log_size, data); + + elsif run("Test error on missing tlast fixed") then + mock(axi_slave_logger, failure); + + buf := allocate(memory, 8); + write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_fixed); + wvalid <= '1'; + wait until (wvalid and wready) = '1' and rising_edge(clk); + wvalid <= '0'; + + wait until mock_queue_length > 0 and rising_edge(clk); + check_only_log(axi_slave_logger, + "Expected wlast='1' on last beat of burst #0 for id 2 with length 1 starting at address 0", + failure); + unmock(axi_slave_logger); + read_response(x"2", axi_resp_okay); + + elsif run("Test error on missing tlast incr") then + buf := allocate(memory, 8); + write_addr(x"3", base_address(buf), 2, 0, axi_burst_type_incr); + + wvalid <= '1'; + wait until (wvalid and wready) = '1' and rising_edge(clk); + wvalid <= '0'; + wait until wvalid = '0' and rising_edge(clk); + + mock(axi_slave_logger, failure); + + wvalid <= '1'; + wait until (wvalid and wready) = '1' and rising_edge(clk); + wvalid <= '0'; + wait until mock_queue_length > 0 and rising_edge(clk); + + check_only_log(axi_slave_logger, + "Expected wlast='1' on last beat of burst #0 for id 3 with length 2 starting at address 0", + failure); + unmock(axi_slave_logger); + read_response(x"3", axi_resp_okay); + + elsif run("Test error on unsupported wrap burst") then + mock(axi_slave_logger, failure); + buf := allocate(memory, 8); + write_addr(x"2", base_address(buf), 2, 0, axi_burst_type_wrap); + wait until mock_queue_length > 0 and rising_edge(clk); + check_only_log(axi_slave_logger, "Wrapping burst type not supported", failure); + unmock(axi_slave_logger); + + elsif run("Test error 4KByte boundary crossing") then + buf := allocate(memory, 4096+32, alignment => 4096); + mock(axi_slave_logger, failure); + write_addr(x"2", base_address(buf)+4000, 256, 0, axi_burst_type_incr); + wait until mock_queue_length > 0 and rising_edge(clk); + check_only_log(axi_slave_logger, "Crossing 4KByte boundary. First page = 0 (4000/4096), last page = 1 (4255/4096)", failure); + unmock(axi_slave_logger); + + elsif run("Test no error on 4KByte boundary crossing with disabled check") then + buf := allocate(memory, 4096+32, alignment => 4096); + disable_4kbyte_boundary_check(net, axi_slave); + write_addr(x"2", base_address(buf)+4000, 256, 0, axi_burst_type_incr); + wait until awvalid = '0' and rising_edge(clk); + + elsif run("Test default address depth is 1") then + write_addr(x"2", 0, 1, 0, axi_burst_type_incr); -- Taken data process + write_addr(x"2", 0, 1, 0, axi_burst_type_incr); -- In the queue + for i in 0 to 127 loop + wait until rising_edge(clk); + assert awready = '0' report "Can only have one address in the queue"; + end loop; + + elsif run("Test set address fifo depth") then + set_address_fifo_depth(net, axi_slave, 16); + + write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- Taken data process + for i in 1 to 16 loop + write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- In the queue + end loop; + + for i in 0 to 127 loop + wait until rising_edge(clk); + assert awready = '0' report "Address queue should be full"; + end loop; + + elsif run("Test changing address depth to smaller than content gives error") then + set_address_fifo_depth(net, axi_slave, 16); + + write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- Taken data process + for i in 1 to 16 loop + write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); -- In the queue + end loop; + + set_address_fifo_depth(net, axi_slave, 17); + set_address_fifo_depth(net, axi_slave, 16); + + mock(axi_slave_logger, failure); + + set_address_fifo_depth(net, axi_slave, 1); + check_only_log(axi_slave_logger, "New address fifo depth 1 is smaller than current content size 16", failure); + unmock(axi_slave_logger); + + elsif run("Test address stall probability") then + set_address_fifo_depth(net, axi_slave, 128); + + start_time := now; + for i in 1 to 16 loop + write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); + end loop; + diff_time := now - start_time; + + set_address_stall_probability(net, axi_slave, 0.9); + start_time := now; + for i in 1 to 16 loop + write_addr(x"2", base_address(buf), 1, 0, axi_burst_type_incr); + end loop; + assert (now - start_time) > 5.0 * diff_time report "Should take about longer with stall probability"; + + elsif run("Test well behaved check does not fail for well behaved bursts") then + buf := allocate(memory, 8); + enable_well_behaved_check(net, axi_slave); + set_address_fifo_depth(net, axi_slave, 3); + set_write_response_fifo_depth(net, axi_slave, 3); + + bready <= '1'; + + wait until rising_edge(clk); + wvalid <= '1'; + wlast <= '1'; + assert wready = '0'; + -- Only allow non max size for single beat bursts + write_addr(x"0", base_address(buf), len => 1, log_size => log_data_size, burst => axi_burst_type_incr); + wvalid <= '1'; + wlast <= '1'; + assert wready = '0'; + write_addr(x"0", base_address(buf), len => 2, log_size => log_data_size, burst => axi_burst_type_incr); + wvalid <= '1'; + wlast <= '0'; + assert wready = '1'; + write_addr(x"0", base_address(buf), len => 1, log_size => 0, burst => axi_burst_type_incr); + wvalid <= '1'; + wlast <= '1'; + assert wready = '1'; + wait until rising_edge(clk); + wvalid <= '1'; + wlast <= '1'; + assert wready = '1'; + wait until rising_edge(clk); + wvalid <= '0'; + wlast <= '0'; + assert wready = '1'; + wait until rising_edge(clk); + wvalid <= '0'; + wlast <= '0'; + assert wready = '0'; + wait until rising_edge(clk); + assert wready = '0'; + wait until rising_edge(clk); + assert wready = '0'; + wait until rising_edge(clk); + assert wready = '0'; + + elsif run("Test well behaved check does not fail after well behaved burst finished") then + buf := allocate(memory, 8); + enable_well_behaved_check(net, axi_slave); + bready <= '1'; + + wait until rising_edge(clk); + wvalid <= '1'; + wlast <= '0'; + assert wready = '0'; + -- Only allow non max size for single beat bursts + write_addr(x"0", base_address(buf), len => 3, log_size => log_data_size, burst => axi_burst_type_incr); + wvalid <= '1'; + wlast <= '0'; + assert wready = '0'; + wait until rising_edge(clk); + wvalid <= '1'; + wlast <= '0'; + assert wready = '1'; + wait until rising_edge(clk); + wvalid <= '1'; + wlast <= '1'; + assert wready = '1'; + wait until rising_edge(clk); + wvalid <= '0'; + wlast <= '0'; + assert wready = '1'; + wait until rising_edge(clk); + wvalid <= '0'; + wlast <= '0'; + assert wready = '0'; + wait until rising_edge(clk); + wvalid <= '0'; + wlast <= '0'; + wait until rising_edge(clk); + wvalid <= '0'; + wlast <= '0'; + assert wready = '0'; + + elsif run("Test well behaved check fails for ill behaved awsize") then + buf := allocate(memory, 8); + enable_well_behaved_check(net, axi_slave); + mock(axi_slave_logger, failure); + bready <= '1'; + + wait until rising_edge(clk); + wvalid <= '1'; + wlast <= '0'; + write_addr(x"0", base_address(buf), len => 2, log_size => 0, burst => axi_burst_type_incr); + check_only_log(axi_slave_logger, "Burst not well behaved, axi size = 1 but bus data width allows " & to_string(data_size), failure); + unmock(axi_slave_logger); + + elsif run("Test well behaved check fails when wvalid not high during active burst") then + buf := allocate(memory, 8); + enable_well_behaved_check(net, axi_slave); + mock(axi_slave_logger, failure); + bready <= '1'; + wait until rising_edge(clk); + write_addr(x"0", base_address(buf), len => 2, log_size => log_data_size, burst => axi_burst_type_incr); + check_only_log(axi_slave_logger, "Burst not well behaved, vwalid was not high during active burst", failure); + unmock(axi_slave_logger); + + elsif run("Test well behaved check fails when bready not high during active burst") then + buf := allocate(memory, 8); + enable_well_behaved_check(net, axi_slave); + mock(axi_slave_logger, failure); + wvalid <= '1'; + wait until rising_edge(clk); + write_addr(x"0", base_address(buf), len => 2, log_size => log_data_size, burst => axi_burst_type_incr); + check_only_log(axi_slave_logger, "Burst not well behaved, bready was not high during active burst", failure); + unmock(axi_slave_logger); + + elsif run("Test well behaved check fails when wvalid not high during active burst and awready is low") then + buf := allocate(memory, 8); + enable_well_behaved_check(net, axi_slave); + mock(axi_slave_logger, failure); + set_address_stall_probability(net, axi_slave, 1.0); + bready <= '1'; + + wait until rising_edge(clk); + wait until rising_edge(clk); + assert awready = '0'; + + awvalid <= '1'; + awid <= x"0"; + awaddr <= std_logic_vector(to_unsigned(base_address(buf), awaddr'length)); + awlen <= std_logic_vector(to_unsigned(0, awlen'length)); + awsize <= std_logic_vector(to_unsigned(log_size, awsize'length)); + awburst <= axi_burst_type_incr; + + wait until rising_edge(clk); + assert awready = '0'; + wait until mock_queue_length > 0 for 0 ns; + + check_only_log(axi_slave_logger, "Burst not well behaved, vwalid was not high during active burst", failure); + unmock(axi_slave_logger); + + end if; + + test_runner_cleanup(runner); + end process; + test_runner_watchdog(runner, 1 ms); + + dut : entity work.axi_write_slave + generic map ( + axi_slave => axi_slave) + port map ( + aclk => clk, + awvalid => awvalid, + awready => awready, + awid => awid, + awaddr => awaddr, + awlen => awlen, + awsize => awsize, + awburst => awburst, + wvalid => wvalid, + wready => wready, + wdata => wdata, + wstrb => wstrb, + wlast => wlast, + bvalid => bvalid, + bready => bready, + bid => bid, + bresp => bresp); + + clk <= not clk after 5 ns; +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_bus_master_pkg.vhd b/vunit/vhdl/verification_components/test/tb_bus_master_pkg.vhd index 419d48e00..1f0b13221 100644 --- a/vunit/vhdl/verification_components/test/tb_bus_master_pkg.vhd +++ b/vunit/vhdl/verification_components/test/tb_bus_master_pkg.vhd @@ -1,105 +1,105 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library vunit_lib; -context vunit_lib.vunit_context; -context work.com_context; - -use work.queue_pkg.all; -use work.bus_master_pkg.all; -use work.memory_pkg.all; -use work.logger_pkg.all; - -entity tb_bus_master_pkg is - generic (runner_cfg : string); -end entity; - -architecture a of tb_bus_master_pkg is - constant memory : memory_t := new_memory; - constant bus_handle : bus_master_t := new_bus(data_length => 32, address_length => 32); - constant logger : logger_t := get_logger("logger"); - constant actor : actor_t := new_actor("actor"); - constant custom_logger_and_actor_bus_handle : bus_master_t := - new_bus(data_length => 32, address_length => 32, logger => logger, actor => actor); -begin - main : process - variable buf : buffer_t; - variable read_data : std_logic_vector(data_length(bus_handle)-1 downto 0); - variable reference : bus_reference_t; - begin - test_runner_setup(runner, runner_cfg); - - if run("test write_bus") then - buf := allocate(memory, 12, permissions => write_only); - set_expected_word(memory, base_address(buf), x"00112233"); - set_expected_word(memory, base_address(buf) + 4, x"00112233"); - set_expected_word(memory, base_address(buf) + 8, x"00112233"); - write_bus(net, bus_handle, x"00000000", x"00112233"); - write_bus(net, bus_handle, x"4", x"00112233"); - write_bus(net, bus_handle, x"00000008", x"112233"); - - elsif run("test write_bus with byte_enable") then - buf := allocate(memory, 12, permissions => write_only); - set_permissions(memory, base_address(buf), no_access); - set_expected_byte(memory, base_address(buf)+1, 16#33#); - set_permissions(memory, base_address(buf)+2, no_access); - set_expected_byte(memory, base_address(buf)+3, 16#11#); - write_bus(net, bus_handle, base_address(buf), x"11223344", byte_enable => "1010"); - - elsif run("test read_bus") then - buf := allocate(memory, 8, permissions => read_only); - write_word(memory, base_address(buf), x"00112233"); - write_word(memory, base_address(buf) + 4, x"00112233"); - read_bus(net, bus_handle, x"00000000", read_data); - check_equal(read_data, std_logic_vector'(x"00112233")); - read_bus(net, bus_handle, x"4", reference); - await_read_bus_reply(net, reference, read_data); - check_equal(read_data, std_logic_vector'(x"00112233")); - - elsif run("test check_bus") then - buf := allocate(memory, 4, permissions => read_only); - write_word(memory, base_address(buf), x"00112233"); - check_bus(net, bus_handle, x"00000000", std_logic_vector'(x"00112233")); - check_bus(net, bus_handle, x"00000000", std_logic_vector'(x"001122--")); - - mock(bus_logger); - check_bus(net, bus_handle, x"00000000", std_logic_vector'(x"00112244")); - check_only_log(bus_logger, "check_bus(x""00000000"") - Got x""00112233"" expected x""00112244""", failure); - - check_bus(net, bus_handle, x"00000000", std_logic_vector'(x"00112244"), msg => "msg"); - check_only_log(bus_logger, "msg - Got x""00112233"" expected x""00112244""", failure); - - check_bus(net, bus_handle, x"00000000", std_logic_vector'(x"--112244")); - check_only_log(bus_logger, "check_bus(x""00000000"") - Got x""00112233"" expected x""XX112244""", failure); - unmock(bus_logger); - - elsif run("test check_bus support reduced data length") then - buf := allocate(memory, 4, permissions => read_only); - write_word(memory, base_address(buf), x"00112233"); - check_bus(net, bus_handle, x"00000000", std_logic_vector'(x"112233")); - - write_word(memory, base_address(buf), x"77112233"); - mock(bus_logger); - check_bus(net, bus_handle, x"00000000", std_logic_vector'(x"112233")); - check_only_log(bus_logger, "check_bus(x""00000000"") - Got x""77112233"" expected x""00112233""", failure); - unmock(bus_logger); - - elsif run("test custom logger and actor bus master") then - check(custom_logger_and_actor_bus_handle.p_logger = logger); - check(custom_logger_and_actor_bus_handle.p_actor = actor); - end if; - test_runner_cleanup(runner); - end process; - - bus2memory_inst : entity work.bus2memory - generic map ( - bus_handle => bus_handle, - memory => memory); -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library vunit_lib; +context vunit_lib.vunit_context; +context work.com_context; + +use work.queue_pkg.all; +use work.bus_master_pkg.all; +use work.memory_pkg.all; +use work.logger_pkg.all; + +entity tb_bus_master_pkg is + generic (runner_cfg : string); +end entity; + +architecture a of tb_bus_master_pkg is + constant memory : memory_t := new_memory; + constant bus_handle : bus_master_t := new_bus(data_length => 32, address_length => 32); + constant logger : logger_t := get_logger("logger"); + constant actor : actor_t := new_actor("actor"); + constant custom_logger_and_actor_bus_handle : bus_master_t := + new_bus(data_length => 32, address_length => 32, logger => logger, actor => actor); +begin + main : process + variable buf : buffer_t; + variable read_data : std_logic_vector(data_length(bus_handle)-1 downto 0); + variable reference : bus_reference_t; + begin + test_runner_setup(runner, runner_cfg); + + if run("test write_bus") then + buf := allocate(memory, 12, permissions => write_only); + set_expected_word(memory, base_address(buf), x"00112233"); + set_expected_word(memory, base_address(buf) + 4, x"00112233"); + set_expected_word(memory, base_address(buf) + 8, x"00112233"); + write_bus(net, bus_handle, x"00000000", x"00112233"); + write_bus(net, bus_handle, x"4", x"00112233"); + write_bus(net, bus_handle, x"00000008", x"112233"); + + elsif run("test write_bus with byte_enable") then + buf := allocate(memory, 12, permissions => write_only); + set_permissions(memory, base_address(buf), no_access); + set_expected_byte(memory, base_address(buf)+1, 16#33#); + set_permissions(memory, base_address(buf)+2, no_access); + set_expected_byte(memory, base_address(buf)+3, 16#11#); + write_bus(net, bus_handle, base_address(buf), x"11223344", byte_enable => "1010"); + + elsif run("test read_bus") then + buf := allocate(memory, 8, permissions => read_only); + write_word(memory, base_address(buf), x"00112233"); + write_word(memory, base_address(buf) + 4, x"00112233"); + read_bus(net, bus_handle, x"00000000", read_data); + check_equal(read_data, std_logic_vector'(x"00112233")); + read_bus(net, bus_handle, x"4", reference); + await_read_bus_reply(net, reference, read_data); + check_equal(read_data, std_logic_vector'(x"00112233")); + + elsif run("test check_bus") then + buf := allocate(memory, 4, permissions => read_only); + write_word(memory, base_address(buf), x"00112233"); + check_bus(net, bus_handle, x"00000000", std_logic_vector'(x"00112233")); + check_bus(net, bus_handle, x"00000000", std_logic_vector'(x"001122--")); + + mock(bus_logger); + check_bus(net, bus_handle, x"00000000", std_logic_vector'(x"00112244")); + check_only_log(bus_logger, "check_bus(x""00000000"") - Got x""00112233"" expected x""00112244""", failure); + + check_bus(net, bus_handle, x"00000000", std_logic_vector'(x"00112244"), msg => "msg"); + check_only_log(bus_logger, "msg - Got x""00112233"" expected x""00112244""", failure); + + check_bus(net, bus_handle, x"00000000", std_logic_vector'(x"--112244")); + check_only_log(bus_logger, "check_bus(x""00000000"") - Got x""00112233"" expected x""XX112244""", failure); + unmock(bus_logger); + + elsif run("test check_bus support reduced data length") then + buf := allocate(memory, 4, permissions => read_only); + write_word(memory, base_address(buf), x"00112233"); + check_bus(net, bus_handle, x"00000000", std_logic_vector'(x"112233")); + + write_word(memory, base_address(buf), x"77112233"); + mock(bus_logger); + check_bus(net, bus_handle, x"00000000", std_logic_vector'(x"112233")); + check_only_log(bus_logger, "check_bus(x""00000000"") - Got x""77112233"" expected x""00112233""", failure); + unmock(bus_logger); + + elsif run("test custom logger and actor bus master") then + check(custom_logger_and_actor_bus_handle.p_logger = logger); + check(custom_logger_and_actor_bus_handle.p_actor = actor); + end if; + test_runner_cleanup(runner); + end process; + + bus2memory_inst : entity work.bus2memory + generic map ( + bus_handle => bus_handle, + memory => memory); +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_memory.vhd b/vunit/vhdl/verification_components/test/tb_memory.vhd index 34e182b79..02c5190d5 100644 --- a/vunit/vhdl/verification_components/test/tb_memory.vhd +++ b/vunit/vhdl/verification_components/test/tb_memory.vhd @@ -1,377 +1,377 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -library vunit_lib; -context vunit_lib.vunit_context; - -use work.memory_pkg.all; -use work.string_ptr_pkg.all; -use work.random_pkg.all; - -entity tb_memory is - generic (runner_cfg : string); -end entity; - -architecture a of tb_memory is -begin - - main : process - variable memory : memory_t; - - procedure test_write_integer(word : integer; bytes : integer_vector) is - begin - write_integer(memory, 0, word, bytes_per_word => bytes'length); - for i in 0 to bytes'length-1 loop - check_equal(read_byte(memory, i), bytes(i)); - end loop; - write_integer(memory, 0, word, bytes_per_word => bytes'length, endian => big_endian); - for i in 0 to bytes'length-1 loop - check_equal(read_byte(memory, i), bytes(bytes'length - 1 - i)); - end loop; - end procedure; - - procedure test_write_word(word : std_logic_vector; bytes : integer_vector) is - begin - write_word(memory, 0, word); - for i in 0 to word'length/8-1 loop - check_equal(read_byte(memory, i), bytes(i)); - end loop; - - write_word(memory, 0, word, endian => big_endian); - for i in 0 to word'length/8-1 loop - check_equal(read_byte(memory, i), bytes(word'length/8 - 1 - i)); - end loop; - end procedure; - - variable buf : buffer_t; - variable byte : byte_t; - variable dummy_permissions : permissions_t; - variable dummy_boolean : boolean; - - begin - test_runner_setup(runner, runner_cfg); - - if run("Test default is null") then - assert memory = null_memory report "Default should be null memory"; - - elsif run("Test new memory") then - memory := new_memory; - assert memory /= null_memory report "Should not be null memory"; - assert num_bytes(memory) = 0 report "Empty memory should have zero bytes"; - - elsif run("Test clear memory") then - memory := new_memory; - for i in 0 to 2 loop - buf := allocate(memory, 1024); - check_equal(base_address(buf), 0, "base address"); - check_equal(last_address(buf), 1023, "last address"); - clear(memory); - end loop; - - elsif run("Test allocate") then - memory := new_memory; - - for i in 0 to 2 loop - buf := allocate(memory, 1024); - check_equal(base_address(buf), 1024*i, "base address"); - check_equal(last_address(buf), 1024*(i+1)-1, "last address"); - - assert num_bytes(buf) = 1024; - assert num_bytes(memory) = (i+1)*1024; - - end loop; - - elsif run("Test allocate a lot") then - -- Test that buf is efficient - memory := new_memory; - - for i in 0 to 500-1 loop - buf := allocate(memory, 500); - check_equal(base_address(buf), 500*i, "base address"); - check_equal(last_address(buf), 500*(i+1)-1, "last address"); - end loop; - - elsif run("Test allocate with alignment") then - memory := new_memory; - buf := allocate(memory, 1); - buf := allocate(memory, 1, alignment => 8); - check_equal(base_address(buf), 8, "aligned base address"); - - assert get_permissions(memory, 1) = no_access report "No access at unallocated location"; - assert get_permissions(memory, 7) = no_access report "No access at unallocated location"; - - buf := allocate(memory, 1, alignment => 16); - check_equal(base_address(buf), 16, "aligned base address"); - - elsif run("Test read and write byte") then - memory := new_memory; - buf := allocate(memory, 2); - - write_byte(memory, 0, 255); - write_byte(memory, 1, 128); - check_equal(read_byte(memory, 0), 255); - check_equal(read_byte(memory, 1), 128); - - elsif run("Test access empty memory") then - memory := new_memory; - mock(memory_logger); - write_byte(memory, 0, 255); - check_only_log(memory_logger, "Writing to empty memory", failure); - - byte := read_byte(memory, 0); - check_only_log(memory_logger, "Reading from empty memory", failure); - unmock(memory_logger); - - elsif run("Test access memory out of range") then - memory := new_memory; - buf := allocate(memory, 1); - - mock(memory_logger); - write_byte(memory, 1, 255); - check_only_log(memory_logger, "Writing to address 1 out of range 0 to 0", failure); - - byte := read_byte(memory, 1); - check_only_log(memory_logger, "Reading from address 1 out of range 0 to 0", failure); - unmock(memory_logger); - - elsif run("Test default permissions") then - memory := new_memory; - buf := allocate(memory, 1); - assert get_permissions(memory, 0) = read_and_write; - - elsif run("Test set permissions") then - memory := new_memory; - buf := allocate(memory, 1); - - set_permissions(memory, 0, no_access); - assert get_permissions(memory, 0) = no_access; - - set_permissions(memory, 0, write_only); - assert get_permissions(memory, 0) = write_only; - - set_permissions(memory, 0, read_only); - assert get_permissions(memory, 0) = read_only; - - set_permissions(memory, 0, read_and_write); - assert get_permissions(memory, 0) = read_and_write; - - elsif run("Test access memory without permission (no_access)") then - memory := new_memory; - buf := allocate(memory, 10); - set_permissions(memory, 5, no_access); - - mock(memory_logger); - write_byte(to_vc_interface(memory), 5, 255); - check_only_log(memory_logger, "Writing to " & describe_address(memory, 5) & " without permission (no_access)", failure); - - byte := read_byte(to_vc_interface(memory), 5); - check_only_log(memory_logger, "Reading from " & describe_address(memory, 5) & " without permission (no_access)", failure); - unmock(memory_logger); - - -- Ignore permissions - write_byte(memory, 5, 255); - byte := read_byte(memory, 5); - - elsif run("Test access memory without permission (write_only)") then - memory := new_memory; - buf := allocate(memory, 10); - set_permissions(memory, 5, write_only); - - write_byte(to_vc_interface(memory), 5, 255); - - mock(memory_logger); - byte := read_byte(to_vc_interface(memory), 5); - check_only_log(memory_logger, "Reading from " & describe_address(memory, 5) & " without permission (write_only)", failure); - unmock(memory_logger); - - -- Ignore permissions - write_byte(memory, 5, 255); - - byte := read_byte(memory, 5); - - elsif run("Test access memory without permission (read_only)") then - memory := new_memory; - buf := allocate(memory, 10); - set_permissions(memory, 5, read_only); - - mock(memory_logger); - write_byte(to_vc_interface(memory), 5, 255); - check_only_log(memory_logger, "Writing to " & describe_address(memory, 5) & " without permission (read_only)", failure); - unmock(memory_logger); - - byte := read_byte(memory, 5); - - -- Ignore permissions - write_byte(memory, 5, 255); - - byte := read_byte(memory, 5); - - elsif run("Test describe address") then - memory := new_memory; - buf := allocate(memory, 2); - buf := allocate(memory, 10, name => "buffer_name"); - assert name(buf) = "buffer_name"; - - check_equal(describe_address(memory, 12), - "address 12 at unallocated location"); - check_equal(describe_address(memory, 1), - "address 1 at offset 1 within anonymous buffer at range (0 to 1)"); - check_equal(describe_address(memory, 2), - "address 2 at offset 0 within buffer 'buffer_name' at range (2 to 11)"); - check_equal(describe_address(memory, 5), - "address 5 at offset 3 within buffer 'buffer_name' at range (2 to 11)"); - - elsif run("Test set expected byte") then - memory := new_memory; - buf := allocate(memory, 2); - set_expected_byte(memory, 0, 77); - check_equal(has_expected_byte(memory, 0), true, "address 0 has expected byte"); - check_equal(has_expected_byte(memory, 1), false, "address 1 has no expected byte"); - - mock(memory_logger); - write_byte(memory, 0, 255); - check_only_log(memory_logger, "Writing to " & describe_address(memory, 0) & ". Got 255 expected 77", failure); - unmock(memory_logger); - - elsif run("Test set expected word") then - memory := new_memory; - buf := allocate(memory, 2); - set_expected_word(memory, 0, x"3322"); - - mock(memory_logger); - write_byte(memory, 0, 16#33#); - check_only_log(memory_logger, "Writing to " & describe_address(memory, 0) & ". Got 51 expected 34", failure); - write_byte(memory, 1, 16#22#); - check_only_log(memory_logger, "Writing to " & describe_address(memory, 1) & ". Got 34 expected 51", failure); - unmock(memory_logger); - - elsif run("Test set expected integer") then - memory := new_memory; - buf := allocate(memory, 2); - set_expected_integer(memory, 0, 16#3322#, bytes_per_word => 2); - check_equal(get_expected_byte(memory, 0), 16#22#); - check_equal(get_expected_byte(memory, 1), 16#33#); - - set_expected_integer(memory, 0, 16#3322#, bytes_per_word => 2, endian => big_endian); - check_equal(get_expected_byte(memory, 0), 16#33#); - check_equal(get_expected_byte(memory, 1), 16#22#); - - elsif run("Test clear expected byte") then - memory := new_memory; - buf := allocate(memory, 2); - set_expected_byte(memory, 0, 77); - check_equal(has_expected_byte(memory, 0), true, "address 0 has expected byte"); - check_equal(has_expected_byte(memory, 1), false, "address 1 has no expected byte"); - clear_expected_byte(memory, 0); - check_equal(has_expected_byte(memory, 0), false, "address 0 cleared expected byte"); - - elsif run("Test permissions and expected access functions ignore permissions") then - memory := new_memory; - buf := allocate(memory, 1, permissions => no_access); - - dummy_permissions := get_permissions(memory, 0); - set_permissions(memory, 0, no_access); - - dummy_boolean := has_expected_byte(memory, 0); - clear_expected_byte(memory, 0); - set_expected_byte(memory, 0, 0); - byte := get_expected_byte(memory, 0); - - elsif run("Test permissions and expected access functions have address check") then - memory := new_memory; - - mock(memory_logger); - dummy_permissions := get_permissions(memory, 0); - check_only_log(memory_logger, "Reading from empty memory", failure); - - set_permissions(memory, 0, no_access); - check_only_log(memory_logger, "Writing to empty memory", failure); - - dummy_boolean := has_expected_byte(memory, 0); - check_only_log(memory_logger, "Reading from empty memory", failure); - - clear_expected_byte(memory, 0); - check_only_log(memory_logger, "Writing to empty memory", failure); - - set_expected_byte(memory, 0, 0); - check_only_log(memory_logger, "Writing to empty memory", failure); - - byte := get_expected_byte(memory, 0); - check_only_log(memory_logger, "Reading from empty memory", failure); - unmock(memory_logger); - - elsif run("Test that expected data was written") then - memory := new_memory; - buf := allocate(memory, 3); - set_expected_byte(memory, 0, 77); - set_expected_byte(memory, 2, 66); - - mock(memory_logger); - check_false(expected_was_written(buf)); - check_expected_was_written(buf); - check_log(memory_logger, "The " & describe_address(memory, 0) & " was never written with expected byte 77", failure); - check_only_log(memory_logger, "The " & describe_address(memory, 2) & " was never written with expected byte 66", failure); - - write_byte(memory, 0, 77); - check_false(expected_was_written(buf)); - check_expected_was_written(buf); - check_only_log(memory_logger, "The " & describe_address(memory, 2) & " was never written with expected byte 66", failure); - unmock(memory_logger); - - write_byte(memory, 2, 66); - check(expected_was_written(buf)); - check_expected_was_written(buf); - unmock(memory_logger); - - elsif run("Test write_integer") then - memory := new_memory; - buf := allocate(memory, 4); - - test_write_integer(1, (0 => 1)); - test_write_integer(-1, (0 => 255)); - - test_write_integer(1, (1, 0, 0, 0)); - test_write_integer(-1, (255, 255, 255, 255)); - - test_write_integer(256, (0, 1, 0, 0)); - test_write_integer(-256, (0, 255, 255, 255)); - - test_write_integer(integer'high, (255, 255, 255, 127)); - test_write_integer(integer'low, (0, 0, 0, 128)); - - elsif run("Test write word") then - memory := new_memory; - buf := allocate(memory, 7); - test_write_word(x"11223344556677", (16#77#, 16#66#, 16#55#, 16#44#, 16#33#, 16#22#, 16#11#)); - - elsif run("Test read word") then - memory := new_memory; - buf := allocate(memory, 7+5); - - write_word(memory, 0, x"11223344556677"); - check_equal(read_word(memory, 0, 7), std_logic_vector'(x"11223344556677")); - check_equal(read_word(memory, 0, 1), std_logic_vector'(x"77")); - check_equal(read_word(memory, 1, 1), std_logic_vector'(x"66")); - - write_word(memory, 7, x"aaffbbccdd", endian => big_endian); - check_equal(read_word(memory, 7, 5, endian => big_endian), std_logic_vector'(x"aaffbbccdd")); - - -- Default endian - memory := new_memory(endian => big_endian); - buf := allocate(memory, 7+5); - - write_word(memory, 7, x"aaffbbccdd"); - check_equal(read_word(memory, 7, 5), std_logic_vector'(x"aaffbbccdd")); - check_equal(read_word(memory, 7, 1), std_logic_vector'(x"aa")); - check_equal(read_word(memory, 8, 1), std_logic_vector'(x"ff")); - end if; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +library vunit_lib; +context vunit_lib.vunit_context; + +use work.memory_pkg.all; +use work.string_ptr_pkg.all; +use work.random_pkg.all; + +entity tb_memory is + generic (runner_cfg : string); +end entity; + +architecture a of tb_memory is +begin + + main : process + variable memory : memory_t; + + procedure test_write_integer(word : integer; bytes : integer_vector) is + begin + write_integer(memory, 0, word, bytes_per_word => bytes'length); + for i in 0 to bytes'length-1 loop + check_equal(read_byte(memory, i), bytes(i)); + end loop; + write_integer(memory, 0, word, bytes_per_word => bytes'length, endian => big_endian); + for i in 0 to bytes'length-1 loop + check_equal(read_byte(memory, i), bytes(bytes'length - 1 - i)); + end loop; + end procedure; + + procedure test_write_word(word : std_logic_vector; bytes : integer_vector) is + begin + write_word(memory, 0, word); + for i in 0 to word'length/8-1 loop + check_equal(read_byte(memory, i), bytes(i)); + end loop; + + write_word(memory, 0, word, endian => big_endian); + for i in 0 to word'length/8-1 loop + check_equal(read_byte(memory, i), bytes(word'length/8 - 1 - i)); + end loop; + end procedure; + + variable buf : buffer_t; + variable byte : byte_t; + variable dummy_permissions : permissions_t; + variable dummy_boolean : boolean; + + begin + test_runner_setup(runner, runner_cfg); + + if run("Test default is null") then + assert memory = null_memory report "Default should be null memory"; + + elsif run("Test new memory") then + memory := new_memory; + assert memory /= null_memory report "Should not be null memory"; + assert num_bytes(memory) = 0 report "Empty memory should have zero bytes"; + + elsif run("Test clear memory") then + memory := new_memory; + for i in 0 to 2 loop + buf := allocate(memory, 1024); + check_equal(base_address(buf), 0, "base address"); + check_equal(last_address(buf), 1023, "last address"); + clear(memory); + end loop; + + elsif run("Test allocate") then + memory := new_memory; + + for i in 0 to 2 loop + buf := allocate(memory, 1024); + check_equal(base_address(buf), 1024*i, "base address"); + check_equal(last_address(buf), 1024*(i+1)-1, "last address"); + + assert num_bytes(buf) = 1024; + assert num_bytes(memory) = (i+1)*1024; + + end loop; + + elsif run("Test allocate a lot") then + -- Test that buf is efficient + memory := new_memory; + + for i in 0 to 500-1 loop + buf := allocate(memory, 500); + check_equal(base_address(buf), 500*i, "base address"); + check_equal(last_address(buf), 500*(i+1)-1, "last address"); + end loop; + + elsif run("Test allocate with alignment") then + memory := new_memory; + buf := allocate(memory, 1); + buf := allocate(memory, 1, alignment => 8); + check_equal(base_address(buf), 8, "aligned base address"); + + assert get_permissions(memory, 1) = no_access report "No access at unallocated location"; + assert get_permissions(memory, 7) = no_access report "No access at unallocated location"; + + buf := allocate(memory, 1, alignment => 16); + check_equal(base_address(buf), 16, "aligned base address"); + + elsif run("Test read and write byte") then + memory := new_memory; + buf := allocate(memory, 2); + + write_byte(memory, 0, 255); + write_byte(memory, 1, 128); + check_equal(read_byte(memory, 0), 255); + check_equal(read_byte(memory, 1), 128); + + elsif run("Test access empty memory") then + memory := new_memory; + mock(memory_logger); + write_byte(memory, 0, 255); + check_only_log(memory_logger, "Writing to empty memory", failure); + + byte := read_byte(memory, 0); + check_only_log(memory_logger, "Reading from empty memory", failure); + unmock(memory_logger); + + elsif run("Test access memory out of range") then + memory := new_memory; + buf := allocate(memory, 1); + + mock(memory_logger); + write_byte(memory, 1, 255); + check_only_log(memory_logger, "Writing to address 1 out of range 0 to 0", failure); + + byte := read_byte(memory, 1); + check_only_log(memory_logger, "Reading from address 1 out of range 0 to 0", failure); + unmock(memory_logger); + + elsif run("Test default permissions") then + memory := new_memory; + buf := allocate(memory, 1); + assert get_permissions(memory, 0) = read_and_write; + + elsif run("Test set permissions") then + memory := new_memory; + buf := allocate(memory, 1); + + set_permissions(memory, 0, no_access); + assert get_permissions(memory, 0) = no_access; + + set_permissions(memory, 0, write_only); + assert get_permissions(memory, 0) = write_only; + + set_permissions(memory, 0, read_only); + assert get_permissions(memory, 0) = read_only; + + set_permissions(memory, 0, read_and_write); + assert get_permissions(memory, 0) = read_and_write; + + elsif run("Test access memory without permission (no_access)") then + memory := new_memory; + buf := allocate(memory, 10); + set_permissions(memory, 5, no_access); + + mock(memory_logger); + write_byte(to_vc_interface(memory), 5, 255); + check_only_log(memory_logger, "Writing to " & describe_address(memory, 5) & " without permission (no_access)", failure); + + byte := read_byte(to_vc_interface(memory), 5); + check_only_log(memory_logger, "Reading from " & describe_address(memory, 5) & " without permission (no_access)", failure); + unmock(memory_logger); + + -- Ignore permissions + write_byte(memory, 5, 255); + byte := read_byte(memory, 5); + + elsif run("Test access memory without permission (write_only)") then + memory := new_memory; + buf := allocate(memory, 10); + set_permissions(memory, 5, write_only); + + write_byte(to_vc_interface(memory), 5, 255); + + mock(memory_logger); + byte := read_byte(to_vc_interface(memory), 5); + check_only_log(memory_logger, "Reading from " & describe_address(memory, 5) & " without permission (write_only)", failure); + unmock(memory_logger); + + -- Ignore permissions + write_byte(memory, 5, 255); + + byte := read_byte(memory, 5); + + elsif run("Test access memory without permission (read_only)") then + memory := new_memory; + buf := allocate(memory, 10); + set_permissions(memory, 5, read_only); + + mock(memory_logger); + write_byte(to_vc_interface(memory), 5, 255); + check_only_log(memory_logger, "Writing to " & describe_address(memory, 5) & " without permission (read_only)", failure); + unmock(memory_logger); + + byte := read_byte(memory, 5); + + -- Ignore permissions + write_byte(memory, 5, 255); + + byte := read_byte(memory, 5); + + elsif run("Test describe address") then + memory := new_memory; + buf := allocate(memory, 2); + buf := allocate(memory, 10, name => "buffer_name"); + assert name(buf) = "buffer_name"; + + check_equal(describe_address(memory, 12), + "address 12 at unallocated location"); + check_equal(describe_address(memory, 1), + "address 1 at offset 1 within anonymous buffer at range (0 to 1)"); + check_equal(describe_address(memory, 2), + "address 2 at offset 0 within buffer 'buffer_name' at range (2 to 11)"); + check_equal(describe_address(memory, 5), + "address 5 at offset 3 within buffer 'buffer_name' at range (2 to 11)"); + + elsif run("Test set expected byte") then + memory := new_memory; + buf := allocate(memory, 2); + set_expected_byte(memory, 0, 77); + check_equal(has_expected_byte(memory, 0), true, "address 0 has expected byte"); + check_equal(has_expected_byte(memory, 1), false, "address 1 has no expected byte"); + + mock(memory_logger); + write_byte(memory, 0, 255); + check_only_log(memory_logger, "Writing to " & describe_address(memory, 0) & ". Got 255 expected 77", failure); + unmock(memory_logger); + + elsif run("Test set expected word") then + memory := new_memory; + buf := allocate(memory, 2); + set_expected_word(memory, 0, x"3322"); + + mock(memory_logger); + write_byte(memory, 0, 16#33#); + check_only_log(memory_logger, "Writing to " & describe_address(memory, 0) & ". Got 51 expected 34", failure); + write_byte(memory, 1, 16#22#); + check_only_log(memory_logger, "Writing to " & describe_address(memory, 1) & ". Got 34 expected 51", failure); + unmock(memory_logger); + + elsif run("Test set expected integer") then + memory := new_memory; + buf := allocate(memory, 2); + set_expected_integer(memory, 0, 16#3322#, bytes_per_word => 2); + check_equal(get_expected_byte(memory, 0), 16#22#); + check_equal(get_expected_byte(memory, 1), 16#33#); + + set_expected_integer(memory, 0, 16#3322#, bytes_per_word => 2, endian => big_endian); + check_equal(get_expected_byte(memory, 0), 16#33#); + check_equal(get_expected_byte(memory, 1), 16#22#); + + elsif run("Test clear expected byte") then + memory := new_memory; + buf := allocate(memory, 2); + set_expected_byte(memory, 0, 77); + check_equal(has_expected_byte(memory, 0), true, "address 0 has expected byte"); + check_equal(has_expected_byte(memory, 1), false, "address 1 has no expected byte"); + clear_expected_byte(memory, 0); + check_equal(has_expected_byte(memory, 0), false, "address 0 cleared expected byte"); + + elsif run("Test permissions and expected access functions ignore permissions") then + memory := new_memory; + buf := allocate(memory, 1, permissions => no_access); + + dummy_permissions := get_permissions(memory, 0); + set_permissions(memory, 0, no_access); + + dummy_boolean := has_expected_byte(memory, 0); + clear_expected_byte(memory, 0); + set_expected_byte(memory, 0, 0); + byte := get_expected_byte(memory, 0); + + elsif run("Test permissions and expected access functions have address check") then + memory := new_memory; + + mock(memory_logger); + dummy_permissions := get_permissions(memory, 0); + check_only_log(memory_logger, "Reading from empty memory", failure); + + set_permissions(memory, 0, no_access); + check_only_log(memory_logger, "Writing to empty memory", failure); + + dummy_boolean := has_expected_byte(memory, 0); + check_only_log(memory_logger, "Reading from empty memory", failure); + + clear_expected_byte(memory, 0); + check_only_log(memory_logger, "Writing to empty memory", failure); + + set_expected_byte(memory, 0, 0); + check_only_log(memory_logger, "Writing to empty memory", failure); + + byte := get_expected_byte(memory, 0); + check_only_log(memory_logger, "Reading from empty memory", failure); + unmock(memory_logger); + + elsif run("Test that expected data was written") then + memory := new_memory; + buf := allocate(memory, 3); + set_expected_byte(memory, 0, 77); + set_expected_byte(memory, 2, 66); + + mock(memory_logger); + check_false(expected_was_written(buf)); + check_expected_was_written(buf); + check_log(memory_logger, "The " & describe_address(memory, 0) & " was never written with expected byte 77", failure); + check_only_log(memory_logger, "The " & describe_address(memory, 2) & " was never written with expected byte 66", failure); + + write_byte(memory, 0, 77); + check_false(expected_was_written(buf)); + check_expected_was_written(buf); + check_only_log(memory_logger, "The " & describe_address(memory, 2) & " was never written with expected byte 66", failure); + unmock(memory_logger); + + write_byte(memory, 2, 66); + check(expected_was_written(buf)); + check_expected_was_written(buf); + unmock(memory_logger); + + elsif run("Test write_integer") then + memory := new_memory; + buf := allocate(memory, 4); + + test_write_integer(1, (0 => 1)); + test_write_integer(-1, (0 => 255)); + + test_write_integer(1, (1, 0, 0, 0)); + test_write_integer(-1, (255, 255, 255, 255)); + + test_write_integer(256, (0, 1, 0, 0)); + test_write_integer(-256, (0, 255, 255, 255)); + + test_write_integer(integer'high, (255, 255, 255, 127)); + test_write_integer(integer'low, (0, 0, 0, 128)); + + elsif run("Test write word") then + memory := new_memory; + buf := allocate(memory, 7); + test_write_word(x"11223344556677", (16#77#, 16#66#, 16#55#, 16#44#, 16#33#, 16#22#, 16#11#)); + + elsif run("Test read word") then + memory := new_memory; + buf := allocate(memory, 7+5); + + write_word(memory, 0, x"11223344556677"); + check_equal(read_word(memory, 0, 7), std_logic_vector'(x"11223344556677")); + check_equal(read_word(memory, 0, 1), std_logic_vector'(x"77")); + check_equal(read_word(memory, 1, 1), std_logic_vector'(x"66")); + + write_word(memory, 7, x"aaffbbccdd", endian => big_endian); + check_equal(read_word(memory, 7, 5, endian => big_endian), std_logic_vector'(x"aaffbbccdd")); + + -- Default endian + memory := new_memory(endian => big_endian); + buf := allocate(memory, 7+5); + + write_word(memory, 7, x"aaffbbccdd"); + check_equal(read_word(memory, 7, 5), std_logic_vector'(x"aaffbbccdd")); + check_equal(read_word(memory, 7, 1), std_logic_vector'(x"aa")); + check_equal(read_word(memory, 8, 1), std_logic_vector'(x"ff")); + end if; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_memory_utils_pkg.vhd b/vunit/vhdl/verification_components/test/tb_memory_utils_pkg.vhd index 4f0888137..a58e5f991 100644 --- a/vunit/vhdl/verification_components/test/tb_memory_utils_pkg.vhd +++ b/vunit/vhdl/verification_components/test/tb_memory_utils_pkg.vhd @@ -1,176 +1,176 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -library vunit_lib; -context vunit_lib.vunit_context; - -use work.memory_pkg.all; -use work.memory_utils_pkg.all; -use work.integer_vector_ptr_pkg.all; -use work.integer_array_pkg.all; -use work.random_pkg.all; - -entity tb_memory_utils is - generic (runner_cfg : string); -end entity; - -architecture a of tb_memory_utils is -begin - - main : process - variable memory : memory_t; - variable buf : buffer_t; - variable integer_vector_ptr : integer_vector_ptr_t; - variable integer_array : integer_array_t; - begin - test_runner_setup(runner, runner_cfg); - - if run("Test write integer_vector_ptr") then - memory := new_memory; - integer_vector_ptr := random_integer_vector_ptr(10, 0, 255); - - buf := write_integer_vector_ptr(memory, integer_vector_ptr); - check_equal(base_address(buf), 0); - check_equal(last_address(buf), 4*10-1); - - for addr in base_address(buf) to last_address(buf) loop - assert get_permissions(memory, addr) = read_only; - end loop; - - for i in 0 to length(integer_vector_ptr)-1 loop - check_equal(read_byte(memory, base_address(buf) + 4*i), get(integer_vector_ptr, i)); - check_equal(read_byte(memory, base_address(buf) + 4*i+1), 0); - check_equal(read_byte(memory, base_address(buf) + 4*i+2), 0); - check_equal(read_byte(memory, base_address(buf) + 4*i+3), 0); - end loop; - - buf := write_integer_vector_ptr(memory, integer_vector_ptr, alignment => 16); - check_equal(base_address(buf), 48); - check_equal(last_address(buf), 48 + 4*10-1); - - buf := write_integer_vector_ptr(memory, integer_vector_ptr, bytes_per_word => 1, - permissions => read_and_write); - check_equal(base_address(buf), 48 + 4*10); - check_equal(last_address(buf), 48 + 4*10 + 10 - 1); - - for addr in base_address(buf) to last_address(buf) loop - assert get_permissions(memory, addr) = read_and_write; - check_equal(read_byte(memory, addr), get(integer_vector_ptr, addr - base_address(buf))); - end loop; - - elsif run("Test set expected integer_vector_ptr") then - memory := new_memory; - integer_vector_ptr := random_integer_vector_ptr(10, 0, 255); - - buf := set_expected_integer_vector_ptr(memory, integer_vector_ptr); - check_equal(base_address(buf), 0); - check_equal(last_address(buf), 4*10-1); - - for addr in base_address(buf) to last_address(buf) loop - assert get_permissions(memory, addr) = write_only; - end loop; - - for i in 0 to length(integer_vector_ptr)-1 loop - check_equal(get_expected_byte(memory, base_address(buf) + 4*i), get(integer_vector_ptr, i)); - check_equal(get_expected_byte(memory, base_address(buf) + 4*i+1), 0); - check_equal(get_expected_byte(memory, base_address(buf) + 4*i+2), 0); - check_equal(get_expected_byte(memory, base_address(buf) + 4*i+3), 0); - end loop; - - buf := set_expected_integer_vector_ptr(memory, integer_vector_ptr, alignment => 16); - check_equal(base_address(buf), 48); - check_equal(last_address(buf), 48 + 4*10-1); - - buf := set_expected_integer_vector_ptr(memory, integer_vector_ptr, - bytes_per_word => 1, permissions => read_and_write); - check_equal(base_address(buf), 48 + 4*10); - check_equal(last_address(buf), 48 + 4*10 + 10 - 1); - - for addr in base_address(buf) to last_address(buf) loop - assert get_permissions(memory, addr) = read_and_write; - check_equal(get_expected_byte(memory, addr), get(integer_vector_ptr, addr - base_address(buf))); - end loop; - - elsif run("Test write integer_array") then - memory := new_memory; - - integer_array := random_integer_array(10, min_value => 0, max_value => 255); - buf := write_integer_array(memory, integer_array); - check_equal(base_address(buf), 0); - check_equal(last_address(buf), 10-1); - - for addr in base_address(buf) to last_address(buf) loop - assert get_permissions(memory, addr) = read_only; - end loop; - - for i in 0 to integer_array.length-1 loop - check_equal(read_byte(memory, base_address(buf) + i), get(integer_array, i)); - end loop; - - elsif run("Test write integer_array with stride") then - memory := new_memory; - - integer_array := random_integer_array(2, 2, min_value => 0, max_value => 255); - buf := write_integer_array(memory, integer_array, stride_in_bytes => 4); - check_equal(base_address(buf), 0); - check_equal(last_address(buf), 2*4-1); - - for y in 0 to integer_array.height - 1 loop - for x in 0 to integer_array.width - 1 loop - check_equal(read_byte(memory, base_address(buf) + x + 4*y), get(integer_array, x, y)); - check(get_permissions(memory, base_address(buf) + x + 4*y) = read_only); - end loop; - - for x in integer_array.width to 3 loop - check(get_permissions(memory, base_address(buf) + x + 4*y) = read_only, - "Padding bytes should have same permissions"); - end loop; - end loop; - - elsif run("Test set expected integer_array") then - memory := new_memory; - - integer_array := random_integer_array(10, min_value => 0, max_value => 255); - buf := set_expected_integer_array(memory, integer_array); - check_equal(base_address(buf), 0); - check_equal(last_address(buf), 10-1); - - for addr in base_address(buf) to last_address(buf) loop - assert get_permissions(memory, addr) = write_only; - end loop; - - for i in 0 to integer_array.length-1 loop - check_equal(get_expected_byte(memory, base_address(buf) + i), get(integer_array, i)); - end loop; - - elsif run("Test set expected integer_array with stride") then - memory := new_memory; - - integer_array := random_integer_array(2, 2, min_value => 0, max_value => 255); - buf := set_expected_integer_array(memory, integer_array, stride_in_bytes => 4); - check_equal(base_address(buf), 0); - check_equal(last_address(buf), 2*4-1); - - for y in 0 to integer_array.height - 1 loop - for x in 0 to integer_array.width - 1 loop - check_equal(get_expected_byte(memory, base_address(buf) + x + 4*y), get(integer_array, x, y)); - check(get_permissions(memory, base_address(buf) + x + 4*y) = write_only); - end loop; - - for x in integer_array.width to 3 loop - check(get_permissions(memory, base_address(buf) + x + 4*y) = write_only, - "Padding bytes should have same permissions"); - end loop; - end loop; - - end if; - - test_runner_cleanup(runner); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +library vunit_lib; +context vunit_lib.vunit_context; + +use work.memory_pkg.all; +use work.memory_utils_pkg.all; +use work.integer_vector_ptr_pkg.all; +use work.integer_array_pkg.all; +use work.random_pkg.all; + +entity tb_memory_utils is + generic (runner_cfg : string); +end entity; + +architecture a of tb_memory_utils is +begin + + main : process + variable memory : memory_t; + variable buf : buffer_t; + variable integer_vector_ptr : integer_vector_ptr_t; + variable integer_array : integer_array_t; + begin + test_runner_setup(runner, runner_cfg); + + if run("Test write integer_vector_ptr") then + memory := new_memory; + integer_vector_ptr := random_integer_vector_ptr(10, 0, 255); + + buf := write_integer_vector_ptr(memory, integer_vector_ptr); + check_equal(base_address(buf), 0); + check_equal(last_address(buf), 4*10-1); + + for addr in base_address(buf) to last_address(buf) loop + assert get_permissions(memory, addr) = read_only; + end loop; + + for i in 0 to length(integer_vector_ptr)-1 loop + check_equal(read_byte(memory, base_address(buf) + 4*i), get(integer_vector_ptr, i)); + check_equal(read_byte(memory, base_address(buf) + 4*i+1), 0); + check_equal(read_byte(memory, base_address(buf) + 4*i+2), 0); + check_equal(read_byte(memory, base_address(buf) + 4*i+3), 0); + end loop; + + buf := write_integer_vector_ptr(memory, integer_vector_ptr, alignment => 16); + check_equal(base_address(buf), 48); + check_equal(last_address(buf), 48 + 4*10-1); + + buf := write_integer_vector_ptr(memory, integer_vector_ptr, bytes_per_word => 1, + permissions => read_and_write); + check_equal(base_address(buf), 48 + 4*10); + check_equal(last_address(buf), 48 + 4*10 + 10 - 1); + + for addr in base_address(buf) to last_address(buf) loop + assert get_permissions(memory, addr) = read_and_write; + check_equal(read_byte(memory, addr), get(integer_vector_ptr, addr - base_address(buf))); + end loop; + + elsif run("Test set expected integer_vector_ptr") then + memory := new_memory; + integer_vector_ptr := random_integer_vector_ptr(10, 0, 255); + + buf := set_expected_integer_vector_ptr(memory, integer_vector_ptr); + check_equal(base_address(buf), 0); + check_equal(last_address(buf), 4*10-1); + + for addr in base_address(buf) to last_address(buf) loop + assert get_permissions(memory, addr) = write_only; + end loop; + + for i in 0 to length(integer_vector_ptr)-1 loop + check_equal(get_expected_byte(memory, base_address(buf) + 4*i), get(integer_vector_ptr, i)); + check_equal(get_expected_byte(memory, base_address(buf) + 4*i+1), 0); + check_equal(get_expected_byte(memory, base_address(buf) + 4*i+2), 0); + check_equal(get_expected_byte(memory, base_address(buf) + 4*i+3), 0); + end loop; + + buf := set_expected_integer_vector_ptr(memory, integer_vector_ptr, alignment => 16); + check_equal(base_address(buf), 48); + check_equal(last_address(buf), 48 + 4*10-1); + + buf := set_expected_integer_vector_ptr(memory, integer_vector_ptr, + bytes_per_word => 1, permissions => read_and_write); + check_equal(base_address(buf), 48 + 4*10); + check_equal(last_address(buf), 48 + 4*10 + 10 - 1); + + for addr in base_address(buf) to last_address(buf) loop + assert get_permissions(memory, addr) = read_and_write; + check_equal(get_expected_byte(memory, addr), get(integer_vector_ptr, addr - base_address(buf))); + end loop; + + elsif run("Test write integer_array") then + memory := new_memory; + + integer_array := random_integer_array(10, min_value => 0, max_value => 255); + buf := write_integer_array(memory, integer_array); + check_equal(base_address(buf), 0); + check_equal(last_address(buf), 10-1); + + for addr in base_address(buf) to last_address(buf) loop + assert get_permissions(memory, addr) = read_only; + end loop; + + for i in 0 to integer_array.length-1 loop + check_equal(read_byte(memory, base_address(buf) + i), get(integer_array, i)); + end loop; + + elsif run("Test write integer_array with stride") then + memory := new_memory; + + integer_array := random_integer_array(2, 2, min_value => 0, max_value => 255); + buf := write_integer_array(memory, integer_array, stride_in_bytes => 4); + check_equal(base_address(buf), 0); + check_equal(last_address(buf), 2*4-1); + + for y in 0 to integer_array.height - 1 loop + for x in 0 to integer_array.width - 1 loop + check_equal(read_byte(memory, base_address(buf) + x + 4*y), get(integer_array, x, y)); + check(get_permissions(memory, base_address(buf) + x + 4*y) = read_only); + end loop; + + for x in integer_array.width to 3 loop + check(get_permissions(memory, base_address(buf) + x + 4*y) = read_only, + "Padding bytes should have same permissions"); + end loop; + end loop; + + elsif run("Test set expected integer_array") then + memory := new_memory; + + integer_array := random_integer_array(10, min_value => 0, max_value => 255); + buf := set_expected_integer_array(memory, integer_array); + check_equal(base_address(buf), 0); + check_equal(last_address(buf), 10-1); + + for addr in base_address(buf) to last_address(buf) loop + assert get_permissions(memory, addr) = write_only; + end loop; + + for i in 0 to integer_array.length-1 loop + check_equal(get_expected_byte(memory, base_address(buf) + i), get(integer_array, i)); + end loop; + + elsif run("Test set expected integer_array with stride") then + memory := new_memory; + + integer_array := random_integer_array(2, 2, min_value => 0, max_value => 255); + buf := set_expected_integer_array(memory, integer_array, stride_in_bytes => 4); + check_equal(base_address(buf), 0); + check_equal(last_address(buf), 2*4-1); + + for y in 0 to integer_array.height - 1 loop + for x in 0 to integer_array.width - 1 loop + check_equal(get_expected_byte(memory, base_address(buf) + x + 4*y), get(integer_array, x, y)); + check(get_permissions(memory, base_address(buf) + x + 4*y) = write_only); + end loop; + + for x in integer_array.width to 3 loop + check(get_permissions(memory, base_address(buf) + x + 4*y) = write_only, + "Padding bytes should have same permissions"); + end loop; + end loop; + + end if; + + test_runner_cleanup(runner); + end process; +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_ram_master.vhd b/vunit/vhdl/verification_components/test/tb_ram_master.vhd index b0b8e71cc..986222c91 100644 --- a/vunit/vhdl/verification_components/test/tb_ram_master.vhd +++ b/vunit/vhdl/verification_components/test/tb_ram_master.vhd @@ -1,158 +1,158 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library vunit_lib; -context vunit_lib.vunit_context; -context work.com_context; - -use work.queue_pkg.all; -use work.bus_master_pkg.all; - -entity tb_ram_master is - generic (runner_cfg : string); -end entity; - -architecture a of tb_ram_master is - constant latency : integer := 2; - constant num_back_to_back_reads : integer := 64; - - signal clk : std_logic := '0'; - signal en : std_logic; - signal we : std_logic_vector(3 downto 0); - signal addr : std_logic_vector(7 downto 0); - signal wdata : std_logic_vector(31 downto 0); - signal rdata : std_logic_vector(31 downto 0) := (others => '0'); - - constant bus_handle : bus_master_t := new_bus(data_length => wdata'length, address_length => addr'length); - - signal start, done : boolean := false; -begin - - main : process - variable reference : bus_reference_t; - variable reference_queue : queue_t := new_queue; - variable tmp : std_logic_vector(rdata'range); - begin - test_runner_setup(runner, runner_cfg); - start <= true; - wait for 0 ns; - - if run("Test single write") then - write_bus(net, bus_handle, x"77", x"11223344"); - - elsif run("Test single write with byte enable") then - write_bus(net, bus_handle, x"77", x"11223344", byte_enable => "0101"); - - elsif run("Test single read") then - read_bus(net, bus_handle, x"33", tmp); - check_equal(tmp, std_logic_vector'(x"55667788"), "read data"); - - elsif run("Test read back to back") then - for i in 1 to num_back_to_back_reads loop - read_bus(net, bus_handle, std_logic_vector(to_unsigned(i, addr'length)), reference); - push(reference_queue, reference); - end loop; - - for i in 1 to num_back_to_back_reads loop - reference := pop(reference_queue); - await_read_bus_reply(net, reference, tmp); - check_equal(tmp, std_logic_vector(to_unsigned(111*i, tmp'length)), "read data"); - end loop; - end if; - - wait for 100 ns; - - if not done then - wait until done; - end if; - - test_runner_cleanup(runner); - end process; - test_runner_watchdog(runner, 100 us); - - - support : process - begin - wait until start; - - if enabled("Test single write") then - wait until en = '1' and rising_edge(clk); - check_equal(en, '1', "en"); - check_equal(we, std_logic_vector'("1111"), "we"); - check_equal(addr, std_logic_vector'(x"77"), "addr"); - check_equal(wdata, std_logic_vector'(x"11223344"), "wdata"); - done <= true; - wait until en = '1' and rising_edge(clk); - assert false report "Should never happen"; - - elsif enabled("Test single write with byte enable") then - wait until en = '1' and rising_edge(clk); - check_equal(en, '1', "en"); - check_equal(we, std_logic_vector'("0101"), "we"); - check_equal(addr, std_logic_vector'(x"77"), "addr"); - check_equal(wdata, std_logic_vector'(x"11223344"), "wdata"); - done <= true; - wait until en = '1' and rising_edge(clk); - assert false report "Should never happen"; - - elsif enabled("Test single read") then - rdata <= x"11223344"; - wait until en = '1' and rising_edge(clk); - check_equal(we, std_logic_vector'("0000"), "we"); - check_equal(addr, std_logic_vector'(x"33"), "addr"); - for i in 2 to latency loop - wait until rising_edge(clk); - check_equal(en, '0', "en"); - end loop; - - rdata <= x"55667788"; - wait until rising_edge(clk); - check_equal(en, '0', "en"); - rdata <= x"99aabbcc"; - done <= true; - - elsif enabled("Test read back to back") then - wait until en = '1' and rising_edge(clk); - - for i in 1 to num_back_to_back_reads + (latency-1) loop - if i <= num_back_to_back_reads then - check_equal(en, '1', "en"); - check_equal(we, std_logic_vector'("0000"), "we"); - check_equal(addr, i, "addr"); - else - check_equal(en, '0', "en"); - end if; - - if i > (latency-1) then - rdata <= std_logic_vector(to_unsigned(111*(i - (latency-1)), rdata'length)); - end if; - - wait until rising_edge(clk); - end loop; - - done <= true; - end if; - end process; - - dut : entity work.ram_master - generic map ( - bus_handle => bus_handle, - latency => latency) - port map ( - clk => clk, - en => en, - we => we, - addr => addr, - wdata => wdata, - rdata => rdata); - - clk <= not clk after 5 ns; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library vunit_lib; +context vunit_lib.vunit_context; +context work.com_context; + +use work.queue_pkg.all; +use work.bus_master_pkg.all; + +entity tb_ram_master is + generic (runner_cfg : string); +end entity; + +architecture a of tb_ram_master is + constant latency : integer := 2; + constant num_back_to_back_reads : integer := 64; + + signal clk : std_logic := '0'; + signal en : std_logic; + signal we : std_logic_vector(3 downto 0); + signal addr : std_logic_vector(7 downto 0); + signal wdata : std_logic_vector(31 downto 0); + signal rdata : std_logic_vector(31 downto 0) := (others => '0'); + + constant bus_handle : bus_master_t := new_bus(data_length => wdata'length, address_length => addr'length); + + signal start, done : boolean := false; +begin + + main : process + variable reference : bus_reference_t; + variable reference_queue : queue_t := new_queue; + variable tmp : std_logic_vector(rdata'range); + begin + test_runner_setup(runner, runner_cfg); + start <= true; + wait for 0 ns; + + if run("Test single write") then + write_bus(net, bus_handle, x"77", x"11223344"); + + elsif run("Test single write with byte enable") then + write_bus(net, bus_handle, x"77", x"11223344", byte_enable => "0101"); + + elsif run("Test single read") then + read_bus(net, bus_handle, x"33", tmp); + check_equal(tmp, std_logic_vector'(x"55667788"), "read data"); + + elsif run("Test read back to back") then + for i in 1 to num_back_to_back_reads loop + read_bus(net, bus_handle, std_logic_vector(to_unsigned(i, addr'length)), reference); + push(reference_queue, reference); + end loop; + + for i in 1 to num_back_to_back_reads loop + reference := pop(reference_queue); + await_read_bus_reply(net, reference, tmp); + check_equal(tmp, std_logic_vector(to_unsigned(111*i, tmp'length)), "read data"); + end loop; + end if; + + wait for 100 ns; + + if not done then + wait until done; + end if; + + test_runner_cleanup(runner); + end process; + test_runner_watchdog(runner, 100 us); + + + support : process + begin + wait until start; + + if enabled("Test single write") then + wait until en = '1' and rising_edge(clk); + check_equal(en, '1', "en"); + check_equal(we, std_logic_vector'("1111"), "we"); + check_equal(addr, std_logic_vector'(x"77"), "addr"); + check_equal(wdata, std_logic_vector'(x"11223344"), "wdata"); + done <= true; + wait until en = '1' and rising_edge(clk); + assert false report "Should never happen"; + + elsif enabled("Test single write with byte enable") then + wait until en = '1' and rising_edge(clk); + check_equal(en, '1', "en"); + check_equal(we, std_logic_vector'("0101"), "we"); + check_equal(addr, std_logic_vector'(x"77"), "addr"); + check_equal(wdata, std_logic_vector'(x"11223344"), "wdata"); + done <= true; + wait until en = '1' and rising_edge(clk); + assert false report "Should never happen"; + + elsif enabled("Test single read") then + rdata <= x"11223344"; + wait until en = '1' and rising_edge(clk); + check_equal(we, std_logic_vector'("0000"), "we"); + check_equal(addr, std_logic_vector'(x"33"), "addr"); + for i in 2 to latency loop + wait until rising_edge(clk); + check_equal(en, '0', "en"); + end loop; + + rdata <= x"55667788"; + wait until rising_edge(clk); + check_equal(en, '0', "en"); + rdata <= x"99aabbcc"; + done <= true; + + elsif enabled("Test read back to back") then + wait until en = '1' and rising_edge(clk); + + for i in 1 to num_back_to_back_reads + (latency-1) loop + if i <= num_back_to_back_reads then + check_equal(en, '1', "en"); + check_equal(we, std_logic_vector'("0000"), "we"); + check_equal(addr, i, "addr"); + else + check_equal(en, '0', "en"); + end if; + + if i > (latency-1) then + rdata <= std_logic_vector(to_unsigned(111*(i - (latency-1)), rdata'length)); + end if; + + wait until rising_edge(clk); + end loop; + + done <= true; + end if; + end process; + + dut : entity work.ram_master + generic map ( + bus_handle => bus_handle, + latency => latency) + port map ( + clk => clk, + en => en, + we => we, + addr => addr, + wdata => wdata, + rdata => rdata); + + clk <= not clk after 5 ns; + +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_std_logic_checker.vhd b/vunit/vhdl/verification_components/test/tb_std_logic_checker.vhd index 599f0bb01..bda8d06ce 100644 --- a/vunit/vhdl/verification_components/test/tb_std_logic_checker.vhd +++ b/vunit/vhdl/verification_components/test/tb_std_logic_checker.vhd @@ -1,138 +1,138 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; - -context work.vunit_context; -context work.com_context; -use work.signal_checker_pkg.all; - -entity tb_std_logic_checker is - generic ( - runner_cfg : string); -end entity; - -architecture tb of tb_std_logic_checker is - constant logger : logger_t := get_logger("signal_checker"); - constant signal_checker : signal_checker_t := new_signal_checker(logger => logger); - - signal value : std_logic_vector(1 downto 0); - -begin - main : process - begin - test_runner_setup(runner, runner_cfg); - show(logger, display_handler, pass); - - if run("Test error on unexpected event") then - wait for 1 ns; - value <= "10"; - - mock(logger, error); - wait for 1 ns; - wait_until_idle(net, signal_checker); - check_only_log(logger, "Unexpected event with value = " & to_string(std_logic_vector'("10")), error); - unmock(logger); - - elsif run("Test error on wrong expected value") then - expect(net, signal_checker, "00", 1 ns); - - wait for 1 ns; - value <= "10"; - - mock(logger, error); - wait_until_idle(net, signal_checker); - check_only_log(logger, - "Got event with wrong value, got " & to_string(std_logic_vector'("10")) & - " expected " & to_string(std_logic_vector'("00")), error); - unmock(logger); - - elsif run("Test error on wrong time") then - expect(net, signal_checker, "10", 2 ns); - - wait for 1 ns; - value <= "10"; - - mock(logger, error); - wait_until_idle(net, signal_checker); - check_only_log(logger, - "Got event at wrong time, occured at " & time'image(1 ns) & - " expected at " & time'image(2 ns), error); - unmock(logger); - - elsif run("Test error wrong time margin") then - expect(net, signal_checker, "10", 3 ns, margin => 1 ns); - - wait for 1 ns; - value <= "10"; - - mock(logger, error); - wait_until_idle(net, signal_checker); - check_only_log(logger, - "Got event at wrong time, occured at " & time'image(1 ns) & - " expected at " & time'image(3 ns) & " +- " & time'image(1 ns), error); - unmock(logger); - - elsif run("Test expect single value") then - - expect(net, signal_checker, "10", 1 ns); - wait for 1 ns; - value <= "10"; - - mock(logger, pass); - wait_until_idle(net, signal_checker); - check_only_log(logger, "Got expected event with value = " & to_string(std_logic_vector'("10")), pass); - unmock(logger); - - elsif run("Test expect single value with late margin") then - - expect(net, signal_checker, "10", 2 ns, margin => 1 ns); - wait for 1 ns; - value <= "10"; - - mock(logger, pass); - wait_until_idle(net, signal_checker); - check_only_log(logger, "Got expected event with value = " & to_string(std_logic_vector'("10")), pass); - unmock(logger); - - elsif run("Test expect single value with early margin") then - - expect(net, signal_checker, "10", 1 ns, margin => 1 ns); - wait for 2 ns; - value <= "10"; - - mock(logger, pass); - wait_until_idle(net, signal_checker); - check_only_log(logger, "Got expected event with value = " & to_string(std_logic_vector'("10")), pass); - unmock(logger); - - elsif run("Test expect multiple values") then - expect(net, signal_checker, "10", 0 ns); - expect(net, signal_checker, "00", 3 ns); - expect(net, signal_checker, "ZZ", 5 ns); - - value <= "10", - "00" after 3 ns, - "00" after 4 ns, - "ZZ" after 5 ns; - - wait_until_idle(net, signal_checker); - - end if; - - test_runner_cleanup(runner); - end process; - - test_runner_watchdog(runner, 1 ms); - - dut: entity work.std_logic_checker - generic map ( - signal_checker => signal_checker) - port map ( - value => value); - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +context work.vunit_context; +context work.com_context; +use work.signal_checker_pkg.all; + +entity tb_std_logic_checker is + generic ( + runner_cfg : string); +end entity; + +architecture tb of tb_std_logic_checker is + constant logger : logger_t := get_logger("signal_checker"); + constant signal_checker : signal_checker_t := new_signal_checker(logger => logger); + + signal value : std_logic_vector(1 downto 0); + +begin + main : process + begin + test_runner_setup(runner, runner_cfg); + show(logger, display_handler, pass); + + if run("Test error on unexpected event") then + wait for 1 ns; + value <= "10"; + + mock(logger, error); + wait for 1 ns; + wait_until_idle(net, signal_checker); + check_only_log(logger, "Unexpected event with value = " & to_string(std_logic_vector'("10")), error); + unmock(logger); + + elsif run("Test error on wrong expected value") then + expect(net, signal_checker, "00", 1 ns); + + wait for 1 ns; + value <= "10"; + + mock(logger, error); + wait_until_idle(net, signal_checker); + check_only_log(logger, + "Got event with wrong value, got " & to_string(std_logic_vector'("10")) & + " expected " & to_string(std_logic_vector'("00")), error); + unmock(logger); + + elsif run("Test error on wrong time") then + expect(net, signal_checker, "10", 2 ns); + + wait for 1 ns; + value <= "10"; + + mock(logger, error); + wait_until_idle(net, signal_checker); + check_only_log(logger, + "Got event at wrong time, occured at " & time'image(1 ns) & + " expected at " & time'image(2 ns), error); + unmock(logger); + + elsif run("Test error wrong time margin") then + expect(net, signal_checker, "10", 3 ns, margin => 1 ns); + + wait for 1 ns; + value <= "10"; + + mock(logger, error); + wait_until_idle(net, signal_checker); + check_only_log(logger, + "Got event at wrong time, occured at " & time'image(1 ns) & + " expected at " & time'image(3 ns) & " +- " & time'image(1 ns), error); + unmock(logger); + + elsif run("Test expect single value") then + + expect(net, signal_checker, "10", 1 ns); + wait for 1 ns; + value <= "10"; + + mock(logger, pass); + wait_until_idle(net, signal_checker); + check_only_log(logger, "Got expected event with value = " & to_string(std_logic_vector'("10")), pass); + unmock(logger); + + elsif run("Test expect single value with late margin") then + + expect(net, signal_checker, "10", 2 ns, margin => 1 ns); + wait for 1 ns; + value <= "10"; + + mock(logger, pass); + wait_until_idle(net, signal_checker); + check_only_log(logger, "Got expected event with value = " & to_string(std_logic_vector'("10")), pass); + unmock(logger); + + elsif run("Test expect single value with early margin") then + + expect(net, signal_checker, "10", 1 ns, margin => 1 ns); + wait for 2 ns; + value <= "10"; + + mock(logger, pass); + wait_until_idle(net, signal_checker); + check_only_log(logger, "Got expected event with value = " & to_string(std_logic_vector'("10")), pass); + unmock(logger); + + elsif run("Test expect multiple values") then + expect(net, signal_checker, "10", 0 ns); + expect(net, signal_checker, "00", 3 ns); + expect(net, signal_checker, "ZZ", 5 ns); + + value <= "10", + "00" after 3 ns, + "00" after 4 ns, + "ZZ" after 5 ns; + + wait_until_idle(net, signal_checker); + + end if; + + test_runner_cleanup(runner); + end process; + + test_runner_watchdog(runner, 1 ms); + + dut: entity work.std_logic_checker + generic map ( + signal_checker => signal_checker) + port map ( + value => value); + +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_sync_pkg.vhd b/vunit/vhdl/verification_components/test/tb_sync_pkg.vhd index e41c9c6a4..d38fcdb15 100644 --- a/vunit/vhdl/verification_components/test/tb_sync_pkg.vhd +++ b/vunit/vhdl/verification_components/test/tb_sync_pkg.vhd @@ -1,48 +1,48 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - - -library vunit_lib; -context vunit_lib.vunit_context; -context work.com_context; -use work.sync_pkg.all; - -entity tb_sync_pkg is - generic (runner_cfg : string); -end entity; - -architecture a of tb_sync_pkg is - constant actor : actor_t := new_actor; -begin - - main : process - variable start : time; - begin - test_runner_setup(runner, runner_cfg); - - start := now; - wait_for_time(net, actor, 11 ns); - wait_until_idle(net, actor); - check_equal(now - start, 11 ns, "wait for time mismatch"); - - start := now; - wait_for_time(net, actor, 37 ms); - wait_until_idle(net, actor); - check_equal(now - start, 37 ms, "wait for time mismatch"); - - test_runner_cleanup(runner); - end process; - - support : process - variable msg : msg_t; - variable msg_type : msg_type_t; - begin - receive(net, actor, msg); - msg_type := message_type(msg); - handle_sync_message(net, msg_type, msg); - unexpected_msg_type(msg_type); - end process; -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + + +library vunit_lib; +context vunit_lib.vunit_context; +context work.com_context; +use work.sync_pkg.all; + +entity tb_sync_pkg is + generic (runner_cfg : string); +end entity; + +architecture a of tb_sync_pkg is + constant actor : actor_t := new_actor; +begin + + main : process + variable start : time; + begin + test_runner_setup(runner, runner_cfg); + + start := now; + wait_for_time(net, actor, 11 ns); + wait_until_idle(net, actor); + check_equal(now - start, 11 ns, "wait for time mismatch"); + + start := now; + wait_for_time(net, actor, 37 ms); + wait_until_idle(net, actor); + check_equal(now - start, 37 ms, "wait for time mismatch"); + + test_runner_cleanup(runner); + end process; + + support : process + variable msg : msg_t; + variable msg_type : msg_type_t; + begin + receive(net, actor, msg); + msg_type := message_type(msg); + handle_sync_message(net, msg_type, msg); + unexpected_msg_type(msg_type); + end process; +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_uart.vhd b/vunit/vhdl/verification_components/test/tb_uart.vhd index b8f1275ae..ca3f3a389 100644 --- a/vunit/vhdl/verification_components/test/tb_uart.vhd +++ b/vunit/vhdl/verification_components/test/tb_uart.vhd @@ -1,104 +1,104 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -context work.vunit_context; -context work.com_context; -context work.data_types_context; -use work.uart_pkg.all; -use work.sync_pkg.all; -use work.stream_master_pkg.all; -use work.stream_slave_pkg.all; - -entity tb_uart is - generic (runner_cfg : string); -end entity; - -architecture a of tb_uart is - constant master_uart : uart_master_t := new_uart_master; - constant master_stream : stream_master_t := as_stream(master_uart); - - constant slave_uart : uart_slave_t := new_uart_slave(data_length => 8); - constant slave_stream : stream_slave_t := as_stream(slave_uart); - - signal chan : std_logic; -begin - - main : process - variable data : std_logic_vector(7 downto 0); - variable reference_queue : queue_t := new_queue; - variable reference : stream_reference_t; - - procedure test_baud_rate(baud_rate : natural) is - variable start : time; - variable got, expected : time; - begin - set_baud_rate(net, master_uart, baud_rate); - set_baud_rate(net, slave_uart, baud_rate); - - start := now; - push_stream(net, master_stream, x"77"); - check_stream(net, slave_stream, x"77"); - - wait_until_idle(net, as_sync(master_uart)); - - got := now - start; - expected := (10 * (1 sec)) / (baud_rate); - - check(abs (got - expected) <= 10 ns, - "Unexpected baud rate got " & to_string(got) & " expected " & to_string(expected)); - end; - begin - test_runner_setup(runner, runner_cfg); - - if run("test single push and pop") then - push_stream(net, master_stream, x"77"); - pop_stream(net, slave_stream, data); - check_equal(data, std_logic_vector'(x"77"), "pop stream data"); - - elsif run("test pop before push") then - for i in 0 to 7 loop - pop_stream(net, slave_stream, reference); - push(reference_queue, reference); - end loop; - - for i in 0 to 7 loop - push_stream(net, master_stream, - std_logic_vector(to_unsigned(i+1, data'length))); - end loop; - - for i in 0 to 7 loop - reference := pop(reference_queue); - await_pop_stream_reply(net, reference, data); - check_equal(data, to_unsigned(i+1, data'length)); - end loop; - - elsif run("test baud rate") then - test_baud_rate(2000); - test_baud_rate(7000); - test_baud_rate(200000); - end if; - - test_runner_cleanup(runner); - end process; - test_runner_watchdog(runner, 20 ms); - - uart_master_inst: entity work.uart_master - generic map ( - uart => master_uart) - port map ( - tx => chan); - - uart_slave_inst: entity work.uart_slave - generic map ( - uart => slave_uart) - port map ( - rx => chan); - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +context work.vunit_context; +context work.com_context; +context work.data_types_context; +use work.uart_pkg.all; +use work.sync_pkg.all; +use work.stream_master_pkg.all; +use work.stream_slave_pkg.all; + +entity tb_uart is + generic (runner_cfg : string); +end entity; + +architecture a of tb_uart is + constant master_uart : uart_master_t := new_uart_master; + constant master_stream : stream_master_t := as_stream(master_uart); + + constant slave_uart : uart_slave_t := new_uart_slave(data_length => 8); + constant slave_stream : stream_slave_t := as_stream(slave_uart); + + signal chan : std_logic; +begin + + main : process + variable data : std_logic_vector(7 downto 0); + variable reference_queue : queue_t := new_queue; + variable reference : stream_reference_t; + + procedure test_baud_rate(baud_rate : natural) is + variable start : time; + variable got, expected : time; + begin + set_baud_rate(net, master_uart, baud_rate); + set_baud_rate(net, slave_uart, baud_rate); + + start := now; + push_stream(net, master_stream, x"77"); + check_stream(net, slave_stream, x"77"); + + wait_until_idle(net, as_sync(master_uart)); + + got := now - start; + expected := (10 * (1 sec)) / (baud_rate); + + check(abs (got - expected) <= 10 ns, + "Unexpected baud rate got " & to_string(got) & " expected " & to_string(expected)); + end; + begin + test_runner_setup(runner, runner_cfg); + + if run("test single push and pop") then + push_stream(net, master_stream, x"77"); + pop_stream(net, slave_stream, data); + check_equal(data, std_logic_vector'(x"77"), "pop stream data"); + + elsif run("test pop before push") then + for i in 0 to 7 loop + pop_stream(net, slave_stream, reference); + push(reference_queue, reference); + end loop; + + for i in 0 to 7 loop + push_stream(net, master_stream, + std_logic_vector(to_unsigned(i+1, data'length))); + end loop; + + for i in 0 to 7 loop + reference := pop(reference_queue); + await_pop_stream_reply(net, reference, data); + check_equal(data, to_unsigned(i+1, data'length)); + end loop; + + elsif run("test baud rate") then + test_baud_rate(2000); + test_baud_rate(7000); + test_baud_rate(200000); + end if; + + test_runner_cleanup(runner); + end process; + test_runner_watchdog(runner, 20 ms); + + uart_master_inst: entity work.uart_master + generic map ( + uart => master_uart) + port map ( + tx => chan); + + uart_slave_inst: entity work.uart_slave + generic map ( + uart => slave_uart) + port map ( + rx => chan); + +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_wishbone_master.vhd b/vunit/vhdl/verification_components/test/tb_wishbone_master.vhd index 78c40c2f2..cd63cac03 100644 --- a/vunit/vhdl/verification_components/test/tb_wishbone_master.vhd +++ b/vunit/vhdl/verification_components/test/tb_wishbone_master.vhd @@ -1,204 +1,204 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com --- Author Slawomir Siluk slaweksiluk@gazeta.pl - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -context work.vunit_context; -context work.com_context; -use work.memory_pkg.all; -use work.wishbone_pkg.all; -use work.bus_master_pkg.all; - -library osvvm; -use osvvm.RandomPkg.all; - -entity tb_wishbone_master is - generic ( - runner_cfg : string; - encoded_tb_cfg : string - ); -end entity; - -architecture a of tb_wishbone_master is - - type tb_cfg_t is record - dat_width : positive; - adr_width : positive; - num_cycles : positive; - strobe_prob : real; - ack_prob : real; - stall_prob : real; - end record tb_cfg_t; - - impure function decode(encoded_tb_cfg : string) return tb_cfg_t is - begin - return (dat_width => positive'value(get(encoded_tb_cfg, "dat_width")), - adr_width => positive'value(get(encoded_tb_cfg, "adr_width")), - num_cycles => positive'value(get(encoded_tb_cfg, "num_cycles")), - strobe_prob => real'value(get(encoded_tb_cfg, "strobe_prob")), - ack_prob => real'value(get(encoded_tb_cfg, "ack_prob")), - stall_prob => real'value(get(encoded_tb_cfg, "stall_prob"))); - end function decode; - - constant tb_cfg : tb_cfg_t := decode(encoded_tb_cfg); - - signal clk : std_logic := '0'; - signal adr : std_logic_vector(tb_cfg.adr_width-1 downto 0); - signal dat_i : std_logic_vector(tb_cfg.dat_width-1 downto 0); - signal dat_o : std_logic_vector(tb_cfg.dat_width-1 downto 0); - signal sel : std_logic_vector(tb_cfg.dat_width/8 -1 downto 0); - signal cyc : std_logic := '0'; - signal stb : std_logic := '0'; - signal we : std_logic := '0'; - signal stall : std_logic := '0'; - signal ack : std_logic := '0'; - - constant master_logger : logger_t := get_logger("master"); - constant tb_logger : logger_t := get_logger("tb"); - constant bus_handle : bus_master_t := new_bus(data_length => tb_cfg.dat_width, - address_length => tb_cfg.adr_width, logger => master_logger); - - constant memory : memory_t := new_memory; - constant buf : buffer_t := allocate(memory, tb_cfg.num_cycles * sel'length); - constant wishbone_slave : wishbone_slave_t := new_wishbone_slave( - memory => memory, - ack_high_probability => tb_cfg.ack_prob, - stall_high_probability => tb_cfg.stall_prob - ); - -begin - - main_stim : process - variable tmp : std_logic_vector(dat_i'range); - variable chk_time : time; - variable value : std_logic_vector(dat_i'range) := (others => '1'); - variable bus_rd_ref1 : bus_reference_t; - variable bus_rd_ref2 : bus_reference_t; - type bus_reference_arr_t is array (0 to tb_cfg.num_cycles-1) of bus_reference_t; - variable rd_ref : bus_reference_arr_t; - begin - test_runner_setup(runner, runner_cfg); - set_format(display_handler, verbose, true); - show(tb_logger, display_handler, verbose); - show(default_logger, display_handler, verbose); - show(master_logger, display_handler, verbose); - show(com_logger, display_handler, verbose); - - wait until rising_edge(clk); - - if run("wr single rd single") then - info(tb_logger, "Writing..."); - write_bus(net, bus_handle, 0, value); - wait until ack = '1' and rising_edge(clk); - wait until rising_edge(clk); - wait for 100 ns; - info(tb_logger, "Reading..."); - read_bus(net, bus_handle, 0, tmp); - check_equal(tmp, value, "read data"); --- elsif run("wr block") then --- -- TODO not sure if is allowed to toggle signal we during --- -- wishbone single cycle --- write_bus(net, bus_handle, 0, value); --- read_bus(net, bus_handle, 0, tmp); --- check_equal(tmp, value, "read data"); - elsif run("wr block rd single") then - info(tb_logger, "Writing..."); - for i in 0 to tb_cfg.num_cycles-1 loop - write_bus(net, bus_handle, i*(sel'length), - std_logic_vector(to_unsigned(i, dat_i'length))); - end loop; - - info(tb_logger, "Reading..."); - for i in 0 to tb_cfg.num_cycles-1 loop - read_bus(net, bus_handle, i*(sel'length), tmp); - check_equal(tmp, std_logic_vector(to_unsigned(i, dat_i'length)), "read data"); - end loop; - - elsif run("wr block rd block") then - info(tb_logger, "Writing..."); - for i in 0 to tb_cfg.num_cycles-1 loop - write_bus(net, bus_handle, i*(sel'length), - std_logic_vector(to_unsigned(i, dat_i'length))); - end loop; - - info(tb_logger, "Reading..."); - for i in 0 to tb_cfg.num_cycles-1 loop - read_bus(net, bus_handle, i*(sel'length), rd_ref(i)); - end loop; - - info(tb_logger, "Get reads by references..."); - for i in 0 to tb_cfg.num_cycles-1 loop - await_read_bus_reply(net, rd_ref(i), tmp); - check_equal(tmp, std_logic_vector(to_unsigned(i, dat_i'length)), "read data"); - end loop; - - elsif run ("wait for idle") then - for i in 0 to 5 loop - chk_time := now; - read_bus(net, bus_handle, 0, bus_rd_ref1); - write_bus(net, bus_handle, 0, value); - read_bus(net, bus_handle, 0, bus_rd_ref2); - check_equal(chk_time, now, "tb concept broken, command issueing takes time"); - wait_until_idle(net, bus_handle); - check_equal(cyc, '0', "bus not idle"); - info(tb_logger, "Waited for " & to_string(now-chk_time)); - chk_time := now; - await_read_bus_reply(net, bus_rd_ref1, tmp); - await_read_bus_reply(net, bus_rd_ref2, tmp); - check_equal(chk_time, now, "reading was not done"); - check_equal(tmp, value, "read value incorrect"); - wait for 20 ns; - end loop; - - end if; - - info(tb_logger, "Done, quit..."); - wait for 50 ns; - test_runner_cleanup(runner); - wait; - end process; - test_runner_watchdog(runner, 100 us); - - dut : entity work.wishbone_master - generic map ( - bus_handle => bus_handle, - strobe_high_probability => tb_cfg.strobe_prob) - port map ( - clk => clk, - adr => adr, - dat_i => dat_i, - dat_o => dat_o, - sel => sel, - cyc => cyc, - stb => stb, - we => we, - stall => stall, - ack => ack - ); - - dut_slave : entity work.wishbone_slave - generic map ( - wishbone_slave => wishbone_slave - ) - port map ( - clk => clk, - adr => adr, - dat_i => dat_o, - dat_o => dat_i, - sel => sel, - cyc => cyc, - stb => stb, - we => we, - stall => stall, - ack => ack - ); - - clk <= not clk after 5 ns; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +-- Author Slawomir Siluk slaweksiluk@gazeta.pl + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +context work.vunit_context; +context work.com_context; +use work.memory_pkg.all; +use work.wishbone_pkg.all; +use work.bus_master_pkg.all; + +library osvvm; +use osvvm.RandomPkg.all; + +entity tb_wishbone_master is + generic ( + runner_cfg : string; + encoded_tb_cfg : string + ); +end entity; + +architecture a of tb_wishbone_master is + + type tb_cfg_t is record + dat_width : positive; + adr_width : positive; + num_cycles : positive; + strobe_prob : real; + ack_prob : real; + stall_prob : real; + end record tb_cfg_t; + + impure function decode(encoded_tb_cfg : string) return tb_cfg_t is + begin + return (dat_width => positive'value(get(encoded_tb_cfg, "dat_width")), + adr_width => positive'value(get(encoded_tb_cfg, "adr_width")), + num_cycles => positive'value(get(encoded_tb_cfg, "num_cycles")), + strobe_prob => real'value(get(encoded_tb_cfg, "strobe_prob")), + ack_prob => real'value(get(encoded_tb_cfg, "ack_prob")), + stall_prob => real'value(get(encoded_tb_cfg, "stall_prob"))); + end function decode; + + constant tb_cfg : tb_cfg_t := decode(encoded_tb_cfg); + + signal clk : std_logic := '0'; + signal adr : std_logic_vector(tb_cfg.adr_width-1 downto 0); + signal dat_i : std_logic_vector(tb_cfg.dat_width-1 downto 0); + signal dat_o : std_logic_vector(tb_cfg.dat_width-1 downto 0); + signal sel : std_logic_vector(tb_cfg.dat_width/8 -1 downto 0); + signal cyc : std_logic := '0'; + signal stb : std_logic := '0'; + signal we : std_logic := '0'; + signal stall : std_logic := '0'; + signal ack : std_logic := '0'; + + constant master_logger : logger_t := get_logger("master"); + constant tb_logger : logger_t := get_logger("tb"); + constant bus_handle : bus_master_t := new_bus(data_length => tb_cfg.dat_width, + address_length => tb_cfg.adr_width, logger => master_logger); + + constant memory : memory_t := new_memory; + constant buf : buffer_t := allocate(memory, tb_cfg.num_cycles * sel'length); + constant wishbone_slave : wishbone_slave_t := new_wishbone_slave( + memory => memory, + ack_high_probability => tb_cfg.ack_prob, + stall_high_probability => tb_cfg.stall_prob + ); + +begin + + main_stim : process + variable tmp : std_logic_vector(dat_i'range); + variable chk_time : time; + variable value : std_logic_vector(dat_i'range) := (others => '1'); + variable bus_rd_ref1 : bus_reference_t; + variable bus_rd_ref2 : bus_reference_t; + type bus_reference_arr_t is array (0 to tb_cfg.num_cycles-1) of bus_reference_t; + variable rd_ref : bus_reference_arr_t; + begin + test_runner_setup(runner, runner_cfg); + set_format(display_handler, verbose, true); + show(tb_logger, display_handler, verbose); + show(default_logger, display_handler, verbose); + show(master_logger, display_handler, verbose); + show(com_logger, display_handler, verbose); + + wait until rising_edge(clk); + + if run("wr single rd single") then + info(tb_logger, "Writing..."); + write_bus(net, bus_handle, 0, value); + wait until ack = '1' and rising_edge(clk); + wait until rising_edge(clk); + wait for 100 ns; + info(tb_logger, "Reading..."); + read_bus(net, bus_handle, 0, tmp); + check_equal(tmp, value, "read data"); +-- elsif run("wr block") then +-- -- TODO not sure if is allowed to toggle signal we during +-- -- wishbone single cycle +-- write_bus(net, bus_handle, 0, value); +-- read_bus(net, bus_handle, 0, tmp); +-- check_equal(tmp, value, "read data"); + elsif run("wr block rd single") then + info(tb_logger, "Writing..."); + for i in 0 to tb_cfg.num_cycles-1 loop + write_bus(net, bus_handle, i*(sel'length), + std_logic_vector(to_unsigned(i, dat_i'length))); + end loop; + + info(tb_logger, "Reading..."); + for i in 0 to tb_cfg.num_cycles-1 loop + read_bus(net, bus_handle, i*(sel'length), tmp); + check_equal(tmp, std_logic_vector(to_unsigned(i, dat_i'length)), "read data"); + end loop; + + elsif run("wr block rd block") then + info(tb_logger, "Writing..."); + for i in 0 to tb_cfg.num_cycles-1 loop + write_bus(net, bus_handle, i*(sel'length), + std_logic_vector(to_unsigned(i, dat_i'length))); + end loop; + + info(tb_logger, "Reading..."); + for i in 0 to tb_cfg.num_cycles-1 loop + read_bus(net, bus_handle, i*(sel'length), rd_ref(i)); + end loop; + + info(tb_logger, "Get reads by references..."); + for i in 0 to tb_cfg.num_cycles-1 loop + await_read_bus_reply(net, rd_ref(i), tmp); + check_equal(tmp, std_logic_vector(to_unsigned(i, dat_i'length)), "read data"); + end loop; + + elsif run ("wait for idle") then + for i in 0 to 5 loop + chk_time := now; + read_bus(net, bus_handle, 0, bus_rd_ref1); + write_bus(net, bus_handle, 0, value); + read_bus(net, bus_handle, 0, bus_rd_ref2); + check_equal(chk_time, now, "tb concept broken, command issueing takes time"); + wait_until_idle(net, bus_handle); + check_equal(cyc, '0', "bus not idle"); + info(tb_logger, "Waited for " & to_string(now-chk_time)); + chk_time := now; + await_read_bus_reply(net, bus_rd_ref1, tmp); + await_read_bus_reply(net, bus_rd_ref2, tmp); + check_equal(chk_time, now, "reading was not done"); + check_equal(tmp, value, "read value incorrect"); + wait for 20 ns; + end loop; + + end if; + + info(tb_logger, "Done, quit..."); + wait for 50 ns; + test_runner_cleanup(runner); + wait; + end process; + test_runner_watchdog(runner, 100 us); + + dut : entity work.wishbone_master + generic map ( + bus_handle => bus_handle, + strobe_high_probability => tb_cfg.strobe_prob) + port map ( + clk => clk, + adr => adr, + dat_i => dat_i, + dat_o => dat_o, + sel => sel, + cyc => cyc, + stb => stb, + we => we, + stall => stall, + ack => ack + ); + + dut_slave : entity work.wishbone_slave + generic map ( + wishbone_slave => wishbone_slave + ) + port map ( + clk => clk, + adr => adr, + dat_i => dat_o, + dat_o => dat_i, + sel => sel, + cyc => cyc, + stb => stb, + we => we, + stall => stall, + ack => ack + ); + + clk <= not clk after 5 ns; + +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_wishbone_slave.vhd b/vunit/vhdl/verification_components/test/tb_wishbone_slave.vhd index 0cb4262a5..972009631 100644 --- a/vunit/vhdl/verification_components/test/tb_wishbone_slave.vhd +++ b/vunit/vhdl/verification_components/test/tb_wishbone_slave.vhd @@ -1,151 +1,151 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com --- Author Slawomir Siluk slaweksiluk@gazeta.pl - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -context work.vunit_context; -context work.com_context; -use work.memory_pkg.all; -use work.wishbone_pkg.all; - -entity tb_wishbone_slave is - generic ( - runner_cfg : string; - encoded_tb_cfg : string - ); -end entity; - -architecture a of tb_wishbone_slave is - - type tb_cfg_t is record - dat_width : positive; - adr_width : positive; - num_cycles : positive; - ack_prob : real; - stall_prob : real; - end record tb_cfg_t; - - impure function decode(encoded_tb_cfg : string) return tb_cfg_t is - begin - return (dat_width => positive'value(get(encoded_tb_cfg, "dat_width")), - adr_width => positive'value(get(encoded_tb_cfg, "adr_width")), - num_cycles => positive'value(get(encoded_tb_cfg, "num_cycles")), - ack_prob => real'value(get(encoded_tb_cfg, "ack_prob")), - stall_prob => real'value(get(encoded_tb_cfg, "stall_prob"))); - end function decode; - - constant tb_cfg : tb_cfg_t := decode(encoded_tb_cfg); - - signal clk : std_logic := '0'; - signal adr : std_logic_vector(tb_cfg.adr_width-1 downto 0) := (others => '0'); - signal dat_i : std_logic_vector(tb_cfg.dat_width-1 downto 0) := (others => '0'); - signal dat_o : std_logic_vector(tb_cfg.dat_width-1 downto 0) := (others => '0'); - signal sel : std_logic_vector(tb_cfg.dat_width/8 -1 downto 0) := (others => '1'); - signal cyc : std_logic := '0'; - signal stb : std_logic := '0'; - signal we : std_logic := '0'; - signal stall : std_logic := '0'; - signal ack : std_logic := '0'; - - - constant tb_logger : logger_t := get_logger("tb"); - - signal wr_ack_cnt : natural range 0 to tb_cfg.num_cycles; - signal rd_ack_cnt : natural range 0 to tb_cfg.num_cycles; - - constant memory : memory_t := new_memory; - constant buf : buffer_t := allocate(memory, tb_cfg.num_cycles * sel'length); - constant wishbone_slave : wishbone_slave_t := - new_wishbone_slave(memory => memory, - ack_high_probability => tb_cfg.ack_prob, - stall_high_probability => tb_cfg.stall_prob - ); -begin - - main_stim : process - variable tmp : std_logic_vector(dat_i'range); - variable value : std_logic_vector(dat_i'range) := (others => '1'); - begin - test_runner_setup(runner, runner_cfg); - set_format(display_handler, verbose, true); - show(tb_logger, display_handler, verbose); - show(default_logger, display_handler, verbose); - show(com_logger, display_handler, verbose); - wait until rising_edge(clk); - - - if run("wr block rd block") then - info(tb_logger, "Writing..."); - for i in 0 to tb_cfg.num_cycles-1 loop - cyc <= '1'; - stb <= '1'; - we <= '1'; - adr <= std_logic_vector(to_unsigned(i*(sel'length), adr'length)); - dat_i <= std_logic_vector(to_unsigned(i, dat_i'length)); - wait until rising_edge(clk) and stall = '0'; - end loop; - stb <= '0'; - wait until wr_ack_cnt = tb_cfg.num_cycles; - cyc <= '0'; - - wait until rising_edge(clk); - - info(tb_logger, "Reading..."); - for i in 0 to tb_cfg.num_cycles-1 loop - cyc <= '1'; - stb <= '1'; - we <= '0'; - adr <= std_logic_vector(to_unsigned(i*(sel'length), adr'length)); - wait until rising_edge(clk) and stall = '0'; - end loop; - stb <= '0'; - wait until rising_edge(clk) and rd_ack_cnt = tb_cfg.num_cycles-1; - cyc <= '0'; - end if; - - wait for 50 ns; - test_runner_cleanup(runner); - wait; - end process; - test_runner_watchdog(runner, 100 us); - - wr_ack: process - begin - wait until rising_edge(clk) and ack = '1' and we = '1'; - wr_ack_cnt <= wr_ack_cnt +1; - end process; - - rd_ack: process - begin - wait until rising_edge(clk) and ack = '1' and we = '0'; - check_equal(dat_o, std_logic_vector(to_unsigned(rd_ack_cnt, - dat_o'length)), "dat_o"); - rd_ack_cnt <= rd_ack_cnt +1; - end process; - - dut_slave : entity work.wishbone_slave - generic map ( - wishbone_slave => wishbone_slave - ) - port map ( - clk => clk, - adr => adr, - dat_i => dat_i, - dat_o => dat_o, - sel => sel, - cyc => cyc, - stb => stb, - we => we, - stall => stall, - ack => ack - ); - - clk <= not clk after 5 ns; - -end architecture; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +-- Author Slawomir Siluk slaweksiluk@gazeta.pl + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +context work.vunit_context; +context work.com_context; +use work.memory_pkg.all; +use work.wishbone_pkg.all; + +entity tb_wishbone_slave is + generic ( + runner_cfg : string; + encoded_tb_cfg : string + ); +end entity; + +architecture a of tb_wishbone_slave is + + type tb_cfg_t is record + dat_width : positive; + adr_width : positive; + num_cycles : positive; + ack_prob : real; + stall_prob : real; + end record tb_cfg_t; + + impure function decode(encoded_tb_cfg : string) return tb_cfg_t is + begin + return (dat_width => positive'value(get(encoded_tb_cfg, "dat_width")), + adr_width => positive'value(get(encoded_tb_cfg, "adr_width")), + num_cycles => positive'value(get(encoded_tb_cfg, "num_cycles")), + ack_prob => real'value(get(encoded_tb_cfg, "ack_prob")), + stall_prob => real'value(get(encoded_tb_cfg, "stall_prob"))); + end function decode; + + constant tb_cfg : tb_cfg_t := decode(encoded_tb_cfg); + + signal clk : std_logic := '0'; + signal adr : std_logic_vector(tb_cfg.adr_width-1 downto 0) := (others => '0'); + signal dat_i : std_logic_vector(tb_cfg.dat_width-1 downto 0) := (others => '0'); + signal dat_o : std_logic_vector(tb_cfg.dat_width-1 downto 0) := (others => '0'); + signal sel : std_logic_vector(tb_cfg.dat_width/8 -1 downto 0) := (others => '1'); + signal cyc : std_logic := '0'; + signal stb : std_logic := '0'; + signal we : std_logic := '0'; + signal stall : std_logic := '0'; + signal ack : std_logic := '0'; + + + constant tb_logger : logger_t := get_logger("tb"); + + signal wr_ack_cnt : natural range 0 to tb_cfg.num_cycles; + signal rd_ack_cnt : natural range 0 to tb_cfg.num_cycles; + + constant memory : memory_t := new_memory; + constant buf : buffer_t := allocate(memory, tb_cfg.num_cycles * sel'length); + constant wishbone_slave : wishbone_slave_t := + new_wishbone_slave(memory => memory, + ack_high_probability => tb_cfg.ack_prob, + stall_high_probability => tb_cfg.stall_prob + ); +begin + + main_stim : process + variable tmp : std_logic_vector(dat_i'range); + variable value : std_logic_vector(dat_i'range) := (others => '1'); + begin + test_runner_setup(runner, runner_cfg); + set_format(display_handler, verbose, true); + show(tb_logger, display_handler, verbose); + show(default_logger, display_handler, verbose); + show(com_logger, display_handler, verbose); + wait until rising_edge(clk); + + + if run("wr block rd block") then + info(tb_logger, "Writing..."); + for i in 0 to tb_cfg.num_cycles-1 loop + cyc <= '1'; + stb <= '1'; + we <= '1'; + adr <= std_logic_vector(to_unsigned(i*(sel'length), adr'length)); + dat_i <= std_logic_vector(to_unsigned(i, dat_i'length)); + wait until rising_edge(clk) and stall = '0'; + end loop; + stb <= '0'; + wait until wr_ack_cnt = tb_cfg.num_cycles; + cyc <= '0'; + + wait until rising_edge(clk); + + info(tb_logger, "Reading..."); + for i in 0 to tb_cfg.num_cycles-1 loop + cyc <= '1'; + stb <= '1'; + we <= '0'; + adr <= std_logic_vector(to_unsigned(i*(sel'length), adr'length)); + wait until rising_edge(clk) and stall = '0'; + end loop; + stb <= '0'; + wait until rising_edge(clk) and rd_ack_cnt = tb_cfg.num_cycles-1; + cyc <= '0'; + end if; + + wait for 50 ns; + test_runner_cleanup(runner); + wait; + end process; + test_runner_watchdog(runner, 100 us); + + wr_ack: process + begin + wait until rising_edge(clk) and ack = '1' and we = '1'; + wr_ack_cnt <= wr_ack_cnt +1; + end process; + + rd_ack: process + begin + wait until rising_edge(clk) and ack = '1' and we = '0'; + check_equal(dat_o, std_logic_vector(to_unsigned(rd_ack_cnt, + dat_o'length)), "dat_o"); + rd_ack_cnt <= rd_ack_cnt +1; + end process; + + dut_slave : entity work.wishbone_slave + generic map ( + wishbone_slave => wishbone_slave + ) + port map ( + clk => clk, + adr => adr, + dat_i => dat_i, + dat_o => dat_o, + sel => sel, + cyc => cyc, + stb => stb, + we => we, + stall => stall, + ack => ack + ); + + clk <= not clk after 5 ns; + +end architecture; diff --git a/vunit/vhdl/vunit_context.vhd b/vunit/vhdl/vunit_context.vhd index 9abb94068..63b70516a 100644 --- a/vunit/vhdl/vunit_context.vhd +++ b/vunit/vhdl/vunit_context.vhd @@ -1,26 +1,26 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -context vunit_context is - library vunit_lib; - context vunit_lib.data_types_context; - - use vunit_lib.string_ops.all; - use vunit_lib.dictionary.all; - use vunit_lib.path.all; - use vunit_lib.print_pkg.all; - use vunit_lib.log_levels_pkg.all; - use vunit_lib.logger_pkg.all; - use vunit_lib.log_handler_pkg.all; - use vunit_lib.log_deprecated_pkg.all; - use vunit_lib.ansi_pkg.all; - use vunit_lib.checker_pkg.all; - use vunit_lib.check_pkg.all; - use vunit_lib.check_deprecated_pkg.all; - use vunit_lib.run_types_pkg.all; - use vunit_lib.run_pkg.all; - use vunit_lib.run_deprecated_pkg.all; -end context; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +context vunit_context is + library vunit_lib; + context vunit_lib.data_types_context; + + use vunit_lib.string_ops.all; + use vunit_lib.dictionary.all; + use vunit_lib.path.all; + use vunit_lib.print_pkg.all; + use vunit_lib.log_levels_pkg.all; + use vunit_lib.logger_pkg.all; + use vunit_lib.log_handler_pkg.all; + use vunit_lib.log_deprecated_pkg.all; + use vunit_lib.ansi_pkg.all; + use vunit_lib.checker_pkg.all; + use vunit_lib.check_pkg.all; + use vunit_lib.check_deprecated_pkg.all; + use vunit_lib.run_types_pkg.all; + use vunit_lib.run_pkg.all; + use vunit_lib.run_deprecated_pkg.all; +end context; diff --git a/vunit/vhdl/vunit_run_context.vhd b/vunit/vhdl/vunit_run_context.vhd index 2f6d0d94e..fee4c7686 100644 --- a/vunit/vhdl/vunit_run_context.vhd +++ b/vunit/vhdl/vunit_run_context.vhd @@ -1,15 +1,15 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -context vunit_run_context is - library vunit_lib; - use vunit_lib.dictionary.all; - use vunit_lib.path.all; - use vunit_lib.checker_pkg.checker_stat_t; - use vunit_lib.run_types_pkg.all; - use vunit_lib.run_pkg.all; - use vunit_lib.run_deprecated_pkg.all; -end context; +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +context vunit_run_context is + library vunit_lib; + use vunit_lib.dictionary.all; + use vunit_lib.path.all; + use vunit_lib.checker_pkg.checker_stat_t; + use vunit_lib.run_types_pkg.all; + use vunit_lib.run_pkg.all; + use vunit_lib.run_deprecated_pkg.all; +end context; diff --git a/vunit/vhdl_parser.py b/vunit/vhdl_parser.py index 7d002d728..d3a1f637f 100644 --- a/vunit/vhdl_parser.py +++ b/vunit/vhdl_parser.py @@ -1,1007 +1,1007 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -# pylint: disable=too-many-lines - -""" -VHDL parsing functionality -""" - -import re -from os.path import abspath -import logging -from vunit.cached import cached -from vunit.parsing.encodings import HDL_FILE_ENCODING -LOGGER = logging.getLogger(__name__) - - -class VHDLParser(object): - """ - Parse a single VHDL file, caching the result to a database if available - """ - - def __init__(self, database=None): - self._database = database - - def parse(self, file_name): - """ - Parse the VHDL code and return a VHDLDesignFile parse result - parse result is re-used if content hash found in database - """ - file_name = abspath(file_name) - return cached("CachedVHDLParser.parse", - VHDLDesignFile.parse, - file_name, - encoding=HDL_FILE_ENCODING, - database=self._database) - - -class VHDLDesignFile(object): # pylint: disable=too-many-instance-attributes - """ - Contains VHDL objects found within a file - """ - def __init__(self, # pylint: disable=too-many-arguments - entities=None, - packages=None, - package_bodies=None, - architectures=None, - contexts=None, - component_instantiations=None, - configurations=None, - references=None): - self.entities = [] if entities is None else entities - self.packages = [] if packages is None else packages - self.package_bodies = [] if package_bodies is None else package_bodies - self.architectures = [] if architectures is None else architectures - self.contexts = [] if contexts is None else contexts - self.component_instantiations = [] if component_instantiations is None else component_instantiations - self.configurations = [] if configurations is None else configurations - self.references = [] if references is None else references - - @classmethod - def parse(cls, code): - """ - Return a new VHDLDesignFile instance by parsing the code - """ - code = remove_comments(code).lower() - return cls(entities=list(VHDLEntity.find(code)), - architectures=list(VHDLArchitecture.find(code)), - packages=list(VHDLPackage.find(code)), - package_bodies=list(VHDLPackageBody.find(code)), - contexts=list(VHDLContext.find(code)), - component_instantiations=list(cls._find_component_instantiations(code)), - configurations=list(VHDLConfiguration.find(code)), - references=list(VHDLReference.find(code))) - - _component_re = re.compile( - r"[a-zA-Z]\w*\s*\:\s*(?:component)?\s*(?:(?:[a-zA-Z]\w*)\.)?([a-zA-Z]\w*)\s*" - r"(?:generic|port) map\s*\([\s\w\=\>\,\.\)\(\+\-\'\"]*\);", - re.IGNORECASE) - - @classmethod - def _find_component_instantiations(cls, code): - """ - Return the component name of all component instantiations found within the code - """ - matches = cls._component_re.findall(code) - return [comp_name for comp_name in matches] - - -class VHDLPackageBody(object): - """ - Representation of a VHDL package body - """ - def __init__(self, identifier): - self.identifier = identifier - - _package_body_pattern = re.compile(r""" - \b # Word boundary - package # package keyword - \s+ # At least one whitespace - body # body keyword - \s+ # At least one whitespace - (?P[a-zA-Z][\w]*) # A package - \s+ # At least one whitespace - is # is keyword - """, re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) - - @classmethod - def find(cls, code): - """ - Iterate over new instances of VHDLPackageBody for all package bodies within the code - """ - matches = cls._package_body_pattern.finditer(code) - for match in matches: - yield VHDLPackageBody(match.group('package')) - - -class VHDLConfiguration(object): - """ - A configuratio declaration - """ - def __init__(self, identifier, entity): - self.identifier = identifier - self.entity = entity - - _configuration_re = re.compile(r""" - \b # Word boundary - configuration # configuration keyword - \s+ # At least one whitespace - (?P[a-zA-Z][\w]*) # An identifier - \s+ # At least one whitespace - of # of keyword - \s+ # At least one whitespace - (?P[a-zA-Z][\w]*) # An identifier - \s+ # At least one whitespace - is # is keyword - """, re.MULTILINE | re.IGNORECASE | re.VERBOSE) - - @classmethod - def find(cls, code): - """ - Return tuple if library_name, unit_name for all entity instantiations found in the code - """ - matches = cls._configuration_re.finditer(code) - return [cls(match.group('id'), match.group('entity_id')) for match in matches] - - -class VHDLArchitecture(object): - """ - Representation of a VHDL architecture - """ - def __init__(self, identifier, entity): - self.identifier = identifier - self.entity = entity - - _architecture_re = re.compile(r""" - \b # Word boundary - architecture # architecture keyword - \s+ # At least one whitespace - (?P[a-zA-Z][\w]*) # An identifier - \s+ # At least one whitespace - of # of keyword - \s+ # At least one whitespace - (?P[a-zA-Z][\w]*) # An identifier - \s+ # At least one whitespace - is # is keyword - """, re.MULTILINE | re.IGNORECASE | re.VERBOSE) - - @classmethod - def find(cls, code): - """ - Iterate over new instances of VHDLArchitecture for all architectures within the code - """ - for arch in cls._architecture_re.finditer(code): - identifier = arch.group('id') - entity_id = arch.group('entity_id') - yield VHDLArchitecture(identifier, entity_id) - - -PACKAGE_INSTANCE_PATTERN = ( - r'\bpackage\s+(?P[a-zA-Z]\w*)\s+is\s+new\s+(?P[a-zA-Z]\w*)\.(?P[a-zA-Z]\w*)') - - -class VHDLPackage(object): - """ - Representation of a VHDL package - """ - def __init__(self, identifier, - enumeration_types, record_types, array_types): - self.identifier = identifier - self.enumeration_types = enumeration_types - self.record_types = record_types - self.array_types = array_types - - _package_start_re = re.compile(r""" - \b # Word boundary - package # package keyword - \s+ # At least one whitespace - (?P[a-zA-Z][\w]*) # An identifier - \s+ # At least one whitespace - is # is keyword - """, re.MULTILINE | re.IGNORECASE | re.VERBOSE) - - @classmethod - def _find_normal_packages(cls, code): - """ - Iterate over new instances of VHDLPackage for all packages within the code - """ - for package in cls._package_start_re.finditer(code): - identifier = package.group('id') - package_end = re.compile(r""" - \b # Word boundary - end # end keyword - (\s+package)? # Optional package keyword - (\s+""" + identifier + r""")? # Optional identifier - [\s]* # Potential whitespaces - ; # Semicolon - """, re.MULTILINE | re.IGNORECASE | re.VERBOSE) - sub_code = code[package.start():] - match = package_end.search(sub_code) - if match: - yield cls.parse(sub_code[:match.end()]) - - _package_instance_re = re.compile("^" + PACKAGE_INSTANCE_PATTERN, - re.MULTILINE | re.IGNORECASE) - - @classmethod - def _find_package_instances(cls, code): - """ - Find global package instances. - Use indentation heuristic to filter out nested package instances. - """ - references = [] - for match in cls._package_instance_re.finditer(code): - references.append(cls(match.group("new_name"), [], [], [])) - return references - - @classmethod - def find(cls, code): - """ - Find normal and global generic package instances - """ - result = list(cls._find_normal_packages(code)) - result += list(cls._find_package_instances(code)) - return result - - @classmethod - def parse(cls, code): - """ - Return a new VHDLPackage instance for a single package found within the code - """ - # Extract identifier - identifier = cls._package_start_re.match(code).group('id') - enumeration_types = [e for e in VHDLEnumerationType.find(code)] - record_types = [r for r in VHDLRecordType.find(code)] - array_types = [a for a in VHDLArrayType.find(code)] - - return cls(identifier, enumeration_types, record_types, array_types) - - -class VHDLEntity(object): - """ - Represents a VHDL Entity - """ - def __init__(self, identifier, generics=None, ports=None): - self.identifier = identifier - - if generics is not None: - self.generics = generics - else: - self.generics = [] - - if ports is not None: - self.ports = ports - else: - self.ports = [] - - def add_generic(self, identifier, subtype_code, init_value=None): - """ - Add a generic to this entity - """ - self.generics.append(VHDLInterfaceElement(identifier, - VHDLSubtypeIndication.parse(subtype_code), - init_value=init_value)) - - def add_port(self, identifier, mode, subtype_code, init_value=None): - """ - Add a port to this entity - """ - self.ports.append(VHDLInterfaceElement(identifier, - VHDLSubtypeIndication.parse(subtype_code), - init_value=init_value, - mode=mode)) - - _entity_start_re = re.compile(r""" - \b # Word boundary - entity # entity keyword - \s+ # At least one whitespace - (?P[a-zA-Z][\w]*) # An identifier - \s+ # At least one whitespace - is # is keyword - """, re.MULTILINE | re.IGNORECASE | re.VERBOSE) - - @classmethod - def find(cls, code): - """ - Iterates over new instances of VHDLEntity for all entities within the code - """ - for entity in cls._entity_start_re.finditer(code): - identifier = entity.group('id') - sub_code = code[entity.start():] - entity_end_re = re.compile(r""" - \b # Word boundary - end # end keyword - [\s]* # Potential whitespaces - (entity)? # Optional entity keyword - [\s]* # Potential whitespaces - (""" + identifier + r""")? # Optional identifier - [\s]* # Potential whitespaces - ; # Semicolon - """, re.MULTILINE | re.IGNORECASE | re.VERBOSE) - - match = entity_end_re.search(sub_code) - if match: - yield VHDLEntity.parse(sub_code[:match.end()]) - - @classmethod - def parse(cls, code): - """ - Create a new instance by parsing the code - """ - # Extract identifier - re_flags = re.MULTILINE | re.IGNORECASE | re.VERBOSE - entity_start = re.compile(r""" - \b # Word boundary - entity # entity keyword - \s+ # At least one whitespace - (?P[a-zA-Z][\w]*) # An identifier - \s+ # At least one whitespace - is # is keyword - """, re_flags) - identifier = entity_start.match(code).group('id') - # Find generics and ports - generics = cls._find_generic_clause(code) - ports = cls._find_port_clause(code) - - return cls(identifier, generics, ports) - - @classmethod - def _find_generic_clause(cls, code): - """ - Find and return the generic clause code contents - """ - re_flags = re.MULTILINE | re.IGNORECASE | re.VERBOSE - generic_clause_start = re.compile(r""" - \b # Word boundary - generic # generic keyword - [\s]* # Potential whitespaces - \( # Opening parenthesis - """, re_flags) - match = generic_clause_start.search(code) - if match: - closing_pos = find_closing_delimiter('\\(', '\\)', code[match.end():]) - semicolon = re.compile(r""" - [\s]* # Potential whitespaces - ; # Semicolon - """, re_flags) - match_semicolon = semicolon.match(code[match.end() + closing_pos:]) - if match_semicolon: - return cls._parse_generic_clause( - code[match.start(): match.end() + closing_pos + match_semicolon.end()]) - return [] - - @classmethod - def _find_port_clause(cls, code): - """ - Find and return the port clause code contents - """ - re_flags = re.MULTILINE | re.IGNORECASE | re.VERBOSE - port_clause_start = re.compile(r""" - ^ # Beginning of line - [\s]* # Potential whitespaces - port # port keyword - [\s]* # Potential whitespaces - \( # Opening parenthesis - """, re_flags) - match = port_clause_start.search(code) - if match: - closing_pos = find_closing_delimiter('\\(', '\\)', code[match.end():]) - semicolon = re.compile(r""" - [\s]* # Potential whitespaces - ; # Semicolon - """, re_flags) - match_semicolon = semicolon.match(code[match.end() + closing_pos:]) - if match_semicolon: - return cls._parse_port_clause( - code[match.start(): match.end() + closing_pos + match_semicolon.end()]) - return [] - - @staticmethod - def _split_not_in_par(string, sep): - """ - Split string at all occurences of sep but not inside of a parenthesis or quoute - """ - result = [] - count = 0 - split = [] - quouted = False - escaped = False - - for idx, char in enumerate(string): - if idx + 1 < len(string): - next_char = string[idx + 1] - else: - next_char = None - - if char == '"' and not escaped: - if next_char == '"': - escaped = True - else: - quouted = not quouted - else: - escaped = False - - if char in '(': - count += 1 - elif char in ')': - count -= 1 - - if char == sep and count == 0 and not quouted: - result.append("".join(split)) - split = [] - else: - split.append(char) - - if split: - result.append("".join(split)) - return result - - _package_generic_re = re.compile(r"\s*package\s+", re.MULTILINE | re.IGNORECASE) - _type_generic_re = re.compile(r"\s*type\s+", re.MULTILINE | re.IGNORECASE) - _function_generic_re = re.compile(r"\s*(impure\s+)?(function|procedure)\s+", re.MULTILINE | re.IGNORECASE) - - @classmethod - def _parse_generic_clause(cls, code): - """ - Parse the generic clause and return a list of interface elements - """ - # The generic list is between the outer parenthesis - generic_list_string = code[code.find('(') + 1: code.rfind(')')] - - # Split the interface elements - interface_elements = cls._split_not_in_par(generic_list_string, ';') - - generic_list = [] - # Add interface elements to the generic list - for interface_element in interface_elements: - - if cls._package_generic_re.match(interface_element) is not None: - # Ignore package generics - continue - - if cls._type_generic_re.match(interface_element) is not None: - # Ignore type generics - continue - - if cls._function_generic_re.match(interface_element) is not None: - # Ignore function generics - continue - - generic_list.append(VHDLInterfaceElement.parse(interface_element)) - - return generic_list - - @classmethod - def _parse_port_clause(cls, code): - """ - Parse the port clause and return a list of interface elements - """ - # The port list is between the outer parenthesis - port_list_string = code[code.find('(') + 1: code.rfind(')')] - - # Split the interface elements - interface_elements = port_list_string.split(';') - - port_list = [] - # Add interface elements to the port list - for interface_element in interface_elements: - port_list.append(VHDLInterfaceElement.parse(interface_element, is_signal=True)) - - return port_list - - -class VHDLContext(object): - """ - Represents a VHDL 2008 context - """ - def __init__(self, identifier): - self.identifier = identifier - - _context_start_re = re.compile(r""" - \b # Word boundary - context # context keyword - \s+ # At least one whitespace - (?P[a-zA-Z][\w]*) # An identifier - \s+ # At least one whitespace - is # is keyword - """, re.MULTILINE | re.IGNORECASE | re.VERBOSE) - - @classmethod - def find(cls, code): - """ - Iterate over new instances of VHDLContext for a contexts found in the code - """ - for context in cls._context_start_re.finditer(code): - identifier = context.group('id') - yield VHDLContext(identifier=identifier) - - -class VHDLSubtypeIndication(object): - """ - Represents a VHDL subtype indication - """ - def __init__(self, code, type_mark, constraint, array_type): - self.code = code - self.type_mark = type_mark - self.constraint = constraint - self.array_type = array_type - - @classmethod - def parse(cls, code): - """ - Returns a new instance from parsing the code - """ - # Extract type mark and find out if it's an array type and if a constraint is given. - re_flags = re.MULTILINE | re.IGNORECASE | re.VERBOSE - subtype_indication_start = re.compile(r""" - ^ # Beginning of line - [\s]* # Potential whitespaces - (?P[a-zA-Z][\w]*) # An type mark - [\s]* # Potential whitespaces - (?P\(.*\))? - """, re_flags) - subtype_indication_declaration = subtype_indication_start.match(code) - type_mark = subtype_indication_declaration.group('type_mark') - constraint = subtype_indication_declaration.group('constraint') - - array_type = type_mark == 'std_logic_vector' - return cls(code, type_mark, constraint, array_type) - - def __str__(self): - return self.code - - -class VHDLInterfaceElement(object): - """ - Represents a VHDL interface element - """ - def __init__(self, identifier, subtype_indication, mode=None, init_value=None): - self.identifier = identifier - self.mode = mode - self.subtype_indication = subtype_indication - self.init_value = init_value - - def without_mode(self): - """ - @returns A copy of this interface element without a mode - """ - return VHDLInterfaceElement(self.identifier, - self.subtype_indication, - init_value=self.init_value) - - @classmethod - def parse(cls, code, is_signal=False): - """ - Returns a new instance by parsing the code - """ - if is_signal: - # Remove 'signal' string if a signal is beeing parsed - code = code.replace("signal", "") - - interface_element_string = code - - # Extract the identifier - identifier = interface_element_string.split(':')[0].strip() - - # Extract subtype indication and mode (if any) - - mode_split = interface_element_string.split(':')[1].strip().split(None, 1) - if cls._is_mode(mode_split[0]): - mode = mode_split[0] - subtype_indication = VHDLSubtypeIndication.parse(mode_split[1]) - else: - mode = None - subtype_indication = VHDLSubtypeIndication.parse(interface_element_string.split(':')[1].strip()) - - # Extract initial value - init_value_split = interface_element_string.split(':=') - if len(init_value_split) > 1: - init_value = init_value_split[1].strip() - else: - init_value = None - - return cls(identifier, subtype_indication, mode, init_value) - - @staticmethod - def _is_mode(code): - """ - Return True if the code is a mode keyword - """ - return code in ('in', 'out', 'inout', 'buffer', 'linkage') - - def __str__(self): - code = self.identifier + " : " - - if self.mode is not None: - code += self.mode + " " - - code += str(self.subtype_indication) - - if self.init_value is not None: - code += " := " + self.init_value - - return code - - -class VHDLEnumerationType(object): - """Represents a VHDL enumeration type""" - def __init__(self, identifier, literals): - self.identifier = identifier - self.literals = literals - - _enum_declaration_re = re.compile(r""" - \b # Word boundary - type - \s+ - (?P[a-zA-Z][\w]*) # An identifier - \s+ - is - \s*\(\s* - (?P[a-zA-Z][\w]* # First enumeration literal - (\s*,\s*[a-zA-Z][\w]*)*) # More enumeration literals - \s*\)\s*;""", re.MULTILINE | re.IGNORECASE | re.VERBOSE) - - @classmethod - def find(cls, code): - """ - Find enumeration types in the code - """ - for enum_type in cls._enum_declaration_re.finditer(code): - identifier = enum_type.group('id') - literals = [e.strip() for e in enum_type.group('literals').split(',')] - yield cls(identifier, literals) - - -class VHDLElementDeclaration(object): - """Represents a VHDL element declaration""" - def __init__(self, identifier_list, subtype_indication): - self.identifier_list = identifier_list - self.subtype_indication = subtype_indication - - -class VHDLRecordType(object): - """Represents a VHDL record type""" - def __init__(self, identifier, elements): - self.identifier = identifier - self.elements = elements - - _record_declaration_re = re.compile(r""" - \b # Word boundary - type - \s+ - (?P[a-zA-Z][\w]*) # An identifier - \s+ - is - \s+ - record - (?P.*?)end\s+record""", re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) - - @classmethod - def find(cls, code): - """ - Find all record types in the code - """ - for record_type in cls._record_declaration_re.finditer(code): - identifier = record_type.group('id') - elements = record_type.group('elements').split(';') - parsed_elements = [] - for element in elements: - if ':' in element: - identifier_list_and_subtype_indication = element.split(':') - identifier_list = [i.strip() for i in identifier_list_and_subtype_indication[0].split(',')] - subtype_indication = VHDLSubtypeIndication.parse(identifier_list_and_subtype_indication[1].strip()) - parsed_elements.append(VHDLElementDeclaration(identifier_list, subtype_indication)) - yield cls(identifier, parsed_elements) - - -class VHDLRange(object): - """Represents a VHDL Range""" - def __init__(self, range_type=None, left=None, right=None, attribute=None): - self.range_type = range_type - self.left = left - self.right = right - self.attribute = attribute - - -class VHDLArrayType(object): - """Represents a VHDL array type""" - def __init__(self, identifier, subtype_indication, range1, range2): - self.identifier = identifier - self.subtype_indication = subtype_indication - self.range1 = range1 - self.range2 = range2 - - _constrained_ranges_re = re.compile(r""" - \s*(?P.+?) - \s+(to|downto)\s+ - (?P.+?)\s* - (, - \s*(?P.+?) - \s+(to|downto)\s+ - (?P.+?)\s*)?""", re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) - - _range_attribute_ranges_re = re.compile(r""" - \s*(?P[a-zA-Z][\w]*'range)\s*""", re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) - - _unconstrained_ranges_re = re.compile(r""" - \s*(?P[a-zA-Z][\w]*) - \s+range\s+<>\s* - (, - \s*(?P[a-zA-Z][\w]*) - \s+range\s+<>\s*)?""", re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) - - _constrained_range_re = re.compile(r""" - \s*(?P.+?) - \s+(to|downto)\s+ - (?P.+?)\s*""", re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) - - _range_attribute_range_re = re.compile(r""" - \s*(?P[a-zA-Z][\w]*'range)\s*""", re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) - - _unconstrained_range_re = re.compile(r""" - \s*(?P[a-zA-Z][\w]*) - \s+range\s+<>\s*""", re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) - - _array_declaration_re = re.compile(r""" - \b # Word boundary - type - \s+ - (?P[a-zA-Z][\w]*) - \s+ - is - \s+ - array - \s*\( - (?P.*?) - \)\s+of\s+ - (?P.*?)\s*;""", re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) - - @classmethod - def find(cls, code): - """Iterate over new instances of VHDLArrayType for all array types within the code""" - for array_type in cls._array_declaration_re.finditer(code): - identifier = array_type.group('id') - subtype_indication = VHDLSubtypeIndication.parse(array_type.group('subtype_indication')) - ranges = array_type.group('ranges') - range1_str, range2_str = cls._split_ranges(ranges) - range1 = cls._parse_range(range1_str) - range2 = cls._parse_range(range2_str) - - yield cls(identifier, subtype_indication, range1, range2) - - @staticmethod - def _split_ranges(ranges): - """Splits 2D ranges in two. 1D ranges will return None as the second range""" - level = 0 - index = 0 - if ',' in ranges: - for char in ranges: - if char == ',' and level == 0: - return ranges[:index], ranges[index + 1:] - - if char == '(': - level += 1 - elif char == ')': - level -= 1 - index += 1 - - return ranges, None - - @classmethod - def _parse_range(cls, the_range): - """Extracts range type, left and right boundary as well as the range when the 'range attribute - is used""" - if the_range is None: - return VHDLRange() - - unconstrained_range = cls._unconstrained_range_re.match(the_range) - if unconstrained_range is not None: - range_type = unconstrained_range.group('range_type') - return VHDLRange(range_type) - - constrained_range = cls._constrained_range_re.match(the_range) - range_attribute = cls._range_attribute_range_re.match(the_range) - if constrained_range is not None: - range_left = constrained_range.group('range_left') - range_right = constrained_range.group('range_right') - return VHDLRange(None, range_left, range_right) - - if range_attribute is not None: - range_attribute = range_attribute.group('range_attribute') - return VHDLRange(attribute=range_attribute) - - return VHDLRange() - - -def find_closing_delimiter(start, end, code): - """ - Find the balanced closing position within the code. - - The balanced closing position is defined as the first position of an end marker - where the number of previous start and end markers are equal - """ - delimiter_pattern = start + '|' + end - start = start.replace('\\', '') - end = end.replace('\\', '') - delimiters = re.compile(delimiter_pattern) - count = 1 - for delimiter in delimiters.finditer(code): - if delimiter.group() == start: - count += 1 - else: - count -= 1 - - if count == 0: - return delimiter.end() - raise ValueError('Failed to find closing delimiter to ' + start + ' in ' + code + '.') - - -class VHDLReference(object): - """ - Reference to design unit - """ - _reference_types = ("package", - "context", - "entity", - "configuration") - - _uses_re = re.compile(r""" - \b # Word boundary - (?Puse|context) # use or context keyword - \s+ # At least one whitespace - (?P[a-zA-Z][\w]*(\.[a-zA-Z][\w]*){1,2}) - (?P(\s*,\s*[a-zA-Z][\w]*(\.[a-zA-Z][\w]*){1,2})*) - \s* # Potential whitespaces - ; # Semi-colon - """, re.MULTILINE | re.IGNORECASE | re.VERBOSE) - - @classmethod - def _find_uses(cls, code): - """ - Find all the libraries and use clasues within the code - """ - - def get_ids(match): - """ - Get all ids found within the match taking the optinal extra ids of - library and use clauses into account such as: - - use foo, bar; - - or - - library foo, bar; - """ - ids = [match.group('id').strip()] - if match.group('extra'): - ids += [name.strip() for name in match.group('extra').split(',')[1:]] - return ids - - references = [] - for match in cls._uses_re.finditer(code): - for uses in get_ids(match): - uses = uses.split(".") - - names_within = uses[2:] if len(uses) > 2 else (None,) - for name_within in names_within: - ref = cls(reference_type="package" if match.group("use_type") == "use" else "context", - library=uses[0], - design_unit=uses[1], - name_within=name_within) - - references.append(ref) - return references - - _entity_reference_re = re.compile( - r'\bentity\s+(?P[a-zA-Z]\w*)\.(?P[a-zA-Z]\w*)\s*(\((?P[a-zA-Z]\w*)\))?', - re.MULTILINE | re.IGNORECASE) - - @classmethod - def _find_entity_references(cls, code): - """ - Find all entity references from instantiations or block configurations - """ - references = [] - for match in cls._entity_reference_re.finditer(code): - if match.group("arch") is None: - references.append(cls('entity', match.group("lib"), match.group("ent"))) - else: - references.append(cls('entity', match.group("lib"), match.group("ent"), match.group("arch"))) - return references - - _configuration_reference_re = re.compile( - r'\bconfiguration\s+(?P[a-zA-Z]\w*)\.(?P[a-zA-Z]\w*)', - re.MULTILINE | re.IGNORECASE) - - @classmethod - def _find_configuration_references(cls, code): - """ - Find all configuration references within block configurations - """ - references = [] - for match in cls._configuration_reference_re.finditer(code): - references.append(cls('configuration', match.group("lib"), match.group("cfg"))) - return references - - _package_instance_re = re.compile(PACKAGE_INSTANCE_PATTERN, - re.MULTILINE | re.IGNORECASE) - - @classmethod - def _find_package_instance_references(cls, code): - """ - Finds all reference causes by package instantiation - """ - references = [] - for match in cls._package_instance_re.finditer(code): - references.append(cls('package', match.group("lib"), match.group("name"))) - return references - - @classmethod - def find(cls, code): - """ - Find entity, use, context and configuration references within the code - """ - return (cls._find_uses(code) - + cls._find_entity_references(code) - + cls._find_configuration_references(code) - + cls._find_package_instance_references(code)) - - def __init__(self, reference_type, library, design_unit, name_within=None): - assert reference_type in self._reference_types - self.reference_type = reference_type - self.library = library - self.design_unit = design_unit - - # String "all" may be used to denote all names within - self.name_within = name_within - - def __repr__(self): - return "VHDLReference(%r, %r, %r, %r)" % ( - self.reference_type, - self.library, - self.design_unit, - self.name_within) - - def __eq__(self, other): - return (self.reference_type == other.reference_type - and self.library == other.library - and self.design_unit == other.design_unit - and self.name_within == other.name_within) - - def copy(self): - return VHDLReference(self.reference_type, - self.library, - self.design_unit, - self.name_within) - - def is_entity_reference(self): - return self.reference_type == 'entity' - - def is_package_reference(self): - return self.reference_type == 'package' - - def reference_all_names_within(self): - return self.name_within == "all" - - -VHDL_REMOVE_COMMENT_RE = re.compile(r'--[^\n]*') - - -def _comment_repl(match): - """ - Replace comment with equal amount of whitespace to make - lexical position unaffected - """ - text = match.group(0) - return " " * len(text) - - -def remove_comments(code): - """ - Return the code with comments removed - """ - return VHDL_REMOVE_COMMENT_RE.sub(_comment_repl, code) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +# pylint: disable=too-many-lines + +""" +VHDL parsing functionality +""" + +import re +from os.path import abspath +import logging +from vunit.cached import cached +from vunit.parsing.encodings import HDL_FILE_ENCODING +LOGGER = logging.getLogger(__name__) + + +class VHDLParser(object): + """ + Parse a single VHDL file, caching the result to a database if available + """ + + def __init__(self, database=None): + self._database = database + + def parse(self, file_name): + """ + Parse the VHDL code and return a VHDLDesignFile parse result + parse result is re-used if content hash found in database + """ + file_name = abspath(file_name) + return cached("CachedVHDLParser.parse", + VHDLDesignFile.parse, + file_name, + encoding=HDL_FILE_ENCODING, + database=self._database) + + +class VHDLDesignFile(object): # pylint: disable=too-many-instance-attributes + """ + Contains VHDL objects found within a file + """ + def __init__(self, # pylint: disable=too-many-arguments + entities=None, + packages=None, + package_bodies=None, + architectures=None, + contexts=None, + component_instantiations=None, + configurations=None, + references=None): + self.entities = [] if entities is None else entities + self.packages = [] if packages is None else packages + self.package_bodies = [] if package_bodies is None else package_bodies + self.architectures = [] if architectures is None else architectures + self.contexts = [] if contexts is None else contexts + self.component_instantiations = [] if component_instantiations is None else component_instantiations + self.configurations = [] if configurations is None else configurations + self.references = [] if references is None else references + + @classmethod + def parse(cls, code): + """ + Return a new VHDLDesignFile instance by parsing the code + """ + code = remove_comments(code).lower() + return cls(entities=list(VHDLEntity.find(code)), + architectures=list(VHDLArchitecture.find(code)), + packages=list(VHDLPackage.find(code)), + package_bodies=list(VHDLPackageBody.find(code)), + contexts=list(VHDLContext.find(code)), + component_instantiations=list(cls._find_component_instantiations(code)), + configurations=list(VHDLConfiguration.find(code)), + references=list(VHDLReference.find(code))) + + _component_re = re.compile( + r"[a-zA-Z]\w*\s*\:\s*(?:component)?\s*(?:(?:[a-zA-Z]\w*)\.)?([a-zA-Z]\w*)\s*" + r"(?:generic|port) map\s*\([\s\w\=\>\,\.\)\(\+\-\'\"]*\);", + re.IGNORECASE) + + @classmethod + def _find_component_instantiations(cls, code): + """ + Return the component name of all component instantiations found within the code + """ + matches = cls._component_re.findall(code) + return [comp_name for comp_name in matches] + + +class VHDLPackageBody(object): + """ + Representation of a VHDL package body + """ + def __init__(self, identifier): + self.identifier = identifier + + _package_body_pattern = re.compile(r""" + \b # Word boundary + package # package keyword + \s+ # At least one whitespace + body # body keyword + \s+ # At least one whitespace + (?P[a-zA-Z][\w]*) # A package + \s+ # At least one whitespace + is # is keyword + """, re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) + + @classmethod + def find(cls, code): + """ + Iterate over new instances of VHDLPackageBody for all package bodies within the code + """ + matches = cls._package_body_pattern.finditer(code) + for match in matches: + yield VHDLPackageBody(match.group('package')) + + +class VHDLConfiguration(object): + """ + A configuratio declaration + """ + def __init__(self, identifier, entity): + self.identifier = identifier + self.entity = entity + + _configuration_re = re.compile(r""" + \b # Word boundary + configuration # configuration keyword + \s+ # At least one whitespace + (?P[a-zA-Z][\w]*) # An identifier + \s+ # At least one whitespace + of # of keyword + \s+ # At least one whitespace + (?P[a-zA-Z][\w]*) # An identifier + \s+ # At least one whitespace + is # is keyword + """, re.MULTILINE | re.IGNORECASE | re.VERBOSE) + + @classmethod + def find(cls, code): + """ + Return tuple if library_name, unit_name for all entity instantiations found in the code + """ + matches = cls._configuration_re.finditer(code) + return [cls(match.group('id'), match.group('entity_id')) for match in matches] + + +class VHDLArchitecture(object): + """ + Representation of a VHDL architecture + """ + def __init__(self, identifier, entity): + self.identifier = identifier + self.entity = entity + + _architecture_re = re.compile(r""" + \b # Word boundary + architecture # architecture keyword + \s+ # At least one whitespace + (?P[a-zA-Z][\w]*) # An identifier + \s+ # At least one whitespace + of # of keyword + \s+ # At least one whitespace + (?P[a-zA-Z][\w]*) # An identifier + \s+ # At least one whitespace + is # is keyword + """, re.MULTILINE | re.IGNORECASE | re.VERBOSE) + + @classmethod + def find(cls, code): + """ + Iterate over new instances of VHDLArchitecture for all architectures within the code + """ + for arch in cls._architecture_re.finditer(code): + identifier = arch.group('id') + entity_id = arch.group('entity_id') + yield VHDLArchitecture(identifier, entity_id) + + +PACKAGE_INSTANCE_PATTERN = ( + r'\bpackage\s+(?P[a-zA-Z]\w*)\s+is\s+new\s+(?P[a-zA-Z]\w*)\.(?P[a-zA-Z]\w*)') + + +class VHDLPackage(object): + """ + Representation of a VHDL package + """ + def __init__(self, identifier, + enumeration_types, record_types, array_types): + self.identifier = identifier + self.enumeration_types = enumeration_types + self.record_types = record_types + self.array_types = array_types + + _package_start_re = re.compile(r""" + \b # Word boundary + package # package keyword + \s+ # At least one whitespace + (?P[a-zA-Z][\w]*) # An identifier + \s+ # At least one whitespace + is # is keyword + """, re.MULTILINE | re.IGNORECASE | re.VERBOSE) + + @classmethod + def _find_normal_packages(cls, code): + """ + Iterate over new instances of VHDLPackage for all packages within the code + """ + for package in cls._package_start_re.finditer(code): + identifier = package.group('id') + package_end = re.compile(r""" + \b # Word boundary + end # end keyword + (\s+package)? # Optional package keyword + (\s+""" + identifier + r""")? # Optional identifier + [\s]* # Potential whitespaces + ; # Semicolon + """, re.MULTILINE | re.IGNORECASE | re.VERBOSE) + sub_code = code[package.start():] + match = package_end.search(sub_code) + if match: + yield cls.parse(sub_code[:match.end()]) + + _package_instance_re = re.compile("^" + PACKAGE_INSTANCE_PATTERN, + re.MULTILINE | re.IGNORECASE) + + @classmethod + def _find_package_instances(cls, code): + """ + Find global package instances. + Use indentation heuristic to filter out nested package instances. + """ + references = [] + for match in cls._package_instance_re.finditer(code): + references.append(cls(match.group("new_name"), [], [], [])) + return references + + @classmethod + def find(cls, code): + """ + Find normal and global generic package instances + """ + result = list(cls._find_normal_packages(code)) + result += list(cls._find_package_instances(code)) + return result + + @classmethod + def parse(cls, code): + """ + Return a new VHDLPackage instance for a single package found within the code + """ + # Extract identifier + identifier = cls._package_start_re.match(code).group('id') + enumeration_types = [e for e in VHDLEnumerationType.find(code)] + record_types = [r for r in VHDLRecordType.find(code)] + array_types = [a for a in VHDLArrayType.find(code)] + + return cls(identifier, enumeration_types, record_types, array_types) + + +class VHDLEntity(object): + """ + Represents a VHDL Entity + """ + def __init__(self, identifier, generics=None, ports=None): + self.identifier = identifier + + if generics is not None: + self.generics = generics + else: + self.generics = [] + + if ports is not None: + self.ports = ports + else: + self.ports = [] + + def add_generic(self, identifier, subtype_code, init_value=None): + """ + Add a generic to this entity + """ + self.generics.append(VHDLInterfaceElement(identifier, + VHDLSubtypeIndication.parse(subtype_code), + init_value=init_value)) + + def add_port(self, identifier, mode, subtype_code, init_value=None): + """ + Add a port to this entity + """ + self.ports.append(VHDLInterfaceElement(identifier, + VHDLSubtypeIndication.parse(subtype_code), + init_value=init_value, + mode=mode)) + + _entity_start_re = re.compile(r""" + \b # Word boundary + entity # entity keyword + \s+ # At least one whitespace + (?P[a-zA-Z][\w]*) # An identifier + \s+ # At least one whitespace + is # is keyword + """, re.MULTILINE | re.IGNORECASE | re.VERBOSE) + + @classmethod + def find(cls, code): + """ + Iterates over new instances of VHDLEntity for all entities within the code + """ + for entity in cls._entity_start_re.finditer(code): + identifier = entity.group('id') + sub_code = code[entity.start():] + entity_end_re = re.compile(r""" + \b # Word boundary + end # end keyword + [\s]* # Potential whitespaces + (entity)? # Optional entity keyword + [\s]* # Potential whitespaces + (""" + identifier + r""")? # Optional identifier + [\s]* # Potential whitespaces + ; # Semicolon + """, re.MULTILINE | re.IGNORECASE | re.VERBOSE) + + match = entity_end_re.search(sub_code) + if match: + yield VHDLEntity.parse(sub_code[:match.end()]) + + @classmethod + def parse(cls, code): + """ + Create a new instance by parsing the code + """ + # Extract identifier + re_flags = re.MULTILINE | re.IGNORECASE | re.VERBOSE + entity_start = re.compile(r""" + \b # Word boundary + entity # entity keyword + \s+ # At least one whitespace + (?P[a-zA-Z][\w]*) # An identifier + \s+ # At least one whitespace + is # is keyword + """, re_flags) + identifier = entity_start.match(code).group('id') + # Find generics and ports + generics = cls._find_generic_clause(code) + ports = cls._find_port_clause(code) + + return cls(identifier, generics, ports) + + @classmethod + def _find_generic_clause(cls, code): + """ + Find and return the generic clause code contents + """ + re_flags = re.MULTILINE | re.IGNORECASE | re.VERBOSE + generic_clause_start = re.compile(r""" + \b # Word boundary + generic # generic keyword + [\s]* # Potential whitespaces + \( # Opening parenthesis + """, re_flags) + match = generic_clause_start.search(code) + if match: + closing_pos = find_closing_delimiter('\\(', '\\)', code[match.end():]) + semicolon = re.compile(r""" + [\s]* # Potential whitespaces + ; # Semicolon + """, re_flags) + match_semicolon = semicolon.match(code[match.end() + closing_pos:]) + if match_semicolon: + return cls._parse_generic_clause( + code[match.start(): match.end() + closing_pos + match_semicolon.end()]) + return [] + + @classmethod + def _find_port_clause(cls, code): + """ + Find and return the port clause code contents + """ + re_flags = re.MULTILINE | re.IGNORECASE | re.VERBOSE + port_clause_start = re.compile(r""" + ^ # Beginning of line + [\s]* # Potential whitespaces + port # port keyword + [\s]* # Potential whitespaces + \( # Opening parenthesis + """, re_flags) + match = port_clause_start.search(code) + if match: + closing_pos = find_closing_delimiter('\\(', '\\)', code[match.end():]) + semicolon = re.compile(r""" + [\s]* # Potential whitespaces + ; # Semicolon + """, re_flags) + match_semicolon = semicolon.match(code[match.end() + closing_pos:]) + if match_semicolon: + return cls._parse_port_clause( + code[match.start(): match.end() + closing_pos + match_semicolon.end()]) + return [] + + @staticmethod + def _split_not_in_par(string, sep): + """ + Split string at all occurences of sep but not inside of a parenthesis or quoute + """ + result = [] + count = 0 + split = [] + quouted = False + escaped = False + + for idx, char in enumerate(string): + if idx + 1 < len(string): + next_char = string[idx + 1] + else: + next_char = None + + if char == '"' and not escaped: + if next_char == '"': + escaped = True + else: + quouted = not quouted + else: + escaped = False + + if char in '(': + count += 1 + elif char in ')': + count -= 1 + + if char == sep and count == 0 and not quouted: + result.append("".join(split)) + split = [] + else: + split.append(char) + + if split: + result.append("".join(split)) + return result + + _package_generic_re = re.compile(r"\s*package\s+", re.MULTILINE | re.IGNORECASE) + _type_generic_re = re.compile(r"\s*type\s+", re.MULTILINE | re.IGNORECASE) + _function_generic_re = re.compile(r"\s*(impure\s+)?(function|procedure)\s+", re.MULTILINE | re.IGNORECASE) + + @classmethod + def _parse_generic_clause(cls, code): + """ + Parse the generic clause and return a list of interface elements + """ + # The generic list is between the outer parenthesis + generic_list_string = code[code.find('(') + 1: code.rfind(')')] + + # Split the interface elements + interface_elements = cls._split_not_in_par(generic_list_string, ';') + + generic_list = [] + # Add interface elements to the generic list + for interface_element in interface_elements: + + if cls._package_generic_re.match(interface_element) is not None: + # Ignore package generics + continue + + if cls._type_generic_re.match(interface_element) is not None: + # Ignore type generics + continue + + if cls._function_generic_re.match(interface_element) is not None: + # Ignore function generics + continue + + generic_list.append(VHDLInterfaceElement.parse(interface_element)) + + return generic_list + + @classmethod + def _parse_port_clause(cls, code): + """ + Parse the port clause and return a list of interface elements + """ + # The port list is between the outer parenthesis + port_list_string = code[code.find('(') + 1: code.rfind(')')] + + # Split the interface elements + interface_elements = port_list_string.split(';') + + port_list = [] + # Add interface elements to the port list + for interface_element in interface_elements: + port_list.append(VHDLInterfaceElement.parse(interface_element, is_signal=True)) + + return port_list + + +class VHDLContext(object): + """ + Represents a VHDL 2008 context + """ + def __init__(self, identifier): + self.identifier = identifier + + _context_start_re = re.compile(r""" + \b # Word boundary + context # context keyword + \s+ # At least one whitespace + (?P[a-zA-Z][\w]*) # An identifier + \s+ # At least one whitespace + is # is keyword + """, re.MULTILINE | re.IGNORECASE | re.VERBOSE) + + @classmethod + def find(cls, code): + """ + Iterate over new instances of VHDLContext for a contexts found in the code + """ + for context in cls._context_start_re.finditer(code): + identifier = context.group('id') + yield VHDLContext(identifier=identifier) + + +class VHDLSubtypeIndication(object): + """ + Represents a VHDL subtype indication + """ + def __init__(self, code, type_mark, constraint, array_type): + self.code = code + self.type_mark = type_mark + self.constraint = constraint + self.array_type = array_type + + @classmethod + def parse(cls, code): + """ + Returns a new instance from parsing the code + """ + # Extract type mark and find out if it's an array type and if a constraint is given. + re_flags = re.MULTILINE | re.IGNORECASE | re.VERBOSE + subtype_indication_start = re.compile(r""" + ^ # Beginning of line + [\s]* # Potential whitespaces + (?P[a-zA-Z][\w]*) # An type mark + [\s]* # Potential whitespaces + (?P\(.*\))? + """, re_flags) + subtype_indication_declaration = subtype_indication_start.match(code) + type_mark = subtype_indication_declaration.group('type_mark') + constraint = subtype_indication_declaration.group('constraint') + + array_type = type_mark == 'std_logic_vector' + return cls(code, type_mark, constraint, array_type) + + def __str__(self): + return self.code + + +class VHDLInterfaceElement(object): + """ + Represents a VHDL interface element + """ + def __init__(self, identifier, subtype_indication, mode=None, init_value=None): + self.identifier = identifier + self.mode = mode + self.subtype_indication = subtype_indication + self.init_value = init_value + + def without_mode(self): + """ + @returns A copy of this interface element without a mode + """ + return VHDLInterfaceElement(self.identifier, + self.subtype_indication, + init_value=self.init_value) + + @classmethod + def parse(cls, code, is_signal=False): + """ + Returns a new instance by parsing the code + """ + if is_signal: + # Remove 'signal' string if a signal is beeing parsed + code = code.replace("signal", "") + + interface_element_string = code + + # Extract the identifier + identifier = interface_element_string.split(':')[0].strip() + + # Extract subtype indication and mode (if any) + + mode_split = interface_element_string.split(':')[1].strip().split(None, 1) + if cls._is_mode(mode_split[0]): + mode = mode_split[0] + subtype_indication = VHDLSubtypeIndication.parse(mode_split[1]) + else: + mode = None + subtype_indication = VHDLSubtypeIndication.parse(interface_element_string.split(':')[1].strip()) + + # Extract initial value + init_value_split = interface_element_string.split(':=') + if len(init_value_split) > 1: + init_value = init_value_split[1].strip() + else: + init_value = None + + return cls(identifier, subtype_indication, mode, init_value) + + @staticmethod + def _is_mode(code): + """ + Return True if the code is a mode keyword + """ + return code in ('in', 'out', 'inout', 'buffer', 'linkage') + + def __str__(self): + code = self.identifier + " : " + + if self.mode is not None: + code += self.mode + " " + + code += str(self.subtype_indication) + + if self.init_value is not None: + code += " := " + self.init_value + + return code + + +class VHDLEnumerationType(object): + """Represents a VHDL enumeration type""" + def __init__(self, identifier, literals): + self.identifier = identifier + self.literals = literals + + _enum_declaration_re = re.compile(r""" + \b # Word boundary + type + \s+ + (?P[a-zA-Z][\w]*) # An identifier + \s+ + is + \s*\(\s* + (?P[a-zA-Z][\w]* # First enumeration literal + (\s*,\s*[a-zA-Z][\w]*)*) # More enumeration literals + \s*\)\s*;""", re.MULTILINE | re.IGNORECASE | re.VERBOSE) + + @classmethod + def find(cls, code): + """ + Find enumeration types in the code + """ + for enum_type in cls._enum_declaration_re.finditer(code): + identifier = enum_type.group('id') + literals = [e.strip() for e in enum_type.group('literals').split(',')] + yield cls(identifier, literals) + + +class VHDLElementDeclaration(object): + """Represents a VHDL element declaration""" + def __init__(self, identifier_list, subtype_indication): + self.identifier_list = identifier_list + self.subtype_indication = subtype_indication + + +class VHDLRecordType(object): + """Represents a VHDL record type""" + def __init__(self, identifier, elements): + self.identifier = identifier + self.elements = elements + + _record_declaration_re = re.compile(r""" + \b # Word boundary + type + \s+ + (?P[a-zA-Z][\w]*) # An identifier + \s+ + is + \s+ + record + (?P.*?)end\s+record""", re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) + + @classmethod + def find(cls, code): + """ + Find all record types in the code + """ + for record_type in cls._record_declaration_re.finditer(code): + identifier = record_type.group('id') + elements = record_type.group('elements').split(';') + parsed_elements = [] + for element in elements: + if ':' in element: + identifier_list_and_subtype_indication = element.split(':') + identifier_list = [i.strip() for i in identifier_list_and_subtype_indication[0].split(',')] + subtype_indication = VHDLSubtypeIndication.parse(identifier_list_and_subtype_indication[1].strip()) + parsed_elements.append(VHDLElementDeclaration(identifier_list, subtype_indication)) + yield cls(identifier, parsed_elements) + + +class VHDLRange(object): + """Represents a VHDL Range""" + def __init__(self, range_type=None, left=None, right=None, attribute=None): + self.range_type = range_type + self.left = left + self.right = right + self.attribute = attribute + + +class VHDLArrayType(object): + """Represents a VHDL array type""" + def __init__(self, identifier, subtype_indication, range1, range2): + self.identifier = identifier + self.subtype_indication = subtype_indication + self.range1 = range1 + self.range2 = range2 + + _constrained_ranges_re = re.compile(r""" + \s*(?P.+?) + \s+(to|downto)\s+ + (?P.+?)\s* + (, + \s*(?P.+?) + \s+(to|downto)\s+ + (?P.+?)\s*)?""", re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) + + _range_attribute_ranges_re = re.compile(r""" + \s*(?P[a-zA-Z][\w]*'range)\s*""", re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) + + _unconstrained_ranges_re = re.compile(r""" + \s*(?P[a-zA-Z][\w]*) + \s+range\s+<>\s* + (, + \s*(?P[a-zA-Z][\w]*) + \s+range\s+<>\s*)?""", re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) + + _constrained_range_re = re.compile(r""" + \s*(?P.+?) + \s+(to|downto)\s+ + (?P.+?)\s*""", re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) + + _range_attribute_range_re = re.compile(r""" + \s*(?P[a-zA-Z][\w]*'range)\s*""", re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) + + _unconstrained_range_re = re.compile(r""" + \s*(?P[a-zA-Z][\w]*) + \s+range\s+<>\s*""", re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) + + _array_declaration_re = re.compile(r""" + \b # Word boundary + type + \s+ + (?P[a-zA-Z][\w]*) + \s+ + is + \s+ + array + \s*\( + (?P.*?) + \)\s+of\s+ + (?P.*?)\s*;""", re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) + + @classmethod + def find(cls, code): + """Iterate over new instances of VHDLArrayType for all array types within the code""" + for array_type in cls._array_declaration_re.finditer(code): + identifier = array_type.group('id') + subtype_indication = VHDLSubtypeIndication.parse(array_type.group('subtype_indication')) + ranges = array_type.group('ranges') + range1_str, range2_str = cls._split_ranges(ranges) + range1 = cls._parse_range(range1_str) + range2 = cls._parse_range(range2_str) + + yield cls(identifier, subtype_indication, range1, range2) + + @staticmethod + def _split_ranges(ranges): + """Splits 2D ranges in two. 1D ranges will return None as the second range""" + level = 0 + index = 0 + if ',' in ranges: + for char in ranges: + if char == ',' and level == 0: + return ranges[:index], ranges[index + 1:] + + if char == '(': + level += 1 + elif char == ')': + level -= 1 + index += 1 + + return ranges, None + + @classmethod + def _parse_range(cls, the_range): + """Extracts range type, left and right boundary as well as the range when the 'range attribute + is used""" + if the_range is None: + return VHDLRange() + + unconstrained_range = cls._unconstrained_range_re.match(the_range) + if unconstrained_range is not None: + range_type = unconstrained_range.group('range_type') + return VHDLRange(range_type) + + constrained_range = cls._constrained_range_re.match(the_range) + range_attribute = cls._range_attribute_range_re.match(the_range) + if constrained_range is not None: + range_left = constrained_range.group('range_left') + range_right = constrained_range.group('range_right') + return VHDLRange(None, range_left, range_right) + + if range_attribute is not None: + range_attribute = range_attribute.group('range_attribute') + return VHDLRange(attribute=range_attribute) + + return VHDLRange() + + +def find_closing_delimiter(start, end, code): + """ + Find the balanced closing position within the code. + + The balanced closing position is defined as the first position of an end marker + where the number of previous start and end markers are equal + """ + delimiter_pattern = start + '|' + end + start = start.replace('\\', '') + end = end.replace('\\', '') + delimiters = re.compile(delimiter_pattern) + count = 1 + for delimiter in delimiters.finditer(code): + if delimiter.group() == start: + count += 1 + else: + count -= 1 + + if count == 0: + return delimiter.end() + raise ValueError('Failed to find closing delimiter to ' + start + ' in ' + code + '.') + + +class VHDLReference(object): + """ + Reference to design unit + """ + _reference_types = ("package", + "context", + "entity", + "configuration") + + _uses_re = re.compile(r""" + \b # Word boundary + (?Puse|context) # use or context keyword + \s+ # At least one whitespace + (?P[a-zA-Z][\w]*(\.[a-zA-Z][\w]*){1,2}) + (?P(\s*,\s*[a-zA-Z][\w]*(\.[a-zA-Z][\w]*){1,2})*) + \s* # Potential whitespaces + ; # Semi-colon + """, re.MULTILINE | re.IGNORECASE | re.VERBOSE) + + @classmethod + def _find_uses(cls, code): + """ + Find all the libraries and use clasues within the code + """ + + def get_ids(match): + """ + Get all ids found within the match taking the optinal extra ids of + library and use clauses into account such as: + + use foo, bar; + + or + + library foo, bar; + """ + ids = [match.group('id').strip()] + if match.group('extra'): + ids += [name.strip() for name in match.group('extra').split(',')[1:]] + return ids + + references = [] + for match in cls._uses_re.finditer(code): + for uses in get_ids(match): + uses = uses.split(".") + + names_within = uses[2:] if len(uses) > 2 else (None,) + for name_within in names_within: + ref = cls(reference_type="package" if match.group("use_type") == "use" else "context", + library=uses[0], + design_unit=uses[1], + name_within=name_within) + + references.append(ref) + return references + + _entity_reference_re = re.compile( + r'\bentity\s+(?P[a-zA-Z]\w*)\.(?P[a-zA-Z]\w*)\s*(\((?P[a-zA-Z]\w*)\))?', + re.MULTILINE | re.IGNORECASE) + + @classmethod + def _find_entity_references(cls, code): + """ + Find all entity references from instantiations or block configurations + """ + references = [] + for match in cls._entity_reference_re.finditer(code): + if match.group("arch") is None: + references.append(cls('entity', match.group("lib"), match.group("ent"))) + else: + references.append(cls('entity', match.group("lib"), match.group("ent"), match.group("arch"))) + return references + + _configuration_reference_re = re.compile( + r'\bconfiguration\s+(?P[a-zA-Z]\w*)\.(?P[a-zA-Z]\w*)', + re.MULTILINE | re.IGNORECASE) + + @classmethod + def _find_configuration_references(cls, code): + """ + Find all configuration references within block configurations + """ + references = [] + for match in cls._configuration_reference_re.finditer(code): + references.append(cls('configuration', match.group("lib"), match.group("cfg"))) + return references + + _package_instance_re = re.compile(PACKAGE_INSTANCE_PATTERN, + re.MULTILINE | re.IGNORECASE) + + @classmethod + def _find_package_instance_references(cls, code): + """ + Finds all reference causes by package instantiation + """ + references = [] + for match in cls._package_instance_re.finditer(code): + references.append(cls('package', match.group("lib"), match.group("name"))) + return references + + @classmethod + def find(cls, code): + """ + Find entity, use, context and configuration references within the code + """ + return (cls._find_uses(code) + + cls._find_entity_references(code) + + cls._find_configuration_references(code) + + cls._find_package_instance_references(code)) + + def __init__(self, reference_type, library, design_unit, name_within=None): + assert reference_type in self._reference_types + self.reference_type = reference_type + self.library = library + self.design_unit = design_unit + + # String "all" may be used to denote all names within + self.name_within = name_within + + def __repr__(self): + return "VHDLReference(%r, %r, %r, %r)" % ( + self.reference_type, + self.library, + self.design_unit, + self.name_within) + + def __eq__(self, other): + return (self.reference_type == other.reference_type + and self.library == other.library + and self.design_unit == other.design_unit + and self.name_within == other.name_within) + + def copy(self): + return VHDLReference(self.reference_type, + self.library, + self.design_unit, + self.name_within) + + def is_entity_reference(self): + return self.reference_type == 'entity' + + def is_package_reference(self): + return self.reference_type == 'package' + + def reference_all_names_within(self): + return self.name_within == "all" + + +VHDL_REMOVE_COMMENT_RE = re.compile(r'--[^\n]*') + + +def _comment_repl(match): + """ + Replace comment with equal amount of whitespace to make + lexical position unaffected + """ + text = match.group(0) + return " " * len(text) + + +def remove_comments(code): + """ + Return the code with comments removed + """ + return VHDL_REMOVE_COMMENT_RE.sub(_comment_repl, code) diff --git a/vunit/vivado/__init__.py b/vunit/vivado/__init__.py index bddf83651..c87b021e4 100644 --- a/vunit/vivado/__init__.py +++ b/vunit/vivado/__init__.py @@ -1,13 +1,13 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Utilities for integrating with Vivado -""" - -from vunit.vivado.vivado import (run_vivado, - add_from_compile_order_file, - create_compile_order_file) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Utilities for integrating with Vivado +""" + +from vunit.vivado.vivado import (run_vivado, + add_from_compile_order_file, + create_compile_order_file) diff --git a/vunit/vivado/vivado.py b/vunit/vivado/vivado.py index 614587aa8..5b05b0d4c 100644 --- a/vunit/vivado/vivado.py +++ b/vunit/vivado/vivado.py @@ -1,121 +1,121 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Utilities for integrating with Vivado -""" - -from __future__ import print_function -from subprocess import check_call -from os import makedirs -from os.path import abspath, join, dirname, exists, basename - - -def add_from_compile_order_file(vunit_obj, compile_order_file, dependency_scan_defaultlib=True): - """ - Add Vivado IP:s from a compile order file - """ - compile_order, libraries, include_dirs = _read_compile_order(compile_order_file) - - # Create libraries - for library_name in libraries: - vunit_obj.add_library(library_name, vhdl_standard="93") - - # Add all source files to VUnit - source_files = [] - - no_dependency_scan = [] - with_dependency_scan = [] - for library_name, file_name in compile_order: - is_verilog = file_name.endswith(".v") or file_name.endswith(".vp") - - # Optionally use VUnit dependency scanning for everything in xil_defaultlib, which - # typically contains unencrypted top levels that instantiate encrypted implementations. - scan_dependencies = dependency_scan_defaultlib and library_name == "xil_defaultlib" - source_file = vunit_obj.library(library_name).add_source_file( - file_name, - no_parse=not scan_dependencies, - include_dirs=include_dirs if is_verilog else None) - - if scan_dependencies: - with_dependency_scan.append(source_file) - else: - no_dependency_scan.append(source_file) - - source_files.append(source_file) - - # Use hardcoded dependency for everthing outside of xil_defaultlib - for idx in range(1, len(no_dependency_scan)): - no_dependency_scan[idx].add_dependency_on(no_dependency_scan[idx - 1]) - - # Add dependency of last item in non-dependency scanned files to the each scanned file - if no_dependency_scan: - for source_file in with_dependency_scan: - source_file.add_dependency_on(no_dependency_scan[-1]) - - return source_files - - -def create_compile_order_file(project_file, compile_order_file, vivado_path=None): - """ - Create compile file from Vivado project - """ - print("Generating Vivado project compile order into %s ..." % abspath(compile_order_file)) - - if not exists(dirname(compile_order_file)): - makedirs(dirname(compile_order_file)) - - print("Extracting compile order ...") - run_vivado(join(dirname(__file__), "tcl", "extract_compile_order.tcl"), - tcl_args=[project_file, compile_order_file], - vivado_path=vivado_path) - - -def _read_compile_order(file_name): - """ - Read the compile order file and filter out duplicate files - """ - compile_order = [] - unique = set() - include_dirs = set() - libraries = set() - - with open(file_name, "r") as ifile: - - for line in ifile.readlines(): - library_name, file_type, file_name = line.strip().split(",", 2) - assert file_type in ("Verilog", "VHDL", "Verilog Header") - libraries.add(library_name) - - # Vivado generates duplicate files for different IP:s - # using the same underlying libraries. We remove duplicates here - key = (library_name, basename(file_name)) - if key in unique: - continue - unique.add(key) - - if file_type == "Verilog Header": - include_dirs.add(dirname(file_name)) - else: - compile_order.append((library_name, file_name)) - - return compile_order, libraries, list(sorted(include_dirs)) - - -def run_vivado(tcl_file_name, tcl_args=None, cwd=None, vivado_path=None): - """ - Run tcl script in Vivado in batch mode. - - Note: the shell=True is important in windows where Vivado is just a bat file. - """ - vivado = "vivado" if vivado_path is None else join(abspath(vivado_path), "bin", "vivado") - cmd = "{} -nojournal -nolog -notrace -mode batch -source {}".format(vivado, - abspath(tcl_file_name)) - if tcl_args is not None: - cmd += " -tclargs " + " ".join([str(val) for val in tcl_args]) - - print(cmd) - check_call(cmd, cwd=cwd, shell=True) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Utilities for integrating with Vivado +""" + +from __future__ import print_function +from subprocess import check_call +from os import makedirs +from os.path import abspath, join, dirname, exists, basename + + +def add_from_compile_order_file(vunit_obj, compile_order_file, dependency_scan_defaultlib=True): + """ + Add Vivado IP:s from a compile order file + """ + compile_order, libraries, include_dirs = _read_compile_order(compile_order_file) + + # Create libraries + for library_name in libraries: + vunit_obj.add_library(library_name, vhdl_standard="93") + + # Add all source files to VUnit + source_files = [] + + no_dependency_scan = [] + with_dependency_scan = [] + for library_name, file_name in compile_order: + is_verilog = file_name.endswith(".v") or file_name.endswith(".vp") + + # Optionally use VUnit dependency scanning for everything in xil_defaultlib, which + # typically contains unencrypted top levels that instantiate encrypted implementations. + scan_dependencies = dependency_scan_defaultlib and library_name == "xil_defaultlib" + source_file = vunit_obj.library(library_name).add_source_file( + file_name, + no_parse=not scan_dependencies, + include_dirs=include_dirs if is_verilog else None) + + if scan_dependencies: + with_dependency_scan.append(source_file) + else: + no_dependency_scan.append(source_file) + + source_files.append(source_file) + + # Use hardcoded dependency for everthing outside of xil_defaultlib + for idx in range(1, len(no_dependency_scan)): + no_dependency_scan[idx].add_dependency_on(no_dependency_scan[idx - 1]) + + # Add dependency of last item in non-dependency scanned files to the each scanned file + if no_dependency_scan: + for source_file in with_dependency_scan: + source_file.add_dependency_on(no_dependency_scan[-1]) + + return source_files + + +def create_compile_order_file(project_file, compile_order_file, vivado_path=None): + """ + Create compile file from Vivado project + """ + print("Generating Vivado project compile order into %s ..." % abspath(compile_order_file)) + + if not exists(dirname(compile_order_file)): + makedirs(dirname(compile_order_file)) + + print("Extracting compile order ...") + run_vivado(join(dirname(__file__), "tcl", "extract_compile_order.tcl"), + tcl_args=[project_file, compile_order_file], + vivado_path=vivado_path) + + +def _read_compile_order(file_name): + """ + Read the compile order file and filter out duplicate files + """ + compile_order = [] + unique = set() + include_dirs = set() + libraries = set() + + with open(file_name, "r") as ifile: + + for line in ifile.readlines(): + library_name, file_type, file_name = line.strip().split(",", 2) + assert file_type in ("Verilog", "VHDL", "Verilog Header") + libraries.add(library_name) + + # Vivado generates duplicate files for different IP:s + # using the same underlying libraries. We remove duplicates here + key = (library_name, basename(file_name)) + if key in unique: + continue + unique.add(key) + + if file_type == "Verilog Header": + include_dirs.add(dirname(file_name)) + else: + compile_order.append((library_name, file_name)) + + return compile_order, libraries, list(sorted(include_dirs)) + + +def run_vivado(tcl_file_name, tcl_args=None, cwd=None, vivado_path=None): + """ + Run tcl script in Vivado in batch mode. + + Note: the shell=True is important in windows where Vivado is just a bat file. + """ + vivado = "vivado" if vivado_path is None else join(abspath(vivado_path), "bin", "vivado") + cmd = "{} -nojournal -nolog -notrace -mode batch -source {}".format(vivado, + abspath(tcl_file_name)) + if tcl_args is not None: + cmd += " -tclargs " + " ".join([str(val) for val in tcl_args]) + + print(cmd) + check_call(cmd, cwd=cwd, shell=True) diff --git a/vunit/vsim_simulator_mixin.py b/vunit/vsim_simulator_mixin.py index 4d118d6b2..1cabef1ea 100644 --- a/vunit/vsim_simulator_mixin.py +++ b/vunit/vsim_simulator_mixin.py @@ -1,342 +1,342 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -Shared simulation logic between vsim based simulators such as ModelSim -and RivieraPRO -""" - -import sys -import os -from os.path import join, dirname, abspath -from vunit.ostools import (write_file, - Process) -from vunit.test_suites import get_result_file_name -from vunit.persistent_tcl_shell import PersistentTclShell - - -class VsimSimulatorMixin(object): - """ - A Mixin class for parts that are common to vsim/TCL based - simulators such as modelsim and rivierapro - """ - - def __init__(self, prefix, persistent, sim_cfg_file_name): - self._prefix = prefix - sim_cfg_file_name = abspath(sim_cfg_file_name) - self._sim_cfg_file_name = sim_cfg_file_name - - prefix = self._prefix # Avoid circular dependency inhibiting process destruction - env = self.get_env() - - def create_process(ident): - return Process([join(prefix, "vsim"), "-c", - "-l", join(dirname(sim_cfg_file_name), "transcript%i" % ident), - "-do", abspath(join(dirname(__file__), "tcl_read_eval_loop.tcl"))], - cwd=dirname(sim_cfg_file_name), - env=env) - - if persistent: - self._persistent_shell = PersistentTclShell(create_process=create_process) - else: - self._persistent_shell = None - - @staticmethod - def _create_restart_function(): - """" - Create the vunit_restart function to recompile and restart the simulation - - This function is quite complicated to work around limitations - of modelsim not being able to change working directory. - - Thus python is called with an explicit command string that in - turn call the python command we actually wanted but in the - correct working directory using subprocess.call - - -u flag is needed for continuous output - """ - recompile_command = [ - sys.executable, - "-u", - sys.argv[0], - "--compile"] - - # Strip --clean from re-compile command - # Leave other arguments intact since users can add custom CLI options - recompile_command += [arg for arg in sys.argv[1:] - if arg != "--clean"] - - recompile_command_visual = " ".join(recompile_command) - - # stderr is intentionally re-directed to stdout so that the tcl's catch - # relies on the return code from the python process rather than being - # tricked by output going to stderr. See issue #228. - recompile_command_eval = [ - "%s" % sys.executable, - "-u", - "-c", - ("import sys;" - "import subprocess;" - "exit(subprocess.call(%r, " - "cwd=%r, " - "bufsize=0, " - "universal_newlines=True, " - "stdout=sys.stdout, " - "stderr=sys.stdout))") % (recompile_command, abspath(os.getcwd()))] - recompile_command_eval_tcl = " ".join(["{%s}" % part for part in recompile_command_eval]) - - tcl = """ -proc vunit_compile {} { - set cmd_show {%s} - puts "Re-compiling using command ${cmd_show}" - - set chan [open |[list %s] r] - - while {[gets $chan line] >= 0} { - puts $line - } - - if {[catch {close $chan} error_msg]} { - puts "Re-compile failed" - puts ${error_msg} - return true - } else { - puts "Re-compile finished" - return false - } -} - -proc vunit_restart {} { - if {![vunit_compile]} { - _vunit_sim_restart - vunit_run - } -} -""" % (recompile_command_visual, recompile_command_eval_tcl) - return tcl - - def _create_common_script(self, - test_suite_name, - config, - script_path, - output_path): - """ - Create tcl script with functions common to interactive and batch modes - """ - tcl = """ -proc vunit_help {} { - puts {List of VUnit commands:} - puts {vunit_help} - puts { - Prints this help} - puts {vunit_load [vsim_extra_args]} - puts { - Load design with correct generics for the test} - puts { - Optional first argument are passed as extra flags to vsim} - puts {vunit_user_init} - puts { - Re-runs the user defined init file} - puts {vunit_run} - puts { - Run test, must do vunit_load first} - puts {vunit_compile} - puts { - Recompiles the source files} - puts {vunit_restart} - puts { - Recompiles the source files} - puts { - and re-runs the simulation if the compile was successful} -} - -proc vunit_run {} { - if {[catch {_vunit_run} failed_or_err]} { - echo $failed_or_err - return true; - } - - if {![is_test_suite_done]} { - echo - echo "Test Run Failed!" - echo - _vunit_run_failure; - return true; - } - - return false; -} - -""" - tcl += self._create_init_files_after_load(config) - tcl += self._create_load_function(test_suite_name, config, script_path) - tcl += get_is_test_suite_done_tcl(get_result_file_name(output_path)) - tcl += self._create_run_function() - tcl += self._create_restart_function() - return tcl - - @staticmethod - def _create_batch_script(common_file_name, load_only=False): - """ - Create tcl script to run in batch mode - """ - batch_do = "" - batch_do += "onerror {quit -code 1}\n" - batch_do += "source \"%s\"\n" % fix_path(common_file_name) - batch_do += "set failed [vunit_load]\n" - batch_do += "if {$failed} {quit -code 1}\n" - if not load_only: - batch_do += "set failed [vunit_run]\n" - batch_do += "if {$failed} {quit -code 1}\n" - batch_do += "quit -code 0\n" - return batch_do - - def _create_init_files_after_load(self, config): - """ - Create the _vunit_source_init_files_after_load function which sources the user defined TCL file in - simulator_name.init_files.after_load - """ - opt_name = self.name + ".init_files.after_load" - init_files = config.sim_options.get(opt_name, []) - tcl = "proc _vunit_source_init_files_after_load {} {\n" - for init_file in init_files: - tcl += self._source_tcl_file(init_file, config, opt_name) - tcl += " return 0\n" - tcl += "}\n" - return tcl - - def _create_user_init_function(self, config): - """ - Create the vunit_user_init function which sources the user defined TCL file in - simulator_name.init_file.gui - """ - opt_name = self.name + ".init_file.gui" - init_file = config.sim_options.get(opt_name, None) - tcl = "proc vunit_user_init {} {\n" - if init_file is not None: - tcl += self._source_tcl_file(init_file, config, opt_name) - tcl += " return 0\n" - tcl += "}\n" - return tcl - - @staticmethod - def _source_tcl_file(file_name, config, message): - """ - Create TCL to source a file and catch errors - Also defines the vunit_tb_path variable as the config.tb_path - """ - template = """ - set vunit_tb_path "%s" - set file_name "%s" - puts "Sourcing file ${file_name} from %s" - if {[catch {source ${file_name}} error_msg]} { - puts "Sourcing ${file_name} failed" - puts ${error_msg} - return true - } -""" - tcl = template % (fix_path(abspath(config.tb_path)), - fix_path(abspath(file_name)), - message) - return tcl - - def _create_gui_script(self, common_file_name, config): - """ - Create the user facing script which loads common functions and prints a help message - """ - tcl = 'source "%s"\n' % fix_path(common_file_name) - tcl += self._create_user_init_function(config) - tcl += "if {![vunit_load]} {\n" - tcl += " vunit_user_init\n" - tcl += " vunit_help\n" - tcl += "}\n" - return tcl - - def _run_batch_file(self, batch_file_name, gui=False): - """ - Run a test bench in batch by invoking a new vsim process from the command line - """ - - try: - args = [join(self._prefix, "vsim"), "-gui" if gui else "-c", - "-l", join(dirname(batch_file_name), "transcript"), - '-do', "source \"%s\"" % fix_path(batch_file_name)] - - proc = Process(args, cwd=dirname(self._sim_cfg_file_name)) - proc.consume_output() - except Process.NonZeroExitCode: - return False - return True - - def _run_persistent(self, common_file_name, load_only=False): - """ - Run a test bench using the persistent vsim process - """ - try: - self._persistent_shell.execute('source "%s"' % fix_path(common_file_name)) - self._persistent_shell.execute("set failed [vunit_load]") - if self._persistent_shell.read_var("failed") == '1': - return False - - run_ok = True - if not load_only: - self._persistent_shell.execute("set failed [vunit_run]") - run_ok = self._persistent_shell.read_var("failed") != '1' - self._persistent_shell.execute("quit -sim") - return run_ok - except Process.NonZeroExitCode: - return False - - def simulate(self, output_path, test_suite_name, config, elaborate_only): - """ - Run a test bench - """ - script_path = join(output_path, self.name) - - common_file_name = join(script_path, "common.do") - gui_file_name = join(script_path, "gui.do") - batch_file_name = join(script_path, "batch.do") - - write_file(common_file_name, - self._create_common_script(test_suite_name, - config, - script_path, - output_path)) - write_file(gui_file_name, - self._create_gui_script(common_file_name, config)) - write_file(batch_file_name, - self._create_batch_script(common_file_name, elaborate_only)) - - if self._gui: - return self._run_batch_file(gui_file_name, gui=True) - - if self._persistent_shell is not None: - return self._run_persistent(common_file_name, load_only=elaborate_only) - - return self._run_batch_file(batch_file_name) - - -def fix_path(path): - """ - Adjust path for TCL usage - """ - return path.replace("\\", "/").replace(" ", "\\ ") - - -def get_is_test_suite_done_tcl(vunit_result_file): - """ - Returns tcl procedure to detect if simulation was successful or not - Simulation is considered successful if the test_suite_done was reached in the results file - """ - - tcl = """ -proc is_test_suite_done {} { - set fd [open "%s" "r"] - set contents [read $fd] - close $fd - set lines [split $contents "\n"] - foreach line $lines { - if {$line=="test_suite_done"} { - return true; - } - } - - return false; -} -""" % (fix_path(vunit_result_file)) - return tcl +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Shared simulation logic between vsim based simulators such as ModelSim +and RivieraPRO +""" + +import sys +import os +from os.path import join, dirname, abspath +from vunit.ostools import (write_file, + Process) +from vunit.test_suites import get_result_file_name +from vunit.persistent_tcl_shell import PersistentTclShell + + +class VsimSimulatorMixin(object): + """ + A Mixin class for parts that are common to vsim/TCL based + simulators such as modelsim and rivierapro + """ + + def __init__(self, prefix, persistent, sim_cfg_file_name): + self._prefix = prefix + sim_cfg_file_name = abspath(sim_cfg_file_name) + self._sim_cfg_file_name = sim_cfg_file_name + + prefix = self._prefix # Avoid circular dependency inhibiting process destruction + env = self.get_env() + + def create_process(ident): + return Process([join(prefix, "vsim"), "-c", + "-l", join(dirname(sim_cfg_file_name), "transcript%i" % ident), + "-do", abspath(join(dirname(__file__), "tcl_read_eval_loop.tcl"))], + cwd=dirname(sim_cfg_file_name), + env=env) + + if persistent: + self._persistent_shell = PersistentTclShell(create_process=create_process) + else: + self._persistent_shell = None + + @staticmethod + def _create_restart_function(): + """" + Create the vunit_restart function to recompile and restart the simulation + + This function is quite complicated to work around limitations + of modelsim not being able to change working directory. + + Thus python is called with an explicit command string that in + turn call the python command we actually wanted but in the + correct working directory using subprocess.call + + -u flag is needed for continuous output + """ + recompile_command = [ + sys.executable, + "-u", + sys.argv[0], + "--compile"] + + # Strip --clean from re-compile command + # Leave other arguments intact since users can add custom CLI options + recompile_command += [arg for arg in sys.argv[1:] + if arg != "--clean"] + + recompile_command_visual = " ".join(recompile_command) + + # stderr is intentionally re-directed to stdout so that the tcl's catch + # relies on the return code from the python process rather than being + # tricked by output going to stderr. See issue #228. + recompile_command_eval = [ + "%s" % sys.executable, + "-u", + "-c", + ("import sys;" + "import subprocess;" + "exit(subprocess.call(%r, " + "cwd=%r, " + "bufsize=0, " + "universal_newlines=True, " + "stdout=sys.stdout, " + "stderr=sys.stdout))") % (recompile_command, abspath(os.getcwd()))] + recompile_command_eval_tcl = " ".join(["{%s}" % part for part in recompile_command_eval]) + + tcl = """ +proc vunit_compile {} { + set cmd_show {%s} + puts "Re-compiling using command ${cmd_show}" + + set chan [open |[list %s] r] + + while {[gets $chan line] >= 0} { + puts $line + } + + if {[catch {close $chan} error_msg]} { + puts "Re-compile failed" + puts ${error_msg} + return true + } else { + puts "Re-compile finished" + return false + } +} + +proc vunit_restart {} { + if {![vunit_compile]} { + _vunit_sim_restart + vunit_run + } +} +""" % (recompile_command_visual, recompile_command_eval_tcl) + return tcl + + def _create_common_script(self, + test_suite_name, + config, + script_path, + output_path): + """ + Create tcl script with functions common to interactive and batch modes + """ + tcl = """ +proc vunit_help {} { + puts {List of VUnit commands:} + puts {vunit_help} + puts { - Prints this help} + puts {vunit_load [vsim_extra_args]} + puts { - Load design with correct generics for the test} + puts { - Optional first argument are passed as extra flags to vsim} + puts {vunit_user_init} + puts { - Re-runs the user defined init file} + puts {vunit_run} + puts { - Run test, must do vunit_load first} + puts {vunit_compile} + puts { - Recompiles the source files} + puts {vunit_restart} + puts { - Recompiles the source files} + puts { - and re-runs the simulation if the compile was successful} +} + +proc vunit_run {} { + if {[catch {_vunit_run} failed_or_err]} { + echo $failed_or_err + return true; + } + + if {![is_test_suite_done]} { + echo + echo "Test Run Failed!" + echo + _vunit_run_failure; + return true; + } + + return false; +} + +""" + tcl += self._create_init_files_after_load(config) + tcl += self._create_load_function(test_suite_name, config, script_path) + tcl += get_is_test_suite_done_tcl(get_result_file_name(output_path)) + tcl += self._create_run_function() + tcl += self._create_restart_function() + return tcl + + @staticmethod + def _create_batch_script(common_file_name, load_only=False): + """ + Create tcl script to run in batch mode + """ + batch_do = "" + batch_do += "onerror {quit -code 1}\n" + batch_do += "source \"%s\"\n" % fix_path(common_file_name) + batch_do += "set failed [vunit_load]\n" + batch_do += "if {$failed} {quit -code 1}\n" + if not load_only: + batch_do += "set failed [vunit_run]\n" + batch_do += "if {$failed} {quit -code 1}\n" + batch_do += "quit -code 0\n" + return batch_do + + def _create_init_files_after_load(self, config): + """ + Create the _vunit_source_init_files_after_load function which sources the user defined TCL file in + simulator_name.init_files.after_load + """ + opt_name = self.name + ".init_files.after_load" + init_files = config.sim_options.get(opt_name, []) + tcl = "proc _vunit_source_init_files_after_load {} {\n" + for init_file in init_files: + tcl += self._source_tcl_file(init_file, config, opt_name) + tcl += " return 0\n" + tcl += "}\n" + return tcl + + def _create_user_init_function(self, config): + """ + Create the vunit_user_init function which sources the user defined TCL file in + simulator_name.init_file.gui + """ + opt_name = self.name + ".init_file.gui" + init_file = config.sim_options.get(opt_name, None) + tcl = "proc vunit_user_init {} {\n" + if init_file is not None: + tcl += self._source_tcl_file(init_file, config, opt_name) + tcl += " return 0\n" + tcl += "}\n" + return tcl + + @staticmethod + def _source_tcl_file(file_name, config, message): + """ + Create TCL to source a file and catch errors + Also defines the vunit_tb_path variable as the config.tb_path + """ + template = """ + set vunit_tb_path "%s" + set file_name "%s" + puts "Sourcing file ${file_name} from %s" + if {[catch {source ${file_name}} error_msg]} { + puts "Sourcing ${file_name} failed" + puts ${error_msg} + return true + } +""" + tcl = template % (fix_path(abspath(config.tb_path)), + fix_path(abspath(file_name)), + message) + return tcl + + def _create_gui_script(self, common_file_name, config): + """ + Create the user facing script which loads common functions and prints a help message + """ + tcl = 'source "%s"\n' % fix_path(common_file_name) + tcl += self._create_user_init_function(config) + tcl += "if {![vunit_load]} {\n" + tcl += " vunit_user_init\n" + tcl += " vunit_help\n" + tcl += "}\n" + return tcl + + def _run_batch_file(self, batch_file_name, gui=False): + """ + Run a test bench in batch by invoking a new vsim process from the command line + """ + + try: + args = [join(self._prefix, "vsim"), "-gui" if gui else "-c", + "-l", join(dirname(batch_file_name), "transcript"), + '-do', "source \"%s\"" % fix_path(batch_file_name)] + + proc = Process(args, cwd=dirname(self._sim_cfg_file_name)) + proc.consume_output() + except Process.NonZeroExitCode: + return False + return True + + def _run_persistent(self, common_file_name, load_only=False): + """ + Run a test bench using the persistent vsim process + """ + try: + self._persistent_shell.execute('source "%s"' % fix_path(common_file_name)) + self._persistent_shell.execute("set failed [vunit_load]") + if self._persistent_shell.read_var("failed") == '1': + return False + + run_ok = True + if not load_only: + self._persistent_shell.execute("set failed [vunit_run]") + run_ok = self._persistent_shell.read_var("failed") != '1' + self._persistent_shell.execute("quit -sim") + return run_ok + except Process.NonZeroExitCode: + return False + + def simulate(self, output_path, test_suite_name, config, elaborate_only): + """ + Run a test bench + """ + script_path = join(output_path, self.name) + + common_file_name = join(script_path, "common.do") + gui_file_name = join(script_path, "gui.do") + batch_file_name = join(script_path, "batch.do") + + write_file(common_file_name, + self._create_common_script(test_suite_name, + config, + script_path, + output_path)) + write_file(gui_file_name, + self._create_gui_script(common_file_name, config)) + write_file(batch_file_name, + self._create_batch_script(common_file_name, elaborate_only)) + + if self._gui: + return self._run_batch_file(gui_file_name, gui=True) + + if self._persistent_shell is not None: + return self._run_persistent(common_file_name, load_only=elaborate_only) + + return self._run_batch_file(batch_file_name) + + +def fix_path(path): + """ + Adjust path for TCL usage + """ + return path.replace("\\", "/").replace(" ", "\\ ") + + +def get_is_test_suite_done_tcl(vunit_result_file): + """ + Returns tcl procedure to detect if simulation was successful or not + Simulation is considered successful if the test_suite_done was reached in the results file + """ + + tcl = """ +proc is_test_suite_done {} { + set fd [open "%s" "r"] + set contents [read $fd] + close $fd + set lines [split $contents "\n"] + foreach line $lines { + if {$line=="test_suite_done"} { + return true; + } + } + + return false; +} +""" % (fix_path(vunit_result_file)) + return tcl diff --git a/vunit/vunit_cli.py b/vunit/vunit_cli.py index 476321a32..8a36b0a23 100644 --- a/vunit/vunit_cli.py +++ b/vunit/vunit_cli.py @@ -1,212 +1,212 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com - -""" -.. _custom_cli: - -Adding Custom Command Line Arguments ------------------------------------- -It is possible to add custom command line arguments to your ``run.py`` -scripts using the :class:`.VUnitCLI` class. - -.. autoclass:: vunit.vunit_cli.VUnitCLI - :members: - -A :class:`.VUnitCLI` object has a ``parser`` field which is an -`ArgumentParser` object of the `argparse`_ library. - -.. _argparse: https://docs.python.org/3/library/argparse.html - -.. code-block:: python - - from vunit import VUnitCLI, VUnit - - # Add custom command line argument to standard CLI - # Beware of conflicts with existing arguments - cli = VUnitCLI() - cli.parser.add_argument('--custom-arg', ...) - args = cli.parse_args() - - # Create VUNit instance from custom arguments - vu = VUnit.from_args(args=args) - - # Use args.custom_arg here ... - print(args.custom_arg) - -""" - -import argparse -from os.path import join, abspath -import os -from vunit.simulator_factory import SIMULATOR_FACTORY -from vunit.about import version - - -class VUnitCLI(object): - """ - VUnit command line interface - """ - - def __init__(self, description=None): - """ - :param description: A custom short description of the command line tool - """ - self.parser = _create_argument_parser(description) - - def parse_args(self, argv=None): - """ - Parse command line arguments - - :param argv: Use explicit argv instead of actual command line argument - :returns: The parsed argument namespace object - """ - return self.parser.parse_args(args=argv) - - -def _create_argument_parser(description=None, for_documentation=False): - """ - Create the argument parser - - :param description: A custom short description of the command line tool - :param for_documentation: When used for user guide documentation - :returns: The created :mod:`argparse` parser object - """ - if description is None: - description = 'VUnit command line tool version %s' % version() - - if for_documentation: - default_output_path = "./vunit_out" - else: - default_output_path = join(abspath(os.getcwd()), "vunit_out") - - parser = argparse.ArgumentParser(description=description) - - parser.add_argument('test_patterns', metavar='tests', nargs='*', - default='*', - help='Tests to run') - - parser.add_argument("--with-attributes", - default=None, - action="append", - help="Only select tests with these attributes set") - - parser.add_argument("--without-attributes", - default=None, - action="append", - help="Only select tests without these attributes set") - - parser.add_argument('-l', '--list', action='store_true', - default=False, - help='Only list all test cases') - - parser.add_argument('-f', '--files', action='store_true', - default=False, - help='Only list all files in compile order') - - parser.add_argument('--compile', action='store_true', - default=False, - help='Only compile project without running tests') - - parser.add_argument('-k', '--keep-compiling', action='store_true', - default=False, - help='Continue compiling even after errors only skipping files that depend on failed files') - - parser.add_argument('--fail-fast', action='store_true', - default=False, - help='Stop immediately on first failing test') - - parser.add_argument('--elaborate', action='store_true', - default=False, - help='Only elaborate test benches without running') - - parser.add_argument('--clean', action='store_true', - default=False, - help='Remove output path first') - - parser.add_argument('-o', '--output-path', - default=default_output_path, - help='Output path for compilation and simulation artifacts') - - parser.add_argument('-x', '--xunit-xml', - default=None, - help='Xunit test report .xml file') - - parser.add_argument('--xunit-xml-format', - choices=['jenkins', 'bamboo'], - default='jenkins', - help=('Only valid with --xunit-xml argument. ' - 'Defines where in the XML file the simulator output is stored on a failure. ' - '"jenkins" = Output stored in , ' - '"bamboo" = Output stored in .')) - - parser.add_argument('--exit-0', - default=False, - action="store_true", - help=('Exit with code 0 even if a test failed. ' - 'Still exits with code 1 on fatal errors such as compilation failure')) - - parser.add_argument('--dont-catch-exceptions', - default=False, - action="store_true", - help=('Let exceptions bubble up all the way. ' - 'Useful when running with "python -m pdb".')) - - parser.add_argument('-v', '--verbose', action="store_true", - default=False, - help='Print test output immediately and not only when failure') - - parser.add_argument('-q', '--quiet', action="store_true", - default=False, - help='Do not print test output even in the case of failure') - - parser.add_argument('--no-color', action='store_true', - default=False, - help='Do not color output') - - parser.add_argument('--log-level', - default="warning", - choices=["info", "error", "warning", "debug"], - help=("Log level of VUnit internal python logging. " - "Used for debugging")) - - parser.add_argument('-p', '--num-threads', type=positive_int, - default=1, - help=('Number of tests to run in parallel. ' - 'Test output is not continuously written in verbose mode with p > 1')) - - parser.add_argument("-u", "--unique-sim", - action="store_true", - default=False, - help="Do not re-use the same simulator process for running different test cases (slower)") - - parser.add_argument("--export-json", - default=None, - help="Export project information to a JSON file.") - - parser.add_argument('--version', action='version', version=version()) - - SIMULATOR_FACTORY.add_arguments(parser) - - return parser - - -def positive_int(val): - """ - ArgumentParse positive int check - """ - try: - ival = int(val) - assert ival > 0 - return ival - except (ValueError, AssertionError): - raise argparse.ArgumentTypeError("'%s' is not a valid positive int" % val) - - -def _parser_for_documentation(): - """ - Returns an argparse object used by sphinx for documentation in user_guide.rst - """ - return _create_argument_parser(for_documentation=True) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +.. _custom_cli: + +Adding Custom Command Line Arguments +------------------------------------ +It is possible to add custom command line arguments to your ``run.py`` +scripts using the :class:`.VUnitCLI` class. + +.. autoclass:: vunit.vunit_cli.VUnitCLI + :members: + +A :class:`.VUnitCLI` object has a ``parser`` field which is an +`ArgumentParser` object of the `argparse`_ library. + +.. _argparse: https://docs.python.org/3/library/argparse.html + +.. code-block:: python + + from vunit import VUnitCLI, VUnit + + # Add custom command line argument to standard CLI + # Beware of conflicts with existing arguments + cli = VUnitCLI() + cli.parser.add_argument('--custom-arg', ...) + args = cli.parse_args() + + # Create VUNit instance from custom arguments + vu = VUnit.from_args(args=args) + + # Use args.custom_arg here ... + print(args.custom_arg) + +""" + +import argparse +from os.path import join, abspath +import os +from vunit.simulator_factory import SIMULATOR_FACTORY +from vunit.about import version + + +class VUnitCLI(object): + """ + VUnit command line interface + """ + + def __init__(self, description=None): + """ + :param description: A custom short description of the command line tool + """ + self.parser = _create_argument_parser(description) + + def parse_args(self, argv=None): + """ + Parse command line arguments + + :param argv: Use explicit argv instead of actual command line argument + :returns: The parsed argument namespace object + """ + return self.parser.parse_args(args=argv) + + +def _create_argument_parser(description=None, for_documentation=False): + """ + Create the argument parser + + :param description: A custom short description of the command line tool + :param for_documentation: When used for user guide documentation + :returns: The created :mod:`argparse` parser object + """ + if description is None: + description = 'VUnit command line tool version %s' % version() + + if for_documentation: + default_output_path = "./vunit_out" + else: + default_output_path = join(abspath(os.getcwd()), "vunit_out") + + parser = argparse.ArgumentParser(description=description) + + parser.add_argument('test_patterns', metavar='tests', nargs='*', + default='*', + help='Tests to run') + + parser.add_argument("--with-attributes", + default=None, + action="append", + help="Only select tests with these attributes set") + + parser.add_argument("--without-attributes", + default=None, + action="append", + help="Only select tests without these attributes set") + + parser.add_argument('-l', '--list', action='store_true', + default=False, + help='Only list all test cases') + + parser.add_argument('-f', '--files', action='store_true', + default=False, + help='Only list all files in compile order') + + parser.add_argument('--compile', action='store_true', + default=False, + help='Only compile project without running tests') + + parser.add_argument('-k', '--keep-compiling', action='store_true', + default=False, + help='Continue compiling even after errors only skipping files that depend on failed files') + + parser.add_argument('--fail-fast', action='store_true', + default=False, + help='Stop immediately on first failing test') + + parser.add_argument('--elaborate', action='store_true', + default=False, + help='Only elaborate test benches without running') + + parser.add_argument('--clean', action='store_true', + default=False, + help='Remove output path first') + + parser.add_argument('-o', '--output-path', + default=default_output_path, + help='Output path for compilation and simulation artifacts') + + parser.add_argument('-x', '--xunit-xml', + default=None, + help='Xunit test report .xml file') + + parser.add_argument('--xunit-xml-format', + choices=['jenkins', 'bamboo'], + default='jenkins', + help=('Only valid with --xunit-xml argument. ' + 'Defines where in the XML file the simulator output is stored on a failure. ' + '"jenkins" = Output stored in , ' + '"bamboo" = Output stored in .')) + + parser.add_argument('--exit-0', + default=False, + action="store_true", + help=('Exit with code 0 even if a test failed. ' + 'Still exits with code 1 on fatal errors such as compilation failure')) + + parser.add_argument('--dont-catch-exceptions', + default=False, + action="store_true", + help=('Let exceptions bubble up all the way. ' + 'Useful when running with "python -m pdb".')) + + parser.add_argument('-v', '--verbose', action="store_true", + default=False, + help='Print test output immediately and not only when failure') + + parser.add_argument('-q', '--quiet', action="store_true", + default=False, + help='Do not print test output even in the case of failure') + + parser.add_argument('--no-color', action='store_true', + default=False, + help='Do not color output') + + parser.add_argument('--log-level', + default="warning", + choices=["info", "error", "warning", "debug"], + help=("Log level of VUnit internal python logging. " + "Used for debugging")) + + parser.add_argument('-p', '--num-threads', type=positive_int, + default=1, + help=('Number of tests to run in parallel. ' + 'Test output is not continuously written in verbose mode with p > 1')) + + parser.add_argument("-u", "--unique-sim", + action="store_true", + default=False, + help="Do not re-use the same simulator process for running different test cases (slower)") + + parser.add_argument("--export-json", + default=None, + help="Export project information to a JSON file.") + + parser.add_argument('--version', action='version', version=version()) + + SIMULATOR_FACTORY.add_arguments(parser) + + return parser + + +def positive_int(val): + """ + ArgumentParse positive int check + """ + try: + ival = int(val) + assert ival > 0 + return ival + except (ValueError, AssertionError): + raise argparse.ArgumentTypeError("'%s' is not a valid positive int" % val) + + +def _parser_for_documentation(): + """ + Returns an argparse object used by sphinx for documentation in user_guide.rst + """ + return _create_argument_parser(for_documentation=True)