uvx format

This commit is contained in:
Arnav Sacheti
2025-10-13 20:14:46 -07:00
parent 3eee8b9cdd
commit 54a199ca9c
17 changed files with 307 additions and 205 deletions

View File

@@ -26,9 +26,7 @@ class AddressDecode:
assert s is not None
return s
def get_access_strobe(
self, node: RegNode | FieldNode, reduce_substrobes: bool = True
) -> str:
def get_access_strobe(self, node: RegNode | FieldNode, reduce_substrobes: bool = True) -> str:
"""
Returns the Verilog string that represents the register/field's access strobe.
"""
@@ -72,9 +70,7 @@ class DecodeLogicGenerator(RDLForLoopGenerator):
# List of address strides for each dimension
self._array_stride_stack: list[int] = []
def enter_AddressableComponent(
self, node: "AddressableNode"
) -> WalkerAction | None:
def enter_AddressableComponent(self, node: "AddressableNode") -> WalkerAction | None:
super().enter_AddressableComponent(node)
if node.array_dimensions:
@@ -103,9 +99,7 @@ class DecodeLogicGenerator(RDLForLoopGenerator):
expr_width = self.addr_decode.exp.ds.addr_width
a = str(
SVInt(
node.raw_absolute_address
- self.addr_decode.top_node.raw_absolute_address
+ subword_offset,
node.raw_absolute_address - self.addr_decode.top_node.raw_absolute_address + subword_offset,
expr_width,
)
)

View File

@@ -1,4 +1,4 @@
from .apb3_cpuif import APB3Cpuif
from .apb3_cpuif_flat import APB3CpuifFlat
__all__ = ["APB3Cpuif", "APB3CpuifFlat"]
__all__ = ["APB3Cpuif", "APB3CpuifFlat"]

View File

@@ -1,4 +1,5 @@
from systemrdl.node import AddressableNode
from ..base_cpuif import BaseCpuif
@@ -17,9 +18,7 @@ class APB3Cpuif(BaseCpuif):
@property
def port_declaration(self) -> str:
slave_ports: list[str] = ["apb3_intf.slave s_apb"]
master_ports: list[str] = list(
map(self._port_declaration, self.addressable_children)
)
master_ports: list[str] = list(map(self._port_declaration, self.addressable_children))
return ",\n".join(slave_ports + master_ports)

View File

@@ -1,4 +1,5 @@
from systemrdl.node import AddressableNode
from ..base_cpuif import BaseCpuif

View File

