From 196e9c002122d3469ab486d2f40af1e66dcdf067 Mon Sep 17 00:00:00 2001 From: Rob Barnes Date: Wed, 26 Aug 2020 12:47:27 -0600 Subject: util/spd_tools: Remove intel subfolder Move ddr4 and lp4x to spd_tools root folder. The tool now applies to non intel platforms. BUG=b:162939176 TEST=Run tool Signed-off-by: Rob Barnes Change-Id: I0941ea036d760ee27eb34f259f4506a4b7584bee Reviewed-on: https://review.coreboot.org/c/coreboot/+/44844 Reviewed-by: Furquan Shaikh Reviewed-by: Nick Vaccaro Tested-by: build bot (Jenkins) --- util/spd_tools/ddr4/README.md | 275 ++++ util/spd_tools/ddr4/gen_part_id.go | 215 +++ util/spd_tools/ddr4/gen_spd.go | 1386 ++++++++++++++++++++ util/spd_tools/ddr4/global_ddr4_mem_parts.json.txt | 37 + util/spd_tools/intel/ddr4/README.md | 275 ---- util/spd_tools/intel/ddr4/gen_part_id.go | 215 --- util/spd_tools/intel/ddr4/gen_spd.go | 1386 -------------------- .../intel/ddr4/global_ddr4_mem_parts.json.txt | 37 - util/spd_tools/intel/lp4x/README.md | 265 ---- util/spd_tools/intel/lp4x/gen_part_id.go | 215 --- util/spd_tools/intel/lp4x/gen_spd.go | 986 -------------- .../intel/lp4x/global_lp4x_mem_parts.json.txt | 186 --- util/spd_tools/lp4x/README.md | 265 ++++ util/spd_tools/lp4x/gen_part_id.go | 215 +++ util/spd_tools/lp4x/gen_spd.go | 986 ++++++++++++++ util/spd_tools/lp4x/global_lp4x_mem_parts.json.txt | 186 +++ 16 files changed, 3565 insertions(+), 3565 deletions(-) create mode 100644 util/spd_tools/ddr4/README.md create mode 100644 util/spd_tools/ddr4/gen_part_id.go create mode 100644 util/spd_tools/ddr4/gen_spd.go create mode 100644 util/spd_tools/ddr4/global_ddr4_mem_parts.json.txt delete mode 100644 util/spd_tools/intel/ddr4/README.md delete mode 100644 util/spd_tools/intel/ddr4/gen_part_id.go delete mode 100644 util/spd_tools/intel/ddr4/gen_spd.go delete mode 100644 util/spd_tools/intel/ddr4/global_ddr4_mem_parts.json.txt delete mode 100644 util/spd_tools/intel/lp4x/README.md delete mode 100644 util/spd_tools/intel/lp4x/gen_part_id.go delete mode 100644 util/spd_tools/intel/lp4x/gen_spd.go delete mode 100644 util/spd_tools/intel/lp4x/global_lp4x_mem_parts.json.txt create mode 100644 util/spd_tools/lp4x/README.md create mode 100644 util/spd_tools/lp4x/gen_part_id.go create mode 100644 util/spd_tools/lp4x/gen_spd.go create mode 100644 util/spd_tools/lp4x/global_lp4x_mem_parts.json.txt (limited to 'util') diff --git a/util/spd_tools/ddr4/README.md b/util/spd_tools/ddr4/README.md new file mode 100644 index 0000000000..31e326d442 --- /dev/null +++ b/util/spd_tools/ddr4/README.md @@ -0,0 +1,275 @@ +# DDR4 SPD tools README + +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 specifications. + +There are two tools provided that assist TGL based mainboards +to generate SPDs and Makefile to integrate these SPDs in coreboot +build. These tools can also be used to allocate DRAM IDs (configure +DRAM hardware straps) for any DDR4 memory part used by the board. + +* 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. + +## Tool 1 - gen_spd.go + +This program takes as input: +* Pointer to directory where the generated SPD files and manifest will + be placed. +* JSON file containing a global list of memory parts with their + attributes as per the datasheet. This is the list of all known + DDR4 memory parts irrespective of their usage on the board. +* SoC platform name for which the SPDs are being generated. Currently + supported platform names are `TGL`. + +Input JSON file requires the following two fields for every memory part: +* `name`: Name of the memory part +* `attribs`: List of attributes of the memory part as per its + datasheet. These attributes match the part specifications and are + independent of any SoC expectations. Tool takes care of translating + the physical attributes of the memory part to match JEDEC and Intel + MRC expectations. + +`attribs` field further contains two types of sub-fields: +* Mandatory: These attributes have to be provided for a memory part. +* Optional: These attributes can be provided by memory part if it wants + to override the defaults. + +### Mandatory `attribs` + +* `speedMTps`: Maximum rate supported by the part in MT/s. Valid values: + `1600, 1866, 2133, 2400, 2666, 2933, 3200` MT/s. + +* `CL_nRCD_nRP`: Refers to CAS Latency specified for the part (find + "CL-nRCD-nRP" in the vendor spec for the DDR4 part). + +* `capacityPerDieGb`: Capacity per die in gigabits. Valid values: + `2, 4, 8, 16` Gb part. + +* `diesPerPackage`: Number of dies on the part. Valid values: + `1, 2` dies per package. + +* `deviceBusWidth`: Number of bits of the device's address bus. Valid values: + `8, 16` bit-wide bus. NOTE: Width of x4 is not supported by this tool. + +* `ranksPerPackage`: From Jedec doc 4_01_02_AnnexL-1R23: + “Package ranks per DIMM” refers to the collections of devices on the module + sharing common chip select signals (across the data width of the DIMM), + either from the edge connector for unbuffered modules or from the outputs of + a registering clock driver for RDIMMs and LRDIMMs.Number of bits of the + device's address bus. Valid values: + `1, 2` package ranks. + +### Optional `attribs` + +The following options are calculated by the tool based on the mandatory +attributes described for the part, but there may be cases where a default value +must be overridden, such as when a device appears to be 3200AA, but does not +support all of the CAS latencies typically supported by a speed bin 3200AA part. +Do deal with such a case, the variable can be overridden here and the tool will +use this value instead of calculating one. All values must be defined in +picosecond units, except for "CASLatencies", which would be represented as a +string like "9 10 11 12 14". + + * `TAAMinPs`: Defines the minimum CAS Latency. + Table 48 of Jedec doc 4_01_02_AnnexL-5R29 lists tAAmin for each speed grade. + + * `TRASMinPs`: Refers to the minimum active to precharge delay time. + Table 55 of Jedec doc 4_01_02_AnnexL-5R29 lists tRPmin for each speed grade. + + * `TCKMinPs`: Refers to the minimum clock cycle time. + Table 42 of Jedec doc 4_01_02_AnnexL-5R29 lists tCKmin for each speed grade. + + * `TCKMaxPs`:Refers to the minimum clock cycle time. + Table 44 of Jedec doc 4_01_02_AnnexL-5R29 lists tCKmin for each speed grade. + + * `TRFC1MinPs`: Refers to the minimum refresh recovery delay time. + Table 59 of Jedec doc 4_01_02_AnnexL-5R29 lists tRFC1min for each page size. + + * `TRFC2MinPs`: Refers to the minimum refresh recovery delay time. + Table 61 of Jedec doc 4_01_02_AnnexL-5R29 lists tRFC2min for each page size. + + * `TRFC4MinPs`: Refers to the minimum refresh recovery delay time. + Table 63 of Jedec doc 4_01_02_AnnexL-5R29 lists tRFC4min for each page size. + + * `TFAWMinPs`:: Refers to the minimum four activate window delay time. + Table 66 of Jedec doc 4_01_02_AnnexL-5R29 lists tFAWmin for each speed grade + and page size combination. + + * `TRRDSMinPs`: Refers to the minimum activate to activate delay time to + different bank groups. + Table 68 of Jedec doc 4_01_02_AnnexL-5R29 lists tRRD_Smin for each speed grade + and page size combination. + + * `TRRDLMinPs`: Refers to the minimum activate to activate delay time to the + same bank group. + Table 70 of Jedec doc 4_01_02_AnnexL-5R29 lists tRRD_Lmin for each speed grade + and page size combination. + + * `TCCDLMinPs`: Refers to the minimum CAS to CAS delay time to same bank group. + Table 72 of Jedec doc 4_01_02_AnnexL-5R29 lists tCCD_Lmin for each speed grade. + + * `TWRMinPs`: Refers to the minimum write recovery time. + Table 75 of Jedec doc 4_01_02_AnnexL-5R29 lists tWRmin for each ddr4 type. + + * `TWTRSMinPs`: Refers to minimum write to read time to different bank group. + Table 78 of Jedec doc 4_01_02_AnnexL-5R29 lists tWTR_Smin for each ddr4 type. + + * `TWTRLMinPs`: Refers to minimum write to read time to same bank group. + Table 80 of Jedec doc 4_01_02_AnnexL-5R29 lists tWTR_Lmin for each ddr4 type. + + * `CASLatencies`: Refers to the CAS latencies supported by the part. + The speed bin tables in the back of Jedec doc 4_01_02_AnnexL-5R29 define the + standard CAS latencies that a speed bin part is supposed to support. + In cases where a part does not support all of the CAS latencies listed in the + speed bin tables, this entry should be used to override the default settings. + +### Example JSON file +``` +{ + "parts": [ + { + "name": "MEMORY_PART_A", + "attribs": { + "speedMTps": 3200, + "CL_nRCD_nRP": 22 + "capacityPerDieGb": 8, + "diesPerPackage": 2, + "deviceBusWidth": 16, + "ranksPerPackage": 1, + } + }, + { + "name": "MEMORY_PART_B", + "attribs": { + "speedMTps": 3200, + "CL_nRCD_nRP": 22 + "capacityPerDieGb": 8, + "diesPerPackage": 1, + "deviceBusWidth": 16, + "ranksPerPackage": 2, + "casLatencies": "9 10 11 12 13 14 15 16 17 18 19 20", + "tCKMaxPs": "1250" + } + } + ] +} +``` + +### Output + +This tool generates the following files using the global list of +memory parts in JSON format as described above: + * De-duplicated SPDs required for the different memory parts. These + SPD files are named (ddr4-spd-1.hex, ddr4-spd-2.hex, and so on) + and placed in the directory provided as an input to the tool. + * CSV file representing which of the deduplicated SPD files is used + by which memory part. This file is named as + `spd_manifest.generated.txt` and placed in the directory provided + as an input to the tool along with the generated SPD + files. Example CSV file: + ``` + MEMORY_PART_A, ddr4-spd-1.hex + MEMORY_PART_B, ddr4-spd-2.hex + MEMORY_PART_C, ddr4-spd-3.hex + MEMORY_PART_D, ddr4-spd-2.hex + MEMORY_PART_E, ddr4-spd-2.hex + ``` + +## Tool 2 - gen_part_id.go + +This program takes as input: +* Pointer to directory where the SPD files and the manifest file + `spd_manifest.generated.txt` (in CSV format) are placed by + gen_spd.go +* File containing list of memory parts used by the board. Each line of + the file is supposed to contain one memory part `name` as present in + the global list of memory parts provided to gen_spd.go +* Pointer to directory where the generated Makefile.inc should be + placed by the tool. + +### Output + +This program provides the following: + +* Prints out the list of DRAM hardware strap IDs that should be + allocated to each memory part listed in the input file. +* Makefile.inc is generated in the provided directory to integrate + SPDs generated by gen_spd.go with the coreboot build for the board. +* dram_id.generated.txt is generated in the same directory as + Makefile. This contains the part IDs assigned to the different + memory parts. (Useful to integrate in board schematics). + +Sample output (dram_id.generated.txt): +``` +DRAM Part Name ID to assign +MEMORY_PART_A 0 (0000) +MEMORY_PART_B 1 (0001) +MEMORY_PART_C 2 (0010) +MEMORY_PART_D 1 (0001) +``` + +Sample Makefile.inc: +``` +## SPDX-License-Identifier: GPL-2.0-or-later +## This is an auto-generated file. Do not edit!! + +SPD_SOURCES = +SPD_SOURCES += ddr4-spd-1.hex # ID = 0(0b0000) Parts = MEMORY_PART_A +SPD_SOURCES += ddr4-spd-2.hex # ID = 1(0b0001) Parts = MEMORY_PART_B, MEMORY_PART_D +SPD_SOURCES += ddr4-spd-3.hex # ID = 2(0b0010) Parts = MEMORY_PART_C +``` + +### Note of caution + +This program assigns DRAM IDs using the order of DRAM part names +provided in the input file. Thus, when adding a new memory part to the +list, it should always go to the end of the input text file. This +guarantees that the memory parts that were already assigned IDs do not +change. + +## How to build the tools? +``` +# go build gen_spd.go +# go build gen_part_id.go +``` + +## How to use the tools? +``` +# ./gen_spd +# ./gen_part_id +``` + +## Example Usage +``` +# ./gen_spd ../../../../src/soc/intel/tigerlake/spd/ddr4 ./global_ddr4_mem_parts.json.txt 'TGL' + +``` + +### Need to add a new memory part for a board? + +* If the memory part is not present in the global list of memory + parts, then add the memory part name and attributes as per the + datasheet to the file containing the global list. + * Use `gen_spd.go` with input as the file containing the global list + of memory parts to generate de-duplicated SPDs. + * If a new SPD file is generated, use `git add` to add it to the + tree and push a CL for review. +* Update the file containing memory parts used by board (variant) to + add the new memory part name at the end of the file. + * Use gen_part_id.go providing it pointer to the location where SPD + files are stored and file containing the list of memory parts used + by the board(variant). + * Use `git add` to add `Makefile.inc` and `dram_id.generated.txt` + with updated changes and push a CL for review. diff --git a/util/spd_tools/ddr4/gen_part_id.go b/util/spd_tools/ddr4/gen_part_id.go new file mode 100644 index 0000000000..f67b4a9434 --- /dev/null +++ b/util/spd_tools/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 \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) + } +} diff --git a/util/spd_tools/ddr4/gen_spd.go b/util/spd_tools/ddr4/gen_spd.go new file mode 100644 index 0000000000..5adadc962a --- /dev/null +++ b/util/spd_tools/ddr4/gen_spd.go @@ -0,0 +1,1386 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "reflect" + "strconv" + "strings" +) + +/* + * This program generates de-duplicated SPD files for DDR4 memory using the global memory + * part list provided in CSV format. In addition to that, it also generates SPD manifest in CSV + * format that contains entries of type (DRAM part name, SPD file name) which provides the SPD + * file name used by a given DRAM part. + * + * It takes as input: + * Pointer to directory where the generated SPD files will be placed. + * JSON file containing a list of memory parts with their attributes as per datasheet. + */ +const ( + SPDManifestFileName = "spd_manifest.generated.txt" + + PlatformTGL = 0 +) + +var platformMap = map[string]int { + "TGL": PlatformTGL, +} + +var currPlatform int + +type memAttributes struct { + /* Primary attributes - must be provided by JSON file for each part */ + SpeedMTps int + CL_nRCD_nRP int + CapacityPerDieGb int + DiesPerPackage int + DeviceBusWidth int + RanksPerPackage int + + /* + * All the following parameters are optional and required only if the part requires + * special parameters as per the datasheet. + */ + /* Timing parameters */ + TAAMinPs int + TRCDMinPs int + TRPMinPs int + TRASMinPs int + TRCMinPs int + TCKMinPs int + TCKMaxPs int + TRFC1MinPs int + TRFC2MinPs int + TRFC4MinPs int + TFAWMinPs int + TRRDLMinPs int + TRRDSMinPs int + TCCDLMinPs int + TWRMinPs int + TWTRLMinPs int + TWTRSMinPs int + + /* CAS */ + CASLatencies string + CASFirstByte byte + CASSecondByte byte + CASThirdByte byte + CASFourthByte byte +} + +/* This encodes the density in Gb to SPD low nibble value as per JESD 4.1.2.L-5 R29 */ +var densityGbToSPDEncoding = map[int]byte { + 2: 0x3, + 4: 0x4, + 8: 0x5, + 16: 0x6, +} + +/* + * Tables 4 thru Table 7 from JESD79-4C. + * Maps density per die to row-column encoding for a device with x8/x16 + * physical channel. + */ +var densityGbx8x16DieCapacityToRowColumnEncoding = map[int]byte { + 2: 0x11, /* 14 rows, 10 columns */ + 4: 0x19, /* 15 rows, 10 columns */ + 8: 0x21, /* 16 rows, 10 columns */ + 16: 0x29, /* 17 rows, 10 columns */ +} + +/* + * Tables 169 & 170 in the JESD79-4C spec + * Maps die density to refresh timings. This is the same for x8 and x16 + * devices. + */ + +/* maps die density to rcf1 timing in pico seconds */ +var tRFC1Encoding = map[int]int { + 2: 160000, + 4: 260000, + 8: 350000, + 16: 550000, +} + +/* maps die density to rcf2 timing in pico seconds */ +var tRFC2Encoding = map[int]int { + 2: 110000, + 4: 160000, + 8: 260000, + 16: 350000, +} + +/* maps die density to rcf4 timing in pico seconds */ +var tRFC4Encoding = map[int]int { + 2: 90000, + 4: 110000, + 8: 160000, + 16: 260000, +} + +func getTRCMinPs(memAttribs *memAttributes) int { + return memAttribs.TAAMinPs + memAttribs.TRASMinPs +} + +func getDefaultTCKMinPs(memAttribs *memAttributes) int { + /* value 2000000 = 2 * 1000000, where 1000000 is to convert mS to pS */ + return 2000000 / memAttribs.SpeedMTps +} + +type speedBinAttributes struct { + TRASMinPs int + TCKMaxPs int +} + +var speedBinToSPDEncoding = map[int]speedBinAttributes { + 1600: { + TRASMinPs: 35000, + TCKMaxPs: 1500, + }, + 1866: { + TRASMinPs: 34000, + TCKMaxPs: 1250, + }, + 2133: { + TRASMinPs: 33000, + TCKMaxPs: 1071, + }, + 2400: { + TRASMinPs: 32000, + TCKMaxPs: 937, + }, + 2666: { + TRASMinPs: 32000, + TCKMaxPs: 833, + }, + 2933: { + TRASMinPs: 32000, + TCKMaxPs: 750, + }, + 3200: { + TRASMinPs: 32000, + TCKMaxPs: 682, + }, +} + +func getBankGroups(memAttribs *memAttributes) byte { + var bg byte + + switch memAttribs.DeviceBusWidth { + case 8: + bg = 4 + case 16: + if memAttribs.DiesPerPackage == 1 { + bg = 2 /* x16 SDP has 2 bank groups */ + } else { + bg = 4 /* x16 DDP has 4 bank groups */ + } + } + + return bg +} + +func encodeBankGroups(bg byte) byte { + var val byte + + switch bg { + case 2: + val = 1 + case 4: + val = 2 + } + + return val << 6 +} + +func encodeDensityBanks(memAttribs *memAttributes) byte { + var b byte + + b = densityGbToSPDEncoding[memAttribs.CapacityPerDieGb] + b |= encodeBankGroups(getBankGroups(memAttribs)) + /* No need to encode banksPerGroup.it's always 4 ([4:5] = 0) */ + + return b +} + +func encodeSdramAddressing(memAttribs *memAttributes) byte { + var b byte + + b = densityGbx8x16DieCapacityToRowColumnEncoding[memAttribs.CapacityPerDieGb] + + return b +} + +func encodePackageDeviceType(dies int) byte { + var b byte + + if dies > 1 { + /* If more than one die, then this is a non-monolithic device. */ + b = 1 + } else { + /* If only single die, then this is a monolithic device. */ + b = 0 + } + + return b << 7 +} + +func encodeSignalLoadingFromDieCount(dies int) byte { + var loading byte + + /* + * If die count = 1, signal loading = "not specified" = 0 + * If die count > 1, signal loading = "multi" = 2 + */ + if dies == 1 { + loading = 0 + } else { + loading = 1 + } + + return loading +} + +func encodeDiesPerPackage(dies int) byte { + var b byte + + b = encodePackageDeviceType(dies) /* Monolithic / Non-monolithic device */ + b |= (byte(dies) - 1) << 4 + + return b +} + +func encodePackageType(memAttribs *memAttributes) byte { + var b byte + + b = encodeDiesPerPackage(memAttribs.DiesPerPackage) + b |= encodeSignalLoadingFromDieCount(memAttribs.DiesPerPackage) + + return b +} + +func encodeDataWidth(bitWidthPerDevice int) byte { + var width byte + + switch bitWidthPerDevice { + case 8: + width = 1 + case 16: + width = 2 + } + + return width +} + +func encodeRanks(ranks int) byte { + var b byte + + b = byte(ranks - 1) + + return b << 3 +} + +func encodeModuleOrganization(memAttribs *memAttributes) byte { + var b byte + + b = encodeDataWidth(memAttribs.DeviceBusWidth) + b |= encodeRanks(memAttribs.RanksPerPackage) + + return b +} + +func encodeTCKMin(memAttribs *memAttributes) byte { + return convPsToMtbByte(memAttribs.TCKMinPs) +} + +func encodeTCKMinFineOffset(memAttribs *memAttributes) byte { + return convPsToFtbByte(memAttribs.TCKMinPs) +} + +func encodeTCKMax(memAttribs *memAttributes) byte { + return convPsToMtbByte(memAttribs.TCKMaxPs) +} + +func encodeTCKMaxFineOffset(memAttribs *memAttributes) byte { + return convPsToFtbByte(memAttribs.TCKMaxPs) +} + +func divRoundUp(dividend int, divisor int) int { + return (dividend + divisor - 1) / divisor +} + +func convNsToPs(timeNs int) int { + return timeNs * 1000 +} + +func convMtbToPs(mtb int) int { + return mtb * 125 +} + +func convPsToMtb(timePs int) int { + return divRoundUp(timePs, 125) +} + +func convPsToMtbByte(timePs int) byte { + return byte(convPsToMtb(timePs) & 0xff) +} + +func convPsToFtbByte(timePs int) byte { + mtb := convPsToMtb(timePs) + ftb := timePs - convMtbToPs(mtb) + + return byte(ftb) +} + +func encodeTAAMin(memAttribs *memAttributes) byte { + return convPsToMtbByte(memAttribs.TAAMinPs) +} + +func encodeTAAMinFineOffset(memAttribs *memAttributes) byte { + return convPsToFtbByte(memAttribs.TAAMinPs) +} + +func encodeTRCDMin(memAttribs *memAttributes) byte { + return convPsToMtbByte(memAttribs.TRCDMinPs) +} + +func encodeTRCDMinFineOffset(memAttribs *memAttributes) byte { + return convPsToFtbByte(memAttribs.TRCDMinPs) +} + +func encodeTRPMin(memAttribs *memAttributes) byte { + return convPsToMtbByte(memAttribs.TRPMinPs) +} + +func encodeTRCMinFineOffset(memAttribs *memAttributes) byte { + return convPsToFtbByte(memAttribs.TRCMinPs) +} + +func encodeTRPMinFineOffset(memAttribs *memAttributes) byte { + return convPsToFtbByte(memAttribs.TRPMinPs) +} + +func encodeTRASRCMinMSNs(memAttribs *memAttributes) byte { + var b byte + + b = byte((convPsToMtb(memAttribs.TRASMinPs) >> 4) & 0xf0) + b |= byte((convPsToMtb(memAttribs.TRCMinPs) >> 8) & 0x0f) + + return b +} + +func encodeTRASMinLsb(memAttribs *memAttributes) byte { + return byte(convPsToMtb(memAttribs.TRASMinPs) & 0xff) +} + +func encodeTRCMinLsb(memAttribs *memAttributes) byte { + return byte(convPsToMtb(memAttribs.TRCMinPs) & 0xff) +} + +var pageSizefromBusWidthEncoding = map[int]int { + 8: 1, + 16: 2, +} + +/* + * Per Table 69 & Table 70 of Jedec JESD79-4C + * tFAW timing is based on : + * Speed bin and page size + */ +func getTFAWMinPs(memAttribs *memAttributes) int { + var tFAWFixed int + + if pageSizefromBusWidthEncoding[memAttribs.DeviceBusWidth] == 1 { + switch memAttribs.SpeedMTps { + case 1600: + tFAWFixed = 25000 + case 1866: + tFAWFixed = 23000 + default: + tFAWFixed = 21000 + } + } else if pageSizefromBusWidthEncoding[memAttribs.DeviceBusWidth] == 2 { + switch memAttribs.SpeedMTps { + case 1600: + tFAWFixed = 35000 + default: + tFAWFixed = 30000 + } + } + + return tFAWFixed +} + +/* Update settings based on data sheet (json) supplied memory attributes */ + +func updateTFAWMin(memAttribs *memAttributes) { + var tFAWFromTck int + + if memAttribs.TFAWMinPs == 0 { + memAttribs.TFAWMinPs = getTFAWMinPs(memAttribs) + } + + switch pageSizefromBusWidthEncoding[memAttribs.DeviceBusWidth] { + case 1: + tFAWFromTck = 20 * memAttribs.TCKMinPs + case 2: + tFAWFromTck = 28 * memAttribs.TCKMinPs + } + + if memAttribs.TFAWMinPs < tFAWFromTck { + memAttribs.TFAWMinPs = tFAWFromTck + } +} + +func updateTRFC1Min(memAttribs *memAttributes) { + if memAttribs.TRFC1MinPs == 0 { + memAttribs.TRFC1MinPs = tRFC1Encoding[memAttribs.CapacityPerDieGb] + } +} + +func updateTRFC2Min(memAttribs *memAttributes) { + if memAttribs.TRFC2MinPs == 0 { + memAttribs.TRFC2MinPs = tRFC2Encoding[memAttribs.CapacityPerDieGb] + } +} + +func updateTRFC4Min(memAttribs *memAttributes) { + if memAttribs.TRFC4MinPs == 0 { + memAttribs.TRFC4MinPs = tRFC4Encoding[memAttribs.CapacityPerDieGb] + } +} + +func getTRRDLMinPs(memAttribs *memAttributes) int { + var tRRDLFixed int + + /* + * Per JESD79-4C Tables 169 & 170, tRRD_L is based on : + * Speed bin and page size + */ + switch pageSizefromBusWidthEncoding[memAttribs.DeviceBusWidth] { + case 1: + switch memAttribs.SpeedMTps { + case 1600: + tRRDLFixed = 6000 + default: + tRRDLFixed = 5300 + } + case 2: + switch memAttribs.SpeedMTps { + case 1600: + tRRDLFixed = 7500 + default: + tRRDLFixed = 6400 + } + } + + return tRRDLFixed +} + +func updateTRRDLMin(memAttribs *memAttributes) { + var tRRDLFromTck int + + if memAttribs.TRRDLMinPs == 0 { + memAttribs.TRRDLMinPs= getTRRDLMinPs(memAttribs) + } + + tRRDLFromTck = 4 * memAttribs.TCKMinPs + + if memAttribs.TRRDLMinPs < tRRDLFromTck { + memAttribs.TRRDLMinPs = tRRDLFromTck + } +} + +var speedToTRRDSMinPsOneKPageSize = map[int]int { + 1600: 5000, + 1866: 4200, + 2133: 3700, + 2400: 3300, + 2666: 3000, + 2933: 2700, + 3200: 2500, +} + +var speedToTRRDSMinPsTwoKPageSize = map[int]int { + 1600: 6000, + 1866: 5300, + 2133: 5300, + 2400: 5300, + 2666: 5300, + 2933: 5300, + 3200: 5300, +} + +func getTRRDSMinPs(memAttribs *memAttributes) int { + var tRRDFixed int + + switch pageSizefromBusWidthEncoding[memAttribs.DeviceBusWidth] { + case 1: + tRRDFixed = speedToTRRDSMinPsOneKPageSize[memAttribs.SpeedMTps] + case 2: + tRRDFixed = speedToTRRDSMinPsTwoKPageSize[memAttribs.SpeedMTps] + } + + return tRRDFixed +} + +func updateTRRDSMin(memAttribs *memAttributes) { + var tRRDFromTck int + + if memAttribs.TRRDSMinPs == 0 { + memAttribs.TRRDSMinPs = getTRRDSMinPs(memAttribs) + } + + tRRDFromTck = 4 * memAttribs.TCKMinPs + + if memAttribs.TRRDSMinPs < tRRDFromTck { + memAttribs.TRRDSMinPs = tRRDFromTck + } +} + +/* + * Per JESD79-4C Tables 169 and 170, + * tCCD_L is based on : + * Speed Bin + */ +func getTCCDLMinPs(memAttribs *memAttributes) int { + var tCCDLFixed int + + switch memAttribs.SpeedMTps { + case 1600: + tCCDLFixed = 6250 + case 1866: + tCCDLFixed = 5355 + case 2133: + tCCDLFixed = 5355 + default: + tCCDLFixed = 5000 + } + + return tCCDLFixed +} + +func updateTCCDLMin(memAttribs *memAttributes) { + var tCCDLFromTck int + + if memAttribs.TCCDLMinPs == 0 { + memAttribs.TCCDLMinPs = getTCCDLMinPs(memAttribs) + } + + tCCDLFromTck = 5 * memAttribs.TCKMinPs + + if memAttribs.TCCDLMinPs < tCCDLFromTck { + memAttribs.TCCDLMinPs = tCCDLFromTck + } +} + +func encodeTRFC1MinLsb(memAttribs *memAttributes) byte { + var mtb int + + mtb = convPsToMtb(memAttribs.TRFC1MinPs) + + return byte(mtb & 0xff) +} + +func encodeTRFC1MinMsb(memAttribs *memAttributes) byte { + var mtb int + + mtb = convPsToMtb(memAttribs.TRFC1MinPs) + + return byte((mtb >> 8) & 0xff) +} + +func encodeTRFC2MinLsb(memAttribs *memAttributes) byte { + var mtb int + + mtb = convPsToMtb(memAttribs.TRFC2MinPs) + + return byte(mtb & 0xff) +} + +func encodeTRFC2MinMsb(memAttribs *memAttributes) byte { + var mtb int + + mtb = convPsToMtb(memAttribs.TRFC2MinPs) + + return byte((mtb >> 8) & 0xff) +} + +func encodeTRFC4MinLsb(memAttribs *memAttributes) byte { + var mtb int + + mtb = convPsToMtb(memAttribs.TRFC4MinPs) + + return byte(mtb & 0xff) +} + +func encodeTRFC4MinMsb(memAttribs *memAttributes) byte { + var mtb int + + mtb = convPsToMtb(memAttribs.TRFC4MinPs) + + return byte((mtb >> 8) & 0xff) +} + +func encodeTFAWMinMSN(memAttribs *memAttributes) byte { + var mtb int + + mtb = convPsToMtb(memAttribs.TFAWMinPs) + + return byte((mtb >> 8) & 0x0f) +} + +func encodeTFAWMinLsb(memAttribs *memAttributes) byte { + var mtb int + + mtb = convPsToMtb(memAttribs.TFAWMinPs) + + return byte(mtb & 0xff) +} + +func encodeCASFirstByte(memAttribs *memAttributes) byte { + return memAttribs.CASFirstByte +} + +func encodeCASSecondByte(memAttribs *memAttributes) byte { + return memAttribs.CASSecondByte +} + +func encodeCASThirdByte(memAttribs *memAttributes) byte { + return memAttribs.CASThirdByte +} + +func encodeCASFourthByte(memAttribs *memAttributes) byte { + return memAttribs.CASFourthByte +} + +func encodeTRRDSMin(memAttribs *memAttributes) byte { + return convPsToMtbByte(memAttribs.TRRDSMinPs) +} + +func encodeTRRDSMinFineOffset(memAttribs *memAttributes) byte { + return convPsToFtbByte(memAttribs.TRRDSMinPs) +} + +func encodeTRRDLMin(memAttribs *memAttributes) byte { + return convPsToMtbByte(memAttribs.TRRDLMinPs) +} + +func encodeTRRDLMinFineOffset(memAttribs *memAttributes) byte { + return convPsToFtbByte(memAttribs.TRRDLMinPs) +} + +func encodeTCCDLMin(memAttribs *memAttributes) byte { + return convPsToMtbByte(memAttribs.TCCDLMinPs) +} + +func encodeTCCDLMinFineOffset(memAttribs *memAttributes) byte { + return convPsToFtbByte(memAttribs.TCCDLMinPs) +} + +func encodeTWRMinMSN(memAttribs *memAttributes) byte { + return byte((convPsToMtb(TimingValueTWRMinPs) >> 8) & 0x0f) +} + +func encodeTWRMinLsb(memAttribs *memAttributes) byte { + return byte(convPsToMtb(TimingValueTWRMinPs) & 0xff) +} + +func encodeTWTRMinMSNs(memAttribs *memAttributes) byte { + var b byte + + b = byte((convPsToMtb(memAttribs.TWTRLMinPs) >> 4) & 0xf0) + b |= byte((convPsToMtb(memAttribs.TWTRSMinPs) >> 8) & 0x0f) + + return b +} + +func encodeTWTRSMinLsb(memAttribs *memAttributes) byte { + return byte(convPsToMtb(memAttribs.TWTRSMinPs) & 0xff) +} + +func encodeTWTRLMinLsb(memAttribs *memAttributes) byte { + return byte(convPsToMtb(memAttribs.TWTRLMinPs) & 0xff) +} + +type SPDMemAttribFunc func (*memAttributes) byte +type SPDConvConstFunc func () byte + +type SPDAttribTableEntry struct { + constVal byte + getVal SPDMemAttribFunc +} + +const ( + /* SPD Byte Index */ + SPDIndexSize = 0 + SPDIndexRevision = 1 + SPDIndexMemoryType = 2 + SPDIndexModuleType = 3 + SPDIndexDensityBanks = 4 + SPDIndexAddressing = 5 + SPDIndexPackageType = 6 + SPDIndexOptionalFeatures = 7 + SPDIndexModuleOrganization = 12 + SPDIndexBusWidth = 13 + SPDIndexTimebases = 17 + SPDIndexTCKMin = 18 + SPDIndexTCKMax = 19 + SPDIndexCASFirstByte = 20 + SPDIndexCASSecondByte = 21 + SPDIndexCASThirdByte = 22 + SPDIndexCASFourthByte = 23 + SPDIndexTAAMin = 24 + SPDIndexTRCDMin = 25 + SPDIndexTRPMin = 26 + SPDIndexTRASRCMinMSNs = 27 + SPDIndexTRASMinLsb = 28 + SPDIndexTRCMinLsb = 29 + SPDIndexTRFC1MinLsb = 30 + SPDIndexTRFC1MinMsb = 31 + SPDIndexTRFC2MinLsb = 32 + SPDIndexTRFC2MinMsb = 33 + SPDIndexTRFC4MinLsb = 34 + SPDIndexTRFC4MinMsb = 35 + SPDIndexTFAWMinMSN = 36 + SPDIndexTFAWMinLsb = 37 + SPDIndexTRRDSMin = 38 + SPDIndexTRRDLMin = 39 + SPDIndexTCCDLMin = 40 + SPDIndexTWRMinMSN = 41 + SPDIndexTWRMinLsb = 42 + SPDIndexTWTRMinMSNs = 43 + SPDIndexWTRSMinLsb = 44 + SPDIndexWTRLMinLsb = 45 + SPDIndexTCCDLMinFineOffset = 117 + SPDIndexTRRDLMinFineOffset = 118 + SPDIndexTRRDSMinFineOffset = 119 + SPDIndexTRCMinFineOffset = 120 + SPDIndexTRPMinFineOffset = 121 + SPDIndexTRCDMinFineOffset = 122 + SPDIndexTAAMinFineOffset = 123 + SPDIndexTCKMaxFineOffset = 124 + SPDIndexTCKMinFineOffset = 125 + SPDIndexManufacturerPartNumberStartByte = 329 + SPDIndexManufacturerPartNumberEndByte = 348 + + /* SPD Byte Value */ + + /* + * From JEDEC spec: + * 6:4 (Bytes total) = 2 (512 bytes) + * 3:0 (Bytes used) = 3 (384 bytes) + * Set to 0x23 for DDR4. + */ + SPDValueSize = 0x23 + + /* + * From JEDEC spec: Revision 1.1 + * Set to 0x11. + */ + SPDValueRevision = 0x11 + + /* DDR4 memory type = 0x0C */ + SPDValueMemoryType = 0x0C + + /* + * From JEDEC spec: + * Module Type [0:3] : + * 0 = Undefined + * 1 = RDIMM (width = 133.35 mm nom) + * 2 = UDIMM (width = 133.35 mm nom) + * 3 = SO-DIMM (width = 68.60 mm nom) + * 4 = LRDIMM (width = 133.35 mm nom) + * + * DDR4 on TGL uses SO-DIMM type for for both memory down and DIMM config. + * Set to 0x03. + */ + SPDValueModuleType = 0x03 + + /* + * From JEDEC spec: + * 5:4 (Maximum Activate Window) = 00 (8192 * tREFI) + * 3:0 (Maximum Activate Count) = 1000 (Unlimited MAC) + * + * Needs to come from datasheet, but most parts seem to support unlimited MAC. + * MR#24 OP3 + */ + SPDValueOptionalFeatures = 0x08 + + /* + * From JEDEC spec: + * 2:0 Primary Bus Width in Bits = 011 (x64 always) + * Set to 0x03. + */ + SPDValueModuleBusWidth = 0x03 + + /* + * From JEDEC spec: + * 3:2 (MTB) = 00 (0.125ns) + * 1:0 (FTB) = 00 (1ps) + * Set to 0x00. + */ + SPDValueTimebases = 0x00 + + /* CAS fourth byte: All bits are reserved */ + SPDValueCASFourthByte = 0x00 + + /* As per JEDEC spec, unused digits of manufacturer part number are left as blank. */ + SPDValueManufacturerPartNumberBlank = 0x20 + +) + +const ( + /* + * As per Table 75 of Jedec spec 4.1.20-L-5 R29 v103: + * tWRMin = 15nS for all DDR4 Speed Bins + * Set to 15000 pS + */ + TimingValueTWRMinPs = 15000 + + /* + * As per Table 78 of Jedec spec 4.1.20-L-5 R29 v103: + * tWTR_SMin = 2.5nS for all DDR4 Speed Bins + * Set to 2500 pS + */ + TimingValueTWTRSMinPs = 2500 + + /* + * As per Table 80 of Jedec spec 4.1.20-L-5 R29 v103: + * tWTR_LMin = 7.5 nS for all DDR4 Speed Bins + * Set to 7500 pS + */ + TimingValueTWTRLMinPs = 7500 +) + +var SPDAttribTable = map[int]SPDAttribTableEntry { + SPDIndexSize: { constVal: SPDValueSize }, + SPDIndexRevision: { constVal: SPDValueRevision }, + SPDIndexMemoryType: { constVal: SPDValueMemoryType }, + SPDIndexModuleType: { constVal: SPDValueModuleType }, + SPDIndexDensityBanks: { getVal: encodeDensityBanks }, + SPDIndexAddressing: { getVal: encodeSdramAddressing }, + SPDIndexPackageType: { getVal: encodePackageType }, + SPDIndexOptionalFeatures: { constVal: SPDValueOptionalFeatures }, + SPDIndexModuleOrganization: { getVal: encodeModuleOrganization }, + SPDIndexBusWidth: { constVal: SPDValueModuleBusWidth }, + SPDIndexTimebases: { constVal: SPDValueTimebases }, + SPDIndexTCKMin: { getVal: encodeTCKMin }, + SPDIndexTCKMinFineOffset: { getVal: encodeTCKMinFineOffset }, + SPDIndexTCKMax: { getVal: encodeTCKMax }, + SPDIndexTCKMaxFineOffset: { getVal: encodeTCKMaxFineOffset }, + SPDIndexCASFirstByte: { getVal: encodeCASFirstByte }, + SPDIndexCASSecondByte: { getVal: encodeCASSecondByte }, + SPDIndexCASThirdByte: { getVal: encodeCASThirdByte }, + SPDIndexCASFourthByte: { getVal: encodeCASFourthByte }, + SPDIndexTAAMin: { getVal: encodeTAAMin }, + SPDIndexTAAMinFineOffset: { getVal: encodeTAAMinFineOffset }, + SPDIndexTRCDMin: { getVal: encodeTRCDMin }, + SPDIndexTRCDMinFineOffset: { getVal: encodeTRCDMinFineOffset }, + SPDIndexTRPMin: { getVal: encodeTRPMin }, + SPDIndexTRPMinFineOffset: { getVal: encodeTRPMinFineOffset }, + SPDIndexTRASRCMinMSNs: { getVal: encodeTRASRCMinMSNs }, + SPDIndexTRASMinLsb: { getVal: encodeTRASMinLsb }, + SPDIndexTRCMinLsb: { getVal: encodeTRCMinLsb }, + SPDIndexTRCMinFineOffset: { getVal: encodeTRCMinFineOffset }, + SPDIndexTRFC1MinLsb: { getVal: encodeTRFC1MinLsb }, + SPDIndexTRFC1MinMsb: { getVal: encodeTRFC1MinMsb }, + SPDIndexTRFC2MinLsb: { getVal: encodeTRFC2MinLsb }, + SPDIndexTRFC2MinMsb: { getVal: encodeTRFC2MinMsb }, + SPDIndexTRFC4MinLsb: { getVal: encodeTRFC4MinLsb }, + SPDIndexTRFC4MinMsb: { getVal: encodeTRFC4MinMsb }, + SPDIndexTFAWMinMSN: { getVal: encodeTFAWMinMSN }, + SPDIndexTFAWMinLsb: { getVal: encodeTFAWMinLsb }, + SPDIndexTRRDSMin: { getVal: encodeTRRDSMin }, + SPDIndexTRRDSMinFineOffset: { getVal: encodeTRRDSMinFineOffset }, + SPDIndexTRRDLMin: { getVal: encodeTRRDLMin }, + SPDIndexTRRDLMinFineOffset: { getVal: encodeTRRDLMinFineOffset }, + SPDIndexTCCDLMin: { getVal: encodeTCCDLMin }, + SPDIndexTCCDLMinFineOffset: { getVal: encodeTCCDLMinFineOffset }, + SPDIndexTWRMinMSN: { getVal: encodeTWRMinMSN }, + SPDIndexTWRMinLsb: { getVal: encodeTWRMinLsb }, + SPDIndexTWTRMinMSNs: { getVal: encodeTWTRMinMSNs }, + SPDIndexWTRSMinLsb: { getVal: encodeTWTRSMinLsb }, + SPDIndexWTRLMinLsb: { getVal: encodeTWTRLMinLsb }, +} + +type memParts struct { + MemParts []memPart `json:"parts"` +} + +type memPart struct { + Name string + Attribs memAttributes + SPDFileName string +} + +func writeSPDManifest(memParts *memParts, SPDDirName string) error { + var s string + + fmt.Printf("Generating SPD Manifest with following entries:\n") + + for i := 0; i < len(memParts.MemParts); i++ { + fmt.Printf("%-40s %s\n", memParts.MemParts[i].Name, memParts.MemParts[i].SPDFileName) + s += fmt.Sprintf("%s,%s\n", memParts.MemParts[i].Name, memParts.MemParts[i].SPDFileName) + } + + return ioutil.WriteFile(filepath.Join(SPDDirName, SPDManifestFileName), []byte(s), 0644) +} + +func isManufacturerPartNumberByte(index int) bool { + if index >= SPDIndexManufacturerPartNumberStartByte && index <= SPDIndexManufacturerPartNumberEndByte { + return true + } + return false +} + + +func getSPDByte(index int, memAttribs *memAttributes) byte { + e, ok := SPDAttribTable[index] + if ok == false { + if isManufacturerPartNumberByte(index) { + return SPDValueManufacturerPartNumberBlank + } + return 0x00 + } + + if e.getVal != nil { + return e.getVal(memAttribs) + } + + return e.constVal +} + +func createSPD(memAttribs *memAttributes) string { + var s string + + for i := 0; i < 512; i++ { + b := getSPDByte(i, memAttribs) + + if (i + 1) % 16 == 0 { + s += fmt.Sprintf("%02X\n", b) + } else { + s += fmt.Sprintf("%02X ", b) + } + } + + return s +} + +func dedupeMemoryPart(dedupedParts []*memPart, memPart *memPart) bool { + for i := 0; i < len(dedupedParts); i++ { + if reflect.DeepEqual(dedupedParts[i].Attribs, memPart.Attribs) { + memPart.SPDFileName = dedupedParts[i].SPDFileName + return true + } + } + + return false +} + +func generateSPD(memPart *memPart, SPDId int, SPDDirName string) { + s := createSPD(&memPart.Attribs) + memPart.SPDFileName = fmt.Sprintf("ddr4-spd-%d.hex", SPDId) + ioutil.WriteFile(filepath.Join(SPDDirName, memPart.SPDFileName), []byte(s), 0644) +} + +func readMemoryParts(memParts *memParts, memPartsFileName string) error { + databytes, err := ioutil.ReadFile(memPartsFileName) + if err != nil { + return err + } + + return json.Unmarshal(databytes, memParts) +} + +func validateSpeedMTps(speedBin int) error { + if _, ok := speedBinToSPDEncoding[speedBin]; ok == false { + return fmt.Errorf("Incorrect speed bin: DDR4-", speedBin) + } + return nil +} + +func validateCapacityPerDie(capacityPerDieGb int) error { + if _, ok := densityGbToSPDEncoding[capacityPerDieGb]; ok == false { + return fmt.Errorf("Incorrect capacity per die: ", capacityPerDieGb) + } + return nil +} + +func validateDiesPerPackage(dieCount int) error { + if dieCount >= 1 && dieCount <= 2 { + return nil + } + return fmt.Errorf("Incorrect dies per package count: ", dieCount) +} + +func validateDeviceBusWidth(width int) error { + if width != 8 && width != 16 { + return fmt.Errorf("Incorrect device bus width: ", width) + } + return nil +} + +func validateRanksPerPackage(ranks int) error { + if ranks >= 1 && ranks <= 2 { + return nil + } + return fmt.Errorf("Incorrect package ranks: ", ranks) +} + + +func validateCASLatency(CL int) error { + if CL >= 10 && CL <= 24 && CL != 23 { + return nil + } + return fmt.Errorf("Incorrect CAS latency: ", CL) +} + +/* +1) validate memory parts +2) remove any fields that Intel does not care about +*/ + +/* verify the supplied CAS Latencies supported does not match default */ +func verifySupportedCASLatencies(part *memPart) error { + if part.Attribs.CASLatencies == getDefaultCASLatencies(&part.Attribs) { + return fmt.Errorf("CASLatencies for %s already matches default,\nPlease remove CASLatencies override line from the %s part attributes in the global part list and regenerate SPD Manifest", part.Name, part.Name) + } + + return nil +} + +func validateMemoryParts(memParts *memParts) error { + for i := 0; i < len(memParts.MemParts); i++ { + if err := validateSpeedMTps(memParts.MemParts[i].Attribs.SpeedMTps); err != nil { + return err + } + if err := validateCapacityPerDie(memParts.MemParts[i].Attribs.CapacityPerDieGb); err != nil { + return err + } + if err := validateDiesPerPackage(memParts.MemParts[i].Attribs.DiesPerPackage); err != nil { + return err + } + if err := validateDeviceBusWidth(memParts.MemParts[i].Attribs.DeviceBusWidth); err != nil { + return err + } + if err := validateRanksPerPackage(memParts.MemParts[i].Attribs.RanksPerPackage); err != nil { + return err + } + if err := validateCASLatency(memParts.MemParts[i].Attribs.CL_nRCD_nRP); err != nil { + return err + } + /* If CAS Latency was supplied, make sure it doesn't match default value */ + if len(memParts.MemParts[i].Attribs.CASLatencies) != 0 { + if err := verifySupportedCASLatencies(&memParts.MemParts[i]); err != nil { + return err + } + } + } + + return nil +} + +const ( + /* First Byte */ + CAS9 = 1 << 2 + CAS10 = 1 << 3 + CAS11 = 1 << 4 + CAS12 = 1 << 5 + CAS13 = 1 << 6 + CAS14 = 1 << 7 + /* Second Byte */ + CAS15 = 1 << 0 + CAS16 = 1 << 1 + CAS17 = 1 << 2 + CAS18 = 1 << 3 + CAS19 = 1 << 4 + CAS20 = 1 << 5 + CAS21 = 1 << 6 + CAS22 = 1 << 7 + /* Third Byte */ + CAS24 = 1 << 1 +) + +func encodeLatencies(latency int, memAttribs *memAttributes) error { + switch latency { + case 9: + memAttribs.CASFirstByte |= CAS9 + case 10: + memAttribs.CASFirstByte |= CAS10 + case 11: + memAttribs.CASFirstByte |= CAS11 + case 12: + memAttribs.CASFirstByte |= CAS12 + case 13: + memAttribs.CASFirstByte |= CAS13 + case 14: + memAttribs.CASFirstByte |= CAS14 + case 15: + memAttribs.CASSecondByte |= CAS15 + case 16: + memAttribs.CASSecondByte |= CAS16 + case 17: + memAttribs.CASSecondByte |= CAS17 + case 18: + memAttribs.CASSecondByte |= CAS18 + case 19: + memAttribs.CASSecondByte |= CAS19 + case 20: + memAttribs.CASSecondByte |= CAS20 + case 21: + memAttribs.CASSecondByte |= CAS21 + case 22: + memAttribs.CASSecondByte |= CAS22 + case 24: + memAttribs.CASThirdByte |= CAS24 + default: + fmt.Errorf("Incorrect CAS Latency: ", latency) + } + + return nil +} + +/* Default CAS Latencies from Speed Bin tables in JEDS79-4C */ +func getDefaultCASLatencies(memAttribs *memAttributes) string { + var str string + + switch memAttribs.SpeedMTps { + case 1600: + switch memAttribs.CL_nRCD_nRP { + case 10: + str = "9 10 11 12" + case 11: + str = "9 11 12" + case 12: + str = "10 12" + } + case 1866: + switch memAttribs.CL_nRCD_nRP { + case 12: + str = "9 10 12 13 14" + case 13: + str = "9 11 12 13 14" + case 14: + str = "10 12 14" + } + case 2133: + switch memAttribs.CL_nRCD_nRP { + case 14: + str = "9 10 12 14 15 16" + case 15: + str = "9 11 12 13 14 15 16" + case 16: + str = "10 12 14 16" + } + case 2400: + switch memAttribs.CL_nRCD_nRP { + case 15: + str = "9 10 12 14 15 16 17 18" + case 16: + str = "9 11 12 13 14 15 16 17 18" + case 17: + str = "10 11 12 13 14 15 16 17 18" + case 18: + str = "10 12 14 16 18" + } + case 2666: + switch memAttribs.CL_nRCD_nRP { + case 17: + str = "9 10 11 12 13 14 15 16 17 18 19 20" + case 18: + str = "9 10 11 12 13 14 15 16 17 18 19 20" + case 19: + str = "10 11 12 13 14 15 16 17 18 19 20" + case 20: + str = "10 12 14 16 18 20" + } + case 2933: + switch memAttribs.CL_nRCD_nRP { + case 19: + str = "9 10 11 12 13 14 15 16 17 18 19 20 21 22" + case 20: + str = "10 11 12 13 14 15 16 17 18 19 20 21 22" + case 21: + str = "10 11 12 13 14 15 16 17 18 19 20 21 22" + case 22: + str = "10 12 14 16 18 20 22" + } + case 3200: + switch memAttribs.CL_nRCD_nRP { + case 20: + str = "9 10 11 12 13 14 15 16 17 18 19 20 21 22 24" + case 22: + str = "10 11 12 13 14 15 16 17 18 19 20 21 22 24" + case 24: + str = "10 12 14 16 18 20 22 24" + } + } + + return str +} + +func updateCAS(memAttribs *memAttributes) error { + if len(memAttribs.CASLatencies) == 0 { + memAttribs.CASLatencies = getDefaultCASLatencies(memAttribs) + } + + latencies := strings.Fields(memAttribs.CASLatencies) + for i := 0; i < len(latencies); i++ { + latency,err := strconv.Atoi(latencies[i]) + if err != nil { + return fmt.Errorf("Unable to convert latency ", latencies[i]) + } + if err := encodeLatencies(latency, memAttribs); err != nil { + return err + } + } + + return nil +} + +func getTAAMinPs(memAttribs *memAttributes) int { + return (memAttribs.CL_nRCD_nRP * 2000000) / memAttribs.SpeedMTps +} + +func updateTAAMin(memAttribs *memAttributes) { + if memAttribs.TAAMinPs == 0 { + memAttribs.TAAMinPs = getTAAMinPs(memAttribs) + } +} + +func updateTRCDMin(memAttribs *memAttributes) { + /* tRCDmin is same as tAAmin for all cases */ + if memAttribs.TRCDMinPs == 0 { + memAttribs.TRCDMinPs = getTAAMinPs(memAttribs) + } +} + +func updateTRPMin(memAttribs *memAttributes) { + /* tRPmin is same as tAAmin for all cases */ + if memAttribs.TRPMinPs == 0 { + memAttribs.TRPMinPs = getTAAMinPs(memAttribs) + } +} + +func updateTRASMin(memAttribs *memAttributes) { + if memAttribs.TRASMinPs == 0 { + memAttribs.TRASMinPs = speedBinToSPDEncoding[memAttribs.SpeedMTps].TRASMinPs + } +} + + +func updateTRCMin(memAttribs *memAttributes) { + if memAttribs.TRCMinPs == 0 { + memAttribs.TRCMinPs = getTRCMinPs(memAttribs) + } +} + +func updateTCK(memAttribs *memAttributes) { + if memAttribs.TCKMinPs == 0 { + memAttribs.TCKMinPs = getDefaultTCKMinPs(memAttribs) + } + if memAttribs.TCKMaxPs == 0 { + memAttribs.TCKMaxPs = speedBinToSPDEncoding[memAttribs.SpeedMTps].TCKMaxPs + } +} + +func updateTWRMin(memAttribs *memAttributes) { + if memAttribs.TWRMinPs == 0 { + memAttribs.TWRMinPs = TimingValueTWRMinPs + } +} + +func updateTWTRMin(memAttribs *memAttributes) { + if memAttribs.TWTRLMinPs == 0 { + memAttribs.TWTRLMinPs = TimingValueTWTRLMinPs + } + if memAttribs.TWTRSMinPs == 0 { + memAttribs.TWTRSMinPs = TimingValueTWTRSMinPs + } +} + +func updateMemoryAttributes(memAttribs *memAttributes) { + updateTCK(memAttribs) + updateTAAMin(memAttribs) + updateTRCDMin(memAttribs) + updateTRPMin(memAttribs) + updateTRASMin(memAttribs) + updateTRCMin(memAttribs) + updateTWRMin(memAttribs) + updateTWTRMin(memAttribs) + updateCAS(memAttribs) + updateTRFC1Min(memAttribs) + updateTRFC2Min(memAttribs) + updateTRFC4Min(memAttribs) + updateTCCDLMin(memAttribs) + updateTRRDSMin(memAttribs) + updateTRRDLMin(memAttribs) + updateTFAWMin(memAttribs) +} + +func isPlatformSupported(platform string) error { + var ok bool + + currPlatform, ok = platformMap[platform] + if ok == false { + return fmt.Errorf("Unsupported platform: ", platform) + } + + return nil +} + +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(" mem_parts_list_json = JSON File containing list of memory parts and attributes\n") + fmt.Printf(" platform = SoC Platform for which the SPDs are being generated\n\n\n") +} + +func main() { + if len(os.Args) != 4 { + usage() + log.Fatal("Incorrect number of arguments") + } + + var memParts memParts + var dedupedParts []*memPart + + SPDDir, GlobalMemPartsFile, Platform := os.Args[1], os.Args[2], strings.ToUpper(os.Args[3]) + + if err := isPlatformSupported(Platform); err != nil { + log.Fatal(err) + } + + if err := readMemoryParts(&memParts, GlobalMemPartsFile); err != nil { + log.Fatal(err) + } + + if err := validateMemoryParts(&memParts); err != nil { + log.Fatal(err) + } + + SPDId := 1 + + for i := 0; i < len(memParts.MemParts); i++ { + updateMemoryAttributes(&memParts.MemParts[i].Attribs) + if dedupeMemoryPart(dedupedParts, &memParts.MemParts[i]) == false { + generateSPD(&memParts.MemParts[i], SPDId, SPDDir) + SPDId++ + dedupedParts = append(dedupedParts, &memParts.MemParts[i]) + } + } + + if err := writeSPDManifest(&memParts, SPDDir); err != nil { + log.Fatal(err) + } +} diff --git a/util/spd_tools/ddr4/global_ddr4_mem_parts.json.txt b/util/spd_tools/ddr4/global_ddr4_mem_parts.json.txt new file mode 100644 index 0000000000..4e9c7c8e95 --- /dev/null +++ b/util/spd_tools/ddr4/global_ddr4_mem_parts.json.txt @@ -0,0 +1,37 @@ +{ + "parts": [ + { + "name": "H5AN8G6NDJR-XNC", + "attribs": { + "speedMTps": 3200, + "CL_nRCD_nRP": 22, + "capacityPerDieGb": 8, + "diesPerPackage": 1, + "deviceBusWidth": 16, + "ranksPerPackage": 1 + } + }, + { + "name": "MT40A512M16TB-062E:J", + "attribs": { + "speedMTps": 3200, + "CL_nRCD_nRP": 22, + "capacityPerDieGb": 8, + "diesPerPackage": 1, + "deviceBusWidth": 16, + "ranksPerPackage": 1 + } + }, + { + "name": "H5ANAG6NCMR-XNC", + "attribs": { + "speedMTps": 3200, + "CL_nRCD_nRP": 22, + "capacityPerDieGb": 8, + "diesPerPackage": 2, + "deviceBusWidth": 16, + "ranksPerPackage": 1 + } + } + ] +} diff --git a/util/spd_tools/intel/ddr4/README.md b/util/spd_tools/intel/ddr4/README.md deleted file mode 100644 index 31e326d442..0000000000 --- a/util/spd_tools/intel/ddr4/README.md +++ /dev/null @@ -1,275 +0,0 @@ -# DDR4 SPD tools README - -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 specifications. - -There are two tools provided that assist TGL based mainboards -to generate SPDs and Makefile to integrate these SPDs in coreboot -build. These tools can also be used to allocate DRAM IDs (configure -DRAM hardware straps) for any DDR4 memory part used by the board. - -* 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. - -## Tool 1 - gen_spd.go - -This program takes as input: -* Pointer to directory where the generated SPD files and manifest will - be placed. -* JSON file containing a global list of memory parts with their - attributes as per the datasheet. This is the list of all known - DDR4 memory parts irrespective of their usage on the board. -* SoC platform name for which the SPDs are being generated. Currently - supported platform names are `TGL`. - -Input JSON file requires the following two fields for every memory part: -* `name`: Name of the memory part -* `attribs`: List of attributes of the memory part as per its - datasheet. These attributes match the part specifications and are - independent of any SoC expectations. Tool takes care of translating - the physical attributes of the memory part to match JEDEC and Intel - MRC expectations. - -`attribs` field further contains two types of sub-fields: -* Mandatory: These attributes have to be provided for a memory part. -* Optional: These attributes can be provided by memory part if it wants - to override the defaults. - -### Mandatory `attribs` - -* `speedMTps`: Maximum rate supported by the part in MT/s. Valid values: - `1600, 1866, 2133, 2400, 2666, 2933, 3200` MT/s. - -* `CL_nRCD_nRP`: Refers to CAS Latency specified for the part (find - "CL-nRCD-nRP" in the vendor spec for the DDR4 part). - -* `capacityPerDieGb`: Capacity per die in gigabits. Valid values: - `2, 4, 8, 16` Gb part. - -* `diesPerPackage`: Number of dies on the part. Valid values: - `1, 2` dies per package. - -* `deviceBusWidth`: Number of bits of the device's address bus. Valid values: - `8, 16` bit-wide bus. NOTE: Width of x4 is not supported by this tool. - -* `ranksPerPackage`: From Jedec doc 4_01_02_AnnexL-1R23: - “Package ranks per DIMM” refers to the collections of devices on the module - sharing common chip select signals (across the data width of the DIMM), - either from the edge connector for unbuffered modules or from the outputs of - a registering clock driver for RDIMMs and LRDIMMs.Number of bits of the - device's address bus. Valid values: - `1, 2` package ranks. - -### Optional `attribs` - -The following options are calculated by the tool based on the mandatory -attributes described for the part, but there may be cases where a default value -must be overridden, such as when a device appears to be 3200AA, but does not -support all of the CAS latencies typically supported by a speed bin 3200AA part. -Do deal with such a case, the variable can be overridden here and the tool will -use this value instead of calculating one. All values must be defined in -picosecond units, except for "CASLatencies", which would be represented as a -string like "9 10 11 12 14". - - * `TAAMinPs`: Defines the minimum CAS Latency. - Table 48 of Jedec doc 4_01_02_AnnexL-5R29 lists tAAmin for each speed grade. - - * `TRASMinPs`: Refers to the minimum active to precharge delay time. - Table 55 of Jedec doc 4_01_02_AnnexL-5R29 lists tRPmin for each speed grade. - - * `TCKMinPs`: Refers to the minimum clock cycle time. - Table 42 of Jedec doc 4_01_02_AnnexL-5R29 lists tCKmin for each speed grade. - - * `TCKMaxPs`:Refers to the minimum clock cycle time. - Table 44 of Jedec doc 4_01_02_AnnexL-5R29 lists tCKmin for each speed grade. - - * `TRFC1MinPs`: Refers to the minimum refresh recovery delay time. - Table 59 of Jedec doc 4_01_02_AnnexL-5R29 lists tRFC1min for each page size. - - * `TRFC2MinPs`: Refers to the minimum refresh recovery delay time. - Table 61 of Jedec doc 4_01_02_AnnexL-5R29 lists tRFC2min for each page size. - - * `TRFC4MinPs`: Refers to the minimum refresh recovery delay time. - Table 63 of Jedec doc 4_01_02_AnnexL-5R29 lists tRFC4min for each page size. - - * `TFAWMinPs`:: Refers to the minimum four activate window delay time. - Table 66 of Jedec doc 4_01_02_AnnexL-5R29 lists tFAWmin for each speed grade - and page size combination. - - * `TRRDSMinPs`: Refers to the minimum activate to activate delay time to - different bank groups. - Table 68 of Jedec doc 4_01_02_AnnexL-5R29 lists tRRD_Smin for each speed grade - and page size combination. - - * `TRRDLMinPs`: Refers to the minimum activate to activate delay time to the - same bank group. - Table 70 of Jedec doc 4_01_02_AnnexL-5R29 lists tRRD_Lmin for each speed grade - and page size combination. - - * `TCCDLMinPs`: Refers to the minimum CAS to CAS delay time to same bank group. - Table 72 of Jedec doc 4_01_02_AnnexL-5R29 lists tCCD_Lmin for each speed grade. - - * `TWRMinPs`: Refers to the minimum write recovery time. - Table 75 of Jedec doc 4_01_02_AnnexL-5R29 lists tWRmin for each ddr4 type. - - * `TWTRSMinPs`: Refers to minimum write to read time to different bank group. - Table 78 of Jedec doc 4_01_02_AnnexL-5R29 lists tWTR_Smin for each ddr4 type. - - * `TWTRLMinPs`: Refers to minimum write to read time to same bank group. - Table 80 of Jedec doc 4_01_02_AnnexL-5R29 lists tWTR_Lmin for each ddr4 type. - - * `CASLatencies`: Refers to the CAS latencies supported by the part. - The speed bin tables in the back of Jedec doc 4_01_02_AnnexL-5R29 define the - standard CAS latencies that a speed bin part is supposed to support. - In cases where a part does not support all of the CAS latencies listed in the - speed bin tables, this entry should be used to override the default settings. - -### Example JSON file -``` -{ - "parts": [ - { - "name": "MEMORY_PART_A", - "attribs": { - "speedMTps": 3200, - "CL_nRCD_nRP": 22 - "capacityPerDieGb": 8, - "diesPerPackage": 2, - "deviceBusWidth": 16, - "ranksPerPackage": 1, - } - }, - { - "name": "MEMORY_PART_B", - "attribs": { - "speedMTps": 3200, - "CL_nRCD_nRP": 22 - "capacityPerDieGb": 8, - "diesPerPackage": 1, - "deviceBusWidth": 16, - "ranksPerPackage": 2, - "casLatencies": "9 10 11 12 13 14 15 16 17 18 19 20", - "tCKMaxPs": "1250" - } - } - ] -} -``` - -### Output - -This tool generates the following files using the global list of -memory parts in JSON format as described above: - * De-duplicated SPDs required for the different memory parts. These - SPD files are named (ddr4-spd-1.hex, ddr4-spd-2.hex, and so on) - and placed in the directory provided as an input to the tool. - * CSV file representing which of the deduplicated SPD files is used - by which memory part. This file is named as - `spd_manifest.generated.txt` and placed in the directory provided - as an input to the tool along with the generated SPD - files. Example CSV file: - ``` - MEMORY_PART_A, ddr4-spd-1.hex - MEMORY_PART_B, ddr4-spd-2.hex - MEMORY_PART_C, ddr4-spd-3.hex - MEMORY_PART_D, ddr4-spd-2.hex - MEMORY_PART_E, ddr4-spd-2.hex - ``` - -## Tool 2 - gen_part_id.go - -This program takes as input: -* Pointer to directory where the SPD files and the manifest file - `spd_manifest.generated.txt` (in CSV format) are placed by - gen_spd.go -* File containing list of memory parts used by the board. Each line of - the file is supposed to contain one memory part `name` as present in - the global list of memory parts provided to gen_spd.go -* Pointer to directory where the generated Makefile.inc should be - placed by the tool. - -### Output - -This program provides the following: - -* Prints out the list of DRAM hardware strap IDs that should be - allocated to each memory part listed in the input file. -* Makefile.inc is generated in the provided directory to integrate - SPDs generated by gen_spd.go with the coreboot build for the board. -* dram_id.generated.txt is generated in the same directory as - Makefile. This contains the part IDs assigned to the different - memory parts. (Useful to integrate in board schematics). - -Sample output (dram_id.generated.txt): -``` -DRAM Part Name ID to assign -MEMORY_PART_A 0 (0000) -MEMORY_PART_B 1 (0001) -MEMORY_PART_C 2 (0010) -MEMORY_PART_D 1 (0001) -``` - -Sample Makefile.inc: -``` -## SPDX-License-Identifier: GPL-2.0-or-later -## This is an auto-generated file. Do not edit!! - -SPD_SOURCES = -SPD_SOURCES += ddr4-spd-1.hex # ID = 0(0b0000) Parts = MEMORY_PART_A -SPD_SOURCES += ddr4-spd-2.hex # ID = 1(0b0001) Parts = MEMORY_PART_B, MEMORY_PART_D -SPD_SOURCES += ddr4-spd-3.hex # ID = 2(0b0010) Parts = MEMORY_PART_C -``` - -### Note of caution - -This program assigns DRAM IDs using the order of DRAM part names -provided in the input file. Thus, when adding a new memory part to the -list, it should always go to the end of the input text file. This -guarantees that the memory parts that were already assigned IDs do not -change. - -## How to build the tools? -``` -# go build gen_spd.go -# go build gen_part_id.go -``` - -## How to use the tools? -``` -# ./gen_spd -# ./gen_part_id -``` - -## Example Usage -``` -# ./gen_spd ../../../../src/soc/intel/tigerlake/spd/ddr4 ./global_ddr4_mem_parts.json.txt 'TGL' - -``` - -### Need to add a new memory part for a board? - -* If the memory part is not present in the global list of memory - parts, then add the memory part name and attributes as per the - datasheet to the file containing the global list. - * Use `gen_spd.go` with input as the file containing the global list - of memory parts to generate de-duplicated SPDs. - * If a new SPD file is generated, use `git add` to add it to the - tree and push a CL for review. -* Update the file containing memory parts used by board (variant) to - add the new memory part name at the end of the file. - * Use gen_part_id.go providing it pointer to the location where SPD - files are stored and file containing the list of memory parts used - by the board(variant). - * Use `git add` to add `Makefile.inc` and `dram_id.generated.txt` - with updated changes and push a CL for review. diff --git a/util/spd_tools/intel/ddr4/gen_part_id.go b/util/spd_tools/intel/ddr4/gen_part_id.go deleted file mode 100644 index f67b4a9434..0000000000 --- a/util/spd_tools/intel/ddr4/gen_part_id.go +++ /dev/null @@ -1,215 +0,0 @@ -/* 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 \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) - } -} diff --git a/util/spd_tools/intel/ddr4/gen_spd.go b/util/spd_tools/intel/ddr4/gen_spd.go deleted file mode 100644 index 5adadc962a..0000000000 --- a/util/spd_tools/intel/ddr4/gen_spd.go +++ /dev/null @@ -1,1386 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "os" - "path/filepath" - "reflect" - "strconv" - "strings" -) - -/* - * This program generates de-duplicated SPD files for DDR4 memory using the global memory - * part list provided in CSV format. In addition to that, it also generates SPD manifest in CSV - * format that contains entries of type (DRAM part name, SPD file name) which provides the SPD - * file name used by a given DRAM part. - * - * It takes as input: - * Pointer to directory where the generated SPD files will be placed. - * JSON file containing a list of memory parts with their attributes as per datasheet. - */ -const ( - SPDManifestFileName = "spd_manifest.generated.txt" - - PlatformTGL = 0 -) - -var platformMap = map[string]int { - "TGL": PlatformTGL, -} - -var currPlatform int - -type memAttributes struct { - /* Primary attributes - must be provided by JSON file for each part */ - SpeedMTps int - CL_nRCD_nRP int - CapacityPerDieGb int - DiesPerPackage int - DeviceBusWidth int - RanksPerPackage int - - /* - * All the following parameters are optional and required only if the part requires - * special parameters as per the datasheet. - */ - /* Timing parameters */ - TAAMinPs int - TRCDMinPs int - TRPMinPs int - TRASMinPs int - TRCMinPs int - TCKMinPs int - TCKMaxPs int - TRFC1MinPs int - TRFC2MinPs int - TRFC4MinPs int - TFAWMinPs int - TRRDLMinPs int - TRRDSMinPs int - TCCDLMinPs int - TWRMinPs int - TWTRLMinPs int - TWTRSMinPs int - - /* CAS */ - CASLatencies string - CASFirstByte byte - CASSecondByte byte - CASThirdByte byte - CASFourthByte byte -} - -/* This encodes the density in Gb to SPD low nibble value as per JESD 4.1.2.L-5 R29 */ -var densityGbToSPDEncoding = map[int]byte { - 2: 0x3, - 4: 0x4, - 8: 0x5, - 16: 0x6, -} - -/* - * Tables 4 thru Table 7 from JESD79-4C. - * Maps density per die to row-column encoding for a device with x8/x16 - * physical channel. - */ -var densityGbx8x16DieCapacityToRowColumnEncoding = map[int]byte { - 2: 0x11, /* 14 rows, 10 columns */ - 4: 0x19, /* 15 rows, 10 columns */ - 8: 0x21, /* 16 rows, 10 columns */ - 16: 0x29, /* 17 rows, 10 columns */ -} - -/* - * Tables 169 & 170 in the JESD79-4C spec - * Maps die density to refresh timings. This is the same for x8 and x16 - * devices. - */ - -/* maps die density to rcf1 timing in pico seconds */ -var tRFC1Encoding = map[int]int { - 2: 160000, - 4: 260000, - 8: 350000, - 16: 550000, -} - -/* maps die density to rcf2 timing in pico seconds */ -var tRFC2Encoding = map[int]int { - 2: 110000, - 4: 160000, - 8: 260000, - 16: 350000, -} - -/* maps die density to rcf4 timing in pico seconds */ -var tRFC4Encoding = map[int]int { - 2: 90000, - 4: 110000, - 8: 160000, - 16: 260000, -} - -func getTRCMinPs(memAttribs *memAttributes) int { - return memAttribs.TAAMinPs + memAttribs.TRASMinPs -} - -func getDefaultTCKMinPs(memAttribs *memAttributes) int { - /* value 2000000 = 2 * 1000000, where 1000000 is to convert mS to pS */ - return 2000000 / memAttribs.SpeedMTps -} - -type speedBinAttributes struct { - TRASMinPs int - TCKMaxPs int -} - -var speedBinToSPDEncoding = map[int]speedBinAttributes { - 1600: { - TRASMinPs: 35000, - TCKMaxPs: 1500, - }, - 1866: { - TRASMinPs: 34000, - TCKMaxPs: 1250, - }, - 2133: { - TRASMinPs: 33000, - TCKMaxPs: 1071, - }, - 2400: { - TRASMinPs: 32000, - TCKMaxPs: 937, - }, - 2666: { - TRASMinPs: 32000, - TCKMaxPs: 833, - }, - 2933: { - TRASMinPs: 32000, - TCKMaxPs: 750, - }, - 3200: { - TRASMinPs: 32000, - TCKMaxPs: 682, - }, -} - -func getBankGroups(memAttribs *memAttributes) byte { - var bg byte - - switch memAttribs.DeviceBusWidth { - case 8: - bg = 4 - case 16: - if memAttribs.DiesPerPackage == 1 { - bg = 2 /* x16 SDP has 2 bank groups */ - } else { - bg = 4 /* x16 DDP has 4 bank groups */ - } - } - - return bg -} - -func encodeBankGroups(bg byte) byte { - var val byte - - switch bg { - case 2: - val = 1 - case 4: - val = 2 - } - - return val << 6 -} - -func encodeDensityBanks(memAttribs *memAttributes) byte { - var b byte - - b = densityGbToSPDEncoding[memAttribs.CapacityPerDieGb] - b |= encodeBankGroups(getBankGroups(memAttribs)) - /* No need to encode banksPerGroup.it's always 4 ([4:5] = 0) */ - - return b -} - -func encodeSdramAddressing(memAttribs *memAttributes) byte { - var b byte - - b = densityGbx8x16DieCapacityToRowColumnEncoding[memAttribs.CapacityPerDieGb] - - return b -} - -func encodePackageDeviceType(dies int) byte { - var b byte - - if dies > 1 { - /* If more than one die, then this is a non-monolithic device. */ - b = 1 - } else { - /* If only single die, then this is a monolithic device. */ - b = 0 - } - - return b << 7 -} - -func encodeSignalLoadingFromDieCount(dies int) byte { - var loading byte - - /* - * If die count = 1, signal loading = "not specified" = 0 - * If die count > 1, signal loading = "multi" = 2 - */ - if dies == 1 { - loading = 0 - } else { - loading = 1 - } - - return loading -} - -func encodeDiesPerPackage(dies int) byte { - var b byte - - b = encodePackageDeviceType(dies) /* Monolithic / Non-monolithic device */ - b |= (byte(dies) - 1) << 4 - - return b -} - -func encodePackageType(memAttribs *memAttributes) byte { - var b byte - - b = encodeDiesPerPackage(memAttribs.DiesPerPackage) - b |= encodeSignalLoadingFromDieCount(memAttribs.DiesPerPackage) - - return b -} - -func encodeDataWidth(bitWidthPerDevice int) byte { - var width byte - - switch bitWidthPerDevice { - case 8: - width = 1 - case 16: - width = 2 - } - - return width -} - -func encodeRanks(ranks int) byte { - var b byte - - b = byte(ranks - 1) - - return b << 3 -} - -func encodeModuleOrganization(memAttribs *memAttributes) byte { - var b byte - - b = encodeDataWidth(memAttribs.DeviceBusWidth) - b |= encodeRanks(memAttribs.RanksPerPackage) - - return b -} - -func encodeTCKMin(memAttribs *memAttributes) byte { - return convPsToMtbByte(memAttribs.TCKMinPs) -} - -func encodeTCKMinFineOffset(memAttribs *memAttributes) byte { - return convPsToFtbByte(memAttribs.TCKMinPs) -} - -func encodeTCKMax(memAttribs *memAttributes) byte { - return convPsToMtbByte(memAttribs.TCKMaxPs) -} - -func encodeTCKMaxFineOffset(memAttribs *memAttributes) byte { - return convPsToFtbByte(memAttribs.TCKMaxPs) -} - -func divRoundUp(dividend int, divisor int) int { - return (dividend + divisor - 1) / divisor -} - -func convNsToPs(timeNs int) int { - return timeNs * 1000 -} - -func convMtbToPs(mtb int) int { - return mtb * 125 -} - -func convPsToMtb(timePs int) int { - return divRoundUp(timePs, 125) -} - -func convPsToMtbByte(timePs int) byte { - return byte(convPsToMtb(timePs) & 0xff) -} - -func convPsToFtbByte(timePs int) byte { - mtb := convPsToMtb(timePs) - ftb := timePs - convMtbToPs(mtb) - - return byte(ftb) -} - -func encodeTAAMin(memAttribs *memAttributes) byte { - return convPsToMtbByte(memAttribs.TAAMinPs) -} - -func encodeTAAMinFineOffset(memAttribs *memAttributes) byte { - return convPsToFtbByte(memAttribs.TAAMinPs) -} - -func encodeTRCDMin(memAttribs *memAttributes) byte { - return convPsToMtbByte(memAttribs.TRCDMinPs) -} - -func encodeTRCDMinFineOffset(memAttribs *memAttributes) byte { - return convPsToFtbByte(memAttribs.TRCDMinPs) -} - -func encodeTRPMin(memAttribs *memAttributes) byte { - return convPsToMtbByte(memAttribs.TRPMinPs) -} - -func encodeTRCMinFineOffset(memAttribs *memAttributes) byte { - return convPsToFtbByte(memAttribs.TRCMinPs) -} - -func encodeTRPMinFineOffset(memAttribs *memAttributes) byte { - return convPsToFtbByte(memAttribs.TRPMinPs) -} - -func encodeTRASRCMinMSNs(memAttribs *memAttributes) byte { - var b byte - - b = byte((convPsToMtb(memAttribs.TRASMinPs) >> 4) & 0xf0) - b |= byte((convPsToMtb(memAttribs.TRCMinPs) >> 8) & 0x0f) - - return b -} - -func encodeTRASMinLsb(memAttribs *memAttributes) byte { - return byte(convPsToMtb(memAttribs.TRASMinPs) & 0xff) -} - -func encodeTRCMinLsb(memAttribs *memAttributes) byte { - return byte(convPsToMtb(memAttribs.TRCMinPs) & 0xff) -} - -var pageSizefromBusWidthEncoding = map[int]int { - 8: 1, - 16: 2, -} - -/* - * Per Table 69 & Table 70 of Jedec JESD79-4C - * tFAW timing is based on : - * Speed bin and page size - */ -func getTFAWMinPs(memAttribs *memAttributes) int { - var tFAWFixed int - - if pageSizefromBusWidthEncoding[memAttribs.DeviceBusWidth] == 1 { - switch memAttribs.SpeedMTps { - case 1600: - tFAWFixed = 25000 - case 1866: - tFAWFixed = 23000 - default: - tFAWFixed = 21000 - } - } else if pageSizefromBusWidthEncoding[memAttribs.DeviceBusWidth] == 2 { - switch memAttribs.SpeedMTps { - case 1600: - tFAWFixed = 35000 - default: - tFAWFixed = 30000 - } - } - - return tFAWFixed -} - -/* Update settings based on data sheet (json) supplied memory attributes */ - -func updateTFAWMin(memAttribs *memAttributes) { - var tFAWFromTck int - - if memAttribs.TFAWMinPs == 0 { - memAttribs.TFAWMinPs = getTFAWMinPs(memAttribs) - } - - switch pageSizefromBusWidthEncoding[memAttribs.DeviceBusWidth] { - case 1: - tFAWFromTck = 20 * memAttribs.TCKMinPs - case 2: - tFAWFromTck = 28 * memAttribs.TCKMinPs - } - - if memAttribs.TFAWMinPs < tFAWFromTck { - memAttribs.TFAWMinPs = tFAWFromTck - } -} - -func updateTRFC1Min(memAttribs *memAttributes) { - if memAttribs.TRFC1MinPs == 0 { - memAttribs.TRFC1MinPs = tRFC1Encoding[memAttribs.CapacityPerDieGb] - } -} - -func updateTRFC2Min(memAttribs *memAttributes) { - if memAttribs.TRFC2MinPs == 0 { - memAttribs.TRFC2MinPs = tRFC2Encoding[memAttribs.CapacityPerDieGb] - } -} - -func updateTRFC4Min(memAttribs *memAttributes) { - if memAttribs.TRFC4MinPs == 0 { - memAttribs.TRFC4MinPs = tRFC4Encoding[memAttribs.CapacityPerDieGb] - } -} - -func getTRRDLMinPs(memAttribs *memAttributes) int { - var tRRDLFixed int - - /* - * Per JESD79-4C Tables 169 & 170, tRRD_L is based on : - * Speed bin and page size - */ - switch pageSizefromBusWidthEncoding[memAttribs.DeviceBusWidth] { - case 1: - switch memAttribs.SpeedMTps { - case 1600: - tRRDLFixed = 6000 - default: - tRRDLFixed = 5300 - } - case 2: - switch memAttribs.SpeedMTps { - case 1600: - tRRDLFixed = 7500 - default: - tRRDLFixed = 6400 - } - } - - return tRRDLFixed -} - -func updateTRRDLMin(memAttribs *memAttributes) { - var tRRDLFromTck int - - if memAttribs.TRRDLMinPs == 0 { - memAttribs.TRRDLMinPs= getTRRDLMinPs(memAttribs) - } - - tRRDLFromTck = 4 * memAttribs.TCKMinPs - - if memAttribs.TRRDLMinPs < tRRDLFromTck { - memAttribs.TRRDLMinPs = tRRDLFromTck - } -} - -var speedToTRRDSMinPsOneKPageSize = map[int]int { - 1600: 5000, - 1866: 4200, - 2133: 3700, - 2400: 3300, - 2666: 3000, - 2933: 2700, - 3200: 2500, -} - -var speedToTRRDSMinPsTwoKPageSize = map[int]int { - 1600: 6000, - 1866: 5300, - 2133: 5300, - 2400: 5300, - 2666: 5300, - 2933: 5300, - 3200: 5300, -} - -func getTRRDSMinPs(memAttribs *memAttributes) int { - var tRRDFixed int - - switch pageSizefromBusWidthEncoding[memAttribs.DeviceBusWidth] { - case 1: - tRRDFixed = speedToTRRDSMinPsOneKPageSize[memAttribs.SpeedMTps] - case 2: - tRRDFixed = speedToTRRDSMinPsTwoKPageSize[memAttribs.SpeedMTps] - } - - return tRRDFixed -} - -func updateTRRDSMin(memAttribs *memAttributes) { - var tRRDFromTck int - - if memAttribs.TRRDSMinPs == 0 { - memAttribs.TRRDSMinPs = getTRRDSMinPs(memAttribs) - } - - tRRDFromTck = 4 * memAttribs.TCKMinPs - - if memAttribs.TRRDSMinPs < tRRDFromTck { - memAttribs.TRRDSMinPs = tRRDFromTck - } -} - -/* - * Per JESD79-4C Tables 169 and 170, - * tCCD_L is based on : - * Speed Bin - */ -func getTCCDLMinPs(memAttribs *memAttributes) int { - var tCCDLFixed int - - switch memAttribs.SpeedMTps { - case 1600: - tCCDLFixed = 6250 - case 1866: - tCCDLFixed = 5355 - case 2133: - tCCDLFixed = 5355 - default: - tCCDLFixed = 5000 - } - - return tCCDLFixed -} - -func updateTCCDLMin(memAttribs *memAttributes) { - var tCCDLFromTck int - - if memAttribs.TCCDLMinPs == 0 { - memAttribs.TCCDLMinPs = getTCCDLMinPs(memAttribs) - } - - tCCDLFromTck = 5 * memAttribs.TCKMinPs - - if memAttribs.TCCDLMinPs < tCCDLFromTck { - memAttribs.TCCDLMinPs = tCCDLFromTck - } -} - -func encodeTRFC1MinLsb(memAttribs *memAttributes) byte { - var mtb int - - mtb = convPsToMtb(memAttribs.TRFC1MinPs) - - return byte(mtb & 0xff) -} - -func encodeTRFC1MinMsb(memAttribs *memAttributes) byte { - var mtb int - - mtb = convPsToMtb(memAttribs.TRFC1MinPs) - - return byte((mtb >> 8) & 0xff) -} - -func encodeTRFC2MinLsb(memAttribs *memAttributes) byte { - var mtb int - - mtb = convPsToMtb(memAttribs.TRFC2MinPs) - - return byte(mtb & 0xff) -} - -func encodeTRFC2MinMsb(memAttribs *memAttributes) byte { - var mtb int - - mtb = convPsToMtb(memAttribs.TRFC2MinPs) - - return byte((mtb >> 8) & 0xff) -} - -func encodeTRFC4MinLsb(memAttribs *memAttributes) byte { - var mtb int - - mtb = convPsToMtb(memAttribs.TRFC4MinPs) - - return byte(mtb & 0xff) -} - -func encodeTRFC4MinMsb(memAttribs *memAttributes) byte { - var mtb int - - mtb = convPsToMtb(memAttribs.TRFC4MinPs) - - return byte((mtb >> 8) & 0xff) -} - -func encodeTFAWMinMSN(memAttribs *memAttributes) byte { - var mtb int - - mtb = convPsToMtb(memAttribs.TFAWMinPs) - - return byte((mtb >> 8) & 0x0f) -} - -func encodeTFAWMinLsb(memAttribs *memAttributes) byte { - var mtb int - - mtb = convPsToMtb(memAttribs.TFAWMinPs) - - return byte(mtb & 0xff) -} - -func encodeCASFirstByte(memAttribs *memAttributes) byte { - return memAttribs.CASFirstByte -} - -func encodeCASSecondByte(memAttribs *memAttributes) byte { - return memAttribs.CASSecondByte -} - -func encodeCASThirdByte(memAttribs *memAttributes) byte { - return memAttribs.CASThirdByte -} - -func encodeCASFourthByte(memAttribs *memAttributes) byte { - return memAttribs.CASFourthByte -} - -func encodeTRRDSMin(memAttribs *memAttributes) byte { - return convPsToMtbByte(memAttribs.TRRDSMinPs) -} - -func encodeTRRDSMinFineOffset(memAttribs *memAttributes) byte { - return convPsToFtbByte(memAttribs.TRRDSMinPs) -} - -func encodeTRRDLMin(memAttribs *memAttributes) byte { - return convPsToMtbByte(memAttribs.TRRDLMinPs) -} - -func encodeTRRDLMinFineOffset(memAttribs *memAttributes) byte { - return convPsToFtbByte(memAttribs.TRRDLMinPs) -} - -func encodeTCCDLMin(memAttribs *memAttributes) byte { - return convPsToMtbByte(memAttribs.TCCDLMinPs) -} - -func encodeTCCDLMinFineOffset(memAttribs *memAttributes) byte { - return convPsToFtbByte(memAttribs.TCCDLMinPs) -} - -func encodeTWRMinMSN(memAttribs *memAttributes) byte { - return byte((convPsToMtb(TimingValueTWRMinPs) >> 8) & 0x0f) -} - -func encodeTWRMinLsb(memAttribs *memAttributes) byte { - return byte(convPsToMtb(TimingValueTWRMinPs) & 0xff) -} - -func encodeTWTRMinMSNs(memAttribs *memAttributes) byte { - var b byte - - b = byte((convPsToMtb(memAttribs.TWTRLMinPs) >> 4) & 0xf0) - b |= byte((convPsToMtb(memAttribs.TWTRSMinPs) >> 8) & 0x0f) - - return b -} - -func encodeTWTRSMinLsb(memAttribs *memAttributes) byte { - return byte(convPsToMtb(memAttribs.TWTRSMinPs) & 0xff) -} - -func encodeTWTRLMinLsb(memAttribs *memAttributes) byte { - return byte(convPsToMtb(memAttribs.TWTRLMinPs) & 0xff) -} - -type SPDMemAttribFunc func (*memAttributes) byte -type SPDConvConstFunc func () byte - -type SPDAttribTableEntry struct { - constVal byte - getVal SPDMemAttribFunc -} - -const ( - /* SPD Byte Index */ - SPDIndexSize = 0 - SPDIndexRevision = 1 - SPDIndexMemoryType = 2 - SPDIndexModuleType = 3 - SPDIndexDensityBanks = 4 - SPDIndexAddressing = 5 - SPDIndexPackageType = 6 - SPDIndexOptionalFeatures = 7 - SPDIndexModuleOrganization = 12 - SPDIndexBusWidth = 13 - SPDIndexTimebases = 17 - SPDIndexTCKMin = 18 - SPDIndexTCKMax = 19 - SPDIndexCASFirstByte = 20 - SPDIndexCASSecondByte = 21 - SPDIndexCASThirdByte = 22 - SPDIndexCASFourthByte = 23 - SPDIndexTAAMin = 24 - SPDIndexTRCDMin = 25 - SPDIndexTRPMin = 26 - SPDIndexTRASRCMinMSNs = 27 - SPDIndexTRASMinLsb = 28 - SPDIndexTRCMinLsb = 29 - SPDIndexTRFC1MinLsb = 30 - SPDIndexTRFC1MinMsb = 31 - SPDIndexTRFC2MinLsb = 32 - SPDIndexTRFC2MinMsb = 33 - SPDIndexTRFC4MinLsb = 34 - SPDIndexTRFC4MinMsb = 35 - SPDIndexTFAWMinMSN = 36 - SPDIndexTFAWMinLsb = 37 - SPDIndexTRRDSMin = 38 - SPDIndexTRRDLMin = 39 - SPDIndexTCCDLMin = 40 - SPDIndexTWRMinMSN = 41 - SPDIndexTWRMinLsb = 42 - SPDIndexTWTRMinMSNs = 43 - SPDIndexWTRSMinLsb = 44 - SPDIndexWTRLMinLsb = 45 - SPDIndexTCCDLMinFineOffset = 117 - SPDIndexTRRDLMinFineOffset = 118 - SPDIndexTRRDSMinFineOffset = 119 - SPDIndexTRCMinFineOffset = 120 - SPDIndexTRPMinFineOffset = 121 - SPDIndexTRCDMinFineOffset = 122 - SPDIndexTAAMinFineOffset = 123 - SPDIndexTCKMaxFineOffset = 124 - SPDIndexTCKMinFineOffset = 125 - SPDIndexManufacturerPartNumberStartByte = 329 - SPDIndexManufacturerPartNumberEndByte = 348 - - /* SPD Byte Value */ - - /* - * From JEDEC spec: - * 6:4 (Bytes total) = 2 (512 bytes) - * 3:0 (Bytes used) = 3 (384 bytes) - * Set to 0x23 for DDR4. - */ - SPDValueSize = 0x23 - - /* - * From JEDEC spec: Revision 1.1 - * Set to 0x11. - */ - SPDValueRevision = 0x11 - - /* DDR4 memory type = 0x0C */ - SPDValueMemoryType = 0x0C - - /* - * From JEDEC spec: - * Module Type [0:3] : - * 0 = Undefined - * 1 = RDIMM (width = 133.35 mm nom) - * 2 = UDIMM (width = 133.35 mm nom) - * 3 = SO-DIMM (width = 68.60 mm nom) - * 4 = LRDIMM (width = 133.35 mm nom) - * - * DDR4 on TGL uses SO-DIMM type for for both memory down and DIMM config. - * Set to 0x03. - */ - SPDValueModuleType = 0x03 - - /* - * From JEDEC spec: - * 5:4 (Maximum Activate Window) = 00 (8192 * tREFI) - * 3:0 (Maximum Activate Count) = 1000 (Unlimited MAC) - * - * Needs to come from datasheet, but most parts seem to support unlimited MAC. - * MR#24 OP3 - */ - SPDValueOptionalFeatures = 0x08 - - /* - * From JEDEC spec: - * 2:0 Primary Bus Width in Bits = 011 (x64 always) - * Set to 0x03. - */ - SPDValueModuleBusWidth = 0x03 - - /* - * From JEDEC spec: - * 3:2 (MTB) = 00 (0.125ns) - * 1:0 (FTB) = 00 (1ps) - * Set to 0x00. - */ - SPDValueTimebases = 0x00 - - /* CAS fourth byte: All bits are reserved */ - SPDValueCASFourthByte = 0x00 - - /* As per JEDEC spec, unused digits of manufacturer part number are left as blank. */ - SPDValueManufacturerPartNumberBlank = 0x20 - -) - -const ( - /* - * As per Table 75 of Jedec spec 4.1.20-L-5 R29 v103: - * tWRMin = 15nS for all DDR4 Speed Bins - * Set to 15000 pS - */ - TimingValueTWRMinPs = 15000 - - /* - * As per Table 78 of Jedec spec 4.1.20-L-5 R29 v103: - * tWTR_SMin = 2.5nS for all DDR4 Speed Bins - * Set to 2500 pS - */ - TimingValueTWTRSMinPs = 2500 - - /* - * As per Table 80 of Jedec spec 4.1.20-L-5 R29 v103: - * tWTR_LMin = 7.5 nS for all DDR4 Speed Bins - * Set to 7500 pS - */ - TimingValueTWTRLMinPs = 7500 -) - -var SPDAttribTable = map[int]SPDAttribTableEntry { - SPDIndexSize: { constVal: SPDValueSize }, - SPDIndexRevision: { constVal: SPDValueRevision }, - SPDIndexMemoryType: { constVal: SPDValueMemoryType }, - SPDIndexModuleType: { constVal: SPDValueModuleType }, - SPDIndexDensityBanks: { getVal: encodeDensityBanks }, - SPDIndexAddressing: { getVal: encodeSdramAddressing }, - SPDIndexPackageType: { getVal: encodePackageType }, - SPDIndexOptionalFeatures: { constVal: SPDValueOptionalFeatures }, - SPDIndexModuleOrganization: { getVal: encodeModuleOrganization }, - SPDIndexBusWidth: { constVal: SPDValueModuleBusWidth }, - SPDIndexTimebases: { constVal: SPDValueTimebases }, - SPDIndexTCKMin: { getVal: encodeTCKMin }, - SPDIndexTCKMinFineOffset: { getVal: encodeTCKMinFineOffset }, - SPDIndexTCKMax: { getVal: encodeTCKMax }, - SPDIndexTCKMaxFineOffset: { getVal: encodeTCKMaxFineOffset }, - SPDIndexCASFirstByte: { getVal: encodeCASFirstByte }, - SPDIndexCASSecondByte: { getVal: encodeCASSecondByte }, - SPDIndexCASThirdByte: { getVal: encodeCASThirdByte }, - SPDIndexCASFourthByte: { getVal: encodeCASFourthByte }, - SPDIndexTAAMin: { getVal: encodeTAAMin }, - SPDIndexTAAMinFineOffset: { getVal: encodeTAAMinFineOffset }, - SPDIndexTRCDMin: { getVal: encodeTRCDMin }, - SPDIndexTRCDMinFineOffset: { getVal: encodeTRCDMinFineOffset }, - SPDIndexTRPMin: { getVal: encodeTRPMin }, - SPDIndexTRPMinFineOffset: { getVal: encodeTRPMinFineOffset }, - SPDIndexTRASRCMinMSNs: { getVal: encodeTRASRCMinMSNs }, - SPDIndexTRASMinLsb: { getVal: encodeTRASMinLsb }, - SPDIndexTRCMinLsb: { getVal: encodeTRCMinLsb }, - SPDIndexTRCMinFineOffset: { getVal: encodeTRCMinFineOffset }, - SPDIndexTRFC1MinLsb: { getVal: encodeTRFC1MinLsb }, - SPDIndexTRFC1MinMsb: { getVal: encodeTRFC1MinMsb }, - SPDIndexTRFC2MinLsb: { getVal: encodeTRFC2MinLsb }, - SPDIndexTRFC2MinMsb: { getVal: encodeTRFC2MinMsb }, - SPDIndexTRFC4MinLsb: { getVal: encodeTRFC4MinLsb }, - SPDIndexTRFC4MinMsb: { getVal: encodeTRFC4MinMsb }, - SPDIndexTFAWMinMSN: { getVal: encodeTFAWMinMSN }, - SPDIndexTFAWMinLsb: { getVal: encodeTFAWMinLsb }, - SPDIndexTRRDSMin: { getVal: encodeTRRDSMin }, - SPDIndexTRRDSMinFineOffset: { getVal: encodeTRRDSMinFineOffset }, - SPDIndexTRRDLMin: { getVal: encodeTRRDLMin }, - SPDIndexTRRDLMinFineOffset: { getVal: encodeTRRDLMinFineOffset }, - SPDIndexTCCDLMin: { getVal: encodeTCCDLMin }, - SPDIndexTCCDLMinFineOffset: { getVal: encodeTCCDLMinFineOffset }, - SPDIndexTWRMinMSN: { getVal: encodeTWRMinMSN }, - SPDIndexTWRMinLsb: { getVal: encodeTWRMinLsb }, - SPDIndexTWTRMinMSNs: { getVal: encodeTWTRMinMSNs }, - SPDIndexWTRSMinLsb: { getVal: encodeTWTRSMinLsb }, - SPDIndexWTRLMinLsb: { getVal: encodeTWTRLMinLsb }, -} - -type memParts struct { - MemParts []memPart `json:"parts"` -} - -type memPart struct { - Name string - Attribs memAttributes - SPDFileName string -} - -func writeSPDManifest(memParts *memParts, SPDDirName string) error { - var s string - - fmt.Printf("Generating SPD Manifest with following entries:\n") - - for i := 0; i < len(memParts.MemParts); i++ { - fmt.Printf("%-40s %s\n", memParts.MemParts[i].Name, memParts.MemParts[i].SPDFileName) - s += fmt.Sprintf("%s,%s\n", memParts.MemParts[i].Name, memParts.MemParts[i].SPDFileName) - } - - return ioutil.WriteFile(filepath.Join(SPDDirName, SPDManifestFileName), []byte(s), 0644) -} - -func isManufacturerPartNumberByte(index int) bool { - if index >= SPDIndexManufacturerPartNumberStartByte && index <= SPDIndexManufacturerPartNumberEndByte { - return true - } - return false -} - - -func getSPDByte(index int, memAttribs *memAttributes) byte { - e, ok := SPDAttribTable[index] - if ok == false { - if isManufacturerPartNumberByte(index) { - return SPDValueManufacturerPartNumberBlank - } - return 0x00 - } - - if e.getVal != nil { - return e.getVal(memAttribs) - } - - return e.constVal -} - -func createSPD(memAttribs *memAttributes) string { - var s string - - for i := 0; i < 512; i++ { - b := getSPDByte(i, memAttribs) - - if (i + 1) % 16 == 0 { - s += fmt.Sprintf("%02X\n", b) - } else { - s += fmt.Sprintf("%02X ", b) - } - } - - return s -} - -func dedupeMemoryPart(dedupedParts []*memPart, memPart *memPart) bool { - for i := 0; i < len(dedupedParts); i++ { - if reflect.DeepEqual(dedupedParts[i].Attribs, memPart.Attribs) { - memPart.SPDFileName = dedupedParts[i].SPDFileName - return true - } - } - - return false -} - -func generateSPD(memPart *memPart, SPDId int, SPDDirName string) { - s := createSPD(&memPart.Attribs) - memPart.SPDFileName = fmt.Sprintf("ddr4-spd-%d.hex", SPDId) - ioutil.WriteFile(filepath.Join(SPDDirName, memPart.SPDFileName), []byte(s), 0644) -} - -func readMemoryParts(memParts *memParts, memPartsFileName string) error { - databytes, err := ioutil.ReadFile(memPartsFileName) - if err != nil { - return err - } - - return json.Unmarshal(databytes, memParts) -} - -func validateSpeedMTps(speedBin int) error { - if _, ok := speedBinToSPDEncoding[speedBin]; ok == false { - return fmt.Errorf("Incorrect speed bin: DDR4-", speedBin) - } - return nil -} - -func validateCapacityPerDie(capacityPerDieGb int) error { - if _, ok := densityGbToSPDEncoding[capacityPerDieGb]; ok == false { - return fmt.Errorf("Incorrect capacity per die: ", capacityPerDieGb) - } - return nil -} - -func validateDiesPerPackage(dieCount int) error { - if dieCount >= 1 && dieCount <= 2 { - return nil - } - return fmt.Errorf("Incorrect dies per package count: ", dieCount) -} - -func validateDeviceBusWidth(width int) error { - if width != 8 && width != 16 { - return fmt.Errorf("Incorrect device bus width: ", width) - } - return nil -} - -func validateRanksPerPackage(ranks int) error { - if ranks >= 1 && ranks <= 2 { - return nil - } - return fmt.Errorf("Incorrect package ranks: ", ranks) -} - - -func validateCASLatency(CL int) error { - if CL >= 10 && CL <= 24 && CL != 23 { - return nil - } - return fmt.Errorf("Incorrect CAS latency: ", CL) -} - -/* -1) validate memory parts -2) remove any fields that Intel does not care about -*/ - -/* verify the supplied CAS Latencies supported does not match default */ -func verifySupportedCASLatencies(part *memPart) error { - if part.Attribs.CASLatencies == getDefaultCASLatencies(&part.Attribs) { - return fmt.Errorf("CASLatencies for %s already matches default,\nPlease remove CASLatencies override line from the %s part attributes in the global part list and regenerate SPD Manifest", part.Name, part.Name) - } - - return nil -} - -func validateMemoryParts(memParts *memParts) error { - for i := 0; i < len(memParts.MemParts); i++ { - if err := validateSpeedMTps(memParts.MemParts[i].Attribs.SpeedMTps); err != nil { - return err - } - if err := validateCapacityPerDie(memParts.MemParts[i].Attribs.CapacityPerDieGb); err != nil { - return err - } - if err := validateDiesPerPackage(memParts.MemParts[i].Attribs.DiesPerPackage); err != nil { - return err - } - if err := validateDeviceBusWidth(memParts.MemParts[i].Attribs.DeviceBusWidth); err != nil { - return err - } - if err := validateRanksPerPackage(memParts.MemParts[i].Attribs.RanksPerPackage); err != nil { - return err - } - if err := validateCASLatency(memParts.MemParts[i].Attribs.CL_nRCD_nRP); err != nil { - return err - } - /* If CAS Latency was supplied, make sure it doesn't match default value */ - if len(memParts.MemParts[i].Attribs.CASLatencies) != 0 { - if err := verifySupportedCASLatencies(&memParts.MemParts[i]); err != nil { - return err - } - } - } - - return nil -} - -const ( - /* First Byte */ - CAS9 = 1 << 2 - CAS10 = 1 << 3 - CAS11 = 1 << 4 - CAS12 = 1 << 5 - CAS13 = 1 << 6 - CAS14 = 1 << 7 - /* Second Byte */ - CAS15 = 1 << 0 - CAS16 = 1 << 1 - CAS17 = 1 << 2 - CAS18 = 1 << 3 - CAS19 = 1 << 4 - CAS20 = 1 << 5 - CAS21 = 1 << 6 - CAS22 = 1 << 7 - /* Third Byte */ - CAS24 = 1 << 1 -) - -func encodeLatencies(latency int, memAttribs *memAttributes) error { - switch latency { - case 9: - memAttribs.CASFirstByte |= CAS9 - case 10: - memAttribs.CASFirstByte |= CAS10 - case 11: - memAttribs.CASFirstByte |= CAS11 - case 12: - memAttribs.CASFirstByte |= CAS12 - case 13: - memAttribs.CASFirstByte |= CAS13 - case 14: - memAttribs.CASFirstByte |= CAS14 - case 15: - memAttribs.CASSecondByte |= CAS15 - case 16: - memAttribs.CASSecondByte |= CAS16 - case 17: - memAttribs.CASSecondByte |= CAS17 - case 18: - memAttribs.CASSecondByte |= CAS18 - case 19: - memAttribs.CASSecondByte |= CAS19 - case 20: - memAttribs.CASSecondByte |= CAS20 - case 21: - memAttribs.CASSecondByte |= CAS21 - case 22: - memAttribs.CASSecondByte |= CAS22 - case 24: - memAttribs.CASThirdByte |= CAS24 - default: - fmt.Errorf("Incorrect CAS Latency: ", latency) - } - - return nil -} - -/* Default CAS Latencies from Speed Bin tables in JEDS79-4C */ -func getDefaultCASLatencies(memAttribs *memAttributes) string { - var str string - - switch memAttribs.SpeedMTps { - case 1600: - switch memAttribs.CL_nRCD_nRP { - case 10: - str = "9 10 11 12" - case 11: - str = "9 11 12" - case 12: - str = "10 12" - } - case 1866: - switch memAttribs.CL_nRCD_nRP { - case 12: - str = "9 10 12 13 14" - case 13: - str = "9 11 12 13 14" - case 14: - str = "10 12 14" - } - case 2133: - switch memAttribs.CL_nRCD_nRP { - case 14: - str = "9 10 12 14 15 16" - case 15: - str = "9 11 12 13 14 15 16" - case 16: - str = "10 12 14 16" - } - case 2400: - switch memAttribs.CL_nRCD_nRP { - case 15: - str = "9 10 12 14 15 16 17 18" - case 16: - str = "9 11 12 13 14 15 16 17 18" - case 17: - str = "10 11 12 13 14 15 16 17 18" - case 18: - str = "10 12 14 16 18" - } - case 2666: - switch memAttribs.CL_nRCD_nRP { - case 17: - str = "9 10 11 12 13 14 15 16 17 18 19 20" - case 18: - str = "9 10 11 12 13 14 15 16 17 18 19 20" - case 19: - str = "10 11 12 13 14 15 16 17 18 19 20" - case 20: - str = "10 12 14 16 18 20" - } - case 2933: - switch memAttribs.CL_nRCD_nRP { - case 19: - str = "9 10 11 12 13 14 15 16 17 18 19 20 21 22" - case 20: - str = "10 11 12 13 14 15 16 17 18 19 20 21 22" - case 21: - str = "10 11 12 13 14 15 16 17 18 19 20 21 22" - case 22: - str = "10 12 14 16 18 20 22" - } - case 3200: - switch memAttribs.CL_nRCD_nRP { - case 20: - str = "9 10 11 12 13 14 15 16 17 18 19 20 21 22 24" - case 22: - str = "10 11 12 13 14 15 16 17 18 19 20 21 22 24" - case 24: - str = "10 12 14 16 18 20 22 24" - } - } - - return str -} - -func updateCAS(memAttribs *memAttributes) error { - if len(memAttribs.CASLatencies) == 0 { - memAttribs.CASLatencies = getDefaultCASLatencies(memAttribs) - } - - latencies := strings.Fields(memAttribs.CASLatencies) - for i := 0; i < len(latencies); i++ { - latency,err := strconv.Atoi(latencies[i]) - if err != nil { - return fmt.Errorf("Unable to convert latency ", latencies[i]) - } - if err := encodeLatencies(latency, memAttribs); err != nil { - return err - } - } - - return nil -} - -func getTAAMinPs(memAttribs *memAttributes) int { - return (memAttribs.CL_nRCD_nRP * 2000000) / memAttribs.SpeedMTps -} - -func updateTAAMin(memAttribs *memAttributes) { - if memAttribs.TAAMinPs == 0 { - memAttribs.TAAMinPs = getTAAMinPs(memAttribs) - } -} - -func updateTRCDMin(memAttribs *memAttributes) { - /* tRCDmin is same as tAAmin for all cases */ - if memAttribs.TRCDMinPs == 0 { - memAttribs.TRCDMinPs = getTAAMinPs(memAttribs) - } -} - -func updateTRPMin(memAttribs *memAttributes) { - /* tRPmin is same as tAAmin for all cases */ - if memAttribs.TRPMinPs == 0 { - memAttribs.TRPMinPs = getTAAMinPs(memAttribs) - } -} - -func updateTRASMin(memAttribs *memAttributes) { - if memAttribs.TRASMinPs == 0 { - memAttribs.TRASMinPs = speedBinToSPDEncoding[memAttribs.SpeedMTps].TRASMinPs - } -} - - -func updateTRCMin(memAttribs *memAttributes) { - if memAttribs.TRCMinPs == 0 { - memAttribs.TRCMinPs = getTRCMinPs(memAttribs) - } -} - -func updateTCK(memAttribs *memAttributes) { - if memAttribs.TCKMinPs == 0 { - memAttribs.TCKMinPs = getDefaultTCKMinPs(memAttribs) - } - if memAttribs.TCKMaxPs == 0 { - memAttribs.TCKMaxPs = speedBinToSPDEncoding[memAttribs.SpeedMTps].TCKMaxPs - } -} - -func updateTWRMin(memAttribs *memAttributes) { - if memAttribs.TWRMinPs == 0 { - memAttribs.TWRMinPs = TimingValueTWRMinPs - } -} - -func updateTWTRMin(memAttribs *memAttributes) { - if memAttribs.TWTRLMinPs == 0 { - memAttribs.TWTRLMinPs = TimingValueTWTRLMinPs - } - if memAttribs.TWTRSMinPs == 0 { - memAttribs.TWTRSMinPs = TimingValueTWTRSMinPs - } -} - -func updateMemoryAttributes(memAttribs *memAttributes) { - updateTCK(memAttribs) - updateTAAMin(memAttribs) - updateTRCDMin(memAttribs) - updateTRPMin(memAttribs) - updateTRASMin(memAttribs) - updateTRCMin(memAttribs) - updateTWRMin(memAttribs) - updateTWTRMin(memAttribs) - updateCAS(memAttribs) - updateTRFC1Min(memAttribs) - updateTRFC2Min(memAttribs) - updateTRFC4Min(memAttribs) - updateTCCDLMin(memAttribs) - updateTRRDSMin(memAttribs) - updateTRRDLMin(memAttribs) - updateTFAWMin(memAttribs) -} - -func isPlatformSupported(platform string) error { - var ok bool - - currPlatform, ok = platformMap[platform] - if ok == false { - return fmt.Errorf("Unsupported platform: ", platform) - } - - return nil -} - -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(" mem_parts_list_json = JSON File containing list of memory parts and attributes\n") - fmt.Printf(" platform = SoC Platform for which the SPDs are being generated\n\n\n") -} - -func main() { - if len(os.Args) != 4 { - usage() - log.Fatal("Incorrect number of arguments") - } - - var memParts memParts - var dedupedParts []*memPart - - SPDDir, GlobalMemPartsFile, Platform := os.Args[1], os.Args[2], strings.ToUpper(os.Args[3]) - - if err := isPlatformSupported(Platform); err != nil { - log.Fatal(err) - } - - if err := readMemoryParts(&memParts, GlobalMemPartsFile); err != nil { - log.Fatal(err) - } - - if err := validateMemoryParts(&memParts); err != nil { - log.Fatal(err) - } - - SPDId := 1 - - for i := 0; i < len(memParts.MemParts); i++ { - updateMemoryAttributes(&memParts.MemParts[i].Attribs) - if dedupeMemoryPart(dedupedParts, &memParts.MemParts[i]) == false { - generateSPD(&memParts.MemParts[i], SPDId, SPDDir) - SPDId++ - dedupedParts = append(dedupedParts, &memParts.MemParts[i]) - } - } - - if err := writeSPDManifest(&memParts, SPDDir); err != nil { - log.Fatal(err) - } -} diff --git a/util/spd_tools/intel/ddr4/global_ddr4_mem_parts.json.txt b/util/spd_tools/intel/ddr4/global_ddr4_mem_parts.json.txt deleted file mode 100644 index 4e9c7c8e95..0000000000 --- a/util/spd_tools/intel/ddr4/global_ddr4_mem_parts.json.txt +++ /dev/null @@ -1,37 +0,0 @@ -{ - "parts": [ - { - "name": "H5AN8G6NDJR-XNC", - "attribs": { - "speedMTps": 3200, - "CL_nRCD_nRP": 22, - "capacityPerDieGb": 8, - "diesPerPackage": 1, - "deviceBusWidth": 16, - "ranksPerPackage": 1 - } - }, - { - "name": "MT40A512M16TB-062E:J", - "attribs": { - "speedMTps": 3200, - "CL_nRCD_nRP": 22, - "capacityPerDieGb": 8, - "diesPerPackage": 1, - "deviceBusWidth": 16, - "ranksPerPackage": 1 - } - }, - { - "name": "H5ANAG6NCMR-XNC", - "attribs": { - "speedMTps": 3200, - "CL_nRCD_nRP": 22, - "capacityPerDieGb": 8, - "diesPerPackage": 2, - "deviceBusWidth": 16, - "ranksPerPackage": 1 - } - } - ] -} diff --git a/util/spd_tools/intel/lp4x/README.md b/util/spd_tools/intel/lp4x/README.md deleted file mode 100644 index e614f259cf..0000000000 --- a/util/spd_tools/intel/lp4x/README.md +++ /dev/null @@ -1,265 +0,0 @@ -# LPDDR4x SPD tools README - -Tools for generating SPD files for LPDDR4x memory used in memory down -configurations on Intel Tiger Lake (TGL) and Jasper Lake (JSL) based -platforms. These tools generate SPDs following JESD209-4C -specification and Intel recommendations (doc #616599, #610202) for -LPDDR4x SPD. - -There are two tools provided that assist TGL and JSL based mainboards -to generate SPDs and Makefile to integrate these SPDs in coreboot -build. These tools can also be used to allocate DRAM IDs (configure -DRAM hardware straps) for any LPDDR4x memory part used by the board. - -* 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 LPDDR4x - 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. - -## Tool 1 - gen_spd.go - -This program takes as input: -* Pointer to directory where the generated SPD files and manifest will - be placed. -* JSON file containing a global list of memory parts with their - attributes as per the datasheet. This is the list of all known - LPDDR4x memory parts irrespective of their usage on the board. -* SoC platform name for which the SPDs are being generated. Currently - supported platform names are `TGL` and `JSL`. - -Input JSON file requires the following two fields for every memory part: -* `name`: Name of the memory part -* `attribs`: List of attributes of the memory part as per its - datasheet. These attributes match the part specifications and are - independent of any SoC expectations. Tool takes care of translating - the physical attributes of the memory part to match JEDEC and Intel - MRC expectations. - -`attribs` field further contains two types of sub-fields: -* Mandatory: These attributes have to be provided for a memory part. -* Optional: These attributes can be provided by memory part if it wants - to override the defaults. - -### Mandatory `attribs` - -* `densityPerChannelGb`: Density in Gb of the physical channel. - -* `banks`: Number of banks per physical channel. This is typically 8 - for LPDDR4x memory parts. - -* `channelsPerDie`: Number of physical channels per die. Valid values: - `1, 2, 4`. For a part with x16 bit width, number of channels per die - is 1 or 2. For a part with x8 bit width, number of channels can be - 2 or 4 (4 is basically when two dual-channel byte mode devices are - combined as shown in Figure 3 in JESD209-4C). - -* `diesPerPackage`: Number of physical dies in each SDRAM - package. As per JESD209-4C, "Standard LPDDR4 package ballmaps - allocate one ZQ ball per die." Thus, number of diesPerPackage is the - number of ZQ balls on the package. - -* `bitWidthPerChannel`: Width of each physical channel. Valid values: - `8, 16` bits. - -* `ranksPerChannel`: Number of ranks per physical channel. Valid - values: `1, 2`. If the channels across multiple dies share the same - DQ/DQS pins but use a separate CS, then ranks is 2 else it is 1. - -* `speedMbps`: Maximum data rate supported by the part in Mbps. Valid - values: `3200, 3733, 4267` Mbps. - -### Optional `attribs` - -* `trfcabNs`: Minimum Refresh Recovery Delay Time (tRFCab) for all - banks in nanoseconds. As per JESD209-4C, this is dependent on the - density per channel. Default values used: - * 6Gb : 280ns - * 8Gb : 280ns - * 12Gb: 380ns - * 16Gb: 380ns - -* `trfcpbNs`: Minimum Refresh Recovery Delay Time (tRFCab) per - bank in nanoseconds. As per JESD209-4C, this is dependent on the - density per channel. Default values used: - * 6Gb : 140ns - * 8Gb : 140ns - * 12Gb: 190ns - * 16Gb: 190ns - -* `trpabMinNs`: Minimum Row Precharge Delay Time (tRPab) for all banks - in nanoseconds. As per JESD209-4C, this is max(21ns, 4nck) which - defaults to `21ns`. - -* `trppbMinNs`: Minimum Row Precharge Delay Time (tRPpb) per bank in - nanoseconds. As per JESD209-4C, this is max(18ns, 4nck) which - defaults to `18ns`. - -* `tckMinPs`: SDRAM minimum cycle time (tckMin) value in - picoseconds. This is typically calculated based on the `speedMbps` - attribute. `(1 / speedMbps) * 2`. Default values used(taken from - JESD209-4C): - * 4267 Mbps: 468ps - * 3733 Mbps: 535ps - * 3200 Mbps: 625ps - -* `tckMaxPs`: SDRAM maximum cycle time (tckMax) value in - picoseconds. Default value used: `31875ps`. As per JESD209-4C, - TCKmax should be 100ns (100000ps) for all speed grades. But the SPD - byte to encode this field is only 1 byte. Hence, the maximum value - that can be encoded is 31875ps. - -* `taaMinPs`: Minimum CAS Latency Time(taaMin) in picoseconds. This - value defaults to nck * tckMin, where nck is minimum CAS latency. - -* `trcdMinNs`: Minimum RAS# to CAS# Delay Time (tRCDmin) in - nanoseconds. As per JESD209-4C, this is max(18ns, 4nck) which - defaults to `18ns`. - -* `casLatencies`: List of CAS latencies supported by the - part. This is dependent on the attrib `speedMbps`. Default values - used: - * 4267: `"6 10 14 20 24 28 32 36"`. - * 3733: `"6 10 14 20 24 28 32"`. - * 3200: `"6 10 14 20 24 28"`. - -### Example JSON file -``` -{ - "parts": [ - { - "name": "MEMORY_PART_A", - "attribs": { - "densityPerChannelGb": 8, - "banks": 8, - "channelsPerDie": 2, - "diesPerPackage": 2, - "bitWidthPerChannel": 16, - "ranksPerChannel": 1, - "speedMbps": 4267 - } - }, - { - "name": "MEMORY_PART_B", - "attribs": { - "densityPerChannelGb": 8, - "banks": 8, - "channelsPerDie": 1, - "diesPerPackage": 2, - "bitWidthPerChannel": 16, - "ranksPerChannel": 1, - "speedMbps": 3733, - "casLatencies": "14 20 24 28 32", - "tckMaxPs": "1250" - } - } - ] -} -``` - -### Output - -This tool generates the following files using the global list of -memory parts in JSON format as described above: - * De-duplicated SPDs required for the different memory parts. These - SPD files are named (spd_1.hex, spd_2.hex, spd_3.hex and so on) - and placed in the directory provided as an input to the tool. - * CSV file representing which of the deduplicated SPD files is used - by which memory part. This file is named as - `spd_manifest.generated.txt` and placed in the directory provided - as an input to the tool along with the generated SPD - files. Example CSV file: - ``` - MEMORY_PART_A, spd_1.hex - MEMORY_PART_B, spd_2.hex - MEMORY_PART_C, spd_3.hex - MEMORY_PART_D, spd_2.hex - MEMORY_PART_E, spd_2.hex - ``` - -## Tool 2 - gen_part_id.go - -This program takes as input: -* Pointer to directory where the SPD files and the manifest file - `spd_manifest.generated.txt` (in CSV format) are placed by - gen_spd.go -* File containing list of memory parts used by the board. Each line of - the file is supposed to contain one memory part `name` as present in - the global list of memory parts provided to gen_spd.go -* Pointer to directory where the generated Makefile.inc should be - placed by the tool. - -### Output - -This program provides the following: - -* Prints out the list of DRAM hardware strap IDs that should be - allocated to each memory part listed in the input file. -* Makefile.inc is generated in the provided directory to integrate - SPDs generated by gen_spd.go with the coreboot build for the board. -* dram_id.generated.txt is generated in the same directory as - Makefile. This contains the part IDs assigned to the different - memory parts. (Useful to integrate in board schematics). - -Sample output (dram_id.generated.txt): -``` -DRAM Part Name ID to assign -MEMORY_PART_A 0 (0000) -MEMORY_PART_B 1 (0001) -MEMORY_PART_C 2 (0010) -MEMORY_PART_D 1 (0001) -``` - -Sample Makefile.inc: -``` -## SPDX-License-Identifier: GPL-2.0-or-later -## This is an auto-generated file. Do not edit!! - -SPD_SOURCES = -SPD_SOURCES += spd_1.hex # ID = 0(0b0000) Parts = MEMORY_PART_A -SPD_SOURCES += spd_2.hex # ID = 1(0b0001) Parts = MEMORY_PART_B, MEMORY_PART_D -SPD_SOURCES += spd_3.hex # ID = 2(0b0010) Parts = MEMORY_PART_C -``` - -### Note of caution - -This program assigns DRAM IDs using the order of DRAM part names -provided in the input file. Thus, when adding a new memory part to the -list, it should always go to the end of the input text file. This -guarantees that the memory parts that were already assigned IDs do not -change. - -## How to build the tools? -``` -# go build gen_spd.go -# go build gen_part_id.go -``` - -## How to use the tools? -``` -# ./gen_spd -# ./gen_part_id -``` - -### Need to add a new memory part for a board? - -* If the memory part is not present in the global list of memory - parts, then add the memory part name and attributes as per the - datasheet to the file containing the global list. - * Use `gen_spd.go` with input as the file containing the global list - of memory parts to generate de-duplicated SPDs. - * If a new SPD file is generated, use `git add` to add it to the - tree and push a CL for review. -* Update the file containing memory parts used by board (variant) to - add the new memory part name at the end of the file. - * Use gen_part_id.go providing it pointer to the location where SPD - files are stored and file containing the list of memory parts used - by the board(variant). - * Use `git add` to add `Makefile.inc` and `dram_id.generated.txt` - with updated changes and push a CL for review. diff --git a/util/spd_tools/intel/lp4x/gen_part_id.go b/util/spd_tools/intel/lp4x/gen_part_id.go deleted file mode 100644 index 7ed255c83b..0000000000 --- a/util/spd_tools/intel/lp4x/gen_part_id.go +++ /dev/null @@ -1,215 +0,0 @@ -/* 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 \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 = lp4x\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) - } -} diff --git a/util/spd_tools/intel/lp4x/gen_spd.go b/util/spd_tools/intel/lp4x/gen_spd.go deleted file mode 100644 index 2465815e49..0000000000 --- a/util/spd_tools/intel/lp4x/gen_spd.go +++ /dev/null @@ -1,986 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "os" - "path/filepath" - "reflect" - "strconv" - "strings" -) - -/* - * This program generates de-duplicated SPD files for LPDDR4x memory using the global memory - * part list provided in CSV format. In addition to that, it also generates SPD manifest in CSV - * format that contains entries of type (DRAM part name, SPD file name) which provides the SPD - * file name used by a given DRAM part. - * - * It takes as input: - * Pointer to directory where the generated SPD files will be placed. - * JSON file containing a list of memory parts with their attributes as per datasheet. - */ -const ( - SPDManifestFileName = "spd_manifest.generated.txt" - - PlatformTGL = 0 - PlatformJSL = 1 -) - -var platformMap = map[string]int { - "TGL": PlatformTGL, - "JSL": PlatformJSL, -} - -var currPlatform int - -type memAttributes struct { - /* Primary attributes - must be provided by JSON file for each part */ - DensityPerChannelGb int - Banks int - ChannelsPerDie int - DiesPerPackage int - BitWidthPerChannel int - RanksPerChannel int - SpeedMbps int - - /* - * All the following parameters are optional and required only if the part requires - * special parameters as per the datasheet. - */ - /* Timing parameters */ - TRFCABNs int - TRFCPBNs int - TRPABMinNs int - TRPPBMinNs int - TCKMinPs int - TCKMaxPs int - TAAMinPs int - TRCDMinNs int - - /* CAS */ - CASLatencies string - CASFirstByte byte - CASSecondByte byte - CASThirdByte byte -} - -/* This encodes the density in Gb to SPD values as per JESD 21-C */ -var densityGbToSPDEncoding = map[int]byte { - 4: 0x4, - 6: 0xb, - 8: 0x5, - 12: 0x8, - 16: 0x6, - 24: 0x9, - 32: 0x7, -} - -/* - * Table 3 from JESD209-4C. - * Maps density per physical channel to row-column encoding as per JESD 21-C for a device with - * x16 physical channel. - */ -var densityGbx16ChannelToRowColumnEncoding = map[int]byte { - 4: 0x19, /* 15 rows, 10 columns */ - 6: 0x21, /* 16 rows, 10 columns */ - 8: 0x21, /* 16 rows, 10 columns */ - 12: 0x29, /* 17 rows, 10 columns */ - 16: 0x29, /* 17 rows, 10 columns */ -} - -/* - * Table 5 from JESD209-4C. - * Maps density per physical channel to row-column encoding as per JESD 21-C for a device with - * x8 physical channel. - */ -var densityGbx8ChannelToRowColumnEncoding = map[int]byte { - 3: 0x21, /* 16 rows, 10 columns */ - 4: 0x21, /* 16 rows, 10 columns */ - 6: 0x29, /* 17 rows, 10 columns */ - 8: 0x29, /* 17 rows, 10 columns */ - 12: 0x31, /* 18 rows, 10 columns */ - 16: 0x31, /* 18 rows, 10 columns */ -} - -type refreshTimings struct { - TRFCABNs int - TRFCPBNs int -} - -/* - * Table 112 from JESD209-4C - * Maps density per physical channel to refresh timings. This is the same for x8 and x16 - * devices. - */ -var densityGbPhysicalChannelToRefreshEncoding = map[int]refreshTimings { - 3: { - TRFCABNs: 180, - TRFCPBNs: 90, - }, - 4: { - TRFCABNs: 180, - TRFCPBNs: 90, - }, - 6: { - TRFCABNs: 280, - TRFCPBNs: 140, - }, - 8: { - TRFCABNs: 280, - TRFCPBNs: 140, - }, - 12: { - TRFCABNs: 380, - TRFCPBNs: 190, - }, - 16: { - TRFCABNs: 380, - TRFCPBNs: 190, - }, -} - -type speedParams struct { - TCKMinPs int - TCKMaxPs int - CASLatenciesx16Channel string - CASLatenciesx8Channel string -} - -const ( - /* First Byte */ - CAS6 = 1 << 1 - CAS10 = 1 << 4 - CAS14 = 1 << 7 - /* Second Byte */ - CAS16 = 1 << 0 - CAS20 = 1 << 2 - CAS22 = 1 << 3 - CAS24 = 1 << 4 - CAS26 = 1 << 5 - CAS28 = 1 << 6 - /* Third Byte */ - CAS32 = 1 << 0 - CAS36 = 1 << 2 - CAS40 = 1 << 4 -) - -const ( - /* - * JEDEC spec says that TCKmax should be 100ns for all speed grades. - * 100ns in MTB units comes out to be 0x320. But since this is a byte field, set it to - * 0xFF i.e. 31.875ns. - */ - TCKMaxPsDefault = 31875 -) - -var speedMbpsToSPDEncoding = map[int]speedParams { - 4267: { - TCKMinPs: 468, /* 1/4267 * 2 */ - TCKMaxPs: TCKMaxPsDefault, - CASLatenciesx16Channel: "6 10 14 20 24 28 32 36", - CASLatenciesx8Channel: "6 10 16 22 26 32 36 40", - }, - 3733: { - TCKMinPs: 535, /* 1/3733 * 2 */ - TCKMaxPs: TCKMaxPsDefault, - CASLatenciesx16Channel: "6 10 14 20 24 28 32", - CASLatenciesx8Channel: "6 10 16 22 26 32 36", - }, - 3200: { - TCKMinPs: 625, /* 1/3200 * 2 */ - TCKMaxPs: TCKMaxPsDefault, - CASLatenciesx16Channel: "6 10 14 20 24 28", - CASLatenciesx8Channel: "6 10 16 22 26 32", - }, -} - -var bankEncoding = map[int]byte { - 4: 0 << 4, - 8: 1 << 4, -} - -const ( - TGLLogicalChannelWidth = 16 -) - -/* Returns density to encode as per Intel MRC expectations. */ -func getMRCDensity(memAttribs *memAttributes) int { - if currPlatform == PlatformTGL { - /* - * Intel MRC on TGL expects density per logical channel to be encoded in - * SPDIndexDensityBanks. Logical channel on TGL is an x16 channel. - */ - return memAttribs.DensityPerChannelGb * TGLLogicalChannelWidth / memAttribs.BitWidthPerChannel - } else if currPlatform == PlatformJSL { - /* - * Intel MRC on JSL expects density per die to be encoded in - * SPDIndexDensityBanks. - */ - return memAttribs.DensityPerChannelGb * memAttribs.ChannelsPerDie - } - - return 0 -} - -func encodeDensityBanks(memAttribs *memAttributes) byte { - var b byte - - b = densityGbToSPDEncoding[getMRCDensity(memAttribs)] - b |= bankEncoding[memAttribs.Banks] - - return b -} - -func encodeSdramAddressing(memAttribs *memAttributes) byte { - densityPerChannelGb := memAttribs.DensityPerChannelGb - if memAttribs.BitWidthPerChannel == 8 { - return densityGbx8ChannelToRowColumnEncoding[densityPerChannelGb] - } else { - return densityGbx16ChannelToRowColumnEncoding[densityPerChannelGb] - } - return 0 -} - -func encodeChannelsPerDie(channels int) byte { - var temp byte - - temp = byte(channels >> 1) - - return temp << 2 -} - -func encodePackage(dies int) byte { - var temp byte - - if dies > 1 { - /* If more than one die, then this is a non-monolithic device. */ - temp = 1 - } else { - /* If only single die, then this is a monolithic device. */ - temp = 0 - } - - return temp << 7 -} - -func encodeDiesPerPackage(memAttribs *memAttributes) byte { - var dies int = 0 - if currPlatform == PlatformTGL { - /* Intel MRC expects logical dies to be encoded for TGL. */ - dies = memAttribs.ChannelsPerDie * memAttribs.RanksPerChannel * memAttribs.BitWidthPerChannel / 16 - } else if currPlatform == PlatformJSL { - /* Intel MRC expects physical dies to be encoded for JSL. */ - dies = memAttribs.DiesPerPackage - } - - b := encodePackage(dies) /* Monolithic / Non-monolithic device */ - b |= (byte(dies) - 1) << 4 - - return b -} - -func encodePackageType(memAttribs *memAttributes) byte { - var b byte - - b |= encodeChannelsPerDie(memAttribs.ChannelsPerDie) - b |= encodeDiesPerPackage(memAttribs) - - return b -} - -func encodeDataWidth(bitWidthPerChannel int) byte { - return byte(bitWidthPerChannel / 8) -} - -func encodeRanks(ranks int) byte { - var b byte - b = byte(ranks - 1) - return b << 3 -} - -func encodeModuleOrganization(memAttribs *memAttributes) byte { - var b byte - - b = encodeDataWidth(memAttribs.BitWidthPerChannel) - b |= encodeRanks(memAttribs.RanksPerChannel) - - return b -} - -const ( - /* - * As per advisory 616599: - * 7:5 (Number of system channels) = 000 (1 channel always) - * 2:0 (Bus width) = 001 (x16 always) - * Set to 0x01. - */ - SPDValueBusWidthTGL = 0x01 - /* - * As per advisory 610202: - * 7:5 (Number of system channels) = 001 (2 channel always) - * 2:0 (Bus width) = 010 (x32 always) - * Set to 0x01. - */ - SPDValueBusWidthJSL = 0x22 -) - -func encodeBusWidth(memAttribs *memAttributes) byte { - if currPlatform == PlatformTGL { - return SPDValueBusWidthTGL - } else if currPlatform == PlatformJSL { - return SPDValueBusWidthJSL - } - return 0 -} - -func encodeTCKMin(memAttribs *memAttributes) byte { - return convPsToMtbByte(memAttribs.TCKMinPs) -} - -func encodeTCKMinFineOffset(memAttribs *memAttributes) byte { - return convPsToFtbByte(memAttribs.TCKMinPs) -} - -func encodeTCKMax(memAttribs *memAttributes) byte { - return convPsToMtbByte(memAttribs.TCKMaxPs) -} - -func encodeTCKMaxFineOffset(memAttribs *memAttributes) byte { - return convPsToFtbByte(memAttribs.TCKMaxPs) -} - -func encodeCASFirstByte(memAttribs *memAttributes) byte { - return memAttribs.CASFirstByte -} - -func encodeCASSecondByte(memAttribs *memAttributes) byte { - return memAttribs.CASSecondByte -} - -func encodeCASThirdByte(memAttribs *memAttributes) byte { - return memAttribs.CASThirdByte -} - -func divRoundUp(dividend int, divisor int) int { - return (dividend + divisor - 1) / divisor -} - -func convNsToPs(timeNs int) int { - return timeNs * 1000 -} - -func convMtbToPs(mtb int) int { - return mtb * 125 -} - -func convPsToMtb(timePs int) int { - return divRoundUp(timePs, 125) -} - -func convPsToMtbByte(timePs int) byte { - return byte(convPsToMtb(timePs) & 0xff) -} - -func convPsToFtbByte(timePs int) byte { - mtb := convPsToMtb(timePs) - ftb := timePs - convMtbToPs(mtb) - - return byte(ftb) -} - -func convNsToMtb(timeNs int) int { - return convPsToMtb(convNsToPs(timeNs)) -} - -func convNsToMtbByte(timeNs int) byte { - return convPsToMtbByte(convNsToPs(timeNs)) -} - -func convNsToFtbByte(timeNs int) byte { - return convPsToFtbByte(convNsToPs(timeNs)) -} - -func encodeTAAMin(memAttribs *memAttributes) byte { - return convPsToMtbByte(memAttribs.TAAMinPs) -} - -func encodeTAAMinFineOffset(memAttribs *memAttributes) byte { - return convPsToFtbByte(memAttribs.TAAMinPs) -} - -func encodeTRCDMin(memAttribs *memAttributes) byte { - return convNsToMtbByte(memAttribs.TRCDMinNs) -} - -func encodeTRCDMinFineOffset(memAttribs *memAttributes) byte { - return convNsToFtbByte(memAttribs.TRCDMinNs) -} - -func encodeTRPABMin(memAttribs *memAttributes) byte { - return convNsToMtbByte(memAttribs.TRPABMinNs) -} - -func encodeTRPABMinFineOffset(memAttribs *memAttributes) byte { - return convNsToFtbByte(memAttribs.TRPABMinNs) -} - -func encodeTRPPBMin(memAttribs *memAttributes) byte { - return convNsToMtbByte(memAttribs.TRPPBMinNs) -} - -func encodeTRPPBMinFineOffset(memAttribs *memAttributes) byte { - return convNsToFtbByte(memAttribs.TRPPBMinNs) -} - -func encodeTRFCABMinMsb(memAttribs *memAttributes) byte { - return byte((convNsToMtb(memAttribs.TRFCABNs) >> 8) & 0xff) -} - -func encodeTRFCABMinLsb(memAttribs *memAttributes) byte { - return byte(convNsToMtb(memAttribs.TRFCABNs) & 0xff) -} - -func encodeTRFCPBMinMsb(memAttribs *memAttributes) byte { - return byte((convNsToMtb(memAttribs.TRFCPBNs) >> 8) & 0xff) -} - -func encodeTRFCPBMinLsb(memAttribs *memAttributes) byte { - return byte(convNsToMtb(memAttribs.TRFCPBNs) & 0xff) -} - -type SPDAttribFunc func (*memAttributes) byte - -type SPDAttribTableEntry struct { - constVal byte - getVal SPDAttribFunc -} - -const ( - /* SPD Byte Index */ - SPDIndexSize = 0 - SPDIndexRevision = 1 - SPDIndexMemoryType = 2 - SPDIndexModuleType = 3 - SPDIndexDensityBanks = 4 - SPDIndexAddressing = 5 - SPDIndexPackageType = 6 - SPDIndexOptionalFeatures = 7 - SPDIndexModuleOrganization = 12 - SPDIndexBusWidth = 13 - SPDIndexTimebases = 17 - SPDIndexTCKMin = 18 - SPDIndexTCKMax = 19 - SPDIndexCASFirstByte = 20 - SPDIndexCASSecondByte = 21 - SPDIndexCASThirdByte = 22 - SPDIndexCASFourthByte = 23 - SPDIndexTAAMin = 24 - SPDIndexReadWriteLatency = 25 - SPDIndexTRCDMin = 26 - SPDIndexTRPABMin = 27 - SPDIndexTRPPBMin = 28 - SPDIndexTRFCABMinLSB = 29 - SPDIndexTRFCABMinMSB = 30 - SPDIndexTRFCPBMinLSB = 31 - SPDIndexTRFCPBMinMSB = 32 - SPDIndexTRPPBMinFineOffset = 120 - SPDIndexTRPABMinFineOffset = 121 - SPDIndexTRCDMinFineOffset = 122 - SPDIndexTAAMinFineOffset = 123 - SPDIndexTCKMaxFineOffset = 124 - SPDIndexTCKMinFineOffset = 125 - SPDIndexManufacturerPartNumberStartByte = 329 - SPDIndexManufacturerPartNumberEndByte = 348 - - /* SPD Byte Value */ - - /* - * From JEDEC spec: - * 6:4 (Bytes total) = 2 (512 bytes) - * 3:0 (Bytes used) = 3 (384 bytes) - * Set to 0x23 for LPDDR4x. - */ - SPDValueSize = 0x23 - - /* - * From JEDEC spec: Revision 1.1 - * Set to 0x11. - */ - SPDValueRevision = 0x11 - - /* LPDDR4x memory type = 0x11 */ - SPDValueMemoryType = 0x11 - - /* - * From JEDEC spec: - * 7:7 (Hybrid) = 0 (Not hybrid) - * 6:4 (Hybrid media) = 000 (Not hybrid) - * 3:0 (Base Module Type) = 1110 (Non-DIMM solution) - * - * This is dependent on hardware design. LPDDR4x only has memory down solution. - * Hence this is not hybrid non-DIMM solution. - * Set to 0x0E. - */ - SPDValueModuleType = 0x0e - - /* - * From JEDEC spec: - * 5:4 (Maximum Activate Window) = 00 (8192 * tREFI) - * 3:0 (Maximum Activate Count) = 1000 (Unlimited MAC) - * - * Needs to come from datasheet, but most parts seem to support unlimited MAC. - * MR#24 OP3 - */ - SPDValueOptionalFeatures = 0x08 - - /* - * From JEDEC spec: - * 3:2 (MTB) = 00 (0.125ns) - * 1:0 (FTB) = 00 (1ps) - * Set to 0x00. - */ - SPDValueTimebases = 0x00 - - /* CAS fourth byte: All bits are reserved */ - SPDValueCASFourthByte = 0x00 - - /* Write Latency Set A and Read Latency DBI-RD disabled. */ - SPDValueReadWriteLatency = 0x00 - - /* As per JEDEC spec, unused digits of manufacturer part number are left as blank. */ - SPDValueManufacturerPartNumberBlank = 0x20 -) - -var SPDAttribTable = map[int]SPDAttribTableEntry { - SPDIndexSize: { constVal: SPDValueSize }, - SPDIndexRevision: { constVal: SPDValueRevision }, - SPDIndexMemoryType: { constVal: SPDValueMemoryType }, - SPDIndexModuleType: { constVal: SPDValueModuleType }, - SPDIndexDensityBanks: { getVal: encodeDensityBanks }, - SPDIndexAddressing: { getVal: encodeSdramAddressing }, - SPDIndexPackageType: { getVal: encodePackageType }, - SPDIndexOptionalFeatures: { constVal: SPDValueOptionalFeatures }, - SPDIndexModuleOrganization: { getVal: encodeModuleOrganization }, - SPDIndexBusWidth: { getVal: encodeBusWidth }, - SPDIndexTimebases: { constVal: SPDValueTimebases }, - SPDIndexTCKMin: { getVal: encodeTCKMin }, - SPDIndexTCKMax: { getVal: encodeTCKMax }, - SPDIndexTCKMaxFineOffset: { getVal: encodeTCKMaxFineOffset }, - SPDIndexTCKMinFineOffset: { getVal: encodeTCKMinFineOffset }, - SPDIndexCASFirstByte: { getVal: encodeCASFirstByte }, - SPDIndexCASSecondByte: { getVal: encodeCASSecondByte }, - SPDIndexCASThirdByte: { getVal: encodeCASThirdByte }, - SPDIndexCASFourthByte: { constVal: SPDValueCASFourthByte }, - SPDIndexTAAMin: { getVal: encodeTAAMin }, - SPDIndexTAAMinFineOffset: { getVal: encodeTAAMinFineOffset }, - SPDIndexReadWriteLatency: { constVal: SPDValueReadWriteLatency }, - SPDIndexTRCDMin: { getVal: encodeTRCDMin }, - SPDIndexTRCDMinFineOffset: { getVal: encodeTRCDMinFineOffset }, - SPDIndexTRPABMin: { getVal: encodeTRPABMin }, - SPDIndexTRPABMinFineOffset: { getVal: encodeTRPABMinFineOffset }, - SPDIndexTRPPBMin: { getVal: encodeTRPPBMin }, - SPDIndexTRPPBMinFineOffset: { getVal: encodeTRPPBMinFineOffset }, - SPDIndexTRFCABMinLSB: { getVal: encodeTRFCABMinLsb }, - SPDIndexTRFCABMinMSB: { getVal: encodeTRFCABMinMsb }, - SPDIndexTRFCPBMinLSB: { getVal: encodeTRFCPBMinLsb }, - SPDIndexTRFCPBMinMSB: { getVal: encodeTRFCPBMinMsb }, -} - -type memParts struct { - MemParts []memPart `json:"parts"` -} - -type memPart struct { - Name string - Attribs memAttributes - SPDFileName string -} - -func writeSPDManifest(memParts *memParts, SPDDirName string) error { - var s string - - fmt.Printf("Generating SPD Manifest with following entries:\n") - - for i := 0; i < len(memParts.MemParts); i++ { - fmt.Printf("%-40s %s\n", memParts.MemParts[i].Name, memParts.MemParts[i].SPDFileName) - s += fmt.Sprintf("%s,%s\n", memParts.MemParts[i].Name, memParts.MemParts[i].SPDFileName) - } - - return ioutil.WriteFile(filepath.Join(SPDDirName, SPDManifestFileName), []byte(s), 0644) -} - -func isManufacturerPartNumberByte(index int) bool { - if index >= SPDIndexManufacturerPartNumberStartByte && index <= SPDIndexManufacturerPartNumberEndByte { - return true - } - return false -} - -func getSPDByte(index int, memAttribs *memAttributes) byte { - e, ok := SPDAttribTable[index] - if ok == false { - if isManufacturerPartNumberByte(index) { - return SPDValueManufacturerPartNumberBlank - } - return 0x00 - } - - if e.getVal != nil { - return e.getVal(memAttribs) - } - - return e.constVal -} - -func createSPD(memAttribs *memAttributes) string { - var s string - - for i := 0; i < 512; i++ { - b := getSPDByte(i, memAttribs) - - if (i + 1) % 16 == 0 { - s += fmt.Sprintf("%02X\n", b) - } else { - s += fmt.Sprintf("%02X ", b) - } - } - - return s -} - -func dedupeMemoryPart(dedupedParts []*memPart, memPart *memPart) bool { - for i := 0; i < len(dedupedParts); i++ { - if reflect.DeepEqual(dedupedParts[i].Attribs, memPart.Attribs) { - memPart.SPDFileName = dedupedParts[i].SPDFileName - return true - } - } - - return false -} - -func generateSPD(memPart *memPart, SPDId int, SPDDirName string) { - s := createSPD(&memPart.Attribs) - memPart.SPDFileName = fmt.Sprintf("spd-%d.hex", SPDId) - ioutil.WriteFile(filepath.Join(SPDDirName, memPart.SPDFileName), []byte(s), 0644) -} - -func readMemoryParts(memParts *memParts, memPartsFileName string) error { - databytes, err := ioutil.ReadFile(memPartsFileName) - if err != nil { - return err - } - - return json.Unmarshal(databytes, memParts) -} - -func validateDensityx8Channel(densityPerChannelGb int) error { - if _, ok := densityGbx8ChannelToRowColumnEncoding[densityPerChannelGb]; ok == false { - return fmt.Errorf("Incorrect x8 density: ", densityPerChannelGb, "Gb") - } - return nil -} - -func validateDensityx16Channel(densityPerChannelGb int) error { - if _, ok := densityGbx16ChannelToRowColumnEncoding[densityPerChannelGb]; ok == false { - return fmt.Errorf("Incorrect x16 density: ", densityPerChannelGb, "Gb") - } - return nil -} - -func validateDensity(memAttribs *memAttributes) error { - if memAttribs.BitWidthPerChannel == 8 { - return validateDensityx8Channel(memAttribs.DensityPerChannelGb) - } else if memAttribs.BitWidthPerChannel == 16 { - return validateDensityx16Channel(memAttribs.DensityPerChannelGb) - } - - return fmt.Errorf("No density table for this bit width: ", memAttribs.BitWidthPerChannel) -} - -func validateBanks(banks int) error { - if banks != 4 && banks != 8 { - return fmt.Errorf("Incorrect banks: ", banks) - } - return nil -} - -func validateChannels(channels int) error { - if channels != 1 && channels != 2 && channels != 4 { - return fmt.Errorf("Incorrect channels per die: ", channels) - } - return nil -} - -func validateDataWidth(width int) error { - if width != 8 && width != 16 { - return fmt.Errorf("Incorrect bit width: ", width) - } - return nil -} - -func validateRanks(ranks int) error { - if ranks != 1 && ranks != 2 { - return fmt.Errorf("Incorrect ranks: ", ranks) - } - return nil -} - -func validateSpeed(speed int) error { - if _, ok := speedMbpsToSPDEncoding[speed]; ok == false { - return fmt.Errorf("Incorrect speed: ", speed, " Mbps") - } - return nil -} - -func validateMemoryParts(memParts *memParts) error { - for i := 0; i < len(memParts.MemParts); i++ { - if err := validateBanks(memParts.MemParts[i].Attribs.Banks); err != nil { - return err - } - if err := validateChannels(memParts.MemParts[i].Attribs.ChannelsPerDie); err != nil { - return err - } - if err := validateDataWidth(memParts.MemParts[i].Attribs.BitWidthPerChannel); err != nil { - return err - } - if err := validateDensity(&memParts.MemParts[i].Attribs); err != nil { - return err - } - if err := validateRanks(memParts.MemParts[i].Attribs.RanksPerChannel); err != nil { - return err - } - if err := validateSpeed(memParts.MemParts[i].Attribs.SpeedMbps); err != nil { - return err - } - } - return nil -} - -func encodeLatencies(latency int, memAttribs *memAttributes) error { - switch latency { - case 6: - memAttribs.CASFirstByte |= CAS6 - case 10: - memAttribs.CASFirstByte |= CAS10 - case 14: - memAttribs.CASFirstByte |= CAS14 - case 16: - memAttribs.CASSecondByte |= CAS16 - case 20: - memAttribs.CASSecondByte |= CAS20 - case 22: - memAttribs.CASSecondByte |= CAS22 - case 24: - memAttribs.CASSecondByte |= CAS24 - case 26: - memAttribs.CASSecondByte |= CAS26 - case 28: - memAttribs.CASSecondByte |= CAS28 - case 32: - memAttribs.CASThirdByte |= CAS32 - case 36: - memAttribs.CASThirdByte |= CAS36 - case 40: - memAttribs.CASThirdByte |= CAS40 - default: - fmt.Errorf("Incorrect CAS Latency: ", latency) - } - - return nil -} - -func updateTCK(memAttribs *memAttributes) { - if memAttribs.TCKMinPs == 0 { - memAttribs.TCKMinPs = speedMbpsToSPDEncoding[memAttribs.SpeedMbps].TCKMinPs - } - if memAttribs.TCKMaxPs == 0 { - memAttribs.TCKMaxPs = speedMbpsToSPDEncoding[memAttribs.SpeedMbps].TCKMaxPs - } -} - -func getCASLatencies(memAttribs *memAttributes) string { - if memAttribs.BitWidthPerChannel == 16 { - return speedMbpsToSPDEncoding[memAttribs.SpeedMbps].CASLatenciesx16Channel - } else if memAttribs.BitWidthPerChannel == 8 { - return speedMbpsToSPDEncoding[memAttribs.SpeedMbps].CASLatenciesx8Channel - } - - return "" -} - -func updateCAS(memAttribs *memAttributes) error { - if len(memAttribs.CASLatencies) == 0 { - memAttribs.CASLatencies = getCASLatencies(memAttribs) - } - - latencies := strings.Fields(memAttribs.CASLatencies) - for i := 0; i < len(latencies); i++ { - latency,err := strconv.Atoi(latencies[i]) - if err != nil { - return fmt.Errorf("Unable to convert latency ", latencies[i]) - } - if err := encodeLatencies(latency, memAttribs); err != nil { - return err - } - } - return nil -} - -func getMinCAS(memAttribs *memAttributes) (int, error) { - if (memAttribs.CASThirdByte & CAS40) != 0 { - return 40, nil - } - if (memAttribs.CASThirdByte & CAS36) != 0 { - return 36, nil - } - if (memAttribs.CASThirdByte & CAS32) != 0 { - return 32, nil - } - if (memAttribs.CASSecondByte & CAS28) != 0 { - return 28, nil - } - - return 0, fmt.Errorf("Unexpected min CAS") -} - -func updateTAAMin(memAttribs *memAttributes) error { - if memAttribs.TAAMinPs == 0 { - minCAS, err := getMinCAS(memAttribs) - if err != nil { - return err - } - memAttribs.TAAMinPs = memAttribs.TCKMinPs * minCAS - } - - return nil -} - -func updateTRFCAB(memAttribs *memAttributes) { - if memAttribs.TRFCABNs == 0 { - memAttribs.TRFCABNs = densityGbPhysicalChannelToRefreshEncoding[memAttribs.DensityPerChannelGb].TRFCABNs - } -} - -func updateTRFCPB(memAttribs *memAttributes) { - if memAttribs.TRFCPBNs == 0 { - memAttribs.TRFCPBNs = densityGbPhysicalChannelToRefreshEncoding[memAttribs.DensityPerChannelGb].TRFCPBNs - } -} - -func updateTRCD(memAttribs *memAttributes) { - if memAttribs.TRCDMinNs == 0 { - /* JEDEC spec says max of 18ns */ - memAttribs.TRCDMinNs = 18 - } -} - -func updateTRPAB(memAttribs *memAttributes) { - if memAttribs.TRPABMinNs == 0 { - /* JEDEC spec says max of 21ns */ - memAttribs.TRPABMinNs = 21 - } -} - -func updateTRPPB(memAttribs *memAttributes) { - if memAttribs.TRPPBMinNs == 0 { - /* JEDEC spec says max of 18ns */ - memAttribs.TRPPBMinNs = 18 - } -} - -func normalizeMemoryAttributes(memAttribs *memAttributes) { - if currPlatform == PlatformTGL { - /* - * TGL does not really use physical organization of dies per package when - * generating the SPD. So, set it to 0 here so that deduplication ignores - * that field. - */ - memAttribs.DiesPerPackage = 0 - } -} - -func updateMemoryAttributes(memAttribs *memAttributes) error { - updateTCK(memAttribs) - if err := updateCAS(memAttribs); err != nil { - return err - } - if err := updateTAAMin(memAttribs); err != nil { - return err - } - updateTRFCAB(memAttribs) - updateTRFCPB(memAttribs) - updateTRCD(memAttribs) - updateTRPAB(memAttribs) - updateTRPPB(memAttribs) - - normalizeMemoryAttributes(memAttribs) - - return nil -} - -func isPlatformSupported(platform string) error { - var ok bool - - currPlatform, ok = platformMap[platform] - if ok == false { - return fmt.Errorf("Unsupported platform: ", platform) - } - - return nil -} - -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(" mem_parts_list_json = JSON File containing list of memory parts and attributes\n") - fmt.Printf(" platform = SoC Platform for which the SPDs are being generated\n\n\n") -} - -func main() { - if len(os.Args) != 4 { - usage() - log.Fatal("Incorrect number of arguments") - } - - var memParts memParts - var dedupedParts []*memPart - - SPDDir, GlobalMemPartsFile, Platform := os.Args[1], os.Args[2], strings.ToUpper(os.Args[3]) - - if err := isPlatformSupported(Platform); err != nil { - log.Fatal(err) - } - - if err := readMemoryParts(&memParts, GlobalMemPartsFile); err != nil { - log.Fatal(err) - } - - if err := validateMemoryParts(&memParts); err != nil { - log.Fatal(err) - } - - SPDId := 1 - - for i := 0; i < len(memParts.MemParts); i++ { - if err := updateMemoryAttributes(&memParts.MemParts[i].Attribs); err != nil { - log.Fatal(err) - } - - if dedupeMemoryPart(dedupedParts, &memParts.MemParts[i]) == false { - generateSPD(&memParts.MemParts[i], SPDId, SPDDir) - SPDId++ - dedupedParts = append(dedupedParts, &memParts.MemParts[i]) - } - } - - if err := writeSPDManifest(&memParts, SPDDir); err != nil { - log.Fatal(err) - } -} diff --git a/util/spd_tools/intel/lp4x/global_lp4x_mem_parts.json.txt b/util/spd_tools/intel/lp4x/global_lp4x_mem_parts.json.txt deleted file mode 100644 index 109fadb916..0000000000 --- a/util/spd_tools/intel/lp4x/global_lp4x_mem_parts.json.txt +++ /dev/null @@ -1,186 +0,0 @@ -{ - "parts": [ - { - "name": "H9HCNNNBKMMLXR-NEE", - "attribs": { - "densityPerChannelGb": 8, - "banks": 8, - "channelsPerDie": 2, - "diesPerPackage": 1, - "bitWidthPerChannel": 16, - "ranksPerChannel": 1, - "speedMbps": 4267 - } - }, - { - "name": "H9HCNNNFAMMLXR-NEE", - "attribs": { - "densityPerChannelGb": 8, - "banks": 8, - "channelsPerDie": 4, - "diesPerPackage": 2, - "bitWidthPerChannel": 8, - "ranksPerChannel": 2, - "speedMbps": 4267 - } - }, - { - "name": "K4U6E3S4AA-MGCL", - "attribs": { - "densityPerChannelGb": 8, - "banks": 8, - "channelsPerDie": 2, - "diesPerPackage": 1, - "bitWidthPerChannel": 16, - "ranksPerChannel": 1, - "speedMbps": 4267 - } - }, - { - "name": "K4UBE3D4AA-MGCL", - "attribs": { - "densityPerChannelGb": 8, - "banks": 8, - "channelsPerDie": 2, - "diesPerPackage": 2, - "bitWidthPerChannel": 16, - "ranksPerChannel": 2, - "speedMbps": 4267 - } - }, - { - "name": "MT53E1G32D2NP-046 WT:A", - "attribs": { - "densityPerChannelGb": 16, - "banks": 8, - "channelsPerDie": 2, - "diesPerPackage": 1, - "bitWidthPerChannel": 16, - "ranksPerChannel": 1, - "speedMbps": 4267 - } - }, - { - "name": "H9HKNNNCRMBVAR-NEH", - "attribs": { - "densityPerChannelGb": 8, - "banks": 8, - "channelsPerDie": 2, - "diesPerPackage": 2, - "bitWidthPerChannel": 16, - "ranksPerChannel": 1, - "speedMbps": 4267 - } - }, - { - "name": "MT53E1G64D4SQ-046 WT:A", - "attribs": { - "densityPerChannelGb": 16, - "banks": 8, - "channelsPerDie": 2, - "diesPerPackage": 2, - "bitWidthPerChannel": 16, - "ranksPerChannel": 1, - "speedMbps": 4267 - } - }, - { - "name": "MT53E512M32D2NP-046 WT:F", - "attribs": { - "densityPerChannelGb": 8, - "banks": 8, - "channelsPerDie": 2, - "diesPerPackage": 1, - "bitWidthPerChannel": 16, - "ranksPerChannel": 1, - "speedMbps": 4267 - } - }, - { - "name": "NT6AP256T32AV-J2", - "attribs": { - "densityPerChannelGb": 4, - "banks": 8, - "channelsPerDie": 2, - "diesPerPackage": 1, - "bitWidthPerChannel": 16, - "ranksPerChannel": 1, - "speedMbps": 3733, - "tckMaxPs": 1250, - "casLatencies": "14 20 24 28 32" - } - }, - { - "name": "K4U6E3S4AA-MGCR", - "attribs": { - "densityPerChannelGb": 8, - "banks": 8, - "channelsPerDie": 2, - "diesPerPackage": 1, - "bitWidthPerChannel": 16, - "ranksPerChannel": 1, - "speedMbps": 4267 - } - }, - { - "name": "MT53E512M32D2NP-046 WT:E", - "attribs": { - "densityPerChannelGb": 8, - "banks": 8, - "channelsPerDie": 2, - "diesPerPackage": 1, - "bitWidthPerChannel": 16, - "ranksPerChannel": 1, - "speedMbps": 4267 - } - }, - { - "name": "H9HCNNNCPMMLXR-NEE", - "attribs": { - "densityPerChannelGb": 8, - "banks": 8, - "channelsPerDie": 2, - "diesPerPackage": 2, - "bitWidthPerChannel": 16, - "ranksPerChannel": 2, - "speedMbps": 4267 - } - }, - { - "name": "K4UBE3D4AA-MGCR", - "attribs": { - "densityPerChannelGb": 8, - "banks": 8, - "channelsPerDie": 2, - "diesPerPackage": 2, - "bitWidthPerChannel": 16, - "ranksPerChannel": 2, - "speedMbps": 4267 - } - }, - { - "name": "MT53E512M64D4NW-046 WT:E", - "attribs": { - "densityPerChannelGb": 8, - "banks": 8, - "channelsPerDie": 2, - "diesPerPackage": 2, - "bitWidthPerChannel": 16, - "ranksPerChannel": 1, - "speedMbps": 4267 - } - }, - { - "name": "MT53E1G64D8NW-046 WT:E", - "attribs": { - "densityPerChannelGb": 8, - "banks": 8, - "channelsPerDie": 2, - "diesPerPackage": 4, - "bitWidthPerChannel": 16, - "ranksPerChannel": 2, - "speedMbps": 4267 - } - } - ] -} diff --git a/util/spd_tools/lp4x/README.md b/util/spd_tools/lp4x/README.md new file mode 100644 index 0000000000..e614f259cf --- /dev/null +++ b/util/spd_tools/lp4x/README.md @@ -0,0 +1,265 @@ +# LPDDR4x SPD tools README + +Tools for generating SPD files for LPDDR4x memory used in memory down +configurations on Intel Tiger Lake (TGL) and Jasper Lake (JSL) based +platforms. These tools generate SPDs following JESD209-4C +specification and Intel recommendations (doc #616599, #610202) for +LPDDR4x SPD. + +There are two tools provided that assist TGL and JSL based mainboards +to generate SPDs and Makefile to integrate these SPDs in coreboot +build. These tools can also be used to allocate DRAM IDs (configure +DRAM hardware straps) for any LPDDR4x memory part used by the board. + +* 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 LPDDR4x + 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. + +## Tool 1 - gen_spd.go + +This program takes as input: +* Pointer to directory where the generated SPD files and manifest will + be placed. +* JSON file containing a global list of memory parts with their + attributes as per the datasheet. This is the list of all known + LPDDR4x memory parts irrespective of their usage on the board. +* SoC platform name for which the SPDs are being generated. Currently + supported platform names are `TGL` and `JSL`. + +Input JSON file requires the following two fields for every memory part: +* `name`: Name of the memory part +* `attribs`: List of attributes of the memory part as per its + datasheet. These attributes match the part specifications and are + independent of any SoC expectations. Tool takes care of translating + the physical attributes of the memory part to match JEDEC and Intel + MRC expectations. + +`attribs` field further contains two types of sub-fields: +* Mandatory: These attributes have to be provided for a memory part. +* Optional: These attributes can be provided by memory part if it wants + to override the defaults. + +### Mandatory `attribs` + +* `densityPerChannelGb`: Density in Gb of the physical channel. + +* `banks`: Number of banks per physical channel. This is typically 8 + for LPDDR4x memory parts. + +* `channelsPerDie`: Number of physical channels per die. Valid values: + `1, 2, 4`. For a part with x16 bit width, number of channels per die + is 1 or 2. For a part with x8 bit width, number of channels can be + 2 or 4 (4 is basically when two dual-channel byte mode devices are + combined as shown in Figure 3 in JESD209-4C). + +* `diesPerPackage`: Number of physical dies in each SDRAM + package. As per JESD209-4C, "Standard LPDDR4 package ballmaps + allocate one ZQ ball per die." Thus, number of diesPerPackage is the + number of ZQ balls on the package. + +* `bitWidthPerChannel`: Width of each physical channel. Valid values: + `8, 16` bits. + +* `ranksPerChannel`: Number of ranks per physical channel. Valid + values: `1, 2`. If the channels across multiple dies share the same + DQ/DQS pins but use a separate CS, then ranks is 2 else it is 1. + +* `speedMbps`: Maximum data rate supported by the part in Mbps. Valid + values: `3200, 3733, 4267` Mbps. + +### Optional `attribs` + +* `trfcabNs`: Minimum Refresh Recovery Delay Time (tRFCab) for all + banks in nanoseconds. As per JESD209-4C, this is dependent on the + density per channel. Default values used: + * 6Gb : 280ns + * 8Gb : 280ns + * 12Gb: 380ns + * 16Gb: 380ns + +* `trfcpbNs`: Minimum Refresh Recovery Delay Time (tRFCab) per + bank in nanoseconds. As per JESD209-4C, this is dependent on the + density per channel. Default values used: + * 6Gb : 140ns + * 8Gb : 140ns + * 12Gb: 190ns + * 16Gb: 190ns + +* `trpabMinNs`: Minimum Row Precharge Delay Time (tRPab) for all banks + in nanoseconds. As per JESD209-4C, this is max(21ns, 4nck) which + defaults to `21ns`. + +* `trppbMinNs`: Minimum Row Precharge Delay Time (tRPpb) per bank in + nanoseconds. As per JESD209-4C, this is max(18ns, 4nck) which + defaults to `18ns`. + +* `tckMinPs`: SDRAM minimum cycle time (tckMin) value in + picoseconds. This is typically calculated based on the `speedMbps` + attribute. `(1 / speedMbps) * 2`. Default values used(taken from + JESD209-4C): + * 4267 Mbps: 468ps + * 3733 Mbps: 535ps + * 3200 Mbps: 625ps + +* `tckMaxPs`: SDRAM maximum cycle time (tckMax) value in + picoseconds. Default value used: `31875ps`. As per JESD209-4C, + TCKmax should be 100ns (100000ps) for all speed grades. But the SPD + byte to encode this field is only 1 byte. Hence, the maximum value + that can be encoded is 31875ps. + +* `taaMinPs`: Minimum CAS Latency Time(taaMin) in picoseconds. This + value defaults to nck * tckMin, where nck is minimum CAS latency. + +* `trcdMinNs`: Minimum RAS# to CAS# Delay Time (tRCDmin) in + nanoseconds. As per JESD209-4C, this is max(18ns, 4nck) which + defaults to `18ns`. + +* `casLatencies`: List of CAS latencies supported by the + part. This is dependent on the attrib `speedMbps`. Default values + used: + * 4267: `"6 10 14 20 24 28 32 36"`. + * 3733: `"6 10 14 20 24 28 32"`. + * 3200: `"6 10 14 20 24 28"`. + +### Example JSON file +``` +{ + "parts": [ + { + "name": "MEMORY_PART_A", + "attribs": { + "densityPerChannelGb": 8, + "banks": 8, + "channelsPerDie": 2, + "diesPerPackage": 2, + "bitWidthPerChannel": 16, + "ranksPerChannel": 1, + "speedMbps": 4267 + } + }, + { + "name": "MEMORY_PART_B", + "attribs": { + "densityPerChannelGb": 8, + "banks": 8, + "channelsPerDie": 1, + "diesPerPackage": 2, + "bitWidthPerChannel": 16, + "ranksPerChannel": 1, + "speedMbps": 3733, + "casLatencies": "14 20 24 28 32", + "tckMaxPs": "1250" + } + } + ] +} +``` + +### Output + +This tool generates the following files using the global list of +memory parts in JSON format as described above: + * De-duplicated SPDs required for the different memory parts. These + SPD files are named (spd_1.hex, spd_2.hex, spd_3.hex and so on) + and placed in the directory provided as an input to the tool. + * CSV file representing which of the deduplicated SPD files is used + by which memory part. This file is named as + `spd_manifest.generated.txt` and placed in the directory provided + as an input to the tool along with the generated SPD + files. Example CSV file: + ``` + MEMORY_PART_A, spd_1.hex + MEMORY_PART_B, spd_2.hex + MEMORY_PART_C, spd_3.hex + MEMORY_PART_D, spd_2.hex + MEMORY_PART_E, spd_2.hex + ``` + +## Tool 2 - gen_part_id.go + +This program takes as input: +* Pointer to directory where the SPD files and the manifest file + `spd_manifest.generated.txt` (in CSV format) are placed by + gen_spd.go +* File containing list of memory parts used by the board. Each line of + the file is supposed to contain one memory part `name` as present in + the global list of memory parts provided to gen_spd.go +* Pointer to directory where the generated Makefile.inc should be + placed by the tool. + +### Output + +This program provides the following: + +* Prints out the list of DRAM hardware strap IDs that should be + allocated to each memory part listed in the input file. +* Makefile.inc is generated in the provided directory to integrate + SPDs generated by gen_spd.go with the coreboot build for the board. +* dram_id.generated.txt is generated in the same directory as + Makefile. This contains the part IDs assigned to the different + memory parts. (Useful to integrate in board schematics). + +Sample output (dram_id.generated.txt): +``` +DRAM Part Name ID to assign +MEMORY_PART_A 0 (0000) +MEMORY_PART_B 1 (0001) +MEMORY_PART_C 2 (0010) +MEMORY_PART_D 1 (0001) +``` + +Sample Makefile.inc: +``` +## SPDX-License-Identifier: GPL-2.0-or-later +## This is an auto-generated file. Do not edit!! + +SPD_SOURCES = +SPD_SOURCES += spd_1.hex # ID = 0(0b0000) Parts = MEMORY_PART_A +SPD_SOURCES += spd_2.hex # ID = 1(0b0001) Parts = MEMORY_PART_B, MEMORY_PART_D +SPD_SOURCES += spd_3.hex # ID = 2(0b0010) Parts = MEMORY_PART_C +``` + +### Note of caution + +This program assigns DRAM IDs using the order of DRAM part names +provided in the input file. Thus, when adding a new memory part to the +list, it should always go to the end of the input text file. This +guarantees that the memory parts that were already assigned IDs do not +change. + +## How to build the tools? +``` +# go build gen_spd.go +# go build gen_part_id.go +``` + +## How to use the tools? +``` +# ./gen_spd +# ./gen_part_id +``` + +### Need to add a new memory part for a board? + +* If the memory part is not present in the global list of memory + parts, then add the memory part name and attributes as per the + datasheet to the file containing the global list. + * Use `gen_spd.go` with input as the file containing the global list + of memory parts to generate de-duplicated SPDs. + * If a new SPD file is generated, use `git add` to add it to the + tree and push a CL for review. +* Update the file containing memory parts used by board (variant) to + add the new memory part name at the end of the file. + * Use gen_part_id.go providing it pointer to the location where SPD + files are stored and file containing the list of memory parts used + by the board(variant). + * Use `git add` to add `Makefile.inc` and `dram_id.generated.txt` + with updated changes and push a CL for review. diff --git a/util/spd_tools/lp4x/gen_part_id.go b/util/spd_tools/lp4x/gen_part_id.go new file mode 100644 index 0000000000..7ed255c83b --- /dev/null +++ b/util/spd_tools/lp4x/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 \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 = lp4x\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) + } +} diff --git a/util/spd_tools/lp4x/gen_spd.go b/util/spd_tools/lp4x/gen_spd.go new file mode 100644 index 0000000000..2465815e49 --- /dev/null +++ b/util/spd_tools/lp4x/gen_spd.go @@ -0,0 +1,986 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "reflect" + "strconv" + "strings" +) + +/* + * This program generates de-duplicated SPD files for LPDDR4x memory using the global memory + * part list provided in CSV format. In addition to that, it also generates SPD manifest in CSV + * format that contains entries of type (DRAM part name, SPD file name) which provides the SPD + * file name used by a given DRAM part. + * + * It takes as input: + * Pointer to directory where the generated SPD files will be placed. + * JSON file containing a list of memory parts with their attributes as per datasheet. + */ +const ( + SPDManifestFileName = "spd_manifest.generated.txt" + + PlatformTGL = 0 + PlatformJSL = 1 +) + +var platformMap = map[string]int { + "TGL": PlatformTGL, + "JSL": PlatformJSL, +} + +var currPlatform int + +type memAttributes struct { + /* Primary attributes - must be provided by JSON file for each part */ + DensityPerChannelGb int + Banks int + ChannelsPerDie int + DiesPerPackage int + BitWidthPerChannel int + RanksPerChannel int + SpeedMbps int + + /* + * All the following parameters are optional and required only if the part requires + * special parameters as per the datasheet. + */ + /* Timing parameters */ + TRFCABNs int + TRFCPBNs int + TRPABMinNs int + TRPPBMinNs int + TCKMinPs int + TCKMaxPs int + TAAMinPs int + TRCDMinNs int + + /* CAS */ + CASLatencies string + CASFirstByte byte + CASSecondByte byte + CASThirdByte byte +} + +/* This encodes the density in Gb to SPD values as per JESD 21-C */ +var densityGbToSPDEncoding = map[int]byte { + 4: 0x4, + 6: 0xb, + 8: 0x5, + 12: 0x8, + 16: 0x6, + 24: 0x9, + 32: 0x7, +} + +/* + * Table 3 from JESD209-4C. + * Maps density per physical channel to row-column encoding as per JESD 21-C for a device with + * x16 physical channel. + */ +var densityGbx16ChannelToRowColumnEncoding = map[int]byte { + 4: 0x19, /* 15 rows, 10 columns */ + 6: 0x21, /* 16 rows, 10 columns */ + 8: 0x21, /* 16 rows, 10 columns */ + 12: 0x29, /* 17 rows, 10 columns */ + 16: 0x29, /* 17 rows, 10 columns */ +} + +/* + * Table 5 from JESD209-4C. + * Maps density per physical channel to row-column encoding as per JESD 21-C for a device with + * x8 physical channel. + */ +var densityGbx8ChannelToRowColumnEncoding = map[int]byte { + 3: 0x21, /* 16 rows, 10 columns */ + 4: 0x21, /* 16 rows, 10 columns */ + 6: 0x29, /* 17 rows, 10 columns */ + 8: 0x29, /* 17 rows, 10 columns */ + 12: 0x31, /* 18 rows, 10 columns */ + 16: 0x31, /* 18 rows, 10 columns */ +} + +type refreshTimings struct { + TRFCABNs int + TRFCPBNs int +} + +/* + * Table 112 from JESD209-4C + * Maps density per physical channel to refresh timings. This is the same for x8 and x16 + * devices. + */ +var densityGbPhysicalChannelToRefreshEncoding = map[int]refreshTimings { + 3: { + TRFCABNs: 180, + TRFCPBNs: 90, + }, + 4: { + TRFCABNs: 180, + TRFCPBNs: 90, + }, + 6: { + TRFCABNs: 280, + TRFCPBNs: 140, + }, + 8: { + TRFCABNs: 280, + TRFCPBNs: 140, + }, + 12: { + TRFCABNs: 380, + TRFCPBNs: 190, + }, + 16: { + TRFCABNs: 380, + TRFCPBNs: 190, + }, +} + +type speedParams struct { + TCKMinPs int + TCKMaxPs int + CASLatenciesx16Channel string + CASLatenciesx8Channel string +} + +const ( + /* First Byte */ + CAS6 = 1 << 1 + CAS10 = 1 << 4 + CAS14 = 1 << 7 + /* Second Byte */ + CAS16 = 1 << 0 + CAS20 = 1 << 2 + CAS22 = 1 << 3 + CAS24 = 1 << 4 + CAS26 = 1 << 5 + CAS28 = 1 << 6 + /* Third Byte */ + CAS32 = 1 << 0 + CAS36 = 1 << 2 + CAS40 = 1 << 4 +) + +const ( + /* + * JEDEC spec says that TCKmax should be 100ns for all speed grades. + * 100ns in MTB units comes out to be 0x320. But since this is a byte field, set it to + * 0xFF i.e. 31.875ns. + */ + TCKMaxPsDefault = 31875 +) + +var speedMbpsToSPDEncoding = map[int]speedParams { + 4267: { + TCKMinPs: 468, /* 1/4267 * 2 */ + TCKMaxPs: TCKMaxPsDefault, + CASLatenciesx16Channel: "6 10 14 20 24 28 32 36", + CASLatenciesx8Channel: "6 10 16 22 26 32 36 40", + }, + 3733: { + TCKMinPs: 535, /* 1/3733 * 2 */ + TCKMaxPs: TCKMaxPsDefault, + CASLatenciesx16Channel: "6 10 14 20 24 28 32", + CASLatenciesx8Channel: "6 10 16 22 26 32 36", + }, + 3200: { + TCKMinPs: 625, /* 1/3200 * 2 */ + TCKMaxPs: TCKMaxPsDefault, + CASLatenciesx16Channel: "6 10 14 20 24 28", + CASLatenciesx8Channel: "6 10 16 22 26 32", + }, +} + +var bankEncoding = map[int]byte { + 4: 0 << 4, + 8: 1 << 4, +} + +const ( + TGLLogicalChannelWidth = 16 +) + +/* Returns density to encode as per Intel MRC expectations. */ +func getMRCDensity(memAttribs *memAttributes) int { + if currPlatform == PlatformTGL { + /* + * Intel MRC on TGL expects density per logical channel to be encoded in + * SPDIndexDensityBanks. Logical channel on TGL is an x16 channel. + */ + return memAttribs.DensityPerChannelGb * TGLLogicalChannelWidth / memAttribs.BitWidthPerChannel + } else if currPlatform == PlatformJSL { + /* + * Intel MRC on JSL expects density per die to be encoded in + * SPDIndexDensityBanks. + */ + return memAttribs.DensityPerChannelGb * memAttribs.ChannelsPerDie + } + + return 0 +} + +func encodeDensityBanks(memAttribs *memAttributes) byte { + var b byte + + b = densityGbToSPDEncoding[getMRCDensity(memAttribs)] + b |= bankEncoding[memAttribs.Banks] + + return b +} + +func encodeSdramAddressing(memAttribs *memAttributes) byte { + densityPerChannelGb := memAttribs.DensityPerChannelGb + if memAttribs.BitWidthPerChannel == 8 { + return densityGbx8ChannelToRowColumnEncoding[densityPerChannelGb] + } else { + return densityGbx16ChannelToRowColumnEncoding[densityPerChannelGb] + } + return 0 +} + +func encodeChannelsPerDie(channels int) byte { + var temp byte + + temp = byte(channels >> 1) + + return temp << 2 +} + +func encodePackage(dies int) byte { + var temp byte + + if dies > 1 { + /* If more than one die, then this is a non-monolithic device. */ + temp = 1 + } else { + /* If only single die, then this is a monolithic device. */ + temp = 0 + } + + return temp << 7 +} + +func encodeDiesPerPackage(memAttribs *memAttributes) byte { + var dies int = 0 + if currPlatform == PlatformTGL { + /* Intel MRC expects logical dies to be encoded for TGL. */ + dies = memAttribs.ChannelsPerDie * memAttribs.RanksPerChannel * memAttribs.BitWidthPerChannel / 16 + } else if currPlatform == PlatformJSL { + /* Intel MRC expects physical dies to be encoded for JSL. */ + dies = memAttribs.DiesPerPackage + } + + b := encodePackage(dies) /* Monolithic / Non-monolithic device */ + b |= (byte(dies) - 1) << 4 + + return b +} + +func encodePackageType(memAttribs *memAttributes) byte { + var b byte + + b |= encodeChannelsPerDie(memAttribs.ChannelsPerDie) + b |= encodeDiesPerPackage(memAttribs) + + return b +} + +func encodeDataWidth(bitWidthPerChannel int) byte { + return byte(bitWidthPerChannel / 8) +} + +func encodeRanks(ranks int) byte { + var b byte + b = byte(ranks - 1) + return b << 3 +} + +func encodeModuleOrganization(memAttribs *memAttributes) byte { + var b byte + + b = encodeDataWidth(memAttribs.BitWidthPerChannel) + b |= encodeRanks(memAttribs.RanksPerChannel) + + return b +} + +const ( + /* + * As per advisory 616599: + * 7:5 (Number of system channels) = 000 (1 channel always) + * 2:0 (Bus width) = 001 (x16 always) + * Set to 0x01. + */ + SPDValueBusWidthTGL = 0x01 + /* + * As per advisory 610202: + * 7:5 (Number of system channels) = 001 (2 channel always) + * 2:0 (Bus width) = 010 (x32 always) + * Set to 0x01. + */ + SPDValueBusWidthJSL = 0x22 +) + +func encodeBusWidth(memAttribs *memAttributes) byte { + if currPlatform == PlatformTGL { + return SPDValueBusWidthTGL + } else if currPlatform == PlatformJSL { + return SPDValueBusWidthJSL + } + return 0 +} + +func encodeTCKMin(memAttribs *memAttributes) byte { + return convPsToMtbByte(memAttribs.TCKMinPs) +} + +func encodeTCKMinFineOffset(memAttribs *memAttributes) byte { + return convPsToFtbByte(memAttribs.TCKMinPs) +} + +func encodeTCKMax(memAttribs *memAttributes) byte { + return convPsToMtbByte(memAttribs.TCKMaxPs) +} + +func encodeTCKMaxFineOffset(memAttribs *memAttributes) byte { + return convPsToFtbByte(memAttribs.TCKMaxPs) +} + +func encodeCASFirstByte(memAttribs *memAttributes) byte { + return memAttribs.CASFirstByte +} + +func encodeCASSecondByte(memAttribs *memAttributes) byte { + return memAttribs.CASSecondByte +} + +func encodeCASThirdByte(memAttribs *memAttributes) byte { + return memAttribs.CASThirdByte +} + +func divRoundUp(dividend int, divisor int) int { + return (dividend + divisor - 1) / divisor +} + +func convNsToPs(timeNs int) int { + return timeNs * 1000 +} + +func convMtbToPs(mtb int) int { + return mtb * 125 +} + +func convPsToMtb(timePs int) int { + return divRoundUp(timePs, 125) +} + +func convPsToMtbByte(timePs int) byte { + return byte(convPsToMtb(timePs) & 0xff) +} + +func convPsToFtbByte(timePs int) byte { + mtb := convPsToMtb(timePs) + ftb := timePs - convMtbToPs(mtb) + + return byte(ftb) +} + +func convNsToMtb(timeNs int) int { + return convPsToMtb(convNsToPs(timeNs)) +} + +func convNsToMtbByte(timeNs int) byte { + return convPsToMtbByte(convNsToPs(timeNs)) +} + +func convNsToFtbByte(timeNs int) byte { + return convPsToFtbByte(convNsToPs(timeNs)) +} + +func encodeTAAMin(memAttribs *memAttributes) byte { + return convPsToMtbByte(memAttribs.TAAMinPs) +} + +func encodeTAAMinFineOffset(memAttribs *memAttributes) byte { + return convPsToFtbByte(memAttribs.TAAMinPs) +} + +func encodeTRCDMin(memAttribs *memAttributes) byte { + return convNsToMtbByte(memAttribs.TRCDMinNs) +} + +func encodeTRCDMinFineOffset(memAttribs *memAttributes) byte { + return convNsToFtbByte(memAttribs.TRCDMinNs) +} + +func encodeTRPABMin(memAttribs *memAttributes) byte { + return convNsToMtbByte(memAttribs.TRPABMinNs) +} + +func encodeTRPABMinFineOffset(memAttribs *memAttributes) byte { + return convNsToFtbByte(memAttribs.TRPABMinNs) +} + +func encodeTRPPBMin(memAttribs *memAttributes) byte { + return convNsToMtbByte(memAttribs.TRPPBMinNs) +} + +func encodeTRPPBMinFineOffset(memAttribs *memAttributes) byte { + return convNsToFtbByte(memAttribs.TRPPBMinNs) +} + +func encodeTRFCABMinMsb(memAttribs *memAttributes) byte { + return byte((convNsToMtb(memAttribs.TRFCABNs) >> 8) & 0xff) +} + +func encodeTRFCABMinLsb(memAttribs *memAttributes) byte { + return byte(convNsToMtb(memAttribs.TRFCABNs) & 0xff) +} + +func encodeTRFCPBMinMsb(memAttribs *memAttributes) byte { + return byte((convNsToMtb(memAttribs.TRFCPBNs) >> 8) & 0xff) +} + +func encodeTRFCPBMinLsb(memAttribs *memAttributes) byte { + return byte(convNsToMtb(memAttribs.TRFCPBNs) & 0xff) +} + +type SPDAttribFunc func (*memAttributes) byte + +type SPDAttribTableEntry struct { + constVal byte + getVal SPDAttribFunc +} + +const ( + /* SPD Byte Index */ + SPDIndexSize = 0 + SPDIndexRevision = 1 + SPDIndexMemoryType = 2 + SPDIndexModuleType = 3 + SPDIndexDensityBanks = 4 + SPDIndexAddressing = 5 + SPDIndexPackageType = 6 + SPDIndexOptionalFeatures = 7 + SPDIndexModuleOrganization = 12 + SPDIndexBusWidth = 13 + SPDIndexTimebases = 17 + SPDIndexTCKMin = 18 + SPDIndexTCKMax = 19 + SPDIndexCASFirstByte = 20 + SPDIndexCASSecondByte = 21 + SPDIndexCASThirdByte = 22 + SPDIndexCASFourthByte = 23 + SPDIndexTAAMin = 24 + SPDIndexReadWriteLatency = 25 + SPDIndexTRCDMin = 26 + SPDIndexTRPABMin = 27 + SPDIndexTRPPBMin = 28 + SPDIndexTRFCABMinLSB = 29 + SPDIndexTRFCABMinMSB = 30 + SPDIndexTRFCPBMinLSB = 31 + SPDIndexTRFCPBMinMSB = 32 + SPDIndexTRPPBMinFineOffset = 120 + SPDIndexTRPABMinFineOffset = 121 + SPDIndexTRCDMinFineOffset = 122 + SPDIndexTAAMinFineOffset = 123 + SPDIndexTCKMaxFineOffset = 124 + SPDIndexTCKMinFineOffset = 125 + SPDIndexManufacturerPartNumberStartByte = 329 + SPDIndexManufacturerPartNumberEndByte = 348 + + /* SPD Byte Value */ + + /* + * From JEDEC spec: + * 6:4 (Bytes total) = 2 (512 bytes) + * 3:0 (Bytes used) = 3 (384 bytes) + * Set to 0x23 for LPDDR4x. + */ + SPDValueSize = 0x23 + + /* + * From JEDEC spec: Revision 1.1 + * Set to 0x11. + */ + SPDValueRevision = 0x11 + + /* LPDDR4x memory type = 0x11 */ + SPDValueMemoryType = 0x11 + + /* + * From JEDEC spec: + * 7:7 (Hybrid) = 0 (Not hybrid) + * 6:4 (Hybrid media) = 000 (Not hybrid) + * 3:0 (Base Module Type) = 1110 (Non-DIMM solution) + * + * This is dependent on hardware design. LPDDR4x only has memory down solution. + * Hence this is not hybrid non-DIMM solution. + * Set to 0x0E. + */ + SPDValueModuleType = 0x0e + + /* + * From JEDEC spec: + * 5:4 (Maximum Activate Window) = 00 (8192 * tREFI) + * 3:0 (Maximum Activate Count) = 1000 (Unlimited MAC) + * + * Needs to come from datasheet, but most parts seem to support unlimited MAC. + * MR#24 OP3 + */ + SPDValueOptionalFeatures = 0x08 + + /* + * From JEDEC spec: + * 3:2 (MTB) = 00 (0.125ns) + * 1:0 (FTB) = 00 (1ps) + * Set to 0x00. + */ + SPDValueTimebases = 0x00 + + /* CAS fourth byte: All bits are reserved */ + SPDValueCASFourthByte = 0x00 + + /* Write Latency Set A and Read Latency DBI-RD disabled. */ + SPDValueReadWriteLatency = 0x00 + + /* As per JEDEC spec, unused digits of manufacturer part number are left as blank. */ + SPDValueManufacturerPartNumberBlank = 0x20 +) + +var SPDAttribTable = map[int]SPDAttribTableEntry { + SPDIndexSize: { constVal: SPDValueSize }, + SPDIndexRevision: { constVal: SPDValueRevision }, + SPDIndexMemoryType: { constVal: SPDValueMemoryType }, + SPDIndexModuleType: { constVal: SPDValueModuleType }, + SPDIndexDensityBanks: { getVal: encodeDensityBanks }, + SPDIndexAddressing: { getVal: encodeSdramAddressing }, + SPDIndexPackageType: { getVal: encodePackageType }, + SPDIndexOptionalFeatures: { constVal: SPDValueOptionalFeatures }, + SPDIndexModuleOrganization: { getVal: encodeModuleOrganization }, + SPDIndexBusWidth: { getVal: encodeBusWidth }, + SPDIndexTimebases: { constVal: SPDValueTimebases }, + SPDIndexTCKMin: { getVal: encodeTCKMin }, + SPDIndexTCKMax: { getVal: encodeTCKMax }, + SPDIndexTCKMaxFineOffset: { getVal: encodeTCKMaxFineOffset }, + SPDIndexTCKMinFineOffset: { getVal: encodeTCKMinFineOffset }, + SPDIndexCASFirstByte: { getVal: encodeCASFirstByte }, + SPDIndexCASSecondByte: { getVal: encodeCASSecondByte }, + SPDIndexCASThirdByte: { getVal: encodeCASThirdByte }, + SPDIndexCASFourthByte: { constVal: SPDValueCASFourthByte }, + SPDIndexTAAMin: { getVal: encodeTAAMin }, + SPDIndexTAAMinFineOffset: { getVal: encodeTAAMinFineOffset }, + SPDIndexReadWriteLatency: { constVal: SPDValueReadWriteLatency }, + SPDIndexTRCDMin: { getVal: encodeTRCDMin }, + SPDIndexTRCDMinFineOffset: { getVal: encodeTRCDMinFineOffset }, + SPDIndexTRPABMin: { getVal: encodeTRPABMin }, + SPDIndexTRPABMinFineOffset: { getVal: encodeTRPABMinFineOffset }, + SPDIndexTRPPBMin: { getVal: encodeTRPPBMin }, + SPDIndexTRPPBMinFineOffset: { getVal: encodeTRPPBMinFineOffset }, + SPDIndexTRFCABMinLSB: { getVal: encodeTRFCABMinLsb }, + SPDIndexTRFCABMinMSB: { getVal: encodeTRFCABMinMsb }, + SPDIndexTRFCPBMinLSB: { getVal: encodeTRFCPBMinLsb }, + SPDIndexTRFCPBMinMSB: { getVal: encodeTRFCPBMinMsb }, +} + +type memParts struct { + MemParts []memPart `json:"parts"` +} + +type memPart struct { + Name string + Attribs memAttributes + SPDFileName string +} + +func writeSPDManifest(memParts *memParts, SPDDirName string) error { + var s string + + fmt.Printf("Generating SPD Manifest with following entries:\n") + + for i := 0; i < len(memParts.MemParts); i++ { + fmt.Printf("%-40s %s\n", memParts.MemParts[i].Name, memParts.MemParts[i].SPDFileName) + s += fmt.Sprintf("%s,%s\n", memParts.MemParts[i].Name, memParts.MemParts[i].SPDFileName) + } + + return ioutil.WriteFile(filepath.Join(SPDDirName, SPDManifestFileName), []byte(s), 0644) +} + +func isManufacturerPartNumberByte(index int) bool { + if index >= SPDIndexManufacturerPartNumberStartByte && index <= SPDIndexManufacturerPartNumberEndByte { + return true + } + return false +} + +func getSPDByte(index int, memAttribs *memAttributes) byte { + e, ok := SPDAttribTable[index] + if ok == false { + if isManufacturerPartNumberByte(index) { + return SPDValueManufacturerPartNumberBlank + } + return 0x00 + } + + if e.getVal != nil { + return e.getVal(memAttribs) + } + + return e.constVal +} + +func createSPD(memAttribs *memAttributes) string { + var s string + + for i := 0; i < 512; i++ { + b := getSPDByte(i, memAttribs) + + if (i + 1) % 16 == 0 { + s += fmt.Sprintf("%02X\n", b) + } else { + s += fmt.Sprintf("%02X ", b) + } + } + + return s +} + +func dedupeMemoryPart(dedupedParts []*memPart, memPart *memPart) bool { + for i := 0; i < len(dedupedParts); i++ { + if reflect.DeepEqual(dedupedParts[i].Attribs, memPart.Attribs) { + memPart.SPDFileName = dedupedParts[i].SPDFileName + return true + } + } + + return false +} + +func generateSPD(memPart *memPart, SPDId int, SPDDirName string) { + s := createSPD(&memPart.Attribs) + memPart.SPDFileName = fmt.Sprintf("spd-%d.hex", SPDId) + ioutil.WriteFile(filepath.Join(SPDDirName, memPart.SPDFileName), []byte(s), 0644) +} + +func readMemoryParts(memParts *memParts, memPartsFileName string) error { + databytes, err := ioutil.ReadFile(memPartsFileName) + if err != nil { + return err + } + + return json.Unmarshal(databytes, memParts) +} + +func validateDensityx8Channel(densityPerChannelGb int) error { + if _, ok := densityGbx8ChannelToRowColumnEncoding[densityPerChannelGb]; ok == false { + return fmt.Errorf("Incorrect x8 density: ", densityPerChannelGb, "Gb") + } + return nil +} + +func validateDensityx16Channel(densityPerChannelGb int) error { + if _, ok := densityGbx16ChannelToRowColumnEncoding[densityPerChannelGb]; ok == false { + return fmt.Errorf("Incorrect x16 density: ", densityPerChannelGb, "Gb") + } + return nil +} + +func validateDensity(memAttribs *memAttributes) error { + if memAttribs.BitWidthPerChannel == 8 { + return validateDensityx8Channel(memAttribs.DensityPerChannelGb) + } else if memAttribs.BitWidthPerChannel == 16 { + return validateDensityx16Channel(memAttribs.DensityPerChannelGb) + } + + return fmt.Errorf("No density table for this bit width: ", memAttribs.BitWidthPerChannel) +} + +func validateBanks(banks int) error { + if banks != 4 && banks != 8 { + return fmt.Errorf("Incorrect banks: ", banks) + } + return nil +} + +func validateChannels(channels int) error { + if channels != 1 && channels != 2 && channels != 4 { + return fmt.Errorf("Incorrect channels per die: ", channels) + } + return nil +} + +func validateDataWidth(width int) error { + if width != 8 && width != 16 { + return fmt.Errorf("Incorrect bit width: ", width) + } + return nil +} + +func validateRanks(ranks int) error { + if ranks != 1 && ranks != 2 { + return fmt.Errorf("Incorrect ranks: ", ranks) + } + return nil +} + +func validateSpeed(speed int) error { + if _, ok := speedMbpsToSPDEncoding[speed]; ok == false { + return fmt.Errorf("Incorrect speed: ", speed, " Mbps") + } + return nil +} + +func validateMemoryParts(memParts *memParts) error { + for i := 0; i < len(memParts.MemParts); i++ { + if err := validateBanks(memParts.MemParts[i].Attribs.Banks); err != nil { + return err + } + if err := validateChannels(memParts.MemParts[i].Attribs.ChannelsPerDie); err != nil { + return err + } + if err := validateDataWidth(memParts.MemParts[i].Attribs.BitWidthPerChannel); err != nil { + return err + } + if err := validateDensity(&memParts.MemParts[i].Attribs); err != nil { + return err + } + if err := validateRanks(memParts.MemParts[i].Attribs.RanksPerChannel); err != nil { + return err + } + if err := validateSpeed(memParts.MemParts[i].Attribs.SpeedMbps); err != nil { + return err + } + } + return nil +} + +func encodeLatencies(latency int, memAttribs *memAttributes) error { + switch latency { + case 6: + memAttribs.CASFirstByte |= CAS6 + case 10: + memAttribs.CASFirstByte |= CAS10 + case 14: + memAttribs.CASFirstByte |= CAS14 + case 16: + memAttribs.CASSecondByte |= CAS16 + case 20: + memAttribs.CASSecondByte |= CAS20 + case 22: + memAttribs.CASSecondByte |= CAS22 + case 24: + memAttribs.CASSecondByte |= CAS24 + case 26: + memAttribs.CASSecondByte |= CAS26 + case 28: + memAttribs.CASSecondByte |= CAS28 + case 32: + memAttribs.CASThirdByte |= CAS32 + case 36: + memAttribs.CASThirdByte |= CAS36 + case 40: + memAttribs.CASThirdByte |= CAS40 + default: + fmt.Errorf("Incorrect CAS Latency: ", latency) + } + + return nil +} + +func updateTCK(memAttribs *memAttributes) { + if memAttribs.TCKMinPs == 0 { + memAttribs.TCKMinPs = speedMbpsToSPDEncoding[memAttribs.SpeedMbps].TCKMinPs + } + if memAttribs.TCKMaxPs == 0 { + memAttribs.TCKMaxPs = speedMbpsToSPDEncoding[memAttribs.SpeedMbps].TCKMaxPs + } +} + +func getCASLatencies(memAttribs *memAttributes) string { + if memAttribs.BitWidthPerChannel == 16 { + return speedMbpsToSPDEncoding[memAttribs.SpeedMbps].CASLatenciesx16Channel + } else if memAttribs.BitWidthPerChannel == 8 { + return speedMbpsToSPDEncoding[memAttribs.SpeedMbps].CASLatenciesx8Channel + } + + return "" +} + +func updateCAS(memAttribs *memAttributes) error { + if len(memAttribs.CASLatencies) == 0 { + memAttribs.CASLatencies = getCASLatencies(memAttribs) + } + + latencies := strings.Fields(memAttribs.CASLatencies) + for i := 0; i < len(latencies); i++ { + latency,err := strconv.Atoi(latencies[i]) + if err != nil { + return fmt.Errorf("Unable to convert latency ", latencies[i]) + } + if err := encodeLatencies(latency, memAttribs); err != nil { + return err + } + } + return nil +} + +func getMinCAS(memAttribs *memAttributes) (int, error) { + if (memAttribs.CASThirdByte & CAS40) != 0 { + return 40, nil + } + if (memAttribs.CASThirdByte & CAS36) != 0 { + return 36, nil + } + if (memAttribs.CASThirdByte & CAS32) != 0 { + return 32, nil + } + if (memAttribs.CASSecondByte & CAS28) != 0 { + return 28, nil + } + + return 0, fmt.Errorf("Unexpected min CAS") +} + +func updateTAAMin(memAttribs *memAttributes) error { + if memAttribs.TAAMinPs == 0 { + minCAS, err := getMinCAS(memAttribs) + if err != nil { + return err + } + memAttribs.TAAMinPs = memAttribs.TCKMinPs * minCAS + } + + return nil +} + +func updateTRFCAB(memAttribs *memAttributes) { + if memAttribs.TRFCABNs == 0 { + memAttribs.TRFCABNs = densityGbPhysicalChannelToRefreshEncoding[memAttribs.DensityPerChannelGb].TRFCABNs + } +} + +func updateTRFCPB(memAttribs *memAttributes) { + if memAttribs.TRFCPBNs == 0 { + memAttribs.TRFCPBNs = densityGbPhysicalChannelToRefreshEncoding[memAttribs.DensityPerChannelGb].TRFCPBNs + } +} + +func updateTRCD(memAttribs *memAttributes) { + if memAttribs.TRCDMinNs == 0 { + /* JEDEC spec says max of 18ns */ + memAttribs.TRCDMinNs = 18 + } +} + +func updateTRPAB(memAttribs *memAttributes) { + if memAttribs.TRPABMinNs == 0 { + /* JEDEC spec says max of 21ns */ + memAttribs.TRPABMinNs = 21 + } +} + +func updateTRPPB(memAttribs *memAttributes) { + if memAttribs.TRPPBMinNs == 0 { + /* JEDEC spec says max of 18ns */ + memAttribs.TRPPBMinNs = 18 + } +} + +func normalizeMemoryAttributes(memAttribs *memAttributes) { + if currPlatform == PlatformTGL { + /* + * TGL does not really use physical organization of dies per package when + * generating the SPD. So, set it to 0 here so that deduplication ignores + * that field. + */ + memAttribs.DiesPerPackage = 0 + } +} + +func updateMemoryAttributes(memAttribs *memAttributes) error { + updateTCK(memAttribs) + if err := updateCAS(memAttribs); err != nil { + return err + } + if err := updateTAAMin(memAttribs); err != nil { + return err + } + updateTRFCAB(memAttribs) + updateTRFCPB(memAttribs) + updateTRCD(memAttribs) + updateTRPAB(memAttribs) + updateTRPPB(memAttribs) + + normalizeMemoryAttributes(memAttribs) + + return nil +} + +func isPlatformSupported(platform string) error { + var ok bool + + currPlatform, ok = platformMap[platform] + if ok == false { + return fmt.Errorf("Unsupported platform: ", platform) + } + + return nil +} + +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(" mem_parts_list_json = JSON File containing list of memory parts and attributes\n") + fmt.Printf(" platform = SoC Platform for which the SPDs are being generated\n\n\n") +} + +func main() { + if len(os.Args) != 4 { + usage() + log.Fatal("Incorrect number of arguments") + } + + var memParts memParts + var dedupedParts []*memPart + + SPDDir, GlobalMemPartsFile, Platform := os.Args[1], os.Args[2], strings.ToUpper(os.Args[3]) + + if err := isPlatformSupported(Platform); err != nil { + log.Fatal(err) + } + + if err := readMemoryParts(&memParts, GlobalMemPartsFile); err != nil { + log.Fatal(err) + } + + if err := validateMemoryParts(&memParts); err != nil { + log.Fatal(err) + } + + SPDId := 1 + + for i := 0; i < len(memParts.MemParts); i++ { + if err := updateMemoryAttributes(&memParts.MemParts[i].Attribs); err != nil { + log.Fatal(err) + } + + if dedupeMemoryPart(dedupedParts, &memParts.MemParts[i]) == false { + generateSPD(&memParts.MemParts[i], SPDId, SPDDir) + SPDId++ + dedupedParts = append(dedupedParts, &memParts.MemParts[i]) + } + } + + if err := writeSPDManifest(&memParts, SPDDir); err != nil { + log.Fatal(err) + } +} diff --git a/util/spd_tools/lp4x/global_lp4x_mem_parts.json.txt b/util/spd_tools/lp4x/global_lp4x_mem_parts.json.txt new file mode 100644 index 0000000000..109fadb916 --- /dev/null +++ b/util/spd_tools/lp4x/global_lp4x_mem_parts.json.txt @@ -0,0 +1,186 @@ +{ + "parts": [ + { + "name": "H9HCNNNBKMMLXR-NEE", + "attribs": { + "densityPerChannelGb": 8, + "banks": 8, + "channelsPerDie": 2, + "diesPerPackage": 1, + "bitWidthPerChannel": 16, + "ranksPerChannel": 1, + "speedMbps": 4267 + } + }, + { + "name": "H9HCNNNFAMMLXR-NEE", + "attribs": { + "densityPerChannelGb": 8, + "banks": 8, + "channelsPerDie": 4, + "diesPerPackage": 2, + "bitWidthPerChannel": 8, + "ranksPerChannel": 2, + "speedMbps": 4267 + } + }, + { + "name": "K4U6E3S4AA-MGCL", + "attribs": { + "densityPerChannelGb": 8, + "banks": 8, + "channelsPerDie": 2, + "diesPerPackage": 1, + "bitWidthPerChannel": 16, + "ranksPerChannel": 1, + "speedMbps": 4267 + } + }, + { + "name": "K4UBE3D4AA-MGCL", + "attribs": { + "densityPerChannelGb": 8, + "banks": 8, + "channelsPerDie": 2, + "diesPerPackage": 2, + "bitWidthPerChannel": 16, + "ranksPerChannel": 2, + "speedMbps": 4267 + } + }, + { + "name": "MT53E1G32D2NP-046 WT:A", + "attribs": { + "densityPerChannelGb": 16, + "banks": 8, + "channelsPerDie": 2, + "diesPerPackage": 1, + "bitWidthPerChannel": 16, + "ranksPerChannel": 1, + "speedMbps": 4267 + } + }, + { + "name": "H9HKNNNCRMBVAR-NEH", + "attribs": { + "densityPerChannelGb": 8, + "banks": 8, + "channelsPerDie": 2, + "diesPerPackage": 2, + "bitWidthPerChannel": 16, + "ranksPerChannel": 1, + "speedMbps": 4267 + } + }, + { + "name": "MT53E1G64D4SQ-046 WT:A", + "attribs": { + "densityPerChannelGb": 16, + "banks": 8, + "channelsPerDie": 2, + "diesPerPackage": 2, + "bitWidthPerChannel": 16, + "ranksPerChannel": 1, + "speedMbps": 4267 + } + }, + { + "name": "MT53E512M32D2NP-046 WT:F", + "attribs": { + "densityPerChannelGb": 8, + "banks": 8, + "channelsPerDie": 2, + "diesPerPackage": 1, + "bitWidthPerChannel": 16, + "ranksPerChannel": 1, + "speedMbps": 4267 + } + }, + { + "name": "NT6AP256T32AV-J2", + "attribs": { + "densityPerChannelGb": 4, + "banks": 8, + "channelsPerDie": 2, + "diesPerPackage": 1, + "bitWidthPerChannel": 16, + "ranksPerChannel": 1, + "speedMbps": 3733, + "tckMaxPs": 1250, + "casLatencies": "14 20 24 28 32" + } + }, + { + "name": "K4U6E3S4AA-MGCR", + "attribs": { + "densityPerChannelGb": 8, + "banks": 8, + "channelsPerDie": 2, + "diesPerPackage": 1, + "bitWidthPerChannel": 16, + "ranksPerChannel": 1, + "speedMbps": 4267 + } + }, + { + "name": "MT53E512M32D2NP-046 WT:E", + "attribs": { + "densityPerChannelGb": 8, + "banks": 8, + "channelsPerDie": 2, + "diesPerPackage": 1, + "bitWidthPerChannel": 16, + "ranksPerChannel": 1, + "speedMbps": 4267 + } + }, + { + "name": "H9HCNNNCPMMLXR-NEE", + "attribs": { + "densityPerChannelGb": 8, + "banks": 8, + "channelsPerDie": 2, + "diesPerPackage": 2, + "bitWidthPerChannel": 16, + "ranksPerChannel": 2, + "speedMbps": 4267 + } + }, + { + "name": "K4UBE3D4AA-MGCR", + "attribs": { + "densityPerChannelGb": 8, + "banks": 8, + "channelsPerDie": 2, + "diesPerPackage": 2, + "bitWidthPerChannel": 16, + "ranksPerChannel": 2, + "speedMbps": 4267 + } + }, + { + "name": "MT53E512M64D4NW-046 WT:E", + "attribs": { + "densityPerChannelGb": 8, + "banks": 8, + "channelsPerDie": 2, + "diesPerPackage": 2, + "bitWidthPerChannel": 16, + "ranksPerChannel": 1, + "speedMbps": 4267 + } + }, + { + "name": "MT53E1G64D8NW-046 WT:E", + "attribs": { + "densityPerChannelGb": 8, + "banks": 8, + "channelsPerDie": 2, + "diesPerPackage": 4, + "bitWidthPerChannel": 16, + "ranksPerChannel": 2, + "speedMbps": 4267 + } + } + ] +} -- cgit v1.2.3