diff --git a/doc/md/axi.md b/doc/md/axi.md index 2c49269..dc6384d 100644 --- a/doc/md/axi.md +++ b/doc/md/axi.md @@ -29,7 +29,7 @@ This structure supports the following configuration keys. ## `mode` -Configures the supported bus access modes. +This key configures the supported bus access modes. The following values are supported: @@ -43,7 +43,7 @@ This key is optional unless required by context. If not specified, the default v ## `interrupt-internal` -Configures driving an internal signal high when the +This key configures driving an internal signal high when the `vhdmmio`-specific interrupt signal associated with the outgoing AXI4L stream is asserted. This internal signal can then be tied to an internal interrupt to propagate the flag. @@ -54,4 +54,19 @@ The following values are supported: - a string matching `[a-zA-Z][a-zA-Z0-9_]*`: an internal signal with the given name is created (if necessary) and driven by the incoming interrupt signal. -This key is optional unless required by context. If not specified, the default value (`null`) is used. \ No newline at end of file +This key is optional unless required by context. If not specified, the default value (`null`) is used. + +## `bus-flatten` + +This key specifies whether records or flattened signals are desired +for the bus interface. Note that `flatten` (defined +[here](interfaceconfig.md#flatten)) should also be set to `yes` to make +this work. + +The following values are supported: + + - `no` (default): the bus is not flattened; the records from `vhdmmio_pkg.vhd` are used. + + - `yes`: the bus is flattened; the standard AXI4-lite signal names are used. + +This key is optional unless required by context. If not specified, the default value (`no`) is used. \ No newline at end of file diff --git a/doc/md/fieldconfig.md b/doc/md/fieldconfig.md index 2163130..649e97b 100644 --- a/doc/md/fieldconfig.md +++ b/doc/md/fieldconfig.md @@ -191,7 +191,7 @@ This key can take the following values: - Fields for interfacing with memories: - - [`memory`](memory.md): not yet implemented! + - [`memory`](memory.md): infers a local memory inside the register file. - Fields for controlling `vhdmmio`-managed interrupts: diff --git a/doc/md/memory.md b/doc/md/memory.md index 8c961b2..8b754c3 100644 --- a/doc/md/memory.md +++ b/doc/md/memory.md @@ -1,5 +1,99 @@ # `memory` behavior -Not yet implemented! +This field behavior infers a local memory inside the register file. +The memory can be accessed through the field and/or a single-cycle, +synchronous RAM port generated on the entity interface. -This structure does not support any configuration keys. \ No newline at end of file +Whether the inferred memory actually maps to memory resources on the +FPGA depends on how smart the synthesizer is. If it doesn't work, use the +`memory-interface` behavior instead, and infer the memory however the +synthesizer expects it to be inferred outside the generated register +file. + +This structure supports the following configuration keys. + +## `bus-mode` + +This key configures the supported bus access modes. + +The following values are supported: + + - `read-write` (default): both read and write accesses are supported. + + - `read-only`: only read accesses are supported. + + - `write-only`: only write accesses are supported. + +This key is optional unless required by context. If not specified, the default value (`read-write`) is used. + +## `hw-mode` + +This key configures the supported hardware access modes. + +The following values are supported: + + - `read-or-write` (default): a shared read-write interface is generated. + + - `read-and-write`: independent read and write interfaces are generated. + + - `read-only`: only a read interface is generated. + + - `write-only`: only a write interface is generated. + + - `disabled`: no hardware interface is generated. + +This key is optional unless required by context. If not specified, the default value (`read-or-write`) is used. + +## `portedness` + +This key specifies the memory port configuration. + +The following values are supported: + + - `auto` (default): `vhdmmio` will choose a fitting configuration based on `bus-mode` and `hw-mode`. + + - `1R`: infer a single-port ROM. + + - `1RW`: infer a RAM with one shared read-write port. + + - `1R1W`: infer a RAM with single independent read and write ports. + + - `2R`: infer a dual-port ROM. + + - `2RW`: infer a RAM with two shared read-write ports. + + - `2R1W`: infer a RAM with two independent read ports and one independent write port. + + - `2R2W`: infer a RAM with two independent read ports and two independent write ports. + +This key is optional unless required by context. If not specified, the default value (`auto`) is used. + +## `byte-enable` + +This key specifies whether this memory should support byte +enables. This is only supported when the bitrange of the field is +byte-aligned. + +The following values are supported: + + - `no` (default): no byte write enable signal is created. Any incomplete bus writes result in zeros being written. + + - `yes`: the inferred memory supports a byte write enable signal. + +This key is optional unless required by context. If not specified, the default value (`no`) is used. + +## `initial-data` + +This key specifies the initial data for the inferred memory. Whether +this actually works depends on whether the synthesizer/FPGA +architecture supports inferring initialized memories. + +The following values are supported: + + - `null` (default): the memory is not initialized. Simulation yields `'U'`s until the first write. + + - an integer: each memory location is initialized with the given value. + + - a string: the memory is initialized using the given data file. The filename is relative to the configuration file, or relative to the current working directory if the configuration is loaded using the Python API. If the filename ends in `.bin`, the file is treated as little-endian binary; in this case, the width of the memory must be an integer number of bytes. If a different file extension is used, the file is expected to consist of the correct number of spacing-separated integers, such that each integer corresponds to a memory location. These integers can be specified in hexadecimal, binary, or decimal format, selected using the usual `0x`/`0b`/lack of a prefix. + +This key is optional unless required by context. If not specified, the default value (`null`) is used. \ No newline at end of file diff --git a/tests/integration/test_axi_field.py b/tests/integration/test_axi_field.py index 43acd39..49e3220 100644 --- a/tests/integration/test_axi_field.py +++ b/tests/integration/test_axi_field.py @@ -100,7 +100,7 @@ def test_flattened(self): 'f_a_wstrb', 'f_a_wvalid', )) - with rft as objs: + with rft: # This should probably be tested beyond just compiling. On the # other hand, What Could Possibly Go Wrong? (TM) pass diff --git a/vhdmmio/config/behavior/memory.py b/vhdmmio/config/behavior/memory.py index e40a366..21ed9ed 100644 --- a/vhdmmio/config/behavior/memory.py +++ b/vhdmmio/config/behavior/memory.py @@ -1,13 +1,82 @@ """Submodule for `Memory` configurable.""" -from ...configurable import configurable, Configurable +from ...configurable import configurable, Configurable, choice from .registry import behavior, behavior_doc behavior_doc('Fields for interfacing with memories:') @behavior( - 'memory', 'not yet implemented!', 1) + 'memory', 'infers a local memory inside the register file.', 1) @configurable(name='`memory` behavior') class Memory(Configurable): - """Not yet implemented!""" # TODO + """This field behavior infers a local memory inside the register file. + The memory can be accessed through the field and/or a single-cycle, + synchronous RAM port generated on the entity interface. + + Whether the inferred memory actually maps to memory resources on the + FPGA depends on how smart the synthesizer is. If it doesn't work, use the + `memory-interface` behavior instead, and infer the memory however the + synthesizer expects it to be inferred outside the generated register + file.""" #pylint: disable=E0211,E0213 + + @choice + def bus_mode(): + """This key configures the supported bus access modes.""" + yield 'read-write', 'both read and write accesses are supported.' + yield 'read-only', 'only read accesses are supported.' + yield 'write-only', 'only write accesses are supported.' + + @choice + def hw_mode(): + """This key configures the supported hardware access modes.""" + yield 'read-or-write', 'a shared read-write interface is generated.' + yield 'read-and-write', 'independent read and write interfaces are generated.' + yield 'read-only', 'only a read interface is generated.' + yield 'write-only', 'only a write interface is generated.' + yield 'disabled', 'no hardware interface is generated.' + + @choice + def portedness(): + """This key specifies the memory port configuration.""" + yield ('auto', '`vhdmmio` will choose a fitting configuration ' + 'based on `bus-mode` and `hw-mode`.') + yield '1R', 'infer a single-port ROM.' + yield '1RW', 'infer a RAM with one shared read-write port.' + yield '1R1W', 'infer a RAM with single independent read and write ports.' + yield '2R', 'infer a dual-port ROM.' + yield '2RW', 'infer a RAM with two shared read-write ports.' + yield ('2R1W', 'infer a RAM with two independent read ports and ' + 'one independent write port.') + yield ('2R2W', 'infer a RAM with two independent read ports and ' + 'two independent write ports.') + + @choice + def byte_enable(): + """This key specifies whether this memory should support byte + enables. This is only supported when the bitrange of the field is + byte-aligned.""" + yield (False, 'no byte write enable signal is created. Any incomplete ' + 'bus writes result in zeros being written.') + yield (True, 'the inferred memory supports a byte write enable signal.') + + @choice + def initial_data(): + """This key specifies the initial data for the inferred memory. Whether + this actually works depends on whether the synthesizer/FPGA + architecture supports inferring initialized memories.""" + yield (None, 'the memory is not initialized. Simulation yields ' + '`\'U\'`s until the first write.') + yield int, 'each memory location is initialized with the given value.' + yield (str, 'the memory is initialized using the given data file. The ' + 'filename is relative to the configuration file, or relative to ' + 'the current working directory if the configuration is loaded ' + 'using the Python API. If the filename ends in `.bin`, the ' + 'file is treated as little-endian binary; in this case, the ' + 'width of the memory must be an integer number of bytes. If a ' + 'different file extension is used, the file is expected to ' + 'consist of the correct number of spacing-separated integers, ' + 'such that each integer corresponds to a memory location. ' + 'These integers can be specified in hexadecimal, binary, or ' + 'decimal format, selected using the usual `0x`/`0b`/lack of a ' + 'prefix.') diff --git a/vhdmmio/vhdl/behavior/axi.py b/vhdmmio/vhdl/behavior/axi.py index 7a2ad76..8cb086c 100644 --- a/vhdmmio/vhdl/behavior/axi.py +++ b/vhdmmio/vhdl/behavior/axi.py @@ -42,7 +42,7 @@ def generate(self): tple['rready'] = self.add_output('rready') tple['rdata'] = self.add_input('rdata', bus_width) tple['rresp'] = self.add_input('rresp', 2) - tple['uirq'] = self.add_input('uirq') + tple['uirq'] = self.add_input('uirq') else: tple['m2s'] = self.add_output('o', typ=Axi4Lite('m2s', bus_width)) tple['s2m'] = self.add_input('i', typ=Axi4Lite('s2m', bus_width))