diff options
author | Reka Norman <rekanorman@google.com> | 2021-09-08 18:50:58 +1000 |
---|---|---|
committer | Felix Held <felix-coreboot@felixheld.de> | 2021-09-17 14:49:22 +0000 |
commit | 0e79274d33ed701d320bd4cc3b7c26b97120557d (patch) | |
tree | aca22f39037a9f973ff1b437ab03a8f19228bfae | |
parent | 20795899999bab47ba875b0d2a2c7e0a6ff4effa (diff) |
util/spd_tools: Implement a unified version of the spd_gen tool
Currently there are two versions of spd_tools: one for LP4x and one for
DDR4. This change is the first step in unifying these into a single
tool.
This change implements a unified version of the spd_gen tool, by
combining the functionality currently in lp4x/gen_spd.go and
ddr4/gen_spd.go. The unified version takes the memory technology as an
argument, and generates SPD files for all platforms supporting that
technology.
BUG=b:191776301
TEST=Compare the SPDs generated by the old and new versions of the tool
for all supported platforms. For reference, the test script used is
here: https://review.coreboot.org/c/coreboot/+/57511
Signed-off-by: Reka Norman <rekanorman@google.com>
Change-Id: I7fc036996dbafbb54e075da0c3ac2ea0886a6db2
Reviewed-on: https://review.coreboot.org/c/coreboot/+/57512
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Furquan Shaikh <furquan@google.com>
Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Reviewed-by: Karthik Ramasubramanian <kramasub@google.com>
Reviewed-by: Michael Niewöhner <foss@mniewoehner.de>
-rw-r--r-- | util/spd_tools/.gitignore | 1 | ||||
-rw-r--r-- | util/spd_tools/Makefile | 11 | ||||
-rw-r--r-- | util/spd_tools/src/spd_gen/ddr4.go | 1284 | ||||
-rw-r--r-- | util/spd_tools/src/spd_gen/lp4x.go | 906 | ||||
-rw-r--r-- | util/spd_tools/src/spd_gen/spd_gen.go | 281 |
5 files changed, 2483 insertions, 0 deletions
diff --git a/util/spd_tools/.gitignore b/util/spd_tools/.gitignore index cafb8530d6..e0d1592184 100644 --- a/util/spd_tools/.gitignore +++ b/util/spd_tools/.gitignore @@ -1,2 +1,3 @@ */gen_spd */gen_part_id +bin diff --git a/util/spd_tools/Makefile b/util/spd_tools/Makefile new file mode 100644 index 0000000000..8a9d3924a1 --- /dev/null +++ b/util/spd_tools/Makefile @@ -0,0 +1,11 @@ +SPD_GEN = bin/spd_gen + +all: $(SPD_GEN) + +$(SPD_GEN): + go build -o $(SPD_GEN) src/spd_gen/*.go + +clean: + rm -rf bin/ + +.PHONY: all diff --git a/util/spd_tools/src/spd_gen/ddr4.go b/util/spd_tools/src/spd_gen/ddr4.go new file mode 100644 index 0000000000..ce60038b77 --- /dev/null +++ b/util/spd_tools/src/spd_gen/ddr4.go @@ -0,0 +1,1284 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +package main + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" +) + +/* ------------------------------------------------------------------------------------------ */ +/* DDR4-defined types */ +/* ------------------------------------------------------------------------------------------ */ + +type ddr4 struct { +} + +type DDR4MemAttributes struct { + /* Primary attributes - must be provided by JSON file for each part */ + SpeedMTps int + CL_nRCD_nRP int + CapacityPerDieGb int + DiesPerPackage int + PackageBusWidth 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 + + /* The following is for internal-use only and is not overridable */ + dieBusWidth int +} + +type DDR4SpeedBinAttributes struct { + TRASMinPs int + TCKMaxPs int +} + +type DDR4SPDMemAttribFunc func(*DDR4MemAttributes) byte + +type DDR4SPDAttribTableEntry struct { + constVal byte + getVal DDR4SPDMemAttribFunc +} + +/* ------------------------------------------------------------------------------------------ */ +/* Constants */ +/* ------------------------------------------------------------------------------------------ */ + +const ( + /* SPD Byte Index */ + DDR4SPDIndexSize = 0 + DDR4SPDIndexRevision = 1 + DDR4SPDIndexMemoryType = 2 + DDR4SPDIndexModuleType = 3 + DDR4SPDIndexDensityBanks = 4 + DDR4SPDIndexAddressing = 5 + DDR4SPDIndexPackageType = 6 + DDR4SPDIndexOptionalFeatures = 7 + DDR4SPDIndexModuleOrganization = 12 + DDR4SPDIndexBusWidth = 13 + DDR4SPDIndexTimebases = 17 + DDR4SPDIndexTCKMin = 18 + DDR4SPDIndexTCKMax = 19 + DDR4SPDIndexCASFirstByte = 20 + DDR4SPDIndexCASSecondByte = 21 + DDR4SPDIndexCASThirdByte = 22 + DDR4SPDIndexCASFourthByte = 23 + DDR4SPDIndexTAAMin = 24 + DDR4SPDIndexTRCDMin = 25 + DDR4SPDIndexTRPMin = 26 + DDR4SPDIndexTRASRCMinMSNs = 27 + DDR4SPDIndexTRASMinLsb = 28 + DDR4SPDIndexTRCMinLsb = 29 + DDR4SPDIndexTRFC1MinLsb = 30 + DDR4SPDIndexTRFC1MinMsb = 31 + DDR4SPDIndexTRFC2MinLsb = 32 + DDR4SPDIndexTRFC2MinMsb = 33 + DDR4SPDIndexTRFC4MinLsb = 34 + DDR4SPDIndexTRFC4MinMsb = 35 + DDR4SPDIndexTFAWMinMSN = 36 + DDR4SPDIndexTFAWMinLsb = 37 + DDR4SPDIndexTRRDSMin = 38 + DDR4SPDIndexTRRDLMin = 39 + DDR4SPDIndexTCCDLMin = 40 + DDR4SPDIndexTWRMinMSN = 41 + DDR4SPDIndexTWRMinLsb = 42 + DDR4SPDIndexTWTRMinMSNs = 43 + DDR4SPDIndexWTRSMinLsb = 44 + DDR4SPDIndexWTRLMinLsb = 45 + DDR4SPDIndexTCCDLMinFineOffset = 117 + DDR4SPDIndexTRRDLMinFineOffset = 118 + DDR4SPDIndexTRRDSMinFineOffset = 119 + DDR4SPDIndexTRCMinFineOffset = 120 + DDR4SPDIndexTRPMinFineOffset = 121 + DDR4SPDIndexTRCDMinFineOffset = 122 + DDR4SPDIndexTAAMinFineOffset = 123 + DDR4SPDIndexTCKMaxFineOffset = 124 + DDR4SPDIndexTCKMinFineOffset = 125 + DDR4SPDIndexManufacturerPartNumberStartByte = 329 + DDR4SPDIndexManufacturerPartNumberEndByte = 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. + */ + DDR4SPDValueSize = 0x23 + + /* + * From JEDEC spec: Revision 1.1 + * Set to 0x11. + */ + DDR4SPDValueRevision = 0x11 + + /* DDR4 memory type = 0x0C */ + DDR4SPDValueMemoryType = 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. + */ + DDR4SPDValueModuleType = 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 + */ + DDR4SPDValueOptionalFeatures = 0x08 + + /* + * From JEDEC spec: + * 2:0 Primary Bus Width in Bits = 011 (x64 always) + * Set to 0x03. + */ + DDR4SPDValueModuleBusWidth = 0x03 + + /* + * From JEDEC spec: + * 3:2 (MTB) = 00 (0.125ns) + * 1:0 (FTB) = 00 (1ps) + * Set to 0x00. + */ + DDR4SPDValueTimebases = 0x00 + + /* CAS fourth byte: All bits are reserved */ + DDR4SPDValueCASFourthByte = 0x00 + + /* As per JEDEC spec, unused digits of manufacturer part number are left as blank. */ + DDR4SPDValueManufacturerPartNumberBlank = 0x20 +) + +const ( + /* First Byte */ + DDR4CAS9 = 1 << 2 + DDR4CAS10 = 1 << 3 + DDR4CAS11 = 1 << 4 + DDR4CAS12 = 1 << 5 + DDR4CAS13 = 1 << 6 + DDR4CAS14 = 1 << 7 + /* Second Byte */ + DDR4CAS15 = 1 << 0 + DDR4CAS16 = 1 << 1 + DDR4CAS17 = 1 << 2 + DDR4CAS18 = 1 << 3 + DDR4CAS19 = 1 << 4 + DDR4CAS20 = 1 << 5 + DDR4CAS21 = 1 << 6 + DDR4CAS22 = 1 << 7 + /* Third Byte */ + DDR4CAS24 = 1 << 1 +) + +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 + */ + DDR4TimingValueTWRMinPs = 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 + */ + DDR4TimingValueTWTRSMinPs = 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 + */ + DDR4TimingValueTWTRLMinPs = 7500 +) + +/* ------------------------------------------------------------------------------------------ */ +/* Global variables */ +/* ------------------------------------------------------------------------------------------ */ + +var DDR4PlatformSetMap = map[int][]int{ + 0: {PlatformTGL, PlatformPCO, PlatformPLK}, +} + +var DDR4PartAttributeMap = map[string]DDR4MemAttributes{} +var DDR4CurrSet int + +/* This encodes the density in Gb to SPD low nibble value as per JESD 4.1.2.L-5 R29 */ +var DDR4DensityGbToSPDEncoding = 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 DDR4DensityGbx8x16DieCapacityToRowColumnEncoding = 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 DDR4TRFC1Encoding = map[int]int{ + 2: 160000, + 4: 260000, + 8: 350000, + 16: 550000, +} + +/* maps die density to rcf2 timing in pico seconds */ +var DDR4TRFC2Encoding = map[int]int{ + 2: 110000, + 4: 160000, + 8: 260000, + 16: 350000, +} + +/* maps die density to rcf4 timing in pico seconds */ +var DDR4TRFC4Encoding = map[int]int{ + 2: 90000, + 4: 110000, + 8: 160000, + 16: 260000, +} + +var DDR4SpeedBinToSPDEncoding = map[int]DDR4SpeedBinAttributes{ + 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, + }, +} + +/* This takes memAttribs.PackageBusWidth as an index */ +var DDR4PageSizefromBusWidthEncoding = map[int]int{ + 8: 1, + 16: 2, +} + +var DDR4SpeedToTRRDSMinPsOneKPageSize = map[int]int{ + 1600: 5000, + 1866: 4200, + 2133: 3700, + 2400: 3300, + 2666: 3000, + 2933: 2700, + 3200: 2500, +} + +var DDR4SpeedToTRRDSMinPsTwoKPageSize = map[int]int{ + 1600: 6000, + 1866: 5300, + 2133: 5300, + 2400: 5300, + 2666: 5300, + 2933: 5300, + 3200: 5300, +} + +var DDR4SPDAttribTable = map[int]DDR4SPDAttribTableEntry{ + DDR4SPDIndexSize: {constVal: DDR4SPDValueSize}, + DDR4SPDIndexRevision: {constVal: DDR4SPDValueRevision}, + DDR4SPDIndexMemoryType: {constVal: DDR4SPDValueMemoryType}, + DDR4SPDIndexModuleType: {constVal: DDR4SPDValueModuleType}, + DDR4SPDIndexDensityBanks: {getVal: DDR4EncodeDensityBanks}, + DDR4SPDIndexAddressing: {getVal: DDR4EncodeSdramAddressing}, + DDR4SPDIndexPackageType: {getVal: DDR4EncodePackageType}, + DDR4SPDIndexOptionalFeatures: {constVal: DDR4SPDValueOptionalFeatures}, + DDR4SPDIndexModuleOrganization: {getVal: DDR4EncodeModuleOrganization}, + DDR4SPDIndexBusWidth: {constVal: DDR4SPDValueModuleBusWidth}, + DDR4SPDIndexTimebases: {constVal: DDR4SPDValueTimebases}, + DDR4SPDIndexTCKMin: {getVal: DDR4EncodeTCKMin}, + DDR4SPDIndexTCKMinFineOffset: {getVal: DDR4EncodeTCKMinFineOffset}, + DDR4SPDIndexTCKMax: {getVal: DDR4EncodeTCKMax}, + DDR4SPDIndexTCKMaxFineOffset: {getVal: DDR4EncodeTCKMaxFineOffset}, + DDR4SPDIndexCASFirstByte: {getVal: DDR4EncodeCASFirstByte}, + DDR4SPDIndexCASSecondByte: {getVal: DDR4EncodeCASSecondByte}, + DDR4SPDIndexCASThirdByte: {getVal: DDR4EncodeCASThirdByte}, + DDR4SPDIndexCASFourthByte: {getVal: DDR4EncodeCASFourthByte}, + DDR4SPDIndexTAAMin: {getVal: DDR4EncodeTAAMin}, + DDR4SPDIndexTAAMinFineOffset: {getVal: DDR4EncodeTAAMinFineOffset}, + DDR4SPDIndexTRCDMin: {getVal: DDR4EncodeTRCDMin}, + DDR4SPDIndexTRCDMinFineOffset: {getVal: DDR4EncodeTRCDMinFineOffset}, + DDR4SPDIndexTRPMin: {getVal: DDR4EncodeTRPMin}, + DDR4SPDIndexTRPMinFineOffset: {getVal: DDR4EncodeTRPMinFineOffset}, + DDR4SPDIndexTRASRCMinMSNs: {getVal: DDR4EncodeTRASRCMinMSNs}, + DDR4SPDIndexTRASMinLsb: {getVal: DDR4EncodeTRASMinLsb}, + DDR4SPDIndexTRCMinLsb: {getVal: DDR4EncodeTRCMinLsb}, + DDR4SPDIndexTRCMinFineOffset: {getVal: DDR4EncodeTRCMinFineOffset}, + DDR4SPDIndexTRFC1MinLsb: {getVal: DDR4EncodeTRFC1MinLsb}, + DDR4SPDIndexTRFC1MinMsb: {getVal: DDR4EncodeTRFC1MinMsb}, + DDR4SPDIndexTRFC2MinLsb: {getVal: DDR4EncodeTRFC2MinLsb}, + DDR4SPDIndexTRFC2MinMsb: {getVal: DDR4EncodeTRFC2MinMsb}, + DDR4SPDIndexTRFC4MinLsb: {getVal: DDR4EncodeTRFC4MinLsb}, + DDR4SPDIndexTRFC4MinMsb: {getVal: DDR4EncodeTRFC4MinMsb}, + DDR4SPDIndexTFAWMinMSN: {getVal: DDR4EncodeTFAWMinMSN}, + DDR4SPDIndexTFAWMinLsb: {getVal: DDR4EncodeTFAWMinLsb}, + DDR4SPDIndexTRRDSMin: {getVal: DDR4EncodeTRRDSMin}, + DDR4SPDIndexTRRDSMinFineOffset: {getVal: DDR4EncodeTRRDSMinFineOffset}, + DDR4SPDIndexTRRDLMin: {getVal: DDR4EncodeTRRDLMin}, + DDR4SPDIndexTRRDLMinFineOffset: {getVal: DDR4EncodeTRRDLMinFineOffset}, + DDR4SPDIndexTCCDLMin: {getVal: DDR4EncodeTCCDLMin}, + DDR4SPDIndexTCCDLMinFineOffset: {getVal: DDR4EncodeTCCDLMinFineOffset}, + DDR4SPDIndexTWRMinMSN: {getVal: DDR4EncodeTWRMinMSN}, + DDR4SPDIndexTWRMinLsb: {getVal: DDR4EncodeTWRMinLsb}, + DDR4SPDIndexTWTRMinMSNs: {getVal: DDR4EncodeTWTRMinMSNs}, + DDR4SPDIndexWTRSMinLsb: {getVal: DDR4EncodeTWTRSMinLsb}, + DDR4SPDIndexWTRLMinLsb: {getVal: DDR4EncodeTWTRLMinLsb}, +} + +/* ------------------------------------------------------------------------------------------ */ +/* Functions */ +/* ------------------------------------------------------------------------------------------ */ + +func DDR4GetBankGroups(memAttribs *DDR4MemAttributes) byte { + var bg byte + + switch memAttribs.PackageBusWidth { + 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 DDR4EncodeBankGroups(bg byte) byte { + var val byte + + switch bg { + case 2: + val = 1 + case 4: + val = 2 + } + + return val << 6 +} + +func DDR4EncodeDensityBanks(memAttribs *DDR4MemAttributes) byte { + var b byte + + b = DDR4DensityGbToSPDEncoding[memAttribs.CapacityPerDieGb] + b |= DDR4EncodeBankGroups(DDR4GetBankGroups(memAttribs)) + /* No need to encode banksPerGroup.it's always 4 ([4:5] = 0) */ + + return b +} + +func DDR4EncodeSdramAddressing(memAttribs *DDR4MemAttributes) byte { + var b byte + + b = DDR4DensityGbx8x16DieCapacityToRowColumnEncoding[memAttribs.CapacityPerDieGb] + + return b +} + +func DDR4EncodePackageDeviceType(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 DDR4EncodeSignalLoadingFromDieCount(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 DDR4EncodeDiesPerPackage(dies int) byte { + var b byte + + b = DDR4EncodePackageDeviceType(dies) /* Monolithic / Non-monolithic device */ + b |= (byte(dies) - 1) << 4 + + return b +} + +func DDR4EncodePackageType(memAttribs *DDR4MemAttributes) byte { + var b byte + + b = DDR4EncodeDiesPerPackage(memAttribs.DiesPerPackage) + b |= DDR4EncodeSignalLoadingFromDieCount(memAttribs.DiesPerPackage) + + return b +} + +func DDR4EncodeDataWidth(bitWidthPerDevice int) byte { + var width byte + + switch bitWidthPerDevice { + case 8: + width = 1 + case 16: + width = 2 + } + + return width +} + +func DDR4EncodeRanks(ranks int) byte { + var b byte + + b = byte(ranks - 1) + + return b << 3 +} + +func DDR4EncodeModuleOrganization(memAttribs *DDR4MemAttributes) byte { + var b byte + + b = DDR4EncodeDataWidth(memAttribs.dieBusWidth) + b |= DDR4EncodeRanks(memAttribs.RanksPerPackage) + + return b +} + +func DDR4EncodeTCKMin(memAttribs *DDR4MemAttributes) byte { + return convPsToMtbByte(memAttribs.TCKMinPs) +} + +func DDR4EncodeTCKMinFineOffset(memAttribs *DDR4MemAttributes) byte { + return convPsToFtbByte(memAttribs.TCKMinPs) +} + +func DDR4EncodeTCKMax(memAttribs *DDR4MemAttributes) byte { + return convPsToMtbByte(memAttribs.TCKMaxPs) +} + +func DDR4EncodeTCKMaxFineOffset(memAttribs *DDR4MemAttributes) byte { + return convPsToFtbByte(memAttribs.TCKMaxPs) +} + +func DDR4EncodeTAAMin(memAttribs *DDR4MemAttributes) byte { + return convPsToMtbByte(memAttribs.TAAMinPs) +} + +func DDR4EncodeTAAMinFineOffset(memAttribs *DDR4MemAttributes) byte { + return convPsToFtbByte(memAttribs.TAAMinPs) +} + +func DDR4EncodeTRCDMin(memAttribs *DDR4MemAttributes) byte { + return convPsToMtbByte(memAttribs.TRCDMinPs) +} + +func DDR4EncodeTRCDMinFineOffset(memAttribs *DDR4MemAttributes) byte { + return convPsToFtbByte(memAttribs.TRCDMinPs) +} + +func DDR4EncodeTRPMin(memAttribs *DDR4MemAttributes) byte { + return convPsToMtbByte(memAttribs.TRPMinPs) +} + +func DDR4EncodeTRCMinFineOffset(memAttribs *DDR4MemAttributes) byte { + return convPsToFtbByte(memAttribs.TRCMinPs) +} + +func DDR4EncodeTRPMinFineOffset(memAttribs *DDR4MemAttributes) byte { + return convPsToFtbByte(memAttribs.TRPMinPs) +} + +func DDR4EncodeTRASRCMinMSNs(memAttribs *DDR4MemAttributes) byte { + var b byte + + b = byte((convPsToMtb(memAttribs.TRASMinPs) >> 4) & 0xf0) + b |= byte((convPsToMtb(memAttribs.TRCMinPs) >> 8) & 0x0f) + + return b +} + +func DDR4EncodeTRASMinLsb(memAttribs *DDR4MemAttributes) byte { + return byte(convPsToMtb(memAttribs.TRASMinPs) & 0xff) +} + +func DDR4EncodeTRCMinLsb(memAttribs *DDR4MemAttributes) byte { + return byte(convPsToMtb(memAttribs.TRCMinPs) & 0xff) +} + +func DDR4EncodeTRFC1MinLsb(memAttribs *DDR4MemAttributes) byte { + var mtb int + + mtb = convPsToMtb(memAttribs.TRFC1MinPs) + + return byte(mtb & 0xff) +} + +func DDR4EncodeTRFC1MinMsb(memAttribs *DDR4MemAttributes) byte { + var mtb int + + mtb = convPsToMtb(memAttribs.TRFC1MinPs) + + return byte((mtb >> 8) & 0xff) +} + +func DDR4EncodeTRFC2MinLsb(memAttribs *DDR4MemAttributes) byte { + var mtb int + + mtb = convPsToMtb(memAttribs.TRFC2MinPs) + + return byte(mtb & 0xff) +} + +func DDR4EncodeTRFC2MinMsb(memAttribs *DDR4MemAttributes) byte { + var mtb int + + mtb = convPsToMtb(memAttribs.TRFC2MinPs) + + return byte((mtb >> 8) & 0xff) +} + +func DDR4EncodeTRFC4MinLsb(memAttribs *DDR4MemAttributes) byte { + var mtb int + + mtb = convPsToMtb(memAttribs.TRFC4MinPs) + + return byte(mtb & 0xff) +} + +func DDR4EncodeTRFC4MinMsb(memAttribs *DDR4MemAttributes) byte { + var mtb int + + mtb = convPsToMtb(memAttribs.TRFC4MinPs) + + return byte((mtb >> 8) & 0xff) +} + +func DDR4EncodeTFAWMinMSN(memAttribs *DDR4MemAttributes) byte { + var mtb int + + mtb = convPsToMtb(memAttribs.TFAWMinPs) + + return byte((mtb >> 8) & 0x0f) +} + +func DDR4EncodeTFAWMinLsb(memAttribs *DDR4MemAttributes) byte { + var mtb int + + mtb = convPsToMtb(memAttribs.TFAWMinPs) + + return byte(mtb & 0xff) +} + +func DDR4EncodeCASFirstByte(memAttribs *DDR4MemAttributes) byte { + return memAttribs.CASFirstByte +} + +func DDR4EncodeCASSecondByte(memAttribs *DDR4MemAttributes) byte { + return memAttribs.CASSecondByte +} + +func DDR4EncodeCASThirdByte(memAttribs *DDR4MemAttributes) byte { + return memAttribs.CASThirdByte +} + +func DDR4EncodeCASFourthByte(memAttribs *DDR4MemAttributes) byte { + return memAttribs.CASFourthByte +} + +func DDR4EncodeTRRDSMin(memAttribs *DDR4MemAttributes) byte { + return convPsToMtbByte(memAttribs.TRRDSMinPs) +} + +func DDR4EncodeTRRDSMinFineOffset(memAttribs *DDR4MemAttributes) byte { + return convPsToFtbByte(memAttribs.TRRDSMinPs) +} + +func DDR4EncodeTRRDLMin(memAttribs *DDR4MemAttributes) byte { + return convPsToMtbByte(memAttribs.TRRDLMinPs) +} + +func DDR4EncodeTRRDLMinFineOffset(memAttribs *DDR4MemAttributes) byte { + return convPsToFtbByte(memAttribs.TRRDLMinPs) +} + +func DDR4EncodeTCCDLMin(memAttribs *DDR4MemAttributes) byte { + return convPsToMtbByte(memAttribs.TCCDLMinPs) +} + +func DDR4EncodeTCCDLMinFineOffset(memAttribs *DDR4MemAttributes) byte { + return convPsToFtbByte(memAttribs.TCCDLMinPs) +} + +func DDR4EncodeTWRMinMSN(memAttribs *DDR4MemAttributes) byte { + return byte((convPsToMtb(DDR4TimingValueTWRMinPs) >> 8) & 0x0f) +} + +func DDR4EncodeTWRMinLsb(memAttribs *DDR4MemAttributes) byte { + return byte(convPsToMtb(DDR4TimingValueTWRMinPs) & 0xff) +} + +func DDR4EncodeTWTRMinMSNs(memAttribs *DDR4MemAttributes) byte { + var b byte + + b = byte((convPsToMtb(memAttribs.TWTRLMinPs) >> 4) & 0xf0) + b |= byte((convPsToMtb(memAttribs.TWTRSMinPs) >> 8) & 0x0f) + + return b +} + +func DDR4EncodeTWTRSMinLsb(memAttribs *DDR4MemAttributes) byte { + return byte(convPsToMtb(memAttribs.TWTRSMinPs) & 0xff) +} + +func DDR4EncodeTWTRLMinLsb(memAttribs *DDR4MemAttributes) byte { + return byte(convPsToMtb(memAttribs.TWTRLMinPs) & 0xff) +} + +func DDR4EncodeLatencies(latency int, memAttribs *DDR4MemAttributes) error { + switch latency { + case 9: + memAttribs.CASFirstByte |= DDR4CAS9 + case 10: + memAttribs.CASFirstByte |= DDR4CAS10 + case 11: + memAttribs.CASFirstByte |= DDR4CAS11 + case 12: + memAttribs.CASFirstByte |= DDR4CAS12 + case 13: + memAttribs.CASFirstByte |= DDR4CAS13 + case 14: + memAttribs.CASFirstByte |= DDR4CAS14 + case 15: + memAttribs.CASSecondByte |= DDR4CAS15 + case 16: + memAttribs.CASSecondByte |= DDR4CAS16 + case 17: + memAttribs.CASSecondByte |= DDR4CAS17 + case 18: + memAttribs.CASSecondByte |= DDR4CAS18 + case 19: + memAttribs.CASSecondByte |= DDR4CAS19 + case 20: + memAttribs.CASSecondByte |= DDR4CAS20 + case 21: + memAttribs.CASSecondByte |= DDR4CAS21 + case 22: + memAttribs.CASSecondByte |= DDR4CAS22 + case 24: + memAttribs.CASThirdByte |= DDR4CAS24 + default: + fmt.Errorf("Incorrect CAS Latency: ", latency) + } + + return nil +} + +/* Default CAS Latencies from Speed Bin tables in JEDS79-4C */ +func DDR4GetDefaultCASLatencies(memAttribs *DDR4MemAttributes) 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 DDR4UpdateDieBusWidth(memAttribs *DDR4MemAttributes) { + if memAttribs.PackageBusWidth == 16 && memAttribs.RanksPerPackage == 1 && + memAttribs.DiesPerPackage == 2 { + /* + * If a x16 part has 2 die with single rank, PackageBusWidth + * needs to be converted to match die bus width. + */ + memAttribs.dieBusWidth = 8 + } else { + memAttribs.dieBusWidth = memAttribs.PackageBusWidth + } +} + +func DDR4UpdateCAS(memAttribs *DDR4MemAttributes) error { + if len(memAttribs.CASLatencies) == 0 { + memAttribs.CASLatencies = DDR4GetDefaultCASLatencies(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 := DDR4EncodeLatencies(latency, memAttribs); err != nil { + return err + } + } + + return nil +} + +func DDR4GetTAAMinPs(memAttribs *DDR4MemAttributes) int { + return (memAttribs.CL_nRCD_nRP * 2000000) / memAttribs.SpeedMTps +} + +func DDR4UpdateTAAMin(memAttribs *DDR4MemAttributes) { + if memAttribs.TAAMinPs == 0 { + memAttribs.TAAMinPs = DDR4GetTAAMinPs(memAttribs) + } +} + +func DDR4UpdateTRCDMin(memAttribs *DDR4MemAttributes) { + /* tRCDmin is same as tAAmin for all cases */ + if memAttribs.TRCDMinPs == 0 { + memAttribs.TRCDMinPs = DDR4GetTAAMinPs(memAttribs) + } +} + +func DDR4UpdateTRPMin(memAttribs *DDR4MemAttributes) { + /* tRPmin is same as tAAmin for all cases */ + if memAttribs.TRPMinPs == 0 { + memAttribs.TRPMinPs = DDR4GetTAAMinPs(memAttribs) + } +} + +func DDR4UpdateTRASMin(memAttribs *DDR4MemAttributes) { + if memAttribs.TRASMinPs == 0 { + memAttribs.TRASMinPs = DDR4SpeedBinToSPDEncoding[memAttribs.SpeedMTps].TRASMinPs + } +} + +func DDR4GetTRCMinPs(memAttribs *DDR4MemAttributes) int { + return memAttribs.TAAMinPs + memAttribs.TRASMinPs +} + +func DDR4UpdateTRCMin(memAttribs *DDR4MemAttributes) { + if memAttribs.TRCMinPs == 0 { + memAttribs.TRCMinPs = DDR4GetTRCMinPs(memAttribs) + } +} + +func DDR4GetDefaultTCKMinPs(memAttribs *DDR4MemAttributes) int { + /* value 2000000 = 2 * 1000000, where 1000000 is to convert mS to pS */ + return 2000000 / memAttribs.SpeedMTps +} + +func DDR4UpdateTCK(memAttribs *DDR4MemAttributes) { + if memAttribs.TCKMinPs == 0 { + memAttribs.TCKMinPs = DDR4GetDefaultTCKMinPs(memAttribs) + } + if memAttribs.TCKMaxPs == 0 { + memAttribs.TCKMaxPs = DDR4SpeedBinToSPDEncoding[memAttribs.SpeedMTps].TCKMaxPs + } +} + +func DDR4UpdateTWRMin(memAttribs *DDR4MemAttributes) { + if memAttribs.TWRMinPs == 0 { + memAttribs.TWRMinPs = DDR4TimingValueTWRMinPs + } +} + +func DDR4UpdateTWTRMin(memAttribs *DDR4MemAttributes) { + if memAttribs.TWTRLMinPs == 0 { + memAttribs.TWTRLMinPs = DDR4TimingValueTWTRLMinPs + } + if memAttribs.TWTRSMinPs == 0 { + memAttribs.TWTRSMinPs = DDR4TimingValueTWTRSMinPs + } +} + +/* + * Per Table 169 & Table 170 of Jedec JESD79-4C + * tFAW timing is based on : + * Speed bin and page size + */ +func DDR4GetTFAWMinPs(memAttribs *DDR4MemAttributes) int { + var tFAWFixed int + + if DDR4PageSizefromBusWidthEncoding[memAttribs.PackageBusWidth] == 1 { + switch memAttribs.SpeedMTps { + case 1600: + tFAWFixed = 25000 + case 1866: + tFAWFixed = 23000 + default: + tFAWFixed = 21000 + } + } else if DDR4PageSizefromBusWidthEncoding[memAttribs.PackageBusWidth] == 2 { + switch memAttribs.SpeedMTps { + case 1600: + tFAWFixed = 35000 + default: + tFAWFixed = 30000 + } + } + + return tFAWFixed +} + +/* Update settings based on data sheet (json) supplied memory attributes */ + +func DDR4UpdateTFAWMin(memAttribs *DDR4MemAttributes) { + var tFAWFromTck int + + if memAttribs.TFAWMinPs == 0 { + memAttribs.TFAWMinPs = DDR4GetTFAWMinPs(memAttribs) + } + + switch DDR4PageSizefromBusWidthEncoding[memAttribs.PackageBusWidth] { + case 1: + tFAWFromTck = 20 * memAttribs.TCKMinPs + case 2: + tFAWFromTck = 28 * memAttribs.TCKMinPs + } + + if memAttribs.TFAWMinPs < tFAWFromTck { + memAttribs.TFAWMinPs = tFAWFromTck + } +} + +func DDR4UpdateTRFC1Min(memAttribs *DDR4MemAttributes) { + if memAttribs.TRFC1MinPs == 0 { + memAttribs.TRFC1MinPs = DDR4TRFC1Encoding[memAttribs.CapacityPerDieGb] + } +} + +func DDR4UpdateTRFC2Min(memAttribs *DDR4MemAttributes) { + if memAttribs.TRFC2MinPs == 0 { + memAttribs.TRFC2MinPs = DDR4TRFC2Encoding[memAttribs.CapacityPerDieGb] + } +} + +func DDR4UpdateTRFC4Min(memAttribs *DDR4MemAttributes) { + if memAttribs.TRFC4MinPs == 0 { + memAttribs.TRFC4MinPs = DDR4TRFC4Encoding[memAttribs.CapacityPerDieGb] + } +} + +func DDR4GetTRRDLMinPs(memAttribs *DDR4MemAttributes) int { + var tRRDLFixed int + + /* + * Per JESD79-4C Tables 169 & 170, tRRD_L is based on : + * Speed bin and page size + */ + switch DDR4PageSizefromBusWidthEncoding[memAttribs.PackageBusWidth] { + 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 DDR4UpdateTRRDLMin(memAttribs *DDR4MemAttributes) { + var tRRDLFromTck int + + if memAttribs.TRRDLMinPs == 0 { + memAttribs.TRRDLMinPs = DDR4GetTRRDLMinPs(memAttribs) + } + + tRRDLFromTck = 4 * memAttribs.TCKMinPs + + if memAttribs.TRRDLMinPs < tRRDLFromTck { + memAttribs.TRRDLMinPs = tRRDLFromTck + } +} + +func DDR4GetTRRDSMinPs(memAttribs *DDR4MemAttributes) int { + var tRRDFixed int + + switch DDR4PageSizefromBusWidthEncoding[memAttribs.PackageBusWidth] { + case 1: + tRRDFixed = DDR4SpeedToTRRDSMinPsOneKPageSize[memAttribs.SpeedMTps] + case 2: + tRRDFixed = DDR4SpeedToTRRDSMinPsTwoKPageSize[memAttribs.SpeedMTps] + } + + return tRRDFixed +} + +func DDR4UpdateTRRDSMin(memAttribs *DDR4MemAttributes) { + var tRRDFromTck int + + if memAttribs.TRRDSMinPs == 0 { + memAttribs.TRRDSMinPs = DDR4GetTRRDSMinPs(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 DDR4GetTCCDLMinPs(memAttribs *DDR4MemAttributes) 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 DDR4UpdateTCCDLMin(memAttribs *DDR4MemAttributes) { + var tCCDLFromTck int + + if memAttribs.TCCDLMinPs == 0 { + memAttribs.TCCDLMinPs = DDR4GetTCCDLMinPs(memAttribs) + } + + tCCDLFromTck = 5 * memAttribs.TCKMinPs + + if memAttribs.TCCDLMinPs < tCCDLFromTck { + memAttribs.TCCDLMinPs = tCCDLFromTck + } +} + +func DDR4UpdateMemoryAttributes(memAttribs *DDR4MemAttributes) { + DDR4UpdateDieBusWidth(memAttribs) + DDR4UpdateTCK(memAttribs) + DDR4UpdateTAAMin(memAttribs) + DDR4UpdateTRCDMin(memAttribs) + DDR4UpdateTRPMin(memAttribs) + DDR4UpdateTRASMin(memAttribs) + DDR4UpdateTRCMin(memAttribs) + DDR4UpdateTWRMin(memAttribs) + DDR4UpdateTWTRMin(memAttribs) + DDR4UpdateCAS(memAttribs) + DDR4UpdateTRFC1Min(memAttribs) + DDR4UpdateTRFC2Min(memAttribs) + DDR4UpdateTRFC4Min(memAttribs) + DDR4UpdateTCCDLMin(memAttribs) + DDR4UpdateTRRDSMin(memAttribs) + DDR4UpdateTRRDLMin(memAttribs) + DDR4UpdateTFAWMin(memAttribs) +} + +func DDR4ValidateSpeedMTps(speedBin int) error { + if _, ok := DDR4SpeedBinToSPDEncoding[speedBin]; ok == false { + return fmt.Errorf("Incorrect speed bin: DDR4-%d", speedBin) + } + return nil +} + +func DDR4ValidateCapacityPerDie(capacityPerDieGb int) error { + if _, ok := DDR4DensityGbToSPDEncoding[capacityPerDieGb]; ok == false { + return fmt.Errorf("Incorrect capacity per die: %d", capacityPerDieGb) + } + return nil +} + +func DDR4ValidateDiesPerPackage(dieCount int) error { + if dieCount >= 1 && dieCount <= 2 { + return nil + } + return fmt.Errorf("Incorrect dies per package count: %d", dieCount) +} + +func DDR4ValidatePackageBusWidth(width int) error { + if width != 8 && width != 16 { + return fmt.Errorf("Incorrect device bus width: %d", width) + } + return nil +} + +func DDR4ValidateRanksPerPackage(ranks int) error { + if ranks >= 1 && ranks <= 2 { + return nil + } + return fmt.Errorf("Incorrect package ranks: %d", ranks) +} + +func DDR4ValidateCASLatency(CL int) error { + if CL >= 10 && CL <= 24 && CL != 23 { + return nil + } + return fmt.Errorf("Incorrect CAS latency: %d", CL) +} + +func DDR4VerifySupportedCASLatencies(name string, memAttribs *DDR4MemAttributes) error { + if memAttribs.CASLatencies == DDR4GetDefaultCASLatencies(memAttribs) { + 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", name, name) + } + + return nil +} + +func DDR4ValidateMemPartAttributes(name string, memAttribs *DDR4MemAttributes) error { + if err := DDR4ValidateSpeedMTps(memAttribs.SpeedMTps); err != nil { + return err + } + if err := DDR4ValidateCapacityPerDie(memAttribs.CapacityPerDieGb); err != nil { + return err + } + if err := DDR4ValidateDiesPerPackage(memAttribs.DiesPerPackage); err != nil { + return err + } + if err := DDR4ValidatePackageBusWidth(memAttribs.PackageBusWidth); err != nil { + return err + } + if err := DDR4ValidateRanksPerPackage(memAttribs.RanksPerPackage); err != nil { + return err + } + if err := DDR4ValidateCASLatency(memAttribs.CL_nRCD_nRP); err != nil { + return err + } + + /* If CAS Latency was supplied, make sure it doesn't match default value */ + if len(memAttribs.CASLatencies) != 0 { + if err := DDR4VerifySupportedCASLatencies(name, memAttribs); err != nil { + return err + } + } + + return nil +} + +func DDR4IsManufacturerPartNumberByte(index int) bool { + if index >= DDR4SPDIndexManufacturerPartNumberStartByte && index <= DDR4SPDIndexManufacturerPartNumberEndByte { + return true + } + return false +} + +/* ------------------------------------------------------------------------------------------ */ +/* Interface Functions */ +/* ------------------------------------------------------------------------------------------ */ + +func (ddr4) getSetMap() map[int][]int { + return DDR4PlatformSetMap +} + +func (ddr4) addNewPart(name string, attribs interface{}) error { + var ddr4MemAttribs DDR4MemAttributes + eByte, err := json.Marshal(attribs) + if err != nil { + return err + } + + if err := json.Unmarshal(eByte, &ddr4MemAttribs); err != nil { + return err + } + + if err := DDR4ValidateMemPartAttributes(name, &ddr4MemAttribs); err != nil { + return err + } + + DDR4PartAttributeMap[name] = ddr4MemAttribs + return nil +} + +func (ddr4) getSPDAttribs(name string, set int) (interface{}, error) { + ddr4Attributes := DDR4PartAttributeMap[name] + + DDR4CurrSet = set + + DDR4UpdateMemoryAttributes(&ddr4Attributes) + + return ddr4Attributes, nil +} + +func (ddr4) getSPDLen() int { + return 512 +} + +func (ddr4) getSPDByte(index int, attribs interface{}) byte { + e, ok := DDR4SPDAttribTable[index] + if ok == false { + if DDR4IsManufacturerPartNumberByte(index) { + return DDR4SPDValueManufacturerPartNumberBlank + } + return 0x00 + } + + if e.getVal != nil { + var ddr4Attribs DDR4MemAttributes + ddr4Attribs = attribs.(DDR4MemAttributes) + return e.getVal(&ddr4Attribs) + + } + + return e.constVal +} diff --git a/util/spd_tools/src/spd_gen/lp4x.go b/util/spd_tools/src/spd_gen/lp4x.go new file mode 100644 index 0000000000..5257bcb92a --- /dev/null +++ b/util/spd_tools/src/spd_gen/lp4x.go @@ -0,0 +1,906 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +package main + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" +) + +/* ------------------------------------------------------------------------------------------ */ +/* LP4x-defined types */ +/* ------------------------------------------------------------------------------------------ */ + +type lp4x struct { +} + +type LP4xMemAttributes 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 +} + +type LP4xSpeedParams struct { + TCKMinPs int + TCKMaxPs int + CASLatenciesx16Channel string + CASLatenciesx8Channel string +} + +type LP4xRefreshTimings struct { + TRFCABNs int + TRFCPBNs int +} + +type LP4xSPDAttribFunc func(*LP4xMemAttributes) byte + +type LP4xSPDAttribTableEntry struct { + constVal byte + getVal LP4xSPDAttribFunc +} + +type LP4xSetFunc func(*LP4xMemAttributes) int + +type LP4xSet struct { + getMRCDensity LP4xSetFunc + getDiesPerPackage LP4xSetFunc + busWidthEncoding byte + normalizeAttribs LP4xSetFunc +} + +/* ------------------------------------------------------------------------------------------ */ +/* Constants */ +/* ------------------------------------------------------------------------------------------ */ + +const ( + /* SPD Byte Index */ + LP4xSPDIndexSize = 0 + LP4xSPDIndexRevision = 1 + LP4xSPDIndexMemoryType = 2 + LP4xSPDIndexModuleType = 3 + LP4xSPDIndexDensityBanks = 4 + LP4xSPDIndexAddressing = 5 + LP4xSPDIndexPackageType = 6 + LP4xSPDIndexOptionalFeatures = 7 + LP4xSPDIndexModuleOrganization = 12 + LP4xSPDIndexBusWidth = 13 + LP4xSPDIndexTimebases = 17 + LP4xSPDIndexTCKMin = 18 + LP4xSPDIndexTCKMax = 19 + LP4xSPDIndexCASFirstByte = 20 + LP4xSPDIndexCASSecondByte = 21 + LP4xSPDIndexCASThirdByte = 22 + LP4xSPDIndexCASFourthByte = 23 + LP4xSPDIndexTAAMin = 24 + LP4xSPDIndexReadWriteLatency = 25 + LP4xSPDIndexTRCDMin = 26 + LP4xSPDIndexTRPABMin = 27 + LP4xSPDIndexTRPPBMin = 28 + LP4xSPDIndexTRFCABMinLSB = 29 + LP4xSPDIndexTRFCABMinMSB = 30 + LP4xSPDIndexTRFCPBMinLSB = 31 + LP4xSPDIndexTRFCPBMinMSB = 32 + LP4xSPDIndexTRPPBMinFineOffset = 120 + LP4xSPDIndexTRPABMinFineOffset = 121 + LP4xSPDIndexTRCDMinFineOffset = 122 + LP4xSPDIndexTAAMinFineOffset = 123 + LP4xSPDIndexTCKMaxFineOffset = 124 + LP4xSPDIndexTCKMinFineOffset = 125 + LP4xSPDIndexManufacturerPartNumberStartByte = 329 + LP4xSPDIndexManufacturerPartNumberEndByte = 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. + */ + LP4xSPDValueSize = 0x23 + + /* + * From JEDEC spec: Revision 1.1 + * Set to 0x11. + */ + LP4xSPDValueRevision = 0x11 + + /* LPDDR4x memory type = 0x11 */ + LP4xSPDValueMemoryType = 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. + */ + LP4xSPDValueModuleType = 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 + */ + LP4xSPDValueOptionalFeatures = 0x08 + + /* + * From JEDEC spec: + * 3:2 (MTB) = 00 (0.125ns) + * 1:0 (FTB) = 00 (1ps) + * Set to 0x00. + */ + LP4xSPDValueTimebases = 0x00 + + /* CAS fourth byte: All bits are reserved */ + LP4xSPDValueCASFourthByte = 0x00 + + /* Write Latency Set A and Read Latency DBI-RD disabled. */ + LP4xSPDValueReadWriteLatency = 0x00 + + /* As per JEDEC spec, unused digits of manufacturer part number are left as blank. */ + LP4xSPDValueManufacturerPartNumberBlank = 0x20 +) + +const ( + /* First Byte */ + LP4xCAS6 = 1 << 1 + LP4xCAS10 = 1 << 4 + LP4xCAS14 = 1 << 7 + /* Second Byte */ + LP4xCAS16 = 1 << 0 + LP4xCAS20 = 1 << 2 + LP4xCAS22 = 1 << 3 + LP4xCAS24 = 1 << 4 + LP4xCAS26 = 1 << 5 + LP4xCAS28 = 1 << 6 + /* Third Byte */ + LP4xCAS32 = 1 << 0 + LP4xCAS36 = 1 << 2 + LP4xCAS40 = 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. + */ + LP4xTCKMaxPsDefault = 31875 +) + +/* ------------------------------------------------------------------------------------------ */ +/* Global variables */ +/* ------------------------------------------------------------------------------------------ */ + +var LP4xPlatformSetMap = map[int][]int{ + 0: {PlatformTGL, PlatformADL}, + 1: {PlatformJSL, PlatformCZN}, +} + +var LP4xSetInfo = map[int]LP4xSet{ + 0: { + getMRCDensity: LP4xGetMRCDensitySet0, + getDiesPerPackage: LP4xGetDiesPerPackageSet0, + /* + * As per advisory 616599: + * 7:5 (Number of system channels) = 000 (1 channel always) + * 2:0 (Bus width) = 001 (x16 always) + * Set to 0x01. + */ + busWidthEncoding: 0x01, + normalizeAttribs: LP4xNormalizeAttribsSet0, + }, + 1: { + getMRCDensity: LP4xGetMRCDensitySet1, + getDiesPerPackage: LP4xGetDiesPerPackageSet1, + /* + * As per advisory 610202: + * 7:5 (Number of system channels) = 001 (2 channel always) + * 2:0 (Bus width) = 010 (x32 always) + * Set to 0x22. + */ + busWidthEncoding: 0x22, + }, +} + +var LP4xPartAttributeMap = map[string]LP4xMemAttributes{} +var LP4xCurrSet int + +/* This encodes the density in Gb to SPD values as per JESD 21-C */ +var LP4xDensityGbToSPDEncoding = map[int]byte{ + 4: 0x4, + 6: 0xb, + 8: 0x5, + 12: 0x8, + 16: 0x6, + 24: 0x9, + 32: 0x7, +} + +/* + * 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 LP4xDensityGbx8ChannelToRowColumnEncoding = 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 */ +} + +/* + * 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 LP4xDensityGbx16ChannelToRowColumnEncoding = 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 112 from JESD209-4C + * Maps density per physical channel to refresh timings. This is the same for x8 and x16 + * devices. + */ +var LP4xDensityGbPhysicalChannelToRefreshEncoding = map[int]LP4xRefreshTimings{ + 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, + }, +} + +var LP4xBankEncoding = map[int]byte{ + 4: 0 << 4, + 8: 1 << 4, +} + +var LP4xSpeedMbpsToSPDEncoding = map[int]LP4xSpeedParams{ + 4267: { + TCKMinPs: 468, /* 1/4267 * 2 */ + TCKMaxPs: LP4xTCKMaxPsDefault, + 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: LP4xTCKMaxPsDefault, + CASLatenciesx16Channel: "6 10 14 20 24 28 32", + CASLatenciesx8Channel: "6 10 16 22 26 32 36", + }, + 3200: { + TCKMinPs: 625, /* 1/3200 * 2 */ + TCKMaxPs: LP4xTCKMaxPsDefault, + CASLatenciesx16Channel: "6 10 14 20 24 28", + CASLatenciesx8Channel: "6 10 16 22 26 32", + }, +} + +var LP4xSPDAttribTable = map[int]LP4xSPDAttribTableEntry{ + LP4xSPDIndexSize: {constVal: LP4xSPDValueSize}, + LP4xSPDIndexRevision: {constVal: LP4xSPDValueRevision}, + LP4xSPDIndexMemoryType: {constVal: LP4xSPDValueMemoryType}, + LP4xSPDIndexModuleType: {constVal: LP4xSPDValueModuleType}, + LP4xSPDIndexDensityBanks: {getVal: LP4xEncodeDensityBanks}, + LP4xSPDIndexAddressing: {getVal: LP4xEncodeSdramAddressing}, + LP4xSPDIndexPackageType: {getVal: LP4xEncodePackageType}, + LP4xSPDIndexOptionalFeatures: {constVal: LP4xSPDValueOptionalFeatures}, + LP4xSPDIndexModuleOrganization: {getVal: LP4xEncodeModuleOrganization}, + LP4xSPDIndexBusWidth: {getVal: LP4xEncodeBusWidth}, + LP4xSPDIndexTimebases: {constVal: LP4xSPDValueTimebases}, + LP4xSPDIndexTCKMin: {getVal: LP4xEncodeTCKMin}, + LP4xSPDIndexTCKMax: {getVal: LP4xEncodeTCKMax}, + LP4xSPDIndexTCKMaxFineOffset: {getVal: LP4xEncodeTCKMaxFineOffset}, + LP4xSPDIndexTCKMinFineOffset: {getVal: LP4xEncodeTCKMinFineOffset}, + LP4xSPDIndexCASFirstByte: {getVal: LP4xEncodeCASFirstByte}, + LP4xSPDIndexCASSecondByte: {getVal: LP4xEncodeCASSecondByte}, + LP4xSPDIndexCASThirdByte: {getVal: LP4xEncodeCASThirdByte}, + LP4xSPDIndexCASFourthByte: {constVal: LP4xSPDValueCASFourthByte}, + LP4xSPDIndexTAAMin: {getVal: LP4xEncodeTAAMin}, + LP4xSPDIndexTAAMinFineOffset: {getVal: LP4xEncodeTAAMinFineOffset}, + LP4xSPDIndexReadWriteLatency: {constVal: LP4xSPDValueReadWriteLatency}, + LP4xSPDIndexTRCDMin: {getVal: LP4xEncodeTRCDMin}, + LP4xSPDIndexTRCDMinFineOffset: {getVal: LP4xEncodeTRCDMinFineOffset}, + LP4xSPDIndexTRPABMin: {getVal: LP4xEncodeTRPABMin}, + LP4xSPDIndexTRPABMinFineOffset: {getVal: LP4xEncodeTRPABMinFineOffset}, + LP4xSPDIndexTRPPBMin: {getVal: LP4xEncodeTRPPBMin}, + LP4xSPDIndexTRPPBMinFineOffset: {getVal: LP4xEncodeTRPPBMinFineOffset}, + LP4xSPDIndexTRFCABMinLSB: {getVal: LP4xEncodeTRFCABMinLsb}, + LP4xSPDIndexTRFCABMinMSB: {getVal: LP4xEncodeTRFCABMinMsb}, + LP4xSPDIndexTRFCPBMinLSB: {getVal: LP4xEncodeTRFCPBMinLsb}, + LP4xSPDIndexTRFCPBMinMSB: {getVal: LP4xEncodeTRFCPBMinMsb}, +} + +/* ------------------------------------------------------------------------------------------ */ +/* Functions */ +/* ------------------------------------------------------------------------------------------ */ + +func LP4xGetMRCDensitySet0(memAttribs *LP4xMemAttributes) int { + /* + * Intel MRC on TGL/ADL expects density per logical channel to be encoded in + * SPDIndexDensityBanks. Logical channel on TGL/ADL is an x16 channel. + */ + return memAttribs.DensityPerChannelGb * 16 / memAttribs.BitWidthPerChannel +} + +func LP4xGetMRCDensitySet1(memAttribs *LP4xMemAttributes) int { + /* + * Intel MRC on JSL expects density per die to be encoded in + * SPDIndexDensityBanks. + */ + return memAttribs.DensityPerChannelGb * memAttribs.ChannelsPerDie +} + +func LP4xGetMRCDensity(memAttribs *LP4xMemAttributes) int { + f, ok := LP4xSetInfo[LP4xCurrSet] + + if ok == false || f.getMRCDensity == nil { + return 0 + } + + return f.getMRCDensity(memAttribs) +} + +func LP4xEncodeDensityBanks(memAttribs *LP4xMemAttributes) byte { + var b byte + + b = LP4xDensityGbToSPDEncoding[LP4xGetMRCDensity(memAttribs)] + b |= LP4xBankEncoding[memAttribs.Banks] + + return b +} + +func LP4xEncodeSdramAddressing(memAttribs *LP4xMemAttributes) byte { + densityPerChannelGb := memAttribs.DensityPerChannelGb + if memAttribs.BitWidthPerChannel == 8 { + return LP4xDensityGbx8ChannelToRowColumnEncoding[densityPerChannelGb] + } else { + return LP4xDensityGbx16ChannelToRowColumnEncoding[densityPerChannelGb] + } + return 0 +} + +func LP4xEncodePackage(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 LP4xEncodeChannelsPerDie(channels int) byte { + var temp byte + + temp = byte(channels >> 1) + + return temp << 2 +} + +func LP4xGetDiesPerPackageSet0(memAttribs *LP4xMemAttributes) int { + /* Intel MRC expects logical dies to be encoded for TGL/ADL. */ + return memAttribs.ChannelsPerDie * memAttribs.RanksPerChannel * memAttribs.BitWidthPerChannel / 16 +} + +func LP4xGetDiesPerPackageSet1(memAttribs *LP4xMemAttributes) int { + /* Intel MRC expects physical dies to be encoded for JSL. */ + /* AMD PSP expects physical dies (ZQ balls) */ + return memAttribs.DiesPerPackage +} + +/* Per JESD209-4C Dies = ZQ balls on the package */ +/* Note that this can be different than the part's die count */ +func LP4xEncodeDiesPerPackage(memAttribs *LP4xMemAttributes) byte { + var dies int = 0 + + f, ok := LP4xSetInfo[LP4xCurrSet] + if ok != false && f.getDiesPerPackage != nil { + dies = f.getDiesPerPackage(memAttribs) + } + + b := LP4xEncodePackage(dies) /* Monolithic / Non-monolithic device */ + b |= (byte(dies) - 1) << 4 + + return b +} + +func LP4xEncodePackageType(memAttribs *LP4xMemAttributes) byte { + var b byte + + b |= LP4xEncodeChannelsPerDie(memAttribs.ChannelsPerDie) + b |= LP4xEncodeDiesPerPackage(memAttribs) + + return b +} + +func LP4xEncodeDataWidth(bitWidthPerChannel int) byte { + return byte(bitWidthPerChannel / 8) +} + +func LP4xEncodeRanks(ranks int) byte { + var b byte + b = byte(ranks - 1) + return b << 3 +} + +func LP4xEncodeModuleOrganization(memAttribs *LP4xMemAttributes) byte { + var b byte + + b = LP4xEncodeDataWidth(memAttribs.BitWidthPerChannel) + b |= LP4xEncodeRanks(memAttribs.RanksPerChannel) + + return b +} + +func LP4xEncodeBusWidth(memAttribs *LP4xMemAttributes) byte { + f, ok := LP4xSetInfo[LP4xCurrSet] + + if ok == false { + return 0 + } + + return f.busWidthEncoding +} + +func LP4xEncodeTCKMin(memAttribs *LP4xMemAttributes) byte { + return convPsToMtbByte(memAttribs.TCKMinPs) +} + +func LP4xEncodeTCKMinFineOffset(memAttribs *LP4xMemAttributes) byte { + return convPsToFtbByte(memAttribs.TCKMinPs) +} + +func LP4xEncodeTCKMax(memAttribs *LP4xMemAttributes) byte { + return convPsToMtbByte(memAttribs.TCKMaxPs) +} + +func LP4xEncodeTCKMaxFineOffset(memAttribs *LP4xMemAttributes) byte { + return convPsToFtbByte(memAttribs.TCKMaxPs) +} + +func LP4xEncodeCASFirstByte(memAttribs *LP4xMemAttributes) byte { + return memAttribs.CASFirstByte +} + +func LP4xEncodeCASSecondByte(memAttribs *LP4xMemAttributes) byte { + return memAttribs.CASSecondByte +} + +func LP4xEncodeCASThirdByte(memAttribs *LP4xMemAttributes) byte { + return memAttribs.CASThirdByte +} + +func LP4xEncodeTAAMin(memAttribs *LP4xMemAttributes) byte { + return convPsToMtbByte(memAttribs.TAAMinPs) +} + +func LP4xEncodeTAAMinFineOffset(memAttribs *LP4xMemAttributes) byte { + return convPsToFtbByte(memAttribs.TAAMinPs) +} + +func LP4xEncodeTRCDMin(memAttribs *LP4xMemAttributes) byte { + return convNsToMtbByte(memAttribs.TRCDMinNs) +} + +func LP4xEncodeTRCDMinFineOffset(memAttribs *LP4xMemAttributes) byte { + return convNsToFtbByte(memAttribs.TRCDMinNs) +} + +func LP4xEncodeTRPABMin(memAttribs *LP4xMemAttributes) byte { + return convNsToMtbByte(memAttribs.TRPABMinNs) +} + +func LP4xEncodeTRPABMinFineOffset(memAttribs *LP4xMemAttributes) byte { + return convNsToFtbByte(memAttribs.TRPABMinNs) +} + +func LP4xEncodeTRPPBMin(memAttribs *LP4xMemAttributes) byte { + return convNsToMtbByte(memAttribs.TRPPBMinNs) +} + +func LP4xEncodeTRPPBMinFineOffset(memAttribs *LP4xMemAttributes) byte { + return convNsToFtbByte(memAttribs.TRPPBMinNs) +} + +func LP4xEncodeTRFCABMinMsb(memAttribs *LP4xMemAttributes) byte { + return byte((convNsToMtb(memAttribs.TRFCABNs) >> 8) & 0xff) +} + +func LP4xEncodeTRFCABMinLsb(memAttribs *LP4xMemAttributes) byte { + return byte(convNsToMtb(memAttribs.TRFCABNs) & 0xff) +} + +func LP4xEncodeTRFCPBMinMsb(memAttribs *LP4xMemAttributes) byte { + return byte((convNsToMtb(memAttribs.TRFCPBNs) >> 8) & 0xff) +} + +func LP4xEncodeTRFCPBMinLsb(memAttribs *LP4xMemAttributes) byte { + return byte(convNsToMtb(memAttribs.TRFCPBNs) & 0xff) +} + +func LP4xEncodeLatencies(latency int, memAttribs *LP4xMemAttributes) error { + switch latency { + case 6: + memAttribs.CASFirstByte |= LP4xCAS6 + case 10: + memAttribs.CASFirstByte |= LP4xCAS10 + case 14: + memAttribs.CASFirstByte |= LP4xCAS14 + case 16: + memAttribs.CASSecondByte |= LP4xCAS16 + case 20: + memAttribs.CASSecondByte |= LP4xCAS20 + case 22: + memAttribs.CASSecondByte |= LP4xCAS22 + case 24: + memAttribs.CASSecondByte |= LP4xCAS24 + case 26: + memAttribs.CASSecondByte |= LP4xCAS26 + case 28: + memAttribs.CASSecondByte |= LP4xCAS28 + case 32: + memAttribs.CASThirdByte |= LP4xCAS32 + case 36: + memAttribs.CASThirdByte |= LP4xCAS36 + case 40: + memAttribs.CASThirdByte |= LP4xCAS40 + default: + fmt.Errorf("Incorrect CAS Latency: ", latency) + } + + return nil +} + +func LP4xUpdateTCK(memAttribs *LP4xMemAttributes) { + if memAttribs.TCKMinPs == 0 { + memAttribs.TCKMinPs = LP4xSpeedMbpsToSPDEncoding[memAttribs.SpeedMbps].TCKMinPs + } + if memAttribs.TCKMaxPs == 0 { + memAttribs.TCKMaxPs = LP4xSpeedMbpsToSPDEncoding[memAttribs.SpeedMbps].TCKMaxPs + } +} + +func LP4xGetCASLatencies(memAttribs *LP4xMemAttributes) string { + if memAttribs.BitWidthPerChannel == 16 { + return LP4xSpeedMbpsToSPDEncoding[memAttribs.SpeedMbps].CASLatenciesx16Channel + } else if memAttribs.BitWidthPerChannel == 8 { + return LP4xSpeedMbpsToSPDEncoding[memAttribs.SpeedMbps].CASLatenciesx8Channel + } + + return "" +} + +func LP4xUpdateCAS(memAttribs *LP4xMemAttributes) error { + if len(memAttribs.CASLatencies) == 0 { + memAttribs.CASLatencies = LP4xGetCASLatencies(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 := LP4xEncodeLatencies(latency, memAttribs); err != nil { + return err + } + } + return nil +} + +func LP4xGetMinCAS(memAttribs *LP4xMemAttributes) (int, error) { + if (memAttribs.CASThirdByte & LP4xCAS40) != 0 { + return 40, nil + } + if (memAttribs.CASThirdByte & LP4xCAS36) != 0 { + return 36, nil + } + if (memAttribs.CASThirdByte & LP4xCAS32) != 0 { + return 32, nil + } + if (memAttribs.CASSecondByte & LP4xCAS28) != 0 { + return 28, nil + } + + return 0, fmt.Errorf("Unexpected min CAS") +} + +func LP4xUpdateTAAMin(memAttribs *LP4xMemAttributes) error { + if memAttribs.TAAMinPs == 0 { + minCAS, err := LP4xGetMinCAS(memAttribs) + if err != nil { + return err + } + memAttribs.TAAMinPs = memAttribs.TCKMinPs * minCAS + } + + return nil +} + +func LP4xUpdateTRFCAB(memAttribs *LP4xMemAttributes) { + if memAttribs.TRFCABNs == 0 { + memAttribs.TRFCABNs = LP4xDensityGbPhysicalChannelToRefreshEncoding[memAttribs.DensityPerChannelGb].TRFCABNs + } +} + +func LP4xUpdateTRFCPB(memAttribs *LP4xMemAttributes) { + if memAttribs.TRFCPBNs == 0 { + memAttribs.TRFCPBNs = LP4xDensityGbPhysicalChannelToRefreshEncoding[memAttribs.DensityPerChannelGb].TRFCPBNs + } +} + +func LP4xUpdateTRCD(memAttribs *LP4xMemAttributes) { + if memAttribs.TRCDMinNs == 0 { + /* JEDEC spec says max of 18ns */ + memAttribs.TRCDMinNs = 18 + } +} + +func LP4xUpdateTRPAB(memAttribs *LP4xMemAttributes) { + if memAttribs.TRPABMinNs == 0 { + /* JEDEC spec says max of 21ns */ + memAttribs.TRPABMinNs = 21 + } +} + +func LP4xUpdateTRPPB(memAttribs *LP4xMemAttributes) { + if memAttribs.TRPPBMinNs == 0 { + /* JEDEC spec says max of 18ns */ + memAttribs.TRPPBMinNs = 18 + } +} + +func LP4xNormalizeAttribsSet0(memAttribs *LP4xMemAttributes) int { + /* + * 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 + + return 0 +} + +func LP4xNormalizeMemoryAttributes(memAttribs *LP4xMemAttributes) { + f, ok := LP4xSetInfo[LP4xCurrSet] + + if ok == false || f.normalizeAttribs == nil { + return + } + + f.normalizeAttribs(memAttribs) +} + +func Lp4xUpdateMemoryAttributes(memAttribs *LP4xMemAttributes) error { + LP4xUpdateTCK(memAttribs) + if err := LP4xUpdateCAS(memAttribs); err != nil { + return err + } + if err := LP4xUpdateTAAMin(memAttribs); err != nil { + return err + } + LP4xUpdateTRFCAB(memAttribs) + LP4xUpdateTRFCPB(memAttribs) + LP4xUpdateTRCD(memAttribs) + LP4xUpdateTRPAB(memAttribs) + LP4xUpdateTRPPB(memAttribs) + + LP4xNormalizeMemoryAttributes(memAttribs) + + return nil +} + +func LP4xValidateDensityx8Channel(densityPerChannelGb int) error { + if _, ok := LP4xDensityGbx8ChannelToRowColumnEncoding[densityPerChannelGb]; ok == false { + return fmt.Errorf("Incorrect x8 density: %d Gb", densityPerChannelGb) + } + return nil +} + +func LP4xValidateDensityx16Channel(densityPerChannelGb int) error { + if _, ok := LP4xDensityGbx16ChannelToRowColumnEncoding[densityPerChannelGb]; ok == false { + return fmt.Errorf("Incorrect x16 density: %d Gb", densityPerChannelGb) + } + return nil +} + +func LP4xValidateDensity(memAttribs *LP4xMemAttributes) error { + if memAttribs.BitWidthPerChannel == 8 { + return LP4xValidateDensityx8Channel(memAttribs.DensityPerChannelGb) + } else if memAttribs.BitWidthPerChannel == 16 { + return LP4xValidateDensityx16Channel(memAttribs.DensityPerChannelGb) + } + + return fmt.Errorf("No density table for this bit width: %d", memAttribs.BitWidthPerChannel) +} + +func LP4xValidateBanks(banks int) error { + if banks != 4 && banks != 8 { + return fmt.Errorf("Incorrect banks: %d", banks) + } + return nil +} + +func LP4xValidateChannels(channels int) error { + if channels != 1 && channels != 2 && channels != 4 { + return fmt.Errorf("Incorrect channels per die: %d ", channels) + } + return nil +} + +func LP4xValidateDataWidth(width int) error { + if width != 8 && width != 16 { + return fmt.Errorf("Incorrect bit width: %d", width) + } + return nil +} + +func LP4xValidateRanks(ranks int) error { + if ranks != 1 && ranks != 2 { + return fmt.Errorf("Incorrect ranks: %d", ranks) + } + return nil +} + +func LP4xValidateSpeed(speed int) error { + if _, ok := LP4xSpeedMbpsToSPDEncoding[speed]; ok == false { + return fmt.Errorf("Incorrect speed: %d Mbps", speed) + } + return nil +} + +func Lp4xValidateMemPartAttributes(memAttribs *LP4xMemAttributes) error { + if err := LP4xValidateBanks(memAttribs.Banks); err != nil { + return err + } + if err := LP4xValidateChannels(memAttribs.ChannelsPerDie); err != nil { + return err + } + if err := LP4xValidateDataWidth(memAttribs.BitWidthPerChannel); err != nil { + return err + } + if err := LP4xValidateDensity(memAttribs); err != nil { + return err + } + if err := LP4xValidateRanks(memAttribs.RanksPerChannel); err != nil { + return err + } + if err := LP4xValidateSpeed(memAttribs.SpeedMbps); err != nil { + return err + } + + return nil +} + +func LP4xIsManufacturerPartNumberByte(index int) bool { + if index >= LP4xSPDIndexManufacturerPartNumberStartByte && + index <= LP4xSPDIndexManufacturerPartNumberEndByte { + return true + } + return false +} + +/* ------------------------------------------------------------------------------------------ */ +/* Interface Functions */ +/* ------------------------------------------------------------------------------------------ */ + +func (lp4x) getSetMap() map[int][]int { + return LP4xPlatformSetMap +} + +func (lp4x) addNewPart(name string, attribs interface{}) error { + var lp4xAttributes LP4xMemAttributes + eByte, err := json.Marshal(attribs) + if err != nil { + return err + } + + if err := json.Unmarshal(eByte, &lp4xAttributes); err != nil { + return err + } + + if err := Lp4xValidateMemPartAttributes(&lp4xAttributes); err != nil { + return err + } + + LP4xPartAttributeMap[name] = lp4xAttributes + return nil +} + +func (lp4x) getSPDAttribs(name string, set int) (interface{}, error) { + lp4xAttributes := LP4xPartAttributeMap[name] + + LP4xCurrSet = set + + if err := Lp4xUpdateMemoryAttributes(&lp4xAttributes); err != nil { + return lp4xAttributes, err + } + + return lp4xAttributes, nil +} + +func (lp4x) getSPDLen() int { + return 512 +} + +func (lp4x) getSPDByte(index int, attribs interface{}) byte { + e, ok := LP4xSPDAttribTable[index] + if ok == false { + if LP4xIsManufacturerPartNumberByte(index) { + return LP4xSPDValueManufacturerPartNumberBlank + } + return 0x00 + } + + if e.getVal != nil { + var lp4xAttribs LP4xMemAttributes + lp4xAttribs = attribs.(LP4xMemAttributes) + return e.getVal(&lp4xAttribs) + } + + return e.constVal +} diff --git a/util/spd_tools/src/spd_gen/spd_gen.go b/util/spd_tools/src/spd_gen/spd_gen.go new file mode 100644 index 0000000000..38d706d309 --- /dev/null +++ b/util/spd_tools/src/spd_gen/spd_gen.go @@ -0,0 +1,281 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "reflect" + "regexp" + "strings" +) + +/* ------------------------------------------------------------------------------------------ */ +/* Program-defined types */ +/* ------------------------------------------------------------------------------------------ */ +type memParts struct { + MemParts []memPart `json:"parts"` +} + +type memPart struct { + Name string + Attribs interface{} + SPDId int +} + +type memTech interface { + getSetMap() map[int][]int + addNewPart(string, interface{}) error + getSPDAttribs(string, int) (interface{}, error) + getSPDLen() int + getSPDByte(int, interface{}) byte +} + +/* ------------------------------------------------------------------------------------------ */ +/* Constants */ +/* ------------------------------------------------------------------------------------------ */ + +const ( + PlatformTGL = iota + PlatformADL + PlatformJSL + PlatformPCO + PlatformPLK + PlatformCZN + PlatformMax +) + +const ( + SPDManifestFileName = "parts_spd_manifest.generated.txt" + PlatformManifestFileName = "platforms_manifest.generated.txt" +) + +/* ------------------------------------------------------------------------------------------ */ +/* Global variables */ +/* ------------------------------------------------------------------------------------------ */ + +var platformNames = map[int]string{ + PlatformTGL: "TGL", + PlatformADL: "ADL", + PlatformJSL: "JSL", + PlatformPCO: "PCO", + PlatformPLK: "PLK", + PlatformCZN: "CZN", +} + +/* ------------------------------------------------------------------------------------------ */ +/* Conversion Helper Functions */ +/* ------------------------------------------------------------------------------------------ */ + +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 divRoundUp(dividend int, divisor int) int { + return (dividend + divisor - 1) / divisor +} + +/* ------------------------------------------------------------------------------------------ */ +/* Functions */ +/* ------------------------------------------------------------------------------------------ */ + +func findIndex(dedupedAttribs []interface{}, newSPDAttribs interface{}) int { + for i := 0; i < len(dedupedAttribs); i++ { + if reflect.DeepEqual(dedupedAttribs[i], newSPDAttribs) { + return i + } + } + + return -1 +} + +func readMemParts(memPartsFilePath string) (memParts, error) { + var memParts memParts + + dataBytes, err := ioutil.ReadFile(memPartsFilePath) + if err != nil { + return memParts, err + } + + // Strip comments from json file + re := regexp.MustCompile(`(?m)^\s*//.*`) + dataBytes = re.ReplaceAll(dataBytes, []byte("")) + + if err := json.Unmarshal(dataBytes, &memParts); err != nil { + return memParts, err + } + + return memParts, nil +} + +func createSPD(memAttribs interface{}, t memTech) string { + var s string + + for i := 0; i < t.getSPDLen(); i++ { + var b byte = 0 + if memAttribs != nil { + b = t.getSPDByte(i, memAttribs) + } + + if (i+1)%16 == 0 { + s += fmt.Sprintf("%02X\n", b) + } else { + s += fmt.Sprintf("%02X ", b) + } + } + + return s +} + +func writeSPD(memAttribs interface{}, SPDId int, SPDSetDirName string, t memTech) { + s := createSPD(memAttribs, t) + SPDFileName := fmt.Sprintf("spd-%d.hex", SPDId) + ioutil.WriteFile(filepath.Join(SPDSetDirName, SPDFileName), []byte(s), 0644) +} + +func writeEmptySPD(SPDSetDirName string, t memTech) { + s := createSPD(nil, t) + SPDFileName := "spd-empty.hex" + ioutil.WriteFile(filepath.Join(SPDSetDirName, SPDFileName), []byte(s), 0644) +} + +func getGeneratedString() string { + return fmt.Sprintf("# Generated by:\n# %s\n\n", strings.Join(os.Args[0:], " ")) +} + +func writeSPDManifest(memPartArray []memPart, SPDSetDirName string) { + var s string + + s += getGeneratedString() + for i := 0; i < len(memPartArray); i++ { + s += fmt.Sprintf("%s,spd-%d.hex\n", memPartArray[i].Name, memPartArray[i].SPDId) + } + + ioutil.WriteFile(filepath.Join(SPDSetDirName, SPDManifestFileName), []byte(s), 0644) +} + +func writeSetMap(setMap map[int][]int, SPDDirName string) { + var s string + + s += getGeneratedString() + + for index, arr := range setMap { + for _, item := range arr { + s += fmt.Sprintf("%s,set-%d\n", platformNames[item], index) + } + } + + ioutil.WriteFile(filepath.Join(SPDDirName, PlatformManifestFileName), []byte(s), 0644) +} + +func usage() { + fmt.Printf("\nUsage: %s <mem_parts_list_json> <mem_technology>\n\n", os.Args[0]) + fmt.Printf(" where,\n") + fmt.Printf(" mem_parts_list_json = JSON File containing list of memory parts and attributes\n") + fmt.Printf(" mem_technology = Memory technology -- one of lp4x, ddr4\n\n\n") +} + +func main() { + if len(os.Args) != 3 { + usage() + log.Fatal("Incorrect number of arguments") + } + + var t memTech + memPartsFilePath, memTechnology := os.Args[1], os.Args[2] + + if strings.ToUpper(memTechnology) == "LP4X" { + t = lp4x{} + } else if strings.ToUpper(memTechnology) == "DDR4" { + t = ddr4{} + } else { + log.Fatal("Unsupported memory technology ", memTechnology) + } + + SPDDir, err := filepath.Abs(filepath.Dir(memPartsFilePath)) + if err != nil { + log.Fatal(err) + } + + memParts, err := readMemParts(memPartsFilePath) + if err != nil { + log.Fatal(err) + } + + memPartExists := make(map[string]bool) + for i := 0; i < len(memParts.MemParts); i++ { + if memPartExists[memParts.MemParts[i].Name] { + log.Fatalf("%s is duplicated in mem_parts_list_json", memParts.MemParts[i].Name) + } + memPartExists[memParts.MemParts[i].Name] = true + + if err := t.addNewPart(memParts.MemParts[i].Name, memParts.MemParts[i].Attribs); err != nil { + log.Fatal(err) + } + } + + setMap := t.getSetMap() + + for i := 0; i < len(setMap); i++ { + var dedupedAttribs []interface{} + + for j := 0; j < len(memParts.MemParts); j++ { + spdAttribs, _ := t.getSPDAttribs(memParts.MemParts[j].Name, i) + index := -1 + + if index = findIndex(dedupedAttribs, spdAttribs); index == -1 { + dedupedAttribs = append(dedupedAttribs, spdAttribs) + index = len(dedupedAttribs) - 1 + } + + memParts.MemParts[j].SPDId = index + 1 + } + + SPDSetDir := fmt.Sprintf("%s/set-%d", SPDDir, i) + os.MkdirAll(SPDSetDir, os.ModePerm) + + for j := 0; j < len(dedupedAttribs); j++ { + writeSPD(dedupedAttribs[j], j+1, SPDSetDir, t) + } + + writeEmptySPD(SPDSetDir, t) + + writeSPDManifest(memParts.MemParts, SPDSetDir) + } + + writeSetMap(setMap, SPDDir) +} |