-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Shift storage #709
base: master
Are you sure you want to change the base?
Shift storage #709
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -307,3 +307,100 @@ def _(arg): | |
m.d.comb += write_port.en.eq(arg.mask) | ||
|
||
return m | ||
|
||
|
||
class ShiftStorage(Elaboratable): | ||
"""Ordered storage, which shifts data | ||
|
||
This module implements a storage, which maintain all elements in continuous, ordered array | ||
according to the age of the elements (element inserted earlier will be earlier in the list). | ||
The order is restored after each `delete` operation by shifting all elemets which are older | ||
that the removed one. | ||
|
||
|
||
Attributes | ||
---------- | ||
read: Method | ||
The read method. Accepts an `addr` from which data should be read. | ||
The read response method. Return `data_layout` View which was saved on `addr` at reading | ||
time. The `valid` signal indicates if the `addr` was inside the range. | ||
push_back : Method | ||
Insert the new entry on the end of storage. Ready only if there is a free place. | ||
delate: Method | ||
Remove the element from the `addr`. Shifts the elements to restore the proper order. | ||
update: Method | ||
The update method. Accepts `addr` where data should be saved, `data` in form of `data_layout`. | ||
This method is available if `support_update` is set to `True`. | ||
""" | ||
|
||
def __init__(self, data_layout: MethodLayout, depth: int, support_update: bool = False): | ||
""" | ||
Parameters | ||
---------- | ||
data_layout: LayoutList | ||
The format of structures stored in the ShiftStorage. | ||
depth: int | ||
Number of elements stored in ShiftStorage. | ||
support_update: bool | ||
Indicates if `update` method should be generated. Default: `False` | ||
""" | ||
self.data_layout = from_method_layout(data_layout) | ||
self.depth = depth | ||
self.support_update = support_update | ||
|
||
self._data = Array([Signal(self.data_layout, name=f"cell_{i}") for i in range(self.depth)]) | ||
self._last = Signal(range(self.depth + 1)) # pointer on first empty cell | ||
|
||
self.read = Method(i=[("addr", range(self.depth))], o=[("data", self.data_layout), ("valid", 1)]) | ||
self.push_back = Method(i=[("data", self.data_layout)]) | ||
self.delete = Method(i=[("addr", range(self.depth))]) | ||
if self.support_update: | ||
self.update = Method(i=[("addr", range(self.depth)), ("data", self.data_layout)], o=[("err", 1)]) | ||
|
||
def _generate_shift(self, m: TModule, addr: Value): | ||
for i in range(1, self.depth): | ||
with m.If(addr < i): | ||
m.d.sync += self._data[i - 1].eq(self._data[i]) | ||
|
||
def elaborate(self, platform): | ||
m = TModule() | ||
|
||
last_incr = Signal() | ||
last_decr = Signal() | ||
delete_address = Signal(range(self.depth)) | ||
|
||
@def_method(m, self.read) | ||
def _(addr): | ||
return {"data": self._data[addr], "valid": addr < self._last} | ||
|
||
@def_method(m, self.delete, ready=self._last > 0) | ||
def _(addr): | ||
m.d.top_comb += delete_address.eq(addr) | ||
with m.If(addr < self._last): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the purpose of this |
||
m.d.comb += last_decr.eq(1) | ||
self._generate_shift(m, addr) | ||
|
||
if self.support_update: | ||
|
||
@def_method(m, self.update) | ||
def _(addr, data): | ||
update_req_valid = Signal() | ||
m.d.top_comb += update_req_valid.eq(addr < self._last) | ||
affected_by_delete = Signal() | ||
m.d.top_comb += affected_by_delete.eq(addr > delete_address) | ||
with m.If(update_req_valid & (last_decr.implies(delete_address != addr))): | ||
m.d.sync += self._data[addr - Mux(last_decr, affected_by_delete, 0)].eq(data) | ||
return {"err": ~update_req_valid} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above. If there is no use case, I prefer an assertion to a bit which can be easily ignored. |
||
|
||
@def_method(m, self.push_back, self._last < self.depth) | ||
def _(data): | ||
m.d.sync += self._data[self._last - last_decr].eq(data) | ||
m.d.comb += last_incr.eq(1) | ||
|
||
with m.Switch(Cat(last_incr, last_decr)): | ||
with m.Case(1): | ||
m.d.sync += self._last.eq(self._last + 1) | ||
with m.Case(2): | ||
m.d.sync += self._last.eq(self._last - 1) | ||
|
||
return m |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have an use case for reading invalid addresses?