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
|
// Auto-generated, DDR4 SDRAM Example Design source
// small modifications to issue writes from all command slots
module ddr4_mc_odt #(parameter
ODTWR = 16'h8421
,ODTWRDEL = 5'd9
,ODTWRDUR = 4'd6
,ODTWRODEL = 5'd9
,ODTWRODUR = 4'd6
,ODTRD = 16'h8421
,ODTRDDEL = 5'd9
,ODTRDDUR = 4'd6
,ODTRDODEL = 5'd9
,ODTRDODUR = 4'd6
,ODTBITS = 4
,ODTNOP = 4'b0000
,TCQ = 0.1
)(
input clk
,input rst
,output [ODTBITS*8-1:0] mc_ODT
,input casSlot2
,input [1:0] casSlot
,input [1:0] rank
,input winRead
,input winWrite
,input tranSentC
);
// ==========================================================================================
// ODT is a multi-fabric-cycle waveform that needs to assert on the same cycle as write CAS,
// and also on the same cycle as read CAS when tCL=tCWL. This block generates the full
// multi-cycle ODT waveform in the same cycle that rdCAS or wrCAS is generated, bypassing
// the first 8 bits per ODT pin to mc_ODT combinationally, and flopping the remaining bits
// in a shift register which are then sent to the XiPhy 8 bits per pin per cycle. If CAS
// commands are issued so that the current ODT waveform overlaps with the waveform from
// previous commands, the waveforms will be OR'd together.
// Note: The original Olympus ODT block had separate timing for the selected rank and
// non-target ranks. This version of the code does not support this.
// ==========================================================================================
// ==========================================================================================
// Signal Declarations
// ==========================================================================================
// Structures holding multi-fabric-cycle ODT pin waveforms for current CAS transaction
wire [31:0] odt_array [ ODTBITS-1:0 ];
wire [31:0] odt_transent [ ODTBITS-1:0 ];
// Shift register holding ODT pin waveforms for previous CAS transactions
reg [23:0] odt_shift [ ODTBITS-1:0 ];
wire [23:0] odt_shift_nxt[ ODTBITS-1:0 ];
// ODT pin waveform for current fabric cycle, with time going from msb to lsb, reverse for XiPhy order
wire [ 7:0] odt_reverse [ ODTBITS-1:0 ];
// ==========================================================================================
// Module Code
// ==========================================================================================
// Set up basic write ODT timing waveform. Note that time increases moving from msb to lsb.
wire [31:0] odt_pulse_wr_slot0 = 32'hff_ff_ff_ff << (32 - 2*ODTWRDUR);
wire [31:0] odt_pulse_wr_slot1 = odt_pulse_wr_slot0 >> 2;
wire [31:0] odt_pulse_wr_slot2 = odt_pulse_wr_slot0 >> 4;
wire [31:0] odt_pulse_wr_slot3 = odt_pulse_wr_slot0 >> 6;
// Set up basic read ODT timing waveform. Note that time increases moving from msb to lsb.
wire [31:0] odt_pulse_rd_slot0 = ( 32'hff_ff_ff_ff << (32 - 2*ODTRDDUR) ) >> ( 2*( ODTRDDEL - ODTWRDEL ) );
wire [31:0] odt_pulse_rd_slot1 = odt_pulse_rd_slot0 >> 2;
wire [31:0] odt_pulse_rd_slot2 = odt_pulse_rd_slot0 >> 4;
wire [31:0] odt_pulse_rd_slot3 = odt_pulse_rd_slot0 >> 6;
// Select ODT timing waveform based on winning command type and slot position
wire [31:0] win_odt_pulse_slot0 = winRead ? odt_pulse_rd_slot0 : odt_pulse_wr_slot0;
wire [31:0] win_odt_pulse_slot1 = winRead ? odt_pulse_rd_slot1: odt_pulse_wr_slot1;
wire [31:0] win_odt_pulse_slot2 = winRead ? odt_pulse_rd_slot2 : odt_pulse_wr_slot2;
wire [31:0] win_odt_pulse_slot3 = winRead ? odt_pulse_rd_slot3 : odt_pulse_wr_slot3;
wire [31:0] win_odt_pulse = casSlot2 ? (casSlot[0] ? win_odt_pulse_slot3 : win_odt_pulse_slot2)
: (casSlot[0] ? win_odt_pulse_slot1 : win_odt_pulse_slot0);
// Select ODT pin pattern based on winning command type and rank
wire [15:0] win_odt_cmd_pat = winRead ? ODTRD : ODTWR;
wire [ 3:0] win_odt_pat = { 4 { winRead | winWrite } } & win_odt_cmd_pat[ 4*rank +:4 ]; // spyglass disable W498
genvar odt_pin;
generate
for (odt_pin = 0; odt_pin < ODTBITS; odt_pin = odt_pin + 1) begin
// Combine selected waveform and pattern to generate full ODT output for the current winning CAS command
assign odt_array[odt_pin] = { 32 { win_odt_pat[ odt_pin ] } } & win_odt_pulse;
// Qualify with tranSendC
assign odt_transent[odt_pin] = { 32 { tranSentC } } & odt_array[ odt_pin ];
// Parallel load lower 24 bits of qualified ODT output into shift register
assign odt_shift_nxt[odt_pin] = odt_transent[odt_pin][23:0] | { odt_shift[odt_pin][15:0], 8'b0 };
// Combine the upper 8 bits of the odt output for the new transaction (bypass path) with
// the upper 8 bits of the shift register output to generate the odt block's output
assign odt_reverse[odt_pin] = odt_transent[odt_pin][31:24] | odt_shift[odt_pin][23:16];
// Reverse the msb/lsb order. XiPhy wants increasing time going from lsb to msb
assign mc_ODT[odt_pin*8+:8] = { odt_reverse[odt_pin][0], odt_reverse[odt_pin][1], odt_reverse[odt_pin][2], odt_reverse[odt_pin][3],
odt_reverse[odt_pin][4], odt_reverse[odt_pin][5], odt_reverse[odt_pin][6], odt_reverse[odt_pin][7] };
end
endgenerate
// ==========================================================================================
// Reset flops
// ==========================================================================================
integer i;
always @(posedge clk) begin
if (rst) begin
for (i = 0; i < ODTBITS; i = i + 1) begin
odt_shift[i] <= #TCQ 28'b0;
end
end else begin
for (i = 0; i < ODTBITS; i = i + 1) begin
odt_shift[i] <= #TCQ odt_shift_nxt[i];
end
end
end
endmodule
|