3
\$\begingroup\$

I have a Verilog test bench that monitors a 64-bit bus and should randomly schedule a flipped bit (packet corruption) to happen every 1-in-X packets. I was surprised to find it not injecting any corruption, and seemingly this is due to patterns in $random ?

Verilog module, placed inline with 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

Looking at the simulation output corruption is always scheduled to occur in zero cycles, so never happens:

...
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
...
 ^ ^

This seems to be caused by bits random_next[24 +: 4] and random_next[0 +: 6] being zero coincidently. I arbitrarily chose the bits from 24 hoping they would have no correlation with the low bits. Is this a weakness with the Verilog pseudo-random number algorithm, or a problem with the Icarus Verilog simulator?

After much bug-hunting, I modified occurrences of random_next[24 +: 4] to random_next[28 +: 4] and the test bench works.

asked Jan 15, 2014 at 19:05
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

I have written a little testbench that demonstrates the problem stand-alone:

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

The really interesting thing here is that the exact behavior of $random is defined in IEEE Std 1364-2005. There is actually C code in the standard (pages 317 and 318). I don't want to quote it here in full, but a copy of the code can be found for example in Icarus Verilog:

https://github.com/steveicarus/iverilog/blob/master/vpi/sys_random.c

Calling $random in Verilog equals to calling rtl_dist_uniform(&a_seed, INT_MIN, INT_MAX) in this C file.

So what you demonstrated here is not only a weakness in $random of just one particular simulator, but a weakness in $random in general.

One solution would be to simply implement your own random number generator. I often use the Xorshift family of random number generators because they work fairly well and are easy to implement:

http://en.wikipedia.org/wiki/Xorshift

The following example uses a 64 bit xorshift to generate 32 bit numbers:

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
answered Jan 17, 2014 at 14:10
\$\endgroup\$
2
  • \$\begingroup\$ Thanks for the succinct testbench and confirmation! I observed the same behaviour in ModelSim and agree the weakness is in $random as defined in the Verilog spec. Thanks for the xorshit64 module, I'll give that a go - clearly fuzz testing with vanilla $random could have subtle bugs. \$\endgroup\$ Commented Jan 17, 2014 at 14:40
  • \$\begingroup\$ I was going to raise a Mantis bug report for this but it's not clear who to contact. Anyone on the IEEE P1800 Working Group? \$\endgroup\$ Commented Jan 28, 2014 at 16:14
0
\$\begingroup\$

Worth mentioning I have found Icarus Verilog includes a non-standard random number generator as a built-in:

$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.
answered Dec 30, 2014 at 3:30
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.