Nick Vaccaro has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/44429 )
Change subject: util: Add spd_tools to generate SPDs for TGL boards ......................................................................
util: Add spd_tools to generate SPDs for TGL boards
Serial Presence Detect (SPD) data for memory modules is used by Memory Reference Code (MRC) for training the memory. This SPD data is typically obtained from part vendors but has to be massaged to format it correctly as per JEDEC and MRC expectations. There have been numerous times in the past where the SPD data used is not always correct.
In order to reduce the manual effort of creating SPDs and generating DRAM IDs, this change adds tools for generating SPD files for DDR4 memory used in memory down configurations on Intel Tiger Lake (TGL) based platforms. These tools generate SPDs following JESD79-4C and Jedec "4.1.2.L-5 R29 v103" specification.
Two tools are provided: * gen_spd.go: Generates de-duplicated SPD files using a global memory part list provided by the mainboard in JSON format. Additionally, generates a SPD manifest file (in CSV format) with information about what memory part from the global list uses which of the generated SPD files.
* gen_part_id.go: Allocates DRAM strap IDs for different DDR4 memory parts used by the board. Takes as input list of memory parts used by the board (with one memory part on each line) and the SPD manifest file generated by gen_spd.go. Generates Makefile.inc for integrating the generated SPD files in the coreboot build.
BUG=b:160157545
Change-Id: I263f936b332520753a6791c8d892fc148cb6f103 Signed-off-by: Nick Vaccaro nvaccaro@google.com --- A util/spd_tools/intel/ddr4/README.md A util/spd_tools/intel/ddr4/gen_part_id A util/spd_tools/intel/ddr4/gen_part_id.go A util/spd_tools/intel/ddr4/gen_spd A util/spd_tools/intel/ddr4/gen_spd.go A util/spd_tools/intel/ddr4/global_ddr4_mem_parts.json.txt 6 files changed, 2,186 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/29/44429/1
diff --git a/util/spd_tools/intel/ddr4/README.md b/util/spd_tools/intel/ddr4/README.md new file mode 100644 index 0000000..543e309 --- /dev/null +++ b/util/spd_tools/intel/ddr4/README.md @@ -0,0 +1,238 @@ +# 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` + +* `name`: Name of the DDR4 memory part being described with these attributes. + +* `speedBin`: Speed bin for the part. Valid values: + `"1600J", "1600K", "1600L", "1866L", "1866M", "1866N", "2133N", "2133P", + "2133R", "2400P", "2400R", "2400T", "2400U", "2666T", "2666U", "2666V", + "2666W", "2933V", "2933W", "2933Y", "2933AA", "3200W", "3200AA", "3200AC"` + +* `bankGroups`: Number of bank groups. Valid values: + `0, 2, 4` bank groups. + +* `banksPerGroup`: Number of banks per bank group. Valid values: + `4, 8` banks per group. + +* `capacityPerDieGb`: Capacity per die in gigabits. Valid values: + `1, 2, 4, 8, 16, 32` Gb part. + +* `pageSize`: Page size represented as number of 512-byte blocks. Valid values: + * 1 : 1/2 KB page size + * 2 : 1 KB page size + * 4 : 2 KB page size + +* `diesPerPackage`: Number of dies on the part. Valid values: + `1, 2, 3, 4, 5, 6, 7, 8` dies per package. + +* `deviceBusWidth`: Number of bits of the device's address bus. Valid values: + `8, 16, 32` bit-wide bus. + +* `signalLoading`: Refers to loading on signals at the SDRAM balls. Loading on + certain signals (CKE, ODT, etc.) per specification of device stacking as + defined in JESD79-4CNumber of bits of the device's address bus. Valid values: + `"not specified", "single", "multi"` + +* `packageRanks`: 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, 3, 4` package ranks. + +### Optional `attribs` + + FIXME - add all optional parameters + +### Example JSON file +``` +{ + "parts": [ + { + "name": "MEMORY_PART_A", + "attribs": { + "speedBin": "3200AA", + "bankGroups": 4, + "banksPerGroup": 4, + "capacityPerDieGb": 8, + "pageSize": 2, + "diesPerPackage": 2, + "deviceBusWidth": 16, + "signalLoading": "multi", + "ranksPerPackage": 1 + } + }, + { + "name": "MEMORY_PART_B", + "attribs": { + "speedBin": "3200AA", + "bankGroups": 2, + "banksPerGroup": 4, + "capacityPerDieGb": 8, + "pageSize": 2, + "diesPerPackage": 1, + "deviceBusWidth": 16, + "signalLoading": "single", + "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 (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 <spd_dir> <mem_parts_list_json> <platform> +# ./gen_part_id <spd_dir> <makefile_dir> <mem_parts_used_file> +``` + +## 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 b/util/spd_tools/intel/ddr4/gen_part_id new file mode 100755 index 0000000..6d0061f --- /dev/null +++ b/util/spd_tools/intel/ddr4/gen_part_id Binary files differ diff --git a/util/spd_tools/intel/ddr4/gen_part_id.go b/util/spd_tools/intel/ddr4/gen_part_id.go new file mode 100644 index 0000000..6c2ca11 --- /dev/null +++ b/util/spd_tools/intel/ddr4/gen_part_id.go @@ -0,0 +1,214 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +package main + +import ( + "encoding/csv" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" +) + +/* + * This program allocates DRAM strap IDs for different parts that are being used by the variant. + * + * It expects the following inputs: + * Pointer to SPD directory. This is the location where SPD files and SPD Manifest generated by + * gen_spd.go are placed. + * Pointer to Makefile directory. Makefile.inc generated by this program is placed in this + * location. + * Text file containing a list of memory parts names used by the board. Each line in the file + * is expected to have one memory part name. + */ +const ( + SPDManifestFileName = "spd_manifest.generated.txt" + MakefileName = "Makefile.inc" + DRAMIdFileName = "dram_id.generated.txt" +) + +func usage() { + fmt.Printf("\nUsage: %s <spd_dir> <makefile_dir> <mem_parts_used_file>\n\n", os.Args[0]) + fmt.Printf(" where,\n") + fmt.Printf(" spd_dir = Directory path containing SPD files and manifest generated by gen_spd.go\n") + fmt.Printf(" makefile_dir = Directory path where generated Makefile.inc should be placed\n") + fmt.Printf(" mem_parts_used_file = File containing list of memory parts used by the board\n\n\n") +} + +func checkArgs() error { + + for _, arg := range os.Args[1:] { + if _, err := os.Stat(arg); err != nil { + return err + } + } + + return nil +} + +/* + * Read input file that contains list of memory part names used by the variant (one on a line) + * and split into separate strings for each part name. + */ +func readParts(memPartsUsedFileName string) ([]string, error) { + lines, err := ioutil.ReadFile(memPartsUsedFileName) + if err != nil { + return nil, err + } + str := string(lines) + parts := strings.Split(str, "\n") + + return parts, nil +} + +/* + * Read SPD manifest file(CSV) generated by gen_spd program and generate two maps: + * 1. Part to SPD Map : This maps global memory part name to generated SPD file name + * 2. SPD to Index Map: This generates a map of deduplicated SPD file names to index assigned to + * that SPD. This function sets index for all SPDs to -1. This index gets + * updated as part of genPartIdInfo() depending upon the SPDs actually used + * by the variant. + */ +func readSPDManifest(SPDDirName string) (map[string]string, map[string]int, error) { + f, err := os.Open(filepath.Join(SPDDirName, SPDManifestFileName)) + if err != nil { + return nil, nil, err + } + defer f.Close() + r := csv.NewReader(f) + + partToSPDMap := make(map[string]string) + SPDToIndexMap := make(map[string]int) + + for { + fields, err := r.Read() + + if err == io.EOF { + break + } + + if err != nil { + return nil, nil, err + } + + if len(fields) != 2 { + return nil, nil, fmt.Errorf("CSV file is incorrectly formatted") + } + + partToSPDMap[fields[0]] = fields[1] + SPDToIndexMap[fields[1]] = -1 + } + + return partToSPDMap, SPDToIndexMap, nil +} + +/* Print information about memory part used by variant and ID assigned to it. */ +func appendPartIdInfo(s *string, partName string, index int) { + *s += fmt.Sprintf("%-30s %d (%04b)\n", partName, index, int64(index)) +} + +type partIds struct { + SPDFileName string + memParts string +} + +/* + * For each part used by variant, check if the SPD (as per the manifest) already has an ID + * assigned to it. If yes, then add the part name to the list of memory parts supported by the + * SPD entry. If not, then assign the next ID to the SPD file and add the part name to the + * list of memory parts supported by the SPD entry. + * + * Returns list of partIds that contains spdFileName and supported memory parts for each + * assigned ID. + */ +func genPartIdInfo(parts []string, partToSPDMap map[string]string, SPDToIndexMap map[string]int, makefileDirName string) ([]partIds, error) { + partIdList := []partIds{} + curId := 0 + var s string + + s += fmt.Sprintf("%-30s %s\n", "DRAM Part Name", "ID to assign") + + for _, p := range parts { + if p == "" { + continue + } + + SPDFileName,ok := partToSPDMap[p] + if !ok { + return nil, fmt.Errorf("Failed to find part ", p, " in SPD Manifest. Please add the part to global part list and regenerate SPD Manifest") + } + + index := SPDToIndexMap[SPDFileName] + if index != -1 { + partIdList[index].memParts += ", " + p + appendPartIdInfo(&s, p, index) + continue + } + + SPDToIndexMap[SPDFileName] = curId + + appendPartIdInfo(&s, p, curId) + entry := partIds{SPDFileName: SPDFileName, memParts: p} + partIdList = append(partIdList, entry) + + curId++ + } + + fmt.Printf("%s", s) + err := ioutil.WriteFile(filepath.Join(makefileDirName, DRAMIdFileName), []byte(s), 0644) + + return partIdList, err +} + +var generatedCodeLicense string = "## SPDX-License-Identifier: GPL-2.0-or-later" +var autoGeneratedInfo string = "## This is an auto-generated file. Do not edit!!" + +/* + * This function generates Makefile.inc under the variant directory path and adds assigned SPDs + * to SPD_SOURCES. + */ +func genMakefile(partIdList []partIds, makefileDirName string) error { + var s string + + s += fmt.Sprintf("%s\n%s\n\n", generatedCodeLicense, autoGeneratedInfo) + s += fmt.Sprintf("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 b/util/spd_tools/intel/ddr4/gen_spd new file mode 100755 index 0000000..c52f528 --- /dev/null +++ b/util/spd_tools/intel/ddr4/gen_spd Binary files differ diff --git a/util/spd_tools/intel/ddr4/gen_spd.go b/util/spd_tools/intel/ddr4/gen_spd.go new file mode 100644 index 0000000..dc0b97c --- /dev/null +++ b/util/spd_tools/intel/ddr4/gen_spd.go @@ -0,0 +1,1716 @@ +/* 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 + PlatformJSL = 1 +) + +var platformMap = map[string]int { + "TGL": PlatformTGL, +} + +var currPlatform int + +type memAttributes struct { + /* Primary attributes - must be provided by JSON file for each part */ + SpeedBin string + BankGroups int + BanksPerGroup int + CapacityPerDieGb int + PageSize int + DiesPerPackage int + DeviceBusWidth int + SignalLoading string + 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 + 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 +} + +const ( + SignalLoadingNotSpecified = "not specified" + SignalLoadingSingleLoadStack = "single" + SignalLoadingMultiLoadStack = "multi" +) + +/* 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 { + 1: 0x2, + 2: 0x3, + 4: 0x4, + 8: 0x5, + 16: 0x6, + 32: 0x7, +} + +/* + * Tables 4 thru Table 7 from JESD79-4C. + * Maps density per physical channel to row-column encoding for a device with x8/x16 + * physical channel. + */ +var densityGbx8x16ChannelToRowColumnEncoding = 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 */ +} + +/* + * Table 61 from JEDEC spec 4_01_02_AnnexL-5R29 + * 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, +} + +const ( + /* SDRAM Configuration Types */ + SDRAMConfig256Mbx8 = 0 + SDRAMConfig512Mbx8 = 1 + SDRAMConfig1Gbx8 = 2 + SDRAMConfig2Gbx8 = 3 + SDRAMConfig128Mbx16 = 4 + SDRAMConfig256Mbx16 = 5 + SDRAMConfig512Mbx16 = 6 + SDRAMConfig1Gbx16 = 7 +) + +const ( + /* SPEED Bin Grades */ + SpeedBin1600J = "1600J" + SpeedBin1600K = "1600K" + SpeedBin1600L = "1600L" + SpeedBin1866L = "1866L" + SpeedBin1866M = "1866M" + SpeedBin1866N = "1866N" + SpeedBin2133N = "2133N" + SpeedBin2133P = "2133P" + SpeedBin2133R = "2133R" + SpeedBin2400P = "2400P" + SpeedBin2400R = "2400R" + SpeedBin2400T = "2400T" + SpeedBin2400U = "2400U" + SpeedBin2666T = "2666T" + SpeedBin2666U = "2666U" + SpeedBin2666V = "2666V" + SpeedBin2666W = "2666W" + SpeedBin2933V = "2933V" + SpeedBin2933W = "2933W" + SpeedBin2933Y = "2933Y" + SpeedBin2933AA = "2933AA" + SpeedBin3200W = "3200W" + SpeedBin3200AA = "3200AA" + SpeedBin3200AC = "3200AC" + + /* + * FIXME - update to include all from Table 48 on Page 4.1.2.L-5 – 30: + * different min CAS times (tAAMin, RCDMin, RPMin)) + */ +) + +const ( + /* SPEED Bin Grades */ + SpeedBin1600 = 0 + SpeedBin1866 = 1 + SpeedBin2133 = 2 + SpeedBin2400 = 3 + SpeedBin2666 = 4 + SpeedBin2933 = 5 + SpeedBin3200 = 6 +) + +var speedBinMinorToMajor = map[string]int { + SpeedBin1600J: SpeedBin1600, + SpeedBin1600K: SpeedBin1600, + SpeedBin1600L: SpeedBin1600, + SpeedBin1866L: SpeedBin1866, + SpeedBin1866M: SpeedBin1866, + SpeedBin1866N: SpeedBin1866, + SpeedBin2133N: SpeedBin2133, + SpeedBin2133P: SpeedBin2133, + SpeedBin2133R: SpeedBin2133, + SpeedBin2400P: SpeedBin2400, + SpeedBin2400R: SpeedBin2400, + SpeedBin2400T: SpeedBin2400, + SpeedBin2400U: SpeedBin2400, + SpeedBin2666T: SpeedBin2666, + SpeedBin2666U: SpeedBin2666, + SpeedBin2666V: SpeedBin2666, + SpeedBin2666W: SpeedBin2666, + SpeedBin2933V: SpeedBin2933, + SpeedBin2933W: SpeedBin2933, + SpeedBin2933Y: SpeedBin2933, + SpeedBin3200W: SpeedBin3200, + SpeedBin3200AA: SpeedBin3200, + SpeedBin3200AC: SpeedBin3200, +} + + /* + * TODO: + * - Implement RFC1, RFC2, RFC4 conversion routine + * - Add updateXXX for each "type XXX struct {" + * - Add map function from speed bin sub-category to major category + * - Fix case statements for speed bins to use new map function to limit options + * - Find and address FIXMEs + * + * Question: Do we support 3DS ? + */ + +var speedBinToSPDEncoding = map[string]memAttributes { + SpeedBin1600J: { + TAAMinPs: 12500, + TRCDMinPs: 12500, + TRPMinPs: 12500, + TRASMinPs: 35000, + TRCMinPs: 47500, + TCKMinPs: 1250, + TCKMaxPs: 1500, + CASLatencies: "9 10 11 12", + }, + SpeedBin1600K: { + TAAMinPs: 13750, + TRCDMinPs: 13750, + TRPMinPs: 13750, + TRASMinPs: 35000, + TRCMinPs: 48750, + TCKMinPs: 1250, + TCKMaxPs: 1500, + CASLatencies: "9 11 12", + }, + SpeedBin1600L: { + TAAMinPs: 15000, + TRCDMinPs: 15000, + TRPMinPs: 15000, + TRASMinPs: 35000, + TRCMinPs: 50000, + TCKMinPs: 1250, + TCKMaxPs: 1500, + CASLatencies: "10 12", + }, + SpeedBin1866L: { + TAAMinPs: 12850, + TRCDMinPs: 12850, + TRPMinPs: 12850, + TRASMinPs: 34000, + TRCMinPs: 46850, + TCKMinPs: 1071, + TCKMaxPs: 1250, + CASLatencies: "9 10 12 13 14", + }, + SpeedBin1866M: { + TAAMinPs: 13920, + TRCDMinPs: 13920, + TRPMinPs: 13920, + TRASMinPs: 34000, + TRCMinPs: 47920, + TCKMinPs: 1071, + TCKMaxPs: 1250, + CASLatencies: "9 11 12 13 14", + }, + SpeedBin1866N: { + TAAMinPs: 15000, + TRCDMinPs: 15000, + TRPMinPs: 15000, + TRASMinPs: 34000, + TRCMinPs: 49000, + TCKMinPs: 1071, + TCKMaxPs: 1250, + CASLatencies: "10 12 14", + }, + SpeedBin2133N: { + TAAMinPs: 13130, + TRCDMinPs: 13130, + TRPMinPs: 13130, + TRASMinPs: 33000, + TRCMinPs: 46130, + TCKMinPs: 937, + TCKMaxPs: 1071, + CASLatencies: "9 10 12 14 15 16", + }, + SpeedBin2133P: { + TAAMinPs: 14060, + TRCDMinPs: 14060, + TRPMinPs: 14060, + TRASMinPs: 33000, + TRCMinPs: 47060, + TCKMinPs: 937, + TCKMaxPs: 1071, + CASLatencies: "9 11 12 13 14 15 16", + }, + SpeedBin2133R: { + TAAMinPs: 15000, + TRCDMinPs: 15000, + TRPMinPs: 15000, + TRASMinPs: 33000, + TRCMinPs: 48000, + TCKMinPs: 937, + TCKMaxPs: 1071, + CASLatencies: "10 12 14 16", + }, + SpeedBin2400P: { + TAAMinPs: 12500, + TRCDMinPs: 12500, + TRPMinPs: 12500, + TRASMinPs: 32000, + TRCMinPs: 44500, + TCKMinPs: 833, + TCKMaxPs: 937, + CASLatencies: "9 10 12 13 15 16 17 18", + }, + SpeedBin2400R: { + TAAMinPs: 13320, + TRCDMinPs: 13320, + TRPMinPs: 13320, + TRASMinPs: 32000, + TRCMinPs: 45320, + TCKMinPs: 833, + TCKMaxPs: 937, + CASLatencies: "9 11 12 13 14 15 16 17 18", + }, + SpeedBin2400T: { + TAAMinPs: 14160, + TRCDMinPs: 14160, + TRPMinPs: 14160, + TRASMinPs: 32000, + TRCMinPs: 46160, + TCKMinPs: 833, + TCKMaxPs: 937, + CASLatencies: "10 11 12 13 14 15 16 17 18", + }, + SpeedBin2400U: { + TAAMinPs: 15000, + TRCDMinPs: 15000, + TRPMinPs: 15000, + TRASMinPs: 32000, + TRCMinPs: 47000, + TCKMinPs: 833, + TCKMaxPs: 937, + CASLatencies: "10 12 14 16 18", + }, + SpeedBin2666T: { + TAAMinPs: 12750, + TRCDMinPs: 12750, + TRPMinPs: 12750, + TRASMinPs: 32000, + TRCMinPs: 44750, + TCKMinPs: 750, + TCKMaxPs: 833, + CASLatencies: "9 10 11 12 13 14 15 16 17 18 19 20", + }, + SpeedBin2666U: { + TAAMinPs: 13500, + TRCDMinPs: 13500, + TRPMinPs: 13500, + TRASMinPs: 32000, + TRCMinPs: 45500, + TCKMinPs: 750, + TCKMaxPs: 833, + CASLatencies: "9 10 11 12 13 14 15 16 17 18 19 20", + }, + SpeedBin2666V: { + TAAMinPs: 14250, + TRCDMinPs: 14250, + TRPMinPs: 14250, + TRASMinPs: 32000, + TRCMinPs: 46250, + TCKMinPs: 750, + TCKMaxPs: 833, + CASLatencies: "10 11 12 13 14 15 16 17 18 19 20", + }, + SpeedBin2666W: { + TAAMinPs: 15000, + TRCDMinPs: 15000, + TRPMinPs: 15000, + TRASMinPs: 32000, + TRCMinPs: 47000, + TCKMinPs: 750, + TCKMaxPs: 833, + CASLatencies: "10 12 14 16 18", + }, + SpeedBin2933V: { + TAAMinPs: 12960, + TRCDMinPs: 12960, + TRPMinPs: 12960, + TRASMinPs: 32000, + TRCMinPs: 44960, + TCKMinPs: 682, + TCKMaxPs: 750, + CASLatencies: "9 10 11 12 13 14 15 16 17 18 19 20 21 22", + }, + SpeedBin2933W: { + TAAMinPs: 13640, + TRCDMinPs: 13640, + TRPMinPs: 13640, + TRASMinPs: 32000, + TRCMinPs: 45640, + TCKMinPs: 682, + TCKMaxPs: 750, + CASLatencies: "10 11 12 13 14 15 16 17 18 19 20 21 22", + }, + SpeedBin2933Y: { + TAAMinPs: 14320, + TRCDMinPs: 14320, + TRPMinPs: 14320, + TRASMinPs: 32000, + TRCMinPs: 46320, + TCKMinPs: 682, + TCKMaxPs: 750, + CASLatencies: "10 11 12 13 14 15 16 17 18 19 20 21 22", + }, + SpeedBin2933AA: { + TAAMinPs: 15000, + TRCDMinPs: 15000, + TRPMinPs: 15000, + TRASMinPs: 32000, + TRCMinPs: 47000, + TCKMinPs: 682, + TCKMaxPs: 750, + CASLatencies: "10 12 14 16 18 20 22", + }, + SpeedBin3200W: { + TAAMinPs: 12500, + TRCDMinPs: 12500, + TRPMinPs: 12500, + TRASMinPs: 32000, + TRCMinPs: 44500, + TCKMinPs: 625, + TCKMaxPs: 682, + CASLatencies: "9 10 11 12 13 14 15 16 17 18 19 20 21 22 24", + }, + SpeedBin3200AA: { + TAAMinPs: 13750, + TRCDMinPs: 13750, + TRPMinPs: 13750, + TRASMinPs: 32000, + TRCMinPs: 45750, + TCKMinPs: 625, + TCKMaxPs: 682, + CASLatencies: "10 11 12 13 14 15 16 17 18 19 20 21 22 24", + }, + SpeedBin3200AC: { + TAAMinPs: 15000, + TRCDMinPs: 15000, + TRPMinPs: 15000, + TRASMinPs: 32000, + TRCMinPs: 47000, + TCKMinPs: 625, + TCKMaxPs: 682, + CASLatencies: "10 12 14 16 18 20 22 24", + }, +} + +var banksPerGroupEncoding = map[int]byte { + 4: 0 << 4, + 8: 1 << 4, +} + +var bankGroupsEncoding = map[int]byte { + 0: 0 << 6, + 2: 1 << 6, + 4: 2 << 6, +} + +func encodeDensityBanks(memAttribs *memAttributes) byte { + var b byte + + b = densityGbToSPDEncoding[memAttribs.CapacityPerDieGb] + b |= banksPerGroupEncoding[memAttribs.BanksPerGroup] + b |= bankGroupsEncoding[memAttribs.BankGroups] + + return b +} + +func encodeSdramAddressing(memAttribs *memAttributes) byte { + var b byte + + b = densityGbx8x16ChannelToRowColumnEncoding[memAttribs.CapacityPerDieGb] + + return b +} + +func encodePackage(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 encodeDiesPerPackage(dies int) byte { + var b byte + + b = encodePackage(dies) /* Monolithic / Non-monolithic device */ + b |= (byte(dies) - 1) << 4 + + return b +} + +var signalLoadingEncode = map[string]byte { + "not specified": 0, + "single": 1, + "multi": 2, +} + +func encodeSignalLoading(loading string) byte { + return(signalLoadingEncode[loading]) +} + +func encodePackageType(memAttribs *memAttributes) byte { + var b byte + + b = encodeSignalLoading(memAttribs.SignalLoading) + b |= encodeDiesPerPackage(memAttribs.DiesPerPackage) + b |= encodePackage(memAttribs.DiesPerPackage) + + return b +} + +func encodeDataWidth(bitWidthPerDevice int) byte { + return byte(bitWidthPerDevice / 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.DeviceBusWidth) + b |= encodeRanks(memAttribs.RanksPerPackage) + + 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 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) +} + + +/* + * Per Table 68 of Jedec spec 4.1.1.L-5R29 v103, + * tRRD_S is based on : + * Speed Bin (not including sub-category) + * Page Size specified in 512KB block count: + * 1 = 1/2 KB page size + * 2 = 1 KB page size + * 4 = 2 KB page size + */ + +func updateTRRDSPs(memAttribs *memAttributes) int { + var pS int + + switch speedBinMinorToMajor[memAttribs.SpeedBin] { + case SpeedBin1600: + switch memAttribs.PageSize { + case 1: + pS = 5000 + case 2: + pS = 5000 + case 4: + pS = 6000 + } + case SpeedBin1866: + switch memAttribs.PageSize { + case 1: + pS = 4200 + case 2: + pS = 4200 + case 4: + pS = 5300 + } + case SpeedBin2133: + switch memAttribs.PageSize { + case 1: + pS = 3700 + case 2: + pS = 3700 + case 4: + pS = 5300 + } + case SpeedBin2400: + switch memAttribs.PageSize { + case 1: + pS = 3300 + case 2: + pS = 3300 + case 4: + pS = 5300 + } + case SpeedBin2666: + switch memAttribs.PageSize { + case 1: + pS = 3000 + case 2: + pS = 3000 + case 4: + pS = 5300 + } + case SpeedBin2933: + switch memAttribs.PageSize { + case 1: + pS = 2700 + case 2: + pS = 2700 + case 4: + pS = 5300 + } + case SpeedBin3200: + switch memAttribs.PageSize { + case 1: + pS = 2500 + case 2: + pS = 2500 + case 4: + pS = 5300 + } + /* FIXME - Table also has 3DS settings, but > DDR4-2400-3DS is TBD */ + } + + return pS +} + +/* + * Per Table 70 of Jedec spec 4.1.1.L-5R29 v103, + * tRRD_L is based on : + * Speed Bin (not including sub-category) and + * Page Size specified in 512KB block count: + * 1 = 1/2 KB page size + * 2 = 1 KB page size + * 4 = 2 KB page size + */ +func getTRRDLPs(memAttribs *memAttributes) int { + var pS int + + switch speedBinMinorToMajor[memAttribs.SpeedBin] { + case SpeedBin1600: + switch memAttribs.PageSize { + case 1: + pS = 6000 + case 2: + pS = 6000 + case 4: + pS = 7500 + } + case SpeedBin1866: + switch memAttribs.PageSize { + case 1: + pS = 5300 + case 2: + pS = 5300 + case 4: + pS = 6400 + } + + case SpeedBin2133: + switch memAttribs.PageSize { + case 1: + pS = 5300 + case 2: + pS = 5300 + case 4: + pS = 6400 + } + case SpeedBin2400: + switch memAttribs.PageSize { + case 1: + pS = 4900 + case 2: + pS = 4900 + case 4: + pS = 6400 + } + case SpeedBin2666: + switch memAttribs.PageSize { + case 1: + pS = 4900 + case 2: + pS = 4900 + case 4: + pS = 6400 + } + case SpeedBin2933: + switch memAttribs.PageSize { + case 1: + pS = 4900 + case 2: + pS = 4900 + case 4: + pS = 6400 + } + case SpeedBin3200: + switch memAttribs.PageSize { + case 1: + pS = 4900 + case 2: + pS = 4900 + case 4: + pS = 6400 + } + /* FIXME - Table also has 3DS settings, but > DDR4-2400-3DS is TBD */ + } + + return pS +} + + +/* + * Per Table 72 of Jedec spec 4.1.1.L-5R29 v103, + * tCCD_L is based on : + * Speed Bin (not including sub-category) + */ +func getTCCDLPs(memAttribs *memAttributes) int { + var pS int + + switch speedBinMinorToMajor[memAttribs.SpeedBin] { + case SpeedBin1600: + pS = 6250 + case SpeedBin1866: + pS = 5355 + case SpeedBin2133: + pS = 5355 + default: + pS = 5000 + /* FIXME - Table also has 3DS settings, but > DDR4-2400-3DS is TBD */ + } + + return pS +} + + +/* + * Per Table 66 of Jedec spec 4.1.1.L-5R29 v103, + * FAW timing is based on : + * Speed Bin (not including sub-category) and + * Page Size specified in 512KB block count: + * 1 = 1/2 KB page size + * 2 = 1 KB page size + * 4 = 2 KB page size + */ + +func getTFAWPs(memAttribs *memAttributes) int { + var pS int + + switch speedBinMinorToMajor[memAttribs.SpeedBin] { + case SpeedBin1600: + switch memAttribs.PageSize { + case 1: + pS = 20000 + case 2: + pS = 25000 + case 4: + pS = 35000 + } + case SpeedBin1866: + switch memAttribs.PageSize { + case 1: + pS = 17000 + case 2: + pS = 23000 + case 4: + pS = 30000 + } + case SpeedBin2133: + switch memAttribs.PageSize { + case 1: + pS = 15000 + case 2: + pS = 21000 + case 4: + pS = 30000 + } + case SpeedBin2400: + switch memAttribs.PageSize { + case 1: + pS = 13000 + case 2: + pS = 21000 + case 4: + pS = 30000 + } + case SpeedBin2666: + switch memAttribs.PageSize { + case 1: + pS = 12000 + case 2: + pS = 21000 + case 4: + pS = 30000 + } + case SpeedBin2933: + switch memAttribs.PageSize { + case 1: + pS = 10875 + case 2: + pS = 21000 + case 4: + pS = 30000 + } + case SpeedBin3200: + switch memAttribs.PageSize { + case 1: + pS = 10000 + case 2: + pS = 21000 + case 4: + pS = 30000 + } + /* FIXME - Table also has 3DS settings, but > DDR4-2400-3DS, x8 is tbd */ + } + + return pS +} + + +/* Update settings based on data sheet (json) supplied memory attributes */ +func encodeTRFC1MinLsb(memAttribs *memAttributes) byte { + var mtb int + + mtb = convPsToMtb(tRFC1Encoding[memAttribs.CapacityPerDieGb]) + + return byte(mtb & 0xff) +} + +func encodeTRFC1MinMsb(memAttribs *memAttributes) byte { + var mtb int + + mtb = convPsToMtb(tRFC1Encoding[memAttribs.CapacityPerDieGb]) + + return byte((mtb >> 8) & 0xff) +} + +func encodeTRFC2MinLsb(memAttribs *memAttributes) byte { + var mtb int + + mtb = convPsToMtb(tRFC2Encoding[memAttribs.CapacityPerDieGb]) + + return byte(mtb & 0xff) +} + +func encodeTRFC2MinMsb(memAttribs *memAttributes) byte { + var mtb int + + mtb = convPsToMtb(tRFC2Encoding[memAttribs.CapacityPerDieGb]) + + return byte((mtb >> 8) & 0xff) +} + +func encodeTRFC4MinLsb(memAttribs *memAttributes) byte { + var mtb int + + mtb = convPsToMtb(tRFC4Encoding[memAttribs.CapacityPerDieGb]) + + return byte(mtb & 0xff) +} + +func encodeTRFC4MinMsb(memAttribs *memAttributes) byte { + var mtb int + + mtb = convPsToMtb(tRFC4Encoding[memAttribs.CapacityPerDieGb]) + + return byte((mtb >> 8) & 0xff) +} + +func encodeTFAWMinMSN(memAttribs *memAttributes) byte { + var mtb int + + mtb = convPsToMtb(getTFAWPs(memAttribs)) + + return byte((mtb >> 8) & 0x0f) +} + +func encodeTFAWMinLsb(memAttribs *memAttributes) byte { + var mtb int + + mtb = convPsToMtb(getTFAWPs(memAttribs)) + + 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("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 validateSpeedBin(speedBin string) error { + if _, ok := speedBinToSPDEncoding[speedBin]; ok == false { + return fmt.Errorf("Incorrect speed bin: DDR4-", speedBin) + } + return nil +} + +func validateBankGroups(bankGroups int) error { + if bankGroups != 0 && bankGroups != 2 && bankGroups != 4 { + return fmt.Errorf("Incorrect bank groups: ", bankGroups) + } + return nil +} + +func validateBanksPerGroup(banksPerGroup int) error { + if banksPerGroup != 4 && banksPerGroup != 8 { + return fmt.Errorf("Incorrect banks per group: ", banksPerGroup) + } + return nil +} + +func validateCapacityPerDie(capacityPerDieGb int) error { + switch capacityPerDieGb { + case 1: + return nil + case 2: + return nil + case 4: + return nil + case 8: + return nil + case 16: + return nil + case 32: + return nil + } + return fmt.Errorf("Incorrect capacity per die: ", capacityPerDieGb) +} + +func validatePageSize(pageSize int) error { + if pageSize != 1 && pageSize != 2 && pageSize != 4 { + return fmt.Errorf("Incorrect page size: ", pageSize) + } + return nil +} + +func validateDiesPerPackage(dieCount int) error { + if dieCount >= 1 && dieCount <= 8 { + 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 validateSignalLoading(loading string) error { + if loading != "not specified" && loading != "single" && loading != "multi" { + return fmt.Errorf("Incorrect signal loading: ", loading) + } + return nil +} + +func validateRanksPerPackage(ranks int) error { + if ranks >= 1 && ranks <= 4 { + return nil + } + return fmt.Errorf("Incorrect package ranks: ", ranks) +} + +/* +1) validate memory parts +2) remove any fields that Intel does not care about +*/ + +func validateMemoryParts(memParts *memParts) error { + for i := 0; i < len(memParts.MemParts); i++ { + if err := validateSpeedBin(memParts.MemParts[i].Attribs.SpeedBin); err != nil { + return err + } + if err := validateBankGroups(memParts.MemParts[i].Attribs.BankGroups); err != nil { + return err + } + if err := validateBanksPerGroup(memParts.MemParts[i].Attribs.BanksPerGroup); err != nil { + return err + } + if err := validateCapacityPerDie(memParts.MemParts[i].Attribs.CapacityPerDieGb); err != nil { + return err + } + if err := validatePageSize(memParts.MemParts[i].Attribs.PageSize); 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 := validateSignalLoading(memParts.MemParts[i].Attribs.SignalLoading); err != nil { + return err + } + if err := validateRanksPerPackage(memParts.MemParts[i].Attribs.RanksPerPackage); 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.CASSecondByte |= CAS24 + default: + fmt.Errorf("Incorrect CAS Latency: ", latency) + } + + return nil +} + +func getCASLatencies(memAttribs *memAttributes) string { + return speedBinToSPDEncoding[memAttribs.SpeedBin].CASLatencies +} + +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 updateTAAMin(memAttribs *memAttributes) error { + if memAttribs.TAAMinPs == 0 { + memAttribs.TAAMinPs = speedBinToSPDEncoding[memAttribs.SpeedBin].TAAMinPs + } + + return nil +} + +func updateTRCDMin(memAttribs *memAttributes) error { + if memAttribs.TRCDMinPs == 0 { + memAttribs.TRCDMinPs = speedBinToSPDEncoding[memAttribs.SpeedBin].TRCDMinPs + } + + return nil +} + +func updateTRPMin(memAttribs *memAttributes) error { + if memAttribs.TRPMinPs == 0 { + memAttribs.TRPMinPs = speedBinToSPDEncoding[memAttribs.SpeedBin].TRPMinPs + } + + return nil +} + +func updateTRASMin(memAttribs *memAttributes) error { + if memAttribs.TRASMinPs == 0 { + memAttribs.TRASMinPs = speedBinToSPDEncoding[memAttribs.SpeedBin].TRASMinPs + } + + return nil +} + +func updateTRCMin(memAttribs *memAttributes) error { + if memAttribs.TRCMinPs == 0 { + memAttribs.TRCMinPs = speedBinToSPDEncoding[memAttribs.SpeedBin].TRCMinPs + } + + return nil +} + +func updateTCK(memAttribs *memAttributes) error { + if memAttribs.TCKMinPs == 0 { + memAttribs.TCKMinPs = speedBinToSPDEncoding[memAttribs.SpeedBin].TCKMinPs + } + if memAttribs.TCKMaxPs == 0 { + memAttribs.TCKMaxPs = speedBinToSPDEncoding[memAttribs.SpeedBin].TCKMaxPs + } + + return nil +} + +func updateTWRMin(memAttribs *memAttributes) error { + if memAttribs.TWRMinPs == 0 { + memAttribs.TWRMinPs = TimingValueTWRMinPs + } + + return nil +} + +func updateTWTRMin(memAttribs *memAttributes) error { + if memAttribs.TWTRLMinPs == 0 { + memAttribs.TWTRLMinPs = TimingValueTWTRLMinPs + } + if memAttribs.TWTRSMinPs == 0 { + memAttribs.TWTRSMinPs = TimingValueTWTRSMinPs + } + + return nil +} + +func updateSpeedBinAttributes(memAttribs *memAttributes) error { + updateTAAMin(memAttribs) + updateTRCDMin(memAttribs) + updateTRPMin(memAttribs) + updateTRASMin(memAttribs) + updateTRCMin(memAttribs) + updateTCK(memAttribs) + updateTWRMin(memAttribs) + updateTWTRMin(memAttribs) + updateCAS(memAttribs) + + return nil +} + +func updateMemoryAttributes(memAttribs *memAttributes) error { + /* Update Primary attributes from JSON file for each part */ + /* Update settings based on Speed Bin */ + if err := updateSpeedBinAttributes(memAttribs); err != nil { + return err + } + + 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 <spd_dir> <mem_parts_list_json> <platform>\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/ddr4/global_ddr4_mem_parts.json.txt b/util/spd_tools/intel/ddr4/global_ddr4_mem_parts.json.txt new file mode 100644 index 0000000..0f2009b --- /dev/null +++ b/util/spd_tools/intel/ddr4/global_ddr4_mem_parts.json.txt @@ -0,0 +1,18 @@ +{ + "parts": [ + { + "name": "H5ANAG6NCMR-XN", + "attribs": { + "speedBin": "3200AA", + "bankGroups": 4, + "banksPerGroup": 4, + "capacityPerDieGb": 8, + "pageSize": 2, + "diesPerPackage": 2, + "deviceBusWidth": 16, + "signalLoading": "multi", + "ranksPerPackage": 1 + } + }, + ] +}