Skip to content

Commit f68aad6

Browse files
authored
Merge pull request #85 from krcb197/75-context-manager-for-doing-multiple-operations-to-the-same-register
Context Manager and Readback Verify
2 parents 7f3a733 + ea5f88e commit f68aad6

11 files changed

Lines changed: 448 additions & 30 deletions

File tree

docs/generated_package.rst

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,31 @@ simulator.
241241
.. literalinclude :: ../example/simulating_callbacks/flashing_the_LED.py
242242
:language: python
243243
244+
Optimised Access
245+
----------------
246+
247+
Each time the ``read`` or ``write`` method for a register field is accessed the hardware is read
248+
and or written (a write to a field will normally require a preceding read). When accessing multiple
249+
fields in the same register, it may be desirable to use one of the optimised access methods.
250+
251+
Consider the following example of an GPIO block with 4 GPIO pins (configured in a single register):
252+
253+
.. literalinclude :: ../example/optimised_access/optimised_access.rdl
254+
:language: systemrdl
255+
256+
In the to configure gpio_0 and gpio_1 whilst leaving the other two unaffected it can be done in two
257+
methods:
258+
259+
* using the ``write_fields`` method of the register
260+
* using the register context manager
261+
262+
Both demonstrated in the following code example:
263+
264+
.. literalinclude :: ../example/optimised_access/optimised_access.py
265+
:language: python
266+
244267
Walking the Structure
245-
=====================
268+
---------------------
246269

247270
The following two example show how to use the generators within the register abstraction layer
248271
package to traverse the structure.
@@ -261,7 +284,7 @@ a file called ``chip_with_registers.rdl``:
261284
peakpython chip_with_registers.rdl --outdir chip_with_registers --test
262285
263286
Traversing without Unrolling Loops
264-
----------------------------------
287+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
265288

266289
The first example is reading all the readable registers from the register map and writing them
267290
into a JSON file. To exploit the capabilities of a JSON file the arrays of registers and
@@ -353,7 +376,7 @@ This will create a JSON file as follows:
353376
}
354377
355378
Traversing without Unrolling Loops
356-
----------------------------------
379+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
357380

358381
The second example is setting every register in the address map back to its default values. In
359382
this case the loops are unrolled to conveniently access all the register without needing to
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""
2+
PeakRDL Python example to show the different methods to access the fields of a register
3+
"""
4+
from optimised_access.reg_model.optimised_access import optimised_access_cls, \
5+
optimised_access_gpio_direction_enc_enumcls
6+
7+
from optimised_access.lib import CallbackSet
8+
9+
# dummy functions to demonstrate the class
10+
def read_addr_space(addr: int, width: int, accesswidth: int) -> int:
11+
"""
12+
Callback to simulate the operation of the package, everytime the read is called, it return
13+
an integer value of 0
14+
15+
Args:
16+
addr: Address to write to
17+
width: Width of the register in bits
18+
accesswidth: Minimum access width of the register in bits
19+
20+
Returns:
21+
value inputted by the used
22+
"""
23+
return int(0)
24+
25+
26+
def write_addr_space(addr: int, width: int, accesswidth: int, data: int) -> None:
27+
"""
28+
Callback to simulate the operation of the package, everytime the read is called, it will
29+
request the user input the value to be read back.
30+
31+
Args:
32+
addr: Address to write to
33+
width: Width of the register in bits
34+
accesswidth: Minimum access width of the register in bits
35+
data: value to be written to the register
36+
37+
Returns:
38+
None
39+
"""
40+
print(f'0x{data:X} written to 0x{addr:X}')
41+
42+
if __name__ == '__main__':
43+
44+
# create an instance of the address map with the simulated callback necessary to demonstrate
45+
# the example
46+
dut = optimised_access_cls(callbacks=CallbackSet(read_callback=read_addr_space,
47+
write_callback=write_addr_space))
48+
49+
# configure the GPIO 0 and GPIO 1 without affecting the state of the GPIO 2 and GPIO 3
50+
# configuration.
51+
52+
# This can be done either using the ``write_fields`` method
53+
dut.gpio_register.write_fields(gpio_0_dir=optimised_access_gpio_direction_enc_enumcls.GPIO_OUT,
54+
gpio_0_pullup=False,
55+
gpio_0_strength=2,
56+
gpio_1_dir=optimised_access_gpio_direction_enc_enumcls.GPIO_IN)
57+
58+
# It can also be done with the context manager
59+
with dut.gpio_register.single_read_modify_write() as reg:
60+
reg.gpio_0_dir.write(optimised_access_gpio_direction_enc_enumcls.GPIO_OUT)
61+
reg.gpio_0_pullup.write(False)
62+
reg.gpio_0_strength.write(2)
63+
reg.gpio_0_dir.write(optimised_access_gpio_direction_enc_enumcls.GPIO_IN)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
addrmap optimised_access {
2+
3+
enum gpio_direction_enc {
4+
gpio_in = 0;
5+
gpio_out = 1;
6+
};
7+
8+
field gpio_direction { encode = gpio_direction_enc; fieldwidth=1; };
9+
field gpio_drive_str { fieldwidth=2; };
10+
field gpio_pullup { fieldwidth=1; };
11+
12+
13+
reg {
14+
default sw = rw;
15+
default hw = r;
16+
17+
gpio_direction gpio_0_dir;
18+
gpio_drive_str gpio_0_strength;
19+
gpio_pullup gpio_0_pullup;
20+
21+
gpio_direction gpio_1_dir;
22+
gpio_drive_str gpio_1_strength;
23+
gpio_pullup gpio_1_pullup;
24+
25+
gpio_direction gpio_2_dir;
26+
gpio_drive_str gpio_2_strength;
27+
gpio_pullup gpio_2_pullup;
28+
29+
gpio_direction gpio_3_dir;
30+
gpio_drive_str gpio_3_strength;
31+
gpio_pullup gpio_3_pullup;
32+
33+
} gpio_register;
34+
};

src/peakrdl_python/__about__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"""
22
Variables that describes the PeakRDL Python Package
33
"""
4-
__version__ = "0.3.12"
4+
__version__ = "0.3.13"

src/peakrdl_python/templates/addrmap.py.jinja

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ from typing import Optional
77
from typing import Union
88
from typing import cast
99
from typing import Dict
10+
from typing import Generator
11+
from contextlib import contextmanager
1012

1113
from ..lib import AddressMap, RegFile, Memory
1214
from ..lib import AddressMapArray, RegFileArray

src/peakrdl_python/templates/addrmap_field.py.jinja

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class {{get_fully_qualified_type_name(node)}}_cls({%- if 'encode' in node.list_p
8585

8686
This returns None:
8787
- if the field is not reset.
88-
- if the register resets to a signal value tht can not be determined
88+
- if the register resets to a signal value that can not be determined
8989
"""
9090
int_default = super().default
9191

