test/test_ecc: review and cleanup.

This commit is contained in:
Florent Kermarrec 2020-03-26 12:00:01 +01:00
parent 265e79f2aa
commit 0ee9d7db5f
1 changed files with 45 additions and 27 deletions

View File

@ -14,33 +14,33 @@ from litex.soc.cores.ecc import *
from test.common import *
# Helpers ------------------------------------------------------------------------------------------
def bits(value, width=32):
# convert int to a string representing binary value
# reverse it so that we can index bits easily with s[0] being LSB
# Convert int to a string representing binary value and reverse it so that we can index bits
# easily with s[0] being LSB
return f"{value:0{width}b}"[::-1]
def frombits(bits):
# reverse of bits()
# Reverse of bits()
return int(bits[::-1], 2)
def bits_pp(value, width=32):
# pretty print binary value, groupped by bytes
# Pretty print binary value, groupped by bytes
if isinstance(value, str):
value = frombits(value)
s = f"{value:0{width}b}"
byte_chunks = [s[i:i+8] for i in range(0, len(s), 8)]
return "0b " + " ".join(byte_chunks)
def extract_ecc_data(data_width, codeword_width, codeword_bits):
extracted = ""
for i in range(8):
word = codeword_bits[codeword_width*i:codeword_width*(i+1)]
# remove parity bit
# Remove parity bit
word = word[1:]
data_pos = compute_data_positions(codeword_width - 1) # -1 for parity
# extract data bits
# Extract data bits
word_ex = list(bits(0, 32))
for j, d in enumerate(data_pos):
word_ex[j] = word[d-1]
@ -48,39 +48,42 @@ def extract_ecc_data(data_width, codeword_width, codeword_bits):
extracted += word_ex
return extracted
# TestECC ------------------------------------------------------------------------------------------
class TestECC(unittest.TestCase):
def test_eccw_connected(self):
"""Verify LiteDRAMNativePortECCW ECC encoding."""
class DUT(Module):
def __init__(self):
self.submodules.eccw = LiteDRAMNativePortECCW(32*8, 39*8)
eccw = LiteDRAMNativePortECCW(data_width_from=32*8, data_width_to=39*8)
self.submodules.eccw = eccw
def main_generator(dut):
sink_data = seed_to_data(0, nbits=32*8)
yield dut.eccw.sink.data.eq(sink_data)
yield
source_data = (yield dut.eccw.source.data)
sink_data_bits = bits(sink_data, 32*8)
sink_data_bits = bits(sink_data, 32*8)
source_data_bits = bits(source_data, 39*8)
self.assertNotEqual(sink_data_bits, source_data_bits[:len(sink_data_bits)])
source_extracted = extract_ecc_data(32, 39, source_data_bits)
# assert each word separately for more readable assert messages
# Assert each word separately for more readable assert messages
for i in range(8):
word = slice(32*i, 32*(i+1))
self.assertEqual(bits_pp(source_extracted[word]), bits_pp(sink_data_bits[word]),
msg=f"Mismatch at i = {i}")
msg=f"Mismatch at i = {i}")
dut = DUT()
run_simulation(dut, main_generator(dut))
def test_eccw_we_enabled(self):
# currently byte enable is not supported so it should be enabled all the time
"""Verify LiteDRAMNativePortECCW always set bytes enable."""
class DUT(Module):
def __init__(self):
self.submodules.eccw = LiteDRAMNativePortECCW(32*8, 39*8)
eccw = LiteDRAMNativePortECCW(data_width_from=32*8, data_width_to=39*8)
self.submodules.eccw = eccw
def main_generator(dut):
yield
@ -92,9 +95,11 @@ class TestECC(unittest.TestCase):
run_simulation(dut, main_generator(dut))
def test_eccr_connected(self):
"""Verify LiteDRAMNativePortECCR ECC decoding."""
class DUT(Module):
def __init__(self):
self.submodules.eccr = LiteDRAMNativePortECCR(32*8, 39*8)
eccr = LiteDRAMNativePortECCR(data_width_from=32*8, data_width_to=39*8)
self.submodules.eccr = eccr
def main_generator(dut):
sink_data = seed_to_data(0, nbits=(39*8 // 32 + 1) * 32)
@ -103,13 +108,13 @@ class TestECC(unittest.TestCase):
yield
source_data = (yield dut.eccr.source.data)
sink_data_bits = bits(sink_data, 39*8)
sink_data_bits = bits(sink_data, 39*8)
source_data_bits = bits(source_data, 32*8)
self.assertNotEqual(sink_data_bits[:len(source_data_bits)], source_data_bits)
sink_extracted = extract_ecc_data(32, 39, sink_data_bits)
self.assertEqual(bits_pp(sink_extracted), bits_pp(source_data_bits))
# assert each word separately for more readable assert messages
# Assert each word separately for more readable assert messages
for i in range(8):
word = slice(32*i, 32*(i+1))
self.assertEqual(bits_pp(sink_extracted[word]), bits_pp(source_data_bits[word]),
@ -119,19 +124,20 @@ class TestECC(unittest.TestCase):
run_simulation(dut, main_generator(dut))
def test_eccr_errors_connected_when_sink_valid(self):
"""Verify LiteDRAMNativePortECCR Error detection."""
class DUT(Module):
def __init__(self):
self.submodules.eccr = LiteDRAMNativePortECCR(32*8, 39*8)
eccr = LiteDRAMNativePortECCR(data_width_from=32*8, data_width_to=39*8)
self.submodules.eccr = eccr
def main_generator(dut):
yield dut.eccr.enable.eq(1)
yield dut.eccr.sink.data.eq(0b10) # wrong parity bit
yield dut.eccr.sink.data.eq(0b10) # Wrong parity bit
yield
# decder sec/ded not connected when valid=0
# Verify no errors are detected
self.assertEqual((yield dut.eccr.sec), 0)
self.assertEqual((yield dut.eccr.ded), 0)
# Set sink.valid and verify errors parity error is detected
yield dut.eccr.sink.valid.eq(1)
yield
self.assertEqual((yield dut.eccr.sec), 1)
@ -141,10 +147,11 @@ class TestECC(unittest.TestCase):
run_simulation(dut, main_generator(dut))
def ecc_encode_decode_test(self, from_width, to_width, n, pre=None, post=None, **kwargs):
"""ECC encoding/decoding generic test."""
class DUT(Module):
def __init__(self):
self.port_from = LiteDRAMNativePort("both", 24, from_width)
self.port_to = LiteDRAMNativePort("both", 24, to_width)
self.port_to = LiteDRAMNativePort("both", 24, to_width)
self.submodules.ecc = LiteDRAMNativePortECC(self.port_from, self.port_to, **kwargs)
self.mem = DRAMMemory(to_width, n)
@ -157,7 +164,7 @@ class TestECC(unittest.TestCase):
port = dut.port_from
# write
# Write
for i in range(n):
yield port.cmd.valid.eq(1)
yield port.cmd.we.eq(1)
@ -175,7 +182,7 @@ class TestECC(unittest.TestCase):
yield port.wdata.valid.eq(0)
yield
# read
# Read
for i in range(n):
yield port.cmd.valid.eq(1)
yield port.cmd.we.eq(0)
@ -206,16 +213,17 @@ class TestECC(unittest.TestCase):
return dut
def test_ecc_32_7(self):
# 32 data bits + 6 code bits + parity bit
"""Verify encoding/decoding on 32 data bits + 6 code bits + parity bit."""
dut = self.ecc_encode_decode_test(32*8, 39*8, 2)
self.assertEqual(dut.wdata, dut.rdata)
def test_ecc_64_8(self):
# 64 data bits + 7 code bits + parity bit
"""Verify encoding/decoding on 64 data bits + 7 code bits + parity bit."""
dut = self.ecc_encode_decode_test(64*8, 72*8, 2)
self.assertEqual(dut.wdata, dut.rdata)
def test_ecc_sec_errors(self):
# Verify SEC errors detection/correction with 1-bit flip."""
def pre(dut):
yield from dut.ecc.flip.write(0b00000100)
@ -229,6 +237,7 @@ class TestECC(unittest.TestCase):
self.assertEqual(dut.ded_errors, 0)
def test_ecc_ded_errors(self):
# Verify DED errors detection with 2-bit flip."""
def pre(dut):
yield from dut.ecc.flip.write(0b00001100)
@ -242,6 +251,7 @@ class TestECC(unittest.TestCase):
self.assertEqual(dut.ded_errors, 4)
def test_ecc_decoder_disable(self):
# Verify enable control."""
def pre(dut):
yield from dut.ecc.flip.write(0b10101100)
yield from dut.ecc.enable.write(0)
@ -256,16 +266,20 @@ class TestECC(unittest.TestCase):
self.assertEqual(dut.ded_errors, 0)
def test_ecc_clear_sec_errors(self):
# Verify SEC errors clear."""
def pre(dut):
yield from dut.ecc.flip.write(0b00000100)
def post(dut):
# Read errors after test (SEC errors expected)
dut.sec_errors = (yield from dut.ecc.sec_errors.read())
dut.ded_errors = (yield from dut.ecc.ded_errors.read())
# Clear errors counters
yield from dut.ecc.clear.write(1)
yield
# Re-Read errors to verify clear
dut.sec_errors_c = (yield from dut.ecc.sec_errors.read())
dut.ded_errors_c = (yield from dut.ecc.ded_errors.read())
@ -277,16 +291,20 @@ class TestECC(unittest.TestCase):
self.assertEqual(dut.ded_errors_c, 0)
def test_ecc_clear_ded_errors(self):
# Verify DED errors clear."""
def pre(dut):
yield from dut.ecc.flip.write(0b10101100)
def post(dut):
# Read errors after test (DED errors expected)
dut.sec_errors = (yield from dut.ecc.sec_errors.read())
dut.ded_errors = (yield from dut.ecc.ded_errors.read())
# Clear errors counters
yield from dut.ecc.clear.write(1)
yield
# Re-Read errors to verify clear
dut.sec_errors_c = (yield from dut.ecc.sec_errors.read())
dut.ded_errors_c = (yield from dut.ecc.ded_errors.read())