litedram/test/common.py

389 lines
13 KiB
Python

# This file is Copyright (c) 2016-2019 Florent Kermarrec <florent@enjoy-digital.fr>
# This file is Copyright (c) 2016 Tim 'mithro' Ansell <mithro@mithis.com>
# This file is Copyright (c) 2020 Antmicro <www.antmicro.com>
# License: BSD
import os
import random
from operator import or_
from migen import *
def seed_to_data(seed, random=True, nbits=32):
if nbits == 32:
if random:
return (seed * 0x31415979 + 1) & 0xffffffff
else:
return seed
else:
assert nbits%32 == 0
data = 0
for i in range(nbits//32):
data = data << 32
data |= seed_to_data(seed*nbits//32 + i, random, 32)
return data
@passive
def timeout_generator(ticks):
# raise exception after given timeout effectively stopping simulation
# because of @passive, simulation can end even if this generator is still running
for _ in range(ticks):
yield
raise TimeoutError("Timeout after %d ticks" % ticks)
class DRAMMemory:
def __init__(self, width, depth, init=[]):
self.width = width
self.depth = depth
self.mem = []
for d in init:
self.mem.append(d)
for _ in range(depth-len(init)):
self.mem.append(0)
# "W" enables write msgs, "R" - read msgs and "1" both
self._debug = os.environ.get("DRAM_MEM_DEBUG", "0")
def show_content(self):
for addr in range(self.depth):
print("0x{:08x}: 0x{:0{dwidth}x}".format(addr, self.mem[addr], dwidth=self.width//4))
def _warn(self, address):
if address > self.depth * self.width:
print("! adr > 0x{:08x}".format(
self.depth * self.width))
def _write(self, address, data, we):
mask = reduce(or_, [0xff << (8 * bit) for bit in range(self.width//8)
if (we & (1 << bit)) != 0], 0)
data = data & mask
self.mem[address%self.depth] = data
if self._debug in ["1", "W"]:
print("W 0x{:08x}: 0x{:0{dwidth}x}".format(address, self.mem[address%self.depth],
dwidth=self.width//4))
self._warn(address)
def _read(self, address):
if self._debug in ["1", "R"]:
print("R 0x{:08x}: 0x{:0{dwidth}x}".format(address, self.mem[address%self.depth],
dwidth=self.width//4))
self._warn(address)
return self.mem[address%self.depth]
@passive
def read_handler(self, dram_port, rdata_valid_random=0):
address = 0
pending = 0
prng = random.Random(42)
yield dram_port.cmd.ready.eq(0)
while True:
yield dram_port.rdata.valid.eq(0)
if pending:
while prng.randrange(100) < rdata_valid_random:
yield
yield dram_port.rdata.valid.eq(1)
yield dram_port.rdata.data.eq(self._read(address))
yield
yield dram_port.rdata.valid.eq(0)
yield dram_port.rdata.data.eq(0)
pending = 0
elif (yield dram_port.cmd.valid):
pending = not (yield dram_port.cmd.we)
address = (yield dram_port.cmd.addr)
if pending:
yield dram_port.cmd.ready.eq(1)
yield
yield dram_port.cmd.ready.eq(0)
yield
@passive
def write_handler(self, dram_port, wdata_ready_random=0):
address = 0
pending = 0
prng = random.Random(42)
yield dram_port.cmd.ready.eq(0)
while True:
yield dram_port.wdata.ready.eq(0)
if pending:
while (yield dram_port.wdata.valid) == 0:
yield
while prng.randrange(100) < wdata_ready_random:
yield
yield dram_port.wdata.ready.eq(1)
yield
self._write(address, (yield dram_port.wdata.data), (yield dram_port.wdata.we))
yield dram_port.wdata.ready.eq(0)
yield
pending = 0
yield
elif (yield dram_port.cmd.valid):
pending = (yield dram_port.cmd.we)
address = (yield dram_port.cmd.addr)
if pending:
yield dram_port.cmd.ready.eq(1)
yield
yield dram_port.cmd.ready.eq(0)
yield
class MemoryTestDataMixin:
@property
def bist_test_data(self):
data = {
"8bit": dict(
base=2,
end=2 + 8, # (end - base) must be pow of 2
length=5,
# 2 3 4 5 6 7=2+5
expected=[0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00],
),
"32bit": dict(
base=0x04,
end=0x04 + 8,
length=5 * 4,
expected=[
0x00000000, # 0x00
0x00000000, # 0x04
0x00000001, # 0x08
0x00000002, # 0x0c
0x00000003, # 0x10
0x00000004, # 0x14
0x00000000, # 0x18
0x00000000, # 0x1c
],
),
"64bit": dict(
base=0x10,
end=0x10 + 8,
length=5 * 8,
expected=[
0x0000000000000000, # 0x00
0x0000000000000000, # 0x08
0x0000000000000000, # 0x10
0x0000000000000001, # 0x18
0x0000000000000002, # 0x20
0x0000000000000003, # 0x28
0x0000000000000004, # 0x30
0x0000000000000000, # 0x38
],
),
"32bit_masked": dict(
base=0x04,
end=0x04 + 0x04, # TODO: fix address masking to be consistent
length=6 * 4,
expected=[ # due to masking
0x00000000, # 0x00
0x00000004, # 0x04
0x00000005, # 0x08
0x00000002, # 0x0c
0x00000003, # 0x10
0x00000000, # 0x14
0x00000000, # 0x18
0x00000000, # 0x1c
],
),
}
data["32bit_long_sequential"] = dict(
base=16,
end=16 + 128,
length=64,
expected=[0x00000000] * 128
)
expected = data["32bit_long_sequential"]["expected"]
expected[16//4:(16 + 64)//4] = list(range(64//4))
return data
@property
def pattern_test_data(self):
data = {
"8bit": dict(
pattern=[
# address, data
(0x00, 0xaa),
(0x05, 0xbb),
(0x02, 0xcc),
(0x07, 0xdd),
],
expected=[
# data, address
0xaa, # 0x00
0x00, # 0x01
0xcc, # 0x02
0x00, # 0x03
0x00, # 0x04
0xbb, # 0x05
0x00, # 0x06
0xdd, # 0x07
],
),
"32bit": dict(
pattern=[
# address, data
(0x00, 0xabadcafe),
(0x07, 0xbaadf00d),
(0x02, 0xcafefeed),
(0x01, 0xdeadc0de),
],
expected=[
# data, address
0xabadcafe, # 0x00
0xdeadc0de, # 0x04
0xcafefeed, # 0x08
0x00000000, # 0x0c
0x00000000, # 0x10
0x00000000, # 0x14
0x00000000, # 0x18
0xbaadf00d, # 0x1c
],
),
"64bit": dict(
pattern=[
# address, data
(0x00, 0x0ddf00dbadc0ffee),
(0x05, 0xabadcafebaadf00d),
(0x02, 0xcafefeedfeedface),
(0x07, 0xdeadc0debaadbeef),
],
expected=[
# data, address
0x0ddf00dbadc0ffee, # 0x00
0x0000000000000000, # 0x08
0xcafefeedfeedface, # 0x10
0x0000000000000000, # 0x18
0x0000000000000000, # 0x20
0xabadcafebaadf00d, # 0x28
0x0000000000000000, # 0x30
0xdeadc0debaadbeef, # 0x38
],
),
"64bit_to_32bit": dict(
pattern=[
# address, data
(0x00, 0x0d15ea5e00facade),
(0x05, 0xabadcafe8badf00d),
(0x01, 0xcafefeedbaadf00d),
(0x02, 0xfee1deaddeadc0de),
],
expected=[
# data, word, address
0x00facade, # 0 0x00
0x0d15ea5e, # 1 0x04
0xbaadf00d, # 2 0x08
0xcafefeed, # 3 0x0c
0xdeadc0de, # 4 0x10
0xfee1dead, # 5 0x14
0x00000000, # 6 0x18
0x00000000, # 7 0x1c
0x00000000, # 8 0x20
0x00000000, # 9 0x24
0x8badf00d, # 10 0x28
0xabadcafe, # 11 0x2c
0x00000000, # 12 0x30
]
),
"32bit_to_8bit": dict(
pattern=[
# address, data
(0x00, 0x00112233),
(0x05, 0x44556677),
(0x01, 0x8899aabb),
(0x02, 0xccddeeff),
],
expected=[
# data, address
0x33, # 0x00
0x22, # 0x01
0x11, # 0x02
0x00, # 0x03
0xbb, # 0x04
0xaa, # 0x05
0x99, # 0x06
0x88, # 0x07
0xff, # 0x08
0xee, # 0x09
0xdd, # 0x0a
0xcc, # 0x0b
0x00, # 0x0c
0x00, # 0x0d
0x00, # 0x0e
0x00, # 0x0f
0x00, # 0x10
0x00, # 0x11
0x00, # 0x12
0x00, # 0x13
0x77, # 0x14
0x66, # 0x15
0x55, # 0x16
0x44, # 0x17
0x00, # 0x18
0x00, # 0x19
]
),
"32bit_not_aligned": dict(
pattern=[
# address, data
(0x00, 0xabadcafe),
(0x07, 0xbaadf00d),
(0x02, 0xcafefeed),
(0x01, 0xdeadc0de),
],
expected=[
# data, address
0xabadcafe, # 0x00
0xdeadc0de, # 0x04
0xcafefeed, # 0x08
0x00000000, # 0x0c
0x00000000, # 0x10
0x00000000, # 0x14
0x00000000, # 0x18
0xbaadf00d, # 0x1c
],
),
"32bit_duplicates": dict(
pattern=[
# address, data
(0x00, 0xabadcafe),
(0x07, 0xbaadf00d),
(0x00, 0xcafefeed),
(0x07, 0xdeadc0de),
],
expected=[
# data, address
0xcafefeed, # 0x00
0x00000000, # 0x04
0x00000000, # 0x08
0x00000000, # 0x0c
0x00000000, # 0x10
0x00000000, # 0x14
0x00000000, # 0x18
0xdeadc0de, # 0x1c
],
),
"32bit_sequential": dict(
pattern=[
# address, data
(0x02, 0xabadcafe),
(0x03, 0xbaadf00d),
(0x04, 0xcafefeed),
(0x05, 0xdeadc0de),
],
expected=[
# data, address
0x00000000, # 0x00
0x00000000, # 0x04
0xabadcafe, # 0x08
0xbaadf00d, # 0x0c
0xcafefeed, # 0x10
0xdeadc0de, # 0x14
0x00000000, # 0x18
0x00000000, # 0x1c
],
),
"32bit_long_sequential": dict(pattern=[], expected=[0] * 64),
}
for i in range(32):
data["32bit_long_sequential"]["pattern"].append((i, 64 + i))
data["32bit_long_sequential"]["expected"][i] = 64 + i
return data