From 20321ff4d5c760306d6d208f63f5e0aa5f588fd2 Mon Sep 17 00:00:00 2001 From: Jeroen van Straten Date: Wed, 28 Aug 2019 13:45:49 +0200 Subject: [PATCH] Add tests for stream registers + bugfixes --- doc/md/internalcounter.md | 4 +- doc/md/volatileinternalcounter.md | 4 +- tests/integration/test_primitive_stream.py | 209 +++++++++++++++++++ vhdmmio/config/behavior/counter.py | 6 +- vhdmmio/config/behavior/flag.py | 2 + vhdmmio/vhdl/behavior/primitive.template.vhd | 8 +- 6 files changed, 223 insertions(+), 10 deletions(-) create mode 100644 tests/integration/test_primitive_stream.py diff --git a/doc/md/internalcounter.md b/doc/md/internalcounter.md index 71ae684..f5b3988 100644 --- a/doc/md/internalcounter.md +++ b/doc/md/internalcounter.md @@ -76,9 +76,9 @@ Controls the existence of the `ctrl_increment` control input signal. When this signal is asserted, the internal data register is incremented. -The value must be a boolean (default `yes`). +The value must be a boolean (default `no`). -This key is optional unless required by context. If not specified, the default value (`yes`) is used. +This key is optional unless required by context. If not specified, the default value (`no`) is used. ## `ctrl-decrement` diff --git a/doc/md/volatileinternalcounter.md b/doc/md/volatileinternalcounter.md index 8c7416d..afe12df 100644 --- a/doc/md/volatileinternalcounter.md +++ b/doc/md/volatileinternalcounter.md @@ -76,9 +76,9 @@ Controls the existence of the `ctrl_increment` control input signal. When this signal is asserted, the internal data register is incremented. -The value must be a boolean (default `yes`). +The value must be a boolean (default `no`). -This key is optional unless required by context. If not specified, the default value (`yes`) is used. +This key is optional unless required by context. If not specified, the default value (`no`) is used. ## `ctrl-decrement` diff --git a/tests/integration/test_primitive_stream.py b/tests/integration/test_primitive_stream.py new file mode 100644 index 0000000..b0a8f4d --- /dev/null +++ b/tests/integration/test_primitive_stream.py @@ -0,0 +1,209 @@ +"""Primitive stream field tests.""" + +from unittest import TestCase +from ..testbench import RegisterFileTestbench + +class TestPrimitiveStreamFields(TestCase): + """Primitive stream field tests""" + + def test_stream_to_mmio(self): + """test stream to MMIO field""" + rft = RegisterFileTestbench({ + 'metadata': {'name': 'test'}, + 'fields': [ + { + 'address': 0, + 'name': 'a', + 'behavior': 'stream-to-mmio', + 'full-internal': 'a_full', + 'empty-internal': 'a_empty', + 'underrun-internal': 'a_under', + }, + { + 'address': 4, + 'name': 'b', + 'behavior': 'stream-to-mmio', + 'bus-read': 'valid-only', + 'full-internal': 'b_full', + 'empty-internal': 'b_empty', + 'reset': 33, + }, + { + 'address': 8, + 'bitrange': 0, + 'name': 'a_full', + 'behavior': 'internal-status', + 'internal': 'a_full', + }, + { + 'address': 8, + 'bitrange': 1, + 'name': 'a_empty', + 'behavior': 'internal-status', + 'internal': 'a_empty', + }, + { + 'address': 8, + 'bitrange': 2, + 'name': 'a_under', + 'behavior': 'internal-flag', + 'internal': 'a_under', + }, + { + 'address': 8, + 'bitrange': 3, + 'name': 'b_full', + 'behavior': 'internal-status', + 'internal': 'b_full', + }, + { + 'address': 8, + 'bitrange': 4, + 'name': 'b_empty', + 'behavior': 'internal-status', + 'internal': 'b_empty', + }, + ]}) + with rft as objs: + rft.testbench.clock() + self.assertEqual(objs.bus.read(8), 0b01010) + self.assertEqual(objs.bus.read(0), 0) + self.assertEqual(objs.bus.read(8), 0b01110) + objs.bus.write(8, 0b00100) + objs.f_a_i.data.val = 33 + objs.f_a_i.valid.val = 1 + rft.testbench.clock() # needed because the test case runner is synchronous + objs.f_a_i.valid.val = 0 + self.assertEqual(int(objs.f_a_o.ready), 1) + rft.testbench.clock() + self.assertEqual(int(objs.f_a_o.ready), 0) + self.assertEqual(objs.bus.read(8), 0b01001) + self.assertEqual(objs.bus.read(0), 33) + self.assertEqual(int(objs.f_a_o.ready), 1) + self.assertEqual(objs.bus.read(8), 0b01010) + self.assertEqual(objs.bus.read(0), 0) + self.assertEqual(objs.bus.read(8), 0b01110) + objs.bus.write(8, 0b00100) + with self.assertRaisesRegex(ValueError, 'decode'): + objs.bus.write(0, 0) + + self.assertEqual(objs.bus.read(4), 33) + self.assertEqual(objs.bus.read(8), 0b10010) + with self.assertRaisesRegex(ValueError, 'slave'): + objs.bus.read(4) + self.assertEqual(objs.bus.read(8), 0b10010) + objs.f_b_i.data.val = 42 + objs.f_b_i.valid.val = 1 + rft.testbench.clock() # needed because the test case runner is synchronous + objs.f_b_i.valid.val = 0 + self.assertEqual(int(objs.f_b_o.ready), 1) + rft.testbench.clock() + self.assertEqual(int(objs.f_b_o.ready), 0) + self.assertEqual(objs.bus.read(8), 0b01010) + self.assertEqual(objs.bus.read(4), 42) + self.assertEqual(objs.bus.read(8), 0b10010) + with self.assertRaisesRegex(ValueError, 'slave'): + objs.bus.read(4) + self.assertEqual(objs.bus.read(8), 0b10010) + with self.assertRaisesRegex(ValueError, 'decode'): + objs.bus.write(4, 0) + + def test_mmio_to_stream(self): + """test MMIO to stream field""" + rft = RegisterFileTestbench({ + 'metadata': {'name': 'test'}, + 'fields': [ + { + 'address': 0, + 'name': 'a', + 'behavior': 'mmio-to-stream', + 'full-internal': 'a_full', + 'empty-internal': 'a_empty', + 'overrun-internal': 'a_over', + }, + { + 'address': 4, + 'name': 'b', + 'behavior': 'mmio-to-stream', + 'bus-write': 'invalid-only', + 'full-internal': 'b_full', + 'empty-internal': 'b_empty', + 'reset': 33, + }, + { + 'address': 8, + 'bitrange': 0, + 'name': 'a_full', + 'behavior': 'internal-status', + 'internal': 'a_full', + }, + { + 'address': 8, + 'bitrange': 1, + 'name': 'a_empty', + 'behavior': 'internal-status', + 'internal': 'a_empty', + }, + { + 'address': 8, + 'bitrange': 2, + 'name': 'a_over', + 'behavior': 'internal-flag', + 'internal': 'a_over', + }, + { + 'address': 8, + 'bitrange': 3, + 'name': 'b_full', + 'behavior': 'internal-status', + 'internal': 'b_full', + }, + { + 'address': 8, + 'bitrange': 4, + 'name': 'b_empty', + 'behavior': 'internal-status', + 'internal': 'b_empty', + }, + ]}) + with rft as objs: + rft.testbench.clock() + self.assertEqual(objs.bus.read(8), 0b01010) + self.assertEqual(int(objs.f_a_o.data), 0) + self.assertEqual(int(objs.f_a_o.valid), 0) + objs.bus.write(0, 33) + self.assertEqual(objs.bus.read(8), 0b01001) + self.assertEqual(int(objs.f_a_o.data), 33) + self.assertEqual(int(objs.f_a_o.valid), 1) + objs.bus.write(0, 42) # ignored! + self.assertEqual(objs.bus.read(8), 0b01101) + objs.bus.write(8, 0b00100) + self.assertEqual(int(objs.f_a_o.data), 33) + self.assertEqual(int(objs.f_a_o.valid), 1) + objs.f_a_i.ready.val = 1 + rft.testbench.clock() + objs.f_a_i.ready.val = 0 + rft.testbench.clock() # needed because the test case runner is synchronous + self.assertEqual(int(objs.f_a_o.valid), 0) + self.assertEqual(objs.bus.read(8), 0b01010) + with self.assertRaisesRegex(ValueError, 'decode'): + objs.bus.read(0) + + self.assertEqual(int(objs.f_b_o.data), 33) + self.assertEqual(int(objs.f_b_o.valid), 1) + objs.f_b_i.ready.val = 1 + rft.testbench.clock() + objs.f_b_i.ready.val = 0 + rft.testbench.clock() # needed because the test case runner is synchronous + self.assertEqual(int(objs.f_b_o.valid), 0) + self.assertEqual(objs.bus.read(8), 0b10010) + objs.bus.write(4, 42) + self.assertEqual(objs.bus.read(8), 0b01010) + self.assertEqual(int(objs.f_b_o.data), 42) + self.assertEqual(int(objs.f_b_o.valid), 1) + with self.assertRaisesRegex(ValueError, 'slave'): + objs.bus.write(4, 33) + self.assertEqual(int(objs.f_b_o.data), 42) + self.assertEqual(int(objs.f_b_o.valid), 1) + with self.assertRaisesRegex(ValueError, 'decode'): + objs.bus.read(4) diff --git a/vhdmmio/config/behavior/counter.py b/vhdmmio/config/behavior/counter.py index ebe8519..560bd86 100644 --- a/vhdmmio/config/behavior/counter.py +++ b/vhdmmio/config/behavior/counter.py @@ -74,7 +74,8 @@ class VolatileCounter(Counter): @behavior( 'internal-counter', 'internal event counter, reset explicitly by a write.', 2) @derive( - name='`internal-counter` behavior') + name='`internal-counter` behavior', + ctrl_increment=[False]) class InternalCounter(Counter): """This field behaves like `counter`, but instead of the counter being incremented by an external signal, it is incremented by an internal @@ -93,7 +94,8 @@ def internal(self, value): 'volatile-internal-counter', 'internal event counter, reset implicitly by the ' 'read.', 2) @derive( - name='`volatile-internal-counter` behavior') + name='`volatile-internal-counter` behavior', + ctrl_increment=[False]) class VolatileInternalCounter(VolatileCounter): """This field behaves like `volatile-counter`, but instead of the counter being incremented by an external signal, it is incremented by an internal diff --git a/vhdmmio/config/behavior/flag.py b/vhdmmio/config/behavior/flag.py index 195cb9b..98cef79 100644 --- a/vhdmmio/config/behavior/flag.py +++ b/vhdmmio/config/behavior/flag.py @@ -82,6 +82,7 @@ class VolatileFlag(Flag): 'internal-flag', 'like `flag`, but set by an internal signal.', 2) @derive( name='`internal-flag` behavior', + ctrl_bit_set=False, monitor_mode='bit-set') class InternalFlag(Flag): """`internal-flag` fields behave like `flag` fields, but instead of the @@ -103,6 +104,7 @@ def internal(self, value): '`internal-flag`.', 2) @derive( name='`volatile-internal-flag` behavior', + ctrl_bit_set=False, monitor_mode='bit-set') class VolatileInternalFlag(VolatileFlag): """`volatile-internal-flag` fields behave like `volatile-flag` fields, but diff --git a/vhdmmio/vhdl/behavior/primitive.template.vhd b/vhdmmio/vhdl/behavior/primitive.template.vhd index 833716d..6d80ded 100644 --- a/vhdmmio/vhdl/behavior/primitive.template.vhd +++ b/vhdmmio/vhdl/behavior/primitive.template.vhd @@ -280,13 +280,13 @@ |$endblock |$block HANDLE_WRITE - |$if b.underrun_internal is not None + |$if b.overrun_internal is not None |@ If the field is already valid, assert the overrun flag. |if $state[i].v$ = '1' then - |$if b.underrun_internal.is_vector() - | $b.underrun_internal.drive_name$($i$) := '1'; + |$if b.overrun_internal.is_vector() + | $b.overrun_internal.drive_name$($i$) := '1'; |$else - | $b.underrun_internal.drive_name$ := '1'; + | $b.overrun_internal.drive_name$ := '1'; |$endif |end if; |