@@ -30,86 +30,4 @@ assign {{self.signal("PWRITE", child)}} = {{self.signal("PWRITE")}};
assign {{self.signal("PADDR", child)}} = {{self.signal("PADDR")}} [{{child.addr_width - 1}}:0]; // FIXME: Check slicing
assign {{self.signal("PWDATA", child)}} = {{self.signal("PWDATA")}};
{%- endif -%}
{%- endfor -%}
//======================================================
// Address Decode Logic
//======================================================
always_comb begin
// Default all PSELx signals to 0
{%- for child in cpuif.addressable_children -%}
{%- if child is array -%}
for (int {{child.inst_name|lower}}_idx = 0; {{child.inst_name|lower}}_idx < N_{{child.inst_name|upper}}S; {{child.inst_name|lower}}_idx++) begin
{{self.signal("PSELx", child, f"{child.inst_name.lower()}_idx")}} = 1'b0;
end
{%- else -%}
{{self.signal("PSELx", child)}} = 1'b0;
{%- endif -%}
{%- endfor -%}
if ({{self.signal("PSELx")}}) begin
{%- for child in cpuif.addressable_children -%}
{%- if loop.first -%}
if ({{cpuif.get_address_decode_condition(child)}}) begin
{%- else -%}
end else if ({{cpuif.get_address_decode_condition(child)}}) begin
{%- endif -%}
// Address matched for {{child.inst_name}}
{%- if child is array -%}
for (genvar {{child.inst_name|lower}}_idx = 0; {{child.inst_name|lower}}_idx < N_{{child.inst_name|upper}}S; {{child.inst_name|lower}}_idx++) begin
{{self.signal("PSELx", child, f"{child.inst_name.lower()}_idx")}} = 1'b1;
end
{%- else -%}
{{self.signal("PSELx", child)}} = 1'b1;
{%- endif -%}
{%- if loop.last -%}
end else begin
// No address matched
{%- endif -%}
{%- endfor -%}
end
end else begin
// PSELx is low, nothing to do
end
end
//======================================================
// Read Data Mux
//======================================================
always_comb begin
// Default read data to 0
{{self.signal("PRDATA")}} = '0;
{{self.signal("PREADY")}} = 1'b1;
{{self.signal("PSLVERR")}} = 1'b0;
if ({{self.signal("PSELx")}} && !{{self.signal("PWRITE")}} && {{self.signal("PENABLE")}}) begin
{%- for child in cpuif.addressable_children -%}
{%- if loop.first -%}
if ({{cpuif.get_address_decode_condition(child)}}) begin
{%- else -%}
end else if ({{cpuif.get_address_decode_condition(child)}}) begin
{%- endif -%}
// Address matched for {{child.inst_name}}
{%- if child is array -%}
for (genvar {{child.inst_name|lower}}_idx = 0; {{child.inst_name|lower}}_idx < N_{{child.inst_name|upper}}S; {{child.inst_name|lower}}_idx++) begin
{{self.signal("PRDATA")}} = {{self.signal("PRDATA", child, f"{child.inst_name.lower()}_idx")}};
{{self.signal("PREADY")}} = {{self.signal("PREADY", child, f"{child.inst_name.lower()}_idx")}};
{{self.signal("PSLVERR")}} = {{self.signal("PSLVERR", child, f"{child.inst_name.lower()}_idx")}};
end
{%- else -%}
{{self.signal("PRDATA")}} = {{self.signal("PRDATA", child)}};
{{self.signal("PREADY")}} = {{self.signal("PREADY", child)}};
{{self.signal("PSLVERR")}} = {{self.signal("PSLVERR", child)}};
{%- endif -%}
{%- if loop.last -%}
end else begin
// No address matched
{{self.signal("PRDATA")}} = {'hdeadbeef}[{{ds.data_width - 1}}:0]; // Indicate error on no match
{{self.signal("PSLVERR")}} = 1'b1; // Indicate error on no match
end
{%- endif -%}
{%- endfor -%}
end else begin
// Not a read transfer, nothing to do
end
end
{%- endfor -%}

View File

@@ -1,4 +1,4 @@
from .apb4_cpuif import APB4Cpuif
from .apb4_cpuif_flat import APB4CpuifFlat
__all__ = ["APB4Cpuif", "APB4CpuifFlat"]
__all__ = ["APB4Cpuif", "APB4CpuifFlat"]

View File

@@ -18,9 +18,7 @@ class APB4Cpuif(BaseCpuif):
def port_declaration(self) -> str:
"""Returns the port declaration for the APB4 interface."""
slave_ports: list[str] = ["apb4_intf.slave s_apb"]
master_ports: list[str] = list(
map(self._port_declaration, self.addressable_children)
)
master_ports: list[str] = list(map(self._port_declaration, self.addressable_children))
return ",\n".join(slave_ports + master_ports)
@@ -78,4 +76,4 @@ class APB4Cpuif(BaseCpuif):
address on the bus matches the address range of the given node.
"""
addr_pred = self.get_address_predicate(node)
return addr_pred
return addr_pred

View File

@@ -27,9 +27,7 @@ class Dereferencer:
def top_node(self) -> AddrmapNode:
return self.exp.ds.top_node
def get_access_strobe(
self, obj: RegNode | FieldNode, reduce_substrobes: bool = True
) -> str:
def get_access_strobe(self, obj: RegNode | FieldNode, reduce_substrobes: bool = True) -> str:
"""
Returns the Verilog string that represents the register's access strobe
"""

View File

@@ -28,9 +28,7 @@ class BusDecoderExporter:
def __init__(self, **kwargs: Any) -> None:
# Check for stray kwargs
if kwargs:
raise TypeError(
f"got an unexpected keyword argument '{list(kwargs.keys())[0]}'"
)
raise TypeError(f"got an unexpected keyword argument '{list(kwargs.keys())[0]}'")
loader = jj.ChoiceLoader(
[
@@ -49,9 +47,7 @@ class BusDecoderExporter:
undefined=jj.StrictUndefined,
)
def export(
self, node: RootNode | AddrmapNode, output_dir: str, **kwargs: Any
) -> None:
def export(self, node: RootNode | AddrmapNode, output_dir: str, **kwargs: Any) -> None:
"""
Parameters
----------
@@ -88,9 +84,7 @@ class BusDecoderExporter:
# Check for stray kwargs
if kwargs:
raise TypeError(
f"got an unexpected keyword argument '{list(kwargs.keys())[0]}'"
)
raise TypeError(f"got an unexpected keyword argument '{list(kwargs.keys())[0]}'")
# Construct exporter components
self.cpuif = cpuif_cls(self)
@@ -139,12 +133,8 @@ class DesignState:
# Extract compiler args
# ------------------------
self.reuse_hwif_typedefs: bool = kwargs.pop("reuse_hwif_typedefs", True)
self.module_name: str = kwargs.pop("module_name", None) or kwf(
self.top_node.inst_name
)
self.package_name: str = kwargs.pop("package_name", None) or (
self.module_name + "_pkg"
)
self.module_name: str = kwargs.pop("module_name", None) or kwf(self.top_node.inst_name)
self.package_name: str = kwargs.pop("package_name", None) or (self.module_name + "_pkg")
user_addr_width: int | None = kwargs.pop("address_width", None)
self.cpuif_unroll: bool = kwargs.pop("cpuif_unroll", False)
@@ -172,9 +162,7 @@ class DesignState:
# ------------------------
# Min address width encloses the total size AND at least 1 useful address bit
self.addr_width = max(
clog2(self.top_node.size), clog2(self.cpuif_data_width // 8) + 1
)
self.addr_width = max(clog2(self.top_node.size), clog2(self.cpuif_data_width // 8) + 1)
if user_addr_width is not None:
if user_addr_width < self.addr_width:

View File

@@ -82,9 +82,7 @@ class RDLForLoopGenerator(ForLoopGenerator, RDLListener):
walker.walk(node, self, skip_top=True)
return self.finish()
def enter_AddressableComponent(
self, node: "AddressableNode"
) -> WalkerAction | None:
def enter_AddressableComponent(self, node: "AddressableNode") -> WalkerAction | None:
if not node.array_dimensions:
return None

View File

@@ -1,42 +1,253 @@
# All SystemVerilog 2017 keywords
SV_KEYWORDS = {
'accept_on', 'alias', 'always', 'always_comb', 'always_ff', 'always_latch',
'and', 'assert', 'assign', 'assume', 'automatic', 'before', 'begin', 'bind',
'bins', 'binsof', 'bit', 'break', 'buf', 'bufif0', 'bufif1', 'byte', 'case',
'casex', 'casez', 'cell', 'chandle', 'checker', 'class', 'clocking', 'cmos',
'config', 'const', 'constraint', 'context', 'continue', 'cover', 'covergroup',
'coverpoint', 'cross', 'deassign', 'default', 'defparam', 'design', 'disable',
'dist', 'do', 'edge', 'else', 'end', 'endcase', 'endchecker', 'endclass',
'endclocking', 'endconfig', 'endfunction', 'endgenerate', 'endgroup',
'endinterface', 'endmodule', 'endpackage', 'endprimitive', 'endprogram',
'endproperty', 'endspecify', 'endsequence', 'endtable', 'endtask', 'enum',
'event', 'eventually', 'expect', 'export', 'extends', 'extern', 'final',
'first_match', 'for', 'force', 'foreach', 'forever', 'fork', 'forkjoin',
'function', 'generate', 'genvar', 'global', 'highz0', 'highz1', 'if', 'iff',
'ifnone', 'ignore_bins', 'illegal_bins', 'implements', 'implies', 'import',
'incdir', 'include', 'initial', 'inout', 'input', 'inside', 'instance',
'int', 'integer', 'interconnect', 'interface', 'intersect', 'join',
'join_any', 'join_none', 'large', 'let', 'liblist', 'library', 'local',
'localparam', 'logic', 'longint', 'macromodule', 'matches', 'medium',
'modport', 'module', 'nand', 'negedge', 'nettype', 'new', 'nexttime', 'nmos',
'nor', 'noshowcancelled', 'not', 'notif0', 'notif1', 'null', 'or', 'output',
'package', 'packed', 'parameter', 'pmos', 'posedge', 'primitive', 'priority',
'program', 'property', 'protected', 'pull0', 'pull1', 'pulldown', 'pullup',
'pulsestyle_ondetect', 'pulsestyle_onevent', 'pure', 'rand', 'randc',
'randcase', 'randsequence', 'rcmos', 'real', 'realtime', 'ref', 'reg',
'reject_on', 'release', 'repeat', 'restrict', 'return', 'rnmos', 'rpmos',
'rtran', 'rtranif0', 'rtranif1', 's_always', 's_eventually', 's_nexttime',
's_until', 's_until_with', 'scalared', 'sequence', 'shortint', 'shortreal',
'showcancelled', 'signed', 'small', 'soft', 'solve', 'specify', 'specparam',
'static', 'string', 'strong', 'strong0', 'strong1', 'struct', 'super',
'supply0', 'supply1', 'sync_accept_on', 'sync_reject_on', 'table', 'tagged',
'task', 'this', 'throughout', 'time', 'timeprecision', 'timeunit', 'tran',
'tranif0', 'tranif1', 'tri', 'tri0', 'tri1', 'triand', 'trior', 'trireg',
'type', 'typedef', 'union', 'unique', 'unique0', 'unsigned', 'until',
'until_with', 'untyped', 'use', 'uwire', 'var', 'vectored', 'virtual', 'void',
'wait', 'wait_order', 'wand', 'weak', 'weak0', 'weak1', 'while', 'wildcard',
'wire', 'with', 'within', 'wor', 'xnor', 'xor'
"accept_on",
"alias",
"always",
"always_comb",
"always_ff",
"always_latch",
"and",
"assert",
"assign",
"assume",
"automatic",
"before",
"begin",
"bind",
"bins",
"binsof",
"bit",
"break",
"buf",
"bufif0",
"bufif1",
"byte",
"case",
"casex",
"casez",
"cell",
"chandle",
"checker",
"class",
"clocking",
"cmos",
"config",
"const",
"constraint",
"context",
"continue",
"cover",
"covergroup",
"coverpoint",
"cross",
"deassign",
"default",
"defparam",
"design",
"disable",
"dist",
"do",
"edge",
"else",
"end",
"endcase",
"endchecker",
"endclass",
"endclocking",
"endconfig",
"endfunction",
"endgenerate",
"endgroup",
"endinterface",
"endmodule",
"endpackage",
"endprimitive",
"endprogram",
"endproperty",
"endspecify",
"endsequence",
"endtable",
"endtask",
"enum",
"event",
"eventually",
"expect",
"export",
"extends",
"extern",
"final",
"first_match",
"for",
"force",
"foreach",
"forever",
"fork",
"forkjoin",
"function",
"generate",
"genvar",
"global",
"highz0",
"highz1",
"if",
"iff",
"ifnone",
"ignore_bins",
"illegal_bins",
"implements",
"implies",
"import",
"incdir",
"include",
"initial",
"inout",
"input",
"inside",
"instance",
"int",
"integer",
"interconnect",
"interface",
"intersect",
"join",
"join_any",
"join_none",
"large",
"let",
"liblist",
"library",
"local",
"localparam",
"logic",
"longint",
"macromodule",
"matches",
"medium",
"modport",
"module",
"nand",
"negedge",
"nettype",
"new",
"nexttime",
"nmos",
"nor",
"noshowcancelled",
"not",
"notif0",
"notif1",
"null",
"or",
"output",
"package",
"packed",
"parameter",
"pmos",
"posedge",
"primitive",
"priority",
"program",
"property",
"protected",
"pull0",
"pull1",
"pulldown",
"pullup",
"pulsestyle_ondetect",
"pulsestyle_onevent",
"pure",
"rand",
"randc",
"randcase",
"randsequence",
"rcmos",
"real",
"realtime",
"ref",
"reg",
"reject_on",
"release",
"repeat",
"restrict",
"return",
"rnmos",
"rpmos",
"rtran",
"rtranif0",
"rtranif1",
"s_always",
"s_eventually",
"s_nexttime",
"s_until",
"s_until_with",
"scalared",
"sequence",
"shortint",
"shortreal",
"showcancelled",
"signed",
"small",
"soft",
"solve",
"specify",
"specparam",
"static",
"string",
"strong",
"strong0",
"strong1",
"struct",
"super",
"supply0",
"supply1",
"sync_accept_on",
"sync_reject_on",
"table",
"tagged",
"task",
"this",
"throughout",
"time",
"timeprecision",
"timeunit",
"tran",
"tranif0",
"tranif1",
"tri",
"tri0",
"tri1",
"triand",
"trior",
"trireg",
"type",
"typedef",
"union",
"unique",
"unique0",
"unsigned",
"until",
"until_with",
"untyped",
"use",
"uwire",
"var",
"vectored",
"virtual",
"void",
"wait",
"wait_order",
"wand",
"weak",
"weak0",
"weak1",
"while",
"wildcard",
"wire",
"with",
"within",
"wor",
"xnor",
"xor",
}

View File

@@ -79,7 +79,6 @@ module {{ds.module_name}}
// Default all read select signals to 0
cpuif_rd_sel = '0;
if (cpuif_req && !cpuif_wr_en) begin
// A read request is pending
{%- for child in cpuif.addressable_children -%}

View File

@@ -6,18 +6,20 @@ from systemrdl.component import Field
if TYPE_CHECKING:
from systemrdl.node import Node
class ReadSwacc(UDPDefinition):
name = "rd_swacc"
valid_components = {Field}
valid_type = bool
def get_unassigned_default(self, node: 'Node') -> Any:
def get_unassigned_default(self, node: "Node") -> Any:
return False
class WriteSwacc(UDPDefinition):
name = "wr_swacc"
valid_components = {Field}
valid_type = bool
def get_unassigned_default(self, node: 'Node') -> Any:
def get_unassigned_default(self, node: "Node") -> Any:
return False

View File

@@ -20,16 +20,12 @@ class _FixedpointWidth(UDPDefinition):
# incompatible with "counter" fields
if node.get_property("counter"):
self.msg.error(
"Fixed-point representations are not supported for counter fields.",
prop_ref
)
self.msg.error("Fixed-point representations are not supported for counter fields.", prop_ref)
# incompatible with "encode" fields
if node.get_property("encode") is not None:
self.msg.error(
"Fixed-point representations are not supported for fields encoded as an enum.",
prop_ref
"Fixed-point representations are not supported for fields encoded as an enum.", prop_ref
)
# ensure node width = fracwidth + intwidth
@@ -37,7 +33,7 @@ class _FixedpointWidth(UDPDefinition):
self.msg.error(
f"Number of integer bits ({intwidth}) plus number of fractional bits ({fracwidth})"
f" must be equal to the width of the component ({node.width}).",
prop_ref
prop_ref,
)

View File

@@ -16,8 +16,7 @@ class xBufferTrigger(UDPDefinition):
if value is NoValue:
self.msg.error(
"Double-buffer trigger property is missing a value assignment",
self.get_src_ref(node)
"Double-buffer trigger property is missing a value assignment", self.get_src_ref(node)
)
elif isinstance(value, VectorNode):
# Trigger can reference a vector, but only if it is a single-bit
@@ -25,17 +24,19 @@ class xBufferTrigger(UDPDefinition):
self.msg.error(
"%s '%s' references %s '%s' but its width is not 1"
% (
type(node.inst).__name__.lower(), node.inst_name,
type(value.inst).__name__.lower(), value.inst_name
type(node.inst).__name__.lower(),
node.inst_name,
type(value.inst).__name__.lower(),
value.inst_name,
),
self.get_src_ref(node)
self.get_src_ref(node),
)
if isinstance(value, SignalNode):
if not value.get_property('activehigh') and not value.get_property('activelow'):
if not value.get_property("activehigh") and not value.get_property("activelow"):
self.msg.error(
"Trigger was asigned a signal, but it does not specify whether it is activehigh/activelow",
self.get_src_ref(node)
self.get_src_ref(node),
)
elif isinstance(value, PropertyReference) and value.width is not None:
@@ -44,10 +45,12 @@ class xBufferTrigger(UDPDefinition):
self.msg.error(
"%s '%s' references property '%s->%s' but its width is not 1"
% (
type(node.inst).__name__.lower(), node.inst_name,
value.node.inst_name, value.name,
type(node.inst).__name__.lower(),
node.inst_name,
value.node.inst_name,
value.name,
),
self.get_src_ref(node)
self.get_src_ref(node),
)
elif isinstance(value, RegNode):
# Trigger can reference a register, which implies access of the
@@ -60,34 +63,35 @@ class xBufferTrigger(UDPDefinition):
self.msg.error(
"Reference to a %s component is incompatible with the '%s' property."
% (type(node.inst).__name__.lower(), self.name),
self.get_src_ref(node)
self.get_src_ref(node),
)
#-------------------------------------------------------------------------------
# -------------------------------------------------------------------------------
class BufferWrites(UDPDefinition):
name = "buffer_writes"
valid_components = {Reg}
valid_type = bool
def validate(self, node: 'Node', value: Any) -> None:
def validate(self, node: "Node", value: Any) -> None:
assert isinstance(node, RegNode)
if value:
if not node.has_sw_writable:
self.msg.error(
"'buffer_writes' is set to true, but this register does not contain any writable fields.",
self.get_src_ref(node)
self.get_src_ref(node),
)
def get_unassigned_default(self, node: 'Node') -> Any:
def get_unassigned_default(self, node: "Node") -> Any:
return False
class WBufferTrigger(xBufferTrigger):
name = "wbuffer_trigger"
def get_unassigned_default(self, node: 'Node') -> Any:
def get_unassigned_default(self, node: "Node") -> Any:
# If buffering is enabled, trigger is the register itself
if node.get_property('buffer_writes'):
if node.get_property("buffer_writes"):
return node
return None
@@ -107,24 +111,24 @@ class BufferReads(UDPDefinition):
valid_components = {Reg}
valid_type = bool
def validate(self, node: 'Node', value: Any) -> None:
def validate(self, node: "Node", value: Any) -> None:
assert isinstance(node, RegNode)
if value:
if not node.has_sw_readable:
self.msg.error(
"'buffer_reads' is set to true, but this register does not contain any readable fields.",
self.get_src_ref(node)
self.get_src_ref(node),
)
def get_unassigned_default(self, node: 'Node') -> Any:
def get_unassigned_default(self, node: "Node") -> Any:
return False
class RBufferTrigger(xBufferTrigger):
name = "rbuffer_trigger"
def get_unassigned_default(self, node: 'Node') -> Any:
def get_unassigned_default(self, node: "Node") -> Any:
# If buffering is enabled, trigger is the register itself
if node.get_property('buffer_reads'):
if node.get_property("buffer_reads"):
return node
return None

View File

@@ -16,14 +16,14 @@ class IsSigned(UDPDefinition):
if value and node.get_property("counter"):
self.msg.error(
"The property is_signed=true is not supported for counter fields.",
node.inst.property_src_ref["is_signed"]
node.inst.property_src_ref["is_signed"],
)
# incompatible with "encode" fields
if value and node.get_property("encode") is not None:
self.msg.error(
"The property is_signed=true is not supported for fields encoded as an enum.",
node.inst.property_src_ref["is_signed"]
node.inst.property_src_ref["is_signed"],
)
def get_unassigned_default(self, node: "Node") -> Any:

View File

@@ -49,9 +49,7 @@ class DesignValidator(RDLListener):
if isinstance(value, PropertyReference):
src_ref = value.src_ref
else:
src_ref = node.inst.property_src_ref.get(
prop_name, node.inst.inst_src_ref
)
src_ref = node.inst.property_src_ref.get(prop_name, node.inst.inst_src_ref)
self.msg.error(
"Property is assigned a reference that points to a component not internal to the busdecoder being exported.",
src_ref,
@@ -125,9 +123,9 @@ class DesignValidator(RDLListener):
def enter_Field(self, node: "FieldNode") -> None:
parent_accesswidth = node.parent.get_property("accesswidth")
parent_regwidth = node.parent.get_property("regwidth")
if (parent_accesswidth < parent_regwidth) and (
node.lsb // parent_accesswidth
) != (node.msb // parent_accesswidth):
if (parent_accesswidth < parent_regwidth) and (node.lsb // parent_accesswidth) != (
node.msb // parent_accesswidth
):
# field spans multiple sub-words
if node.is_sw_writable and not node.parent.get_property("buffer_writes"):
@@ -141,9 +139,7 @@ class DesignValidator(RDLListener):
node.inst.inst_src_ref,
)
if node.get_property("onread") is not None and not node.parent.get_property(
"buffer_reads"
):
if node.get_property("onread") is not None and not node.parent.get_property("buffer_reads"):
# ... is modified by an onread action without the atomicity of read buffering
# Enforce 10.6.1-f
self.msg.error(