| /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| |
| package main |
| |
| import ( |
| "encoding/json" |
| "fmt" |
| "io/ioutil" |
| "log" |
| "os" |
| "path/filepath" |
| "reflect" |
| "strconv" |
| "strings" |
| ) |
| |
| /* |
| * This program generates de-duplicated SPD files for LPDDR4x memory using the global memory |
| * part list provided in CSV format. In addition to that, it also generates SPD manifest in CSV |
| * format that contains entries of type (DRAM part name, SPD file name) which provides the SPD |
| * file name used by a given DRAM part. |
| * |
| * It takes as input: |
| * Pointer to directory where the generated SPD files will be placed. |
| * JSON file containing a list of memory parts with their attributes as per datasheet. |
| */ |
| const ( |
| SPDManifestFileName = "lp4x_spd_manifest.generated.txt" |
| |
| PlatformTGLADL = 0 |
| PlatformJSL = 1 |
| PlatformCZN = 2 |
| ) |
| |
| var platformMap = map[string]int{ |
| "TGL": PlatformTGLADL, |
| "JSL": PlatformJSL, |
| "ADL": PlatformTGLADL, |
| "CZN": PlatformCZN, |
| } |
| |
| var currPlatform int |
| |
| type memAttributes struct { |
| /* Primary attributes - must be provided by JSON file for each part */ |
| DensityPerChannelGb int |
| Banks int |
| ChannelsPerDie int |
| DiesPerPackage int |
| BitWidthPerChannel int |
| RanksPerChannel int |
| SpeedMbps int |
| |
| /* |
| * All the following parameters are optional and required only if the part requires |
| * special parameters as per the datasheet. |
| */ |
| /* Timing parameters */ |
| TRFCABNs int |
| TRFCPBNs int |
| TRPABMinNs int |
| TRPPBMinNs int |
| TCKMinPs int |
| TCKMaxPs int |
| TAAMinPs int |
| TRCDMinNs int |
| |
| /* CAS */ |
| CASLatencies string |
| CASFirstByte byte |
| CASSecondByte byte |
| CASThirdByte byte |
| } |
| |
| /* This encodes the density in Gb to SPD values as per JESD 21-C */ |
| var densityGbToSPDEncoding = map[int]byte{ |
| 4: 0x4, |
| 6: 0xb, |
| 8: 0x5, |
| 12: 0x8, |
| 16: 0x6, |
| 24: 0x9, |
| 32: 0x7, |
| } |
| |
| /* |
| * Table 3 from JESD209-4C. |
| * Maps density per physical channel to row-column encoding as per JESD 21-C for a device with |
| * x16 physical channel. |
| */ |
| var densityGbx16ChannelToRowColumnEncoding = map[int]byte{ |
| 4: 0x19, /* 15 rows, 10 columns */ |
| 6: 0x21, /* 16 rows, 10 columns */ |
| 8: 0x21, /* 16 rows, 10 columns */ |
| 12: 0x29, /* 17 rows, 10 columns */ |
| 16: 0x29, /* 17 rows, 10 columns */ |
| } |
| |
| /* |
| * Table 5 from JESD209-4C. |
| * Maps density per physical channel to row-column encoding as per JESD 21-C for a device with |
| * x8 physical channel. |
| */ |
| var densityGbx8ChannelToRowColumnEncoding = map[int]byte{ |
| 3: 0x21, /* 16 rows, 10 columns */ |
| 4: 0x21, /* 16 rows, 10 columns */ |
| 6: 0x29, /* 17 rows, 10 columns */ |
| 8: 0x29, /* 17 rows, 10 columns */ |
| 12: 0x31, /* 18 rows, 10 columns */ |
| 16: 0x31, /* 18 rows, 10 columns */ |
| } |
| |
| type refreshTimings struct { |
| TRFCABNs int |
| TRFCPBNs int |
| } |
| |
| /* |
| * Table 112 from JESD209-4C |
| * Maps density per physical channel to refresh timings. This is the same for x8 and x16 |
| * devices. |
| */ |
| var densityGbPhysicalChannelToRefreshEncoding = map[int]refreshTimings{ |
| 3: { |
| TRFCABNs: 180, |
| TRFCPBNs: 90, |
| }, |
| 4: { |
| TRFCABNs: 180, |
| TRFCPBNs: 90, |
| }, |
| 6: { |
| TRFCABNs: 280, |
| TRFCPBNs: 140, |
| }, |
| 8: { |
| TRFCABNs: 280, |
| TRFCPBNs: 140, |
| }, |
| 12: { |
| TRFCABNs: 380, |
| TRFCPBNs: 190, |
| }, |
| 16: { |
| TRFCABNs: 380, |
| TRFCPBNs: 190, |
| }, |
| } |
| |
| type speedParams struct { |
| TCKMinPs int |
| TCKMaxPs int |
| CASLatenciesx16Channel string |
| CASLatenciesx8Channel string |
| } |
| |
| const ( |
| /* First Byte */ |
| CAS6 = 1 << 1 |
| CAS10 = 1 << 4 |
| CAS14 = 1 << 7 |
| /* Second Byte */ |
| CAS16 = 1 << 0 |
| CAS20 = 1 << 2 |
| CAS22 = 1 << 3 |
| CAS24 = 1 << 4 |
| CAS26 = 1 << 5 |
| CAS28 = 1 << 6 |
| /* Third Byte */ |
| CAS32 = 1 << 0 |
| CAS36 = 1 << 2 |
| CAS40 = 1 << 4 |
| ) |
| |
| const ( |
| /* |
| * JEDEC spec says that TCKmax should be 100ns for all speed grades. |
| * 100ns in MTB units comes out to be 0x320. But since this is a byte field, set it to |
| * 0xFF i.e. 31.875ns. |
| */ |
| TCKMaxPsDefault = 31875 |
| ) |
| |
| var speedMbpsToSPDEncoding = map[int]speedParams{ |
| 4267: { |
| TCKMinPs: 468, /* 1/4267 * 2 */ |
| TCKMaxPs: TCKMaxPsDefault, |
| CASLatenciesx16Channel: "6 10 14 20 24 28 32 36", |
| CASLatenciesx8Channel: "6 10 16 22 26 32 36 40", |
| }, |
| 3733: { |
| TCKMinPs: 535, /* 1/3733 * 2 */ |
| TCKMaxPs: TCKMaxPsDefault, |
| CASLatenciesx16Channel: "6 10 14 20 24 28 32", |
| CASLatenciesx8Channel: "6 10 16 22 26 32 36", |
| }, |
| 3200: { |
| TCKMinPs: 625, /* 1/3200 * 2 */ |
| TCKMaxPs: TCKMaxPsDefault, |
| CASLatenciesx16Channel: "6 10 14 20 24 28", |
| CASLatenciesx8Channel: "6 10 16 22 26 32", |
| }, |
| } |
| |
| var bankEncoding = map[int]byte{ |
| 4: 0 << 4, |
| 8: 1 << 4, |
| } |
| |
| const ( |
| TGLLogicalChannelWidth = 16 |
| ) |
| |
| /* Returns density to encode as per Intel MRC expectations. */ |
| func getMRCDensity(memAttribs *memAttributes) int { |
| if currPlatform == PlatformTGLADL { |
| /* |
| * Intel MRC on TGL expects density per logical channel to be encoded in |
| * SPDIndexDensityBanks. Logical channel on TGL is an x16 channel. |
| */ |
| return memAttribs.DensityPerChannelGb * TGLLogicalChannelWidth / memAttribs.BitWidthPerChannel |
| } else if currPlatform == PlatformJSL || currPlatform == PlatformCZN { |
| /* |
| * Intel MRC on JSL expects density per die to be encoded in |
| * SPDIndexDensityBanks. |
| */ |
| return memAttribs.DensityPerChannelGb * memAttribs.ChannelsPerDie |
| } |
| |
| return 0 |
| } |
| |
| func encodeDensityBanks(memAttribs *memAttributes) byte { |
| var b byte |
| |
| b = densityGbToSPDEncoding[getMRCDensity(memAttribs)] |
| b |= bankEncoding[memAttribs.Banks] |
| |
| return b |
| } |
| |
| func encodeSdramAddressing(memAttribs *memAttributes) byte { |
| densityPerChannelGb := memAttribs.DensityPerChannelGb |
| if memAttribs.BitWidthPerChannel == 8 { |
| return densityGbx8ChannelToRowColumnEncoding[densityPerChannelGb] |
| } else { |
| return densityGbx16ChannelToRowColumnEncoding[densityPerChannelGb] |
| } |
| return 0 |
| } |
| |
| func encodeChannelsPerDie(channels int) byte { |
| var temp byte |
| |
| temp = byte(channels >> 1) |
| |
| return temp << 2 |
| } |
| |
| func encodePackage(dies int) byte { |
| var temp byte |
| |
| if dies > 1 { |
| /* If more than one die, then this is a non-monolithic device. */ |
| temp = 1 |
| } else { |
| /* If only single die, then this is a monolithic device. */ |
| temp = 0 |
| } |
| |
| return temp << 7 |
| } |
| |
| /* Per JESD209-4C Dies = ZQ balls on the package */ |
| /* Note that this can be different than the part's die count */ |
| func encodeDiesPerPackage(memAttribs *memAttributes) byte { |
| var dies int = 0 |
| if currPlatform == PlatformTGLADL { |
| /* Intel MRC expects logical dies to be encoded for TGL. */ |
| dies = memAttribs.ChannelsPerDie * memAttribs.RanksPerChannel * memAttribs.BitWidthPerChannel / 16 |
| } else if currPlatform == PlatformJSL || currPlatform == PlatformCZN { |
| /* Intel MRC expects physical dies to be encoded for JSL. */ |
| /* AMD PSP expects physical dies (ZQ balls) */ |
| dies = memAttribs.DiesPerPackage |
| } |
| |
| b := encodePackage(dies) /* Monolithic / Non-monolithic device */ |
| b |= (byte(dies) - 1) << 4 |
| |
| return b |
| } |
| |
| func encodePackageType(memAttribs *memAttributes) byte { |
| var b byte |
| |
| b |= encodeChannelsPerDie(memAttribs.ChannelsPerDie) |
| b |= encodeDiesPerPackage(memAttribs) |
| |
| return b |
| } |
| |
| func encodeDataWidth(bitWidthPerChannel int) byte { |
| return byte(bitWidthPerChannel / 8) |
| } |
| |
| func encodeRanks(ranks int) byte { |
| var b byte |
| b = byte(ranks - 1) |
| return b << 3 |
| } |
| |
| func encodeModuleOrganization(memAttribs *memAttributes) byte { |
| var b byte |
| |
| b = encodeDataWidth(memAttribs.BitWidthPerChannel) |
| b |= encodeRanks(memAttribs.RanksPerChannel) |
| |
| return b |
| } |
| |
| const ( |
| /* |
| * As per advisory 616599: |
| * 7:5 (Number of system channels) = 000 (1 channel always) |
| * 2:0 (Bus width) = 001 (x16 always) |
| * Set to 0x01. |
| */ |
| SPDValueBusWidthTGL = 0x01 |
| /* |
| * As per advisory 610202: |
| * 7:5 (Number of system channels) = 001 (2 channel always) |
| * 2:0 (Bus width) = 010 (x32 always) |
| * Set to 0x01. |
| */ |
| SPDValueBusWidthJSL = 0x22 |
| ) |
| |
| func encodeBusWidth(memAttribs *memAttributes) byte { |
| if currPlatform == PlatformTGLADL { |
| return SPDValueBusWidthTGL |
| } else if currPlatform == PlatformJSL || currPlatform == PlatformCZN { |
| return SPDValueBusWidthJSL |
| } |
| return 0 |
| } |
| |
| func encodeTCKMin(memAttribs *memAttributes) byte { |
| return convPsToMtbByte(memAttribs.TCKMinPs) |
| } |
| |
| func encodeTCKMinFineOffset(memAttribs *memAttributes) byte { |
| return convPsToFtbByte(memAttribs.TCKMinPs) |
| } |
| |
| func encodeTCKMax(memAttribs *memAttributes) byte { |
| return convPsToMtbByte(memAttribs.TCKMaxPs) |
| } |
| |
| func encodeTCKMaxFineOffset(memAttribs *memAttributes) byte { |
| return convPsToFtbByte(memAttribs.TCKMaxPs) |
| } |
| |
| func encodeCASFirstByte(memAttribs *memAttributes) byte { |
| return memAttribs.CASFirstByte |
| } |
| |
| func encodeCASSecondByte(memAttribs *memAttributes) byte { |
| return memAttribs.CASSecondByte |
| } |
| |
| func encodeCASThirdByte(memAttribs *memAttributes) byte { |
| return memAttribs.CASThirdByte |
| } |
| |
| func divRoundUp(dividend int, divisor int) int { |
| return (dividend + divisor - 1) / divisor |
| } |
| |
| func convNsToPs(timeNs int) int { |
| return timeNs * 1000 |
| } |
| |
| func convMtbToPs(mtb int) int { |
| return mtb * 125 |
| } |
| |
| func convPsToMtb(timePs int) int { |
| return divRoundUp(timePs, 125) |
| } |
| |
| func convPsToMtbByte(timePs int) byte { |
| return byte(convPsToMtb(timePs) & 0xff) |
| } |
| |
| func convPsToFtbByte(timePs int) byte { |
| mtb := convPsToMtb(timePs) |
| ftb := timePs - convMtbToPs(mtb) |
| |
| return byte(ftb) |
| } |
| |
| func convNsToMtb(timeNs int) int { |
| return convPsToMtb(convNsToPs(timeNs)) |
| } |
| |
| func convNsToMtbByte(timeNs int) byte { |
| return convPsToMtbByte(convNsToPs(timeNs)) |
| } |
| |
| func convNsToFtbByte(timeNs int) byte { |
| return convPsToFtbByte(convNsToPs(timeNs)) |
| } |
| |
| func encodeTAAMin(memAttribs *memAttributes) byte { |
| return convPsToMtbByte(memAttribs.TAAMinPs) |
| } |
| |
| func encodeTAAMinFineOffset(memAttribs *memAttributes) byte { |
| return convPsToFtbByte(memAttribs.TAAMinPs) |
| } |
| |
| func encodeTRCDMin(memAttribs *memAttributes) byte { |
| return convNsToMtbByte(memAttribs.TRCDMinNs) |
| } |
| |
| func encodeTRCDMinFineOffset(memAttribs *memAttributes) byte { |
| return convNsToFtbByte(memAttribs.TRCDMinNs) |
| } |
| |
| func encodeTRPABMin(memAttribs *memAttributes) byte { |
| return convNsToMtbByte(memAttribs.TRPABMinNs) |
| } |
| |
| func encodeTRPABMinFineOffset(memAttribs *memAttributes) byte { |
| return convNsToFtbByte(memAttribs.TRPABMinNs) |
| } |
| |
| func encodeTRPPBMin(memAttribs *memAttributes) byte { |
| return convNsToMtbByte(memAttribs.TRPPBMinNs) |
| } |
| |
| func encodeTRPPBMinFineOffset(memAttribs *memAttributes) byte { |
| return convNsToFtbByte(memAttribs.TRPPBMinNs) |
| } |
| |
| func encodeTRFCABMinMsb(memAttribs *memAttributes) byte { |
| return byte((convNsToMtb(memAttribs.TRFCABNs) >> 8) & 0xff) |
| } |
| |
| func encodeTRFCABMinLsb(memAttribs *memAttributes) byte { |
| return byte(convNsToMtb(memAttribs.TRFCABNs) & 0xff) |
| } |
| |
| func encodeTRFCPBMinMsb(memAttribs *memAttributes) byte { |
| return byte((convNsToMtb(memAttribs.TRFCPBNs) >> 8) & 0xff) |
| } |
| |
| func encodeTRFCPBMinLsb(memAttribs *memAttributes) byte { |
| return byte(convNsToMtb(memAttribs.TRFCPBNs) & 0xff) |
| } |
| |
| type SPDAttribFunc func(*memAttributes) byte |
| |
| type SPDAttribTableEntry struct { |
| constVal byte |
| getVal SPDAttribFunc |
| } |
| |
| const ( |
| /* SPD Byte Index */ |
| SPDIndexSize = 0 |
| SPDIndexRevision = 1 |
| SPDIndexMemoryType = 2 |
| SPDIndexModuleType = 3 |
| SPDIndexDensityBanks = 4 |
| SPDIndexAddressing = 5 |
| SPDIndexPackageType = 6 |
| SPDIndexOptionalFeatures = 7 |
| SPDIndexModuleOrganization = 12 |
| SPDIndexBusWidth = 13 |
| SPDIndexTimebases = 17 |
| SPDIndexTCKMin = 18 |
| SPDIndexTCKMax = 19 |
| SPDIndexCASFirstByte = 20 |
| SPDIndexCASSecondByte = 21 |
| SPDIndexCASThirdByte = 22 |
| SPDIndexCASFourthByte = 23 |
| SPDIndexTAAMin = 24 |
| SPDIndexReadWriteLatency = 25 |
| SPDIndexTRCDMin = 26 |
| SPDIndexTRPABMin = 27 |
| SPDIndexTRPPBMin = 28 |
| SPDIndexTRFCABMinLSB = 29 |
| SPDIndexTRFCABMinMSB = 30 |
| SPDIndexTRFCPBMinLSB = 31 |
| SPDIndexTRFCPBMinMSB = 32 |
| SPDIndexTRPPBMinFineOffset = 120 |
| SPDIndexTRPABMinFineOffset = 121 |
| SPDIndexTRCDMinFineOffset = 122 |
| SPDIndexTAAMinFineOffset = 123 |
| SPDIndexTCKMaxFineOffset = 124 |
| SPDIndexTCKMinFineOffset = 125 |
| SPDIndexManufacturerPartNumberStartByte = 329 |
| SPDIndexManufacturerPartNumberEndByte = 348 |
| |
| /* SPD Byte Value */ |
| |
| /* |
| * From JEDEC spec: |
| * 6:4 (Bytes total) = 2 (512 bytes) |
| * 3:0 (Bytes used) = 3 (384 bytes) |
| * Set to 0x23 for LPDDR4x. |
| */ |
| SPDValueSize = 0x23 |
| |
| /* |
| * From JEDEC spec: Revision 1.1 |
| * Set to 0x11. |
| */ |
| SPDValueRevision = 0x11 |
| |
| /* LPDDR4x memory type = 0x11 */ |
| SPDValueMemoryType = 0x11 |
| |
| /* |
| * From JEDEC spec: |
| * 7:7 (Hybrid) = 0 (Not hybrid) |
| * 6:4 (Hybrid media) = 000 (Not hybrid) |
| * 3:0 (Base Module Type) = 1110 (Non-DIMM solution) |
| * |
| * This is dependent on hardware design. LPDDR4x only has memory down solution. |
| * Hence this is not hybrid non-DIMM solution. |
| * Set to 0x0E. |
| */ |
| SPDValueModuleType = 0x0e |
| |
| /* |
| * From JEDEC spec: |
| * 5:4 (Maximum Activate Window) = 00 (8192 * tREFI) |
| * 3:0 (Maximum Activate Count) = 1000 (Unlimited MAC) |
| * |
| * Needs to come from datasheet, but most parts seem to support unlimited MAC. |
| * MR#24 OP3 |
| */ |
| SPDValueOptionalFeatures = 0x08 |
| |
| /* |
| * From JEDEC spec: |
| * 3:2 (MTB) = 00 (0.125ns) |
| * 1:0 (FTB) = 00 (1ps) |
| * Set to 0x00. |
| */ |
| SPDValueTimebases = 0x00 |
| |
| /* CAS fourth byte: All bits are reserved */ |
| SPDValueCASFourthByte = 0x00 |
| |
| /* Write Latency Set A and Read Latency DBI-RD disabled. */ |
| SPDValueReadWriteLatency = 0x00 |
| |
| /* As per JEDEC spec, unused digits of manufacturer part number are left as blank. */ |
| SPDValueManufacturerPartNumberBlank = 0x20 |
| ) |
| |
| var SPDAttribTable = map[int]SPDAttribTableEntry{ |
| SPDIndexSize: {constVal: SPDValueSize}, |
| SPDIndexRevision: {constVal: SPDValueRevision}, |
| SPDIndexMemoryType: {constVal: SPDValueMemoryType}, |
| SPDIndexModuleType: {constVal: SPDValueModuleType}, |
| SPDIndexDensityBanks: {getVal: encodeDensityBanks}, |
| SPDIndexAddressing: {getVal: encodeSdramAddressing}, |
| SPDIndexPackageType: {getVal: encodePackageType}, |
| SPDIndexOptionalFeatures: {constVal: SPDValueOptionalFeatures}, |
| SPDIndexModuleOrganization: {getVal: encodeModuleOrganization}, |
| SPDIndexBusWidth: {getVal: encodeBusWidth}, |
| SPDIndexTimebases: {constVal: SPDValueTimebases}, |
| SPDIndexTCKMin: {getVal: encodeTCKMin}, |
| SPDIndexTCKMax: {getVal: encodeTCKMax}, |
| SPDIndexTCKMaxFineOffset: {getVal: encodeTCKMaxFineOffset}, |
| SPDIndexTCKMinFineOffset: {getVal: encodeTCKMinFineOffset}, |
| SPDIndexCASFirstByte: {getVal: encodeCASFirstByte}, |
| SPDIndexCASSecondByte: {getVal: encodeCASSecondByte}, |
| SPDIndexCASThirdByte: {getVal: encodeCASThirdByte}, |
| SPDIndexCASFourthByte: {constVal: SPDValueCASFourthByte}, |
| SPDIndexTAAMin: {getVal: encodeTAAMin}, |
| SPDIndexTAAMinFineOffset: {getVal: encodeTAAMinFineOffset}, |
| SPDIndexReadWriteLatency: {constVal: SPDValueReadWriteLatency}, |
| SPDIndexTRCDMin: {getVal: encodeTRCDMin}, |
| SPDIndexTRCDMinFineOffset: {getVal: encodeTRCDMinFineOffset}, |
| SPDIndexTRPABMin: {getVal: encodeTRPABMin}, |
| SPDIndexTRPABMinFineOffset: {getVal: encodeTRPABMinFineOffset}, |
| SPDIndexTRPPBMin: {getVal: encodeTRPPBMin}, |
| SPDIndexTRPPBMinFineOffset: {getVal: encodeTRPPBMinFineOffset}, |
| SPDIndexTRFCABMinLSB: {getVal: encodeTRFCABMinLsb}, |
| SPDIndexTRFCABMinMSB: {getVal: encodeTRFCABMinMsb}, |
| SPDIndexTRFCPBMinLSB: {getVal: encodeTRFCPBMinLsb}, |
| SPDIndexTRFCPBMinMSB: {getVal: encodeTRFCPBMinMsb}, |
| } |
| |
| type memParts struct { |
| MemParts []memPart `json:"parts"` |
| } |
| |
| type memPart struct { |
| Name string |
| Attribs memAttributes |
| SPDFileName string |
| } |
| |
| func writeSPDManifest(memParts *memParts, SPDDirName string) error { |
| var s string |
| |
| fmt.Printf("Generating SPD Manifest with following entries:\n") |
| |
| for i := 0; i < len(memParts.MemParts); i++ { |
| fmt.Printf("%-40s %s\n", memParts.MemParts[i].Name, memParts.MemParts[i].SPDFileName) |
| s += fmt.Sprintf("%s,%s\n", memParts.MemParts[i].Name, memParts.MemParts[i].SPDFileName) |
| } |
| |
| return ioutil.WriteFile(filepath.Join(SPDDirName, SPDManifestFileName), []byte(s), 0644) |
| } |
| |
| func isManufacturerPartNumberByte(index int) bool { |
| if index >= SPDIndexManufacturerPartNumberStartByte && index <= SPDIndexManufacturerPartNumberEndByte { |
| return true |
| } |
| return false |
| } |
| |
| func getSPDByte(index int, memAttribs *memAttributes) byte { |
| e, ok := SPDAttribTable[index] |
| if ok == false { |
| if isManufacturerPartNumberByte(index) { |
| return SPDValueManufacturerPartNumberBlank |
| } |
| return 0x00 |
| } |
| |
| if e.getVal != nil { |
| return e.getVal(memAttribs) |
| } |
| |
| return e.constVal |
| } |
| |
| func createSPD(memAttribs *memAttributes) string { |
| var s string |
| |
| for i := 0; i < 512; i++ { |
| b := getSPDByte(i, memAttribs) |
| |
| if (i+1)%16 == 0 { |
| s += fmt.Sprintf("%02X\n", b) |
| } else { |
| s += fmt.Sprintf("%02X ", b) |
| } |
| } |
| |
| return s |
| } |
| |
| func dedupeMemoryPart(dedupedParts []*memPart, memPart *memPart) bool { |
| for i := 0; i < len(dedupedParts); i++ { |
| if reflect.DeepEqual(dedupedParts[i].Attribs, memPart.Attribs) { |
| memPart.SPDFileName = dedupedParts[i].SPDFileName |
| return true |
| } |
| } |
| |
| return false |
| } |
| |
| func generateSPD(memPart *memPart, SPDId int, SPDDirName string) { |
| s := createSPD(&memPart.Attribs) |
| memPart.SPDFileName = fmt.Sprintf("lp4x-spd-%d.hex", SPDId) |
| ioutil.WriteFile(filepath.Join(SPDDirName, memPart.SPDFileName), []byte(s), 0644) |
| } |
| |
| func readMemoryParts(memParts *memParts, memPartsFileName string) error { |
| databytes, err := ioutil.ReadFile(memPartsFileName) |
| if err != nil { |
| return err |
| } |
| |
| return json.Unmarshal(databytes, memParts) |
| } |
| |
| func validateDensityx8Channel(densityPerChannelGb int) error { |
| if _, ok := densityGbx8ChannelToRowColumnEncoding[densityPerChannelGb]; ok == false { |
| return fmt.Errorf("Incorrect x8 density: ", densityPerChannelGb, "Gb") |
| } |
| return nil |
| } |
| |
| func validateDensityx16Channel(densityPerChannelGb int) error { |
| if _, ok := densityGbx16ChannelToRowColumnEncoding[densityPerChannelGb]; ok == false { |
| return fmt.Errorf("Incorrect x16 density: ", densityPerChannelGb, "Gb") |
| } |
| return nil |
| } |
| |
| func validateDensity(memAttribs *memAttributes) error { |
| if memAttribs.BitWidthPerChannel == 8 { |
| return validateDensityx8Channel(memAttribs.DensityPerChannelGb) |
| } else if memAttribs.BitWidthPerChannel == 16 { |
| return validateDensityx16Channel(memAttribs.DensityPerChannelGb) |
| } |
| |
| return fmt.Errorf("No density table for this bit width: ", memAttribs.BitWidthPerChannel) |
| } |
| |
| func validateBanks(banks int) error { |
| if banks != 4 && banks != 8 { |
| return fmt.Errorf("Incorrect banks: ", banks) |
| } |
| return nil |
| } |
| |
| func validateChannels(channels int) error { |
| if channels != 1 && channels != 2 && channels != 4 { |
| return fmt.Errorf("Incorrect channels per die: ", channels) |
| } |
| return nil |
| } |
| |
| func validateDataWidth(width int) error { |
| if width != 8 && width != 16 { |
| return fmt.Errorf("Incorrect bit width: ", width) |
| } |
| return nil |
| } |
| |
| func validateRanks(ranks int) error { |
| if ranks != 1 && ranks != 2 { |
| return fmt.Errorf("Incorrect ranks: ", ranks) |
| } |
| return nil |
| } |
| |
| func validateSpeed(speed int) error { |
| if _, ok := speedMbpsToSPDEncoding[speed]; ok == false { |
| return fmt.Errorf("Incorrect speed: ", speed, " Mbps") |
| } |
| return nil |
| } |
| |
| func validateMemoryParts(memParts *memParts) error { |
| for i := 0; i < len(memParts.MemParts); i++ { |
| if err := validateBanks(memParts.MemParts[i].Attribs.Banks); err != nil { |
| return err |
| } |
| if err := validateChannels(memParts.MemParts[i].Attribs.ChannelsPerDie); err != nil { |
| return err |
| } |
| if err := validateDataWidth(memParts.MemParts[i].Attribs.BitWidthPerChannel); err != nil { |
| return err |
| } |
| if err := validateDensity(&memParts.MemParts[i].Attribs); err != nil { |
| return err |
| } |
| if err := validateRanks(memParts.MemParts[i].Attribs.RanksPerChannel); err != nil { |
| return err |
| } |
| if err := validateSpeed(memParts.MemParts[i].Attribs.SpeedMbps); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| |
| func encodeLatencies(latency int, memAttribs *memAttributes) error { |
| switch latency { |
| case 6: |
| memAttribs.CASFirstByte |= CAS6 |
| case 10: |
| memAttribs.CASFirstByte |= CAS10 |
| case 14: |
| memAttribs.CASFirstByte |= CAS14 |
| case 16: |
| memAttribs.CASSecondByte |= CAS16 |
| case 20: |
| memAttribs.CASSecondByte |= CAS20 |
| case 22: |
| memAttribs.CASSecondByte |= CAS22 |
| case 24: |
| memAttribs.CASSecondByte |= CAS24 |
| case 26: |
| memAttribs.CASSecondByte |= CAS26 |
| case 28: |
| memAttribs.CASSecondByte |= CAS28 |
| case 32: |
| memAttribs.CASThirdByte |= CAS32 |
| case 36: |
| memAttribs.CASThirdByte |= CAS36 |
| case 40: |
| memAttribs.CASThirdByte |= CAS40 |
| default: |
| fmt.Errorf("Incorrect CAS Latency: ", latency) |
| } |
| |
| return nil |
| } |
| |
| func updateTCK(memAttribs *memAttributes) { |
| if memAttribs.TCKMinPs == 0 { |
| memAttribs.TCKMinPs = speedMbpsToSPDEncoding[memAttribs.SpeedMbps].TCKMinPs |
| } |
| if memAttribs.TCKMaxPs == 0 { |
| memAttribs.TCKMaxPs = speedMbpsToSPDEncoding[memAttribs.SpeedMbps].TCKMaxPs |
| } |
| } |
| |
| func getCASLatencies(memAttribs *memAttributes) string { |
| if memAttribs.BitWidthPerChannel == 16 { |
| return speedMbpsToSPDEncoding[memAttribs.SpeedMbps].CASLatenciesx16Channel |
| } else if memAttribs.BitWidthPerChannel == 8 { |
| return speedMbpsToSPDEncoding[memAttribs.SpeedMbps].CASLatenciesx8Channel |
| } |
| |
| return "" |
| } |
| |
| func updateCAS(memAttribs *memAttributes) error { |
| if len(memAttribs.CASLatencies) == 0 { |
| memAttribs.CASLatencies = getCASLatencies(memAttribs) |
| } |
| |
| latencies := strings.Fields(memAttribs.CASLatencies) |
| for i := 0; i < len(latencies); i++ { |
| latency, err := strconv.Atoi(latencies[i]) |
| if err != nil { |
| return fmt.Errorf("Unable to convert latency ", latencies[i]) |
| } |
| if err := encodeLatencies(latency, memAttribs); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| |
| func getMinCAS(memAttribs *memAttributes) (int, error) { |
| if (memAttribs.CASThirdByte & CAS40) != 0 { |
| return 40, nil |
| } |
| if (memAttribs.CASThirdByte & CAS36) != 0 { |
| return 36, nil |
| } |
| if (memAttribs.CASThirdByte & CAS32) != 0 { |
| return 32, nil |
| } |
| if (memAttribs.CASSecondByte & CAS28) != 0 { |
| return 28, nil |
| } |
| |
| return 0, fmt.Errorf("Unexpected min CAS") |
| } |
| |
| func updateTAAMin(memAttribs *memAttributes) error { |
| if memAttribs.TAAMinPs == 0 { |
| minCAS, err := getMinCAS(memAttribs) |
| if err != nil { |
| return err |
| } |
| memAttribs.TAAMinPs = memAttribs.TCKMinPs * minCAS |
| } |
| |
| return nil |
| } |
| |
| func updateTRFCAB(memAttribs *memAttributes) { |
| if memAttribs.TRFCABNs == 0 { |
| memAttribs.TRFCABNs = densityGbPhysicalChannelToRefreshEncoding[memAttribs.DensityPerChannelGb].TRFCABNs |
| } |
| } |
| |
| func updateTRFCPB(memAttribs *memAttributes) { |
| if memAttribs.TRFCPBNs == 0 { |
| memAttribs.TRFCPBNs = densityGbPhysicalChannelToRefreshEncoding[memAttribs.DensityPerChannelGb].TRFCPBNs |
| } |
| } |
| |
| func updateTRCD(memAttribs *memAttributes) { |
| if memAttribs.TRCDMinNs == 0 { |
| /* JEDEC spec says max of 18ns */ |
| memAttribs.TRCDMinNs = 18 |
| } |
| } |
| |
| func updateTRPAB(memAttribs *memAttributes) { |
| if memAttribs.TRPABMinNs == 0 { |
| /* JEDEC spec says max of 21ns */ |
| memAttribs.TRPABMinNs = 21 |
| } |
| } |
| |
| func updateTRPPB(memAttribs *memAttributes) { |
| if memAttribs.TRPPBMinNs == 0 { |
| /* JEDEC spec says max of 18ns */ |
| memAttribs.TRPPBMinNs = 18 |
| } |
| } |
| |
| func normalizeMemoryAttributes(memAttribs *memAttributes) { |
| if currPlatform == PlatformTGLADL { |
| /* |
| * TGL does not really use physical organization of dies per package when |
| * generating the SPD. So, set it to 0 here so that deduplication ignores |
| * that field. |
| */ |
| memAttribs.DiesPerPackage = 0 |
| } |
| } |
| |
| func updateMemoryAttributes(memAttribs *memAttributes) error { |
| updateTCK(memAttribs) |
| if err := updateCAS(memAttribs); err != nil { |
| return err |
| } |
| if err := updateTAAMin(memAttribs); err != nil { |
| return err |
| } |
| updateTRFCAB(memAttribs) |
| updateTRFCPB(memAttribs) |
| updateTRCD(memAttribs) |
| updateTRPAB(memAttribs) |
| updateTRPPB(memAttribs) |
| |
| normalizeMemoryAttributes(memAttribs) |
| |
| return nil |
| } |
| |
| func isPlatformSupported(platform string) error { |
| var ok bool |
| |
| currPlatform, ok = platformMap[platform] |
| if ok == false { |
| return fmt.Errorf("Unsupported platform: ", platform) |
| } |
| |
| return nil |
| } |
| |
| func usage() { |
| fmt.Printf("\nUsage: %s <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") |
| fmt.Printf(" supported platforms: ") |
| keys := reflect.ValueOf(platformMap).MapKeys() |
| fmt.Println(keys) |
| fmt.Printf("\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) |
| } |
| } |