| /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| |
| package main |
| |
| import ( |
| "encoding/csv" |
| "fmt" |
| "io" |
| "io/ioutil" |
| "log" |
| "os" |
| "path/filepath" |
| "strconv" |
| ) |
| |
| /* |
| * This program allocates DRAM strap IDs for different parts that are being used by the variant. |
| * |
| * It expects the following inputs: |
| * Pointer to SPD directory. This is the location where SPD files and SPD Manifest generated by |
| * gen_spd.go are placed. |
| * Pointer to Makefile directory. Makefile.inc generated by this program is placed in this |
| * location. |
| * Text file containing a list of memory parts names used by the board. Each line in the file |
| * is expected to have one memory part name. |
| */ |
| const ( |
| SPDManifestFileName = "ddr4_spd_manifest.generated.txt" |
| MakefileName = "Makefile.inc" |
| DRAMIdFileName = "dram_id.generated.txt" |
| MaxMemoryId = 15 |
| ) |
| |
| func usage() { |
| fmt.Printf("\nUsage: %s <spd_dir> <makefile_dir> <mem_parts_used_file>\n\n", os.Args[0]) |
| fmt.Printf(" where,\n") |
| fmt.Printf(" spd_dir = Directory path containing SPD files and manifest generated by gen_spd.go\n") |
| fmt.Printf(" makefile_dir = Directory path where generated Makefile.inc should be placed\n") |
| fmt.Printf(" mem_parts_used_file = CSV file containing list of memory parts used by the board and optional fixed ids\n\n\n") |
| } |
| |
| func checkArgs() error { |
| |
| for _, arg := range os.Args[1:] { |
| if _, err := os.Stat(arg); err != nil { |
| return err |
| } |
| } |
| |
| return nil |
| } |
| |
| type usedPart struct { |
| partName string |
| index int |
| } |
| |
| /* |
| * Read input file CSV that contains list of memory part names used by the variant |
| * and an optional assigned id. |
| */ |
| func readParts(memPartsUsedFileName string) ([]usedPart, error) { |
| |
| f, err := os.Open(memPartsUsedFileName) |
| if err != nil { |
| return nil, err |
| } |
| defer f.Close() |
| r := csv.NewReader(f) |
| r.FieldsPerRecord = -1 // Allow variable length records |
| r.TrimLeadingSpace = true |
| r.Comment = '#' |
| |
| parts := []usedPart{} |
| |
| for { |
| fields, err := r.Read() |
| |
| if err == io.EOF { |
| break |
| } |
| |
| if err != nil { |
| return nil, err |
| } |
| |
| if len(fields) == 1 { |
| parts = append(parts, usedPart{fields[0], -1}) |
| } else if len(fields) == 2 { |
| assignedId, err := strconv.Atoi(fields[1]) |
| if err != nil { |
| return nil, err |
| } |
| if assignedId > MaxMemoryId || assignedId < 0 { |
| return nil, fmt.Errorf("Out of bounds assigned id %d for part %s", assignedId, fields[0]) |
| } |
| parts = append(parts, usedPart{fields[0], assignedId}) |
| } else { |
| return nil, fmt.Errorf("mem_parts_used_file file is incorrectly formatted") |
| } |
| } |
| |
| return parts, nil |
| } |
| |
| /* |
| * Read SPD manifest file(CSV) generated by gen_spd program and generate two maps: |
| * 1. Part to SPD Map : This maps global memory part name to generated SPD file name |
| * 2. SPD to Index Map: This generates a map of deduplicated SPD file names to index assigned to |
| * that SPD. This function sets index for all SPDs to -1. This index gets |
| * updated as part of genPartIdInfo() depending upon the SPDs actually used |
| * by the variant. |
| */ |
| func readSPDManifest(SPDDirName string) (map[string]string, map[string]int, error) { |
| f, err := os.Open(filepath.Join(SPDDirName, SPDManifestFileName)) |
| if err != nil { |
| return nil, nil, err |
| } |
| defer f.Close() |
| r := csv.NewReader(f) |
| |
| partToSPDMap := make(map[string]string) |
| SPDToIndexMap := make(map[string]int) |
| |
| for { |
| fields, err := r.Read() |
| |
| if err == io.EOF { |
| break |
| } |
| |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| if len(fields) != 2 { |
| return nil, nil, fmt.Errorf("CSV file is incorrectly formatted") |
| } |
| |
| partToSPDMap[fields[0]] = fields[1] |
| SPDToIndexMap[fields[1]] = -1 |
| } |
| |
| return partToSPDMap, SPDToIndexMap, nil |
| } |
| |
| /* Print information about memory part used by variant and ID assigned to it. */ |
| func appendPartIdInfo(s *string, partName string, index int) { |
| *s += fmt.Sprintf("%-30s %d (%04b)\n", partName, index, int64(index)) |
| } |
| |
| type partIds struct { |
| SPDFileName string |
| memParts string |
| } |
| |
| /* |
| * For each part used by variant, check if the SPD (as per the manifest) already has an ID |
| * assigned to it. If yes, then add the part name to the list of memory parts supported by the |
| * SPD entry. If not, then assign the next ID to the SPD file and add the part name to the |
| * list of memory parts supported by the SPD entry. |
| * |
| * Returns list of partIds that contains spdFileName and supported memory parts for each |
| * assigned ID. |
| */ |
| func genPartIdInfo(parts []usedPart, partToSPDMap map[string]string, SPDToIndexMap map[string]int, makefileDirName string) ([]partIds, error) { |
| |
| partIdList := []partIds{} |
| var s string |
| |
| // Assign parts with fixed ids first |
| for _, p := range parts { |
| |
| if p.index == -1 { |
| continue |
| } |
| |
| if p.partName == "" { |
| return nil, fmt.Errorf("Invalid part entry") |
| } |
| |
| SPDFileName, ok := partToSPDMap[p.partName] |
| if !ok { |
| 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") |
| } |
| |
| // Extend partIdList with empty entries if needed |
| for i := len(partIdList) - 1; i < p.index; i++ { |
| partIdList = append(partIdList, partIds{}) |
| } |
| |
| if partIdList[p.index].SPDFileName != "" { |
| return nil, fmt.Errorf("Part ", p.partName, " is assigned to an already assigned ID ", p.index) |
| } |
| |
| partIdList[p.index] = partIds{SPDFileName: SPDFileName, memParts: p.partName} |
| |
| // SPDToIndexMap should point to first assigned index in the used part list |
| if SPDToIndexMap[SPDFileName] < 0 { |
| SPDToIndexMap[SPDFileName] = p.index |
| } |
| } |
| |
| s += fmt.Sprintf("%-30s %s\n", "DRAM Part Name", "ID to assign") |
| |
| // Assign parts with no fixed id |
| for _, p := range parts { |
| if p.partName == "" { |
| return nil, fmt.Errorf("Invalid part entry") |
| } |
| |
| // Add assigned parts to dram id file in the order they appear |
| if p.index != -1 { |
| appendPartIdInfo(&s, p.partName, p.index) |
| continue |
| } |
| |
| SPDFileName, ok := partToSPDMap[p.partName] |
| if !ok { |
| 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") |
| } |
| |
| index := SPDToIndexMap[SPDFileName] |
| if index != -1 { |
| partIdList[index].memParts += ", " + p.partName |
| appendPartIdInfo(&s, p.partName, index) |
| continue |
| } |
| |
| // Find first empty index |
| for i, partId := range partIdList { |
| if partId.SPDFileName == "" { |
| index = i |
| break |
| } |
| } |
| |
| // Append new entry |
| if index == -1 { |
| index = len(partIdList) |
| partIdList = append(partIdList, partIds{}) |
| } |
| |
| SPDToIndexMap[SPDFileName] = index |
| appendPartIdInfo(&s, p.partName, index) |
| partIdList[index] = partIds{SPDFileName: SPDFileName, memParts: p.partName} |
| } |
| |
| fmt.Printf("%s", s) |
| err := ioutil.WriteFile(filepath.Join(makefileDirName, DRAMIdFileName), []byte(s), 0644) |
| |
| return partIdList, err |
| } |
| |
| var generatedCodeLicense string = "## SPDX-License-Identifier: GPL-2.0-or-later" |
| var autoGeneratedInfo string = "## This is an auto-generated file. Do not edit!!" |
| |
| /* |
| * This function generates Makefile.inc under the variant directory path and adds assigned SPDs |
| * to SPD_SOURCES. |
| */ |
| func genMakefile(partIdList []partIds, makefileDirName string) error { |
| var s string |
| |
| s += fmt.Sprintf("%s\n%s\n\n", generatedCodeLicense, autoGeneratedInfo) |
| s += fmt.Sprintf("SPD_SOURCES =\n") |
| |
| for i := 0; i < len(partIdList); i++ { |
| if partIdList[i].SPDFileName == "" { |
| s += fmt.Sprintf("SPD_SOURCES += %s ", "ddr4-spd-empty.hex") |
| s += fmt.Sprintf(" # ID = %d(0b%04b)\n", i, int64(i)) |
| } else { |
| s += fmt.Sprintf("SPD_SOURCES += %s ", partIdList[i].SPDFileName) |
| s += fmt.Sprintf(" # ID = %d(0b%04b) ", i, int64(i)) |
| s += fmt.Sprintf(" Parts = %04s\n", partIdList[i].memParts) |
| } |
| } |
| |
| return ioutil.WriteFile(filepath.Join(makefileDirName, MakefileName), []byte(s), 0644) |
| } |
| |
| func main() { |
| if len(os.Args) != 4 { |
| usage() |
| log.Fatal("Incorrect number of arguments") |
| } |
| |
| SPDDir, MakefileDir, MemPartsUsedFile := os.Args[1], os.Args[2], os.Args[3] |
| |
| partToSPDMap, SPDToIndexMap, err := readSPDManifest(SPDDir) |
| if err != nil { |
| log.Fatal(err) |
| } |
| |
| parts, err := readParts(MemPartsUsedFile) |
| if err != nil { |
| log.Fatal(err) |
| } |
| |
| partIdList, err := genPartIdInfo(parts, partToSPDMap, SPDToIndexMap, MakefileDir) |
| if err != nil { |
| log.Fatal(err) |
| } |
| |
| if err := genMakefile(partIdList, MakefileDir); err != nil { |
| log.Fatal(err) |
| } |
| } |