Implement write buffering (#22)

This commit is contained in:
Alex Mykyta
2022-10-24 21:49:47 -07:00
parent 808067fac9
commit 279a3c5788
29 changed files with 968 additions and 93 deletions

View File

@@ -0,0 +1,80 @@
from typing import TYPE_CHECKING, Union
from systemrdl.node import AddrmapNode, RegNode, FieldNode, SignalNode
from .storage_generator import WBufStorageStructGenerator
from .implementation_generator import WBufLogicGenerator
from ..utils import get_indexed_path
if TYPE_CHECKING:
from ..exporter import RegblockExporter
class WriteBuffering:
def __init__(self, exp:'RegblockExporter'):
self.exp = exp
@property
def top_node(self) -> 'AddrmapNode':
return self.exp.top_node
def get_storage_struct(self) -> str:
struct_gen = WBufStorageStructGenerator(self)
s = struct_gen.get_struct(self.top_node, "wbuf_storage_t")
assert s is not None
return s + "\nwbuf_storage_t wbuf_storage;"
def get_implementation(self) -> str:
gen = WBufLogicGenerator(self)
s = gen.get_content(self.top_node)
assert s is not None
return s
def get_wbuf_prefix(self, node: Union[RegNode, FieldNode]) -> str:
if isinstance(node, FieldNode):
node = node.parent
wbuf_prefix = "wbuf_storage." + get_indexed_path(self.top_node, node)
return wbuf_prefix
def get_write_strobe(self, node: Union[RegNode, FieldNode]) -> str:
prefix = self.get_wbuf_prefix(node)
return f"{prefix}.pending && {self.get_trigger(node)}"
def get_raw_trigger(self, node: 'RegNode') -> str:
trigger = node.get_property('wbuffer_trigger')
if isinstance(trigger, RegNode):
# Trigger is a register.
# trigger when uppermost address of the register is written
regwidth = node.get_property('regwidth')
accesswidth = node.get_property('accesswidth')
strb_prefix = self.exp.dereferencer.get_access_strobe(trigger, reduce_substrobes=False)
if accesswidth < regwidth:
n_subwords = regwidth // accesswidth
return f"{strb_prefix}[{n_subwords-1}] && decoded_req_is_wr"
else:
return f"{strb_prefix} && decoded_req_is_wr"
elif isinstance(trigger, SignalNode):
s = self.exp.dereferencer.get_value(trigger)
if trigger.get_property('activehigh'):
return s
else:
return f"~{s}"
else:
# Trigger is a field or propref bit
return self.exp.dereferencer.get_value(trigger)
def get_trigger(self, node: Union[RegNode, FieldNode]) -> str:
if isinstance(node, FieldNode):
node = node.parent
trigger = node.get_property('wbuffer_trigger')
if isinstance(trigger, RegNode) and trigger == node:
# register is its own trigger
# use the delayed trigger signal
return self.get_wbuf_prefix(node) + ".trigger_q"
else:
return self.get_raw_trigger(node)

View File

@@ -0,0 +1,60 @@
from typing import TYPE_CHECKING
from collections import namedtuple
from systemrdl.component import Reg
from systemrdl.node import RegNode
from ..forloop_generator import RDLForLoopGenerator
from ..utils import get_always_ff_event
if TYPE_CHECKING:
from . import WriteBuffering
class WBufLogicGenerator(RDLForLoopGenerator):
i_type = "genvar"
def __init__(self, wbuf: 'WriteBuffering') -> None:
super().__init__()
self.wbuf = wbuf
self.exp = wbuf.exp
self.template = self.exp.jj_env.get_template(
"write_buffering/template.sv"
)
def enter_Reg(self, node: 'RegNode') -> None:
super().enter_Reg(node)
assert isinstance(node.inst, Reg)
if not node.get_property('buffer_writes'):
return
regwidth = node.get_property('regwidth')
accesswidth = node.get_property('accesswidth')
strb_prefix = self.exp.dereferencer.get_access_strobe(node, reduce_substrobes=False)
Segment = namedtuple("Segment", ["strobe", "bslice"])
segments = []
if accesswidth < regwidth:
n_subwords = regwidth // accesswidth
for i in range(n_subwords):
strobe = strb_prefix + f"[{i}]"
if node.inst.is_msb0_order:
bslice = f"[{regwidth - (accesswidth * i) - 1}: {regwidth - (accesswidth * (i+1))}]"
else:
bslice = f"[{(accesswidth * (i + 1)) - 1}:{accesswidth * i}]"
segments.append(Segment(strobe, bslice))
else:
segments.append(Segment(strb_prefix, ""))
trigger = node.get_property('wbuffer_trigger')
is_own_trigger = (isinstance(trigger, RegNode) and trigger == node)
context = {
'wbuf': self.wbuf,
'wbuf_prefix': self.wbuf.get_wbuf_prefix(node),
'segments': segments,
'node': node,
'cpuif': self.exp.cpuif,
'get_resetsignal': self.exp.dereferencer.get_resetsignal,
'get_always_ff_event': lambda resetsignal : get_always_ff_event(self.exp.dereferencer, resetsignal),
'is_own_trigger': is_own_trigger,
}
self.add_content(self.template.render(context))

View File

@@ -0,0 +1,32 @@
from typing import TYPE_CHECKING
from systemrdl.node import FieldNode, RegNode
from ..struct_generator import RDLStructGenerator
if TYPE_CHECKING:
from . import WriteBuffering
class WBufStorageStructGenerator(RDLStructGenerator):
def __init__(self, wbuf: 'WriteBuffering') -> None:
super().__init__()
self.wbuf = wbuf
def enter_Field(self, node: FieldNode) -> None:
# suppress parent class's field behavior
pass
def enter_Reg(self, node: RegNode) -> None:
super().enter_Reg(node)
if not node.get_property('buffer_writes'):
return
regwidth = node.get_property('regwidth')
self.add_member("data", regwidth)
self.add_member("biten", regwidth)
self.add_member("pending")
trigger = node.get_property('wbuffer_trigger')
if isinstance(trigger, RegNode) and trigger == node:
self.add_member("trigger_q")

View File

@@ -0,0 +1,31 @@
always_ff {{get_always_ff_event(cpuif.reset)}} begin
if({{get_resetsignal(cpuif.reset)}}) begin
{{wbuf_prefix}}.pending <= '0;
{{wbuf_prefix}}.data <= '0;
{{wbuf_prefix}}.biten <= '0;
{%- if is_own_trigger %}
{{wbuf_prefix}}.trigger_q <= '0;
{%- endif %}
end else begin
{%- for segment in segments %}
if({{segment.strobe}} && decoded_req_is_wr) begin
{{wbuf_prefix}}.pending <= '1;
{%- if node.inst.is_msb0_order %}
{{wbuf_prefix}}.data{{segment.bslice}} <= decoded_wr_data_bswap;
{{wbuf_prefix}}.biten{{segment.bslice}} <= decoded_wr_biten_bswap;
{%- else %}
{{wbuf_prefix}}.data{{segment.bslice}} <= decoded_wr_data;
{{wbuf_prefix}}.biten{{segment.bslice}} <= decoded_wr_biten;
{%- endif %}
end
{%- endfor %}
{% if is_own_trigger %}
{{wbuf_prefix}}.trigger_q <= {{wbuf.get_raw_trigger(node)}};
{%- endif %}
if({{wbuf.get_trigger(node)}}) begin
{{wbuf_prefix}}.pending <= '0;
{{wbuf_prefix}}.data <= '0;
{{wbuf_prefix}}.biten <= '0;
end
end
end