RTL Bench PRO

Verilog and VHDL in one design

RTL Bench elaborates VHDL with GHDL and Verilog with Yosys, then stitches the two netlists into a single hierarchy. You can mix languages freely in one project — a SystemVerilog top instantiating a VHDL entity, or the other way round.

When you actually need this

Most new designs are single-language. You pick Verilog or VHDL and stay there. Mixed-language comes up when:

  • You are integrating third-party IP. A vendor ships a VHDL block, your project is Verilog, and rewriting it is not an option.
  • You inherited a legacy design. The old blocks are VHDL, the new ones are SystemVerilog, and both have to compile together.
  • Your team is split. One group writes VHDL for safety-critical blocks, another uses SystemVerilog for verification-heavy logic.
  • You are learning. You want to see how the same design looks in both languages side by side.
💡 If you are starting fresh and have no integration constraints, pick one language. Mixed-language projects work, but they add elaboration complexity and one more class of bug to debug.

How RTL Bench handles it

When you upload a folder with mixed files, RTL Bench sorts them by extension and routes each group to the right backend:

Extension Parser Output
.v Yosys read_verilog -sv Yosys RTLIL netlist
.sv Yosys read_verilog -sv Yosys RTLIL netlist
.vhd GHDL via ghdl --std=08 Converted to RTLIL through the Yosys GHDL plugin
.vhdl GHDL via ghdl --std=08 Converted to RTLIL through the Yosys GHDL plugin

Once both are RTLIL, Yosys sees one design. Cross-language instantiation works both ways because the hierarchy is resolved by name, not by language.

A working example

Three files. Two VHDL, one SystemVerilog. Upload all three at once, and the SystemVerilog top instantiates the VHDL entity.

1. VHDL package — alu_pkg.vhd

library ieee;
use ieee.std_logic_1164.all;

package alu_pkg is
    constant WIDTH : integer := 8;
end package alu_pkg;

2. VHDL entity — alu_core.vhd

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.alu_pkg.all;

entity alu_core is
    port (
        a    : in  std_logic_vector(WIDTH-1 downto 0);
        b    : in  std_logic_vector(WIDTH-1 downto 0);
        op   : in  std_logic_vector(1 downto 0);
        y    : out std_logic_vector(WIDTH-1 downto 0)
    );
end entity alu_core;

architecture rtl of alu_core is
begin
    with op select
        y <= std_logic_vector(unsigned(a) + unsigned(b)) when "00",
             std_logic_vector(unsigned(a) - unsigned(b)) when "01",
             a and b                                            when "10",
             a or  b                                            when others;
end architecture rtl;

3. SystemVerilog top — top.sv

module top (
    input  logic [7:0] a,
    input  logic [7:0] b,
    input  logic [1:0] op,
    output logic [7:0] y
);
    // VHDL entity instantiated from Verilog
    alu_core u_alu (
        .a (a),
        .b (b),
        .op(op),
        .y (y)
    );
endmodule

Upload all three files, click Generate. The schematic shows top at the root and u_alu as a submodule with ports matching the VHDL entity. The TOP: dropdown auto-selects top because it is the only module that nothing else instantiates.

ℹ️ RTL Bench does not require you to declare which file uses which language. Extensions are enough. The tool sorts them by suffix before invoking either backend.

Type mapping across languages

When a Verilog module instantiates a VHDL entity, RTL Bench maps the types on the port list. The rules are simple but worth memorising:

VHDL Verilog / SystemVerilog Notes
std_logic logic 1-bit signal. Both are 4-state (0, 1, X, Z).
std_logic_vector(N-1 downto 0) logic [N-1:0] Bit order must match. downto maps to big-endian bits.
unsigned(N-1 downto 0) logic [N-1:0] Only a type hint in VHDL. On the port list it is still a vector.
signed(N-1 downto 0) logic [N-1:0] Same as unsigned — signedness is not tracked at the port boundary.
integer — Not legal as a port type. Use std_logic_vector or a generic.
boolean — Not legal as a port type. Use std_logic.
⚠️ VHDL ports declared with integer or boolean fail to elaborate in a mixed-language project. Convert them to std_logic_vector in the VHDL source before mixing.

Case sensitivity — the #1 bug

VHDL is case-insensitive. Verilog is case-sensitive. When the two meet, casing matters on the Verilog side and must exactly match what GHDL reports.

