blob: 719cfd530644d7fd9a467241b0de94a1f45dd09d [file] [log] [blame]
Reka Normanb05edb12021-09-09 14:49:55 +10001/* SPDX-License-Identifier: GPL-2.0-or-later */
2
3package main
4
5import (
6 "encoding/csv"
7 "fmt"
8 "io"
9 "io/ioutil"
10 "log"
11 "os"
12 "path/filepath"
13 "strconv"
Reka Norman4e4e2d72021-09-13 11:47:54 +100014 "strings"
Reka Normanb05edb12021-09-09 14:49:55 +100015)
16
17/*
18 * This program allocates DRAM strap IDs for different parts that are being used by the variant.
19 *
20 * It expects the following inputs:
Reka Norman4e4e2d72021-09-13 11:47:54 +100021 * Name of the SoC platform, e.g. TGL.
22 * Memory technology used by the variant, e.g. lp4x.
Martin Roth2dd12182024-01-18 16:24:41 -070023 * Path to Makefile directory. Makefile.mk generated by this program is placed in this
Reka Normanb05edb12021-09-09 14:49:55 +100024 * location.
25 * Text file containing a list of memory part names used by the board. Each line in the file
26 * is expected to have one memory part name.
27 */
28const (
Reka Norman4e4e2d72021-09-13 11:47:54 +100029 SPDManifestFileName = "parts_spd_manifest.generated.txt"
30 PlatformsManifestFileName = "platforms_manifest.generated.txt"
31 SPDEmptyFileName = "spd-empty.hex"
Martin Roth2dd12182024-01-18 16:24:41 -070032 MakefileName = "Makefile.mk"
Reka Norman4e4e2d72021-09-13 11:47:54 +100033 DRAMIdFileName = "dram_id.generated.txt"
34 MaxMemoryId = 15
Reka Normanb05edb12021-09-09 14:49:55 +100035)
36
Reka Norman4e4e2d72021-09-13 11:47:54 +100037var supportedPlatforms = [...]string{
38 "TGL",
39 "ADL",
40 "JSL",
41 "PCO",
Reka Norman4e4e2d72021-09-13 11:47:54 +100042 "CZN",
EricKY Cheng20f092d2022-08-27 00:05:22 +080043 "MDN",
Subrata Banik123bcb72022-06-28 15:43:00 +053044 "MTL",
Karthikeyan Ramasubramanian9e45e322023-03-31 15:24:00 -060045 "PHX",
Reka Norman4e4e2d72021-09-13 11:47:54 +100046}
47
48var supportedMemTechs = [...]string{
49 "lp4x",
50 "ddr4",
Reka Norman2c439ad2021-10-07 16:08:18 +110051 "lp5",
Reka Norman4e4e2d72021-09-13 11:47:54 +100052}
53
Reka Normanb05edb12021-09-09 14:49:55 +100054func usage() {
Reka Norman4e4e2d72021-09-13 11:47:54 +100055 fmt.Printf("\nUsage: %s <platform> <mem_technology> <makefile_dir> <mem_parts_used_file>\n\n", os.Args[0])
Reka Normanb05edb12021-09-09 14:49:55 +100056 fmt.Printf(" where,\n")
Reka Norman4e4e2d72021-09-13 11:47:54 +100057 fmt.Printf(" platform = SoC platform which the board is based on\n")
58 fmt.Printf(" supported platforms: %v\n", supportedPlatforms)
59 fmt.Printf(" mem_technology = Memory technology used by the board\n")
60 fmt.Printf(" supported technologies: %v\n", supportedMemTechs)
Martin Roth2dd12182024-01-18 16:24:41 -070061 fmt.Printf(" makefile_dir = Directory path where generated Makefile.mk should be placed\n")
Reka Normanb05edb12021-09-09 14:49:55 +100062 fmt.Printf(" mem_parts_used_file = CSV file containing list of memory parts used by the board and optional fixed ids\n\n\n")
63}
64
Reka Norman4e4e2d72021-09-13 11:47:54 +100065func checkArgs(platform string, memTech string, makefileDir string, memPartsUsedFile string) error {
66 supported := false
67 for _, p := range supportedPlatforms {
68 if strings.ToUpper(platform) == p {
69 supported = true
70 break
Reka Normanb05edb12021-09-09 14:49:55 +100071 }
72 }
Reka Norman4e4e2d72021-09-13 11:47:54 +100073 if !supported {
74 return fmt.Errorf("Platform %s is not supported", platform)
75 }
76
77 supported = false
78 for _, m := range supportedMemTechs {
79 if strings.ToLower(memTech) == m {
80 supported = true
81 break
82 }
83 }
84 if !supported {
85 return fmt.Errorf("Memory technology %s is not supported", memTech)
86 }
87
88 if _, err := os.Stat(makefileDir); err != nil {
89 return fmt.Errorf("Invalid makefile_dir %s: %v", makefileDir, err)
90 }
91
92 if _, err := os.Stat(memPartsUsedFile); err != nil {
93 return fmt.Errorf("Invalid mem_parts_used_file %s: %v", memPartsUsedFile, err)
94 }
Reka Normanb05edb12021-09-09 14:49:55 +100095
96 return nil
97}
98
Robert Ziebaa6425f12022-03-17 13:14:12 -060099type mappingType int
100
101const (
102 Auto mappingType = iota
103 Fixed
104 Exclusive
105)
106
Reka Normanb05edb12021-09-09 14:49:55 +1000107type usedPart struct {
Robert Zieba255b1fb2022-04-01 09:48:28 -0600108 partName string
109 index int
110 mapping mappingType
111 SPDOverride string
Reka Normanb05edb12021-09-09 14:49:55 +1000112}
113
Reka Norman4e4e2d72021-09-13 11:47:54 +1000114func readPlatformsManifest(memTech string) (map[string]string, error) {
115 manifestFilePath := filepath.Join("spd", strings.ToLower(memTech), PlatformsManifestFileName)
116 f, err := os.Open(manifestFilePath)
117 if err != nil {
118 return nil, err
119 }
120 defer f.Close()
121 r := csv.NewReader(f)
122 r.Comment = '#'
123
124 platformToSetMap := make(map[string]string)
125
126 for {
127 fields, err := r.Read()
128
129 if err == io.EOF {
130 break
131 }
132
133 if err != nil {
134 return nil, err
135 }
136
137 if len(fields) != 2 {
138 return nil, fmt.Errorf("Platforms manifest file is incorrectly formatted: %s", manifestFilePath)
139 }
140
141 platformToSetMap[fields[0]] = fields[1]
142 }
143
144 return platformToSetMap, nil
145}
146
147func getSPDDir(platform string, memTech string) (string, error) {
148 platformToSetMap, err := readPlatformsManifest(memTech)
149 if err != nil {
150 return "", err
151 }
152
153 setName, ok := platformToSetMap[strings.ToUpper(platform)]
154 if !ok {
155 return "", fmt.Errorf("Platform %s does not support memory technology %s", platform, memTech)
156 }
157
158 return filepath.Join("spd", strings.ToLower(memTech), setName), nil
159}
160
Reka Normanb05edb12021-09-09 14:49:55 +1000161/*
162 * Read input file CSV that contains list of memory part names used by the variant
163 * and an optional assigned id.
164 */
165func readParts(memPartsUsedFileName string) ([]usedPart, error) {
Reka Normanb05edb12021-09-09 14:49:55 +1000166 f, err := os.Open(memPartsUsedFileName)
167 if err != nil {
168 return nil, err
169 }
170 defer f.Close()
171 r := csv.NewReader(f)
172 r.FieldsPerRecord = -1 // Allow variable length records
173 r.TrimLeadingSpace = true
174 r.Comment = '#'
175
176 parts := []usedPart{}
177
178 for {
179 fields, err := r.Read()
180
181 if err == io.EOF {
182 break
183 }
184
185 if err != nil {
186 return nil, err
187 }
188
189 if len(fields) == 1 {
Robert Zieba255b1fb2022-04-01 09:48:28 -0600190 parts = append(parts, usedPart{fields[0], -1, Auto, ""})
191 } else {
Robert Ziebaa6425f12022-03-17 13:14:12 -0600192 var mapping = Auto
193 var assignedId = -1
194 var err error = nil
Robert Zieba255b1fb2022-04-01 09:48:28 -0600195 var spdOverride string = ""
Robert Ziebaa6425f12022-03-17 13:14:12 -0600196
Robert Zieba255b1fb2022-04-01 09:48:28 -0600197 // Second column, ID override
198 if len(fields) >= 2 {
199 if len(fields[1]) >= 2 && fields[1][0] == '*' {
200 // Exclusive mapping
201 mapping = Exclusive
202 assignedId, err = strconv.Atoi(fields[1][1:])
203 } else if fields[1] != "" {
204 // Fixed mapping
205 mapping = Fixed
206 assignedId, err = strconv.Atoi(fields[1])
207 }
208 }
209
210 // Third column, SPD file override
211 if len(fields) >= 3 {
212 if len(fields[2]) == 0 {
213 err = fmt.Errorf("mem_parts_used_file file is incorrectly formatted, SPD file column is empty")
214 } else {
215 spdOverride = fields[2]
216 }
Robert Ziebaa6425f12022-03-17 13:14:12 -0600217 }
218
Reka Normanb05edb12021-09-09 14:49:55 +1000219 if err != nil {
220 return nil, err
221 }
Robert Zieba255b1fb2022-04-01 09:48:28 -0600222
223 if assignedId > MaxMemoryId {
Reka Normanb05edb12021-09-09 14:49:55 +1000224 return nil, fmt.Errorf("Out of bounds assigned id %d for part %s", assignedId, fields[0])
225 }
Robert Zieba255b1fb2022-04-01 09:48:28 -0600226
227 parts = append(parts, usedPart{fields[0], assignedId, mapping, spdOverride})
Reka Normanb05edb12021-09-09 14:49:55 +1000228 }
229 }
230
231 return parts, nil
232}
233
234/*
235 * Read SPD manifest file(CSV) generated by gen_spd program and generate two maps:
236 * 1. Part to SPD Map : This maps global memory part name to generated SPD file name
237 * 2. SPD to Index Map: This generates a map of deduplicated SPD file names to index assigned to
238 * that SPD. This function sets the index for all SPDs to -1. This index gets
239 * updated as part of genPartIdInfo() depending upon the SPDs actually used
240 * by the variant.
241 */
242func readSPDManifest(SPDDirName string) (map[string]string, map[string]int, error) {
243 f, err := os.Open(filepath.Join(SPDDirName, SPDManifestFileName))
244 if err != nil {
245 return nil, nil, err
246 }
247 defer f.Close()
248 r := csv.NewReader(f)
249 r.Comment = '#'
250
251 partToSPDMap := make(map[string]string)
252 SPDToIndexMap := make(map[string]int)
253
254 for {
255 fields, err := r.Read()
256
257 if err == io.EOF {
258 break
259 }
260
261 if err != nil {
262 return nil, nil, err
263 }
264
265 if len(fields) != 2 {
266 return nil, nil, fmt.Errorf("CSV file is incorrectly formatted")
267 }
268
269 partToSPDMap[fields[0]] = fields[1]
270 SPDToIndexMap[fields[1]] = -1
271 }
272
273 return partToSPDMap, SPDToIndexMap, nil
274}
275
276/* Print information about memory part used by variant and ID assigned to it. */
277func appendPartIdInfo(s *string, partName string, index int) {
278 *s += fmt.Sprintf("%-30s %d (%04b)\n", partName, index, int64(index))
279}
280
281type partIds struct {
282 SPDFileName string
283 memParts string
284}
285
Reka Normanf4be6f62021-09-16 10:50:02 +1000286func getFileHeader() string {
Robert Ziebaa6425f12022-03-17 13:14:12 -0600287 return `# SPDX-License-Identifier: GPL-2.0-or-later
Reka Normanf4be6f62021-09-16 10:50:02 +1000288# This is an auto-generated file. Do not edit!!
289# Generated by:
290` + fmt.Sprintf("# %s\n\n", strings.Join(os.Args[0:], " "))
291}
292
Reka Normanb05edb12021-09-09 14:49:55 +1000293/*
294 * For each part used by the variant, check if the SPD (as per the manifest) already has an ID
295 * assigned to it. If yes, then add the part name to the list of memory parts supported by the
296 * SPD entry. If not, then assign the next ID to the SPD file and add the part name to the
297 * list of memory parts supported by the SPD entry.
298 *
299 * Returns list of partIds that contains spdFileName and supported memory parts for each
300 * assigned ID.
301 */
302func genPartIdInfo(parts []usedPart, partToSPDMap map[string]string, SPDToIndexMap map[string]int, makefileDirName string) ([]partIds, error) {
Reka Normanb05edb12021-09-09 14:49:55 +1000303 partIdList := []partIds{}
Robert Ziebaa6425f12022-03-17 13:14:12 -0600304 assignedMapping := []mappingType{}
Reka Normanb05edb12021-09-09 14:49:55 +1000305 var s string
306
307 // Assign parts with fixed ids first
308 for _, p := range parts {
Reka Normanb05edb12021-09-09 14:49:55 +1000309 if p.index == -1 {
310 continue
311 }
312
313 if p.partName == "" {
314 return nil, fmt.Errorf("Invalid part entry")
315 }
316
317 SPDFileName, ok := partToSPDMap[p.partName]
318 if !ok {
Robert Zieba255b1fb2022-04-01 09:48:28 -0600319 return nil, fmt.Errorf("Failed to find part %s in SPD Manifest. Please add the part to global part list and regenerate SPD Manifest", p.partName)
Reka Normanb05edb12021-09-09 14:49:55 +1000320 }
321
Robert Ziebaa6425f12022-03-17 13:14:12 -0600322 // Extend partIdList and assignedMapping with empty entries if needed
Reka Normanb05edb12021-09-09 14:49:55 +1000323 for i := len(partIdList) - 1; i < p.index; i++ {
324 partIdList = append(partIdList, partIds{})
Robert Ziebaa6425f12022-03-17 13:14:12 -0600325 assignedMapping = append(assignedMapping, Auto)
Reka Normanb05edb12021-09-09 14:49:55 +1000326 }
327
Robert Ziebaa6425f12022-03-17 13:14:12 -0600328 // Only allow parts with the same index if they share the same SPD
329 assignedSPD := partIdList[p.index].SPDFileName
330 if assignedSPD != "" && assignedSPD != partToSPDMap[p.partName] {
331 return nil, fmt.Errorf("ID %d is already assigned to %s, conflicting with %s(%s)", p.index, assignedSPD, p.partName, SPDFileName)
Reka Normanb05edb12021-09-09 14:49:55 +1000332 }
333
Robert Ziebaa6425f12022-03-17 13:14:12 -0600334 mapping := assignedMapping[p.index]
335 if (mapping == Fixed && p.mapping == Exclusive) || (mapping == Exclusive && p.mapping == Fixed) {
336 return nil, fmt.Errorf("Exclusive/non-exclusive conflict in assigning %s to ID %d", p.partName, p.index)
337 } else {
338 assignedMapping[p.index] = p.mapping
339 }
340
341 if partIdList[p.index].memParts == "" {
342 partIdList[p.index] = partIds{SPDFileName: SPDFileName, memParts: p.partName}
343 } else {
344 partIdList[p.index].memParts += ", " + p.partName
345 }
Reka Normanb05edb12021-09-09 14:49:55 +1000346
347 // SPDToIndexMap should point to first assigned index in the used part list
Robert Ziebaa6425f12022-03-17 13:14:12 -0600348 // Exclusive entries don't update the map because they're not valid for auto assigning
349 if SPDToIndexMap[SPDFileName] < 0 && p.mapping != Exclusive {
Reka Normanb05edb12021-09-09 14:49:55 +1000350 SPDToIndexMap[SPDFileName] = p.index
351 }
352 }
353
354 s += fmt.Sprintf("%-30s %s\n", "DRAM Part Name", "ID to assign")
355
356 // Assign parts with no fixed id
357 for _, p := range parts {
358 if p.partName == "" {
359 return nil, fmt.Errorf("Invalid part entry")
360 }
361
362 // Add assigned parts to dram id file in the order they appear
363 if p.index != -1 {
364 appendPartIdInfo(&s, p.partName, p.index)
365 continue
366 }
367
368 SPDFileName, ok := partToSPDMap[p.partName]
369 if !ok {
370 return nil, fmt.Errorf("Failed to find part ", p.partName, " in SPD Manifest. Please add the part to global part list and regenerate SPD Manifest")
371 }
372
373 index := SPDToIndexMap[SPDFileName]
Robert Ziebaa6425f12022-03-17 13:14:12 -0600374 // Only Exclusive mappings don't allow automatic assigning of parts
375 if index != -1 && assignedMapping[index] != Exclusive {
Reka Normanb05edb12021-09-09 14:49:55 +1000376 partIdList[index].memParts += ", " + p.partName
377 appendPartIdInfo(&s, p.partName, index)
378 continue
379 }
380
381 // Find first empty index
382 for i, partId := range partIdList {
383 if partId.SPDFileName == "" {
384 index = i
385 break
386 }
387 }
388
389 // Append new entry
390 if index == -1 {
391 index = len(partIdList)
Reka Norman780b04e2021-09-16 10:12:31 +1000392 if index > MaxMemoryId {
393 return nil, fmt.Errorf("Maximum part ID %d exceeded.", MaxMemoryId)
394 }
Reka Normanb05edb12021-09-09 14:49:55 +1000395 partIdList = append(partIdList, partIds{})
Robert Ziebaa6425f12022-03-17 13:14:12 -0600396 assignedMapping = append(assignedMapping, Auto)
Reka Normanb05edb12021-09-09 14:49:55 +1000397 }
398
399 SPDToIndexMap[SPDFileName] = index
400 appendPartIdInfo(&s, p.partName, index)
401 partIdList[index] = partIds{SPDFileName: SPDFileName, memParts: p.partName}
402 }
403
404 fmt.Printf("%s", s)
Reka Normanf4be6f62021-09-16 10:50:02 +1000405
406 s = getFileHeader() + s
Reka Normanb05edb12021-09-09 14:49:55 +1000407 err := ioutil.WriteFile(filepath.Join(makefileDirName, DRAMIdFileName), []byte(s), 0644)
408
409 return partIdList, err
410}
411
Reka Normanb05edb12021-09-09 14:49:55 +1000412/*
Martin Roth2dd12182024-01-18 16:24:41 -0700413 * This function generates Makefile.mk under the variant directory path and adds assigned SPDs
Reka Normanb05edb12021-09-09 14:49:55 +1000414 * to SPD_SOURCES.
415 */
Robert Zieba255b1fb2022-04-01 09:48:28 -0600416func genMakefile(partIdList []partIds, makefileDirName string, SPDDir string, partsDir string) error {
Reka Normanf4be6f62021-09-16 10:50:02 +1000417 s := getFileHeader()
Reka Normanb05edb12021-09-09 14:49:55 +1000418 s += fmt.Sprintf("SPD_SOURCES =\n")
419
420 for i := 0; i < len(partIdList); i++ {
421 if partIdList[i].SPDFileName == "" {
422 s += fmt.Sprintf("SPD_SOURCES += %v ", filepath.Join(SPDDir, SPDEmptyFileName))
423 s += fmt.Sprintf(" # ID = %d(0b%04b)\n", i, int64(i))
424 } else {
Robert Zieba255b1fb2022-04-01 09:48:28 -0600425 SPDFileName := partIdList[i].SPDFileName
426 path := filepath.Join(partsDir, SPDFileName)
427
428 // Check if the file exists in the directory of the parts file
429 if _, err := os.Stat(path); err != nil {
430 // File doesn't exist, check spd directory
431 path = filepath.Join(SPDDir, SPDFileName)
432 if _, err = os.Stat(path); err != nil {
433 return fmt.Errorf("Failed to write Makefile, SPD file '%s' doesn't exist", SPDFileName)
434 }
435 }
436 s += fmt.Sprintf("SPD_SOURCES += %v ", path)
Reka Normanb05edb12021-09-09 14:49:55 +1000437 s += fmt.Sprintf(" # ID = %d(0b%04b) ", i, int64(i))
438 s += fmt.Sprintf(" Parts = %04s\n", partIdList[i].memParts)
439 }
440 }
441
442 return ioutil.WriteFile(filepath.Join(makefileDirName, MakefileName), []byte(s), 0644)
443}
444
445func main() {
Reka Norman4e4e2d72021-09-13 11:47:54 +1000446 if len(os.Args) != 5 {
Reka Normanb05edb12021-09-09 14:49:55 +1000447 usage()
448 log.Fatal("Incorrect number of arguments")
449 }
450
Reka Norman4e4e2d72021-09-13 11:47:54 +1000451 platform, memTech, makefileDir, memPartsUsedFile := os.Args[1], os.Args[2], os.Args[3], os.Args[4]
452
453 err := checkArgs(platform, memTech, makefileDir, memPartsUsedFile)
454 if err != nil {
455 log.Fatal(err)
456 }
457
458 SPDDir, err := getSPDDir(platform, memTech)
459 if err != nil {
460 log.Fatal(err)
461 }
Reka Normanb05edb12021-09-09 14:49:55 +1000462
463 partToSPDMap, SPDToIndexMap, err := readSPDManifest(SPDDir)
464 if err != nil {
465 log.Fatal(err)
466 }
467
Reka Norman4e4e2d72021-09-13 11:47:54 +1000468 parts, err := readParts(memPartsUsedFile)
Reka Normanb05edb12021-09-09 14:49:55 +1000469 if err != nil {
470 log.Fatal(err)
471 }
472
Robert Zieba255b1fb2022-04-01 09:48:28 -0600473 // Update our SPD maps with part specific overrides
474 for _, p := range parts {
475 if p.SPDOverride != "" {
476 partToSPDMap[p.partName] = p.SPDOverride
477 SPDToIndexMap[p.SPDOverride] = -1
478 }
479 }
480
Reka Norman4e4e2d72021-09-13 11:47:54 +1000481 partIdList, err := genPartIdInfo(parts, partToSPDMap, SPDToIndexMap, makefileDir)
Reka Normanb05edb12021-09-09 14:49:55 +1000482 if err != nil {
483 log.Fatal(err)
484 }
485
Robert Zieba255b1fb2022-04-01 09:48:28 -0600486 if err := genMakefile(partIdList, makefileDir, SPDDir, filepath.Dir(memPartsUsedFile)); err != nil {
Reka Normanb05edb12021-09-09 14:49:55 +1000487 log.Fatal(err)
488 }
489}