HBM Tester
The HBM_TESTER component validates HBM (High Bandwidth Memory) throughput, latency and
data integrity. It stress-tests memory controllers and physical layers using both linear
and pseudorandom access patterns.
One tester is instantiated per physical HBM module, each in the clock and reset domain of
that module. The MI bus is bridged into the HBM clock domain by the internal MI_ASYNC
component, so the tester can be controlled from the application clock domain.
Hardware Configuration
Clock and Reset
One HBM module has a single frequency and reset domain.
If the design has several HBM modules, instantiate a dedicated tester for each.
PORT_ADDR_HBIT – capacity (addressing range) of one port.
28for 256 MB,29for 512 MB,30for 1 GB.
BASE_ADDR_OFFSET – size of one port in bytes, that is the step between the base addresses of two neighbouring ports. It must be a multiple of
2**PORT_ADDR_HBIT. The base address of a port isPORT_ID * BASE_ADDR_OFFSET.Use
0when every port has its own address space, for example a NoC attached HBM.For a 256 MB port packed into one map, use
0x10000000.
CNT_WIDTH – width of the test duration counter. The maximum duration is
2**CNT_WIDTHclock cycles.
Principle of Operation
The HBM_TESTER_GEN module acts as a traffic generator and data integrity checker.
Address Generation
Sequential mode: addresses increment linearly. The stride follows the burst length (BL4 or BL8) so that the generated traffic stays contiguous.
Pseudorandom mode: a Linear Feedback Shift Register (LFSR) generates the addresses. Useful for worst-case latency, because it stops the HBM controller from consistently hitting open rows.
Data Generation and Integrity
Write path: controlled by
CS_GEN_WR_DEAD. With0the word holds an incrementing counter in the first 8 bits and zeros in the rest. With1the whole word is the constant0xDEADCAFEand no counter is written.Read path and verification: the tester maintains an internal expected counter and compares it with the incoming
RD_DATA(7 downto 0).Statistics:
STAT_DATA_OK_INCpulses when the read data matches the expected counter,STAT_DATA_ERR_INCpulses on a mismatch.
Note
Valid checking requires a strict “write then read” sequence.
Read after Write
The AXI write and read channels are not ordered against each other. A read issued right
after a write to the same address may therefore be served first and return the old data,
which is what the coherency test looks for. The effect grows with the latency of the
memory path; on a NoC attached HBM the read wins often.
To keep this from firing on its own traffic, the generator only enables the read address
once the write response of the previous burst has arrived (s_wr_rsp_pending in
HBM_TESTER_GEN). This applies to the R/W switching mode only and costs the full write
response latency between each write and its read, so that mode is not a bandwidth
measurement. Config bit [7] (-w on the command line) turns the wait off, which
lets the test measure how often a read overtakes its write on a given memory path.
Traffic Control
Write-only / Read-only: continuous streams of a single transaction type.
R/W switching: with
CS_GEN_RW_SWITCHenabled the module toggles between writing a burst and reading an address, simulating bidirectional traffic.Burst support: BL8 (64B access via two 256b words) and BL4 (32B access).
Warning
The burst is counted in bus words, so these sizes only hold on a 256b bus. On a 512b bus the same encoding issues 128B and 64B, and a real 32B access cannot be generated at all.
Component port and generics description
- ENTITY HBM_TESTER IS
- Ports
Port
Type
Mode
Description
=====
CLOCK AND RESET
=====
=====
HBM_CLK
std_logic
in
HBM_RESET
std_logic
in
HBM_INIT_DONE
std_logic
in
=====
COMMON MI32 INTERFACE (MI_CLK)
=====
=====
MI_CLK
std_logic
in
MI_RESET
std_logic
in
MI_DWR
std_logic_vector(31 downto 0)
in
MI_ADDR
std_logic_vector(31 downto 0)
in
MI_BE
std_logic_vector(3 downto 0)
in
MI_RD
std_logic
in
MI_WR
std_logic
in
MI_ARDY
std_logic
out
MI_DRD
std_logic_vector(31 downto 0)
out
MI_DRDY
std_logic
out
=====
USER HBM SIMPLE INTERFACE (HSI) PER HBM PORT (HBM_CLK)
=====
=====
WR_ADDR
slv_array_t(PORTS-1 downto 0)(AXI_ADDR_WIDTH-1 downto 0)
in
WRITE ADDR/DATA SIGNALS
WR_DATA
slv_array_t(PORTS-1 downto 0)(USR_DATA_WIDTH-1 downto 0)
in
WR_DATA_LAST
std_logic_vector(PORTS-1 downto 0)
in
WR_VALID
std_logic_vector(PORTS-1 downto 0)
in
WR_READY
std_logic_vector(PORTS-1 downto 0)
out
WR_RSP_ACK
std_logic_vector(PORTS-1 downto 0)
out
WRITE RESPONSE SIGNALS
WR_RSP_VALID
std_logic_vector(PORTS-1 downto 0)
out
WR_RSP_READY
std_logic_vector(PORTS-1 downto 0)
in
RD_ADDR
slv_array_t(PORTS-1 downto 0)(AXI_ADDR_WIDTH-1 downto 0)
in
READ ADDRESS SIGNALS
RD_ADDR_VALID
std_logic_vector(PORTS-1 downto 0)
in
RD_ADDR_READY
std_logic_vector(PORTS-1 downto 0)
out
RD_DATA
slv_array_t(PORTS-1 downto 0)(USR_DATA_WIDTH-1 downto 0)
out
READ DATA SIGNALS
RD_DATA_LAST
std_logic_vector(PORTS-1 downto 0)
out
RD_DATA_VALID
std_logic_vector(PORTS-1 downto 0)
out
RD_DATA_READY
std_logic_vector(PORTS-1 downto 0)
in
=====
AXI INTERFACE PER HBM PORT (HBM_CLK)
=====
=====
AXI_AWID
slv_array_t(PORTS-1 downto 0)(AXI_ID_WIDTH-1 downto 0)
out
WRITE ADDRESS SIGNALS
AXI_AWADDR
slv_array_t(PORTS-1 downto 0)(AXI_ADDR_WIDTH-1 downto 0)
out
AXI_AWLEN
slv_array_t(PORTS-1 downto 0)(AXI_LEN_WIDTH-1 downto 0)
out
AXI_AWSIZE
slv_array_t(PORTS-1 downto 0)(AXI_SIZE_WIDTH-1 downto 0)
out
AXI_AWBURST
slv_array_t(PORTS-1 downto 0)(AXI_BURST_WIDTH-1 downto 0)
out
AXI_AWPROT
slv_array_t(PORTS-1 downto 0)(3-1 downto 0)
out
AXI_AWQOS
slv_array_t(PORTS-1 downto 0)(4-1 downto 0)
out
AXI_AWUSER
slv_array_t(PORTS-1 downto 0)(1-1 downto 0)
out
AXI_AWVALID
std_logic_vector(PORTS-1 downto 0)
out
AXI_AWREADY
std_logic_vector(PORTS-1 downto 0)
in
AXI_WDATA
slv_array_t(PORTS-1 downto 0)(AXI_DATA_WIDTH-1 downto 0)
out
WRITE DATA SIGNALS
AXI_WSTRB
slv_array_t(PORTS-1 downto 0)(AXI_DATA_WIDTH/8-1 downto 0)
out
AXI_WUSER_DATA
slv_array_t(PORTS-1 downto 0)(AXI_DATA_WIDTH/8-1 downto 0)
out
AXI_WUSER_STRB
slv_array_t(PORTS-1 downto 0)(AXI_DATA_WIDTH/64-1 downto 0)
out
AXI_WLAST
std_logic_vector(PORTS-1 downto 0)
out
AXI_WVALID
std_logic_vector(PORTS-1 downto 0)
out
AXI_WREADY
std_logic_vector(PORTS-1 downto 0)
in
AXI_BID
slv_array_t(PORTS-1 downto 0)(AXI_ID_WIDTH-1 downto 0)
in
WRITE RESPONSE SIGNALS
AXI_BRESP
slv_array_t(PORTS-1 downto 0)(AXI_RESP_WIDTH-1 downto 0)
in
AXI_BVALID
std_logic_vector(PORTS-1 downto 0)
in
AXI_BREADY
std_logic_vector(PORTS-1 downto 0)
out
AXI_ARID
slv_array_t(PORTS-1 downto 0)(AXI_ID_WIDTH-1 downto 0)
out
READ ADDRESS SIGNALS
AXI_ARADDR
slv_array_t(PORTS-1 downto 0)(AXI_ADDR_WIDTH-1 downto 0)
out
AXI_ARLEN
slv_array_t(PORTS-1 downto 0)(AXI_LEN_WIDTH-1 downto 0)
out
AXI_ARSIZE
slv_array_t(PORTS-1 downto 0)(AXI_SIZE_WIDTH-1 downto 0)
out
AXI_ARBURST
slv_array_t(PORTS-1 downto 0)(AXI_BURST_WIDTH-1 downto 0)
out
AXI_ARPROT
slv_array_t(PORTS-1 downto 0)(3-1 downto 0)
out
AXI_ARQOS
slv_array_t(PORTS-1 downto 0)(4-1 downto 0)
out
AXI_ARUSER
slv_array_t(PORTS-1 downto 0)(1-1 downto 0)
out
AXI_ARVALID
std_logic_vector(PORTS-1 downto 0)
out
AXI_ARREADY
std_logic_vector(PORTS-1 downto 0)
in
AXI_RID
slv_array_t(PORTS-1 downto 0)(AXI_ID_WIDTH-1 downto 0)
in
READ DATA SIGNALS
AXI_RDATA
slv_array_t(PORTS-1 downto 0)(AXI_DATA_WIDTH-1 downto 0)
in
AXI_RUSER_DATA
slv_array_t(PORTS-1 downto 0)(AXI_DATA_WIDTH/8-1 downto 0)
in
AXI_RUSER_ERR_DBE
std_logic_vector(PORTS-1 downto 0)
in
AXI_RRESP
slv_array_t(PORTS-1 downto 0)(AXI_RESP_WIDTH-1 downto 0)
in
AXI_RLAST
std_logic_vector(PORTS-1 downto 0)
in
AXI_RVALID
std_logic_vector(PORTS-1 downto 0)
in
AXI_RREADY
std_logic_vector(PORTS-1 downto 0)
out
Control Software
The hbm_tester.py script in the sw directory controls the hardware.
Warning
The script is not self-configuring and has to be told the geometry of your build.
It does not read the geometry of the tester from the firmware or from the Device Tree,
it assumes 32 ports per tester instance, a 256b data bus and a 450 MHz HBM_CLK.
When these defaults do not match the design, pass the correct values with -P,
-W and -F, otherwise the script reports wrong numbers without any warning, or
accesses the registers of ports that do not exist.
One hbm_tester object is created per Device Tree node, that is per HBM module, and the
values given on the command line are used for all of them. A design whose modules do not
have the same geometry is not supported by the script.
Tests
Test |
What it does |
|---|---|
|
Writes and reads continuously and reports the throughput of each port. |
|
Same, but the generator only reads. The write counter stays at zero, so the read figure is what a port reaches with the whole bus to itself. |
|
Same, but the generator only writes. |
|
Same traffic, but reports the average read and write latency. Use |
|
Writes the counter to all addresses, then reads them back and checks. Answers “does what I wrote come back?”. |
|
Fills the memory with |
Command Line Arguments
Argument |
Description |
|---|---|
|
Index of the HBM tester (e.g. |
|
Device index (default: |
|
Type of test: |
|
Enable random addressing (for latency or speed tests). |
|
Do not wait for the write response before reading the same address (coherency test). |
|
Use the BL8 burst mode, that is a 64B access (default: BL4, a 32B access). |
|
Give each transaction its own AXI ID, the default is one ID for all of them. |
|
Number of active ports/channels (default: all). |
|
Number of ports of one tester instance, that is |
|
The |
|
Frequency of |
Register Map
Offset |
Name |
Description |
|---|---|---|
|
Version |
|
|
Port Enable |
One-hot bitmask to enable/run specific ports. |
|
Config |
Configuration register (see the bitfield below). |
|
Reset |
Writing to this register resets the tester logic. |
|
Duration |
Test duration (limit defined by |
|
Status |
Monitoring done / stats ready of each port (one-hot address). |
|
Stats 0 |
Base address for counter 0. |
|
Stats 1 |
Base address for counter 1. |
Configuration Register (0x14) Bitfield
Bits |
Function |
Description |
|---|---|---|
|
Connection |
0 = user source, 1 = generator source. |
|
Addr Mode |
0 = sequential, 1 = pseudorandom. |
|
Dead Data |
0 = counter value, 1 = |
|
R/W Switch |
Enable automatic R/W toggling. |
|
Run Mode |
00 = none, 01 = WR only, 10 = RD only, 11 = RD and WR. |
|
Burst Mode |
Size of one access: 0 = BL4 (32B), 1 = BL8 (64B). The number of bus words the
access takes is derived from |
|
R/W No Wait |
0 = wait for the write response before reading the same address, 1 = read immediately. See Read after Write. |
|
AXI ID Mode |
0 = one ID for all transactions, 1 = a new ID for each one. With one ID, AXI4 guarantees the order of the responses within a channel. It does not guarantee the order between the write and the read channel, see Read after Write. A new ID for each transaction needs a reorder buffer in the memory or its IP, otherwise the responses come back in a different order than the tester expects and the data integrity test reports false errors. The NoC attached HBM on ThunderFjord (fb2cdg1) has no reorder buffer, so leave the bit at 0 there. |
|
Counter 0 Mode |
Selects the metric for result counter 0 (see below). |
|
Counter 1 Mode |
Selects the metric for result counter 1 (see below). |
Counter modes
000– read words count001– write words count010– read latency011– write latency100– data OK count101– data error count