Skip to content

Commit

Permalink
Add 'rd_swacc' and 'wr_swacc' UDPs. #21
Browse files Browse the repository at this point in the history
  • Loading branch information
amykyta3 committed Nov 10, 2022
1 parent 17afaf1 commit 489b4d1
Show file tree
Hide file tree
Showing 17 changed files with 243 additions and 12 deletions.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,4 @@ Links
udps/intro
udps/read_buffering
udps/write_buffering
udps/extended_swacc
47 changes: 47 additions & 0 deletions docs/udps/extended_swacc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
.. _extended_swacc:

Read/Write-specific swacc
=========================

SystemRDL defines the ``swacc`` property, but it does not distinguish between
read and write operations - it is asserted on *all* software accesses.
Similarly, the spec defines ``swmod`` which gets asserted on software writes,
but can also get asserted if the field has on-read side-effects.

What if you just wanted a plan and simple strobe that is asserted when software
reads or writes a field? The ``rd_swacc`` and ``wr_swacc`` UDPs provide this
functionality.


Properties
----------
These UDP definitions, along with others supported by PeakRDL-regblock can be
enabled by compiling the following file along with your design:
:download:`regblock_udps.rdl <../../hdl-src/regblock_udps.rdl>`.

``rd_swacc``
If true, infers an output signal ``hwif_out..rd_swacc`` that is asserted
when accessed by a software read operation. The output signal is asserted
on the same clock cycle that the field is being sampled during the software
read operation.

.. wavedrom::

{"signal": [
{"name": "clk", "wave": "p...."},
{"name": "hwif_in..next", "wave": "x.=x.", "data": ["D"]},
{"name": "hwif_out..rd_swacc", "wave": "0.10."}
]}


``wr_swacc``
If true, infers an output signal ``hwif_out..wr_swacc`` that is asserted
as the field is being modified by a software write operation.

.. wavedrom::

{"signal": [
{"name": "clk", "wave": "p....."},
{"name": "hwif_out..value", "wave": "=..=..", "data": ["old", "new"]},
{"name": "hwif_out..wr_swacc", "wave": "0.10.."}
]}
14 changes: 14 additions & 0 deletions docs/udps/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,17 @@ To enable these UDPs, compile this RDL file prior to the rest of your design:
- Defines the buffered write commit trigger.

See: :ref:`write_buffering`.

* - rd_swacc
- field
- boolean
- Enables an output strobe that is asserted on sw reads.

See: :ref:`extended_swacc`.

* - wr_swacc
- field
- boolean
- Enables an output strobe that is asserted on sw writes.

See: :ref:`extended_swacc`.
10 changes: 10 additions & 0 deletions hdl-src/regblock_udps.rdl
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,13 @@ property wbuffer_trigger {
component = reg;
type = ref;
};

property rd_swacc {
component = field;
type = boolean;
};

property wr_swacc {
component = field;
type = boolean;
};
2 changes: 1 addition & 1 deletion src/peakrdl_regblock/__about__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.8.0"
__version__ = "0.9.0"
4 changes: 4 additions & 0 deletions src/peakrdl_regblock/dereferencer.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ def get_field_propref_value(self, field: FieldNode, prop_name: str) -> str:
return self.field_logic.get_swacc_identifier(field)
if prop_name == "swmod":
return self.field_logic.get_swmod_identifier(field)
if prop_name == "rd_swacc":
return self.field_logic.get_rd_swacc_identifier(field)
if prop_name == "wr_swacc":
return self.field_logic.get_wr_swacc_identifier(field)


# translate aliases
Expand Down
24 changes: 24 additions & 0 deletions src/peakrdl_regblock/field_logic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,30 @@ def get_swacc_identifier(self, field: 'FieldNode') -> str:
strb = self.exp.dereferencer.get_access_strobe(field)
return strb

def get_rd_swacc_identifier(self, field: 'FieldNode') -> str:
"""
Asserted when field is software accessed (read)
"""
buffer_reads = field.parent.get_property('buffer_reads')
if buffer_reads:
rstrb = self.exp.read_buffering.get_trigger(field.parent)
return rstrb
else:
strb = self.exp.dereferencer.get_access_strobe(field)
return f"{strb} && !decoded_req_is_wr"

def get_wr_swacc_identifier(self, field: 'FieldNode') -> str:
"""
Asserted when field is software accessed (write)
"""
buffer_writes = field.parent.get_property('buffer_writes')
if buffer_writes:
wstrb = self.exp.write_buffering.get_write_strobe(field)
return wstrb
else:
strb = self.exp.dereferencer.get_access_strobe(field)
return f"{strb} && decoded_req_is_wr"

