From b05edb1fea25c71c802ba5bc6c64c7f9cb9a5e85 Mon Sep 17 00:00:00 2001 From: Reka Norman Date: Thu, 9 Sep 2021 14:49:55 +1000 Subject: util/spd_tools: Implement a unified version of the part_id_gen tool Currently there are two versions of gen_part_id.go, one for LP4x and one DDR4. This change implements a unified version of this tool. The new part_id_gen.go is almost identical to the existing ddr4/gen_part_id.go. The new version was based on the ddr4 version and not the lp4x version, since the ddr4 version contains extra logic to support fixed IDs in the mem_parts_used files. The only non-trivial change from ddr4/gen_part_id.go is to include the full paths of SPD files in the generated Makefile.inc. E.g. instead of SPD_SOURCES += lp4x-spd-1.hex the full path relative to the coreboot root directory is included: SPD_SOURCES += spd/lp4x/set-0/spd-1.hex BUG=b:191776301 TEST=For each variant of brya/volteer/dedede/guybrush/zork, run part_id_gen and verify that the generated Makefile.inc and dram_id.generated.txt are identical to those currently in the src tree, except for the modified SPD file paths in Makefile.inc. Example: util/spd_tools/bin/part_id_gen \ spd/lp4x/set-0 \ src/mainboard/google/brya/variants/kano/memory \ src/mainboard/google/brya/variants/kano/memory/mem_parts_used.txt Change-Id: Ib33d09076f340f688519dae7956a2b27af090c0b Signed-off-by: Reka Norman Reviewed-on: https://review.coreboot.org/c/coreboot/+/57585 Tested-by: build bot (Jenkins) Reviewed-by: Tim Wawrzynczak Reviewed-by: Furquan Shaikh Reviewed-by: Karthik Ramasubramanian --- util/spd_tools/Makefile | 6 +- util/spd_tools/src/part_id_gen/part_id_gen.go | 310 ++++++++++++++++++++++++++ 2 files changed, 315 insertions(+), 1 deletion(-) create mode 100644 util/spd_tools/src/part_id_gen/part_id_gen.go (limited to 'util') diff --git a/util/spd_tools/Makefile b/util/spd_tools/Makefile index 8a9d3924a1..d8806724fa 100644 --- a/util/spd_tools/Makefile +++ b/util/spd_tools/Makefile @@ -1,10 +1,14 @@ SPD_GEN = bin/spd_gen +PART_ID_GEN = bin/part_id_gen -all: $(SPD_GEN) +all: $(SPD_GEN) $(PART_ID_GEN) $(SPD_GEN): go build -o $(SPD_GEN) src/spd_gen/*.go +$(PART_ID_GEN): + go build -o $(PART_ID_GEN) src/part_id_gen/*.go + clean: rm -rf bin/ diff --git a/util/spd_tools/src/part_id_gen/part_id_gen.go b/util/spd_tools/src/part_id_gen/part_id_gen.go new file mode 100644 index 0000000000..3db2b6e8d9 --- /dev/null +++ b/util/spd_tools/src/part_id_gen/part_id_gen.go @@ -0,0 +1,310 @@ +/* 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: + * Path to SPD directory. This is the location where SPD files and SPD Manifest generated by + * gen_spd.go are placed. + * Path to Makefile directory. Makefile.inc generated by this program is placed in this + * location. + * Text file containing a list of memory part names used by the board. Each line in the file + * is expected to have one memory part name. + */ +const ( + SPDManifestFileName = "parts_spd_manifest.generated.txt" + SPDEmptyFileName = "spd-empty.hex" + MakefileName = "Makefile.inc" + DRAMIdFileName = "dram_id.generated.txt" + MaxMemoryId = 15 +) + +func usage() { + fmt.Printf("\nUsage: %s \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 the 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) + r.Comment = '#' + + 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 the 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, SPDDir 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 += %v ", filepath.Join(SPDDir, SPDEmptyFileName)) + s += fmt.Sprintf(" # ID = %d(0b%04b)\n", i, int64(i)) + } else { + s += fmt.Sprintf("SPD_SOURCES += %v ", filepath.Join(SPDDir, 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, SPDDir); err != nil { + log.Fatal(err) + } +} -- cgit v1.2.3