I don’t want to get into the discussion of which is better for HDL programming VHDL or Verilog. I just want to compare some language constructs of the two. The reason I am writing this post is just to remember me and who reads it what language construct, block is equivalent to other, a kind of cheat sheet or etc.
Personally, I used VHDL for 10 years. I can say I am proficient in VHDL, I mean of course I don’t use all the keywords or specific language constructs but at least I can read other’s code without difficulty. Well, for the Verilog case, I can still read and understand most of it but I need to google sometimes to better understanding.
With my new role at a new company, I am assigned to look at new open-source synthesis capabilities. Yosys (Yosys Open SYnthesis Suite) is one of the open-source synthesis frameworks available:
https://github.com/YosysHQ/yosys
It basically takes Verilog files as input, makes synthesis operation and generates netlist files in one of the formats such as BLIF, EDIF or etc. Yosys framework only accepts Verilog-2005 design files, not VHDL nor SystemVerilog. Of course, if you want to use these languages you can get support from Yosyshq company with a price:
https://www.yosyshq.com/products-and-services
I will not cover all the blocks or keywords of both VHLD or Verilog. I will only look at synthesible constructs, not simulation properties. This is a list to remind me the basic constructs, so if you want you can add extra comparisons for building blocks in the comment section.
Before I start, I will refer some of the links that talk about the differences of VHDL and Verilog:
https://digilent.com/blog/battle-over-the-fpga-vhdl-vs-verilog-who-is-the-true-champ/
https://electronics.stackexchange.com/questions/16767/vhdl-vs-verilog
https://www.fpga4student.com/2017/08/verilog-vs-vhdl-explain-by-example.html
Let me tell the first word is a VHDL construct or keyword and the second is Verilog. Let’s start:
1) Package vs Include
You can define libraries and packages related to these libraries in VHDL. These packages can include type or subtype definitions, functions or procedures, namely subprograms. In Verilog, you can use `include compiler directive to tell the tool which file you want to include in this module. Most users find VHDL’s package capability is better than Verilog’s Include statement. There is nothing such package in Verilog. Library management is another good property of VHDL.
-- define a library in VHDL
library IEEE;
-- include a package in VHDL
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;
-- define a library in VHDL
library uvvm_util;
-- include a package in VHDL
use uvvm_util.i2c_bfm_pkg.all;
// including a module in Verilog
`include "verilog_functions.v"
2) Function vs Function
Functions are subprograms in both VHDL and Verilog. Like their keywords are the same, working principles are also the same. Functions in both languages does not accept time intervals, I mean no delay elements, they only synthesis combinational logic. Functions in both languages return a single value.
-- declare a function in a package decleration block in VHDL
function init_i2c_if_signals (
constant VOID : in t_void
) return t_i2c_if;
-- define the function in package body block in VHLD
function init_i2c_if_signals (
constant VOID : in t_void
) return t_i2c_if is
variable result : t_i2c_if;
begin
result.sda := 'Z';
result.scl := 'Z';
return result;
end function;
-- call the function in another VHDL module
use uvvm_util.i2c_bfm_pkg.all;
i2c_if <= init_i2c_if_signals(VOID);
// function definition inside a module in Verilog
module simple_function();
function myfunction;
input a, b, c, d;
begin
myfunction = ((a+b) + (c-d));
end
endfunction
endmodule
// call the function in another Verilog module
`include "myfunction.v"
assign f = (myfunction (a,b,c,d)) ? e :0;
3) Procedure vs Task
Procedures in VHDL and Tasks in Verilog are also subprograms like functions in both languages. Both procedure and task can have delay elements inside their scope, which is different than functions in both languages where delay element is prohibited. Procedures can also be declared and defined inside package scope and tasks can be defined in a module and can be called by another module by adding include directive. Both procedure and task can model both combinational and sequential behavior again different than functions where only combinational behavior is allowed. Both procedure and task can have multiple input and output ports but do not return a value.
My experience is function blocks in both languages find more usage area for synthesis, while procedure and task are mostly utilized for verification purposes.
Procedure overloading, which means having procedures with same name, but different input/output port definitions, is allowed in VHDL and highly utilized in verification packages like UVVM or OSVVM. I couldn’t find any example of Verilog task overloading, there are some discussions about function overloading for SystemVerilog on internet but they also argue if SV supports overloaded functions or not:
https://www.quora.com/Is-function-overloading-possible-in-SystemVerilog
https://stackoverflow.com/questions/68196646/how-to-overcome-function-overloading-in-system-verilog
The discussions, it is stated that Verilog is weakly-typed, so it can assign signals with different sizes, so it is not possible how compiler understand which is used. But as I said, I don’t have much experience with Verilog to be sure about this question. Up to my knowledge and research, I couldn’t find any evidence that Verilog supports function or task overloading. There are discussions about SystemVerilog function overloading by utilizing functions inside a class. However, Verilog does not support class based designs.
-- procedure decleration in a package in VHDL
procedure i2c_slave_transmit (
constant data : in t_byte_array;
constant msg : in string;
signal i2c_if : inout t_i2c_if;
constant scope : in string := C_SCOPE;
constant msg_id_panel : in t_msg_id_panel := shared_msg_id_panel;
constant config : in t_i2c_bfm_config := C_I2C_BFM_CONFIG_DEFAULT
);
-- procedure definition in a package body in VHDL
procedure i2c_slave_transmit(
constant data : in t_byte_array;
constant msg : in string;
signal i2c_if : inout t_i2c_if;
constant scope : in string := C_SCOPE;
constant msg_id_panel : in t_msg_id_panel := shared_msg_id_panel;
constant config : in t_i2c_bfm_config := C_I2C_BFM_CONFIG_DEFAULT
) is
begin
i2c_slave_transmit(data, msg,
i2c_if.scl, i2c_if.sda,
scope, msg_id_panel, config);
end procedure;
-- create a local copy of the procedure within another module/entity
procedure i2c_slave_transmit (
constant data : in std_logic_vector) is
begin
i2c_slave_transmit (
data => v_slave_data,
msg => "",
i2c_if => i2c_if,
scope => C_SCOPE,
msg_id_panel=> shared_msg_id_panel,
config => i2c_bfm_config
);
end;
-- calling procedure within another module/entity
v_slave_data(8*1-1 downto 0) := x"AB";
i2c_slave_transmit(v_slave_data);
// example is from asic-world.com
// mytask.v file
module simple_task();
task convert;
input [7:0] temp_in;
output [7:0] temp_out;
begin
temp_out = (9/5) *( temp_in + 32)
end
endtask
endmodule
// calling convert task within another module
module task_calling (temp_a, temp_b, temp_c, temp_d);
input [7:0] temp_a, temp_c;
output [7:0] temp_b, temp_d;
reg [7:0] temp_b, temp_d;
`include "mytask.v"
always @ (temp_a)
begin
convert (temp_a, temp_b);
end
always @ (temp_c)
begin
convert (temp_c, temp_d);
end
endmodule
4) Signal vs Reg/Wire
In VHDL, you define signals with “signal” keyword, whether this signal is a combinational wire connection or a FF. In Verilog, you define combinational wire connections with “wire” and sequential elements with “reg” keywords. Of course, defining a signal with “wire” or “reg” keyword doesn’t guarantee you that they become automatically combinational wire or sequential element, you have to be careful with wire and reg as beginners mostly make mistakes and have misunderstandings. Logic keyword comes with SystemVerilog and can be used instead of both wire and reg. Actually, in 2009 IEEE combined Verilog and SystemVerilog standards. I am trying to compare Verilog with VHDL by neglecting SystemVerilog constructs as Yosys, for example, only takes Verilog input files not SystemVerilog.
-- VHDL signal definitions
signal a : std_logic := '0';
signal b : std_logic_vector(7 downto 0) := (others => '0');
-- VHDL signal assignments
a <= '0';
b <= x"00";
// Verilog reg definition
reg [7:0] q_next;
// Verilog reg assignment
always @(posedge clk) begin
q_next <= 8'h00;
end
// wire definitions
wire F;
wire AB, CD, O;
// wire assignments
assign AB = A & B;
assign CD = C & D;
assign O = AB | CD;
assign F = ~O;
5) process vs always
proceses and always blocks are used to model combinational or sequential logic inside a module. The statements inside a process or always block evaluated sequentially. I mean, multiple assignments to a signal is allowed but the signal takes the latest assignment. You have to be careful for sensitivity lists of process and always blocks for simulation purposes.
SystemVerilog has 4 different always blocks: always, always_comb, always_ff and always_latch, but Verilog only has always so I only consider always block example:
-- VHDL sequential process blcok
P_SEQ : process (clk) begin
if (rising_edge(clk)) then
q <= d;
end if;
end process P_SEQ;
-- VHDL combinational process blcok
P_COMB : process (a,b,c) begin
d <= a and b or c;
end process P_COMB;
// Verilog sequential always block
always @(posedge clk) begin
q <= d;
end
// Verilog combinational always block
// @(*) covers all sensitivity list
always @(*) begin
d = a & b | c;
end
6) if-then-elsif-else-end if vs if-begin-else if-else-end
Inside a process or always block, if conditional block can be used to define selection(s) over condition(s). If-else clause usage nearly same in all languages with slightly different syntaxes:
P_IF : process(a,b,c,e,f) begin
if (e = '1') then
d <= a and b or c;
elsif (f = '1') then
d <= a or b and c;
else
d <= a and b and c;
end if;
end process P_IF;
always @(*) begin
if (e == 1) begin
d = a & b | c;
end
else if (f == 1) begin
d = a | b & c;
else
d = a & b & c;
end
end
7) case-end case vs case-endcase
case is another conditional block. Usage in both languages is similar:
P_CASE : process(counter,b,c,d) begin
case (counter) is
when 0 =>
a <= b;
when 1 =>
a <= c;
when others =>
a <= d;
end case;
end process P_CASE;
always @(*) begin
case (counter)
4'b0000 : a = b;
4'b0001 : a = c;
default : a = d;
endcase;
end
8) concurrent conditional assignments
You can also assign different values to a signal according to the condition outside of process or always blocks. Signal assignments outside of these blocks are concurrent assignments:
-- with-select conditional assignments
with sel select a <=
b when "0000",
c when "0001",
d when others;
-- when-else conditional assignments
a <= b when sel = "0000" else
c when sel = "0001" else
d;
// Verilog conditional assignment
assign a = (sel == 4'b0000) ?
b : (sel == 4'b0001) ?
c : d;
9) generic vs parameter
Parametric design is an important aspect for RTL design. generic and parameter keywords are used to pass parameters to a module from a higher level:
-- VHDL Generic Example
entity param_reg is
generic (
N : integer := 8
);
port (
clk : in std_logic;
D : in std_logic_vector(N-1 downto 0);
Q : out std_logic_vector(N-1 downto 0)
);
end param_reg;
architecture rtl of param_reg is
begin
process (clk)
begin
if rising_edge(clk) then
Q <= D;
end if;
end process;
end architecture;
-- VHDL Generic Instantiation
D0 : param_reg
generic map (
N => 16
)
port map (
D => D,
Q => Q
);
// Verilog parameter example
module param_reg #(
parameter N = 8
) (
input clk,
input [N-1:0] D,
output [N-1:0] Q
);
always @(posedge clk) begin
Q <= D;
end
endmodule
// parametric module instantiation
param_reg #(16) D0 (D,Q);
10) entity vs module
I personally use “module” word to define verbally a block that has inputs and outputs, but in VHDL it is an entity, and in Verilog it is called as module, literally. Most synthesis tools support mixed language mode, which means you can instantiate Verilog modules in VHDL files or visa-versa. In a module, generic/parametric definitions and I/O signals are defined. The examples for both languages are:
-- VHDL entity
entity param_reg is
generic (
N : integer := 8
);
port (
clk : in std_logic;
D : in std_logic_vector(N-1 downto 0);
Q : out std_logic_vector(N-1 downto 0)
);
end param_reg;
// Verilog module
module param_reg #(
parameter N = 8
) (
input clk,
input [N-1:0] D,
output [N-1:0] Q
);
11) record vs ???
“Record” in VHDL is similar to “struct” in C. You can pack signals of different types in a record structure in a VHDL package file and easily access record parameters with ‘.’ by using this package in another module. Record is a very useful subject, used in both synthesis constructs and simulation constructs.
I researched record counterpart in Verilog. There is “typedef struct” construct like in C in SystemVerilog. However, pure Verilog does not support this building blocks. I found an example usage like records but not that efficient in web (https://www.edaboard.com/threads/substitute-for-records-structures-of-vhdl-in-verilog.347836/);
type Operation is record
OpCode : Bit_Vector(3 downto 0);
Op1, Op2, Res : Bit_Vector(1 downto 0);
end record;
instr : Operation;
instr.OpCode = "1010";
reg [9:0] instr; // width is the sum of all field widths
`define OpCode [9:6]
`define Op1 [5:4]
`define Op2 [3:2]
`define Res [1:0]
instr `Opcode = 'b1010;
Also I am not sure if you can define this fields in another Verilog file and use these names in another Verilog module like package and records in VHDL.
12) constant vs localparam
You can define constant values inside a module in VHDL by “constant” keyword and in Verilog by “localparam” keyword:
-- constant definition in VHDL
constant timer_lim : integer := 1_000;
// constant definition in Verilog
localparam timer_lim = 1000;
13) concat & vs concat (,)
Concatenation is done with & in VHDL and (,) in Verilog:
-- concat in VHDL
signal reg1 : std_logic_vector(1 downto 0);
signal reg2 : std_logic_vector(2 downto 0);
signal reg3 : std_logic;
signal reg4 : std_logic_vector(5 downto 0);
process (reg1,reg2,reg3) begin
reg4 <= reg1 & reg2 & reg3;
end process;
// concat in Verilog
wire [1:0] reg1;
wire [2:0] reg2;
wire reg3;
wire [5:0] reg4;
always @(*) begin
reg4 = (reg1,reg2,reg3);
end
I think most of the language keywords and constructs are covered in this post. I can enhance and edit the post in the future maybe, but for now this is enough for me. You can add extra comparisons in the comment section, which I will be very happy.
Regards,
Mehmet Burak AYKENAR
You can connect me via LinledIn: Just sent me an invitation
VHDL functions are much more powerful than Verilog functions. Verilog functions cannot be parameterized unlike VHDL functions.