-
Notifications
You must be signed in to change notification settings - Fork 36
/
ddr3.sv
2987 lines (2831 loc) · 163 KB
/
ddr3.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
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
`define MAX_MEM
/****************************************************************************************
*
* File Name: ddr3.v
* Version: 1.74
* Model: BUS Functional
*
* Dependencies: ddr3_parameters.vh
*
* Description: Micron SDRAM DDR3 (Double Data Rate 3)
*
* Limitation: - doesn't check for average refresh timings
* - positive ck and ck_n edges are used to form internal clock
* - positive dqs and dqs_n edges are used to latch data
* - test mode is not modeled
* - Duty Cycle Corrector is not modeled
* - Temperature Compensated Self Refresh is not modeled
* - DLL off mode is not modeled.
*
* Note: - Set simulator resolution to "ps" accuracy
* - Set DEBUG = 0 to disable $display messages
*
* Disclaimer This software code and all associated documentation, comments or other
* of Warranty: information (collectively "Software") is provided "AS IS" without
* warranty of any kind. MICRON TECHNOLOGY, INC. ("MTI") EXPRESSLY
* DISCLAIMS ALL WARRANTIES EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO, NONINFRINGEMENT OF THIRD PARTY RIGHTS, AND ANY IMPLIED WARRANTIES
* OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE. MTI DOES NOT
* WARRANT THAT THE SOFTWARE WILL MEET YOUR REQUIREMENTS, OR THAT THE
* OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR ERROR-FREE.
* FURTHERMORE, MTI DOES NOT MAKE ANY REPRESENTATIONS REGARDING THE USE OR
* THE RESULTS OF THE USE OF THE SOFTWARE IN TERMS OF ITS CORRECTNESS,
* ACCURACY, RELIABILITY, OR OTHERWISE. THE ENTIRE RISK ARISING OUT OF USE
* OR PERFORMANCE OF THE SOFTWARE REMAINS WITH YOU. IN NO EVENT SHALL MTI,
* ITS AFFILIATED COMPANIES OR THEIR SUPPLIERS BE LIABLE FOR ANY DIRECT,
* INDIRECT, CONSEQUENTIAL, INCIDENTAL, OR SPECIAL DAMAGES (INCLUDING,
* WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, BUSINESS INTERRUPTION,
* OR LOSS OF INFORMATION) ARISING OUT OF YOUR USE OF OR INABILITY TO USE
* THE SOFTWARE, EVEN IF MTI HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGES. Because some jurisdictions prohibit the exclusion or
* limitation of liability for consequential or incidental damages, the
* above limitation may not apply to you.
*
* Copyright 2003 Micron Technology, Inc. All rights reserved.
*
* Rev Author Date Changes
* ---------------------------------------------------------------------------------------
* 0.41 JMK 05/12/06 Removed auto-precharge to power down error check.
* 0.42 JMK 08/25/06 Created internal clock using ck and ck_n.
* TDQS can only be enabled in EMR for x8 configurations.
* CAS latency is checked vs frequency when DLL locks.
* Improved checking of DQS during writes.
* Added true BL4 operation.
* 0.43 JMK 08/14/06 Added checking for setting reserved bits in Mode Registers.
* Added ODTS Readout.
* Replaced tZQCL with tZQinit and tZQoper
* Fixed tWRPDEN and tWRAPDEN during BC4MRS and BL4MRS.
* Added tRFC checking for Refresh to Power-Down Re-Entry.
* Added tXPDLL checking for Power-Down Exit to Refresh to Power-Down Entry
* Added Clock Frequency Change during Precharge Power-Down.
* Added -125x speed grades.
* Fixed tRCD checking during Write.
* 1.00 JMK 05/11/07 Initial release
* 1.10 JMK 06/26/07 Fixed ODTH8 check during BLOTF
* Removed temp sensor readout from MPR
* Updated initialization sequence
* Updated timing parameters
* 1.20 JMK 09/05/07 Updated clock frequency change
* Added ddr3_dimm module
* 1.30 JMK 01/23/08 Updated timing parameters
* 1.40 JMK 12/02/08 Added support for DDR3-1866 and DDR3-2133
* renamed ddr3_dimm.v to ddr3_module.v and added SODIMM support.
* Added multi-chip package model support in ddr3_mcp.v
* 1.50 JMK 05/04/08 Added 1866 and 2133 speed grades.
* 1.60 MYY 07/10/09 Merging of 1.50 version and pre-1.0 version changes
* 1.61 SPH 12/10/09 Only check tIH for cmd_addr if CS# LOW
* 1.62 SPH 10/26/10 Added 4Gb DDR3 SDRAM support
* 1.63 MYY 11/09/10 Added Dll Disable mode
* 1.64 MYY 07/28/11 Check dqs_in for dqs timing check
* 1.65 MYY 09/19/11 Widen internal bus width
* 1.66 MYY 01/20/12 Support ODT tied high feature
* 1.67 MYY 02/03/12 Added TJIT_PER margin for timing checks
* 1.68 SPH 04/02/12 Added memory preload
* 1.69 SPH 03/19/13 Update tZQCS, tZQinit, tZQoper timing parameters
* 1.70 SPH 04/08/14 Update tRFC to PRECHARGE check
* 1.71 SPH 04/21/14 Added 8Gb mono die parameters
* Remove strict CL check
* 1.72 DLH 06/18/15 calculate TZQCS from current tCK
* 1.73 SPH 08/20/15 calculate TZQINIT/TZQOPER from current tCK
* 1.74 SPH 09/08/15 Fix CWL 9 check to use 1071ps instead of 15e3/14 (round up error)
* Fix tIS don't care for Bank and Address when DES / NOP
*****************************************************************************************/
// DO NOT CHANGE THE TIMESCALE
// MAKE SURE YOUR SIMULATOR USES "PS" RESOLUTION
`timescale 1ps / 1ps
`define den8192Mb
`define sg125
`define x16
`default_nettype wire
module ddr3 (
rst_n,
ck,
ck_n,
cke,
cs_n,
ras_n,
cas_n,
we_n,
dm_tdqs,
ba,
addr,
dq,
dqs,
dqs_n,
tdqs_n,
odt
);
`ifdef den1024Mb
`include "1024Mb_ddr3_parameters.vh"
`elsif den2048Mb
`include "2048Mb_ddr3_parameters.vh"
`elsif den4096Mb
`include "4096Mb_ddr3_parameters.vh"
`elsif den8192Mb
`include "8192Mb_ddr3_parameters.vh"
`else
// NOTE: Intentionally cause a compile fail here to force the users
// to select the correct component density before continuing
ERROR: You must specify component density with +define+den____Mb.
`endif
parameter check_strict_mrbits = 1;
parameter check_strict_timing = 1;
parameter feature_pasr = 1;
parameter feature_truebl4 = 0;
parameter feature_odt_hi = 0;
parameter PERTCKAVG=TDLLK;
parameter FLY_BY_DELAY = 0, DQ_DELAY = 0;
// text macros
`define DQ_PER_DQS DQ_BITS/DQS_BITS
`define BANKS (1<<BA_BITS)
`define MAX_BITS (BA_BITS+ROW_BITS+COL_BITS-BL_BITS)
`define MAX_SIZE (1<<(BA_BITS+ROW_BITS+COL_BITS-BL_BITS))
`define MEM_SIZE (1<<MEM_BITS)
`define MAX_PIPE 4*CL_MAX
// Declare Ports
input rst_n;
input ck;
input ck_n;
input cke;
input cs_n;
input ras_n;
input cas_n;
input we_n;
input [DM_BITS-1:0] dm_tdqs;
input [BA_BITS-1:0] ba;
input [ROW_BITS-1:0] addr;
inout [DQ_BITS-1:0] dq;
inout [DQS_BITS-1:0] dqs;
inout [DQS_BITS-1:0] dqs_n;
output [DQS_BITS-1:0] tdqs_n;
input odt;
// clock jitter
real tck_avg;
time tck_sample [PERTCKAVG-1:0];
time tch_sample [PERTCKAVG:0];
time tcl_sample [PERTCKAVG:0];
time tck_i;
time tch_i;
time tcl_i;
real tch_avg;
real tcl_avg;
time tm_ck_pos;
time tm_ck_neg;
real tjit_per_rtime;
integer tjit_cc_time;
real terr_nper_rtime;
//DDR3 clock jitter variables
real tjit_ch_rtime;
integer duty_cycle;
// clock skew
integer out_delay;
integer dqsck [DQS_BITS-1:0];
integer dqsck_min;
integer dqsck_max;
integer dqsq_min;
integer dqsq_max;
integer seed;
// Mode Registers
reg [ADDR_BITS-1:0] mode_reg [`BANKS-1:0];
reg burst_order;
reg [BL_BITS:0] burst_length;
reg blotf;
reg truebl4;
integer cas_latency;
reg dll_reset;
reg dll_locked;
integer write_recovery;
reg low_power;
reg dll_en;
reg [2:0] odt_rtt_nom;
reg [1:0] odt_rtt_wr;
reg odt_en;
reg dyn_odt_en;
reg [1:0] al;
integer additive_latency;
reg write_levelization;
reg duty_cycle_corrector;
reg tdqs_en;
reg out_en;
reg [2:0] pasr;
integer cas_write_latency;
reg asr; // auto self refresh
reg srt; // self refresh temperature range
reg [1:0] mpr_select;
reg mpr_en;
reg odts_readout;
integer read_latency;
integer write_latency;
// cmd encoding
parameter // {cs, ras, cas, we}
LOAD_MODE = 4'b0000,
REFRESH = 4'b0001,
PRECHARGE = 4'b0010,
ACTIVATE = 4'b0011,
WRITE = 4'b0100,
READ = 4'b0101,
ZQ = 4'b0110,
NOP = 4'b0111,
// DESEL = 4'b1xxx,
PWR_DOWN = 4'b1000,
SELF_REF = 4'b1001
;
reg [8*9-1:0] cmd_string [9:0];
reg[8*9-1:0] prev_cmd;
integer prev_time;
initial begin
cmd_string[LOAD_MODE] = "Load Mode";
cmd_string[REFRESH ] = "Refresh ";
cmd_string[PRECHARGE] = "Precharge";
cmd_string[ACTIVATE ] = "Activate ";
cmd_string[WRITE ] = "Write ";
cmd_string[READ ] = "Read ";
cmd_string[ZQ ] = "ZQ ";
cmd_string[NOP ] = "No Op ";
cmd_string[PWR_DOWN ] = "Pwr Down ";
cmd_string[SELF_REF ] = "Self Ref ";
end
// command state
reg [`BANKS-1:0] active_bank;
reg [`BANKS-1:0] auto_precharge_bank;
reg [`BANKS-1:0] write_precharge_bank;
reg [`BANKS-1:0] read_precharge_bank;
reg [ROW_BITS-1:0] active_row [`BANKS-1:0];
integer index;
initial for(index = 0; index < `BANKS; index = index + 1) active_row[index] = 0;
reg in_power_down;
reg in_self_refresh;
reg [3:0] init_mode_reg;
reg init_dll_reset;
reg init_done;
integer init_step;
reg zq_set;
reg er_trfc_max;
reg odt_state;
reg odt_state_dly;
reg dyn_odt_state;
reg dyn_odt_state_dly;
reg prev_odt;
wire [7:0] calibration_pattern = 8'b10101010; // value returned during mpr pre-defined pattern readout
wire [7:0] temp_sensor = 8'h01; // value returned during mpr temp sensor readout
reg [1:0] mr_chk;
reg rd_bc;
integer banki;
// cmd timers/counters
integer ref_cntr;
integer odt_cntr;
integer ck_cntr;
integer ck_txpr;
integer ck_load_mode;
integer ck_refresh;
integer ck_precharge;
integer ck_activate;
integer ck_write;
integer ck_read;
integer ck_zqinit;
integer ck_zqoper;
integer ck_zqcs;
integer ck_power_down;
integer ck_slow_exit_pd;
integer ck_self_refresh;
integer ck_freq_change;
integer ck_odt;
integer ck_odth8;
integer ck_dll_reset;
integer ck_cke_cmd;
integer ck_bank_write [`BANKS-1:0];
integer ck_bank_read [`BANKS-1:0];
integer ck_group_activate [1:0];
integer ck_group_write [1:0];
integer ck_group_read [1:0];
time tm_txpr;
time tm_load_mode;
time tm_refresh;
time tm_precharge;
time tm_activate;
time tm_write_end;
time tm_power_down;
time tm_slow_exit_pd;
time tm_self_refresh;
time tm_freq_change;
time tm_cke_cmd;
time tm_ttsinit;
time tm_bank_precharge [`BANKS-1:0];
time tm_bank_activate [`BANKS-1:0];
time tm_bank_write_end [`BANKS-1:0];
time tm_bank_read_end [`BANKS-1:0];
time tm_group_activate [1:0];
time tm_group_write_end [1:0];
// pipelines
reg [`MAX_PIPE:0] al_pipeline;
reg [`MAX_PIPE:0] wr_pipeline;
reg [`MAX_PIPE:0] rd_pipeline;
reg [`MAX_PIPE:0] odt_pipeline;
reg [`MAX_PIPE:0] dyn_odt_pipeline;
reg [BL_BITS:0] bl_pipeline [`MAX_PIPE:0];
reg [BA_BITS-1:0] ba_pipeline [`MAX_PIPE:0];
reg [ROW_BITS-1:0] row_pipeline [`MAX_PIPE:0];
reg [COL_BITS-1:0] col_pipeline [`MAX_PIPE:0];
reg prev_cke;
reg [5:0] cmd_tran_index, cmd_tran_i;
reg [63:0] cmd_tran_pipeline;
reg [2:0] cmd_n_in_pipeline [63:0];
reg [BA_BITS-1:0] ba_in_pipeline [63:0];
reg [ADDR_BITS-1:0] addr_in_pipeline [63:0];
// data state
reg [BL_MAX*DQ_BITS-1:0] memory_data;
reg [BL_MAX*DQ_BITS-1:0] bit_mask;
reg [BL_BITS-1:0] burst_position;
reg [BL_BITS:0] burst_cntr;
reg [DQ_BITS-1:0] dq_temp;
reg [63:0] check_write_postamble;
reg [63:0] check_write_preamble;
reg [63:0] check_write_dqs_high;
reg [63:0] check_write_dqs_low;
reg [31:0] check_dm_tdipw;
reg [127:0] check_dq_tdipw;
// data timers/counters
time tm_rst_n;
time tm_cke;
time tm_odt;
time tm_tdqss;
time tm_dm [31:0];
time tm_dqs [31:0];
time tm_dqs_pos [63:0];
time tm_dqss_pos [63:0];
time tm_dqs_neg [63:0];
time tm_dq [127:0];
time tm_cmd_addr [23:0];
reg [8*7-1:0] cmd_addr_string [23:0];
initial begin
cmd_addr_string[ 0] = "CS_N ";
cmd_addr_string[ 1] = "RAS_N ";
cmd_addr_string[ 2] = "CAS_N ";
cmd_addr_string[ 3] = "WE_N ";
cmd_addr_string[ 4] = "BA 0 ";
cmd_addr_string[ 5] = "BA 1 ";
cmd_addr_string[ 6] = "BA 2 ";
cmd_addr_string[ 7] = "ADDR 0";
cmd_addr_string[ 8] = "ADDR 1";
cmd_addr_string[ 9] = "ADDR 2";
cmd_addr_string[10] = "ADDR 3";
cmd_addr_string[11] = "ADDR 4";
cmd_addr_string[12] = "ADDR 5";
cmd_addr_string[13] = "ADDR 6";
cmd_addr_string[14] = "ADDR 7";
cmd_addr_string[15] = "ADDR 8";
cmd_addr_string[16] = "ADDR 9";
cmd_addr_string[17] = "ADDR 10";
cmd_addr_string[18] = "ADDR 11";
cmd_addr_string[19] = "ADDR 12";
cmd_addr_string[20] = "ADDR 13";
cmd_addr_string[21] = "ADDR 14";
cmd_addr_string[22] = "ADDR 15";
cmd_addr_string[23] = "ADDR 16";
end
reg [8*5-1:0] dqs_string [1:0];
initial begin
dqs_string[0] = "DQS ";
dqs_string[1] = "DQS_N";
end
// Memory Storage
`ifdef MAX_MEM
parameter RFF_BITS = DQ_BITS*BL_MAX;
// %z format uses 8 bytes for every 32 bits or less.
parameter RFF_CHUNK = 8 * (RFF_BITS/32 + (RFF_BITS%32 ? 1 : 0));
reg [1024:1] tmp_model_dir;
integer memfd[`BANKS-1:0];
initial
begin : file_io_open
reg [BA_BITS - 1 : 0] bank;
reg [ROW_BITS - 1 : 0] row;
reg [COL_BITS - 1 : 0] col;
reg [BA_BITS + ROW_BITS + COL_BITS - 1 : 0] addr;
reg [BL_MAX * DQ_BITS - 1 : 0] data;
string _char;
integer in, fio_status;
if (!$value$plusargs("model_data+%s", tmp_model_dir))
begin
tmp_model_dir = "/tmp";
$display(
"%m: at time %t WARNING: no +model_data option specified, using /tmp.",
$time
);
end
for (integer i = 0; i < `BANKS; i = i + 1)
memfd[i] = open_bank_file(i);
// Preload section
`ifdef mem_init
in = $fopen("mem_init.txt","r");
while (! $feof(in)) begin
fio_status = $fscanf(in, "%h %s %h", addr, _char, data);
if (fio_status != -1) begin // Check for blank line or EOF
bank = addr [BA_BITS + ROW_BITS + COL_BITS - 1 : ROW_BITS + COL_BITS];
row = addr [ROW_BITS + COL_BITS - 1 : COL_BITS];
col = addr [COL_BITS - 1 : 0];
memory_write (bank, row, col, data);
// Next 4 lines are for debug only
$display ("MEMORY_WRITE: Bank = %h, Row = %h, Col = %h, Data = %h", bank, row, col, data);
data = 'hx; // This is to reset data to verify memory_read
memory_read(bank, row, col, data);
$display ("MEMORY_READ: Bank = %h, Row = %h, Col = %h, Data = %h", bank, row, col, data);
end
end
$fclose(in);
//$finish;
`endif
end
`else
reg [BL_MAX*DQ_BITS-1:0] memory [0:`MEM_SIZE-1];
reg [`MAX_BITS-1:0] address [0:`MEM_SIZE-1];
reg [MEM_BITS:0] memory_index;
reg [MEM_BITS:0] memory_used = 0;
`endif
// receive
reg rst_n_in;
reg ck_in;
reg ck_n_in;
reg cke_in;
reg cs_n_in;
reg ras_n_in;
reg cas_n_in;
reg we_n_in;
reg [31:0] dm_in;
reg [2:0] ba_in;
reg [16:0] addr_in;
reg [127:0] dq_in;
reg [63:0] dqs_in;
reg odt_in;
reg [31:0] dm_in_pos;
reg [31:0] dm_in_neg;
reg [127:0] dq_in_pos;
reg [127:0] dq_in_neg;
reg dq_in_valid;
reg dqs_in_valid;
integer wdqs_cntr;
integer wdq_cntr;
integer wdqs_pos_cntr [63:0];
reg b2b_write;
reg [BL_BITS:0] wr_burst_length;
reg [63:0] prev_dqs_in;
reg diff_ck;
always @(rst_n ) rst_n_in <= #(BUS_DELAY+FLY_BY_DELAY) rst_n;
always @(ck ) ck_in <= #(BUS_DELAY+FLY_BY_DELAY) ck;
always @(ck_n ) ck_n_in <= #(BUS_DELAY+FLY_BY_DELAY) ck_n;
always @(cke )
cke_in <= #(BUS_DELAY+FLY_BY_DELAY) cke;
always @(cs_n ) cs_n_in <= #(BUS_DELAY+FLY_BY_DELAY) cs_n;
always @(ras_n ) ras_n_in <= #(BUS_DELAY+FLY_BY_DELAY) ras_n;
always @(cas_n ) cas_n_in <= #(BUS_DELAY+FLY_BY_DELAY) cas_n;
always @(we_n ) we_n_in <= #(BUS_DELAY+FLY_BY_DELAY) we_n;
always @(dm_tdqs) dm_in <= #(BUS_DELAY) dm_tdqs;
always @(ba ) ba_in <= #(BUS_DELAY+FLY_BY_DELAY) ba;
always @(addr ) addr_in <= #(BUS_DELAY+FLY_BY_DELAY) addr;
always @(dq ) dq_in <= #(BUS_DELAY) dq;
always @(dqs or dqs_n) dqs_in <= #(BUS_DELAY) (dqs_n<<32) | dqs;
always @(odt ) if (!feature_odt_hi) odt_in <= #(BUS_DELAY+FLY_BY_DELAY) odt;
// create internal clock
always @(posedge ck_in) diff_ck <= ck_in;
always @(posedge ck_n_in) diff_ck <= ~ck_n_in;
wire [31:0] dqs_even = dqs_in[31:0];
wire [31:0] dqs_odd = dqs_in[63:32];
// wire [3:0] cmd_n_in = !cs_n_in ? {ras_n_in, cas_n_in, we_n_in} : NOP; //deselect = nop
reg [3:0] cmd_n_in;
always @(cs_n_in or ras_n_in or cas_n_in or we_n_in)
cmd_n_in = !cs_n_in ? {ras_n_in, cas_n_in, we_n_in} : NOP;
// transmit
reg dqs_out_en;
reg [DQS_BITS-1:0] dqs_out_en_dly;
reg dqs_out;
reg [DQS_BITS-1:0] dqs_out_dly;
reg dq_out_en;
reg [DQ_BITS-1:0] dq_out_en_dly, dq_out_en_dly_delayed;
reg [DQ_BITS-1:0] dq_out;
reg [DQ_BITS-1:0] dq_out_dly, dq_out_dly_delayed;
reg out_en_delayed;
integer rdqsen_cntr;
integer rdqs_cntr;
integer rdqen_cntr;
integer rdq_cntr;
bufif1 buf_dqs [DQS_BITS-1:0] (dqs, dqs_out_dly, dqs_out_en_dly & {DQS_BITS{out_en}});
bufif1 buf_dqs_n [DQS_BITS-1:0] (dqs_n, ~dqs_out_dly, dqs_out_en_dly & {DQS_BITS{out_en}});
//add delay to DQ
always @(dq_out_dly) dq_out_dly_delayed <= #(DQ_DELAY) dq_out_dly;
always @(dq_out_en_dly) dq_out_en_dly_delayed <= #(DQ_DELAY) dq_out_en_dly;
always @(out_en) out_en_delayed <= #(DQ_DELAY) out_en;
bufif1 buf_dq [DQ_BITS-1:0] (dq, dq_out_dly_delayed, dq_out_en_dly_delayed & {DQ_BITS {out_en_delayed}});
assign tdqs_n = {DQS_BITS{1'bz}};
assign TZQCS = max( 64, ceil( 80000/tck_avg));
assign TZQINIT = max(512, ceil(640000/tck_avg));
assign TZQOPER = max(256, ceil(320000/tck_avg));
initial begin
if (BL_MAX < 2)
$display("%m ERROR: BL_MAX parameter must be >= 2. \nBL_MAX = %d", BL_MAX);
if ((1<<BO_BITS) > BL_MAX)
$display("%m ERROR: 2^BO_BITS cannot be greater than BL_MAX parameter.");
$timeformat (-12, 1, " ps", 1);
seed = RANDOM_SEED;
ck_cntr = 0;
end
function integer get_rtt_wr;
input [1:0] rtt;
begin
get_rtt_wr = RZQ/{rtt[0], rtt[1], 1'b0};
end
endfunction
function integer get_rtt_nom;
input [2:0] rtt;
begin
case (rtt)
1: get_rtt_nom = RZQ/4;
2: get_rtt_nom = RZQ/2;
3: get_rtt_nom = RZQ/6;
4: get_rtt_nom = RZQ/12;
5: get_rtt_nom = RZQ/8;
default : get_rtt_nom = 0;
endcase
end
endfunction
// calculate the absolute value of a real number
function real abs_value;
input arg;
real arg;
begin
if (arg < 0.0)
abs_value = -1.0 * arg;
else
abs_value = arg;
end
endfunction
function integer ceil;
input number;
real number;
// LMR 4.1.7
// When either operand of a relational expression is a real operand then the other operand shall be converted
// to an equivalent real value, and the expression shall be interpreted as a comparison between two real values.
if (number > $rtoi(number))
ceil = $rtoi(number) + 1;
else
ceil = number;
endfunction
function integer floor;
input number;
real number;
// LMR 4.1.7
// When either operand of a relational expression is a real operand then the other operand shall be converted
// to an equivalent real value, and the expression shall be interpreted as a comparison between two real values.
if (number < $rtoi(number))
floor = $rtoi(number) - 1;
else
floor = number;
endfunction
function int max( input int a, b );
max = (a < b) ? b : a;
endfunction
function int min( input int a, b );
min = (a > b) ? b : a;
endfunction
`ifdef MAX_MEM
function integer open_bank_file( input integer bank );
integer fd;
reg [2048:1] filename;
begin
$sformat( filename, "%0s/%m.%0d", tmp_model_dir, bank );
fd = $fopen(filename, "wb+");
if (fd == 0)
begin
$display("%m: at time %0t ERROR: failed to open %0s.", $time, filename);
$finish;
end
else
begin
if (DEBUG) $display("%m: at time %0t INFO: opening %0s.", $time, filename);
open_bank_file = fd;
end
end
endfunction
function [RFF_BITS:1] read_from_file(
input integer fd,
input integer index
);
integer code;
integer offset;
reg [1024:1] msg;
reg [RFF_BITS:1] read_value;
begin
offset = index * RFF_CHUNK;
code = $fseek( fd, offset, 0 );
// $fseek returns 0 on success, -1 on failure
if (code != 0)
begin
$display("%m: at time %t ERROR: fseek to %d failed", $time, offset);
$finish;
end
code = $fscanf(fd, "%z", read_value);
// $fscanf returns number of items read
if (code != 1)
begin
if ($ferror(fd,msg) != 0)
begin
$display("%m: at time %t ERROR: fscanf failed at %d", $time, index);
$display(msg);
$finish;
end
else
read_value = 'hx;
end
/* when reading from unwritten portions of the file, 0 will be returned.
* Use 0 in bit 1 as indicator that invalid data has been read.
* A true 0 is encoded as Z.
*/
if (read_value[1] === 1'bz)
// true 0 encoded as Z, data is valid
read_value[1] = 1'b0;
else if (read_value[1] === 1'b0)
// read from file section that has not been written
read_value = 'hx;
read_from_file = read_value;
end
endfunction
task write_to_file(
input integer fd,
input integer index,
input [RFF_BITS:1] data
);
integer code;
integer offset;
begin
offset = index * RFF_CHUNK;
code = $fseek( fd, offset, 0 );
if (code != 0)
begin
$display("%m: at time %t ERROR: fseek to %d failed", $time, offset);
$finish;
end
// encode a valid data
if (data[1] === 1'bz)
data[1] = 1'bx;
else if (data[1] === 1'b0)
data[1] = 1'bz;
$fwrite( fd, "%z", data );
end
endtask
`else
function get_index;
input [`MAX_BITS-1:0] addr;
begin : index
get_index = 0;
for (memory_index=0; memory_index<memory_used; memory_index=memory_index+1) begin
if (address[memory_index] == addr) begin
get_index = 1;
disable index;
end
end
end
endfunction
`endif
task memory_write;
input [BA_BITS-1:0] bank;
input [ROW_BITS-1:0] row;
input [COL_BITS-1:0] col;
input [BL_MAX*DQ_BITS-1:0] data;
reg [`MAX_BITS-1:0] addr;
begin
`ifdef MAX_MEM
addr = {row, col}/BL_MAX;
write_to_file( memfd[bank], addr, data );
`else
// chop off the lowest address bits
addr = {bank, row, col}/BL_MAX;
if (get_index(addr)) begin
address[memory_index] = addr;
memory[memory_index] = data;
end else if (memory_used == `MEM_SIZE) begin
$display ("%m: at time %t ERROR: Memory overflow. Write to Address %h with Data %h will be lost.\nYou must increase the MEM_BITS parameter or define MAX_MEM.", $time, addr, data);
if (STOP_ON_ERROR) $stop(0);
end else begin
address[memory_used] = addr;
memory[memory_used] = data;
memory_used = memory_used + 1;
end
`endif
end
endtask
task memory_read;
input [BA_BITS-1:0] bank;
input [ROW_BITS-1:0] row;
input [COL_BITS-1:0] col;
output [BL_MAX*DQ_BITS-1:0] data;
reg [`MAX_BITS-1:0] addr_1;
begin
`ifdef MAX_MEM
addr_1 = {row, col}/BL_MAX;
data = read_from_file( memfd[bank], addr_1 );
`else
// chop off the lowest address bits
addr_1 = {bank, row, col}/BL_MAX;
if (get_index(addr_1)) begin
data = memory[memory_index];
end else begin
data = {BL_MAX*DQ_BITS{1'bx}};
end
`endif
end
endtask
task set_latency;
begin
if (al == 0) begin
additive_latency = 0;
end else begin
additive_latency = cas_latency - al;
end
if (dll_en)
read_latency = cas_latency + additive_latency;
else
read_latency = cas_latency + additive_latency - 1;
write_latency = cas_write_latency + additive_latency;
end
endtask
// this task will erase the contents of 0 or more banks
task erase_banks;
input [`BANKS-1:0] banks; //one select bit per bank
reg [BA_BITS-1:0] ba;
reg [`MAX_BITS-1:0] i;
integer bank;
begin
`ifdef MAX_MEM
for (bank = 0; bank < `BANKS; bank = bank + 1)
if (banks[bank] === 1'b1) begin
$fclose(memfd[bank]);
memfd[bank] = open_bank_file(bank);
end
`else
memory_index = 0;
i = 0;
// remove the selected banks
for (memory_index=0; memory_index<memory_used; memory_index=memory_index+1) begin
ba = (address[memory_index]>>(ROW_BITS+COL_BITS-BL_BITS));
if (!banks[ba]) begin //bank is selected to keep
address[i] = address[memory_index];
memory[i] = memory[memory_index];
i = i + 1;
end
end
// clean up the unused banks
for (memory_index=i; memory_index<memory_used; memory_index=memory_index+1) begin
address[memory_index] = 'bx;
memory[memory_index] = {8*DQ_BITS{1'bx}};
end
memory_used = i;
`endif
end
endtask
// Before this task runs, the model must be in a valid state for precharge power down and out of reset.
// After this task runs, NOP commands must be issued until TZQINIT has been met
task initialize;
input [ADDR_BITS-1:0] mode_reg0;
input [ADDR_BITS-1:0] mode_reg1;
input [ADDR_BITS-1:0] mode_reg2;
input [ADDR_BITS-1:0] mode_reg3;
begin
if (DEBUG) $display ("%m: at time %t INFO: Performing Initialization Sequence", $time);
cmd_task(prev_cke, 1, NOP, 'bx, 'bx);
cmd_task(prev_cke, 1, ZQ, 'bx, 'h400); //ZQCL
cmd_task(prev_cke, 1, LOAD_MODE, 3, mode_reg3);
cmd_task(prev_cke, 1, LOAD_MODE, 2, mode_reg2);
cmd_task(prev_cke, 1, LOAD_MODE, 1, mode_reg1);
cmd_task(prev_cke, 1, LOAD_MODE, 0, mode_reg0 | 'h100); // DLL Reset
cmd_task(prev_cke, 0, NOP, 'bx, 'bx);
end
endtask
task reset_task;
integer i;
begin
// disable inputs
dq_in_valid = 0;
dqs_in_valid <= 0;
wdqs_cntr = 0;
wdq_cntr = 0;
for (i=0; i<64; i=i+1) begin
wdqs_pos_cntr[i] <= 0;
end
b2b_write <= 0;
// disable outputs
out_en = 0;
dq_out_en = 0;
rdq_cntr = 0;
dqs_out_en = 0;
rdqs_cntr = 0;
// disable ODT
odt_en = 0;
dyn_odt_en = 0;
odt_state = 0;
dyn_odt_state = 0;
// reset bank state
active_bank = 0;
auto_precharge_bank = 0;
read_precharge_bank = 0;
write_precharge_bank = 0;
// require initialization sequence
init_done = 0;
mpr_en = 0;
init_step = 0;
init_mode_reg = 0;
init_dll_reset = 0;
zq_set = 0;
// reset DLL
dll_en = 0;
dll_reset = 0;
dll_locked = 0;
// exit power down and self refresh
prev_cke = 1'bx;
in_power_down = 0;
in_self_refresh = 0;
// clear pipelines
al_pipeline = 0;
wr_pipeline = 0;
rd_pipeline = 0;
odt_pipeline = 0;
dyn_odt_pipeline = 0;
cmd_tran_pipeline = 0;
cmd_tran_index = 0;
end
endtask
parameter SAME_BANK = 2'd0; // same bank, same group
parameter DIFF_BANK = 2'd1; // different bank, same group
parameter DIFF_GROUP = 2'd2; // different bank, different group
task chk_err;
input [1:0] relationship;
input [BA_BITS-1:0] bank;
input [3:0] fromcmd;
input [3:0] cmd;
reg err;
begin
// $display ("truebl4 = %d, relationship = %d, fromcmd = %h, cmd = %h", truebl4, relationship, fromcmd, cmd);
casex ({truebl4, relationship, fromcmd, cmd})
// load mode
{1'bx, DIFF_BANK , LOAD_MODE, LOAD_MODE} : begin if (ck_cntr - ck_load_mode < TMRD) $display ("%m: at time %t ERROR: tMRD violation during %s", $time, cmd_string[cmd]); end
{1'bx, DIFF_BANK , LOAD_MODE, READ } : begin if (($time - tm_load_mode < TMOD-TJIT_PER) || (ck_cntr - ck_load_mode < TMOD_TCK)) $display ("%m: at time %t ERROR: tMOD violation during %s", $time, cmd_string[cmd]); end
{1'bx, DIFF_BANK , LOAD_MODE, REFRESH } ,
{1'bx, DIFF_BANK , LOAD_MODE, PRECHARGE} ,
{1'bx, DIFF_BANK , LOAD_MODE, ACTIVATE } ,
{1'bx, DIFF_BANK , LOAD_MODE, ZQ } ,
{1'bx, DIFF_BANK , LOAD_MODE, PWR_DOWN } ,
{1'bx, DIFF_BANK , LOAD_MODE, SELF_REF } : begin if (($time - tm_load_mode < TMOD-TJIT_PER) || (ck_cntr - ck_load_mode < TMOD_TCK)) $display ("%m: at time %t ERROR: tMOD violation during %s", $time, cmd_string[cmd]); end
// refresh
{1'bx, DIFF_BANK , REFRESH , LOAD_MODE} ,
{1'bx, DIFF_BANK , REFRESH , REFRESH } ,
{1'bx, DIFF_BANK , REFRESH , PRECHARGE} ,
{1'bx, DIFF_BANK , REFRESH , ACTIVATE } ,
{1'bx, DIFF_BANK , REFRESH , ZQ } ,
{1'bx, DIFF_BANK , REFRESH , SELF_REF } : begin if ($time - tm_refresh < TRFC_MIN) $display ("%m: at time %t ERROR: tRFC violation during %s", $time, cmd_string[cmd]); end
{1'bx, DIFF_BANK , REFRESH , PWR_DOWN } : begin if (ck_cntr - ck_refresh < TREFPDEN) $display ("%m: at time %t ERROR: tREFPDEN violation during %s", $time, cmd_string[cmd]); end
// precharge
{1'bx, SAME_BANK , PRECHARGE, ACTIVATE } : begin if ($time - tm_bank_precharge[bank] < TRP-TJIT_PER) $display ("%m: at time %t ERROR: tRP violation during %s to bank %d", $time, cmd_string[cmd], bank); end
{1'bx, DIFF_BANK , PRECHARGE, LOAD_MODE} ,
{1'bx, DIFF_BANK , PRECHARGE, REFRESH } ,
{1'bx, DIFF_BANK , PRECHARGE, SELF_REF } : begin if ($time - tm_precharge < TRP-TJIT_PER) $display ("%m: at time %t ERROR: tRP violation during %s", $time, cmd_string[cmd]); end
{1'bx, DIFF_BANK , PRECHARGE, ZQ } :
begin if ($time - tm_precharge < TRP) $display ("%m: at time %t ERROR: tRP violation during %s", $time, cmd_string[cmd]); end
{1'bx, DIFF_BANK , PRECHARGE, PWR_DOWN } : ; //tPREPDEN = 1 tCK, can be concurrent with auto precharge
// activate
{1'bx, SAME_BANK , ACTIVATE , PRECHARGE} : begin if ($time - tm_bank_activate[bank] > TRAS_MAX) $display ("%m: at time %t ERROR: tRAS maximum violation during %s to bank %d", $time, cmd_string[cmd], bank);
if ($time - tm_bank_activate[bank] < TRAS_MIN-TJIT_PER) $display ("%m: at time %t ERROR: tRAS minimum violation during %s to bank %d", $time, cmd_string[cmd], bank);end
{1'bx, SAME_BANK , ACTIVATE , ACTIVATE } : begin if ($time - tm_bank_activate[bank] < TRC-TJIT_PER) $display ("%m: at time %t ERROR: tRC violation during %s to bank %d", $time, cmd_string[cmd], bank); end
{1'bx, SAME_BANK , ACTIVATE , WRITE } ,
{1'bx, SAME_BANK , ACTIVATE , READ } : ; // tRCD is checked outside this task
{1'b0, DIFF_BANK , ACTIVATE , ACTIVATE } : begin if (($time - tm_activate < TRRD) || (ck_cntr - ck_activate < TRRD_TCK)) $display ("%m: at time %t ERROR: tRRD violation during %s to bank %d", $time, cmd_string[cmd], bank); end
{1'b1, DIFF_BANK , ACTIVATE , ACTIVATE } : begin if (($time - tm_group_activate[bank[1]] < TRRD) || (ck_cntr - ck_group_activate[bank[1]] < TRRD_TCK)) $display ("%m: at time %t ERROR: tRRD violation during %s to bank %d", $time, cmd_string[cmd], bank); end
{1'b1, DIFF_GROUP, ACTIVATE , ACTIVATE } : begin if (($time - tm_activate < TRRD_DG) || (ck_cntr - ck_activate < TRRD_DG_TCK)) $display ("%m: at time %t ERROR: tRRD_DG violation during %s to bank %d", $time, cmd_string[cmd], bank); end
{1'bx, DIFF_BANK , ACTIVATE , REFRESH } : begin if ($time - tm_activate < TRC-TJIT_PER) $display ("%m: at time %t ERROR: tRC violation during %s", $time, cmd_string[cmd]); end
{1'bx, DIFF_BANK , ACTIVATE , PWR_DOWN } : begin if (ck_cntr - ck_activate < TACTPDEN) $display ("%m: at time %t ERROR: tACTPDEN violation during %s", $time, cmd_string[cmd]); end
// write
{1'bx, SAME_BANK , WRITE , PRECHARGE} : begin if (($time - tm_bank_write_end[bank] < TWR-TJIT_PER) || (ck_cntr - ck_bank_write[bank] <= write_latency + burst_length/2)) $display ("%m: at time %t ERROR: tWR violation during %s to bank %d", $time, cmd_string[cmd], bank); end
{1'b0, DIFF_BANK , WRITE , WRITE } : begin if (ck_cntr - ck_write < TCCD) $display ("%m: at time %t ERROR: tCCD violation during %s to bank %d", $time, cmd_string[cmd], bank); end
{1'b1, DIFF_BANK , WRITE , WRITE } : begin if (ck_cntr - ck_group_write[bank[1]] < TCCD) $display ("%m: at time %t ERROR: tCCD violation during %s to bank %d", $time, cmd_string[cmd], bank); end
{1'b0, DIFF_BANK , WRITE , READ } : begin if (ck_cntr - ck_write < write_latency + burst_length/2 + TWTR_TCK - additive_latency) $display ("%m: at time %t ERROR: tWTR violation during %s to bank %d", $time, cmd_string[cmd], bank); end
{1'b1, DIFF_BANK , WRITE , READ } : begin if (ck_cntr - ck_group_write[bank[1]] < write_latency + burst_length/2 + TWTR_TCK - additive_latency) $display ("%m: at time %t ERROR: tWTR violation during %s to bank %d", $time, cmd_string[cmd], bank); end
{1'b1, DIFF_GROUP, WRITE , WRITE } : begin if (ck_cntr - ck_write < TCCD_DG) $display ("%m: at time %t ERROR: tCCD_DG violation during %s to bank %d", $time, cmd_string[cmd], bank); end
{1'b1, DIFF_GROUP, WRITE , READ } : begin if (ck_cntr - ck_write < write_latency + burst_length/2 + TWTR_DG_TCK - additive_latency) $display ("%m: at time %t ERROR: tWTR_DG violation during %s to bank %d", $time, cmd_string[cmd], bank); end
{1'bx, DIFF_BANK , WRITE , PWR_DOWN } : begin if (($time - tm_write_end < TWR-TJIT_PER) || (ck_cntr - ck_write < write_latency + burst_length/2)) $display ("%m: at time %t ERROR: tWRPDEN violation during %s", $time, cmd_string[cmd]); end
// read
{1'bx, SAME_BANK , READ , PRECHARGE} : begin if (($time - tm_bank_read_end[bank] < TRTP-TJIT_PER) || (ck_cntr - ck_bank_read[bank] < additive_latency + TRTP_TCK)) $display ("%m: at time %t ERROR: tRTP violation during %s to bank %d", $time, cmd_string[cmd], bank); end
{1'b0, DIFF_BANK , READ , WRITE } : ; // tRTW is checked outside this task
{1'b1, DIFF_BANK , READ , WRITE } : ; // tRTW is checked outside this task
{1'b0, DIFF_BANK , READ , READ } : begin if (ck_cntr - ck_read < TCCD) $display ("%m: at time %t ERROR: tCCD violation during %s to bank %d", $time, cmd_string[cmd], bank); end
{1'b1, DIFF_BANK , READ , READ } : begin if (ck_cntr - ck_group_read[bank[1]] < TCCD) $display ("%m: at time %t ERROR: tCCD violation during %s to bank %d", $time, cmd_string[cmd], bank); end
{1'b1, DIFF_GROUP, READ , WRITE } : ; // tRTW is checked outside this task
{1'b1, DIFF_GROUP, READ , READ } : begin if (ck_cntr - ck_read < TCCD_DG) $display ("%m: at time %t ERROR: tCCD_DG violation during %s to bank %d", $time, cmd_string[cmd], bank); end
{1'bx, DIFF_BANK , READ , PWR_DOWN } : begin if (ck_cntr - ck_read < read_latency + 5) $display ("%m: at time %t ERROR: tRDPDEN violation during %s", $time, cmd_string[cmd]); end