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:
parent
8f690dd762
commit
e4cf38ed36
@ -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.
|
@ -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
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -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`
|
@ -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.
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user