Patrones inesperados en Verilog $ random

3

Tengo un banco de pruebas Verilog que supervisa un bus de 64 bits y debo programar aleatoriamente un bit volteado (corrupción de paquetes) para que suceda cada paquete 1 en X. Me sorprendió descubrir que no inyectaba ningún tipo de corrupción, y al parecer esto se debe a los patrones en $ aleatorios?

Módulo Verilog, colocado en línea con el bus:

module test_cable  #(
    parameter EMPTY_BITS = 3,
    parameter DATA_BITS = 64
) (
input wire         clk,

input wire [DATA_BITS-1:0]   eth_c2cable_data,
input wire                   eth_c2cable_valid,
input wire                   eth_c2cable_sop,
input wire [EMPTY_BITS-1:0]  eth_c2cable_empty,
input wire                   eth_c2cable_eop,
input wire                   eth_c2cable_error,
output wire                  eth_c2cable_ready,

output wire [DATA_BITS-1:0]  eth_cable2s_data,
output wire                  eth_cable2s_valid,
output wire                  eth_cable2s_sop,
output wire [EMPTY_BITS-1:0] eth_cable2s_empty,
output wire                  eth_cable2s_eop,
output wire                  eth_cable2s_error,
input wire                   eth_cable2s_ready
);

// Corruption control task
// 2'b00  clean
// 2'b01  corrupt 1 pkt in 2^6
// 2'b10  corrupt 1 pkt in 2^7
// 2'b11  corrupt 1 pkt in 2^8
reg [1:0]  ready_control = 2'b00;
reg [31:0] random_next = 0;

task setCorruptControl;
    input [1:0] bitf;
    begin
        ready_control <= bitf;
    end
endtask

// ready bit control
reg [7:0] corrupt_at_cycle = 0;
wire [31:0] random_mask = (1'b1 << (6+ready_control))-1;
always @(posedge clk) begin
    random_next   <= $random;

    if ((eth_c2cable_sop && eth_c2cable_valid) && ready_control > 0 && (random_next & random_mask) == 32'b0) begin
        // corrupt this packet at some point. we don't know how big packet is, so somewhere in first 16 cycles
        // if the packet stops before we corrupt, we'll hit the next one
        corrupt_at_cycle <= random_next[24 +: 4];
        $display("Scheduling corruption %t for +%d cycles %08x", $time, random_next[24 +: 4], random_next);

    end else if (corrupt_at_cycle > 0 && eth_c2cable_valid) begin
        corrupt_at_cycle <= corrupt_at_cycle - 1'b1;
    end

    if (eth_c2cable_corruption != 0) begin
        $display("Corrupting wire packet with noise %016x", eth_c2cable_corruption);
    end
end

// corrupt by flipping single bit in this cycle
wire [63:0] eth_c2cable_corruption = corrupt_at_cycle == 1 ? { 1'b1 << random_next[24 +: 5] } : 64'b0;

assign eth_cable2s_data   = eth_c2cable_data ^ eth_c2cable_corruption;
assign eth_cable2s_valid  = eth_c2cable_valid;
assign eth_cable2s_sop    = eth_c2cable_sop;
assign eth_cable2s_eop    = eth_c2cable_eop;
assign eth_cable2s_empty  = eth_c2cable_empty;
assign eth_cable2s_error  = eth_c2cable_error;
assign eth_c2cable_ready  = eth_cable2s_ready;

endmodule

La corrupción de la salida de simulación siempre se programa en cero , por lo que nunca sucede:

...
Scheduling corruption            882531000 for + 0 cycles 40002780
Scheduling corruption            887190000 for + 0 cycles 002edf00
Scheduling corruption            894192000 for + 0 cycles 4049c380
Scheduling corruption            907395000 for + 0 cycles c0270a80
Scheduling corruption            948746000 for + 0 cycles 4010dd80
Scheduling corruption            959389000 for + 0 cycles 40155b80
Scheduling corruption            964957000 for + 0 cycles c0357a80
...
                                                           ^     ^

Esto parece deberse a que los bits random_next [24 +: 4] y random_next [0 +: 6] coinciden con cero. Elegí arbitrariamente los bits de 24 con la esperanza de que no tuvieran correlación con los bits bajos. ¿Es esto una debilidad con el algoritmo de número pseudoaleatorio Verilog, o un problema con el simulador Icarus Verilog?

Después de mucha búsqueda de errores, modifiqué las incidencias de random_next[24 +: 4] a random_next[28 +: 4] y el banco de pruebas funciona.

    
pregunta shuckc

2 respuestas

2

He escrito un pequeño banco de pruebas que demuestra el problema de forma independiente:

module testbench;

integer i, j = 0;
reg [31:0] randval;

initial begin
    for (i = 0; i < 100000; i = i+1) begin
        randval = $random;
        if (randval[0 +: 6] == 0) begin
            $display("%8d %6d: %x -> %d %d", i, j, randval, randval[0 +: 6], randval[24 +: 4]);
            if (randval[24 +: 4] != 0) $finish;
            j = j + 1;
        end
    end
    $display("IEEE Std 1364-2005 $random (rtl_dist_uniform(seed, LONG_MIN, LONG_MAX)) sucks!");
end

endmodule

Lo realmente interesante aquí es que el comportamiento exacto de $ random se define en IEEE Std 1364-2005. En realidad, hay un código C en la norma (páginas 317 y 318). No quiero citarlo aquí en su totalidad, pero se puede encontrar una copia del código, por ejemplo, en Icarus Verilog:

enlace

Llamar a $ random en Verilog es igual a llamar a rtl_dist_uniform(&a_seed, INT_MIN, INT_MAX) en este archivo C.

Entonces, lo que demostró aquí no es solo una debilidad en $ aleatoria de un solo simulador en particular, sino una debilidad en $ aleatoria en general.

Una solución sería simplemente implementar su propio generador de números aleatorios. A menudo utilizo la familia Xorshift de generadores de números aleatorios porque funcionan bastante bien y son fáciles de implementar:

enlace

El siguiente ejemplo utiliza un xorshift de 64 bits para generar números de 32 bits:

module testbench;

reg [63:0] xorshift64_state = 64'd88172645463325252;

task xorshift64_next;
    begin
        // see page 4 of Marsaglia, George (July 2003). "Xorshift RNGs". Journal of Statistical Software 8 (14).
        xorshift64_state = xorshift64_state ^ (xorshift64_state << 13);
        xorshift64_state = xorshift64_state ^ (xorshift64_state >>  7);
        xorshift64_state = xorshift64_state ^ (xorshift64_state << 17);
    end
endtask

integer i;
initial begin
    for (i = 0; i < 100; i = i+1) begin
        xorshift64_next;
        $display("%x", xorshift64_state[31:0]);
    end
end

endmodule
    
respondido por el CliffordVienna
0

Vale la pena mencionar que he encontrado que Icarus Verilog incluye un generador de números aleatorios no estándar como integrado:

$mti_random()
$mti_dist_uniform
These functions are similar to the IEEE1364 standard $random
functions, but they use the Mersenne Twister (MT19937)
algorithm. This is considered an excellent random number
generator, but does not generate the same sequence as the
standardized $random.
    
respondido por el shuckc

Lea otras preguntas en las etiquetas