SystemVerilog nace para solucionar las limitaciones de Verilog cuando los diseños digitales empiezan a crecer en tamaño (SoC, CPUs, GPUs) y complejidad (pipelines, múltiples dominios de reloj) además de difíciles de verificar con Verilog clásico.
Por eso, a principios de los 2000 Accellera combina Verilog + Superlog (Synopsys) y en 2005 se estandariza como IEEE 1800. A partir de ahí SystemVerilog sustituye a Verilog como estándar moderno. Actualmente Verilog 2001 prácticamente no evoluciona; SystemVerilog es el estándar vivo.
SystemVerilog no reemplaza Verilog -todo Verilog 2001 es compatible con SystemVerilog- si no que lo extiende añadiendo construcciones de diseño y nuevas herramientas de verificación con características de lenguaje de programación moderno.
Mejor lenguaje de diseño añadiendo nuevos tipos de variables, por ejemplo las tipo logic que evita errores de múltiples drivers (wire vs reg) permitiendo un código más limpio y seguro. Añade también nuevo bloques always_comb, always_ff, always_latch separando lógica secuencial y combinacional y permitiendo detectar errores de diseño automáticamente.
Parametrización y reutilización; parámetros tipados e interfaces.
Verificación potente (gran salto); aquí está la mayor razón de su existencia.
Código más legible y fácil de mantener.
Como circuito Hola Mundo describiremos un multiplexor 2x1 y otro multiplexor 4x1 instanciando el primero.
Nótese las variables tipo logic frente al tradicional wire de Verilog
module mux_2x1(
input logic in0, in1, sel,
output logic out
);
logic w0, w1;
assign out = w0 | w1;
assign w0 = in0 & ~sel;
assign w1 = in1 & sel;
endmodule
La forma de instanciar no cambia respecto a Verilog.
module mux_4x1(
logic in0, in1, in2, in3, // INPUTS
logic sel0, sel1, // SELECTION
logic out); // OUTPUT
logic w0, w1;
mux_2x1 M0 (.in0(in0), .in1(in1), .sel(sel0), .out(w0));
mux_2x1 M1 (.in0(in2), .in1(in3), .sel(sel0), .out(w1));
mux_2x1 M2 (.in0(w0), .in1(w1), .sel(sel1), .out(out));
endmodule
Este nuevo tipo de bloques always son una de las novedades de SystemVerilog.
Las ventajas son numerosas, por ejemplo, no hay que declarar la lista de sensibilidades (lo hace el simulador automáticamente), detecta latches, mejora la legibilidad del código, detecta errores, etc.
Nota: en bloques combinacionales hay que emplear asignaciones bloking "=" y las asignaciones a variables se deben hacer en un único bloque always.
// always_comb + procedural assignment
module mux_2x1 (
input logic in0, // entrada 0
input logic in1, // entrada 1
input logic sel, // selector
output logic out // salida
);
logic w0, w1;
always_comb begin
out = w0 | w1;
w0 = in0 & ~sel;
w1 = in1 & sel;
end
endmodule
Nota: en declaraciones if-else y case hay que listar todos los casos posibles (sel = 0 y sel = 1) y en todos ellos asignar un valor a la variable que corresponda, en este caso out.
Si hay algún caso en que la variable no recibe asignación, se puede inicializar como out = 0 (o lo que corresponda) justo después del begin.
// always_comb + declaración case
module mux_2x1 (
input logic in0, // entrada 0
input logic in1, // entrada 1
input logic sel, // selector
output logic out // salida
);
always_comb begin
case (sel)
1'b0: out = in0;
1'b1: out = in1;
default: out = in0; // defensivo
endcase
end
endmodule
// always_comb + declaración if-else
module mux_2x1 (
input logic in0, // entrada 0
input logic in1, // entrada 1
input logic sel, // selector
output logic out // salida
);
always_comb begin
if (sel)
out = in1;
else
out = in0;
end
endmodule
Al igual que hemos visto en Verilog, las declaraciones if-else implican prioridad mientras que una declaracion case se evalua en paralelo.
El bloque always_ff en SystemVerilog es un tipo especial de bloque always que se usa exclusivamente para describir lógica secuencial, es decir, flip-flops y registros.
Nota: en bloques secuenciales hay que emplear asignaciones non-bloking "<=".
Los siguientes ejemplos muestran cómo describir un Flip-Flop tipo D mediante bloque always_ff.
module d_ff(
input logic d,
input logic clk,
output logic q
);
always_ff @(posedge clk)
begin
q <= d;
end
endmodule
// Filp-Flop tipo D con reset y enable
module d_ff_enable(
input logic d, clk, reset, en,
output logic q
);
//always_ff @(posedge clk) // synchronous reset
always_ff @(posedge clk, posedge reset) // asynchronous reset
begin
if (reset)
q <= 0;
else if (en)
q <= d;
end
endmodule
// Filp-Flop tipo D con reset síncrono / asíncrono
odule d_ff_reset(
input logic d, clk, reset,
output logic q
);
//always_ff @(posedge clk) // synchronous resert
always_ff @(posedge clk, posedge reset) // asynchronous reset
begin
if (reset)
q <= 0;
else
q <= d;
end
endmodule