-
Notifications
You must be signed in to change notification settings - Fork 813
/
Copy pathprim_lfsr.sv
633 lines (572 loc) · 24.6 KB
/
prim_lfsr.sv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// This module implements different LFSR types:
//
// 0) Galois XOR type LFSR ([1], internal XOR gates, very fast).
// Parameterizable width from 3 to 168 bits.
// Coefficients obtained from [3].
//
// 1) Fibonacci XNOR type LFSR, parameterizable from 3 to 168 bits.
// Coefficients obtained from [3].
//
// All flavors have an additional entropy input and lockup protection, which
// reseeds the state once it has accidentally fallen into the all-zero (XOR) or
// all-one (XNOR) state. Further, an external seed can be loaded into the LFSR
// state at runtime. If that seed is all-zero (XOR case) or all-one (XNOR case),
// the state will be reseeded in the next cycle using the lockup protection mechanism.
// Note that the external seed input takes precedence over internal state updates.
//
// All polynomials up to 34 bit in length have been verified in simulation.
//
// Refs: [1] https://en.wikipedia.org/wiki/Linear-feedback_shift_register
// [2] https://users.ece.cmu.edu/~koopman/lfsr/
// [3] https://www.xilinx.com/support/documentation/application_notes/xapp052.pdf
`include "prim_assert.sv"
module prim_lfsr #(
// Lfsr Type, can be FIB_XNOR or GAL_XOR
parameter LfsrType = "GAL_XOR",
// Lfsr width
parameter int unsigned LfsrDw = 32,
// Derived parameter, do not override
localparam int unsigned LfsrIdxDw = $clog2(LfsrDw),
// Width of the entropy input to be XOR'd into state (lfsr_q[EntropyDw-1:0])
parameter int unsigned EntropyDw = 8,
// Width of output tap (from lfsr_q[StateOutDw-1:0])
parameter int unsigned StateOutDw = 8,
// Lfsr reset state, must be nonzero!
parameter logic [LfsrDw-1:0] DefaultSeed = LfsrDw'(1),
// Custom polynomial coeffs
parameter logic [LfsrDw-1:0] CustomCoeffs = '0,
// If StatePermEn is set to 1, the custom permutation specified via StatePerm is applied to the
// state output, in order to break linear shifting patterns of the LFSR. Note that this
// permutation represents a way of customizing the LFSR via a random netlist constant. This is
// different from the NonLinearOut feature below which just transforms the output non-linearly
// with a fixed function. In most cases, designers should consider enabling StatePermEn as it
// comes basically "for free" in terms of area and timing impact. NonLinearOut on the other hand
// has area and timing implications and designers should consider whether the use of that feature
// is justified.
parameter bit StatePermEn = 1'b0,
parameter logic [LfsrDw-1:0][LfsrIdxDw-1:0] StatePerm = '0,
// Enable this for DV, disable this for long LFSRs in FPV
parameter bit MaxLenSVA = 1'b1,
// Can be disabled in cases where seed and entropy
// inputs are unused in order to not distort coverage
// (the SVA will be unreachable in such cases)
parameter bit LockupSVA = 1'b1,
parameter bit ExtSeedSVA = 1'b1,
// Introduce non-linearity to lfsr output. Note, unlike StatePermEn, this feature is not "for
// free". Please double check that this feature is indeed required. Also note that this feature
// is only available for LFSRs that have a power-of-two width greater or equal 16bit.
parameter bit NonLinearOut = 1'b0
) (
input clk_i,
input rst_ni,
input seed_en_i, // load external seed into the state (takes precedence)
input [LfsrDw-1:0] seed_i, // external seed input
input lfsr_en_i, // enables the LFSR
input [EntropyDw-1:0] entropy_i, // additional entropy to be XOR'ed into the state
output logic [StateOutDw-1:0] state_o // (partial) LFSR state output
);
// automatically generated with util/design/get-lfsr-coeffs.py script
localparam int unsigned LUT_OFF = 3;
localparam logic [167:0] LFSR_COEFFS [166] =
'{ 168'h6,
168'hC,
168'h14,
168'h30,
168'h60,
168'hB8,
168'h110,
168'h240,
168'h500,
168'h829,
168'h100D,
168'h2015,
168'h6000,
168'hD008,
168'h12000,
168'h20400,
168'h40023,
168'h90000,
168'h140000,
168'h300000,
168'h420000,
168'hE10000,
168'h1200000,
168'h2000023,
168'h4000013,
168'h9000000,
168'h14000000,
168'h20000029,
168'h48000000,
168'h80200003,
168'h100080000,
168'h204000003,
168'h500000000,
168'h801000000,
168'h100000001F,
168'h2000000031,
168'h4400000000,
168'hA000140000,
168'h12000000000,
168'h300000C0000,
168'h63000000000,
168'hC0000030000,
168'h1B0000000000,
168'h300003000000,
168'h420000000000,
168'hC00000180000,
168'h1008000000000,
168'h3000000C00000,
168'h6000C00000000,
168'h9000000000000,
168'h18003000000000,
168'h30000000030000,
168'h40000040000000,
168'hC0000600000000,
168'h102000000000000,
168'h200004000000000,
168'h600003000000000,
168'hC00000000000000,
168'h1800300000000000,
168'h3000000000000030,
168'h6000000000000000,
168'hD800000000000000,
168'h10000400000000000,
168'h30180000000000000,
168'h60300000000000000,
168'h80400000000000000,
168'h140000028000000000,
168'h300060000000000000,
168'h410000000000000000,
168'h820000000001040000,
168'h1000000800000000000,
168'h3000600000000000000,
168'h6018000000000000000,
168'hC000000018000000000,
168'h18000000600000000000,
168'h30000600000000000000,
168'h40200000000000000000,
168'hC0000000060000000000,
168'h110000000000000000000,
168'h240000000480000000000,
168'h600000000003000000000,
168'h800400000000000000000,
168'h1800000300000000000000,
168'h3003000000000000000000,
168'h4002000000000000000000,
168'hC000000000000000018000,
168'h10000000004000000000000,
168'h30000C00000000000000000,
168'h600000000000000000000C0,
168'hC00C0000000000000000000,
168'h140000000000000000000000,
168'h200001000000000000000000,
168'h400800000000000000000000,
168'hA00000000001400000000000,
168'h1040000000000000000000000,
168'h2004000000000000000000000,
168'h5000000000028000000000000,
168'h8000000004000000000000000,
168'h18600000000000000000000000,
168'h30000000000000000C00000000,
168'h40200000000000000000000000,
168'hC0300000000000000000000000,
168'h100010000000000000000000000,
168'h200040000000000000000000000,
168'h5000000000000000A0000000000,
168'h800000010000000000000000000,
168'h1860000000000000000000000000,
168'h3003000000000000000000000000,
168'h4010000000000000000000000000,
168'hA000000000140000000000000000,
168'h10080000000000000000000000000,
168'h30000000000000000000180000000,
168'h60018000000000000000000000000,
168'hC0000000000000000300000000000,
168'h140005000000000000000000000000,
168'h200000001000000000000000000000,
168'h404000000000000000000000000000,
168'h810000000000000000000000000102,
168'h1000040000000000000000000000000,
168'h3000000000000006000000000000000,
168'h5000000000000000000000000000000,
168'h8000000004000000000000000000000,
168'h18000000000000000000000000030000,
168'h30000000030000000000000000000000,
168'h60000000000000000000000000000000,
168'hA0000014000000000000000000000000,
168'h108000000000000000000000000000000,
168'h240000000000000000000000000000000,
168'h600000000000C00000000000000000000,
168'h800000040000000000000000000000000,
168'h1800000000000300000000000000000000,
168'h2000000000000010000000000000000000,
168'h4008000000000000000000000000000000,
168'hC000000000000000000000000000000600,
168'h10000080000000000000000000000000000,
168'h30600000000000000000000000000000000,
168'h4A400000000000000000000000000000000,
168'h80000004000000000000000000000000000,
168'h180000003000000000000000000000000000,
168'h200001000000000000000000000000000000,
168'h600006000000000000000000000000000000,
168'hC00000000000000006000000000000000000,
168'h1000000000000100000000000000000000000,
168'h3000000000000006000000000000000000000,
168'h6000000003000000000000000000000000000,
168'h8000001000000000000000000000000000000,
168'h1800000000000000000000000000C000000000,
168'h20000000000001000000000000000000000000,
168'h48000000000000000000000000000000000000,
168'hC0000000000000006000000000000000000000,
168'h180000000000000000000000000000000000000,
168'h280000000000000000000000000000005000000,
168'h60000000C000000000000000000000000000000,
168'hC00000000000000000000000000018000000000,
168'h1800000600000000000000000000000000000000,
168'h3000000C00000000000000000000000000000000,
168'h4000000080000000000000000000000000000000,
168'hC000300000000000000000000000000000000000,
168'h10000400000000000000000000000000000000000,
168'h30000000000000000000006000000000000000000,
168'h600000000000000C0000000000000000000000000,
168'hC0060000000000000000000000000000000000000,
168'h180000006000000000000000000000000000000000,
168'h3000000000C0000000000000000000000000000000,
168'h410000000000000000000000000000000000000000,
168'hA00140000000000000000000000000000000000000 };
logic lockup;
logic [LfsrDw-1:0] lfsr_d, lfsr_q;
logic [LfsrDw-1:0] next_lfsr_state, coeffs;
// Enable the randomization of DefaultSeed using DefaultSeedLocal in DV simulations.
`ifdef SIMULATION
`ifdef VERILATOR
localparam logic [LfsrDw-1:0] DefaultSeedLocal = DefaultSeed;
`else
logic [LfsrDw-1:0] DefaultSeedLocal;
logic prim_lfsr_use_default_seed;
initial begin : p_randomize_default_seed
if (!$value$plusargs("prim_lfsr_use_default_seed=%0d", prim_lfsr_use_default_seed)) begin
// 30% of the time, use the DefaultSeed parameter; 70% of the time, randomize it.
`ASSERT_I(UseDefaultSeedRandomizeCheck_A, std::randomize(prim_lfsr_use_default_seed) with {
prim_lfsr_use_default_seed dist {0:/7, 1:/3};})
end
if (prim_lfsr_use_default_seed) begin
DefaultSeedLocal = DefaultSeed;
end else begin
// Randomize the DefaultSeedLocal ensuring its not all 0s or all 1s.
`ASSERT_I(DefaultSeedLocalRandomizeCheck_A, std::randomize(DefaultSeedLocal) with {
!(DefaultSeedLocal inside {'0, '1});})
end
$display("%m: DefaultSeed = 0x%0h, DefaultSeedLocal = 0x%0h", DefaultSeed, DefaultSeedLocal);
end
`endif // ifdef VERILATOR
`else
localparam logic [LfsrDw-1:0] DefaultSeedLocal = DefaultSeed;
`endif // ifdef SIMULATION
////////////////
// Galois XOR //
////////////////
if (64'(LfsrType) == 64'("GAL_XOR")) begin : gen_gal_xor
// if custom polynomial is provided
if (CustomCoeffs > 0) begin : gen_custom
assign coeffs = CustomCoeffs[LfsrDw-1:0];
end else begin : gen_lut
assign coeffs = LFSR_COEFFS[LfsrDw-LUT_OFF][LfsrDw-1:0];
// check that the most significant bit of polynomial is 1
`ASSERT_INIT(MinLfsrWidth_A, LfsrDw >= $low(LFSR_COEFFS)+LUT_OFF)
`ASSERT_INIT(MaxLfsrWidth_A, LfsrDw <= $high(LFSR_COEFFS)+LUT_OFF)
end
// calculate next state using internal XOR feedback and entropy input
assign next_lfsr_state = LfsrDw'(entropy_i) ^ ({LfsrDw{lfsr_q[0]}} & coeffs) ^ (lfsr_q >> 1);
// lockup condition is all-zero
assign lockup = ~(|lfsr_q);
// check that seed is not all-zero
`ASSERT_INIT(DefaultSeedNzCheck_A, |DefaultSeedLocal)
////////////////////
// Fibonacci XNOR //
////////////////////
end else if (64'(LfsrType) == "FIB_XNOR") begin : gen_fib_xnor
// if custom polynomial is provided
if (CustomCoeffs > 0) begin : gen_custom
assign coeffs = CustomCoeffs[LfsrDw-1:0];
end else begin : gen_lut
assign coeffs = LFSR_COEFFS[LfsrDw-LUT_OFF][LfsrDw-1:0];
// check that the most significant bit of polynomial is 1
`ASSERT_INIT(MinLfsrWidth_A, LfsrDw >= $low(LFSR_COEFFS)+LUT_OFF)
`ASSERT_INIT(MaxLfsrWidth_A, LfsrDw <= $high(LFSR_COEFFS)+LUT_OFF)
end
// calculate next state using external XNOR feedback and entropy input
assign next_lfsr_state = LfsrDw'(entropy_i) ^ {lfsr_q[LfsrDw-2:0], ~(^(lfsr_q & coeffs))};
// lockup condition is all-ones
assign lockup = &lfsr_q;
// check that seed is not all-ones
`ASSERT_INIT(DefaultSeedNzCheck_A, !(&DefaultSeedLocal))
/////////////
// Unknown //
/////////////
end else begin : gen_unknown_type
assign coeffs = '0;
assign next_lfsr_state = '0;
assign lockup = 1'b0;
`ASSERT_INIT(UnknownLfsrType_A, 0)
end
//////////////////
// Shared logic //
//////////////////
assign lfsr_d = (seed_en_i) ? seed_i :
(lfsr_en_i && lockup) ? DefaultSeedLocal :
(lfsr_en_i) ? next_lfsr_state :
lfsr_q;
logic [LfsrDw-1:0] sbox_out;
if (NonLinearOut) begin : gen_out_non_linear
// The "aligned" permutation ensures that adjacent bits do not go into the same SBox. It is
// different from the state permutation that can be specified via the StatePerm parameter. The
// permutation taps out 4 SBox input bits at regular stride intervals. E.g., for a 16bit
// vector, the input assignment looks as follows:
//
// SBox0: 0, 4, 8, 12
// SBox1: 1, 5, 9, 13
// SBox2: 2, 6, 10, 14
// SBox3: 3, 7, 11, 15
//
// Note that this permutation can be produced by filling the input vector into matrix columns
// and reading out the SBox inputs as matrix rows.
localparam int NumSboxes = LfsrDw / 4;
// Fill in the input vector in col-major order.
logic [3:0][NumSboxes-1:0][LfsrIdxDw-1:0] matrix_indices;
for (genvar j = 0; j < LfsrDw; j++) begin : gen_input_idx_map
assign matrix_indices[j / NumSboxes][j % NumSboxes] = j;
end
// Due to the LFSR shifting pattern, the above permutation has the property that the output of
// SBox(n) is going to be equal to SBox(n+1) in the subsequent cycle (unless the LFSR polynomial
// modifies some of the associated shifted bits via an XOR tap).
// We therefore tweak this permutation by rotating and reversing some of the assignment matrix
// columns. The rotation and reversion operations have been chosen such that this
// generalizes to all power of two widths supported by the LFSR primitive. For 16bit, this
// looks as follows:
//
// SBox0: 0, 6, 11, 14
// SBox1: 1, 7, 10, 13
// SBox2: 2, 4, 9, 12
// SBox3: 3, 5, 8, 15
//
// This can be achieved by:
// 1) down rotating the second column by NumSboxes/2
// 2) reversing the third column
// 3) down rotating the fourth column by 1 and reversing it
//
logic [3:0][NumSboxes-1:0][LfsrIdxDw-1:0] matrix_rotrev_indices;
typedef logic [NumSboxes-1:0][LfsrIdxDw-1:0] matrix_col_t;
// left-rotates a matrix column by the shift amount
function automatic matrix_col_t lrotcol(matrix_col_t col, integer shift);
matrix_col_t out;
for (int k = 0; k < NumSboxes; k++) begin
out[(k + shift) % NumSboxes] = col[k];
end
return out;
endfunction : lrotcol
// reverses a matrix column
function automatic matrix_col_t revcol(matrix_col_t col);
return {<<LfsrIdxDw{col}};
endfunction : revcol
always_comb begin : p_rotrev
matrix_rotrev_indices[0] = matrix_indices[0];
matrix_rotrev_indices[1] = lrotcol(matrix_indices[1], NumSboxes/2);
matrix_rotrev_indices[2] = revcol(matrix_indices[2]);
matrix_rotrev_indices[3] = revcol(lrotcol(matrix_indices[3], 1));
end
// Read out the matrix rows and linearize.
logic [LfsrDw-1:0][LfsrIdxDw-1:0] sbox_in_indices;
for (genvar k = 0; k < LfsrDw; k++) begin : gen_reverse_upper
assign sbox_in_indices[k] = matrix_rotrev_indices[k % 4][k / 4];
end
`ifndef SYNTHESIS
// Check that the permutation is indeed a permutation.
logic [LfsrDw-1:0] sbox_perm_test;
always_comb begin : p_perm_check
for (int k = 0; k < LfsrDw; k++) begin
sbox_perm_test[sbox_in_indices[k]] = 1'b1;
end
end
// All bit positions must be marked with 1.
`ASSERT(SboxPermutationCheck_A, &sbox_perm_test)
`endif
`ifdef FPV_ON
// Verify that the permutation indeed breaks linear shifting patterns of 4bit input groups.
// The symbolic variables let the FPV tool select all sbox index combinations and linear shift
// offsets.
int shift;
int unsigned sk, sj;
`ASSUME(SjSkRange_M, (sj < NumSboxes) && (sk < NumSboxes))
`ASSUME(SjSkDifferent_M, sj != sk)
`ASSUME(SjSkStable_M, ##1 $stable(sj) && $stable(sk) && $stable(shift))
`ASSERT(SboxInputIndexGroupIsUnique_A,
!((((sbox_in_indices[sj * 4 + 0] + shift) % LfsrDw) == sbox_in_indices[sk * 4 + 0]) &&
(((sbox_in_indices[sj * 4 + 1] + shift) % LfsrDw) == sbox_in_indices[sk * 4 + 1]) &&
(((sbox_in_indices[sj * 4 + 2] + shift) % LfsrDw) == sbox_in_indices[sk * 4 + 2]) &&
(((sbox_in_indices[sj * 4 + 3] + shift) % LfsrDw) == sbox_in_indices[sk * 4 + 3])))
// this checks that the permutations does not preserve neighboring bit positions.
// i.e. no two neighboring bits are mapped to neighboring bit positions.
int y;
int unsigned ik;
`ASSUME(IkYRange_M, (ik < LfsrDw) && (y == 1 || y == -1))
`ASSUME(IkStable_M, ##1 $stable(ik) && $stable(y))
`ASSERT(IndicesNotAdjacent_A, (sbox_in_indices[ik] - sbox_in_indices[(ik + y) % LfsrDw]) != 1)
`endif
// Use the permutation indices to create the SBox layer
for (genvar k = 0; k < NumSboxes; k++) begin : gen_sboxes
logic [3:0] sbox_in;
assign sbox_in = {lfsr_q[sbox_in_indices[k*4 + 3]],
lfsr_q[sbox_in_indices[k*4 + 2]],
lfsr_q[sbox_in_indices[k*4 + 1]],
lfsr_q[sbox_in_indices[k*4 + 0]]};
assign sbox_out[k*4 +: 4] = prim_cipher_pkg::PRINCE_SBOX4[sbox_in];
end
end else begin : gen_out_passthru
assign sbox_out = lfsr_q;
end
// Random output permutation, defined at compile time
if (StatePermEn) begin : gen_state_perm
for (genvar k = 0; k < StateOutDw; k++) begin : gen_perm_loop
assign state_o[k] = sbox_out[StatePerm[k]];
end
// if lfsr width is greater than the output, then by definition
// not every bit will be picked
if (LfsrDw > StateOutDw) begin : gen_tieoff_unused
logic unused_sbox_out;
assign unused_sbox_out = ^sbox_out;
end
end else begin : gen_no_state_perm
assign state_o = StateOutDw'(sbox_out);
end
always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg
if (!rst_ni) begin
lfsr_q <= DefaultSeedLocal;
end else begin
lfsr_q <= lfsr_d;
end
end
///////////////////////
// shared assertions //
///////////////////////
`ASSERT_KNOWN(DataKnownO_A, state_o)
// the code below is not meant to be synthesized,
// but it is intended to be used in simulation and FPV
`ifndef SYNTHESIS
function automatic logic [LfsrDw-1:0] compute_next_state(logic [LfsrDw-1:0] lfsrcoeffs,
logic [EntropyDw-1:0] entropy,
logic [LfsrDw-1:0] current_state);
logic state0;
logic [LfsrDw-1:0] next_state;
next_state = current_state;
// Galois XOR
if (64'(LfsrType) == 64'("GAL_XOR")) begin
if (next_state == 0) begin
next_state = DefaultSeedLocal;
end else begin
state0 = next_state[0];
next_state = next_state >> 1;
if (state0) next_state ^= lfsrcoeffs;
next_state ^= LfsrDw'(entropy);
end
// Fibonacci XNOR
end else if (64'(LfsrType) == "FIB_XNOR") begin
if (&next_state) begin
next_state = DefaultSeedLocal;
end else begin
state0 = ~(^(next_state & lfsrcoeffs));
next_state = next_state << 1;
next_state[0] = state0;
next_state ^= LfsrDw'(entropy);
end
end else begin
$error("unknown lfsr type");
end
return next_state;
endfunction : compute_next_state
// check whether next state is computed correctly
// we shift the assertion by one clock cycle (##1) in order to avoid
// erroneous SVA triggers right after reset deassertion in cases where
// the precondition is true throughout the reset.
// this can happen since the disable_iff evaluates using unsampled values,
// meaning that the assertion may already read rst_ni == 1 on an active
// clock edge while the flops in the design have not yet changed state.
`ASSERT(NextStateCheck_A, ##1 lfsr_en_i && !seed_en_i |=> lfsr_q ==
compute_next_state(coeffs, $past(entropy_i), $past(lfsr_q)))
// Only check this if enabled.
if (StatePermEn) begin : gen_perm_check
// Check that the supplied permutation is valid.
logic [LfsrDw-1:0] lfsr_perm_test;
initial begin : p_perm_check
lfsr_perm_test = '0;
for (int k = 0; k < LfsrDw; k++) begin
lfsr_perm_test[StatePerm[k]] = 1'b1;
end
// All bit positions must be marked with 1.
`ASSERT_I(PermutationCheck_A, &lfsr_perm_test)
end
end
`endif
`ASSERT_INIT(InputWidth_A, LfsrDw >= EntropyDw)
`ASSERT_INIT(OutputWidth_A, LfsrDw >= StateOutDw)
// MSB must be one in any case
`ASSERT(CoeffCheck_A, coeffs[LfsrDw-1])
// output check
`ASSERT_KNOWN(OutputKnown_A, state_o)
if (!StatePermEn && !NonLinearOut) begin : gen_output_sva
`ASSERT(OutputCheck_A, state_o == StateOutDw'(lfsr_q))
end
// if no external input changes the lfsr state, a lockup must not occur (by design)
//`ASSERT(NoLockups_A, (!entropy_i) && (!seed_en_i) |=> !lockup, clk_i, !rst_ni)
`ASSERT(NoLockups_A, lfsr_en_i && !entropy_i && !seed_en_i |=> !lockup)
// this can be disabled if unused in order to not distort coverage
if (ExtSeedSVA) begin : gen_ext_seed_sva
// check that external seed is correctly loaded into the state
// rst_ni is used directly as part of the pre-condition since the usage of rst_ni
// in disable_iff is unsampled. See #1985 for more details
`ASSERT(ExtDefaultSeedInputCheck_A, (seed_en_i && rst_ni) |=> lfsr_q == $past(seed_i))
end
// if the external seed mechanism is not used,
// there is theoretically no way we end up in a lockup condition
// in order to not distort coverage, this SVA can be disabled in such cases
if (LockupSVA) begin : gen_lockup_mechanism_sva
// check that a stuck LFSR is correctly reseeded
`ASSERT(LfsrLockupCheck_A, lfsr_en_i && lockup && !seed_en_i |=> !lockup)
end
// If non-linear output requested, the LFSR width must be a power of 2 and greater than 16.
if(NonLinearOut) begin : gen_nonlinear_align_check_sva
`ASSERT_INIT(SboxByteAlign_A, 2**$clog2(LfsrDw) == LfsrDw && LfsrDw >= 16)
end
if (MaxLenSVA) begin : gen_max_len_sva
`ifndef SYNTHESIS
// the code below is a workaround to enable long sequences to be checked.
// some simulators do not support SVA sequences longer than 2**32-1.
logic [LfsrDw-1:0] cnt_d, cnt_q;
logic perturbed_d, perturbed_q;
logic [LfsrDw-1:0] cmp_val;
assign cmp_val = {{(LfsrDw-1){1'b1}}, 1'b0}; // 2**LfsrDw-2
assign cnt_d = (lfsr_en_i && lockup) ? '0 :
(lfsr_en_i && (cnt_q == cmp_val)) ? '0 :
(lfsr_en_i) ? cnt_q + 1'b1 :
cnt_q;
assign perturbed_d = perturbed_q | (|entropy_i) | seed_en_i;
always_ff @(posedge clk_i or negedge rst_ni) begin : p_max_len
if (!rst_ni) begin
cnt_q <= '0;
perturbed_q <= 1'b0;
end else begin
cnt_q <= cnt_d;
perturbed_q <= perturbed_d;
end
end
`ASSERT(MaximalLengthCheck0_A, cnt_q == 0 |-> lfsr_q == DefaultSeedLocal,
clk_i, !rst_ni || perturbed_q)
`ASSERT(MaximalLengthCheck1_A, cnt_q != 0 |-> lfsr_q != DefaultSeedLocal,
clk_i, !rst_ni || perturbed_q)
`endif
end
endmodule