util/spd_tools: Remove old lp4x and ddr4 versions of spd_tools

The migration to the new unified version of spd_tools is complete, so
the old lp4x and ddr4 versions can be removed.

BUG=b:191776301
TEST=None

Signed-off-by: Reka Norman <rekanorman@google.com>
Change-Id: I6b1fc297739efc8dc7d7eec64956bf3343984604
Reviewed-on: https://review.coreboot.org/c/coreboot/+/57822
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Furquan Shaikh <furquan@google.com>
Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
This commit is contained in:
Reka Norman 2021-09-22 12:28:47 +10:00 committed by Furquan Shaikh
parent 8f690dd762
commit e4cf38ed36
9 changed files with 0 additions and 4115 deletions

View File

@ -1,294 +0,0 @@
# DDR4 SPD tools README
Tools for generating SPD files for DDR4 memory used in platforms with
memory down configuration. These tools generate SPDs following JESD79-4C
and Jedec 4.1.2.L-5 R29 v103 specifications.
There are two tools provided that assist with generating SPDs and Makefiles
to integrate in coreboot build. These tools can also be used to allocate
DRAM IDs (configure DRAM hardware straps) for any DDR4 memory part used
by the board.
* gen_spd.go: Generates de-duplicated SPD files using a global memory
part list provided by the mainboard in JSON format. Additionally,
generates a SPD manifest file(in CSV format) with information about
what memory part from the global list uses which of the generated
SPD files.
* gen_part_id.go: Allocates DRAM strap IDs for different DDR4
memory parts used by the board. Takes as input a list of memory parts
used (in CSV format) by the board with optional fixed ids and the SPD
manifest file generated by gen_spd.go. Generates Makefile.inc for
integrating the generated SPD files in the coreboot build.
## Tool 1 - gen_spd.go
This program takes as input:
* Pointer to directory where the generated SPD files and manifest will
be placed.
* JSON file containing a global list of memory parts with their
attributes as per the datasheet. This is the list of all known
DDR4 memory parts irrespective of their usage on the board.
* SoC platform name for which the SPDs are being generated. Currently
supported platform names are `TGL`, `PCO` and `PLK`.
Input JSON file requires the following two fields for every memory part:
* `name`: Name of the memory part
* `attribs`: List of attributes of the memory part as per its
datasheet. These attributes match the part specifications and are
independent of any SoC expectations. Tool takes care of translating
the physical attributes of the memory part to match JEDEC and Intel
MRC expectations.
`attribs` field further contains two types of sub-fields:
* Mandatory: These attributes have to be provided for a memory part.
* Optional: These attributes can be provided by memory part if it wants
to override the defaults.
### Mandatory `attribs`
* `speedMTps`: Maximum rate supported by the part in MT/s. Valid values:
`1600, 1866, 2133, 2400, 2666, 2933, 3200` MT/s.
* `CL_nRCD_nRP`: Refers to CAS Latency specified for the part (find
"CL-nRCD-nRP" in the vendor spec for the DDR4 part).
* `capacityPerDieGb`: Capacity per die in gigabits. Valid values:
`2, 4, 8, 16` Gb part.
* `diesPerPackage`: Number of dies on the part. Valid values:
`1, 2` dies per package.
* `packageBusWidth`: Number of bits of the device's address bus. Valid values:
`8, 16` bit-wide bus. NOTE: Width of x4 is not supported by this tool.
* `ranksPerPackage`: From Jedec doc 4_01_02_AnnexL-1R23:
“Package ranks per DIMM” refers to the collections of devices on the module
sharing common chip select signals (across the data width of the DIMM),
either from the edge connector for unbuffered modules or from the outputs of
a registering clock driver for RDIMMs and LRDIMMs.Number of bits of the
device's address bus. Valid values:
`1, 2` package ranks.
### Optional `attribs`
The following options are calculated by the tool based on the mandatory
attributes described for the part, but there may be cases where a default value
must be overridden, such as when a device appears to be 3200AA, but does not
support all of the CAS latencies typically supported by a speed bin 3200AA part.
Do deal with such a case, the variable can be overridden here and the tool will
use this value instead of calculating one. All values must be defined in
picosecond units, except for "CASLatencies", which would be represented as a
string like "9 10 11 12 14".
* `TAAMinPs`: Defines the minimum CAS Latency.
Table 48 of Jedec doc 4_01_02_AnnexL-5R29 lists tAAmin for each speed grade.
* `TRASMinPs`: Refers to the minimum active to precharge delay time.
Table 55 of Jedec doc 4_01_02_AnnexL-5R29 lists tRPmin for each speed grade.
* `TCKMinPs`: Refers to the minimum clock cycle time.
Table 42 of Jedec doc 4_01_02_AnnexL-5R29 lists tCKmin for each speed grade.
* `TCKMaxPs`:Refers to the minimum clock cycle time.
Table 44 of Jedec doc 4_01_02_AnnexL-5R29 lists tCKmin for each speed grade.
* `TRFC1MinPs`: Refers to the minimum refresh recovery delay time.
Table 59 of Jedec doc 4_01_02_AnnexL-5R29 lists tRFC1min for each page size.
* `TRFC2MinPs`: Refers to the minimum refresh recovery delay time.
Table 61 of Jedec doc 4_01_02_AnnexL-5R29 lists tRFC2min for each page size.
* `TRFC4MinPs`: Refers to the minimum refresh recovery delay time.
Table 63 of Jedec doc 4_01_02_AnnexL-5R29 lists tRFC4min for each page size.
* `TFAWMinPs`:: Refers to the minimum four activate window delay time.
Table 66 of Jedec doc 4_01_02_AnnexL-5R29 lists tFAWmin for each speed grade
and page size combination.
* `TRRDSMinPs`: Refers to the minimum activate to activate delay time to
different bank groups.
Table 68 of Jedec doc 4_01_02_AnnexL-5R29 lists tRRD_Smin for each speed grade
and page size combination.
* `TRRDLMinPs`: Refers to the minimum activate to activate delay time to the
same bank group.
Table 70 of Jedec doc 4_01_02_AnnexL-5R29 lists tRRD_Lmin for each speed grade
and page size combination.
* `TCCDLMinPs`: Refers to the minimum CAS to CAS delay time to same bank group.
Table 72 of Jedec doc 4_01_02_AnnexL-5R29 lists tCCD_Lmin for each speed grade.
* `TWRMinPs`: Refers to the minimum write recovery time.
Table 75 of Jedec doc 4_01_02_AnnexL-5R29 lists tWRmin for each ddr4 type.
* `TWTRSMinPs`: Refers to minimum write to read time to different bank group.
Table 78 of Jedec doc 4_01_02_AnnexL-5R29 lists tWTR_Smin for each ddr4 type.
* `TWTRLMinPs`: Refers to minimum write to read time to same bank group.
Table 80 of Jedec doc 4_01_02_AnnexL-5R29 lists tWTR_Lmin for each ddr4 type.
* `CASLatencies`: Refers to the CAS latencies supported by the part.
The speed bin tables in the back of Jedec doc 4_01_02_AnnexL-5R29 define the
standard CAS latencies that a speed bin part is supposed to support.
In cases where a part does not support all of the CAS latencies listed in the
speed bin tables, this entry should be used to override the default settings.
### Example JSON file
```
{
"parts": [
{
"name": "MEMORY_PART_A",
"attribs": {
"speedMTps": 3200,
"CL_nRCD_nRP": 22
"capacityPerDieGb": 8,
"diesPerPackage": 2,
"packageBusWidth": 16,
"ranksPerPackage": 1,
}
},
{
"name": "MEMORY_PART_B",
"attribs": {
"speedMTps": 3200,
"CL_nRCD_nRP": 22
"capacityPerDieGb": 8,
"diesPerPackage": 1,
"packageBusWidth": 16,
"ranksPerPackage": 2,
"casLatencies": "9 10 11 12 13 14 15 16 17 18 19 20",
"tCKMaxPs": "1250"
}
}
]
}
```
### Output
This tool generates the following files using the global list of
memory parts in JSON format as described above:
* De-duplicated SPDs required for the different memory parts. These
SPD files are named (ddr4-spd-1.hex, ddr4-spd-2.hex, and so on)
and placed in the directory provided as an input to the tool.
* CSV file representing which of the deduplicated SPD files is used
by which memory part. This file is named as
`ddr4_spd_manifest.generated.txt` and placed in the directory provided
as an input to the tool along with the generated SPD
files. Example CSV file:
```
MEMORY_PART_A, ddr4-spd-1.hex
MEMORY_PART_B, ddr4-spd-2.hex
MEMORY_PART_C, ddr4-spd-3.hex
MEMORY_PART_D, ddr4-spd-2.hex
MEMORY_PART_E, ddr4-spd-2.hex
```
## Tool 2 - gen_part_id.go
This program takes as input:
* Pointer to directory where the SPD files and the manifest file
`ddr4_spd_manifest.generated.txt` (in CSV format) are placed by
gen_spd.go
* CSV file containing list of memory parts used by the board and optional
fixed id. Each line of the file is supposed to contain one memory part `name`
as present in the global list of memory parts provided to gen_spd.go.
Optionally a fixed id may also be assigned to the part if required.
NOTE: Only assign a fixed ID if required for legacy reasons.
* Pointer to directory where the generated Makefile.inc should be
placed by the tool.
Sample input (mem_parts_used_file.txt):
```
K4AAG165WA-BCWE,1
MT40A512M16TB-062E:J
MT40A1G16KD-062E:E
K4A8G165WC-BCWE
H5AN8G6NDJR-XNC,8
H5ANAG6NCMR-XNC
```
NOTE: This will ensure SPDs compatible with K4AAG165WA-BCWE and H5AN8G6NDJR-XNC
are assigned to ID 1 and 8 respectively. All other memory parts will be
assigned to the first compatible ID. Assigning fixed IDs may result in
duplicate SPD entries or gaps in the ID mapping.
### Output
This program provides the following:
* Prints out the list of DRAM hardware strap IDs that should be
allocated to each memory part listed in the input file.
* Makefile.inc is generated in the provided directory to integrate
SPDs generated by gen_spd.go with the coreboot build for the board.
* dram_id.generated.txt is generated in the same directory as
Makefile. This contains the part IDs assigned to the different
memory parts. (Useful to integrate in board schematics).
Sample output (dram_id.generated.txt):
```
DRAM Part Name ID to assign
MEMORY_PART_A 0 (0000)
MEMORY_PART_B 1 (0001)
MEMORY_PART_C 2 (0010)
MEMORY_PART_D 1 (0001)
```
Sample Makefile.inc:
```
## SPDX-License-Identifier: GPL-2.0-or-later
## This is an auto-generated file. Do not edit!!
SPD_SOURCES =
SPD_SOURCES += ddr4-spd-1.hex # ID = 0(0b0000) Parts = MEMORY_PART_A
SPD_SOURCES += ddr4-spd-2.hex # ID = 1(0b0001) Parts = MEMORY_PART_B, MEMORY_PART_D
SPD_SOURCES += ddr4-spd-empty.hex # ID = 2(0b0010)
SPD_SOURCES += ddr4-spd-3.hex # ID = 2(0b0010) Parts = MEMORY_PART_C
```
NOTE: Empty entries may be required if there is a gap created by a memory part
with a fixed id.
### Note of caution
This program assigns DRAM IDs using the order of DRAM part names
provided in the input file. Thus, when adding a new memory part to the
list, it should always go to the end of the input text file. This
guarantees that the memory parts that were already assigned IDs do not
change.
## How to build the tools?
```
# go build gen_spd.go
# go build gen_part_id.go
```
## How to use the tools?
```
# ./gen_spd <spd_dir> <mem_parts_list_json> <platform>
# ./gen_part_id <spd_dir> <makefile_dir> <mem_parts_used_file>
```
## Example Usage
```
# ./gen_spd ../../../../src/soc/intel/tigerlake/spd/ddr4 ./global_ddr4_mem_parts.json.txt 'TGL'
```
### Need to add a new memory part for a board?
* If the memory part is not present in the global list of memory
parts, then add the memory part name and attributes as per the
datasheet to the file containing the global list.
* Use `gen_spd.go` with input as the file containing the global list
of memory parts to generate de-duplicated SPDs.
* If a new SPD file is generated, use `git add` to add it to the
tree and push a CL for review.
* Update the file containing memory parts used by board (variant) to
add the new memory part name at the end of the file.
* Use gen_part_id.go providing it pointer to the location where SPD
files are stored and file containing the list of memory parts used
by the board(variant).
* Use `git add` to add `Makefile.inc` and `dram_id.generated.txt`
with updated changes and push a CL for review.