This is fine:

-- VHDL
entity ALU_Core is ... end entity;

// Verilog — same casing
ALU_Core u_alu ( ... );

// Verilog — all lowercase also works
alu_core u_alu ( ... );

This fails silently until elaboration:

// Verilog — mixed casing that matches neither
Alu_Core u_alu ( ... );

Yosys looks for a module called Alu_Core, does not find it, and reports "module not found". GHDL does not know about Verilog's case rules, so the VHDL side has already been elaborated into a netlist with whatever casing the entity was declared with.

💡 Pick one convention and enforce it: either declare all VHDL entities in lower_snake_case (matching Verilog habits), or declare them in PascalCase and use that exact casing in every Verilog instantiation. Do not mix within a project.

Generics and parameters

A VHDL generic and a Verilog parameter are the same idea — a value passed at elaboration. But they are not interchangeable across the language boundary in RTL Bench.

  • VHDL generic with a default. Works. The default is used when the entity is instantiated from Verilog.
  • VHDL generic without a default. Fails to elaborate. GHDL cannot resolve the value, and the Verilog side has no syntax to pass a generic anyway.
  • Verilog parameter overridden from VHDL. Not supported. VHDL has no direct syntax for Verilog's parameter-override mechanism.
-- Good: default value
entity alu_core is
    generic ( WIDTH : integer := 8 );
    port ( ... );
end entity;

-- Bad: no default — mixed-language instantiation fails
entity alu_core is
    generic ( WIDTH : integer );
    port ( ... );
end entity;

What does not work

Honest limitations of the mixed-language flow in RTL Bench:

  • Passing generics across the boundary. Verilog cannot override a VHDL generic. Every mixed-language generic must have a default.
  • Sharing a package with Verilog. VHDL packages exist only inside VHDL. Verilog has no way to use them.
  • VHDL record ports. Records cannot cross into Verilog. Flatten them into separate ports or a std_logic_vector.
  • VHDL integer and boolean ports. Not legal across the boundary. Convert to std_logic or std_logic_vector.
  • Signals and variables shared across languages. Only ports cross the boundary. Internal signals stay inside their language.
  • Verification constructs. VHDL assert and SystemVerilog assert are ignored by the synthesis flow. They only run in a simulator, and the auto-generated testbench does not exercise them.

Uploading the files

The upload flow is identical to a single-language project:

  1. Click 📁 Folder in the top bar.
  2. Select a folder containing every file — .v, .sv, .vhd, .vhdl, and any .hex or .mem data files.
  3. RTL Bench preserves the folder structure and the relative paths. `include directives and $readmemh calls resolve against the working directory.
  4. Click Generate. The TOP dropdown auto-selects the module or entity that nothing else instantiates.

See Multi-file projects for the full upload and top-detection walkthrough.

When to not mix languages

Mixed-language works but costs you:

  • Two syntaxes to debug. Every build failure could be a Verilog parse error or a VHDL elaboration error.
  • Casing bugs. The most common failure mode, described above.
  • Type mapping friction. Every VHDL port must use a subset of types that Verilog can understand.
  • Readability. A new engineer reading the file tree has to context-switch between two languages.

If you have a choice, pick one language and stick with it. If you do not — because the IP is fixed, or the team is split — RTL Bench's mixed-language flow is genuinely usable for small and medium designs.

Frequently asked questions

Can you mix Verilog and VHDL in the same design?

Yes. RTL Bench parses VHDL with GHDL and Verilog or SystemVerilog with Yosys, then synthesizes both into a unified internal netlist. You can instantiate a VHDL entity inside a Verilog module or instantiate a Verilog module inside a VHDL architecture.

How does VHDL std_logic map to Verilog?

std_logic maps to a 1-bit logic signal, std_logic_vector maps to a packed vector ([N-1:0]), and integer generics map to Verilog parameters. Unconstrained arrays and user-defined records should be flattened to std_logic_vector before crossing language boundaries.

Why does my VHDL entity not connect to my Verilog instance?

Case sensitivity is the most common cause. VHDL is case-insensitive, so an entity named my_core will be exported in lowercase. Verilog is case-sensitive — instantiating My_Core will result in an unresolved reference. Always use lowercase port and module names across the boundary.

Next

Try it in the workbench →