This just reformats these files. go fmt should probably be run on the check-in of every .go file. Signed-off-by: Martin Roth <martin@coreboot.org> Change-Id: I70ced115bad42d123474b18bbff2e4c0a16f3d88 Reviewed-on: https://review.coreboot.org/c/coreboot/+/51019 Reviewed-by: Furquan Shaikh <furquan@google.com> Reviewed-by: EricR Lai <ericr_lai@compal.corp-partner.google.com> Reviewed-by: Angel Pons <th3fanbus@gmail.com> Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net> Reviewed-by: Arthur Heymans <arthur@aheymans.xyz> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
		
			
				
	
	
		
			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)
 | |
| 	}
 | |
| }
 |