Skip to content

Commit

Permalink
Add a component for accessing the neighbour lookup
Browse files Browse the repository at this point in the history
I wrote this a couple months ago but never finished self-reviewing it
before committing. Untested.
  • Loading branch information
Liamolucko committed Aug 20, 2024
1 parent a266b4c commit c342899
Show file tree
Hide file tree
Showing 4 changed files with 460 additions and 0 deletions.
106 changes: 106 additions & 0 deletions fpga/core/base_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import math

from amaranth import *
from amaranth.hdl import ValueLike
from amaranth.lib import data
from amaranth.utils import ceil_log2


def net_size(max_area: int):
"""Returns the width/height of the net."""

# Round it up to the nearest multiple of 4, since our scheme for splitting the
# net up into 4 shards requires being able to divide it up into 4x4 tiles.
return int(4 * math.ceil(max_area / 4))


class PosLayout(data.StructLayout):
def __init__(self, max_area: int):
super().__init__(
{
"x": ceil_log2(net_size(max_area)),
"y": ceil_log2(net_size(max_area)),
}
)

self._net_size = net_size(max_area)

def __call__(self, value):
return PosView(self, value)


class PosView(data.View):
def moved_in(self, m: Module, direction: ValueLike):
# src_loc_at tells Signal how far up in the call chain to look for what to name
# the signal: so, setting it to 1 means we want it to use the name of the
# variable the caller's assigning our result to.
output = Signal(self.shape(), src_loc_at=1)

unchecked_x = Signal(self.x.shape())
unchecked_y = Signal(self.y.shape())

with m.Switch(direction):
with m.Case(0):
m.d.comb += unchecked_x.eq(self.x - 1)
m.d.comb += unchecked_y.eq(self.y)
with m.Case(1):
m.d.comb += unchecked_x.eq(self.x)
m.d.comb += unchecked_y.eq(self.y + 1)
with m.Case(2):
m.d.comb += unchecked_x.eq(self.x + 1)
m.d.comb += unchecked_y.eq(self.y)
with m.Case(3):
m.d.comb += unchecked_x.eq(self.x)
m.d.comb += unchecked_y.eq(self.y - 1)

# This probably isn't really necessary since `net_size` is now always a power of
# 2 (due to `ChunkedMemory` requiring that), and the natural wrapping already
# does what we want, but it doesn't hurt to include this in case that changes.
#
# It's done as a postprocessing step like this to make it extremely obvious to
# the synthesiser that no extra logic is needed in the power-of-2 case: when it
# sees unchecked_x == 0 ? 0 : unchecked_x == 0x3f ? 0x3f : unchecked_x, it can
# easily optimise that down to just unchecked_x.
net_size = self.shape()._net_size
coord_underflow = (1 << self.x.shape().width) - 1
coord_overflow = net_size & coord_underflow

with m.Switch(unchecked_x):
with m.Case(coord_overflow):
m.d.comb += output.x.eq(0)
with m.Case(coord_underflow):
m.d.comb += output.x.eq(net_size - 1)
with m.Default():
m.d.comb += output.x.eq(unchecked_x)

with m.Switch(unchecked_y):
with m.Case(coord_overflow):
m.d.comb += output.y.eq(0)
with m.Case(coord_underflow):
m.d.comb += output.y.eq(net_size - 1)
with m.Default():
m.d.comb += output.y.eq(unchecked_y)

return output


def cursor_layout(max_area: int):
return data.StructLayout(
{
"square": ceil_log2(max_area),
"orientation": 2,
}
)


def mapping_layout(cuboids: int, max_area: int):
return data.ArrayLayout(cursor_layout(max_area), cuboids)


def instruction_layout(cuboids: int, max_area: int):
return data.StructLayout(
{
"pos": PosLayout(max_area),
"mapping": mapping_layout(cuboids, max_area),
}
)
2 changes: 2 additions & 0 deletions fpga/memory.py → fpga/core/memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def elaborate(self, platform) -> Module:
addr = Mux(inner_write_port.en, write_port.addr, read_port.addr)
m.d.comb += inner_write_port.addr.eq(addr[:chunk_addr_width])
m.d.comb += inner_read_port.addr.eq(addr[:chunk_addr_width])
# TODO: handle read_port.en

inner_sdp_read_ports[port_index].append(inner_read_port)

Expand Down Expand Up @@ -197,5 +198,6 @@ def elaborate(self, platform) -> Module:
m.d.comb += inner_read_port.addr.eq(read_port.addr)

m.d.comb += read_port.data.eq(inner_read_port.data)
# TODO: handle read_port.en

return m
Loading

0 comments on commit c342899

Please sign in to comment.