1429 lines
34 KiB
Go
1429 lines
34 KiB
Go
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
/*
|
|
* This program generates de-duplicated SPD files for DDR4 memory using the global memory
|
|
* part list provided in CSV format. In addition to that, it also generates SPD manifest in CSV
|
|
* format that contains entries of type (DRAM part name, SPD file name) which provides the SPD
|
|
* file name used by a given DRAM part.
|
|
*
|
|
* It takes as input:
|
|
* Pointer to directory where the generated SPD files will be placed.
|
|
* JSON file containing a list of memory parts with their attributes as per datasheet.
|
|
*/
|
|
const (
|
|
SPDManifestFileName = "ddr4_spd_manifest.generated.txt"
|
|
|
|
PlatformTGL = 0
|
|
PlatformPCO = 1
|
|
PlatformPLK = 2
|
|
)
|
|
|
|
var platformMap = map[string]int{
|
|
"TGL": PlatformTGL,
|
|
"PCO": PlatformPCO,
|
|
"PLK": PlatformPLK,
|
|
}
|
|
|
|
var currPlatform int
|
|
|
|
type memAttributes struct {
|
|
/* Primary attributes - must be provided by JSON file for each part */
|
|
SpeedMTps int
|
|
CL_nRCD_nRP int
|
|
CapacityPerDieGb int
|
|
DiesPerPackage int
|
|
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
|
|
}
|
|
|
|
/* This encodes the density in Gb to SPD low nibble value as per JESD 4.1.2.L-5 R29 */
|
|
var densityGbToSPDEncoding = map[int]byte{
|
|
2: 0x3,
|
|
4: 0x4,
|
|
8: 0x5,
|
|
16: 0x6,
|
|
}
|
|
|
|
/*
|
|
* Tables 4 thru Table 7 from JESD79-4C.
|
|
* Maps density per die to row-column encoding for a device with x8/x16
|
|
* physical channel.
|
|
*/
|
|
var densityGbx8x16DieCapacityToRowColumnEncoding = map[int]byte{
|
|
2: 0x11, /* 14 rows, 10 columns */
|
|
4: 0x19, /* 15 rows, 10 columns */
|
|
8: 0x21, /* 16 rows, 10 columns */
|
|
16: 0x29, /* 17 rows, 10 columns */
|
|
}
|
|
|
|
/*
|
|
* Tables 169 & 170 in the JESD79-4C spec
|
|
* Maps die density to refresh timings. This is the same for x8 and x16
|
|
* devices.
|
|
*/
|
|
|
|
/* maps die density to rcf1 timing in pico seconds */
|
|
var tRFC1Encoding = map[int]int{
|
|
2: 160000,
|
|
4: 260000,
|
|
8: 350000,
|
|
16: 550000,
|
|
}
|
|
|
|
/* maps die density to rcf2 timing in pico seconds */
|
|
var tRFC2Encoding = map[int]int{
|
|
2: 110000,
|
|
4: 160000,
|
|
8: 260000,
|
|
16: 350000,
|
|
}
|
|
|
|
/* maps die density to rcf4 timing in pico seconds */
|
|
var tRFC4Encoding = map[int]int{
|
|
2: 90000,
|
|
4: 110000,
|
|
8: 160000,
|
|
16: 260000,
|
|
}
|
|
|
|
func getTRCMinPs(memAttribs *memAttributes) int {
|
|
return memAttribs.TAAMinPs + memAttribs.TRASMinPs
|
|
}
|
|
|
|
func getDefaultTCKMinPs(memAttribs *memAttributes) int {
|
|
/* value 2000000 = 2 * 1000000, where 1000000 is to convert mS to pS */
|
|
return 2000000 / memAttribs.SpeedMTps
|
|
}
|
|
|
|
type speedBinAttributes struct {
|
|
TRASMinPs int
|
|
TCKMaxPs int
|
|
}
|
|
|
|
var speedBinToSPDEncoding = map[int]speedBinAttributes{
|
|
1600: {
|
|
TRASMinPs: 35000,
|
|
TCKMaxPs: 1500,
|
|
},
|
|
1866: {
|
|
TRASMinPs: 34000,
|
|
TCKMaxPs: 1250,
|
|
},
|
|
2133: {
|
|
TRASMinPs: 33000,
|
|
TCKMaxPs: 1071,
|
|
},
|
|
2400: {
|
|
TRASMinPs: 32000,
|
|
TCKMaxPs: 937,
|
|
},
|
|
2666: {
|
|
TRASMinPs: 32000,
|
|
TCKMaxPs: 833,
|
|
},
|
|
2933: {
|
|
TRASMinPs: 32000,
|
|
TCKMaxPs: 750,
|
|
},
|
|
3200: {
|
|
TRASMinPs: 32000,
|
|
TCKMaxPs: 682,
|
|
},
|
|
}
|
|
|
|
func getBankGroups(memAttribs *memAttributes) byte {
|
|
var bg byte
|
|
|
|
switch memAttribs.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 encodeBankGroups(bg byte) byte {
|
|
var val byte
|
|
|
|
switch bg {
|
|
case 2:
|
|
val = 1
|
|
case 4:
|
|
val = 2
|
|
}
|
|
|
|
return val << 6
|
|
}
|
|
|
|
func encodeDensityBanks(memAttribs *memAttributes) byte {
|
|
var b byte
|
|
|
|
b = densityGbToSPDEncoding[memAttribs.CapacityPerDieGb]
|
|
b |= encodeBankGroups(getBankGroups(memAttribs))
|
|
/* No need to encode banksPerGroup.it's always 4 ([4:5] = 0) */
|
|
|
|
return b
|
|
}
|
|
|
|
func encodeSdramAddressing(memAttribs *memAttributes) byte {
|
|
var b byte
|
|
|
|
b = densityGbx8x16DieCapacityToRowColumnEncoding[memAttribs.CapacityPerDieGb]
|
|
|
|
return b
|
|
}
|
|
|
|
func encodePackageDeviceType(dies int) byte {
|
|
var b byte
|
|
|
|
if dies > 1 {
|
|
/* If more than one die, then this is a non-monolithic device. */
|
|
b = 1
|
|
} else {
|
|
/* If only single die, then this is a monolithic device. */
|
|
b = 0
|
|
}
|
|
|
|
return b << 7
|
|
}
|
|
|
|
func encodeSignalLoadingFromDieCount(dies int) byte {
|
|
var loading byte
|
|
|
|
/*
|
|
* If die count = 1, signal loading = "not specified" = 0
|
|
* If die count > 1, signal loading = "multi" = 2
|
|
*/
|
|
if dies == 1 {
|
|
loading = 0
|
|
} else {
|
|
loading = 1
|
|
}
|
|
|
|
return loading
|
|
}
|
|
|
|
func encodeDiesPerPackage(dies int) byte {
|
|
var b byte
|
|
|
|
b = encodePackageDeviceType(dies) /* Monolithic / Non-monolithic device */
|
|
b |= (byte(dies) - 1) << 4
|
|
|
|
return b
|
|
}
|
|
|
|
func encodePackageType(memAttribs *memAttributes) byte {
|
|
var b byte
|
|
|
|
b = encodeDiesPerPackage(memAttribs.DiesPerPackage)
|
|
b |= encodeSignalLoadingFromDieCount(memAttribs.DiesPerPackage)
|
|
|
|
return b
|
|
}
|
|
|
|
func encodeDataWidth(bitWidthPerDevice int) byte {
|
|
var width byte
|
|
|
|
switch bitWidthPerDevice {
|
|
case 8:
|
|
width = 1
|
|
case 16:
|
|
width = 2
|
|
}
|
|
|
|
return width
|
|
}
|
|
|
|
func encodeRanks(ranks int) byte {
|
|
var b byte
|
|
|
|
b = byte(ranks - 1)
|
|
|
|
return b << 3
|
|
}
|
|
|
|
func encodeModuleOrganization(memAttribs *memAttributes) byte {
|
|
var b byte
|
|
|
|
b = encodeDataWidth(memAttribs.dieBusWidth)
|
|
b |= encodeRanks(memAttribs.RanksPerPackage)
|
|
|
|
return b
|
|
}
|
|
|
|
func encodeTCKMin(memAttribs *memAttributes) byte {
|
|
return convPsToMtbByte(memAttribs.TCKMinPs)
|
|
}
|
|
|
|
func encodeTCKMinFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TCKMinPs)
|
|
}
|
|
|
|
func encodeTCKMax(memAttribs *memAttributes) byte {
|
|
return convPsToMtbByte(memAttribs.TCKMaxPs)
|
|
}
|
|
|
|
func encodeTCKMaxFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TCKMaxPs)
|
|
}
|
|
|
|
func divRoundUp(dividend int, divisor int) int {
|
|
return (dividend + divisor - 1) / divisor
|
|
}
|
|
|
|
func convNsToPs(timeNs int) int {
|
|
return timeNs * 1000
|
|
}
|
|
|
|
func convMtbToPs(mtb int) int {
|
|
return mtb * 125
|
|
}
|
|
|
|
func convPsToMtb(timePs int) int {
|
|
return divRoundUp(timePs, 125)
|
|
}
|
|
|
|
func convPsToMtbByte(timePs int) byte {
|
|
return byte(convPsToMtb(timePs) & 0xff)
|
|
}
|
|
|
|
func convPsToFtbByte(timePs int) byte {
|
|
mtb := convPsToMtb(timePs)
|
|
ftb := timePs - convMtbToPs(mtb)
|
|
|
|
return byte(ftb)
|
|
}
|
|
|
|
func encodeTAAMin(memAttribs *memAttributes) byte {
|
|
return convPsToMtbByte(memAttribs.TAAMinPs)
|
|
}
|
|
|
|
func encodeTAAMinFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TAAMinPs)
|
|
}
|
|
|
|
func encodeTRCDMin(memAttribs *memAttributes) byte {
|
|
return convPsToMtbByte(memAttribs.TRCDMinPs)
|
|
}
|
|
|
|
func encodeTRCDMinFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TRCDMinPs)
|
|
}
|
|
|
|
func encodeTRPMin(memAttribs *memAttributes) byte {
|
|
return convPsToMtbByte(memAttribs.TRPMinPs)
|
|
}
|
|
|
|
func encodeTRCMinFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TRCMinPs)
|
|
}
|
|
|
|
func encodeTRPMinFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TRPMinPs)
|
|
}
|
|
|
|
func encodeTRASRCMinMSNs(memAttribs *memAttributes) byte {
|
|
var b byte
|
|
|
|
b = byte((convPsToMtb(memAttribs.TRASMinPs) >> 4) & 0xf0)
|
|
b |= byte((convPsToMtb(memAttribs.TRCMinPs) >> 8) & 0x0f)
|
|
|
|
return b
|
|
}
|
|
|
|
func encodeTRASMinLsb(memAttribs *memAttributes) byte {
|
|
return byte(convPsToMtb(memAttribs.TRASMinPs) & 0xff)
|
|
}
|
|
|
|
func encodeTRCMinLsb(memAttribs *memAttributes) byte {
|
|
return byte(convPsToMtb(memAttribs.TRCMinPs) & 0xff)
|
|
}
|
|
|
|
/* This takes memAttribs.PackageBusWidth as an index */
|
|
var pageSizefromBusWidthEncoding = map[int]int{
|
|
8: 1,
|
|
16: 2,
|
|
}
|
|
|
|
/*
|
|
* Per Table 169 & Table 170 of Jedec JESD79-4C
|
|
* tFAW timing is based on :
|
|
* Speed bin and page size
|
|
*/
|
|
func getTFAWMinPs(memAttribs *memAttributes) int {
|
|
var tFAWFixed int
|
|
|
|
if pageSizefromBusWidthEncoding[memAttribs.PackageBusWidth] == 1 {
|
|
switch memAttribs.SpeedMTps {
|
|
case 1600:
|
|
tFAWFixed = 25000
|
|
case 1866:
|
|
tFAWFixed = 23000
|
|
default:
|
|
tFAWFixed = 21000
|
|
}
|
|
} else if pageSizefromBusWidthEncoding[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 updateTFAWMin(memAttribs *memAttributes) {
|
|
var tFAWFromTck int
|
|
|
|
if memAttribs.TFAWMinPs == 0 {
|
|
memAttribs.TFAWMinPs = getTFAWMinPs(memAttribs)
|
|
}
|
|
|
|
switch pageSizefromBusWidthEncoding[memAttribs.PackageBusWidth] {
|
|
case 1:
|
|
tFAWFromTck = 20 * memAttribs.TCKMinPs
|
|
case 2:
|
|
tFAWFromTck = 28 * memAttribs.TCKMinPs
|
|
}
|
|
|
|
if memAttribs.TFAWMinPs < tFAWFromTck {
|
|
memAttribs.TFAWMinPs = tFAWFromTck
|
|
}
|
|
}
|
|
|
|
func updateTRFC1Min(memAttribs *memAttributes) {
|
|
if memAttribs.TRFC1MinPs == 0 {
|
|
memAttribs.TRFC1MinPs = tRFC1Encoding[memAttribs.CapacityPerDieGb]
|
|
}
|
|
}
|
|
|
|
func updateTRFC2Min(memAttribs *memAttributes) {
|
|
if memAttribs.TRFC2MinPs == 0 {
|
|
memAttribs.TRFC2MinPs = tRFC2Encoding[memAttribs.CapacityPerDieGb]
|
|
}
|
|
}
|
|
|
|
func updateTRFC4Min(memAttribs *memAttributes) {
|
|
if memAttribs.TRFC4MinPs == 0 {
|
|
memAttribs.TRFC4MinPs = tRFC4Encoding[memAttribs.CapacityPerDieGb]
|
|
}
|
|
}
|
|
|
|
func getTRRDLMinPs(memAttribs *memAttributes) int {
|
|
var tRRDLFixed int
|
|
|
|
/*
|
|
* Per JESD79-4C Tables 169 & 170, tRRD_L is based on :
|
|
* Speed bin and page size
|
|
*/
|
|
switch pageSizefromBusWidthEncoding[memAttribs.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 updateTRRDLMin(memAttribs *memAttributes) {
|
|
var tRRDLFromTck int
|
|
|
|
if memAttribs.TRRDLMinPs == 0 {
|
|
memAttribs.TRRDLMinPs = getTRRDLMinPs(memAttribs)
|
|
}
|
|
|
|
tRRDLFromTck = 4 * memAttribs.TCKMinPs
|
|
|
|
if memAttribs.TRRDLMinPs < tRRDLFromTck {
|
|
memAttribs.TRRDLMinPs = tRRDLFromTck
|
|
}
|
|
}
|
|
|
|
var speedToTRRDSMinPsOneKPageSize = map[int]int{
|
|
1600: 5000,
|
|
1866: 4200,
|
|
2133: 3700,
|
|
2400: 3300,
|
|
2666: 3000,
|
|
2933: 2700,
|
|
3200: 2500,
|
|
}
|
|
|
|
var speedToTRRDSMinPsTwoKPageSize = map[int]int{
|
|
1600: 6000,
|
|
1866: 5300,
|
|
2133: 5300,
|
|
2400: 5300,
|
|
2666: 5300,
|
|
2933: 5300,
|
|
3200: 5300,
|
|
}
|
|
|
|
func getTRRDSMinPs(memAttribs *memAttributes) int {
|
|
var tRRDFixed int
|
|
|
|
switch pageSizefromBusWidthEncoding[memAttribs.PackageBusWidth] {
|
|
case 1:
|
|
tRRDFixed = speedToTRRDSMinPsOneKPageSize[memAttribs.SpeedMTps]
|
|
case 2:
|
|
tRRDFixed = speedToTRRDSMinPsTwoKPageSize[memAttribs.SpeedMTps]
|
|
}
|
|
|
|
return tRRDFixed
|
|
}
|
|
|
|
func updateTRRDSMin(memAttribs *memAttributes) {
|
|
var tRRDFromTck int
|
|
|
|
if memAttribs.TRRDSMinPs == 0 {
|
|
memAttribs.TRRDSMinPs = getTRRDSMinPs(memAttribs)
|
|
}
|
|
|
|
tRRDFromTck = 4 * memAttribs.TCKMinPs
|
|
|
|
if memAttribs.TRRDSMinPs < tRRDFromTck {
|
|
memAttribs.TRRDSMinPs = tRRDFromTck
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Per JESD79-4C Tables 169 and 170,
|
|
* tCCD_L is based on :
|
|
* Speed Bin
|
|
*/
|
|
func getTCCDLMinPs(memAttribs *memAttributes) int {
|
|
var tCCDLFixed int
|
|
|
|
switch memAttribs.SpeedMTps {
|
|
case 1600:
|
|
tCCDLFixed = 6250
|
|
case 1866:
|
|
tCCDLFixed = 5355
|
|
case 2133:
|
|
tCCDLFixed = 5355
|
|
default:
|
|
tCCDLFixed = 5000
|
|
}
|
|
|
|
return tCCDLFixed
|
|
}
|
|
|
|
func updateTCCDLMin(memAttribs *memAttributes) {
|
|
var tCCDLFromTck int
|
|
|
|
if memAttribs.TCCDLMinPs == 0 {
|
|
memAttribs.TCCDLMinPs = getTCCDLMinPs(memAttribs)
|
|
}
|
|
|
|
tCCDLFromTck = 5 * memAttribs.TCKMinPs
|
|
|
|
if memAttribs.TCCDLMinPs < tCCDLFromTck {
|
|
memAttribs.TCCDLMinPs = tCCDLFromTck
|
|
}
|
|
}
|
|
|
|
func encodeTRFC1MinLsb(memAttribs *memAttributes) byte {
|
|
var mtb int
|
|
|
|
mtb = convPsToMtb(memAttribs.TRFC1MinPs)
|
|
|
|
return byte(mtb & 0xff)
|
|
}
|
|
|
|
func encodeTRFC1MinMsb(memAttribs *memAttributes) byte {
|
|
var mtb int
|
|
|
|
mtb = convPsToMtb(memAttribs.TRFC1MinPs)
|
|
|
|
return byte((mtb >> 8) & 0xff)
|
|
}
|
|
|
|
func encodeTRFC2MinLsb(memAttribs *memAttributes) byte {
|
|
var mtb int
|
|
|
|
mtb = convPsToMtb(memAttribs.TRFC2MinPs)
|
|
|
|
return byte(mtb & 0xff)
|
|
}
|
|
|
|
func encodeTRFC2MinMsb(memAttribs *memAttributes) byte {
|
|
var mtb int
|
|
|
|
mtb = convPsToMtb(memAttribs.TRFC2MinPs)
|
|
|
|
return byte((mtb >> 8) & 0xff)
|
|
}
|
|
|
|
func encodeTRFC4MinLsb(memAttribs *memAttributes) byte {
|
|
var mtb int
|
|
|
|
mtb = convPsToMtb(memAttribs.TRFC4MinPs)
|
|
|
|
return byte(mtb & 0xff)
|
|
}
|
|
|
|
func encodeTRFC4MinMsb(memAttribs *memAttributes) byte {
|
|
var mtb int
|
|
|
|
mtb = convPsToMtb(memAttribs.TRFC4MinPs)
|
|
|
|
return byte((mtb >> 8) & 0xff)
|
|
}
|
|
|
|
func encodeTFAWMinMSN(memAttribs *memAttributes) byte {
|
|
var mtb int
|
|
|
|
mtb = convPsToMtb(memAttribs.TFAWMinPs)
|
|
|
|
return byte((mtb >> 8) & 0x0f)
|
|
}
|
|
|
|
func encodeTFAWMinLsb(memAttribs *memAttributes) byte {
|
|
var mtb int
|
|
|
|
mtb = convPsToMtb(memAttribs.TFAWMinPs)
|
|
|
|
return byte(mtb & 0xff)
|
|
}
|
|
|
|
func encodeCASFirstByte(memAttribs *memAttributes) byte {
|
|
return memAttribs.CASFirstByte
|
|
}
|
|
|
|
func encodeCASSecondByte(memAttribs *memAttributes) byte {
|
|
return memAttribs.CASSecondByte
|
|
}
|
|
|
|
func encodeCASThirdByte(memAttribs *memAttributes) byte {
|
|
return memAttribs.CASThirdByte
|
|
}
|
|
|
|
func encodeCASFourthByte(memAttribs *memAttributes) byte {
|
|
return memAttribs.CASFourthByte
|
|
}
|
|
|
|
func encodeTRRDSMin(memAttribs *memAttributes) byte {
|
|
return convPsToMtbByte(memAttribs.TRRDSMinPs)
|
|
}
|
|
|
|
func encodeTRRDSMinFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TRRDSMinPs)
|
|
}
|
|
|
|
func encodeTRRDLMin(memAttribs *memAttributes) byte {
|
|
return convPsToMtbByte(memAttribs.TRRDLMinPs)
|
|
}
|
|
|
|
func encodeTRRDLMinFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TRRDLMinPs)
|
|
}
|
|
|
|
func encodeTCCDLMin(memAttribs *memAttributes) byte {
|
|
return convPsToMtbByte(memAttribs.TCCDLMinPs)
|
|
}
|
|
|
|
func encodeTCCDLMinFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TCCDLMinPs)
|
|
}
|
|
|
|
func encodeTWRMinMSN(memAttribs *memAttributes) byte {
|
|
return byte((convPsToMtb(TimingValueTWRMinPs) >> 8) & 0x0f)
|
|
}
|
|
|
|
func encodeTWRMinLsb(memAttribs *memAttributes) byte {
|
|
return byte(convPsToMtb(TimingValueTWRMinPs) & 0xff)
|
|
}
|
|
|
|
func encodeTWTRMinMSNs(memAttribs *memAttributes) byte {
|
|
var b byte
|
|
|
|
b = byte((convPsToMtb(memAttribs.TWTRLMinPs) >> 4) & 0xf0)
|
|
b |= byte((convPsToMtb(memAttribs.TWTRSMinPs) >> 8) & 0x0f)
|
|
|
|
return b
|
|
}
|
|
|
|
func encodeTWTRSMinLsb(memAttribs *memAttributes) byte {
|
|
return byte(convPsToMtb(memAttribs.TWTRSMinPs) & 0xff)
|
|
}
|
|
|
|
func encodeTWTRLMinLsb(memAttribs *memAttributes) byte {
|
|
return byte(convPsToMtb(memAttribs.TWTRLMinPs) & 0xff)
|
|
}
|
|
|
|
type SPDMemAttribFunc func(*memAttributes) byte
|
|
type SPDConvConstFunc func() byte
|
|
|
|
type SPDAttribTableEntry struct {
|
|
constVal byte
|
|
getVal SPDMemAttribFunc
|
|
}
|
|
|
|
const (
|
|
/* SPD Byte Index */
|
|
SPDIndexSize = 0
|
|
SPDIndexRevision = 1
|
|
SPDIndexMemoryType = 2
|
|
SPDIndexModuleType = 3
|
|
SPDIndexDensityBanks = 4
|
|
SPDIndexAddressing = 5
|
|
SPDIndexPackageType = 6
|
|
SPDIndexOptionalFeatures = 7
|
|
SPDIndexModuleOrganization = 12
|
|
SPDIndexBusWidth = 13
|
|
SPDIndexTimebases = 17
|
|
SPDIndexTCKMin = 18
|
|
SPDIndexTCKMax = 19
|
|
SPDIndexCASFirstByte = 20
|
|
SPDIndexCASSecondByte = 21
|
|
SPDIndexCASThirdByte = 22
|
|
SPDIndexCASFourthByte = 23
|
|
SPDIndexTAAMin = 24
|
|
SPDIndexTRCDMin = 25
|
|
SPDIndexTRPMin = 26
|
|
SPDIndexTRASRCMinMSNs = 27
|
|
SPDIndexTRASMinLsb = 28
|
|
SPDIndexTRCMinLsb = 29
|
|
SPDIndexTRFC1MinLsb = 30
|
|
SPDIndexTRFC1MinMsb = 31
|
|
SPDIndexTRFC2MinLsb = 32
|
|
SPDIndexTRFC2MinMsb = 33
|
|
SPDIndexTRFC4MinLsb = 34
|
|
SPDIndexTRFC4MinMsb = 35
|
|
SPDIndexTFAWMinMSN = 36
|
|
SPDIndexTFAWMinLsb = 37
|
|
SPDIndexTRRDSMin = 38
|
|
SPDIndexTRRDLMin = 39
|
|
SPDIndexTCCDLMin = 40
|
|
SPDIndexTWRMinMSN = 41
|
|
SPDIndexTWRMinLsb = 42
|
|
SPDIndexTWTRMinMSNs = 43
|
|
SPDIndexWTRSMinLsb = 44
|
|
SPDIndexWTRLMinLsb = 45
|
|
SPDIndexTCCDLMinFineOffset = 117
|
|
SPDIndexTRRDLMinFineOffset = 118
|
|
SPDIndexTRRDSMinFineOffset = 119
|
|
SPDIndexTRCMinFineOffset = 120
|
|
SPDIndexTRPMinFineOffset = 121
|
|
SPDIndexTRCDMinFineOffset = 122
|
|
SPDIndexTAAMinFineOffset = 123
|
|
SPDIndexTCKMaxFineOffset = 124
|
|
SPDIndexTCKMinFineOffset = 125
|
|
SPDIndexManufacturerPartNumberStartByte = 329
|
|
SPDIndexManufacturerPartNumberEndByte = 348
|
|
|
|
/* SPD Byte Value */
|
|
|
|
/*
|
|
* From JEDEC spec:
|
|
* 6:4 (Bytes total) = 2 (512 bytes)
|
|
* 3:0 (Bytes used) = 3 (384 bytes)
|
|
* Set to 0x23 for DDR4.
|
|
*/
|
|
SPDValueSize = 0x23
|
|
|
|
/*
|
|
* From JEDEC spec: Revision 1.1
|
|
* Set to 0x11.
|
|
*/
|
|
SPDValueRevision = 0x11
|
|
|
|
/* DDR4 memory type = 0x0C */
|
|
SPDValueMemoryType = 0x0C
|
|
|
|
/*
|
|
* From JEDEC spec:
|
|
* Module Type [0:3] :
|
|
* 0 = Undefined
|
|
* 1 = RDIMM (width = 133.35 mm nom)
|
|
* 2 = UDIMM (width = 133.35 mm nom)
|
|
* 3 = SO-DIMM (width = 68.60 mm nom)
|
|
* 4 = LRDIMM (width = 133.35 mm nom)
|
|
*
|
|
* DDR4 on TGL uses SO-DIMM type for for both memory down and DIMM config.
|
|
* Set to 0x03.
|
|
*/
|
|
SPDValueModuleType = 0x03
|
|
|
|
/*
|
|
* From JEDEC spec:
|
|
* 5:4 (Maximum Activate Window) = 00 (8192 * tREFI)
|
|
* 3:0 (Maximum Activate Count) = 1000 (Unlimited MAC)
|
|
*
|
|
* Needs to come from datasheet, but most parts seem to support unlimited MAC.
|
|
* MR#24 OP3
|
|
*/
|
|
SPDValueOptionalFeatures = 0x08
|
|
|
|
/*
|
|
* From JEDEC spec:
|
|
* 2:0 Primary Bus Width in Bits = 011 (x64 always)
|
|
* Set to 0x03.
|
|
*/
|
|
SPDValueModuleBusWidth = 0x03
|
|
|
|
/*
|
|
* From JEDEC spec:
|
|
* 3:2 (MTB) = 00 (0.125ns)
|
|
* 1:0 (FTB) = 00 (1ps)
|
|
* Set to 0x00.
|
|
*/
|
|
SPDValueTimebases = 0x00
|
|
|
|
/* CAS fourth byte: All bits are reserved */
|
|
SPDValueCASFourthByte = 0x00
|
|
|
|
/* As per JEDEC spec, unused digits of manufacturer part number are left as blank. */
|
|
SPDValueManufacturerPartNumberBlank = 0x20
|
|
)
|
|
|
|
const (
|
|
/*
|
|
* As per Table 75 of Jedec spec 4.1.20-L-5 R29 v103:
|
|
* tWRMin = 15nS for all DDR4 Speed Bins
|
|
* Set to 15000 pS
|
|
*/
|
|
TimingValueTWRMinPs = 15000
|
|
|
|
/*
|
|
* As per Table 78 of Jedec spec 4.1.20-L-5 R29 v103:
|
|
* tWTR_SMin = 2.5nS for all DDR4 Speed Bins
|
|
* Set to 2500 pS
|
|
*/
|
|
TimingValueTWTRSMinPs = 2500
|
|
|
|
/*
|
|
* As per Table 80 of Jedec spec 4.1.20-L-5 R29 v103:
|
|
* tWTR_LMin = 7.5 nS for all DDR4 Speed Bins
|
|
* Set to 7500 pS
|
|
*/
|
|
TimingValueTWTRLMinPs = 7500
|
|
)
|
|
|
|
var SPDAttribTable = map[int]SPDAttribTableEntry{
|
|
SPDIndexSize: {constVal: SPDValueSize},
|
|
SPDIndexRevision: {constVal: SPDValueRevision},
|
|
SPDIndexMemoryType: {constVal: SPDValueMemoryType},
|
|
SPDIndexModuleType: {constVal: SPDValueModuleType},
|
|
SPDIndexDensityBanks: {getVal: encodeDensityBanks},
|
|
SPDIndexAddressing: {getVal: encodeSdramAddressing},
|
|
SPDIndexPackageType: {getVal: encodePackageType},
|
|
SPDIndexOptionalFeatures: {constVal: SPDValueOptionalFeatures},
|
|
SPDIndexModuleOrganization: {getVal: encodeModuleOrganization},
|
|
SPDIndexBusWidth: {constVal: SPDValueModuleBusWidth},
|
|
SPDIndexTimebases: {constVal: SPDValueTimebases},
|
|
SPDIndexTCKMin: {getVal: encodeTCKMin},
|
|
SPDIndexTCKMinFineOffset: {getVal: encodeTCKMinFineOffset},
|
|
SPDIndexTCKMax: {getVal: encodeTCKMax},
|
|
SPDIndexTCKMaxFineOffset: {getVal: encodeTCKMaxFineOffset},
|
|
SPDIndexCASFirstByte: {getVal: encodeCASFirstByte},
|
|
SPDIndexCASSecondByte: {getVal: encodeCASSecondByte},
|
|
SPDIndexCASThirdByte: {getVal: encodeCASThirdByte},
|
|
SPDIndexCASFourthByte: {getVal: encodeCASFourthByte},
|
|
SPDIndexTAAMin: {getVal: encodeTAAMin},
|
|
SPDIndexTAAMinFineOffset: {getVal: encodeTAAMinFineOffset},
|
|
SPDIndexTRCDMin: {getVal: encodeTRCDMin},
|
|
SPDIndexTRCDMinFineOffset: {getVal: encodeTRCDMinFineOffset},
|
|
SPDIndexTRPMin: {getVal: encodeTRPMin},
|
|
SPDIndexTRPMinFineOffset: {getVal: encodeTRPMinFineOffset},
|
|
SPDIndexTRASRCMinMSNs: {getVal: encodeTRASRCMinMSNs},
|
|
SPDIndexTRASMinLsb: {getVal: encodeTRASMinLsb},
|
|
SPDIndexTRCMinLsb: {getVal: encodeTRCMinLsb},
|
|
SPDIndexTRCMinFineOffset: {getVal: encodeTRCMinFineOffset},
|
|
SPDIndexTRFC1MinLsb: {getVal: encodeTRFC1MinLsb},
|
|
SPDIndexTRFC1MinMsb: {getVal: encodeTRFC1MinMsb},
|
|
SPDIndexTRFC2MinLsb: {getVal: encodeTRFC2MinLsb},
|
|
SPDIndexTRFC2MinMsb: {getVal: encodeTRFC2MinMsb},
|
|
SPDIndexTRFC4MinLsb: {getVal: encodeTRFC4MinLsb},
|
|
SPDIndexTRFC4MinMsb: {getVal: encodeTRFC4MinMsb},
|
|
SPDIndexTFAWMinMSN: {getVal: encodeTFAWMinMSN},
|
|
SPDIndexTFAWMinLsb: {getVal: encodeTFAWMinLsb},
|
|
SPDIndexTRRDSMin: {getVal: encodeTRRDSMin},
|
|
SPDIndexTRRDSMinFineOffset: {getVal: encodeTRRDSMinFineOffset},
|
|
SPDIndexTRRDLMin: {getVal: encodeTRRDLMin},
|
|
SPDIndexTRRDLMinFineOffset: {getVal: encodeTRRDLMinFineOffset},
|
|
SPDIndexTCCDLMin: {getVal: encodeTCCDLMin},
|
|
SPDIndexTCCDLMinFineOffset: {getVal: encodeTCCDLMinFineOffset},
|
|
SPDIndexTWRMinMSN: {getVal: encodeTWRMinMSN},
|
|
SPDIndexTWRMinLsb: {getVal: encodeTWRMinLsb},
|
|
SPDIndexTWTRMinMSNs: {getVal: encodeTWTRMinMSNs},
|
|
SPDIndexWTRSMinLsb: {getVal: encodeTWTRSMinLsb},
|
|
SPDIndexWTRLMinLsb: {getVal: encodeTWTRLMinLsb},
|
|
}
|
|
|
|
type memParts struct {
|
|
MemParts []memPart `json:"parts"`
|
|
}
|
|
|
|
type memPart struct {
|
|
Name string
|
|
Attribs memAttributes
|
|
SPDFileName string
|
|
}
|
|
|
|
func writeSPDManifest(memParts *memParts, SPDDirName string) error {
|
|
var s string
|
|
|
|
fmt.Printf("Generating SPD Manifest with following entries:\n")
|
|
|
|
for i := 0; i < len(memParts.MemParts); i++ {
|
|
fmt.Printf("%-40s %s\n", memParts.MemParts[i].Name, memParts.MemParts[i].SPDFileName)
|
|
s += fmt.Sprintf("%s,%s\n", memParts.MemParts[i].Name, memParts.MemParts[i].SPDFileName)
|
|
}
|
|
|
|
return ioutil.WriteFile(filepath.Join(SPDDirName, SPDManifestFileName), []byte(s), 0644)
|
|
}
|
|
|
|
func isManufacturerPartNumberByte(index int) bool {
|
|
if index >= SPDIndexManufacturerPartNumberStartByte && index <= SPDIndexManufacturerPartNumberEndByte {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func getSPDByte(index int, memAttribs *memAttributes) byte {
|
|
e, ok := SPDAttribTable[index]
|
|
if ok == false {
|
|
if isManufacturerPartNumberByte(index) {
|
|
return SPDValueManufacturerPartNumberBlank
|
|
}
|
|
return 0x00
|
|
}
|
|
|
|
if e.getVal != nil {
|
|
return e.getVal(memAttribs)
|
|
}
|
|
|
|
return e.constVal
|
|
}
|
|
|
|
func createSPD(memAttribs *memAttributes) string {
|
|
var s string
|
|
|
|
for i := 0; i < 512; i++ {
|
|
var b byte = 0
|
|
if memAttribs != nil {
|
|
b = getSPDByte(i, memAttribs)
|
|
}
|
|
|
|
if (i+1)%16 == 0 {
|
|
s += fmt.Sprintf("%02X\n", b)
|
|
} else {
|
|
s += fmt.Sprintf("%02X ", b)
|
|
}
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func dedupeMemoryPart(dedupedParts []*memPart, memPart *memPart) bool {
|
|
for i := 0; i < len(dedupedParts); i++ {
|
|
if reflect.DeepEqual(dedupedParts[i].Attribs, memPart.Attribs) {
|
|
memPart.SPDFileName = dedupedParts[i].SPDFileName
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func generateSPD(memPart *memPart, SPDId int, SPDDirName string) {
|
|
s := createSPD(&memPart.Attribs)
|
|
memPart.SPDFileName = fmt.Sprintf("ddr4-spd-%d.hex", SPDId)
|
|
ioutil.WriteFile(filepath.Join(SPDDirName, memPart.SPDFileName), []byte(s), 0644)
|
|
}
|
|
|
|
func generateEmptySPD(SPDDirName string) {
|
|
|
|
s := createSPD(nil)
|
|
SPDFileName := "ddr4-spd-empty.hex"
|
|
ioutil.WriteFile(filepath.Join(SPDDirName, SPDFileName), []byte(s), 0644)
|
|
}
|
|
|
|
func readMemoryParts(memParts *memParts, memPartsFileName string) error {
|
|
databytes, err := ioutil.ReadFile(memPartsFileName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Strip comments from json file
|
|
re := regexp.MustCompile(`(?m)^\s*//.*`)
|
|
databytes = re.ReplaceAll(databytes, []byte(""))
|
|
|
|
return json.Unmarshal(databytes, memParts)
|
|
}
|
|
|
|
func validateSpeedMTps(speedBin int) error {
|
|
if _, ok := speedBinToSPDEncoding[speedBin]; ok == false {
|
|
return fmt.Errorf("Incorrect speed bin: DDR4-", speedBin)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateCapacityPerDie(capacityPerDieGb int) error {
|
|
if _, ok := densityGbToSPDEncoding[capacityPerDieGb]; ok == false {
|
|
return fmt.Errorf("Incorrect capacity per die: ", capacityPerDieGb)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateDiesPerPackage(dieCount int) error {
|
|
if dieCount >= 1 && dieCount <= 2 {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("Incorrect dies per package count: ", dieCount)
|
|
}
|
|
|
|
func validatePackageBusWidth(width int) error {
|
|
if width != 8 && width != 16 {
|
|
return fmt.Errorf("Incorrect device bus width: ", width)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateRanksPerPackage(ranks int) error {
|
|
if ranks >= 1 && ranks <= 2 {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("Incorrect package ranks: ", ranks)
|
|
}
|
|
|
|
func validateCASLatency(CL int) error {
|
|
if CL >= 10 && CL <= 24 && CL != 23 {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("Incorrect CAS latency: ", CL)
|
|
}
|
|
|
|
/*
|
|
1) validate memory parts
|
|
2) remove any fields that Intel does not care about
|
|
*/
|
|
|
|
/* verify the supplied CAS Latencies supported does not match default */
|
|
func verifySupportedCASLatencies(part *memPart) error {
|
|
if part.Attribs.CASLatencies == getDefaultCASLatencies(&part.Attribs) {
|
|
return fmt.Errorf("CASLatencies for %s already matches default,\nPlease remove CASLatencies override line from the %s part attributes in the global part list and regenerate SPD Manifest", part.Name, part.Name)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateMemoryParts(memParts *memParts) error {
|
|
memPartExists := make(map[string]bool)
|
|
|
|
for i := 0; i < len(memParts.MemParts); i++ {
|
|
if memPartExists[memParts.MemParts[i].Name] {
|
|
return fmt.Errorf(memParts.MemParts[i].Name + " is duplicated in mem_parts_list_json")
|
|
}
|
|
memPartExists[memParts.MemParts[i].Name] = true
|
|
|
|
if err := validateSpeedMTps(memParts.MemParts[i].Attribs.SpeedMTps); err != nil {
|
|
return err
|
|
}
|
|
if err := validateCapacityPerDie(memParts.MemParts[i].Attribs.CapacityPerDieGb); err != nil {
|
|
return err
|
|
}
|
|
if err := validateDiesPerPackage(memParts.MemParts[i].Attribs.DiesPerPackage); err != nil {
|
|
return err
|
|
}
|
|
if err := validatePackageBusWidth(memParts.MemParts[i].Attribs.PackageBusWidth); err != nil {
|
|
return err
|
|
}
|
|
if err := validateRanksPerPackage(memParts.MemParts[i].Attribs.RanksPerPackage); err != nil {
|
|
return err
|
|
}
|
|
if err := validateCASLatency(memParts.MemParts[i].Attribs.CL_nRCD_nRP); err != nil {
|
|
return err
|
|
}
|
|
/* If CAS Latency was supplied, make sure it doesn't match default value */
|
|
if len(memParts.MemParts[i].Attribs.CASLatencies) != 0 {
|
|
if err := verifySupportedCASLatencies(&memParts.MemParts[i]); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
const (
|
|
/* First Byte */
|
|
CAS9 = 1 << 2
|
|
CAS10 = 1 << 3
|
|
CAS11 = 1 << 4
|
|
CAS12 = 1 << 5
|
|
CAS13 = 1 << 6
|
|
CAS14 = 1 << 7
|
|
/* Second Byte */
|
|
CAS15 = 1 << 0
|
|
CAS16 = 1 << 1
|
|
CAS17 = 1 << 2
|
|
CAS18 = 1 << 3
|
|
CAS19 = 1 << 4
|
|
CAS20 = 1 << 5
|
|
CAS21 = 1 << 6
|
|
CAS22 = 1 << 7
|
|
/* Third Byte */
|
|
CAS24 = 1 << 1
|
|
)
|
|
|
|
func encodeLatencies(latency int, memAttribs *memAttributes) error {
|
|
switch latency {
|
|
case 9:
|
|
memAttribs.CASFirstByte |= CAS9
|
|
case 10:
|
|
memAttribs.CASFirstByte |= CAS10
|
|
case 11:
|
|
memAttribs.CASFirstByte |= CAS11
|
|
case 12:
|
|
memAttribs.CASFirstByte |= CAS12
|
|
case 13:
|
|
memAttribs.CASFirstByte |= CAS13
|
|
case 14:
|
|
memAttribs.CASFirstByte |= CAS14
|
|
case 15:
|
|
memAttribs.CASSecondByte |= CAS15
|
|
case 16:
|
|
memAttribs.CASSecondByte |= CAS16
|
|
case 17:
|
|
memAttribs.CASSecondByte |= CAS17
|
|
case 18:
|
|
memAttribs.CASSecondByte |= CAS18
|
|
case 19:
|
|
memAttribs.CASSecondByte |= CAS19
|
|
case 20:
|
|
memAttribs.CASSecondByte |= CAS20
|
|
case 21:
|
|
memAttribs.CASSecondByte |= CAS21
|
|
case 22:
|
|
memAttribs.CASSecondByte |= CAS22
|
|
case 24:
|
|
memAttribs.CASThirdByte |= CAS24
|
|
default:
|
|
fmt.Errorf("Incorrect CAS Latency: ", latency)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
/* Default CAS Latencies from Speed Bin tables in JEDS79-4C */
|
|
func getDefaultCASLatencies(memAttribs *memAttributes) string {
|
|
var str string
|
|
|
|
switch memAttribs.SpeedMTps {
|
|
case 1600:
|
|
switch memAttribs.CL_nRCD_nRP {
|
|
case 10:
|
|
str = "9 10 11 12"
|
|
case 11:
|
|
str = "9 11 12"
|
|
case 12:
|
|
str = "10 12"
|
|
}
|
|
case 1866:
|
|
switch memAttribs.CL_nRCD_nRP {
|
|
case 12:
|
|
str = "9 10 12 13 14"
|
|
case 13:
|
|
str = "9 11 12 13 14"
|
|
case 14:
|
|
str = "10 12 14"
|
|
}
|
|
case 2133:
|
|
switch memAttribs.CL_nRCD_nRP {
|
|
case 14:
|
|
str = "9 10 12 14 15 16"
|
|
case 15:
|
|
str = "9 11 12 13 14 15 16"
|
|
case 16:
|
|
str = "10 12 14 16"
|
|
}
|
|
case 2400:
|
|
switch memAttribs.CL_nRCD_nRP {
|
|
case 15:
|
|
str = "9 10 12 14 15 16 17 18"
|
|
case 16:
|
|
str = "9 11 12 13 14 15 16 17 18"
|
|
case 17:
|
|
str = "10 11 12 13 14 15 16 17 18"
|
|
case 18:
|
|
str = "10 12 14 16 18"
|
|
}
|
|
case 2666:
|
|
switch memAttribs.CL_nRCD_nRP {
|
|
case 17:
|
|
str = "9 10 11 12 13 14 15 16 17 18 19 20"
|
|
case 18:
|
|
str = "9 10 11 12 13 14 15 16 17 18 19 20"
|
|
case 19:
|
|
str = "10 11 12 13 14 15 16 17 18 19 20"
|
|
case 20:
|
|
str = "10 12 14 16 18 20"
|
|
}
|
|
case 2933:
|
|
switch memAttribs.CL_nRCD_nRP {
|
|
case 19:
|
|
str = "9 10 11 12 13 14 15 16 17 18 19 20 21 22"
|
|
case 20:
|
|
str = "10 11 12 13 14 15 16 17 18 19 20 21 22"
|
|
case 21:
|
|
str = "10 11 12 13 14 15 16 17 18 19 20 21 22"
|
|
case 22:
|
|
str = "10 12 14 16 18 20 22"
|
|
}
|
|
case 3200:
|
|
switch memAttribs.CL_nRCD_nRP {
|
|
case 20:
|
|
str = "9 10 11 12 13 14 15 16 17 18 19 20 21 22 24"
|
|
case 22:
|
|
str = "10 11 12 13 14 15 16 17 18 19 20 21 22 24"
|
|
case 24:
|
|
str = "10 12 14 16 18 20 22 24"
|
|
}
|
|
}
|
|
|
|
return str
|
|
}
|
|
|
|
func updateDieBusWidth(memAttribs *memAttributes) {
|
|
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 updateCAS(memAttribs *memAttributes) error {
|
|
if len(memAttribs.CASLatencies) == 0 {
|
|
memAttribs.CASLatencies = getDefaultCASLatencies(memAttribs)
|
|
}
|
|
|
|
latencies := strings.Fields(memAttribs.CASLatencies)
|
|
for i := 0; i < len(latencies); i++ {
|
|
latency, err := strconv.Atoi(latencies[i])
|
|
if err != nil {
|
|
return fmt.Errorf("Unable to convert latency ", latencies[i])
|
|
}
|
|
if err := encodeLatencies(latency, memAttribs); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getTAAMinPs(memAttribs *memAttributes) int {
|
|
return (memAttribs.CL_nRCD_nRP * 2000000) / memAttribs.SpeedMTps
|
|
}
|
|
|
|
func updateTAAMin(memAttribs *memAttributes) {
|
|
if memAttribs.TAAMinPs == 0 {
|
|
memAttribs.TAAMinPs = getTAAMinPs(memAttribs)
|
|
}
|
|
}
|
|
|
|
func updateTRCDMin(memAttribs *memAttributes) {
|
|
/* tRCDmin is same as tAAmin for all cases */
|
|
if memAttribs.TRCDMinPs == 0 {
|
|
memAttribs.TRCDMinPs = getTAAMinPs(memAttribs)
|
|
}
|
|
}
|
|
|
|
func updateTRPMin(memAttribs *memAttributes) {
|
|
/* tRPmin is same as tAAmin for all cases */
|
|
if memAttribs.TRPMinPs == 0 {
|
|
memAttribs.TRPMinPs = getTAAMinPs(memAttribs)
|
|
}
|
|
}
|
|
|
|
func updateTRASMin(memAttribs *memAttributes) {
|
|
if memAttribs.TRASMinPs == 0 {
|
|
memAttribs.TRASMinPs = speedBinToSPDEncoding[memAttribs.SpeedMTps].TRASMinPs
|
|
}
|
|
}
|
|
|
|
func updateTRCMin(memAttribs *memAttributes) {
|
|
if memAttribs.TRCMinPs == 0 {
|
|
memAttribs.TRCMinPs = getTRCMinPs(memAttribs)
|
|
}
|
|
}
|
|
|
|
func updateTCK(memAttribs *memAttributes) {
|
|
if memAttribs.TCKMinPs == 0 {
|
|
memAttribs.TCKMinPs = getDefaultTCKMinPs(memAttribs)
|
|
}
|
|
if memAttribs.TCKMaxPs == 0 {
|
|
memAttribs.TCKMaxPs = speedBinToSPDEncoding[memAttribs.SpeedMTps].TCKMaxPs
|
|
}
|
|
}
|
|
|
|
func updateTWRMin(memAttribs *memAttributes) {
|
|
if memAttribs.TWRMinPs == 0 {
|
|
memAttribs.TWRMinPs = TimingValueTWRMinPs
|
|
}
|
|
}
|
|
|
|
func updateTWTRMin(memAttribs *memAttributes) {
|
|
if memAttribs.TWTRLMinPs == 0 {
|
|
memAttribs.TWTRLMinPs = TimingValueTWTRLMinPs
|
|
}
|
|
if memAttribs.TWTRSMinPs == 0 {
|
|
memAttribs.TWTRSMinPs = TimingValueTWTRSMinPs
|
|
}
|
|
}
|
|
|
|
func updateMemoryAttributes(memAttribs *memAttributes) {
|
|
updateDieBusWidth(memAttribs)
|
|
updateTCK(memAttribs)
|
|
updateTAAMin(memAttribs)
|
|
updateTRCDMin(memAttribs)
|
|
updateTRPMin(memAttribs)
|
|
updateTRASMin(memAttribs)
|
|
updateTRCMin(memAttribs)
|
|
updateTWRMin(memAttribs)
|
|
updateTWTRMin(memAttribs)
|
|
updateCAS(memAttribs)
|
|
updateTRFC1Min(memAttribs)
|
|
updateTRFC2Min(memAttribs)
|
|
updateTRFC4Min(memAttribs)
|
|
updateTCCDLMin(memAttribs)
|
|
updateTRRDSMin(memAttribs)
|
|
updateTRRDLMin(memAttribs)
|
|
updateTFAWMin(memAttribs)
|
|
}
|
|
|
|
func isPlatformSupported(platform string) error {
|
|
var ok bool
|
|
|
|
currPlatform, ok = platformMap[platform]
|
|
if ok == false {
|
|
return fmt.Errorf("Unsupported platform: ", platform)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func usage() {
|
|
fmt.Printf("\nUsage: %s <spd_dir> <mem_parts_list_json> <platform>\n\n", os.Args[0])
|
|
fmt.Printf(" where,\n")
|
|
fmt.Printf(" spd_dir = Directory path containing SPD files and manifest generated by gen_spd.go\n")
|
|
fmt.Printf(" mem_parts_list_json = JSON File containing list of memory parts and attributes\n")
|
|
fmt.Printf(" platform = SoC Platform for which the SPDs are being generated\n\n\n")
|
|
}
|
|
|
|
func main() {
|
|
if len(os.Args) != 4 {
|
|
usage()
|
|
log.Fatal("Incorrect number of arguments")
|
|
}
|
|
|
|
var memParts memParts
|
|
var dedupedParts []*memPart
|
|
|
|
SPDDir, GlobalMemPartsFile, Platform := os.Args[1], os.Args[2], strings.ToUpper(os.Args[3])
|
|
|
|
if err := isPlatformSupported(Platform); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
if err := readMemoryParts(&memParts, GlobalMemPartsFile); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
if err := validateMemoryParts(&memParts); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
SPDId := 1
|
|
|
|
for i := 0; i < len(memParts.MemParts); i++ {
|
|
updateMemoryAttributes(&memParts.MemParts[i].Attribs)
|
|
if dedupeMemoryPart(dedupedParts, &memParts.MemParts[i]) == false {
|
|
generateSPD(&memParts.MemParts[i], SPDId, SPDDir)
|
|
SPDId++
|
|
dedupedParts = append(dedupedParts, &memParts.MemParts[i])
|
|
}
|
|
}
|
|
|
|
generateEmptySPD(SPDDir)
|
|
|
|
if err := writeSPDManifest(&memParts, SPDDir); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|