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