This reverts commit f23794cf04.
Reason for revert: This change breaks compatibility if the changes
in CB:44775 are not also included. CB:44775 is still under discussion,
so revert this change to make spd_tools usable again.
Signed-off-by: Rob Barnes <robbarnes@google.com>
Change-Id: I5840a1b895dcbc8b91c76d8b60df2f95b93a4370
Reviewed-on: https://review.coreboot.org/c/coreboot/+/44999
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
Reviewed-by: Michael Niewöhner <foss@mniewoehner.de>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
		
	
		
			
				
	
	
		
			1433 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			1433 lines
		
	
	
		
			33 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
 | |
| 	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)
 | |
| 	}
 | |
| }
 |