def get_swmod_identifier(self, field: 'FieldNode') -> str:
"""
Asserted when field is modified by software (written or read with a
Expand Down
16 changes: 15 additions & 1 deletion src/peakrdl_regblock/field_logic/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,20 +224,33 @@ def assign_field_outputs(self, node: 'FieldNode') -> None:
f"assign {output_identifier} = {value};"
)

# Software access strobes
if node.get_property('swmod'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "swmod")
value = self.field_logic.get_swmod_identifier(node)
self.add_content(
f"assign {output_identifier} = {value};"
)

if node.get_property('swacc'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "swacc")
value = self.field_logic.get_swacc_identifier(node)
self.add_content(
f"assign {output_identifier} = {value};"
)
if node.get_property('rd_swacc'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "rd_swacc")
value = self.field_logic.get_rd_swacc_identifier(node)
self.add_content(
f"assign {output_identifier} = {value};"
)
if node.get_property('wr_swacc'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "wr_swacc")
value = self.field_logic.get_wr_swacc_identifier(node)
self.add_content(
f"assign {output_identifier} = {value};"
)

# Counter thresholds
if node.get_property('incrthreshold') is not False: # (explicitly not False. Not 0)
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "incrthreshold")
value = self.field_logic.get_field_combo_identifier(node, 'incrthreshold')
Expand All @@ -251,6 +264,7 @@ def assign_field_outputs(self, node: 'FieldNode') -> None:
f"assign {output_identifier} = {value};"
)

# Counter events
if node.get_property('overflow'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "overflow")
value = self.field_logic.get_field_combo_identifier(node, 'overflow')
Expand Down
1 change: 1 addition & 0 deletions src/peakrdl_regblock/hwif/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ def get_implied_prop_output_identifier(self, node: Union[FieldNode, RegNode], pr
assert prop in {
"anded", "ored", "xored", "swmod", "swacc",
"incrthreshold", "decrthreshold", "overflow", "underflow",
"rd_swacc", "wr_swacc",
}
elif isinstance(node, RegNode):
assert prop in {
Expand Down
2 changes: 1 addition & 1 deletion src/peakrdl_regblock/hwif/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def enter_Field(self, node: 'FieldNode') -> None:
self.add_member("value", node.width)

# Generate output bit signals enabled via property
for prop_name in ["anded", "ored", "xored", "swmod", "swacc", "overflow", "underflow"]:
for prop_name in ["anded", "ored", "xored", "swmod", "swacc", "overflow", "underflow", "rd_swacc", "wr_swacc"]:
if node.get_property(prop_name):
self.add_member(prop_name)

Expand Down
12 changes: 12 additions & 0 deletions src/peakrdl_regblock/udps/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from .rw_buffering import BufferWrites, WBufferTrigger
from .rw_buffering import BufferReads, RBufferTrigger
from .extended_swacc import ReadSwacc, WriteSwacc

ALL_UDPS = [
BufferWrites,
WBufferTrigger,
BufferReads,
RBufferTrigger,
ReadSwacc,
WriteSwacc,
]
23 changes: 23 additions & 0 deletions src/peakrdl_regblock/udps/extended_swacc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from typing import TYPE_CHECKING, Any

from systemrdl.udp import UDPDefinition
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:
return False

class WriteSwacc(UDPDefinition):
name = "wr_swacc"
valid_components = {Field}
valid_type = bool

def get_unassigned_default(self, node: 'Node') -> Any:
return False
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,3 @@ def get_unassigned_default(self, node: 'Node') -> Any:
if node.get_property('buffer_reads'):
return node
return None



ALL_UDPS = [
BufferWrites,
WBufferTrigger,
BufferReads,
RBufferTrigger,
]
Empty file.
17 changes: 17 additions & 0 deletions tests/test_extended_swacc_swmod/regblock.rdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
addrmap top {
default regwidth = 8;

reg {
field {
sw=r; hw=w;
rd_swacc;
} f[8];
} r1;

reg {
field {
sw=rw; hw=r;
wr_swacc;
} f[8] = 20;
} r2;
};
68 changes: 68 additions & 0 deletions tests/test_extended_swacc_swmod/tb_template.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{% extends "lib/tb_base.sv" %}

{% block seq %}
{% sv_line_anchor %}
logic [7:0] counter;
logic [7:0] rd_data;
logic [7:0] latched_data;
int event_count;
latched_data = 'x;

##1;
cb.rst <= '0;
##1;

// Verify that hwif gets sampled at the same cycle as rd_swacc strobe
counter = 'h10;
cb.hwif_in.r1.f.next <= counter;
@cb;
event_count = 0;
fork
begin
##0;
forever begin
counter++;
cb.hwif_in.r1.f.next <= counter;
@cb;
if(cb.hwif_out.r1.f.rd_swacc) begin
latched_data = counter;
event_count++;
end
end
end

begin
cpuif.read('h0, rd_data);
@cb;
end
join_any
disable fork;
assert(rd_data == latched_data) else $error("Read returned 0x%0x but rd_swacc strobed during 0x%0x", rd_data, latched_data);
assert(event_count == 1) else $error("Observed excess rd_swacc events: %0d", event_count);


// Verify that hwif changes 1 cycle after wr_swacc
fork
begin
##0;
forever begin
assert(cb.hwif_out.r2.f.value == 20);
if(cb.hwif_out.r2.f.wr_swacc) break;
@cb;
end
@cb;
forever begin
assert(cb.hwif_out.r2.f.value == 21);
assert(cb.hwif_out.r2.f.wr_swacc == 0);
@cb;
end
end

begin
cpuif.write('h1, 21);
@cb;
end
join_any
disable fork;

{% endblock %}
5 changes: 5 additions & 0 deletions tests/test_extended_swacc_swmod/testcase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from ..lib.sim_testcase import SimTestCase

class Test(SimTestCase):
def test_dut(self):
self.run_test()

0 comments on commit 489b4d1

Please sign in to comment.