Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion docs/props/field.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,36 @@ If set, field will get cleared back to zero after being written.

sw
^^^
All sw access modes are supported except for ``w1`` and ``rw1``.

All sw access modes are supported.

For write-once (``w1`` / ``rw1```) fields an additional storage bit is inferred.
On first successful software write it is blown and all further software writes
are ignored. A software write only consumes the signle write if it actually
reaches the field:

* If ``swwe`` / ``swwel`` is blocking the write, the field remains writable.
* If the register sets ``buffer_writes``, the write is consumed when the buffer
commits to the field.

Resetting the field makes it writable again. It uses either the reset
associated with the field or the default reset when the field itself is not
resettable.

.. wavedrom::
{"signal": [
{"name": "clk", "wave": "p................"},
{"name": "rst", "wave": "01.....01........"},
{"name": "<sw write>", "wave": "0.10.10..10.10.10"},
{"name": "<write data>", "wave": "x.=x.=x..=x.=x.=x",
"data": ["A", "B", "C", "D", "E"]},
{"name": "<field swwe>", "wave": "1......0...1....."},
{"name": "<field value>", "wave": "=..=....=....=...",
"data": ["reset", "A", "reset", "D"]}
]}

.. note:: A software write to a write-once field that was written still asserts
``swacc`` and ``swmod``.

swacc
^^^^^
Expand Down
16 changes: 15 additions & 1 deletion src/peakrdl_regblock/field_logic/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import TYPE_CHECKING, Union

from systemrdl.rdltypes import PrecedenceType, InterruptType
from systemrdl.rdltypes import PrecedenceType, InterruptType, AccessType

from .bases import AssignmentPrecedence, NextStateConditional
from . import sw_onread
Expand Down Expand Up @@ -282,6 +282,20 @@ def get_parity_error_identifier(self, field: 'FieldNode') -> str:
path = get_indexed_path(self.top_node, field)
return f"field_combo.{path}.parity_error"

def has_write_once(self, field: 'FieldNode') -> bool:
"""
Returns True if the field is software write once (sw = w1 or rw1)
"""
return field.get_property('sw') in (AccessType.rw1, AccessType.w1)

def get_write_once_identifier(self, field: 'FieldNode') -> str:
""""
Returns the identifier for the write-once lock bit of the field
"""
assert field.implements_storage
path = get_indexed_path(self.top_node, field)
return f"field_storage.{path}.write_once"

def has_next_q(self, field: 'FieldNode') -> bool:
"""
Some fields require a delayed version of their 'next' input signal in
Expand Down
6 changes: 5 additions & 1 deletion src/peakrdl_regblock/field_logic/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ def enter_Field(self, node: 'FieldNode') -> None:
self.add_member("value", node.width)
if node.get_property('paritycheck'):
self.add_member("parity")
if self.field_logic.has_write_once(node):
self.add_member("write_once")

if self.field_logic.has_next_q(node):
self.add_member("next_q", node.width)
Expand Down Expand Up @@ -213,7 +215,7 @@ def exit_Reg(self, node: 'RegNode') -> None:

def generate_field_storage(self, node: 'FieldNode') -> None:
conditionals = self.field_logic.get_conditionals(node)
extra_combo_signals = OrderedDict()
extra_combo_signals: OrderedDict[str, SVLogic] = OrderedDict()
unconditional: Optional[NextStateUnconditional] = None
new_conditionals = []
for conditional in conditionals:
Expand Down Expand Up @@ -252,6 +254,8 @@ def generate_field_storage(self, node: 'FieldNode') -> None:
'conditionals': conditionals,
'unconditional': unconditional,
'resetsignal': resetsignal,
# Write-once lock bit is always reset
'write_once_resetsignal': node.get_property("resetsignal"),
'get_always_ff_event': self.exp.dereferencer.get_always_ff_event,
'get_value': self.exp.dereferencer.get_value,
'get_resetsignal': self.exp.dereferencer.get_resetsignal,
Expand Down
2 changes: 1 addition & 1 deletion src/peakrdl_regblock/field_logic/sw_onread.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from systemrdl.node import FieldNode

class _OnRead(NextStateConditional):
onreadtype = None # type: OnReadType
onreadtype: OnReadType | None = None
def is_match(self, field: 'FieldNode') -> bool:
return field.get_property('onread') == self.onreadtype

Expand Down
38 changes: 23 additions & 15 deletions src/peakrdl_regblock/field_logic/sw_onwrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@

from systemrdl.rdltypes import OnWriteType

from .bases import NextStateConditional
from .bases import NextStateConditional, SVLogic

if TYPE_CHECKING:
from systemrdl.node import FieldNode

# TODO: implement sw=w1 "write once" fields

class _OnWrite(NextStateConditional):
onwritetype: Optional[OnWriteType] = None
Expand All @@ -17,24 +16,30 @@ def is_match(self, field: 'FieldNode') -> bool:
def get_predicate(self, field: 'FieldNode') -> str:
if field.parent.get_property('buffer_writes'):
# Is buffered write. Use alternate strobe
wstrb = self.exp.write_buffering.get_write_strobe(field)

if field.get_property('swwe') or field.get_property('swwel'):
# dereferencer will wrap swwel complement if necessary
qualifier = self.exp.dereferencer.get_field_propref_value(field, 'swwe')
return f"{wstrb} && {qualifier}"

return wstrb
predicate = self.exp.write_buffering.get_write_strobe(field)
else:
# is regular register
strb = self.exp.dereferencer.get_access_strobe(field)
predicate = f"{strb} && decoded_req_is_wr"

if field.get_property('swwe') or field.get_property('swwel'):
# dereferencer will wrap swwel complement if necessary
qualifier = self.exp.dereferencer.get_field_propref_value(field, 'swwe')
predicate += f" && {qualifier}"

if self.exp.field_logic.has_write_once(field):
predicate += f" && {self.exp.field_logic.get_write_once_identifier(field)}"

return predicate

if field.get_property('swwe') or field.get_property('swwel'):
# dereferencer will wrap swwel complement if necessary
qualifier = self.exp.dereferencer.get_field_propref_value(field, 'swwe')
return f"{strb} && decoded_req_is_wr && {qualifier}"
def get_extra_combo_signals(self, field: 'FieldNode') -> List[SVLogic]:
"""Get additional signals that need to be added to the combinatorial logic for this field.

return f"{strb} && decoded_req_is_wr"
- Write-once fields require a write_once_clr signal to be added to the logic.
"""
if self.exp.field_logic.has_write_once(field):
return [SVLogic("write_once_clr", 1, "1'b0")]
return []

def get_assignments(self, field: 'FieldNode') -> List[str]:
accesswidth = field.parent.get_property("accesswidth")
Expand All @@ -52,6 +57,9 @@ def get_assignments(self, field: 'FieldNode') -> List[str]:
f"next_c = {self.get_onwrite_rhs(R, D, S)};",
"load_next_c = '1;",
]
if self.exp.field_logic.has_write_once(field):
write_once_clr = self.exp.field_logic.get_field_combo_identifier(field, "write_once_clr")
lines.append(f"{write_once_clr} = '1;")
return lines

def get_onwrite_rhs(self, reg: str, data: str, strb: str) -> str:
Expand Down
12 changes: 11 additions & 1 deletion src/peakrdl_regblock/field_logic/templates/field_storage.sv
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ always_comb begin
next_c = {{field_logic.get_storage_identifier(node)}};
load_next_c = '0;

{%- for signal in extra_combo_signals %}
{%- for signal in extra_combo_signals.values() %}
{{field_logic.get_field_combo_identifier(node, signal.name)}} = {{signal.default_assignment}};
{%- endfor %}
{% for conditional in conditionals %}
Expand Down Expand Up @@ -84,3 +84,13 @@ always_ff @(posedge clk) begin
{%- endif %}
end
{%- endif %}

{%- if field_logic.has_write_once(node) %}
always_ff {{get_always_ff_event(write_once_resetsignal)}} begin
if({{get_resetsignal(write_once_resetsignal)}}) begin
{{field_logic.get_write_once_identifier(node)}} <= 1'b1;
end else if({{field_logic.get_field_combo_identifier(node, "write_once_clr")}}) begin
{{field_logic.get_write_once_identifier(node)}} <= 1'b0;
end
end
{%- endif %}
Empty file.
87 changes: 87 additions & 0 deletions tests/test_write_once/regblock.rdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
addrmap top {
default regwidth = 8;

///////////////
// basic rw1 //
///////////////
reg {
field {
sw=rw1;
hw=na;
} f[8] = 0x11;
} r1;

/////////////////////////
// w1, observed via hw //
/////////////////////////
reg {
field {
sw=w1; hw=r;
} f[8] = 0x22;
} r2;

//////////////////////////////////////////////////
// locked write does not consume the write-once //
//////////////////////////////////////////////////
reg {
field {
sw=rw1;
hw=na;
swwe;
} f[8] = 0x33;
} r3;

///////////////////////////////
// rw1 without a reset value //
///////////////////////////////
reg {
field {
sw=rw1;
hw=na;
} f[8];
} r4;

//////////////////////////////////////
// rw1 in a write-buffered register //
//////////////////////////////////////
reg {
buffer_writes = true;
field {
sw=rw1;
hw=na;
} f[8] = 0x55;
} r5;

/////////////////////////////////////////////
// rw1 with a register-scoped custom reset //
/////////////////////////////////////////////
reg {
field {
sw=rw1;
hw=na;
} f[8] = 0x66;

signal {
field_reset;
activehigh;
sync;
} my_reset;
} r6;

//////////////////////////////////////////////
// rw1 with an explicitly wired async reset //
//////////////////////////////////////////////
reg {
field {
sw=rw1;
hw=na;
} f[8] = 0x77;
} r7;

signal {
activelow;
async;
} f_areset_n;

r7.f->resetsignal = f_areset_n;
};
Loading