diff --git a/examples/mixed/uart/run.py b/examples/mixed/uart/run.py new file mode 100644 index 000000000..e0ee77a28 --- /dev/null +++ b/examples/mixed/uart/run.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Copyright (c) 2014-2023, Lars Asplund lars.anders.asplund@gmail.com + +""" +SystemVerilog UART +------------------ + +A more realistic test bench of an UART to show VUnit SystemVerilog +usage on a typical module. +""" + +from pathlib import Path +from vunit import VUnit + +SRC_PATH = Path(__file__).parent / "src" +VHDL_SRC_PATH = Path(__file__).parent / "../../vhdl/uart/src" +VU = VUnit.from_argv() +VU.add_verilog_builtins() +VU.add_vhdl_builtins() + +uart_lib = VU.add_library("uart_lib") +uart_lib.add_source_files(SRC_PATH / "*.sv") +uart_lib.add_source_files(VHDL_SRC_PATH / "*.vhd") + +VU.add_library("tb_uart_lib").add_source_files(SRC_PATH / "test" / "*.sv") + +VU.set_sim_option("modelsim.vsim_flags", ["-suppress", "3839"]) + +VU.main() diff --git a/examples/mixed/uart/src/axis_intf.sv b/examples/mixed/uart/src/axis_intf.sv new file mode 100644 index 000000000..c33daa29a --- /dev/null +++ b/examples/mixed/uart/src/axis_intf.sv @@ -0,0 +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-2023, Lars Asplund lars.anders.asplund@gmail.com + +`include "vunit_defines.svh" + +interface axis_intf #(parameter int DW=8) (input logic clk); + + logic tready; + logic tvalid; + logic [DW-1:0] tdata; + + modport master (input tready, output tvalid, tdata); + modport slave (output tready, input tvalid, tdata); + + task automatic send(int word); + tvalid <= 1'b1; + tdata <= word; + //words.push_back(word); + @(posedge clk iff tvalid == 1'b1 && tready == 1'b1); + $info($sformatf("AXIs: Sent word x%0h", word)); + tvalid <= 1'b0; + endtask // send + + task automatic check (int word); + tready <= 1'b1; + do + @(posedge clk); + while (tvalid !=1); + `CHECK_EQUAL(tdata, word); + tready <= 1'b0; + @(posedge clk); + `CHECK_EQUAL(tvalid, 1'b0); + endtask // receive + +endinterface // axis_intf + diff --git a/examples/mixed/uart/src/bit_intf.sv b/examples/mixed/uart/src/bit_intf.sv new file mode 100644 index 000000000..c6c855328 --- /dev/null +++ b/examples/mixed/uart/src/bit_intf.sv @@ -0,0 +1,46 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2023, Lars Asplund lars.anders.asplund@gmail.com + +`include "vunit_defines.svh" + +interface bit_intf (input clk); + + logic rx_tx; + + modport master (output rx_tx); + modport slave (input rx_tx); + + task automatic uart_send(input integer data, baud_rate); + integer time_per_bit; + time_per_bit = (10**9 / baud_rate); + rx_tx = 1'b0; + #(time_per_bit * 1ns); + + for (int i=0; i<8; i++) begin + rx_tx = data[i]; + #(time_per_bit * 1ns); + end + + rx_tx = 1'b1; + #(time_per_bit * 1ns); + endtask // uart_send + + task automatic check(output integer data, input int time_per_bit); + data = 0; + wait(rx_tx == 1'b0); + #(time_per_bit/2 * 1ns); + `CHECK_EQUAL(rx_tx, 1'b0, "Expected low rx_tx"); + #(time_per_bit * 1ns); + for (int i=0; i<8; i++) begin + data[i] = rx_tx; + #(time_per_bit * 1ns); + end + `CHECK_EQUAL(rx_tx, 1'b1, "Expected high rx_tx"); + #(time_per_bit / 2 * 1ns); + endtask + + +endinterface // bit_intf diff --git a/examples/mixed/uart/src/test/tb_uart_mix.sv b/examples/mixed/uart/src/test/tb_uart_mix.sv new file mode 100644 index 000000000..5a5f855af --- /dev/null +++ b/examples/mixed/uart/src/test/tb_uart_mix.sv @@ -0,0 +1,103 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2014-2023, Lars Asplund lars.anders.asplund@gmail.com + +`include "vunit_defines.svh" + +module tb_uart_mix; + + 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; + + axis_intf #(.DW(8)) m_axis (clk); + axis_intf s_axis (clk); + + bit_intf bit_if(clk); + + logic overflow; + + int uart_data; + + int num_overflows = 0; + + `TEST_SUITE begin + `TEST_CASE("test_tvalid_low_at_start") begin + fork : tvalid_low_check + begin + wait (s_axis.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_send_receive_one_byte") begin + fork + m_axis.send(8'h77); + s_axis.check(8'h77); + bit_if.check(uart_data, time_per_bit); + join + `CHECK_EQUAL(num_overflows, 0) + end + + `TEST_CASE("test_two_bytes_cause_overflow") begin + fork + m_axis.send(8'h77); + bit_if.check(uart_data, time_per_bit); + join + `CHECK_EQUAL(uart_data, 8'h77) + @(posedge clk iff s_axis.tvalid == 1'b1); + `CHECK_EQUAL(num_overflows, 0) + fork + m_axis.send(8'h77); + bit_if.check(uart_data, time_per_bit); + join + `CHECK_EQUAL(uart_data, 8'h77) + `CHECK_EQUAL(num_overflows, 1); + end + end + + `WATCHDOG(10ms); + + initial begin + m_axis.tvalid = 0; + s_axis.tready = 0; + end + + always @(posedge clk iff overflow == 1'b1) begin + num_overflows <= num_overflows + 1; + end + + always begin + #(clk_period/2 * 1ns); + clk <= !clk; + end + + uart_tx #(.cycles_per_bit(cycles_per_bit)) + dut_tx + (.clk (clk), + .tx (bit_if.rx_tx), + .tready (m_axis.tready), + .tvalid (m_axis.tvalid), + .tdata (m_axis.tdata)); + + uart_rx #(.cycles_per_bit(cycles_per_bit)) + dut_rx + (.clk (clk), + .rx (bit_if.rx_tx), + .overflow (overflow), + .tready (s_axis.tready), + .tvalid (s_axis.tvalid), + .tdata (s_axis.tdata)); + +endmodule // tb_uart_mix diff --git a/tests/acceptance/test_external_run_scripts.py b/tests/acceptance/test_external_run_scripts.py index e7eab8c17..6983a5286 100644 --- a/tests/acceptance/test_external_run_scripts.py +++ b/tests/acceptance/test_external_run_scripts.py @@ -59,6 +59,10 @@ def test_verilog_user_guide_example_project(self): def test_verilog_uart_example_project(self): self.check(ROOT / "examples/verilog/uart/run.py") + @mark.skipif(not simulator_supports_verilog(), reason="Requires a Verilog simulator") + def test_mixed_uart_example_project(self): + self.check(ROOT / "examples/mixed/uart/run.py") + @mark.skipif(not simulator_supports_verilog(), reason="Requires a Verilog simulator") @mark.xfail(reason="Requires AMS") def test_verilog_ams_example(self): diff --git a/vunit/parsing/verilog/parser.py b/vunit/parsing/verilog/parser.py index 89432cec7..e099be6ba 100644 --- a/vunit/parsing/verilog/parser.py +++ b/vunit/parsing/verilog/parser.py @@ -27,10 +27,12 @@ COLON, COMMENT, END, + ENDINTERFACE, ENDMODULE, DOUBLE_COLON, HASH, IDENTIFIER, + INTERFACE, IMPORT, MODULE, MULTI_COMMENT, @@ -288,7 +290,7 @@ def _parse_block_label(stream): class VerilogModule(object): """ - A verilog module + A verilog module (or interface) """ def __init__(self, name, parameters): @@ -319,13 +321,13 @@ def find(cls, tokens): results = [] parameters = [] while idx < len(tokens): - if tokens[idx].kind == MODULE: + if tokens[idx].kind in (INTERFACE, MODULE): if balance == 0: name = tokens[idx + 1].value parameters = [] balance += 1 - elif tokens[idx].kind == ENDMODULE: + elif tokens[idx].kind in (ENDINTERFACE, ENDMODULE): balance -= 1 if balance == 0: results.append(cls(name, parameters)) diff --git a/vunit/project.py b/vunit/project.py index 27d4d5533..e6c91e6e5 100644 --- a/vunit/project.py +++ b/vunit/project.py @@ -358,7 +358,7 @@ def _find_verilog_package_dependencies(self, source_file): def _find_verilog_module_dependencies(self, source_file): """ - Find dependencies from instantiation of verilog modules + Find dependencies from instantiation of verilog modules or vhdl entities """ for module_name in source_file.module_dependencies: if module_name in source_file.library.modules: @@ -370,7 +370,12 @@ def _find_verilog_module_dependencies(self, source_file): design_unit = library.modules[module_name] yield design_unit.source_file except KeyError: - pass + try: + # VHDL? + design_unit = library.primary_design_units[module_name] + yield design_unit.source_file + except KeyError: + pass @staticmethod def _find_component_design_unit_dependencies(source_file):