a662648a7f
For boards that have already assigned memory ids, there needs to be a way to fix parts to a specific id. After assigning all the fixed ids the tool still attempts to minimize the SPDs entries. Since a fixed ID could be anywhere, gaps can be created in the list. So an empty SPD entry is created to fill the gaps in the list until they are used. BUG=b:162939176 TEST=Generate various outputs Signed-off-by: Rob Barnes <robbarnes@google.com> Change-Id: I1f8ea1ff4f33a97ab28ba94896a1054e89189576 Reviewed-on: https://review.coreboot.org/c/coreboot/+/44463 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Furquan Shaikh <furquan@google.com> Reviewed-by: Nick Vaccaro <nvaccaro@google.com>
1414 lines
32 KiB
Go
1414 lines
32 KiB
Go
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"regexp"
|
|
)
|
|
|
|
/*
|
|
* 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
|
|
DeviceBusWidth int
|
|
RanksPerPackage int
|
|
|
|
/*
|
|
* All the following parameters are optional and required only if the part requires
|
|
* special parameters as per the datasheet.
|
|
*/
|
|
/* Timing parameters */
|
|
TAAMinPs int
|
|
TRCDMinPs int
|
|
TRPMinPs int
|
|
TRASMinPs int
|
|
TRCMinPs int
|
|
TCKMinPs int
|
|
TCKMaxPs int
|
|
TRFC1MinPs int
|
|
TRFC2MinPs int
|
|
TRFC4MinPs int
|
|
TFAWMinPs int
|
|
TRRDLMinPs int
|
|
TRRDSMinPs int
|
|
TCCDLMinPs int
|
|
TWRMinPs int
|
|
TWTRLMinPs int
|
|
TWTRSMinPs int
|
|
|
|
/* CAS */
|
|
CASLatencies string
|
|
CASFirstByte byte
|
|
CASSecondByte byte
|
|
CASThirdByte byte
|
|
CASFourthByte byte
|
|
}
|
|
|
|
/* This encodes the density in Gb to SPD low nibble value as per JESD 4.1.2.L-5 R29 */
|
|
var densityGbToSPDEncoding = map[int]byte {
|
|
2: 0x3,
|
|
4: 0x4,
|
|
8: 0x5,
|
|
16: 0x6,
|
|
}
|
|
|
|
/*
|
|
* Tables 4 thru Table 7 from JESD79-4C.
|
|
* Maps density per die to row-column encoding for a device with x8/x16
|
|
* physical channel.
|
|
*/
|
|
var densityGbx8x16DieCapacityToRowColumnEncoding = map[int]byte {
|
|
2: 0x11, /* 14 rows, 10 columns */
|
|
4: 0x19, /* 15 rows, 10 columns */
|
|
8: 0x21, /* 16 rows, 10 columns */
|
|
16: 0x29, /* 17 rows, 10 columns */
|
|
}
|
|
|
|
/*
|
|
* Tables 169 & 170 in the JESD79-4C spec
|
|
* Maps die density to refresh timings. This is the same for x8 and x16
|
|
* devices.
|
|
*/
|
|
|
|
/* maps die density to rcf1 timing in pico seconds */
|
|
var tRFC1Encoding = map[int]int {
|
|
2: 160000,
|
|
4: 260000,
|
|
8: 350000,
|
|
16: 550000,
|
|
}
|
|
|
|
/* maps die density to rcf2 timing in pico seconds */
|
|
var tRFC2Encoding = map[int]int {
|
|
2: 110000,
|
|
4: 160000,
|
|
8: 260000,
|
|
16: 350000,
|
|
}
|
|
|
|
/* maps die density to rcf4 timing in pico seconds */
|
|
var tRFC4Encoding = map[int]int {
|
|
2: 90000,
|
|
4: 110000,
|
|
8: 160000,
|
|
16: 260000,
|
|
}
|
|
|
|
func getTRCMinPs(memAttribs *memAttributes) int {
|
|
return memAttribs.TAAMinPs + memAttribs.TRASMinPs
|
|
}
|
|
|
|
func getDefaultTCKMinPs(memAttribs *memAttributes) int {
|
|
/* value 2000000 = 2 * 1000000, where 1000000 is to convert mS to pS */
|
|
return 2000000 / memAttribs.SpeedMTps
|
|
}
|
|
|
|
type speedBinAttributes struct {
|
|
TRASMinPs int
|
|
TCKMaxPs int
|
|
}
|
|
|
|
var speedBinToSPDEncoding = map[int]speedBinAttributes {
|
|
1600: {
|
|
TRASMinPs: 35000,
|
|
TCKMaxPs: 1500,
|
|
},
|
|
1866: {
|
|
TRASMinPs: 34000,
|
|
TCKMaxPs: 1250,
|
|
},
|
|
2133: {
|
|
TRASMinPs: 33000,
|
|
TCKMaxPs: 1071,
|
|
},
|
|
2400: {
|
|
TRASMinPs: 32000,
|
|
TCKMaxPs: 937,
|
|
},
|
|
2666: {
|
|
TRASMinPs: 32000,
|
|
TCKMaxPs: 833,
|
|
},
|
|
2933: {
|
|
TRASMinPs: 32000,
|
|
TCKMaxPs: 750,
|
|
},
|
|
3200: {
|
|
TRASMinPs: 32000,
|
|
TCKMaxPs: 682,
|
|
},
|
|
}
|
|
|
|
func getBankGroups(memAttribs *memAttributes) byte {
|
|
var bg byte
|
|
|
|
switch memAttribs.DeviceBusWidth {
|
|
case 8:
|
|
bg = 4
|
|
case 16:
|
|
if memAttribs.DiesPerPackage == 1 {
|
|
bg = 2 /* x16 SDP has 2 bank groups */
|
|
} else {
|
|
bg = 4 /* x16 DDP has 4 bank groups */
|
|
}
|
|
}
|
|
|
|
return bg
|
|
}
|
|
|
|
func encodeBankGroups(bg byte) byte {
|
|
var val byte
|
|
|
|
switch bg {
|
|
case 2:
|
|
val = 1
|
|
case 4:
|
|
val = 2
|
|
}
|
|
|
|
return val << 6
|
|
}
|
|
|
|
func encodeDensityBanks(memAttribs *memAttributes) byte {
|
|
var b byte
|
|
|
|
b = densityGbToSPDEncoding[memAttribs.CapacityPerDieGb]
|
|
b |= encodeBankGroups(getBankGroups(memAttribs))
|
|
/* No need to encode banksPerGroup.it's always 4 ([4:5] = 0) */
|
|
|
|
return b
|
|
}
|
|
|
|
func encodeSdramAddressing(memAttribs *memAttributes) byte {
|
|
var b byte
|
|
|
|
b = densityGbx8x16DieCapacityToRowColumnEncoding[memAttribs.CapacityPerDieGb]
|
|
|
|
return b
|
|
}
|
|
|
|
func encodePackageDeviceType(dies int) byte {
|
|
var b byte
|
|
|
|
if dies > 1 {
|
|
/* If more than one die, then this is a non-monolithic device. */
|
|
b = 1
|
|
} else {
|
|
/* If only single die, then this is a monolithic device. */
|
|
b = 0
|
|
}
|
|
|
|
return b << 7
|
|
}
|
|
|
|
func encodeSignalLoadingFromDieCount(dies int) byte {
|
|
var loading byte
|
|
|
|
/*
|
|
* If die count = 1, signal loading = "not specified" = 0
|
|
* If die count > 1, signal loading = "multi" = 2
|
|
*/
|
|
if dies == 1 {
|
|
loading = 0
|
|
} else {
|
|
loading = 1
|
|
}
|
|
|
|
return loading
|
|
}
|
|
|
|
func encodeDiesPerPackage(dies int) byte {
|
|
var b byte
|
|
|
|
b = encodePackageDeviceType(dies) /* Monolithic / Non-monolithic device */
|
|
b |= (byte(dies) - 1) << 4
|
|
|
|
return b
|
|
}
|
|
|
|
func encodePackageType(memAttribs *memAttributes) byte {
|
|
var b byte
|
|
|
|
b = encodeDiesPerPackage(memAttribs.DiesPerPackage)
|
|
b |= encodeSignalLoadingFromDieCount(memAttribs.DiesPerPackage)
|
|
|
|
return b
|
|
}
|
|
|
|
func encodeDataWidth(bitWidthPerDevice int) byte {
|
|
var width byte
|
|
|
|
switch bitWidthPerDevice {
|
|
case 8:
|
|
width = 1
|
|
case 16:
|
|
width = 2
|
|
}
|
|
|
|
return width
|
|
}
|
|
|
|
func encodeRanks(ranks int) byte {
|
|
var b byte
|
|
|
|
b = byte(ranks - 1)
|
|
|
|
return b << 3
|
|
}
|
|
|
|
func encodeModuleOrganization(memAttribs *memAttributes) byte {
|
|
var b byte
|
|
|
|
b = encodeDataWidth(memAttribs.DeviceBusWidth)
|
|
b |= encodeRanks(memAttribs.RanksPerPackage)
|
|
|
|
return b
|
|
}
|
|
|
|
func encodeTCKMin(memAttribs *memAttributes) byte {
|
|
return convPsToMtbByte(memAttribs.TCKMinPs)
|
|
}
|
|
|
|
func encodeTCKMinFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TCKMinPs)
|
|
}
|
|
|
|
func encodeTCKMax(memAttribs *memAttributes) byte {
|
|
return convPsToMtbByte(memAttribs.TCKMaxPs)
|
|
}
|
|
|
|
func encodeTCKMaxFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TCKMaxPs)
|
|
}
|
|
|
|
func divRoundUp(dividend int, divisor int) int {
|
|
return (dividend + divisor - 1) / divisor
|
|
}
|
|
|
|
func convNsToPs(timeNs int) int {
|
|
return timeNs * 1000
|
|
}
|
|
|
|
func convMtbToPs(mtb int) int {
|
|
return mtb * 125
|
|
}
|
|
|
|
func convPsToMtb(timePs int) int {
|
|
return divRoundUp(timePs, 125)
|
|
}
|
|
|
|
func convPsToMtbByte(timePs int) byte {
|
|
return byte(convPsToMtb(timePs) & 0xff)
|
|
}
|
|
|
|
func convPsToFtbByte(timePs int) byte {
|
|
mtb := convPsToMtb(timePs)
|
|
ftb := timePs - convMtbToPs(mtb)
|
|
|
|
return byte(ftb)
|
|
}
|
|
|
|
func encodeTAAMin(memAttribs *memAttributes) byte {
|
|
return convPsToMtbByte(memAttribs.TAAMinPs)
|
|
}
|
|
|
|
func encodeTAAMinFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TAAMinPs)
|
|
}
|
|
|
|
func encodeTRCDMin(memAttribs *memAttributes) byte {
|
|
return convPsToMtbByte(memAttribs.TRCDMinPs)
|
|
}
|
|
|
|
func encodeTRCDMinFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TRCDMinPs)
|
|
}
|
|
|
|
func encodeTRPMin(memAttribs *memAttributes) byte {
|
|
return convPsToMtbByte(memAttribs.TRPMinPs)
|
|
}
|
|
|
|
func encodeTRCMinFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TRCMinPs)
|
|
}
|
|
|
|
func encodeTRPMinFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TRPMinPs)
|
|
}
|
|
|
|
func encodeTRASRCMinMSNs(memAttribs *memAttributes) byte {
|
|
var b byte
|
|
|
|
b = byte((convPsToMtb(memAttribs.TRASMinPs) >> 4) & 0xf0)
|
|
b |= byte((convPsToMtb(memAttribs.TRCMinPs) >> 8) & 0x0f)
|
|
|
|
return b
|
|
}
|
|
|
|
func encodeTRASMinLsb(memAttribs *memAttributes) byte {
|
|
return byte(convPsToMtb(memAttribs.TRASMinPs) & 0xff)
|
|
}
|
|
|
|
func encodeTRCMinLsb(memAttribs *memAttributes) byte {
|
|
return byte(convPsToMtb(memAttribs.TRCMinPs) & 0xff)
|
|
}
|
|
|
|
var pageSizefromBusWidthEncoding = map[int]int {
|
|
8: 1,
|
|
16: 2,
|
|
}
|
|
|
|
/*
|
|
* Per Table 69 & Table 70 of Jedec JESD79-4C
|
|
* tFAW timing is based on :
|
|
* Speed bin and page size
|
|
*/
|
|
func getTFAWMinPs(memAttribs *memAttributes) int {
|
|
var tFAWFixed int
|
|
|
|
if pageSizefromBusWidthEncoding[memAttribs.DeviceBusWidth] == 1 {
|
|
switch memAttribs.SpeedMTps {
|
|
case 1600:
|
|
tFAWFixed = 25000
|
|
case 1866:
|
|
tFAWFixed = 23000
|
|
default:
|
|
tFAWFixed = 21000
|
|
}
|
|
} else if pageSizefromBusWidthEncoding[memAttribs.DeviceBusWidth] == 2 {
|
|
switch memAttribs.SpeedMTps {
|
|
case 1600:
|
|
tFAWFixed = 35000
|
|
default:
|
|
tFAWFixed = 30000
|
|
}
|
|
}
|
|
|
|
return tFAWFixed
|
|
}
|
|
|
|
/* Update settings based on data sheet (json) supplied memory attributes */
|
|
|
|
func updateTFAWMin(memAttribs *memAttributes) {
|
|
var tFAWFromTck int
|
|
|
|
if memAttribs.TFAWMinPs == 0 {
|
|
memAttribs.TFAWMinPs = getTFAWMinPs(memAttribs)
|
|
}
|
|
|
|
switch pageSizefromBusWidthEncoding[memAttribs.DeviceBusWidth] {
|
|
case 1:
|
|
tFAWFromTck = 20 * memAttribs.TCKMinPs
|
|
case 2:
|
|
tFAWFromTck = 28 * memAttribs.TCKMinPs
|
|
}
|
|
|
|
if memAttribs.TFAWMinPs < tFAWFromTck {
|
|
memAttribs.TFAWMinPs = tFAWFromTck
|
|
}
|
|
}
|
|
|
|
func updateTRFC1Min(memAttribs *memAttributes) {
|
|
if memAttribs.TRFC1MinPs == 0 {
|
|
memAttribs.TRFC1MinPs = tRFC1Encoding[memAttribs.CapacityPerDieGb]
|
|
}
|
|
}
|
|
|
|
func updateTRFC2Min(memAttribs *memAttributes) {
|
|
if memAttribs.TRFC2MinPs == 0 {
|
|
memAttribs.TRFC2MinPs = tRFC2Encoding[memAttribs.CapacityPerDieGb]
|
|
}
|
|
}
|
|
|
|
func updateTRFC4Min(memAttribs *memAttributes) {
|
|
if memAttribs.TRFC4MinPs == 0 {
|
|
memAttribs.TRFC4MinPs = tRFC4Encoding[memAttribs.CapacityPerDieGb]
|
|
}
|
|
}
|
|
|
|
func getTRRDLMinPs(memAttribs *memAttributes) int {
|
|
var tRRDLFixed int
|
|
|
|
/*
|
|
* Per JESD79-4C Tables 169 & 170, tRRD_L is based on :
|
|
* Speed bin and page size
|
|
*/
|
|
switch pageSizefromBusWidthEncoding[memAttribs.DeviceBusWidth] {
|
|
case 1:
|
|
switch memAttribs.SpeedMTps {
|
|
case 1600:
|
|
tRRDLFixed = 6000
|
|
default:
|
|
tRRDLFixed = 5300
|
|
}
|
|
case 2:
|
|
switch memAttribs.SpeedMTps {
|
|
case 1600:
|
|
tRRDLFixed = 7500
|
|
default:
|
|
tRRDLFixed = 6400
|
|
}
|
|
}
|
|
|
|
return tRRDLFixed
|
|
}
|
|
|
|
func updateTRRDLMin(memAttribs *memAttributes) {
|
|
var tRRDLFromTck int
|
|
|
|
if memAttribs.TRRDLMinPs == 0 {
|
|
memAttribs.TRRDLMinPs= getTRRDLMinPs(memAttribs)
|
|
}
|
|
|
|
tRRDLFromTck = 4 * memAttribs.TCKMinPs
|
|
|
|
if memAttribs.TRRDLMinPs < tRRDLFromTck {
|
|
memAttribs.TRRDLMinPs = tRRDLFromTck
|
|
}
|
|
}
|
|
|
|
var speedToTRRDSMinPsOneKPageSize = map[int]int {
|
|
1600: 5000,
|
|
1866: 4200,
|
|
2133: 3700,
|
|
2400: 3300,
|
|
2666: 3000,
|
|
2933: 2700,
|
|
3200: 2500,
|
|
}
|
|
|
|
var speedToTRRDSMinPsTwoKPageSize = map[int]int {
|
|
1600: 6000,
|
|
1866: 5300,
|
|
2133: 5300,
|
|
2400: 5300,
|
|
2666: 5300,
|
|
2933: 5300,
|
|
3200: 5300,
|
|
}
|
|
|
|
func getTRRDSMinPs(memAttribs *memAttributes) int {
|
|
var tRRDFixed int
|
|
|
|
switch pageSizefromBusWidthEncoding[memAttribs.DeviceBusWidth] {
|
|
case 1:
|
|
tRRDFixed = speedToTRRDSMinPsOneKPageSize[memAttribs.SpeedMTps]
|
|
case 2:
|
|
tRRDFixed = speedToTRRDSMinPsTwoKPageSize[memAttribs.SpeedMTps]
|
|
}
|
|
|
|
return tRRDFixed
|
|
}
|
|
|
|
func updateTRRDSMin(memAttribs *memAttributes) {
|
|
var tRRDFromTck int
|
|
|
|
if memAttribs.TRRDSMinPs == 0 {
|
|
memAttribs.TRRDSMinPs = getTRRDSMinPs(memAttribs)
|
|
}
|
|
|
|
tRRDFromTck = 4 * memAttribs.TCKMinPs
|
|
|
|
if memAttribs.TRRDSMinPs < tRRDFromTck {
|
|
memAttribs.TRRDSMinPs = tRRDFromTck
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Per JESD79-4C Tables 169 and 170,
|
|
* tCCD_L is based on :
|
|
* Speed Bin
|
|
*/
|
|
func getTCCDLMinPs(memAttribs *memAttributes) int {
|
|
var tCCDLFixed int
|
|
|
|
switch memAttribs.SpeedMTps {
|
|
case 1600:
|
|
tCCDLFixed = 6250
|
|
case 1866:
|
|
tCCDLFixed = 5355
|
|
case 2133:
|
|
tCCDLFixed = 5355
|
|
default:
|
|
tCCDLFixed = 5000
|
|
}
|
|
|
|
return tCCDLFixed
|
|
}
|
|
|
|
func updateTCCDLMin(memAttribs *memAttributes) {
|
|
var tCCDLFromTck int
|
|
|
|
if memAttribs.TCCDLMinPs == 0 {
|
|
memAttribs.TCCDLMinPs = getTCCDLMinPs(memAttribs)
|
|
}
|
|
|
|
tCCDLFromTck = 5 * memAttribs.TCKMinPs
|
|
|
|
if memAttribs.TCCDLMinPs < tCCDLFromTck {
|
|
memAttribs.TCCDLMinPs = tCCDLFromTck
|
|
}
|
|
}
|
|
|
|
func encodeTRFC1MinLsb(memAttribs *memAttributes) byte {
|
|
var mtb int
|
|
|
|
mtb = convPsToMtb(memAttribs.TRFC1MinPs)
|
|
|
|
return byte(mtb & 0xff)
|
|
}
|
|
|
|
func encodeTRFC1MinMsb(memAttribs *memAttributes) byte {
|
|
var mtb int
|
|
|
|
mtb = convPsToMtb(memAttribs.TRFC1MinPs)
|
|
|
|
return byte((mtb >> 8) & 0xff)
|
|
}
|
|
|
|
func encodeTRFC2MinLsb(memAttribs *memAttributes) byte {
|
|
var mtb int
|
|
|
|
mtb = convPsToMtb(memAttribs.TRFC2MinPs)
|
|
|
|
return byte(mtb & 0xff)
|
|
}
|
|
|
|
func encodeTRFC2MinMsb(memAttribs *memAttributes) byte {
|
|
var mtb int
|
|
|
|
mtb = convPsToMtb(memAttribs.TRFC2MinPs)
|
|
|
|
return byte((mtb >> 8) & 0xff)
|
|
}
|
|
|
|
func encodeTRFC4MinLsb(memAttribs *memAttributes) byte {
|
|
var mtb int
|
|
|
|
mtb = convPsToMtb(memAttribs.TRFC4MinPs)
|
|
|
|
return byte(mtb & 0xff)
|
|
}
|
|
|
|
func encodeTRFC4MinMsb(memAttribs *memAttributes) byte {
|
|
var mtb int
|
|
|
|
mtb = convPsToMtb(memAttribs.TRFC4MinPs)
|
|
|
|
return byte((mtb >> 8) & 0xff)
|
|
}
|
|
|
|
func encodeTFAWMinMSN(memAttribs *memAttributes) byte {
|
|
var mtb int
|
|
|
|
mtb = convPsToMtb(memAttribs.TFAWMinPs)
|
|
|
|
return byte((mtb >> 8) & 0x0f)
|
|
}
|
|
|
|
func encodeTFAWMinLsb(memAttribs *memAttributes) byte {
|
|
var mtb int
|
|
|
|
mtb = convPsToMtb(memAttribs.TFAWMinPs)
|
|
|
|
return byte(mtb & 0xff)
|
|
}
|
|
|
|
func encodeCASFirstByte(memAttribs *memAttributes) byte {
|
|
return memAttribs.CASFirstByte
|
|
}
|
|
|
|
func encodeCASSecondByte(memAttribs *memAttributes) byte {
|
|
return memAttribs.CASSecondByte
|
|
}
|
|
|
|
func encodeCASThirdByte(memAttribs *memAttributes) byte {
|
|
return memAttribs.CASThirdByte
|
|
}
|
|
|
|
func encodeCASFourthByte(memAttribs *memAttributes) byte {
|
|
return memAttribs.CASFourthByte
|
|
}
|
|
|
|
func encodeTRRDSMin(memAttribs *memAttributes) byte {
|
|
return convPsToMtbByte(memAttribs.TRRDSMinPs)
|
|
}
|
|
|
|
func encodeTRRDSMinFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TRRDSMinPs)
|
|
}
|
|
|
|
func encodeTRRDLMin(memAttribs *memAttributes) byte {
|
|
return convPsToMtbByte(memAttribs.TRRDLMinPs)
|
|
}
|
|
|
|
func encodeTRRDLMinFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TRRDLMinPs)
|
|
}
|
|
|
|
func encodeTCCDLMin(memAttribs *memAttributes) byte {
|
|
return convPsToMtbByte(memAttribs.TCCDLMinPs)
|
|
}
|
|
|
|
func encodeTCCDLMinFineOffset(memAttribs *memAttributes) byte {
|
|
return convPsToFtbByte(memAttribs.TCCDLMinPs)
|
|
}
|
|
|
|
func encodeTWRMinMSN(memAttribs *memAttributes) byte {
|
|
return byte((convPsToMtb(TimingValueTWRMinPs) >> 8) & 0x0f)
|
|
}
|
|
|
|
func encodeTWRMinLsb(memAttribs *memAttributes) byte {
|
|
return byte(convPsToMtb(TimingValueTWRMinPs) & 0xff)
|
|
}
|
|
|
|
func encodeTWTRMinMSNs(memAttribs *memAttributes) byte {
|
|
var b byte
|
|
|
|
b = byte((convPsToMtb(memAttribs.TWTRLMinPs) >> 4) & 0xf0)
|
|
b |= byte((convPsToMtb(memAttribs.TWTRSMinPs) >> 8) & 0x0f)
|
|
|
|
return b
|
|
}
|
|
|
|
func encodeTWTRSMinLsb(memAttribs *memAttributes) byte {
|
|
return byte(convPsToMtb(memAttribs.TWTRSMinPs) & 0xff)
|
|
}
|
|
|
|
func encodeTWTRLMinLsb(memAttribs *memAttributes) byte {
|
|
return byte(convPsToMtb(memAttribs.TWTRLMinPs) & 0xff)
|
|
}
|
|
|
|
type SPDMemAttribFunc func (*memAttributes) byte
|
|
type SPDConvConstFunc func () byte
|
|
|
|
type SPDAttribTableEntry struct {
|
|
constVal byte
|
|
getVal SPDMemAttribFunc
|
|
}
|
|
|
|
const (
|
|
/* SPD Byte Index */
|
|
SPDIndexSize = 0
|
|
SPDIndexRevision = 1
|
|
SPDIndexMemoryType = 2
|
|
SPDIndexModuleType = 3
|
|
SPDIndexDensityBanks = 4
|
|
SPDIndexAddressing = 5
|
|
SPDIndexPackageType = 6
|
|
SPDIndexOptionalFeatures = 7
|
|
SPDIndexModuleOrganization = 12
|
|
SPDIndexBusWidth = 13
|
|
SPDIndexTimebases = 17
|
|
SPDIndexTCKMin = 18
|
|
SPDIndexTCKMax = 19
|
|
SPDIndexCASFirstByte = 20
|
|
SPDIndexCASSecondByte = 21
|
|
SPDIndexCASThirdByte = 22
|
|
SPDIndexCASFourthByte = 23
|
|
SPDIndexTAAMin = 24
|
|
SPDIndexTRCDMin = 25
|
|
SPDIndexTRPMin = 26
|
|
SPDIndexTRASRCMinMSNs = 27
|
|
SPDIndexTRASMinLsb = 28
|
|
SPDIndexTRCMinLsb = 29
|
|
SPDIndexTRFC1MinLsb = 30
|
|
SPDIndexTRFC1MinMsb = 31
|
|
SPDIndexTRFC2MinLsb = 32
|
|
SPDIndexTRFC2MinMsb = 33
|
|
SPDIndexTRFC4MinLsb = 34
|
|
SPDIndexTRFC4MinMsb = 35
|
|
SPDIndexTFAWMinMSN = 36
|
|
SPDIndexTFAWMinLsb = 37
|
|
SPDIndexTRRDSMin = 38
|
|
SPDIndexTRRDLMin = 39
|
|
SPDIndexTCCDLMin = 40
|
|
SPDIndexTWRMinMSN = 41
|
|
SPDIndexTWRMinLsb = 42
|
|
SPDIndexTWTRMinMSNs = 43
|
|
SPDIndexWTRSMinLsb = 44
|
|
SPDIndexWTRLMinLsb = 45
|
|
SPDIndexTCCDLMinFineOffset = 117
|
|
SPDIndexTRRDLMinFineOffset = 118
|
|
SPDIndexTRRDSMinFineOffset = 119
|
|
SPDIndexTRCMinFineOffset = 120
|
|
SPDIndexTRPMinFineOffset = 121
|
|
SPDIndexTRCDMinFineOffset = 122
|
|
SPDIndexTAAMinFineOffset = 123
|
|
SPDIndexTCKMaxFineOffset = 124
|
|
SPDIndexTCKMinFineOffset = 125
|
|
SPDIndexManufacturerPartNumberStartByte = 329
|
|
SPDIndexManufacturerPartNumberEndByte = 348
|
|
|
|
/* SPD Byte Value */
|
|
|
|
/*
|
|
* From JEDEC spec:
|
|
* 6:4 (Bytes total) = 2 (512 bytes)
|
|
* 3:0 (Bytes used) = 3 (384 bytes)
|
|
* Set to 0x23 for DDR4.
|
|
*/
|
|
SPDValueSize = 0x23
|
|
|
|
/*
|
|
* From JEDEC spec: Revision 1.1
|
|
* Set to 0x11.
|
|
*/
|
|
SPDValueRevision = 0x11
|
|
|
|
/* DDR4 memory type = 0x0C */
|
|
SPDValueMemoryType = 0x0C
|
|
|
|
/*
|
|
* From JEDEC spec:
|
|
* Module Type [0:3] :
|
|
* 0 = Undefined
|
|
* 1 = RDIMM (width = 133.35 mm nom)
|
|
* 2 = UDIMM (width = 133.35 mm nom)
|
|
* 3 = SO-DIMM (width = 68.60 mm nom)
|
|
* 4 = LRDIMM (width = 133.35 mm nom)
|
|
*
|
|
* DDR4 on TGL uses SO-DIMM type for for both memory down and DIMM config.
|
|
* Set to 0x03.
|
|
*/
|
|
SPDValueModuleType = 0x03
|
|
|
|
/*
|
|
* From JEDEC spec:
|
|
* 5:4 (Maximum Activate Window) = 00 (8192 * tREFI)
|
|
* 3:0 (Maximum Activate Count) = 1000 (Unlimited MAC)
|
|
*
|
|
* Needs to come from datasheet, but most parts seem to support unlimited MAC.
|
|
* MR#24 OP3
|
|
*/
|
|
SPDValueOptionalFeatures = 0x08
|
|
|
|
/*
|
|
* From JEDEC spec:
|
|
* 2:0 Primary Bus Width in Bits = 011 (x64 always)
|
|
* Set to 0x03.
|
|
*/
|
|
SPDValueModuleBusWidth = 0x03
|
|
|
|
/*
|
|
* From JEDEC spec:
|
|
* 3:2 (MTB) = 00 (0.125ns)
|
|
* 1:0 (FTB) = 00 (1ps)
|
|
* Set to 0x00.
|
|
*/
|
|
SPDValueTimebases = 0x00
|
|
|
|
/* CAS fourth byte: All bits are reserved */
|
|
SPDValueCASFourthByte = 0x00
|
|
|
|
/* As per JEDEC spec, unused digits of manufacturer part number are left as blank. */
|
|
SPDValueManufacturerPartNumberBlank = 0x20
|
|
|
|
)
|
|
|
|
const (
|
|
/*
|
|
* As per Table 75 of Jedec spec 4.1.20-L-5 R29 v103:
|
|
* tWRMin = 15nS for all DDR4 Speed Bins
|
|
* Set to 15000 pS
|
|
*/
|
|
TimingValueTWRMinPs = 15000
|
|
|
|
/*
|
|
* As per Table 78 of Jedec spec 4.1.20-L-5 R29 v103:
|
|
* tWTR_SMin = 2.5nS for all DDR4 Speed Bins
|
|
* Set to 2500 pS
|
|
*/
|
|
TimingValueTWTRSMinPs = 2500
|
|
|
|
/*
|
|
* As per Table 80 of Jedec spec 4.1.20-L-5 R29 v103:
|
|
* tWTR_LMin = 7.5 nS for all DDR4 Speed Bins
|
|
* Set to 7500 pS
|
|
*/
|
|
TimingValueTWTRLMinPs = 7500
|
|
)
|
|
|
|
var SPDAttribTable = map[int]SPDAttribTableEntry {
|
|
SPDIndexSize: { constVal: SPDValueSize },
|
|
SPDIndexRevision: { constVal: SPDValueRevision },
|
|
SPDIndexMemoryType: { constVal: SPDValueMemoryType },
|
|
SPDIndexModuleType: { constVal: SPDValueModuleType },
|
|
SPDIndexDensityBanks: { getVal: encodeDensityBanks },
|
|
SPDIndexAddressing: { getVal: encodeSdramAddressing },
|
|
SPDIndexPackageType: { getVal: encodePackageType },
|
|
SPDIndexOptionalFeatures: { constVal: SPDValueOptionalFeatures },
|
|
SPDIndexModuleOrganization: { getVal: encodeModuleOrganization },
|
|
SPDIndexBusWidth: { constVal: SPDValueModuleBusWidth },
|
|
SPDIndexTimebases: { constVal: SPDValueTimebases },
|
|
SPDIndexTCKMin: { getVal: encodeTCKMin },
|
|
SPDIndexTCKMinFineOffset: { getVal: encodeTCKMinFineOffset },
|
|
SPDIndexTCKMax: { getVal: encodeTCKMax },
|
|
SPDIndexTCKMaxFineOffset: { getVal: encodeTCKMaxFineOffset },
|
|
SPDIndexCASFirstByte: { getVal: encodeCASFirstByte },
|
|
SPDIndexCASSecondByte: { getVal: encodeCASSecondByte },
|
|
SPDIndexCASThirdByte: { getVal: encodeCASThirdByte },
|
|
SPDIndexCASFourthByte: { getVal: encodeCASFourthByte },
|
|
SPDIndexTAAMin: { getVal: encodeTAAMin },
|
|
SPDIndexTAAMinFineOffset: { getVal: encodeTAAMinFineOffset },
|
|
SPDIndexTRCDMin: { getVal: encodeTRCDMin },
|
|
SPDIndexTRCDMinFineOffset: { getVal: encodeTRCDMinFineOffset },
|
|
SPDIndexTRPMin: { getVal: encodeTRPMin },
|
|
SPDIndexTRPMinFineOffset: { getVal: encodeTRPMinFineOffset },
|
|
SPDIndexTRASRCMinMSNs: { getVal: encodeTRASRCMinMSNs },
|
|
SPDIndexTRASMinLsb: { getVal: encodeTRASMinLsb },
|
|
SPDIndexTRCMinLsb: { getVal: encodeTRCMinLsb },
|
|
SPDIndexTRCMinFineOffset: { getVal: encodeTRCMinFineOffset },
|
|
SPDIndexTRFC1MinLsb: { getVal: encodeTRFC1MinLsb },
|
|
SPDIndexTRFC1MinMsb: { getVal: encodeTRFC1MinMsb },
|
|
SPDIndexTRFC2MinLsb: { getVal: encodeTRFC2MinLsb },
|
|
SPDIndexTRFC2MinMsb: { getVal: encodeTRFC2MinMsb },
|
|
SPDIndexTRFC4MinLsb: { getVal: encodeTRFC4MinLsb },
|
|
SPDIndexTRFC4MinMsb: { getVal: encodeTRFC4MinMsb },
|
|
SPDIndexTFAWMinMSN: { getVal: encodeTFAWMinMSN },
|
|
SPDIndexTFAWMinLsb: { getVal: encodeTFAWMinLsb },
|
|
SPDIndexTRRDSMin: { getVal: encodeTRRDSMin },
|
|
SPDIndexTRRDSMinFineOffset: { getVal: encodeTRRDSMinFineOffset },
|
|
SPDIndexTRRDLMin: { getVal: encodeTRRDLMin },
|
|
SPDIndexTRRDLMinFineOffset: { getVal: encodeTRRDLMinFineOffset },
|
|
SPDIndexTCCDLMin: { getVal: encodeTCCDLMin },
|
|
SPDIndexTCCDLMinFineOffset: { getVal: encodeTCCDLMinFineOffset },
|
|
SPDIndexTWRMinMSN: { getVal: encodeTWRMinMSN },
|
|
SPDIndexTWRMinLsb: { getVal: encodeTWRMinLsb },
|
|
SPDIndexTWTRMinMSNs: { getVal: encodeTWTRMinMSNs },
|
|
SPDIndexWTRSMinLsb: { getVal: encodeTWTRSMinLsb },
|
|
SPDIndexWTRLMinLsb: { getVal: encodeTWTRLMinLsb },
|
|
}
|
|
|
|
type memParts struct {
|
|
MemParts []memPart `json:"parts"`
|
|
}
|
|
|
|
type memPart struct {
|
|
Name string
|
|
Attribs memAttributes
|
|
SPDFileName string
|
|
}
|
|
|
|
func writeSPDManifest(memParts *memParts, SPDDirName string) error {
|
|
var s string
|
|
|
|
fmt.Printf("Generating SPD Manifest with following entries:\n")
|
|
|
|
for i := 0; i < len(memParts.MemParts); i++ {
|
|
fmt.Printf("%-40s %s\n", memParts.MemParts[i].Name, memParts.MemParts[i].SPDFileName)
|
|
s += fmt.Sprintf("%s,%s\n", memParts.MemParts[i].Name, memParts.MemParts[i].SPDFileName)
|
|
}
|
|
|
|
return ioutil.WriteFile(filepath.Join(SPDDirName, SPDManifestFileName), []byte(s), 0644)
|
|
}
|
|
|
|
func isManufacturerPartNumberByte(index int) bool {
|
|
if index >= SPDIndexManufacturerPartNumberStartByte && index <= SPDIndexManufacturerPartNumberEndByte {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
|
|
func getSPDByte(index int, memAttribs *memAttributes) byte {
|
|
e, ok := SPDAttribTable[index]
|
|
if ok == false {
|
|
if isManufacturerPartNumberByte(index) {
|
|
return SPDValueManufacturerPartNumberBlank
|
|
}
|
|
return 0x00
|
|
}
|
|
|
|
if e.getVal != nil {
|
|
return e.getVal(memAttribs)
|
|
}
|
|
|
|
return e.constVal
|
|
}
|
|
|
|
func createSPD(memAttribs *memAttributes) string {
|
|
var s string
|
|
|
|
for i := 0; i < 512; i++ {
|
|
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 validateDeviceBusWidth(width int) error {
|
|
if width != 8 && width != 16 {
|
|
return fmt.Errorf("Incorrect device bus width: ", width)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateRanksPerPackage(ranks int) error {
|
|
if ranks >= 1 && ranks <= 2 {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("Incorrect package ranks: ", ranks)
|
|
}
|
|
|
|
|
|
func validateCASLatency(CL int) error {
|
|
if CL >= 10 && CL <= 24 && CL != 23 {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("Incorrect CAS latency: ", CL)
|
|
}
|
|
|
|
/*
|
|
1) validate memory parts
|
|
2) remove any fields that Intel does not care about
|
|
*/
|
|
|
|
/* verify the supplied CAS Latencies supported does not match default */
|
|
func verifySupportedCASLatencies(part *memPart) error {
|
|
if part.Attribs.CASLatencies == getDefaultCASLatencies(&part.Attribs) {
|
|
return fmt.Errorf("CASLatencies for %s already matches default,\nPlease remove CASLatencies override line from the %s part attributes in the global part list and regenerate SPD Manifest", part.Name, part.Name)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateMemoryParts(memParts *memParts) error {
|
|
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 := validateDeviceBusWidth(memParts.MemParts[i].Attribs.DeviceBusWidth); err != nil {
|
|
return err
|
|
}
|
|
if err := validateRanksPerPackage(memParts.MemParts[i].Attribs.RanksPerPackage); err != nil {
|
|
return err
|
|
}
|
|
if err := validateCASLatency(memParts.MemParts[i].Attribs.CL_nRCD_nRP); err != nil {
|
|
return err
|
|
}
|
|
/* If CAS Latency was supplied, make sure it doesn't match default value */
|
|
if len(memParts.MemParts[i].Attribs.CASLatencies) != 0 {
|
|
if err := verifySupportedCASLatencies(&memParts.MemParts[i]); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
const (
|
|
/* First Byte */
|
|
CAS9 = 1 << 2
|
|
CAS10 = 1 << 3
|
|
CAS11 = 1 << 4
|
|
CAS12 = 1 << 5
|
|
CAS13 = 1 << 6
|
|
CAS14 = 1 << 7
|
|
/* Second Byte */
|
|
CAS15 = 1 << 0
|
|
CAS16 = 1 << 1
|
|
CAS17 = 1 << 2
|
|
CAS18 = 1 << 3
|
|
CAS19 = 1 << 4
|
|
CAS20 = 1 << 5
|
|
CAS21 = 1 << 6
|
|
CAS22 = 1 << 7
|
|
/* Third Byte */
|
|
CAS24 = 1 << 1
|
|
)
|
|
|
|
func encodeLatencies(latency int, memAttribs *memAttributes) error {
|
|
switch latency {
|
|
case 9:
|
|
memAttribs.CASFirstByte |= CAS9
|
|
case 10:
|
|
memAttribs.CASFirstByte |= CAS10
|
|
case 11:
|
|
memAttribs.CASFirstByte |= CAS11
|
|
case 12:
|
|
memAttribs.CASFirstByte |= CAS12
|
|
case 13:
|
|
memAttribs.CASFirstByte |= CAS13
|
|
case 14:
|
|
memAttribs.CASFirstByte |= CAS14
|
|
case 15:
|
|
memAttribs.CASSecondByte |= CAS15
|
|
case 16:
|
|
memAttribs.CASSecondByte |= CAS16
|
|
case 17:
|
|
memAttribs.CASSecondByte |= CAS17
|
|
case 18:
|
|
memAttribs.CASSecondByte |= CAS18
|
|
case 19:
|
|
memAttribs.CASSecondByte |= CAS19
|
|
case 20:
|
|
memAttribs.CASSecondByte |= CAS20
|
|
case 21:
|
|
memAttribs.CASSecondByte |= CAS21
|
|
case 22:
|
|
memAttribs.CASSecondByte |= CAS22
|
|
case 24:
|
|
memAttribs.CASThirdByte |= CAS24
|
|
default:
|
|
fmt.Errorf("Incorrect CAS Latency: ", latency)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
/* Default CAS Latencies from Speed Bin tables in JEDS79-4C */
|
|
func getDefaultCASLatencies(memAttribs *memAttributes) string {
|
|
var str string
|
|
|
|
switch memAttribs.SpeedMTps {
|
|
case 1600:
|
|
switch memAttribs.CL_nRCD_nRP {
|
|
case 10:
|
|
str = "9 10 11 12"
|
|
case 11:
|
|
str = "9 11 12"
|
|
case 12:
|
|
str = "10 12"
|
|
}
|
|
case 1866:
|
|
switch memAttribs.CL_nRCD_nRP {
|
|
case 12:
|
|
str = "9 10 12 13 14"
|
|
case 13:
|
|
str = "9 11 12 13 14"
|
|
case 14:
|
|
str = "10 12 14"
|
|
}
|
|
case 2133:
|
|
switch memAttribs.CL_nRCD_nRP {
|
|
case 14:
|
|
str = "9 10 12 14 15 16"
|
|
case 15:
|
|
str = "9 11 12 13 14 15 16"
|
|
case 16:
|
|
str = "10 12 14 16"
|
|
}
|
|
case 2400:
|
|
switch memAttribs.CL_nRCD_nRP {
|
|
case 15:
|
|
str = "9 10 12 14 15 16 17 18"
|
|
case 16:
|
|
str = "9 11 12 13 14 15 16 17 18"
|
|
case 17:
|
|
str = "10 11 12 13 14 15 16 17 18"
|
|
case 18:
|
|
str = "10 12 14 16 18"
|
|
}
|
|
case 2666:
|
|
switch memAttribs.CL_nRCD_nRP {
|
|
case 17:
|
|
str = "9 10 11 12 13 14 15 16 17 18 19 20"
|
|
case 18:
|
|
str = "9 10 11 12 13 14 15 16 17 18 19 20"
|
|
case 19:
|
|
str = "10 11 12 13 14 15 16 17 18 19 20"
|
|
case 20:
|
|
str = "10 12 14 16 18 20"
|
|
}
|
|
case 2933:
|
|
switch memAttribs.CL_nRCD_nRP {
|
|
case 19:
|
|
str = "9 10 11 12 13 14 15 16 17 18 19 20 21 22"
|
|
case 20:
|
|
str = "10 11 12 13 14 15 16 17 18 19 20 21 22"
|
|
case 21:
|
|
str = "10 11 12 13 14 15 16 17 18 19 20 21 22"
|
|
case 22:
|
|
str = "10 12 14 16 18 20 22"
|
|
}
|
|
case 3200:
|
|
switch memAttribs.CL_nRCD_nRP {
|
|
case 20:
|
|
str = "9 10 11 12 13 14 15 16 17 18 19 20 21 22 24"
|
|
case 22:
|
|
str = "10 11 12 13 14 15 16 17 18 19 20 21 22 24"
|
|
case 24:
|
|
str = "10 12 14 16 18 20 22 24"
|
|
}
|
|
}
|
|
|
|
return str
|
|
}
|
|
|
|
func updateCAS(memAttribs *memAttributes) error {
|
|
if len(memAttribs.CASLatencies) == 0 {
|
|
memAttribs.CASLatencies = getDefaultCASLatencies(memAttribs)
|
|
}
|
|
|
|
latencies := strings.Fields(memAttribs.CASLatencies)
|
|
for i := 0; i < len(latencies); i++ {
|
|
latency,err := strconv.Atoi(latencies[i])
|
|
if err != nil {
|
|
return fmt.Errorf("Unable to convert latency ", latencies[i])
|
|
}
|
|
if err := encodeLatencies(latency, memAttribs); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getTAAMinPs(memAttribs *memAttributes) int {
|
|
return (memAttribs.CL_nRCD_nRP * 2000000) / memAttribs.SpeedMTps
|
|
}
|
|
|
|
func updateTAAMin(memAttribs *memAttributes) {
|
|
if memAttribs.TAAMinPs == 0 {
|
|
memAttribs.TAAMinPs = getTAAMinPs(memAttribs)
|
|
}
|
|
}
|
|
|
|
func updateTRCDMin(memAttribs *memAttributes) {
|
|
/* tRCDmin is same as tAAmin for all cases */
|
|
if memAttribs.TRCDMinPs == 0 {
|
|
memAttribs.TRCDMinPs = getTAAMinPs(memAttribs)
|
|
}
|
|
}
|
|
|
|
func updateTRPMin(memAttribs *memAttributes) {
|
|
/* tRPmin is same as tAAmin for all cases */
|
|
if memAttribs.TRPMinPs == 0 {
|
|
memAttribs.TRPMinPs = getTAAMinPs(memAttribs)
|
|
}
|
|
}
|
|
|
|
func updateTRASMin(memAttribs *memAttributes) {
|
|
if memAttribs.TRASMinPs == 0 {
|
|
memAttribs.TRASMinPs = speedBinToSPDEncoding[memAttribs.SpeedMTps].TRASMinPs
|
|
}
|
|
}
|
|
|
|
|
|
func updateTRCMin(memAttribs *memAttributes) {
|
|
if memAttribs.TRCMinPs == 0 {
|
|
memAttribs.TRCMinPs = getTRCMinPs(memAttribs)
|
|
}
|
|
}
|
|
|
|
func updateTCK(memAttribs *memAttributes) {
|
|
if memAttribs.TCKMinPs == 0 {
|
|
memAttribs.TCKMinPs = getDefaultTCKMinPs(memAttribs)
|
|
}
|
|
if memAttribs.TCKMaxPs == 0 {
|
|
memAttribs.TCKMaxPs = speedBinToSPDEncoding[memAttribs.SpeedMTps].TCKMaxPs
|
|
}
|
|
}
|
|
|
|
func updateTWRMin(memAttribs *memAttributes) {
|
|
if memAttribs.TWRMinPs == 0 {
|
|
memAttribs.TWRMinPs = TimingValueTWRMinPs
|
|
}
|
|
}
|
|
|
|
func updateTWTRMin(memAttribs *memAttributes) {
|
|
if memAttribs.TWTRLMinPs == 0 {
|
|
memAttribs.TWTRLMinPs = TimingValueTWTRLMinPs
|
|
}
|
|
if memAttribs.TWTRSMinPs == 0 {
|
|
memAttribs.TWTRSMinPs = TimingValueTWTRSMinPs
|
|
}
|
|
}
|
|
|
|
func updateMemoryAttributes(memAttribs *memAttributes) {
|
|
updateTCK(memAttribs)
|
|
updateTAAMin(memAttribs)
|
|
updateTRCDMin(memAttribs)
|
|
updateTRPMin(memAttribs)
|
|
updateTRASMin(memAttribs)
|
|
updateTRCMin(memAttribs)
|
|
updateTWRMin(memAttribs)
|
|
updateTWTRMin(memAttribs)
|
|
updateCAS(memAttribs)
|
|
updateTRFC1Min(memAttribs)
|
|
updateTRFC2Min(memAttribs)
|
|
updateTRFC4Min(memAttribs)
|
|
updateTCCDLMin(memAttribs)
|
|
updateTRRDSMin(memAttribs)
|
|
updateTRRDLMin(memAttribs)
|
|
updateTFAWMin(memAttribs)
|
|
}
|
|
|
|
func isPlatformSupported(platform string) error {
|
|
var ok bool
|
|
|
|
currPlatform, ok = platformMap[platform]
|
|
if ok == false {
|
|
return fmt.Errorf("Unsupported platform: ", platform)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func usage() {
|
|
fmt.Printf("\nUsage: %s <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)
|
|
}
|
|
}
|