summaryrefslogtreecommitdiff
path: root/util/spd_tools/intel/ddr4/gen_part_id.go
diff options
context:
space:
mode:
authorNick Vaccaro <nvaccaro@google.com>2020-08-12 17:02:49 -0700
committerFurquan Shaikh <furquan@google.com>2020-08-25 16:48:02 +0000
commit90aeb4d1b5154c7978414f11b3b37cc9b4782b29 (patch)
tree4aa8afa3f257e5f418eee7d46bb291481cc2ec71 /util/spd_tools/intel/ddr4/gen_part_id.go
parentc9689e0591e053efefa268680cd3aeb80451001e (diff)
downloadcoreboot-90aeb4d1b5154c7978414f11b3b37cc9b4782b29.tar.xz
util: Add spd_tools to generate DDR4 SPDs for TGL boards
Serial Presence Detect (SPD) data for memory modules is used by Memory Reference Code (MRC) for training the memory. This SPD data is typically obtained from part vendors but has to be massaged to format it correctly as per JEDEC and MRC expectations. There have been numerous times in the past where the SPD data used is not always correct. In order to reduce the manual effort of creating SPDs and generating DRAM IDs, this change adds tools for generating SPD files for DDR4 memory used in memory down configurations on Intel Tiger Lake (TGL) based platforms. These tools generate SPDs following JESD79-4C and Jedec "4.1.2.L-5 R29 v103" specification. Two tools are provided: * gen_spd.go: Generates de-duplicated SPD files using a global memory part list provided by the mainboard in JSON format. Additionally, generates a SPD manifest file (in CSV format) with information about what memory part from the global list uses which of the generated SPD files. * gen_part_id.go: Allocates DRAM strap IDs for different DDR4 memory parts used by the board. Takes as input list of memory parts used by the board (with one memory part on each line) and the SPD manifest file generated by gen_spd.go. Generates Makefile.inc for integrating the generated SPD files in the coreboot build. BUG=b:160157545 Change-Id: I263f936b332520753a6791c8d892fc148cb6f103 Signed-off-by: Nick Vaccaro <nvaccaro@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/44429 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Rob Barnes <robbarnes@google.com> Reviewed-by: Furquan Shaikh <furquan@google.com>
Diffstat (limited to 'util/spd_tools/intel/ddr4/gen_part_id.go')
-rw-r--r--util/spd_tools/intel/ddr4/gen_part_id.go215
1 files changed, 215 insertions, 0 deletions
diff --git a/util/spd_tools/intel/ddr4/gen_part_id.go b/util/spd_tools/intel/ddr4/gen_part_id.go
new file mode 100644
index 0000000000..f67b4a9434
--- /dev/null
+++ b/util/spd_tools/intel/ddr4/gen_part_id.go
@@ -0,0 +1,215 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+package main
+
+import (
+ "encoding/csv"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+/*
+ * 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 = "spd_manifest.generated.txt"
+ MakefileName = "Makefile.inc"
+ DRAMIdFileName = "dram_id.generated.txt"
+)
+
+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 = File containing list of memory parts used by the board\n\n\n")
+}
+
+func checkArgs() error {
+
+ for _, arg := range os.Args[1:] {
+ if _, err := os.Stat(arg); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+/*
+ * Read input file that contains list of memory part names used by the variant (one on a line)
+ * and split into separate strings for each part name.
+ */
+func readParts(memPartsUsedFileName string) ([]string, error) {
+ lines, err := ioutil.ReadFile(memPartsUsedFileName)
+ if err != nil {
+ return nil, err
+ }
+ str := string(lines)
+ parts := strings.Split(str, "\n")
+
+ 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 []string, partToSPDMap map[string]string, SPDToIndexMap map[string]int, makefileDirName string) ([]partIds, error) {
+ partIdList := []partIds{}
+ curId := 0
+ var s string
+
+ s += fmt.Sprintf("%-30s %s\n", "DRAM Part Name", "ID to assign")
+
+ for _, p := range parts {
+ if p == "" {
+ continue
+ }
+
+ SPDFileName,ok := partToSPDMap[p]
+ if !ok {
+ return nil, fmt.Errorf("Failed to find part ", p, " 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
+ appendPartIdInfo(&s, p, index)
+ continue
+ }
+
+ SPDToIndexMap[SPDFileName] = curId
+
+ appendPartIdInfo(&s, p, curId)
+ entry := partIds{SPDFileName: SPDFileName, memParts: p}
+ partIdList = append(partIdList, entry)
+
+ curId++
+ }
+
+ 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("MEMORY_TYPE = ddr4\n\n")
+ s += fmt.Sprintf("SPD_SOURCES =\n")
+
+ for i := 0; i < len(partIdList); i++ {
+ 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)
+ }
+}