View File

@ -1,308 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
package main
import (
"encoding/csv"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"strconv"
)
/*
* This program allocates DRAM strap IDs for different parts that are being used by the variant.
*
* It expects the following inputs:
* Pointer to SPD directory. This is the location where SPD files and SPD Manifest generated by
* gen_spd.go are placed.
* Pointer to Makefile directory. Makefile.inc generated by this program is placed in this
* location.
* Text file containing a list of memory parts names used by the board. Each line in the file
* is expected to have one memory part name.
*/
const (
SPDManifestFileName = "ddr4_spd_manifest.generated.txt"
MakefileName = "Makefile.inc"
DRAMIdFileName = "dram_id.generated.txt"
MaxMemoryId = 15
)
func usage() {
fmt.Printf("\nUsage: %s <spd_dir> <makefile_dir> <mem_parts_used_file>\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(" makefile_dir = Directory path where generated Makefile.inc should be placed\n")
fmt.Printf(" mem_parts_used_file = CSV file containing list of memory parts used by the board and optional fixed ids\n\n\n")
}
func checkArgs() error {
for _, arg := range os.Args[1:] {
if _, err := os.Stat(arg); err != nil {
return err
}
}
return nil
}
type usedPart struct {
partName string
index int
}
/*
* Read input file CSV that contains list of memory part names used by the variant
* and an optional assigned id.
*/
func readParts(memPartsUsedFileName string) ([]usedPart, error) {
f, err := os.Open(memPartsUsedFileName)
if err != nil {
return nil, err
}
defer f.Close()
r := csv.NewReader(f)
r.FieldsPerRecord = -1 // Allow variable length records
r.TrimLeadingSpace = true
r.Comment = '#'
parts := []usedPart{}
for {
fields, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
if len(fields) == 1 {
parts = append(parts, usedPart{fields[0], -1})
} else if len(fields) == 2 {
assignedId, err := strconv.Atoi(fields[1])
if err != nil {
return nil, err
}
if assignedId > MaxMemoryId || assignedId < 0 {
return nil, fmt.Errorf("Out of bounds assigned id %d for part %s", assignedId, fields[0])
}
parts = append(parts, usedPart{fields[0], assignedId})
} else {
return nil, fmt.Errorf("mem_parts_used_file file is incorrectly formatted")
}
}
return parts, nil
}
/*
* Read SPD manifest file(CSV) generated by gen_spd program and generate two maps:
* 1. Part to SPD Map : This maps global memory part name to generated SPD file name
* 2. SPD to Index Map: This generates a map of deduplicated SPD file names to index assigned to
* that SPD. This function sets index for all SPDs to -1. This index gets
* updated as part of genPartIdInfo() depending upon the SPDs actually used
* by the variant.
*/
func readSPDManifest(SPDDirName string) (map[string]string, map[string]int, error) {
f, err := os.Open(filepath.Join(SPDDirName, SPDManifestFileName))
if err != nil {
return nil, nil, err
}
defer f.Close()
r := csv.NewReader(f)
partToSPDMap := make(map[string]string)
SPDToIndexMap := make(map[string]int)
for {
fields, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
return nil, nil, err
}
if len(fields) != 2 {
return nil, nil, fmt.Errorf("CSV file is incorrectly formatted")
}
partToSPDMap[fields[0]] = fields[1]
SPDToIndexMap[fields[1]] = -1
}
return partToSPDMap, SPDToIndexMap, nil
}
/* Print information about memory part used by variant and ID assigned to it. */
func appendPartIdInfo(s *string, partName string, index int) {
*s += fmt.Sprintf("%-30s %d (%04b)\n", partName, index, int64(index))
}
type partIds struct {
SPDFileName string
memParts string
}
/*
* For each part used by variant, check if the SPD (as per the manifest) already has an ID
* assigned to it. If yes, then add the part name to the list of memory parts supported by the
* SPD entry. If not, then assign the next ID to the SPD file and add the part name to the
* list of memory parts supported by the SPD entry.
*
* Returns list of partIds that contains spdFileName and supported memory parts for each
* assigned ID.
*/
func genPartIdInfo(parts []usedPart, partToSPDMap map[string]string, SPDToIndexMap map[string]int, makefileDirName string) ([]partIds, error) {
partIdList := []partIds{}
var s string
// Assign parts with fixed ids first
for _, p := range parts {
if p.index == -1 {
continue
}
if p.partName == "" {
return nil, fmt.Errorf("Invalid part entry")
}
SPDFileName, ok := partToSPDMap[p.partName]
if !ok {
return nil, fmt.Errorf("Failed to find part ", p.partName, " in SPD Manifest. Please add the part to global part list and regenerate SPD Manifest")
}
// Extend partIdList with empty entries if needed
for i := len(partIdList) - 1; i < p.index; i++ {
partIdList = append(partIdList, partIds{})
}
if partIdList[p.index].SPDFileName != "" {
return nil, fmt.Errorf("Part ", p.partName, " is assigned to an already assigned ID ", p.index)
}
partIdList[p.index] = partIds{SPDFileName: SPDFileName, memParts: p.partName}
// SPDToIndexMap should point to first assigned index in the used part list
if SPDToIndexMap[SPDFileName] < 0 {
SPDToIndexMap[SPDFileName] = p.index
}
}
s += fmt.Sprintf("%-30s %s\n", "DRAM Part Name", "ID to assign")
// Assign parts with no fixed id
for _, p := range parts {
if p.partName == "" {
return nil, fmt.Errorf("Invalid part entry")
}
// Add assigned parts to dram id file in the order they appear
if p.index != -1 {
appendPartIdInfo(&s, p.partName, p.index)
continue
}
SPDFileName, ok := partToSPDMap[p.partName]
if !ok {
return nil, fmt.Errorf("Failed to find part ", p.partName, " in SPD Manifest. Please add the part to global part list and regenerate SPD Manifest")
}
index := SPDToIndexMap[SPDFileName]
if index != -1 {
partIdList[index].memParts += ", " + p.partName
appendPartIdInfo(&s, p.partName, index)
continue
}
// Find first empty index
for i, partId := range partIdList {
if partId.SPDFileName == "" {
index = i
break
}
}
// Append new entry
if index == -1 {
index = len(partIdList)
partIdList = append(partIdList, partIds{})
}
SPDToIndexMap[SPDFileName] = index
appendPartIdInfo(&s, p.partName, index)
partIdList[index] = partIds{SPDFileName: SPDFileName, memParts: p.partName}
}
fmt.Printf("%s", s)
err := ioutil.WriteFile(filepath.Join(makefileDirName, DRAMIdFileName), []byte(s), 0644)
return partIdList, err
}
var generatedCodeLicense string = "## SPDX-License-Identifier: GPL-2.0-or-later"
var autoGeneratedInfo string = "## This is an auto-generated file. Do not edit!!"
/*
* This function generates Makefile.inc under the variant directory path and adds assigned SPDs
* to SPD_SOURCES.
*/
func genMakefile(partIdList []partIds, makefileDirName string) error {
var s string
s += fmt.Sprintf("%s\n%s\n\n", generatedCodeLicense, autoGeneratedInfo)
s += fmt.Sprintf("SPD_SOURCES =\n")
for i := 0; i < len(partIdList); i++ {
if partIdList[i].SPDFileName == "" {
s += fmt.Sprintf("SPD_SOURCES += %s ", "ddr4-spd-empty.hex")
s += fmt.Sprintf(" # ID = %d(0b%04b)\n", i, int64(i))
} else {
s += fmt.Sprintf("SPD_SOURCES += %s ", partIdList[i].SPDFileName)
s += fmt.Sprintf(" # ID = %d(0b%04b) ", i, int64(i))
s += fmt.Sprintf(" Parts = %04s\n", partIdList[i].memParts)
}
}
return ioutil.WriteFile(filepath.Join(makefileDirName, MakefileName), []byte(s), 0644)
}
func main() {
if len(os.Args) != 4 {
usage()
log.Fatal("Incorrect number of arguments")
}
SPDDir, MakefileDir, MemPartsUsedFile := os.Args[1], os.Args[2], os.Args[3]
partToSPDMap, SPDToIndexMap, err := readSPDManifest(SPDDir)
if err != nil {
log.Fatal(err)
}
parts, err := readParts(MemPartsUsedFile)
if err != nil {
log.Fatal(err)
}
partIdList, err := genPartIdInfo(parts, partToSPDMap, SPDToIndexMap, MakefileDir)
if err != nil {
log.Fatal(err)
}
if err := genMakefile(partIdList, MakefileDir); err != nil {
log.Fatal(err)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,254 +0,0 @@
// Global list of ddr4 memory part attributes.
// These attributes match the part specifications and are independent
// of any SoC expectations.
{
"parts": [
{
"name": "H5AN8G6NDJR-XNC",
"attribs": {
"speedMTps": 3200,
"CL_nRCD_nRP": 22,
"capacityPerDieGb": 8,
"diesPerPackage": 1,
"packageBusWidth": 16,
"ranksPerPackage": 1
}
},
{
"name": "MT40A512M16TB-062E:J",
"attribs": {
"speedMTps": 3200,
"CL_nRCD_nRP": 22,
"capacityPerDieGb": 8,
"diesPerPackage": 1,
"packageBusWidth": 16,
"ranksPerPackage": 1
}
},
{
"name": "H5ANAG6NCMR-XNC",
"attribs": {
"speedMTps": 3200,
"CL_nRCD_nRP": 22,
"capacityPerDieGb": 8,
"diesPerPackage": 2,
"packageBusWidth": 16,
"ranksPerPackage": 1
}
},
{
// Datasheet Revision: Rev. 1.1 / Sep.2017
"name": "HMA851S6CJR6N-VK",
"attribs": {
"speedMTps": 2666,
"CL_nRCD_nRP": 19,
"capacityPerDieGb": 8,
"diesPerPackage": 1,
"packageBusWidth": 16,
"ranksPerPackage": 1
}
},
{
// Datasheet Revision: Rev. 1.5, Apr. 2017
"name": "K4A8G165WC-BCTD",
"attribs": {
"speedMTps": 2666,
"CL_nRCD_nRP": 19,
"capacityPerDieGb": 8,
"diesPerPackage": 1,
"packageBusWidth": 16,
"ranksPerPackage": 1
}
},
{
// Datasheet Revision: Rev. 1.3 / Jun.2018
"name": "H5AN8G6NCJR-VKC",
"attribs": {
"speedMTps": 2666,
"CL_nRCD_nRP": 19,
"capacityPerDieGb": 8,
"diesPerPackage": 1,
"packageBusWidth": 16,
"ranksPerPackage": 1
}
},
{
// Datasheet Revision: Rev. F 10/17 EN
"name": "MT40A1G16KNR-075:E",
"attribs": {
"speedMTps": 2666,
"CL_nRCD_nRP": 18,
"capacityPerDieGb": 8,
"diesPerPackage": 2,
"packageBusWidth": 16,
"ranksPerPackage": 1
}
},
{
// Datasheet Revision: Rev. 1.4, Jul. 2017
"name": "K4AAG165WB-MCTD",
"attribs": {
"speedMTps": 2666,
"CL_nRCD_nRP": 19,
"capacityPerDieGb": 8,
"diesPerPackage": 1,
"packageBusWidth": 8,
"ranksPerPackage": 1
}
},
{
// Datasheet Revision: Rev. 1.0 / Aug.2018
"name": "H5ANAG6NCMR-VKC",
"attribs": {
"speedMTps": 2666,
"CL_nRCD_nRP": 19,
"capacityPerDieGb": 8,
"diesPerPackage": 2,
"packageBusWidth": 16,
"ranksPerPackage": 1
}
},
{
// Datasheet Revision: Rev. 0.5, Jun. 2019
"name": "K4A8G165WC-BCWE",
"attribs": {
"speedMTps": 3200,
"CL_nRCD_nRP": 22,
"capacityPerDieGb": 8,
"diesPerPackage": 1,
"packageBusWidth": 16,
"ranksPerPackage": 1
}
},
{
// Datasheet Revision: Rev. G 08/2020 EN
"name": "MT40A1G16KD-062E:E",
"attribs": {
"speedMTps": 3200,
"CL_nRCD_nRP": 22,
"capacityPerDieGb": 16,
"diesPerPackage": 1,
"packageBusWidth": 16,
"ranksPerPackage": 1,
// Table 158 - Refersh Timing - 16Gb
"TRFC1MinPs": 350000,
"TRFC2MinPs": 260000,
"TRFC4MinPs": 160000
}
},
{
// Datasheet Revision: Rev. 0.5, Feb. 2019
"name": "K4AAG165WA-BCWE",
"attribs": {
"speedMTps": 3200,
"CL_nRCD_nRP": 22,
"capacityPerDieGb": 16,
"diesPerPackage": 1,
"packageBusWidth": 16,
"ranksPerPackage": 1,
// Table 57 - 16Gb
"TRFC1MinPs": 350000,
"TRFC2MinPs": 260000,
"TRFC4MinPs": 160000
}
},
{
// Datasheet Revision: Rev. 1.5 / Mar.2019
"name": "H5AN8G6NCJR-XNC",
"attribs": {
"speedMTps": 3200,
"CL_nRCD_nRP": 22,
"capacityPerDieGb": 8,
"diesPerPackage": 1,
"packageBusWidth": 16,
"ranksPerPackage": 1
}
},
{
// Datasheet Revision: Rev. 1.0, Dec. 2019
"name": "K4AAG165WA-BCTD",
"attribs": {
"speedMTps": 2666,
"CL_nRCD_nRP": 19,
"capacityPerDieGb": 16,
"diesPerPackage": 1,
"packageBusWidth": 16,
"ranksPerPackage": 1,
// Table 41 - Note: Both 550ns and 350ns tRFC1 is supported
"TRFC1MinPs": 350000,
"TRFC2MinPs": 260000,
"TRFC4MinPs": 160000
}
},
{
// Datasheet Revision: Rev. 1.0, Feb. 2020
"name": "H5ANAG6NDMR-XNC",
"attribs": {
"speedMTps": 3200,
"CL_nRCD_nRP": 22,
"capacityPerDieGb": 8,
"diesPerPackage": 2,
"packageBusWidth": 16,
"ranksPerPackage": 1
}
},
{
// Datasheet Revision: Rev. 1.4, May. 2020
"name": "H5ANAG6NCJR-XNC",
"attribs": {
"speedMTps": 3200,
"CL_nRCD_nRP": 22,
"capacityPerDieGb": 16,
"diesPerPackage": 1,
"packageBusWidth": 16,
"ranksPerPackage": 1
}
},
{
// Datasheet Revision: Rev. 0.0, Apr. 2020
"name": "K4AAG165WB-BCWE",
"attribs": {
"speedMTps": 3200,
"CL_nRCD_nRP": 22,
"capacityPerDieGb": 16,
"diesPerPackage": 1,
"packageBusWidth": 16,
"ranksPerPackage": 1
}
},
{
// Datasheet Revision: Rev. A 03/2021 EN
"name": "MT40A1G16RC-062E:B",
"attribs": {
"speedMTps": 3200,
"CL_nRCD_nRP": 22,
"capacityPerDieGb": 16,
"diesPerPackage": 1,
"packageBusWidth": 16,
"ranksPerPackage": 1
}
},
{
"name": "MT40A512M16TB-062E:R",
"attribs": {
"speedMTps": 3200,
"CL_nRCD_nRP": 22,
"capacityPerDieGb": 8,
"diesPerPackage": 1,
"packageBusWidth": 16,
"ranksPerPackage": 1
}
},
{
"name": "4JQA-0622AD",
"attribs": {
"speedMTps": 3200,
"CL_nRCD_nRP": 22,
"capacityPerDieGb": 8,
"diesPerPackage": 1,
"packageBusWidth": 16,
"ranksPerPackage": 1
}
}
]
}

View File

@ -1,11 +0,0 @@
Tools for generating SPD files for DDR4 memory used in platforms with
memory down configuration.
* _gen_spd.go_ - Generates de-duplicated SPD files using a
global memory part list provided by the
mainboard in JSON format. `Go`
* _gen_part_id.go_ - Allocates DRAM strap IDs for different
DDR4 memory parts used by the board. `Go`

View File

@ -1,266 +0,0 @@
# LPDDR4x SPD tools README
Tools for generating SPD files for LPDDR4x memory used in memory down
configurations on Intel Tiger Lake (TGL), Jasper Lake (JSL), and Alder
Lake (ADL) based platforms. These tools generate SPDs following
JESD209-4C specification and Intel recommendations (doc #616599,
#610202, #634730) for LPDDR4x SPD.
There are two tools provided that assist TGL, JSL and ADL based
mainboards to generate SPDs and Makefile to integrate these SPDs in
coreboot build. These tools can also be used to allocate DRAM IDs
(configure DRAM hardware straps) for any LPDDR4x memory part used by the
board.
* gen_spd.go: Generates de-duplicated SPD files using a global memory
part list provided by the mainboard in JSON format. Additionally,
generates a SPD manifest file(in CSV format) with information about
what memory part from the global list uses which of the generated
SPD files.
* gen_part_id.go: Allocates DRAM strap IDs for different LPDDR4x
memory parts used by the board. Takes as input list of memory parts
used by the board (with one memory part on each line) and the SPD
manifest file generated by gen_spd.go. Generates Makefile.inc for
integrating the generated SPD files in the coreboot build.
## Tool 1 - gen_spd.go
This program takes as input:
* Pointer to directory where the generated SPD files and manifest will
be placed.
* JSON file containing a global list of memory parts with their
attributes as per the datasheet. This is the list of all known
LPDDR4x memory parts irrespective of their usage on the board.
* SoC platform name for which the SPDs are being generated. Currently
supported platform names are `TGL`, `JSL` and `ADL`.
Input JSON file requires the following two fields for every memory part:
* `name`: Name of the memory part
* `attribs`: List of attributes of the memory part as per its
datasheet. These attributes match the part specifications and are
independent of any SoC expectations. Tool takes care of translating
the physical attributes of the memory part to match JEDEC and Intel
MRC expectations.
`attribs` field further contains two types of sub-fields:
* Mandatory: These attributes have to be provided for a memory part.
* Optional: These attributes can be provided by memory part if it wants
to override the defaults.
### Mandatory `attribs`
* `densityPerChannelGb`: Density in Gb of the physical channel.
* `banks`: Number of banks per physical channel. This is typically 8
for LPDDR4x memory parts.
* `channelsPerDie`: Number of physical channels per die. Valid values:
`1, 2, 4`. For a part with x16 bit width, number of channels per die
is 1 or 2. For a part with x8 bit width, number of channels can be
2 or 4 (4 is basically when two dual-channel byte mode devices are
combined as shown in Figure 3 in JESD209-4C).
* `diesPerPackage`: Number of physical dies in each SDRAM
package. As per JESD209-4C, "Standard LPDDR4 package ballmaps
allocate one ZQ ball per die." Thus, number of diesPerPackage is the
number of ZQ balls on the package.
* `bitWidthPerChannel`: Width of each physical channel. Valid values:
`8, 16` bits.
* `ranksPerChannel`: Number of ranks per physical channel. Valid
values: `1, 2`. If the channels across multiple dies share the same
DQ/DQS pins but use a separate CS, then ranks is 2 else it is 1.
* `speedMbps`: Maximum data rate supported by the part in Mbps. Valid
values: `3200, 3733, 4267` Mbps.
### Optional `attribs`
* `trfcabNs`: Minimum Refresh Recovery Delay Time (tRFCab) for all
banks in nanoseconds. As per JESD209-4C, this is dependent on the
density per channel. Default values used:
* 6Gb : 280ns
* 8Gb : 280ns
* 12Gb: 380ns
* 16Gb: 380ns
* `trfcpbNs`: Minimum Refresh Recovery Delay Time (tRFCab) per
bank in nanoseconds. As per JESD209-4C, this is dependent on the
density per channel. Default values used:
* 6Gb : 140ns
* 8Gb : 140ns
* 12Gb: 190ns
* 16Gb: 190ns
* `trpabMinNs`: Minimum Row Precharge Delay Time (tRPab) for all banks
in nanoseconds. As per JESD209-4C, this is max(21ns, 4nck) which
defaults to `21ns`.
* `trppbMinNs`: Minimum Row Precharge Delay Time (tRPpb) per bank in
nanoseconds. As per JESD209-4C, this is max(18ns, 4nck) which
defaults to `18ns`.
* `tckMinPs`: SDRAM minimum cycle time (tckMin) value in
picoseconds. This is typically calculated based on the `speedMbps`
attribute. `(1 / speedMbps) * 2`. Default values used(taken from
JESD209-4C):
* 4267 Mbps: 468ps
* 3733 Mbps: 535ps
* 3200 Mbps: 625ps
* `tckMaxPs`: SDRAM maximum cycle time (tckMax) value in
picoseconds. Default value used: `31875ps`. As per JESD209-4C,
TCKmax should be 100ns (100000ps) for all speed grades. But the SPD
byte to encode this field is only 1 byte. Hence, the maximum value
that can be encoded is 31875ps.
* `taaMinPs`: Minimum CAS Latency Time(taaMin) in picoseconds. This
value defaults to nck * tckMin, where nck is minimum CAS latency.
* `trcdMinNs`: Minimum RAS# to CAS# Delay Time (tRCDmin) in
nanoseconds. As per JESD209-4C, this is max(18ns, 4nck) which
defaults to `18ns`.
* `casLatencies`: List of CAS latencies supported by the
part. This is dependent on the attrib `speedMbps`. Default values
used:
* 4267: `"6 10 14 20 24 28 32 36"`.
* 3733: `"6 10 14 20 24 28 32"`.
* 3200: `"6 10 14 20 24 28"`.
### Example JSON file
```
{
"parts": [
{
"name": "MEMORY_PART_A",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 2,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 4267
}
},
{
"name": "MEMORY_PART_B",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 1,
"diesPerPackage": 2,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 3733,
"casLatencies": "14 20 24 28 32",
"tckMaxPs": "1250"
}
}
]
}
```
### Output
This tool generates the following files using the global list of
memory parts in JSON format as described above:
* De-duplicated SPDs required for the different memory parts. These
SPD files are named (spd_1.hex, spd_2.hex, spd_3.hex and so on)
and placed in the directory provided as an input to the tool.
* CSV file representing which of the deduplicated SPD files is used
by which memory part. This file is named as
`spd_manifest.generated.txt` and placed in the directory provided
as an input to the tool along with the generated SPD
files. Example CSV file:
```
MEMORY_PART_A, spd_1.hex
MEMORY_PART_B, spd_2.hex
MEMORY_PART_C, spd_3.hex
MEMORY_PART_D, spd_2.hex
MEMORY_PART_E, spd_2.hex
```
## Tool 2 - gen_part_id.go
This program takes as input:
* Pointer to directory where the SPD files and the manifest file
`spd_manifest.generated.txt` (in CSV format) are placed by
gen_spd.go
* File containing list of memory parts used by the board. Each line of
the file is supposed to contain one memory part `name` as present in
the global list of memory parts provided to gen_spd.go
* Pointer to directory where the generated Makefile.inc should be
placed by the tool.
### Output
This program provides the following:
* Prints out the list of DRAM hardware strap IDs that should be
allocated to each memory part listed in the input file.
* Makefile.inc is generated in the provided directory to integrate
SPDs generated by gen_spd.go with the coreboot build for the board.
* dram_id.generated.txt is generated in the same directory as
Makefile. This contains the part IDs assigned to the different
memory parts. (Useful to integrate in board schematics).
Sample output (dram_id.generated.txt):
```
DRAM Part Name ID to assign
MEMORY_PART_A 0 (0000)
MEMORY_PART_B 1 (0001)
MEMORY_PART_C 2 (0010)
MEMORY_PART_D 1 (0001)
```
Sample Makefile.inc:
```
## SPDX-License-Identifier: GPL-2.0-or-later
## This is an auto-generated file. Do not edit!!
SPD_SOURCES =
SPD_SOURCES += spd_1.hex # ID = 0(0b0000) Parts = MEMORY_PART_A
SPD_SOURCES += spd_2.hex # ID = 1(0b0001) Parts = MEMORY_PART_B, MEMORY_PART_D
SPD_SOURCES += spd_3.hex # ID = 2(0b0010) Parts = MEMORY_PART_C
```
### Note of caution
This program assigns DRAM IDs using the order of DRAM part names
provided in the input file. Thus, when adding a new memory part to the
list, it should always go to the end of the input text file. This
guarantees that the memory parts that were already assigned IDs do not
change.
## How to build the tools?
```
# go build gen_spd.go
# go build gen_part_id.go
```
## How to use the tools?
```
# ./gen_spd <spd_dir> <mem_parts_list_json> <platform>
# ./gen_part_id <spd_dir> <makefile_dir> <mem_parts_used_file>
```
### Need to add a new memory part for a board?
* If the memory part is not present in the global list of memory
parts, then add the memory part name and attributes as per the
datasheet to the file containing the global list.
* Use `gen_spd.go` with input as the file containing the global list
of memory parts to generate de-duplicated SPDs.
* If a new SPD file is generated, use `git add` to add it to the
tree and push a CL for review.
* Update the file containing memory parts used by board (variant) to
add the new memory part name at the end of the file.
* Use gen_part_id.go providing it pointer to the location where SPD
files are stored and file containing the list of memory parts used
by the board(variant).
* Use `git add` to add `Makefile.inc` and `dram_id.generated.txt`
with updated changes and push a CL for review.

View File

@ -1,214 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
package main
import (
"encoding/csv"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
)
/*
* This program allocates DRAM strap IDs for different parts that are being used by the variant.
*
* It expects the following inputs:
* Pointer to SPD directory. This is the location where SPD files and SPD Manifest generated by
* gen_spd.go are placed.
* Pointer to Makefile directory. Makefile.inc generated by this program is placed in this
* location.
* Text file containing a list of memory parts names used by the board. Each line in the file
* is expected to have one memory part name.
*/
const (
SPDManifestFileName = "lp4x_spd_manifest.generated.txt"
MakefileName = "Makefile.inc"
DRAMIdFileName = "dram_id.generated.txt"
)
func usage() {
fmt.Printf("\nUsage: %s <spd_dir> <makefile_dir> <mem_parts_used_file>\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(" makefile_dir = Directory path where generated Makefile.inc should be placed\n")
fmt.Printf(" mem_parts_used_file = File containing list of memory parts used by the board\n\n\n")
}
func checkArgs() error {
for _, arg := range os.Args[1:] {
if _, err := os.Stat(arg); err != nil {
return err
}
}
return nil
}
/*
* Read input file that contains list of memory part names used by the variant (one on a line)
* and split into separate strings for each part name.
*/
func readParts(memPartsUsedFileName string) ([]string, error) {
lines, err := ioutil.ReadFile(memPartsUsedFileName)
if err != nil {
return nil, err
}
str := string(lines)
parts := strings.Split(str, "\n")
return parts, nil
}
/*
* Read SPD manifest file(CSV) generated by gen_spd program and generate two maps:
* 1. Part to SPD Map : This maps global memory part name to generated SPD file name
* 2. SPD to Index Map: This generates a map of deduplicated SPD file names to index assigned to
* that SPD. This function sets index for all SPDs to -1. This index gets
* updated as part of genPartIdInfo() depending upon the SPDs actually used
* by the variant.
*/
func readSPDManifest(SPDDirName string) (map[string]string, map[string]int, error) {
f, err := os.Open(filepath.Join(SPDDirName, SPDManifestFileName))
if err != nil {
return nil, nil, err
}
defer f.Close()
r := csv.NewReader(f)
partToSPDMap := make(map[string]string)
SPDToIndexMap := make(map[string]int)
for {
fields, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
return nil, nil, err
}
if len(fields) != 2 {
return nil, nil, fmt.Errorf("CSV file is incorrectly formatted")
}
partToSPDMap[fields[0]] = fields[1]
SPDToIndexMap[fields[1]] = -1
}
return partToSPDMap, SPDToIndexMap, nil
}
/* Print information about memory part used by variant and ID assigned to it. */
func appendPartIdInfo(s *string, partName string, index int) {
*s += fmt.Sprintf("%-30s %d (%04b)\n", partName, index, int64(index))
}
type partIds struct {
SPDFileName string
memParts string
}
/*
* For each part used by variant, check if the SPD (as per the manifest) already has an ID
* assigned to it. If yes, then add the part name to the list of memory parts supported by the
* SPD entry. If not, then assign the next ID to the SPD file and add the part name to the
* list of memory parts supported by the SPD entry.
*
* Returns list of partIds that contains spdFileName and supported memory parts for each
* assigned ID.
*/
func genPartIdInfo(parts []string, partToSPDMap map[string]string, SPDToIndexMap map[string]int, makefileDirName string) ([]partIds, error) {
partIdList := []partIds{}
curId := 0
var s string
s += fmt.Sprintf("%-30s %s\n", "DRAM Part Name", "ID to assign")
for _, p := range parts {
if p == "" {
continue
}
SPDFileName, ok := partToSPDMap[p]
if !ok {
return nil, fmt.Errorf("Failed to find part ", p, " in SPD Manifest. Please add the part to global part list and regenerate SPD Manifest")
}
index := SPDToIndexMap[SPDFileName]
if index != -1 {
partIdList[index].memParts += ", " + p
appendPartIdInfo(&s, p, index)
continue
}
SPDToIndexMap[SPDFileName] = curId
appendPartIdInfo(&s, p, curId)
entry := partIds{SPDFileName: SPDFileName, memParts: p}
partIdList = append(partIdList, entry)
curId++
}
fmt.Printf("%s", s)
err := ioutil.WriteFile(filepath.Join(makefileDirName, DRAMIdFileName), []byte(s), 0644)
return partIdList, err
}
var generatedCodeLicense string = "## SPDX-License-Identifier: GPL-2.0-or-later"
var autoGeneratedInfo string = "## This is an auto-generated file. Do not edit!!"
/*
* This function generates Makefile.inc under the variant directory path and adds assigned SPDs
* to SPD_SOURCES.
*/
func genMakefile(partIdList []partIds, makefileDirName string) error {
var s string
s += fmt.Sprintf("%s\n%s\n\n", generatedCodeLicense, autoGeneratedInfo)
s += fmt.Sprintf("SPD_SOURCES =\n")
for i := 0; i < len(partIdList); i++ {
s += fmt.Sprintf("SPD_SOURCES += %s ", partIdList[i].SPDFileName)
s += fmt.Sprintf(" # ID = %d(0b%04b) ", i, int64(i))
s += fmt.Sprintf(" Parts = %04s\n", partIdList[i].memParts)
}
return ioutil.WriteFile(filepath.Join(makefileDirName, MakefileName), []byte(s), 0644)
}
func main() {
if len(os.Args) != 4 {
usage()
log.Fatal("Incorrect number of arguments")
}
SPDDir, MakefileDir, MemPartsUsedFile := os.Args[1], os.Args[2], os.Args[3]
partToSPDMap, SPDToIndexMap, err := readSPDManifest(SPDDir)
if err != nil {
log.Fatal(err)
}
parts, err := readParts(MemPartsUsedFile)
if err != nil {
log.Fatal(err)
}
partIdList, err := genPartIdInfo(parts, partToSPDMap, SPDToIndexMap, MakefileDir)
if err != nil {
log.Fatal(err)
}
if err := genMakefile(partIdList, MakefileDir); err != nil {
log.Fatal(err)
}
}

View File

@ -1,996 +0,0 @@
/* 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"
PlatformTGLADL = 0
PlatformJSL = 1
PlatformCZN = 2
)
var platformMap = map[string]int{
"TGL": PlatformTGLADL,
"JSL": PlatformJSL,
"ADL": PlatformTGLADL,
"CZN": PlatformCZN,
}
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 == PlatformTGLADL {
/*
* 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 || currPlatform == PlatformCZN {
/*
* 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
}
/* Per JESD209-4C Dies = ZQ balls on the package */
/* Note that this can be different than the part's die count */
func encodeDiesPerPackage(memAttribs *memAttributes) byte {
var dies int = 0
if currPlatform == PlatformTGLADL {
/* Intel MRC expects logical dies to be encoded for TGL. */
dies = memAttribs.ChannelsPerDie * memAttribs.RanksPerChannel * memAttribs.BitWidthPerChannel / 16
} else if currPlatform == PlatformJSL || currPlatform == PlatformCZN {
/* Intel MRC expects physical dies to be encoded for JSL. */
/* AMD PSP expects physical dies (ZQ balls) */
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 == PlatformTGLADL {
return SPDValueBusWidthTGL
} else if currPlatform == PlatformJSL || currPlatform == PlatformCZN {
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 == PlatformTGLADL {
/*
* 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")
fmt.Printf(" supported platforms: ")
keys := reflect.ValueOf(platformMap).MapKeys()
fmt.Println(keys)
fmt.Printf("\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)
}
}

View File

@ -1,344 +0,0 @@
{
"parts": [
{
"name": "H9HCNNNBKMMLXR-NEE",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 1,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 4267
}
},
{
"name": "H9HCNNNFAMMLXR-NEE",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 4,
"diesPerPackage": 2,
"bitWidthPerChannel": 8,
"ranksPerChannel": 2,
"speedMbps": 4267
}
},
{
"name": "K4U6E3S4AA-MGCL",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 1,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 4267
}
},
{
"name": "K4UBE3D4AA-MGCL",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 2,
"bitWidthPerChannel": 16,
"ranksPerChannel": 2,
"speedMbps": 4267
}
},
{
"name": "MT53E1G32D2NP-046 WT:A",
"attribs": {
"densityPerChannelGb": 16,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 1,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 4267
}
},
{
"name": "MT53E1G32D2NP-046 WT:B",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 2,
"bitWidthPerChannel": 16,
"ranksPerChannel": 2,
"speedMbps": 4267
}
},
{
"name": "H9HKNNNCRMBVAR-NEH",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 2,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 4267
}
},
{
"name": "MT53E1G64D4SQ-046 WT:A",
"attribs": {
"densityPerChannelGb": 16,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 2,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 4267
}
},
{
"name": "MT53E512M32D2NP-046 WT:F",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 1,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 4267
}
},
{
"name": "NT6AP256T32AV-J2",
"attribs": {
"densityPerChannelGb": 4,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 1,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 3733,
"tckMaxPs": 1250,
"casLatencies": "14 20 24 28 32"
}
},
{
"name": "K4U6E3S4AA-MGCR",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 1,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 4267
}
},
{
"name": "MT53E512M32D2NP-046 WT:E",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 1,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 4267
}
},
{
"name": "H9HCNNNCPMMLXR-NEE",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 2,
"bitWidthPerChannel": 16,
"ranksPerChannel": 2,
"speedMbps": 4267
}
},
{
"name": "K4UBE3D4AA-MGCR",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 2,
"bitWidthPerChannel": 16,
"ranksPerChannel": 2,
"speedMbps": 4267
}
},
{
"name": "MT53E512M64D4NW-046 WT:E",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 2,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 4267
}
},
{
"name": "MT53E1G64D8NW-046 WT:E",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 4,
"bitWidthPerChannel": 16,
"ranksPerChannel": 2,
"speedMbps": 4267
}
},
{
"name": "H9HCNNNCRMBLPR-NEE",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 2,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 4267
}
},
{
"name": "H9HCNNNFBMBLPR-NEE",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 4,
"bitWidthPerChannel": 16,
"ranksPerChannel": 2,
"speedMbps": 4267
}
},
{
"name": "MT53D1G64D4NW-046 WT:A",
"attribs": {
"densityPerChannelGb": 16,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 2,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 4267
}
},
{
"name": "MT53D512M64D4NW-046 WT:F",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 2,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 4267
}
},
{
"name": "NT6AP256T32AV-J1",
"attribs": {
"densityPerChannelGb": 4,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 1,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 4267,
"tckMaxPs": 1250,
"casLatencies": "14 20 24 28 32 36"
}
},
{
"name": "MT53E1G32D4NQ-046 WT:E",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 2,
"bitWidthPerChannel": 16,
"ranksPerChannel": 2,
"speedMbps": 4267
}
},
{
"name": "MT53E2G32D4NQ-046 WT:A",
"attribs": {
"densityPerChannelGb": 16,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 2,
"bitWidthPerChannel": 16,
"ranksPerChannel": 2,
"speedMbps": 4267
}
},
{
"name": "MT53E512M32D1NP-046 WT:B",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 1,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 4267
}
},
{
"name": "H54G46CYRBX267",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 1,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 4267
}
},
{
"name": "H54G56CYRBX247",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 2,
"bitWidthPerChannel": 16,
"ranksPerChannel": 2,
"speedMbps": 4267
}
},
{
"name": "K4U6E3S4AB-MGCL",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 1,
"bitWidthPerChannel": 16,
"ranksPerChannel": 1,
"speedMbps": 4267
}
},
{
"name": "K4UBE3D4AB-MGCL",
"attribs": {
"densityPerChannelGb": 8,
"banks": 8,
"channelsPerDie": 2,
"diesPerPackage": 2,
"bitWidthPerChannel": 16,
"ranksPerChannel": 2,
"speedMbps": 4267
}
}
]
}