Add 'rd_swacc' and 'wr_swacc' UDPs. #21
This commit is contained in:
@@ -123,3 +123,4 @@ Links
|
||||
udps/intro
|
||||
udps/read_buffering
|
||||
udps/write_buffering
|
||||
udps/extended_swacc
|
||||
|
||||
47
docs/udps/extended_swacc.rst
Normal file
47
docs/udps/extended_swacc.rst
Normal file
@@ -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.."}
|
||||
]}
|
||||
@@ -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`.
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "0.8.0"
|
||||
__version__ = "0.9.0"
|
||||
|
||||
@@ -161,6 +161,10 @@ class Dereferencer:
|
||||
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
|
||||
|
||||
@@ -193,6 +193,30 @@ class FieldLogic:
|
||||
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
|
||||
|
||||
@@ -224,20 +224,33 @@ class FieldLogicGenerator(RDLForLoopGenerator):
|
||||
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')
|
||||
@@ -251,6 +264,7 @@ class FieldLogicGenerator(RDLForLoopGenerator):
|
||||
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')
|
||||
|
||||
@@ -190,6 +190,7 @@ class Hwif:
|
||||
assert prop in {
|
||||
"anded", "ored", "xored", "swmod", "swacc",
|
||||
"incrthreshold", "decrthreshold", "overflow", "underflow",
|
||||
"rd_swacc", "wr_swacc",
|
||||
}
|
||||
elif isinstance(node, RegNode):
|
||||
assert prop in {
|
||||
|
||||
@@ -128,7 +128,7 @@ class OutputStructGenerator_Hier(HWIFStructGenerator):
|
||||
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)
|
||||
|
||||
|
||||
12
src/peakrdl_regblock/udps/__init__.py
Normal file
12
src/peakrdl_regblock/udps/__init__.py
Normal file
@@ -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
src/peakrdl_regblock/udps/extended_swacc.py
Normal file
23
src/peakrdl_regblock/udps/extended_swacc.py
Normal file
@@ -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
|
||||
@@ -118,12 +118,3 @@ class RBufferTrigger(xBufferTrigger):
|
||||
if node.get_property('buffer_reads'):
|
||||
return node
|
||||
return None
|
||||
|
||||
|
||||
|
||||
ALL_UDPS = [
|
||||
BufferWrites,
|
||||
WBufferTrigger,
|
||||
BufferReads,
|
||||
RBufferTrigger,
|
||||
]
|
||||
0
tests/test_extended_swacc_swmod/__init__.py
Normal file
0
tests/test_extended_swacc_swmod/__init__.py
Normal file
17
tests/test_extended_swacc_swmod/regblock.rdl
Normal file
17
tests/test_extended_swacc_swmod/regblock.rdl
Normal file
@@ -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
tests/test_extended_swacc_swmod/tb_template.sv
Normal file
68
tests/test_extended_swacc_swmod/tb_template.sv
Normal file
@@ -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
tests/test_extended_swacc_swmod/testcase.py
Normal file
5
tests/test_extended_swacc_swmod/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