diff --git a/test/test_ecc.py b/test/test_ecc.py index 5b5bcef..20c5729 100644 --- a/test/test_ecc.py +++ b/test/test_ecc.py @@ -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())