Add support for field paritycheck. #35
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,6 +10,7 @@
|
|||||||
**/*.log
|
**/*.log
|
||||||
**/*.pb
|
**/*.pb
|
||||||
**/.Xil
|
**/.Xil
|
||||||
|
**/.coverage.*
|
||||||
|
|
||||||
build/
|
build/
|
||||||
dist/
|
dist/
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ your hardware design.
|
|||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
The PeakRDL-regblock SV generator is still in pre-production (v0.x version numbers).
|
The PeakRDL-regblock SV generator is still in pre-production (v0.x version numbers).
|
||||||
During this time, I may decide to refactor things which could break compatibility.
|
During this time, I may decide to refactor things which could affect compatibility.
|
||||||
|
|
||||||
|
|
||||||
Installing
|
Installing
|
||||||
|
|||||||
@@ -460,7 +460,15 @@ If assigned, replaces the inferred ``hwif_in..next`` input with an explicit refe
|
|||||||
|
|
||||||
paritycheck
|
paritycheck
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|NO|
|
If set, enables parity checking for this field.
|
||||||
|
|
||||||
|
Adds a ``parity_error`` output signal to the module.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If this field does not implement storage, the ``partycheck`` property is ignored.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
precedence
|
precedence
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "0.14.0-rc1"
|
__version__ = "0.14.0-rc2"
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from .hwif import Hwif
|
|||||||
from .write_buffering import WriteBuffering
|
from .write_buffering import WriteBuffering
|
||||||
from .read_buffering import ReadBuffering
|
from .read_buffering import ReadBuffering
|
||||||
from .external_acks import ExternalWriteAckGenerator, ExternalReadAckGenerator
|
from .external_acks import ExternalWriteAckGenerator, ExternalReadAckGenerator
|
||||||
|
from .parity import ParityErrorReduceGenerator
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from systemrdl.node import SignalNode
|
from systemrdl.node import SignalNode
|
||||||
@@ -153,6 +154,7 @@ class RegblockExporter:
|
|||||||
self.dereferencer = Dereferencer(self)
|
self.dereferencer = Dereferencer(self)
|
||||||
ext_write_acks = ExternalWriteAckGenerator(self)
|
ext_write_acks = ExternalWriteAckGenerator(self)
|
||||||
ext_read_acks = ExternalReadAckGenerator(self)
|
ext_read_acks = ExternalReadAckGenerator(self)
|
||||||
|
parity = ParityErrorReduceGenerator(self)
|
||||||
|
|
||||||
# Validate that there are no unsupported constructs
|
# Validate that there are no unsupported constructs
|
||||||
DesignValidator(self).do_validate()
|
DesignValidator(self).do_validate()
|
||||||
@@ -176,6 +178,7 @@ class RegblockExporter:
|
|||||||
"readback_implementation": readback_implementation,
|
"readback_implementation": readback_implementation,
|
||||||
"ext_write_acks": ext_write_acks,
|
"ext_write_acks": ext_write_acks,
|
||||||
"ext_read_acks": ext_read_acks,
|
"ext_read_acks": ext_read_acks,
|
||||||
|
"parity": parity,
|
||||||
"get_always_ff_event": self.dereferencer.get_always_ff_event,
|
"get_always_ff_event": self.dereferencer.get_always_ff_event,
|
||||||
"ds": self.ds,
|
"ds": self.ds,
|
||||||
"kwf": kwf,
|
"kwf": kwf,
|
||||||
@@ -243,6 +246,8 @@ class DesignState:
|
|||||||
self.has_external_block = False
|
self.has_external_block = False
|
||||||
self.has_external_addressable = False
|
self.has_external_addressable = False
|
||||||
|
|
||||||
|
self.has_paritycheck = False
|
||||||
|
|
||||||
# Track any referenced enums
|
# Track any referenced enums
|
||||||
self.user_enums = [] # type: List[Type[UserEnum]]
|
self.user_enums = [] # type: List[Type[UserEnum]]
|
||||||
|
|
||||||
|
|||||||
@@ -274,6 +274,19 @@ class FieldLogic:
|
|||||||
# Not sw modifiable
|
# Not sw modifiable
|
||||||
return "1'b0"
|
return "1'b0"
|
||||||
|
|
||||||
|
def get_parity_identifier(self, field: 'FieldNode') -> str:
|
||||||
|
"""
|
||||||
|
Returns the identifier for the stored 'golden' parity value of the field
|
||||||
|
"""
|
||||||
|
path = get_indexed_path(self.top_node, field)
|
||||||
|
return f"field_storage.{path}.parity"
|
||||||
|
|
||||||
|
def get_parity_error_identifier(self, field: 'FieldNode') -> str:
|
||||||
|
"""
|
||||||
|
Returns the identifier for whether the field currently has a parity error
|
||||||
|
"""
|
||||||
|
path = get_indexed_path(self.top_node, field)
|
||||||
|
return f"field_combo.{path}.parity_error"
|
||||||
|
|
||||||
def has_next_q(self, field: 'FieldNode') -> bool:
|
def has_next_q(self, field: 'FieldNode') -> bool:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ class CombinationalStructGenerator(RDLStructGenerator):
|
|||||||
self.add_up_counter_members(node)
|
self.add_up_counter_members(node)
|
||||||
if node.is_down_counter:
|
if node.is_down_counter:
|
||||||
self.add_down_counter_members(node)
|
self.add_down_counter_members(node)
|
||||||
|
if node.get_property('paritycheck'):
|
||||||
|
self.add_member("parity_error")
|
||||||
self.pop_struct()
|
self.pop_struct()
|
||||||
|
|
||||||
def add_up_counter_members(self, node: 'FieldNode') -> None:
|
def add_up_counter_members(self, node: 'FieldNode') -> None:
|
||||||
@@ -88,6 +90,8 @@ class FieldStorageStructGenerator(RDLStructGenerator):
|
|||||||
|
|
||||||
if node.implements_storage:
|
if node.implements_storage:
|
||||||
self.add_member("value", node.width)
|
self.add_member("value", node.width)
|
||||||
|
if node.get_property('paritycheck'):
|
||||||
|
self.add_member("parity")
|
||||||
|
|
||||||
if self.field_logic.has_next_q(node):
|
if self.field_logic.has_next_q(node):
|
||||||
self.add_member("next_q", node.width)
|
self.add_member("next_q", node.width)
|
||||||
@@ -233,6 +237,7 @@ class FieldLogicGenerator(RDLForLoopGenerator):
|
|||||||
'get_value': self.exp.dereferencer.get_value,
|
'get_value': self.exp.dereferencer.get_value,
|
||||||
'get_resetsignal': self.exp.dereferencer.get_resetsignal,
|
'get_resetsignal': self.exp.dereferencer.get_resetsignal,
|
||||||
'get_input_identifier': self.exp.hwif.get_input_identifier,
|
'get_input_identifier': self.exp.hwif.get_input_identifier,
|
||||||
|
'ds': self.ds,
|
||||||
}
|
}
|
||||||
self.add_content(self.field_storage_template.render(context))
|
self.add_content(self.field_storage_template.render(context))
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
always_comb begin
|
always_comb begin
|
||||||
automatic logic [{{node.width-1}}:0] next_c = {{field_logic.get_storage_identifier(node)}};
|
automatic logic [{{node.width-1}}:0] next_c = {{field_logic.get_storage_identifier(node)}};
|
||||||
automatic logic load_next_c = '0;
|
automatic logic load_next_c = '0;
|
||||||
|
|
||||||
{%- for signal in extra_combo_signals %}
|
{%- for signal in extra_combo_signals %}
|
||||||
{{field_logic.get_field_combo_identifier(node, signal.name)}} = {{signal.default_assignment}};
|
{{field_logic.get_field_combo_identifier(node, signal.name)}} = {{signal.default_assignment}};
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
@@ -13,22 +14,35 @@ always_comb begin
|
|||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
end
|
end
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
|
|
||||||
{%- if node.is_up_counter %}
|
{%- if node.is_up_counter %}
|
||||||
{{counter_macros.up_counter(node)}}
|
{{counter_macros.up_counter(node)}}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
{%- if node.is_down_counter %}
|
{%- if node.is_down_counter %}
|
||||||
{{counter_macros.down_counter(node)}}
|
{{counter_macros.down_counter(node)}}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{{field_logic.get_field_combo_identifier(node, "next")}} = next_c;
|
{{field_logic.get_field_combo_identifier(node, "next")}} = next_c;
|
||||||
{{field_logic.get_field_combo_identifier(node, "load_next")}} = load_next_c;
|
{{field_logic.get_field_combo_identifier(node, "load_next")}} = load_next_c;
|
||||||
|
|
||||||
|
{%- if node.get_property('paritycheck') %}
|
||||||
|
{{field_logic.get_parity_error_identifier(node)}} = ({{field_logic.get_parity_identifier(node)}} != ^{{field_logic.get_storage_identifier(node)}});
|
||||||
|
{%- endif %}
|
||||||
end
|
end
|
||||||
always_ff {{get_always_ff_event(resetsignal)}} begin
|
always_ff {{get_always_ff_event(resetsignal)}} begin
|
||||||
{% if reset is not none -%}
|
{% if reset is not none -%}
|
||||||
if({{get_resetsignal(resetsignal)}}) begin
|
if({{get_resetsignal(resetsignal)}}) begin
|
||||||
{{field_logic.get_storage_identifier(node)}} <= {{reset}};
|
{{field_logic.get_storage_identifier(node)}} <= {{reset}};
|
||||||
|
{%- if node.get_property('paritycheck') %}
|
||||||
|
{{field_logic.get_parity_identifier(node)}} <= ^{{reset}};
|
||||||
|
{%- endif %}
|
||||||
end else {% endif %}if({{field_logic.get_field_combo_identifier(node, "load_next")}}) begin
|
end else {% endif %}if({{field_logic.get_field_combo_identifier(node, "load_next")}}) begin
|
||||||
{{field_logic.get_storage_identifier(node)}} <= {{field_logic.get_field_combo_identifier(node, "next")}};
|
{{field_logic.get_storage_identifier(node)}} <= {{field_logic.get_field_combo_identifier(node, "next")}};
|
||||||
|
{%- if node.get_property('paritycheck') %}
|
||||||
|
{{field_logic.get_parity_identifier(node)}} <= ^{{field_logic.get_field_combo_identifier(node, "next")}};
|
||||||
|
{%- endif %}
|
||||||
end
|
end
|
||||||
|
|
||||||
{%- if field_logic.has_next_q(node) %}
|
{%- if field_logic.has_next_q(node) %}
|
||||||
{{field_logic.get_next_q_identifier(node)}} <= {{get_input_identifier(node)}};
|
{{field_logic.get_next_q_identifier(node)}} <= {{get_input_identifier(node)}};
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ module {{ds.module_name}} (
|
|||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
|
|
||||||
|
{%- if ds.has_paritycheck %}
|
||||||
|
|
||||||
|
output logic parity_error,
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
{{cpuif.port_declaration|indent(8)}}
|
{{cpuif.port_declaration|indent(8)}}
|
||||||
{%- if hwif.has_input_struct or hwif.has_output_struct %},{% endif %}
|
{%- if hwif.has_input_struct or hwif.has_output_struct %},{% endif %}
|
||||||
|
|
||||||
@@ -168,6 +173,22 @@ module {{ds.module_name}} (
|
|||||||
|
|
||||||
{{field_logic.get_implementation()|indent}}
|
{{field_logic.get_implementation()|indent}}
|
||||||
|
|
||||||
|
{%- if ds.has_paritycheck %}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Parity Error
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
always_ff {{get_always_ff_event(cpuif.reset)}} begin
|
||||||
|
if({{get_resetsignal(cpuif.reset)}}) begin
|
||||||
|
parity_error <= '0;
|
||||||
|
end else begin
|
||||||
|
automatic logic err = '0;
|
||||||
|
{{parity.get_implementation()|indent(12)}}
|
||||||
|
parity_error <= err;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
{%- if ds.has_buffered_read_regs %}
|
{%- if ds.has_buffered_read_regs %}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|||||||
34
src/peakrdl_regblock/parity.py
Normal file
34
src/peakrdl_regblock/parity.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from systemrdl.walker import WalkerAction
|
||||||
|
|
||||||
|
|
||||||
|
from .forloop_generator import RDLForLoopGenerator
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .exporter import RegblockExporter
|
||||||
|
from systemrdl.node import FieldNode, AddressableNode
|
||||||
|
|
||||||
|
|
||||||
|
class ParityErrorReduceGenerator(RDLForLoopGenerator):
|
||||||
|
def __init__(self, exp: 'RegblockExporter') -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.exp = exp
|
||||||
|
|
||||||
|
def get_implementation(self) -> str:
|
||||||
|
content = self.get_content(self.exp.ds.top_node)
|
||||||
|
if content is None:
|
||||||
|
return ""
|
||||||
|
return content
|
||||||
|
|
||||||
|
def enter_AddressableComponent(self, node: 'AddressableNode') -> WalkerAction:
|
||||||
|
super().enter_AddressableComponent(node)
|
||||||
|
if node.external:
|
||||||
|
return WalkerAction.SkipDescendants
|
||||||
|
return WalkerAction.Continue
|
||||||
|
|
||||||
|
def enter_Field(self, node: 'FieldNode') -> None:
|
||||||
|
if node.get_property('paritycheck') and node.implements_storage:
|
||||||
|
self.add_content(
|
||||||
|
f"err |= {self.exp.field_logic.get_parity_error_identifier(node)};"
|
||||||
|
)
|
||||||
@@ -106,3 +106,14 @@ class DesignScanner(RDLListener):
|
|||||||
def enter_Field(self, node: 'FieldNode') -> None:
|
def enter_Field(self, node: 'FieldNode') -> None:
|
||||||
if node.is_sw_writable and (node.msb < node.lsb):
|
if node.is_sw_writable and (node.msb < node.lsb):
|
||||||
self.ds.has_writable_msb0_fields = True
|
self.ds.has_writable_msb0_fields = True
|
||||||
|
|
||||||
|
if node.get_property('paritycheck') and node.implements_storage:
|
||||||
|
self.ds.has_paritycheck = True
|
||||||
|
|
||||||
|
if node.get_property('reset') is None:
|
||||||
|
self.msg.warning(
|
||||||
|
f"Field '{node.inst_name}' includes parity check logic, but "
|
||||||
|
"its reset value was not defined. Will result in an undefined "
|
||||||
|
"value on the module's 'parity_error' output.",
|
||||||
|
self.top_node.inst.property_src_ref.get('paritycheck', self.top_node.inst.inst_src_ref)
|
||||||
|
)
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ module tb;
|
|||||||
regblock_pkg::regblock__out_t hwif_out;
|
regblock_pkg::regblock__out_t hwif_out;
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- if exporter.ds.has_paritycheck %}
|
||||||
|
logic parity_error;
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
|
||||||
{%- block declarations %}
|
{%- block declarations %}
|
||||||
{%- endblock %}
|
{%- endblock %}
|
||||||
|
|
||||||
@@ -43,6 +48,10 @@ module tb;
|
|||||||
input hwif_out;
|
input hwif_out;
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- if exporter.ds.has_paritycheck %}
|
||||||
|
input parity_error;
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
{%- filter indent %}
|
{%- filter indent %}
|
||||||
{%- block clocking_dirs %}
|
{%- block clocking_dirs %}
|
||||||
{%- endblock %}
|
{%- endblock %}
|
||||||
@@ -68,6 +77,9 @@ module tb;
|
|||||||
##1;
|
##1;
|
||||||
tmp = {>>{hwif_out}}; // Workaround for Xilinx's xsim - assign to tmp variable
|
tmp = {>>{hwif_out}}; // Workaround for Xilinx's xsim - assign to tmp variable
|
||||||
if(!rst) assert(!$isunknown(tmp)) else $error("hwif_out has X's!");
|
if(!rst) assert(!$isunknown(tmp)) else $error("hwif_out has X's!");
|
||||||
|
{%- if exporter.ds.has_paritycheck %}
|
||||||
|
if(!rst) assert(!$isunknown(parity_error)) else $error("parity_error has X's!");
|
||||||
|
{%- endif %}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|||||||
0
tests/test_parity/__init__.py
Normal file
0
tests/test_parity/__init__.py
Normal file
14
tests/test_parity/regblock.rdl
Normal file
14
tests/test_parity/regblock.rdl
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
addrmap top {
|
||||||
|
default paritycheck;
|
||||||
|
default sw=rw;
|
||||||
|
default hw=na;
|
||||||
|
|
||||||
|
reg my_reg {
|
||||||
|
field {} f1[16] = 0;
|
||||||
|
field {} f2[8] = 0;
|
||||||
|
field {} f3 = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
my_reg r1 @ 0x000;
|
||||||
|
my_reg r2[8] @ 0x1000;
|
||||||
|
};
|
||||||
37
tests/test_parity/tb_template.sv
Normal file
37
tests/test_parity/tb_template.sv
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{% extends "lib/tb_base.sv" %}
|
||||||
|
|
||||||
|
{% block seq %}
|
||||||
|
{% sv_line_anchor %}
|
||||||
|
##1;
|
||||||
|
cb.rst <= '0;
|
||||||
|
##1;
|
||||||
|
|
||||||
|
fork
|
||||||
|
begin
|
||||||
|
repeat(50) begin
|
||||||
|
automatic int i = $urandom_range(7,0);
|
||||||
|
cpuif.write('h0, $urandom());
|
||||||
|
cpuif.write('h1000 + i*4, $urandom());
|
||||||
|
end
|
||||||
|
end
|
||||||
|
begin
|
||||||
|
forever begin
|
||||||
|
assert(cb.parity_error != 1'b1);
|
||||||
|
@cb;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
join_any
|
||||||
|
disable fork;
|
||||||
|
|
||||||
|
cpuif.write('h0, 'd0);
|
||||||
|
force dut.field_storage.r1.f1.value[3] = 1'b1;
|
||||||
|
release dut.field_storage.r1.f1.value[3];
|
||||||
|
@cb;
|
||||||
|
@cb;
|
||||||
|
assert(cb.parity_error == 1'b1);
|
||||||
|
cpuif.write('h0, 'd0);
|
||||||
|
@cb;
|
||||||
|
@cb;
|
||||||
|
assert(cb.parity_error == 1'b0);
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
5
tests/test_parity/testcase.py
Normal file
5
tests/test_parity/testcase.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from ..lib.sim_testcase import SimTestCase
|
||||||
|
|
||||||
|
class Test(SimTestCase):
|
||||||
|
def test_dut(self):
|
||||||
|
self.run_test()
|
||||||
Reference in New Issue
Block a user