util: Add spd_tools to generate DDR4 SPDs for TGL boards
Serial Presence Detect (SPD) data for memory modules is used by Memory Reference Code (MRC) for training the memory. This SPD data is typically obtained from part vendors but has to be massaged to format it correctly as per JEDEC and MRC expectations. There have been numerous times in the past where the SPD data used is not always correct. In order to reduce the manual effort of creating SPDs and generating DRAM IDs, this change adds tools for generating SPD files for DDR4 memory used in memory down configurations on Intel Tiger Lake (TGL) based platforms. These tools generate SPDs following JESD79-4C and Jedec "4.1.2.L-5 R29 v103" specification. Two tools are provided: * 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 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. BUG=b:160157545 Change-Id: I263f936b332520753a6791c8d892fc148cb6f103 Signed-off-by: Nick Vaccaro <nvaccaro@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/44429 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Rob Barnes <robbarnes@google.com> Reviewed-by: Furquan Shaikh <furquan@google.com>
This commit is contained in:
parent
c9689e0591
commit
90aeb4d1b5
|
@ -0,0 +1,32 @@
|
||||||
|
23 11 0C 03 45 21 00 08 00 00 00 00 02 03 00 00
|
||||||
|
00 00 05 06 F8 FF 02 00 6E 6E 6E 11 00 6E F0 0A
|
||||||
|
20 08 00 05 00 F0 2B 34 28 00 78 00 14 3C 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 9C B5 00 00 00 00 BC 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 20 20 20 20 20 20 20
|
||||||
|
20 20 20 20 20 20 20 20 20 20 20 20 20 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
@ -0,0 +1,32 @@
|
||||||
|
23 11 0C 03 85 21 91 08 00 00 00 00 02 03 00 00
|
||||||
|
00 00 05 06 F8 FF 02 00 6E 6E 6E 11 00 6E F0 0A
|
||||||
|
20 08 00 05 00 F0 2B 34 28 00 78 00 14 3C 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 9C B5 00 00 00 00 BC 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 20 20 20 20 20 20 20
|
||||||
|
20 20 20 20 20 20 20 20 20 20 20 20 20 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
@ -0,0 +1,3 @@
|
||||||
|
H5AN8G6NDJR-XNC,ddr4-spd-1.hex
|
||||||
|
MT40A512M16TB-062E:J,ddr4-spd-1.hex
|
||||||
|
H5ANAG6NCMR-XNC,ddr4-spd-2.hex
|
|
@ -0,0 +1,275 @@
|
||||||
|
# DDR4 SPD tools README
|
||||||
|
|
||||||
|
Tools for generating SPD files for DDR4 memory used in memory down
|
||||||
|
configurations on Intel Tiger Lake (TGL) based
|
||||||
|
platforms. These tools generate SPDs following JESD79-4C
|
||||||
|
and Jedec 4.1.2.L-5 R29 v103 specifications.
|
||||||
|
|
||||||
|
There are two tools provided that assist TGL 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 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 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
|
||||||
|
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`.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
* `deviceBusWidth`: 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,
|
||||||
|
"deviceBusWidth": 16,
|
||||||
|
"ranksPerPackage": 1,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MEMORY_PART_B",
|
||||||
|
"attribs": {
|
||||||
|
"speedMTps": 3200,
|
||||||
|
"CL_nRCD_nRP": 22
|
||||||
|
"capacityPerDieGb": 8,
|
||||||
|
"diesPerPackage": 1,
|
||||||
|
"deviceBusWidth": 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
|
||||||
|
`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
|
||||||
|
`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 += 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-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>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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.
|
|
@ -0,0 +1,215 @@
|
||||||
|
/* 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 = "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("MEMORY_TYPE = ddr4\n\n")
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"parts": [
|
||||||
|
{
|
||||||
|
"name": "H5AN8G6NDJR-XNC",
|
||||||
|
"attribs": {
|
||||||
|
"speedMTps": 3200,
|
||||||
|
"CL_nRCD_nRP": 22,
|
||||||
|
"capacityPerDieGb": 8,
|
||||||
|
"diesPerPackage": 1,
|
||||||
|
"deviceBusWidth": 16,
|
||||||
|
"ranksPerPackage": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MT40A512M16TB-062E:J",
|
||||||
|
"attribs": {
|
||||||
|
"speedMTps": 3200,
|
||||||
|
"CL_nRCD_nRP": 22,
|
||||||
|
"capacityPerDieGb": 8,
|
||||||
|
"diesPerPackage": 1,
|
||||||
|
"deviceBusWidth": 16,
|
||||||
|
"ranksPerPackage": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "H5ANAG6NCMR-XNC",
|
||||||
|
"attribs": {
|
||||||
|
"speedMTps": 3200,
|
||||||
|
"CL_nRCD_nRP": 22,
|
||||||
|
"capacityPerDieGb": 8,
|
||||||
|
"diesPerPackage": 2,
|
||||||
|
"deviceBusWidth": 16,
|
||||||
|
"ranksPerPackage": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue