-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy patha10-dram-timings-calculator
executable file
·333 lines (298 loc) · 12 KB
/
a10-dram-timings-calculator
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
#!/usr/bin/env ruby
#
# Copyright © 2014 Siarhei Siamashka <[email protected]>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
VERBOSE = false
require_relative 'a10-dram-info.rb'
###############################################################################
# Bitfields for DDR_PHYCTL_DTPR0, DDR_PHYCTL_DTPR1, DDR_PHYCTL_DTPR2 from
# RK30xx manual represented as [highest bit, lowest bit, add constant, name].
# Update: actually the RK29 sources are a somewhat better match.
$tpr_bitfields = [
[ # .tpr0
[31, 31, 4, :tCCD],
[30, 25, 0, :tRC],
[24, 21, 0, :tRRD],
[20, 16, 0, :tRAS],
[15, 12, 0, :tRCD],
[11, 8, 0, :tRP],
[ 7, 5, 0, :tWTR],
[ 4, 2, 0, :tRTP],
[ 1, 0, 4, :tMRD],
],
[ # .tpr1
[15, 14, 0, :tRNKWTW],
[13, 12, 0, :tRNKRTR],
[11, 11, 0, :tRTODT],
[10, 9, 12, :tMOD],
[ 8, 3, 0, :tFAW],
[ 2, 2, 0, :tRTW],
],
[ # .tpr2
[18, 15, 0, :tCKE],
[14, 10, 0, :tXP],
[ 9, 0, 0, :tXS],
]
]
# Convert an array with 3 magic constants to a key->value hash
def convert_from_tpr(tpr)
result = {}
tpr.each_index {|i|
$tpr_bitfields[i].each {|v|
maxbit = v[0]
minbit = v[1]
x = (tpr[i] >> minbit) & ((1 << (maxbit - minbit + 1)) - 1)
result[v[3]] = x + v[2]
}
}
return result
end
# Convert from a key->value hash back to an array with 3 magic constants
def convert_to_tpr(a)
result = [0, 0, 0]
tmp_hash = {}
$tpr_bitfields.each_index {|i|
$tpr_bitfields[i].each {|v|
tmp_hash[v[3]] = [i, v[1], v[2]]
}
}
a.each {|k, v|
result[tmp_hash[k][0]] |= (v - tmp_hash[k][2]) << tmp_hash[k][1]
}
return result
end
# Get CL value for the selected DRAM clock frequency
def get_cl(dram_freq, dram_timings)
tCK = 1000.0 / dram_freq
dram_timings[:tCK].each {|v| return v[1] if v[0] <= tCK }
end
def get_cwl(dram_freq, dram_timings)
tCK = 1000.0 / dram_freq
dram_timings[:tCK].each {|v| return v[2] if v[0] <= tCK }
end
def get_mr0_WR(dram_freq, dram_timings)
tCK = 1000.0 / dram_freq
return (dram_timings[:tWR][:ns] / tCK).ceil.to_i
end
# Convert from nanoseconds to cycles, but no less than 'min_ck'
def ns_to_ck(dram_freq, dram_timings, param_name)
min_ck = dram_timings[param_name][:ck]
ns = dram_timings[param_name][:ns]
if ns then
ck = (dram_freq * ns / 1000.0).ceil.to_i
ck = min_ck if min_ck and ck < min_ck
return ck
else
return min_ck
end
end
# A sanity check: ensure that the roundtrip conversion to the
# key->value hash and back to three 32-bit magic constants does
# not introduce any errors
def check_roundtrip_conversion(tpr)
if tpr.instance_of? Array then
# Array of 3 elements
new_tpr = convert_to_tpr(convert_from_tpr(tpr))
raise "Roundtrip conversion failed" if (new_tpr <=> tpr) != 0
else
# Hash with key->value pairs
new_tpr = convert_from_tpr(convert_to_tpr(tpr))
raise "Roundtrip conversion failed" if new_tpr.size != tpr.size
new_tpr.each {|k, v|
raise "Roundtrip conversion failed" if tpr[k] != v
}
end
end
def calc_tpr(dram_freq, dram_timings)
tpr_cas9 = [0x42d899b7, 0xa090, 0x22a00]
check_roundtrip_conversion(tpr_cas9)
tmp = convert_from_tpr(tpr_cas9)
tmp[:tXS] = ns_to_ck(dram_freq, dram_timings, :tXS)
tmp[:tRCD] = ns_to_ck(dram_freq, dram_timings, :tRCD)
tmp[:tRP] = ns_to_ck(dram_freq, dram_timings, :tRP)
tmp[:tRC] = ns_to_ck(dram_freq, dram_timings, :tRC)
tmp[:tRAS] = ns_to_ck(dram_freq, dram_timings, :tRAS)
tmp[:tFAW] = ns_to_ck(dram_freq, dram_timings, :tFAW)
tmp[:tRRD] = ns_to_ck(dram_freq, dram_timings, :tRRD)
tmp[:tCKE] = ns_to_ck(dram_freq, dram_timings, :tCKE)
tmp[:tWTR] = ns_to_ck(dram_freq, dram_timings, :tWTR)
tmp[:tXP] = ns_to_ck(dram_freq, dram_timings, :tXP)
tmp[:tMRD] = ns_to_ck(dram_freq, dram_timings, :tMRD)
tmp[:tRTP] = ns_to_ck(dram_freq, dram_timings, :tRTP)
tmp[:tRTW] = ns_to_ck(dram_freq, dram_timings, :tRTW)
tmp[:tMOD] = ns_to_ck(dram_freq, dram_timings, :tMOD)
tmp[:tRTODT] = ns_to_ck(dram_freq, dram_timings, :tRTODT)
tmp[:tCCD] = ns_to_ck(dram_freq, dram_timings, :tCCD)
# Apply extra requirements from the RK30XX manual:
# tXS = max(tXS, tXSDLL)
# tXP = max(tXP, tXPDLL)
# tRTP = max(tRTP, 2)
# tCKE = max(tCKE, tCKESR)
tmp[:tXS] = [tmp[:tXS], ns_to_ck(dram_freq, dram_timings, :tDLLK)].max
tmp[:tXP] = [tmp[:tXP], ns_to_ck(dram_freq, dram_timings, :tXPDLL)].max
tmp[:tRTP] = [tmp[:tRTP], 2].max
tmp[:tCKE] += 1 # tCKESR = tCKE(min) + 1 nCK
check_roundtrip_conversion(tmp)
return convert_to_tpr(tmp)
end
def print_verbose_timings_info(tmp, dram_freq, dram_timings)
used_keys = {}
printf("/* %s\n", "")
# Show the most interesting values in a special sorting order
[:tRCD, :tRP, :tRAS, :tRC, :tFAW, :tRRD, :tCKE, :tWTR, :tXP, :tMRD, :tRTP].each {|k|
used_keys[k] = 1
printf(" * %8s = %8.2f ns (%d)\n", k,
tmp[k].to_f * 1000 / dram_freq, tmp[k])
}
# Show the rest of the values in any order
tmp.each {|k, v|
next if used_keys[k]
printf(" * %8s = %8.2f ns (%d)\n", k,
tmp[k].to_f * 1000 / dram_freq, tmp[k])
}
printf(" *\n")
printf(" * WR (write recovery) needs to be set to at least %d in MR0\n",
get_mr0_WR(dram_freq, dram_timings))
printf(" */\n")
end
# Print a part of dram_para stuct for u-boot sources
def generate_dram_para(board, dram_freq)
dram_timings = board[:dram_chip]
tpr = calc_tpr(dram_freq, dram_timings)
tmp = convert_from_tpr(tpr)
cas = get_cl(dram_freq, dram_timings)
if VERBOSE then
print_verbose_timings_info(tmp, dram_freq, dram_timings)
end
printf("static struct dram_para dram_para = {")
printf(" /* DRAM timings: %d-%d-%d-%d (%d MHz) */\n",
cas, tmp[:tRCD], tmp[:tRP], tmp[:tRAS], dram_freq)
printf("\t.clock = %d,\n", dram_freq)
printf("\t.type = 3,\n")
printf("\t.rank_num = 1,\n")
if board[:dram_size] then
printf("\t.density = #{dram_timings[:density]},\n")
printf("\t.io_width = #{dram_timings[:io_width]},\n")
printf("\t.bus_width = %d,\n", 8 * board[:dram_size] /
dram_timings[:density] *
dram_timings[:io_width])
end
printf("\t.cas = %d,\n", cas)
printf("\t.zq = 0x%x,\n", board[:dram_para][:zq])
printf("\t.odt_en = %d,\n", board[:dram_para][:odt_en])
printf("\t.tpr0 = 0x%x,\n", tpr[0])
printf("\t.tpr1 = 0x%x,\n", tpr[1])
printf("\t.tpr2 = 0x%x,\n", tpr[2])
printf("\t.tpr3 = 0x%x,\n", board[:dram_para][:tpr3])
printf("\t.tpr4 = 0x%x,\n", board[:dram_para][:tpr4])
printf("\t.tpr5 = 0x0,\n")
printf("\t.emr1 = 0x%x,\n", board[:dram_para][:emr1])
printf("\t.emr2 = 0x%x,\n",
([get_cwl(dram_freq, dram_timings), 5].max - 5) << 3)
printf("\t.emr3 = 0x0,\n")
printf("\t.active_windowing = 1,\n")
printf("};\n")
end
###############################################################################
boards = get_the_list_of_boards()
board_info = ARGV[1] ? boards[ARGV[1]] : get_generic_board()
if not board_info or ARGV.include?("--help") then
printf("Usage: #{$PROGRAM_NAME} [dram_clock_frequency] <board_name>\n")
printf("\nThe list of supported boards:\n")
boards.sort.each {|name, info|
printf(" %s\n", name)
}
exit(1)
end
###############################################################################
# Generate only timings alone
def generate_timings(dram_timings, label, dram_freq)
tpr = calc_tpr(dram_freq, dram_timings)
tmp = convert_from_tpr(tpr)
cas = get_cl(dram_freq, dram_timings)
printf("/* %s @%dMHz, timings: %d-%d-%d-%d */\n",
label, dram_freq, cas, tmp[:tRCD], tmp[:tRP], tmp[:tRAS])
printf("\t.cas = %d,\n", cas)
printf("\t.tpr0 = 0x%x,\n", tpr[0])
printf("\t.tpr1 = 0x%x,\n", tpr[1])
printf("\t.tpr2 = 0x%x,\n", tpr[2])
printf("\t.emr2 = 0x%x,\n",
([get_cwl(dram_freq, dram_timings), 5].max - 5) << 3)
end
ddr3_800e = derive_from_JEDEC_DDR3_800_6_6_6({:page_size => 2048,
:tRTW => {:ck => 0},
:tRTODT => {:ck => 0},
:label => "DDR3-800E"})
ddr3_1066f = derive_from_JEDEC_DDR3_1066_7_7_7({:page_size => 2048,
:tRTW => {:ck => 0},
:tRTODT => {:ck => 0},
:label => "DDR3-1066F"})
ddr3_1066g = derive_from_JEDEC_DDR3_1066_8_8_8({:page_size => 2048,
:tRTW => {:ck => 0},
:tRTODT => {:ck => 0},
:label => "DDR3-1066G"})
ddr3_1333h = derive_from_JEDEC_DDR3_1333_9_9_9({:page_size => 2048,
:tRTW => {:ck => 0},
:tRTODT => {:ck => 0},
:label => "DDR3-1333H"})
ddr3_1333j = derive_from_JEDEC_DDR3_1333_10_10_10({:page_size => 2048,
:tRTW => {:ck => 0},
:tRTODT => {:ck => 0},
:label => "DDR3-1333J"})
if not ARGV[0]
dram_freq_list = ((360 .. 648).step(24).to_a + [396, 468, 540]).sort
printf("/* This file is automatically generated, do not edit */\n\n")
printf("#if defined(CONFIG_DRAM_TIMINGS_DDR3_1066F_1333H)\n")
printf("# ")
dram_freq_list.each {|dram_freq|
if dram_freq <= 533
dram_timings = ddr3_1066f
else
dram_timings = ddr3_1333h
end
printf("if CONFIG_DRAM_CLK <= %d ", dram_freq)
generate_timings(dram_timings, dram_timings[:label], dram_freq)
printf("# el")
}
printf("se\n")
printf("# error CONFIG_DRAM_CLK is set too high\n")
printf("# endif\n")
printf("#elif defined(CONFIG_DRAM_TIMINGS_DDR3_800E_1066G_1333J)\n")
printf("# ")
dram_freq_list.each {|dram_freq|
if dram_freq <= 400
dram_timings = ddr3_800e
elsif dram_freq <= 533
dram_timings = ddr3_1066g
else
dram_timings = ddr3_1333j
end
printf("if CONFIG_DRAM_CLK <= %d ", dram_freq)
generate_timings(dram_timings, dram_timings[:label], dram_freq)
printf("# el")
}
printf("se\n")
printf("# error CONFIG_DRAM_CLK is set too high\n")
printf("# endif\n")
printf("#else\n")
printf("# error CONFIG_DRAM_TIMINGS_* is not defined\n")
printf("#endif\n")
exit(0)
end
###############################################################################
# Sanitize input to put it into the [360, 648] range
dram_freq = [[ARGV[0].to_i, 300].max, 648].min
generate_dram_para(board_info, dram_freq)