src/peakrdl_python/templates/addrmap_register.py.jinja

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,17 @@ class {{get_fully_qualified_type_name(node)}}_cls(RegWriteOnly):
5454
"""
5555
read the register and return a dictionary of the field values
5656
"""
57-
reg_value = self.read()
58-
59-
return_dict = {
60-
{%- for child_node in get_reg_readable_fields(node) %}
61-
'{{child_node.inst_name}}' : self.{{safe_node_name(child_node)}}.decode_read_value(reg_value)
62-
{%- if not loop.last -%} , {%- endif -%}
63-
{% endfor %}
64-
}
57+
{% if node.has_sw_writable %}
58+
with self.single_read_modify_write(skip_write=True) as reg:
59+
{% else %}
60+
with self.single_read() as reg:
61+
{% endif %}
62+
return_dict = {
63+
{%- for child_node in get_reg_readable_fields(node) %}
64+
'{{child_node.inst_name}}' : reg.{{safe_node_name(child_node)}}.read()
65+
{%- if not loop.last -%} , {%- endif -%}
66+
{% endfor %}
67+
}
6568

6669
return return_dict
6770

@@ -74,6 +77,18 @@ class {{get_fully_qualified_type_name(node)}}_cls(RegWriteOnly):
7477
yield self.{{safe_node_name(child_node)}}
7578
{% endfor %}
7679

80+
{# context manager #}
81+
{% if node.has_sw_writable %}
82+
@contextmanager
83+
def single_read_modify_write(self, verify:bool = False, skip_write: bool = False) -> Generator['{{get_fully_qualified_type_name(node)}}_cls', None, None]:
84+
"""
85+
Context Manager to do multiple accesses using a single read/modify write operation
86+
"""
87+
with super().single_read_modify_write(verify=verify, skip_write=skip_write) as reg:
88+
yield reg
89+
{% endif %}
90+
91+
7792
{% endif %}
7893

7994
{% if node.has_sw_writable %}
@@ -92,25 +107,20 @@ class {{get_fully_qualified_type_name(node)}}_cls(RegWriteOnly):
92107
Do a read-modify-write to the register, updating any field included in
93108
the arguments
94109
"""
95-
96110
if len(kwargs) == 0:
97111
raise ValueError('no command args')
98112

99-
bit_mask = 0
100-
reg_value = 0
113+
with self.single_read_modify_write() as reg:
101114
{%- for child_node in get_reg_writable_fields(node) %}
102-
if '{{safe_node_name(child_node)}}' in kwargs:
103-
reg_value |= self.{{safe_node_name(child_node)}}.encode_write_value(kwargs['{{safe_node_name(child_node)}}'])
104-
bit_mask |= self.{{safe_node_name(child_node)}}.bitmask
105-
kwargs.pop('{{safe_node_name(child_node)}}')
115+
if '{{safe_node_name(child_node)}}' in kwargs:
116+
self.{{safe_node_name(child_node)}}.write(kwargs['{{safe_node_name(child_node)}}'])
117+
kwargs.pop('{{safe_node_name(child_node)}}')
106118
{%- endfor %}
107-
if len(kwargs) != 0:
108-
# left over unhandled arguments
109-
raise ValueError('unrecognised arguments in field')
119+
if len(kwargs) != 0:
120+
# left over unhandled arguments
121+
raise ValueError('unrecognised arguments in field')
110122

111-
inverse_bit_mask = self.max_value ^ bit_mask
112123

113-
self.write((self.read() & inverse_bit_mask) | reg_value)
114124

115125
{% else %}
116126
def write_fields(self, {%- for child_node in node.fields() -%} {{safe_node_name(child_node)}} : {%- if 'encode' in child_node.list_properties() %}{{get_fully_qualified_enum_type(child_node.get_property('encode'), top_node.parent)}}_enumcls{% else %}int{% endif %}{%- if not loop.last -%},{%- endif -%}{%- endfor -%}) -> None: # type: ignore[override]

0 commit comments

Comments
 (0)