Skip to content

Commit

Permalink
[ESI] MMIO: add read/write port so service (#7407)
Browse files Browse the repository at this point in the history
Replaces write with read_write.
  • Loading branch information
teqdruid authored Jul 31, 2024
1 parent 41841eb commit 05136f0
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 75 deletions.
39 changes: 36 additions & 3 deletions frontends/PyCDE/integration_test/esi_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
import pycde
from pycde import (AppID, Clock, Module, Reset, modparams, generator)
from pycde.bsp import cosim
from pycde.constructs import Wire
from pycde.esi import FuncService, MMIO
from pycde.constructs import Reg, Wire
from pycde.esi import FuncService, MMIO, MMIOReadWriteCmdType
from pycde.types import (Bits, Channel, UInt)
from pycde.behavioral import If, Else, EndIf

import sys

Expand Down Expand Up @@ -48,7 +49,7 @@ def build(ports):

address_chan_wire = Wire(Channel(UInt(32)))
address, address_valid = address_chan_wire.unwrap(1)
response_data = (address.as_uint() + add_amt).as_bits(64)
response_data = (address + add_amt).as_bits(64)
response_chan, response_ready = Channel(Bits(64)).wrap(
response_data, address_valid)

Expand All @@ -58,6 +59,37 @@ def build(ports):
return MMIOClient


class MMIOReadWriteClient(Module):
clk = Clock()
rst = Reset()

@generator
def build(ports):
mmio_read_write_bundle = MMIO.read_write(appid=AppID("mmio_rw_client"))

cmd_chan_wire = Wire(Channel(MMIOReadWriteCmdType))
resp_ready_wire = Wire(Bits(1))
cmd, cmd_valid = cmd_chan_wire.unwrap(resp_ready_wire)

add_amt = Reg(UInt(64),
clk=ports.clk,
rst=ports.rst,
rst_value=0,
ce=cmd_valid & cmd.write & (cmd.offset == 0x8).as_bits())
add_amt.assign(cmd.data.as_uint())
with If(cmd.write):
response_data = Bits(64)(0)
with Else():
response_data = (cmd.offset + add_amt).as_bits(64)
EndIf()
response_chan, response_ready = Channel(Bits(64)).wrap(
response_data, cmd_valid)
resp_ready_wire.assign(response_ready)

cmd_chan = mmio_read_write_bundle.unpack(data=response_chan)['cmd']
cmd_chan_wire.assign(cmd_chan)


class Top(Module):
clk = Clock()
rst = Reset()
Expand All @@ -67,6 +99,7 @@ def construct(ports):
LoopbackInOutAdd7(clk=ports.clk, rst=ports.rst)
for i in range(4, 18, 5):
MMIOClient(i)()
MMIOReadWriteClient(clk=ports.clk, rst=ports.rst)


if __name__ == "__main__":
Expand Down
20 changes: 20 additions & 0 deletions frontends/PyCDE/integration_test/test_software/esi_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,26 @@ def read_offset(mmio_offset: int, offset: int, add_amt: int):
read_offset(mmio_client_14_offset, 0, 14)
read_offset(mmio_client_14_offset, 13, 14)

################################################################################
# MMIOReadWriteClient tests
################################################################################

mmio_rw_client_offset = 262144


def read_offset_check(i: int, add_amt: int):
d = mmio.read(mmio_rw_client_offset + i)
if d == i + 9:
print(f"PASS: read_offset_check({mmio_rw_client_offset} + {i}: {d}")
else:
assert False, f": read_offset_check({mmio_rw_client_offset} + {i}: {d}"


mmio.write(mmio_rw_client_offset + 8, 9)
read_offset_check(0, 9)
read_offset_check(12, 9)
read_offset_check(0x1400, 9)

################################################################################
# Manifest tests
################################################################################
Expand Down
92 changes: 50 additions & 42 deletions frontends/PyCDE/src/pycde/bsp/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class ChannelMMIO(esi.ServiceImplementation):
clk = Clock()
rst = Input(Bits(1))

read = Input(esi.MMIO.read.type)
cmd = Input(esi.MMIOReadWriteCmdType)

# Amount of register space each client gets. This is a GIANT HACK and needs to
# be replaced by parameterizable services.
Expand All @@ -140,70 +140,75 @@ class ChannelMMIO(esi.ServiceImplementation):

@generator
def generate(ports, bundles: esi._ServiceGeneratorBundles):
read_table, write_table, manifest_loc = ChannelMMIO.build_table(
ports, bundles)
ChannelMMIO.build_read(ports, manifest_loc, read_table)
ChannelMMIO.build_write(ports, write_table)
table, manifest_loc = ChannelMMIO.build_table(bundles)
ChannelMMIO.build_read(ports, manifest_loc, table)
return True

@staticmethod
def build_table(
ports, bundles
) -> Tuple[Dict[int, AssignableSignal], Dict[int, AssignableSignal], int]:
def build_table(bundles) -> Tuple[Dict[int, AssignableSignal], int]:
"""Build a table of read and write addresses to BundleSignals."""
offset = ChannelMMIO.initial_offset
read_table: Dict[int, AssignableSignal] = {}
write_table: Dict[int, AssignableSignal] = {}
table: Dict[int, AssignableSignal] = {}
for bundle in bundles.to_client_reqs:
if bundle.port == 'read':
read_table[offset] = bundle
bundle.add_record({"offset": offset})
table[offset] = bundle
bundle.add_record({"offset": offset, "type": "ro"})
offset += ChannelMMIO.RegisterSpace
elif bundle.port == 'read_write':
table[offset] = bundle
bundle.add_record({"offset": offset, "type": "rw"})
offset += ChannelMMIO.RegisterSpace
else:
assert False, "Unrecognized port name."

manifest_loc = offset
return read_table, write_table, manifest_loc
return table, manifest_loc

@staticmethod
def build_read(ports, manifest_loc: int, read_table: Dict[int,
AssignableSignal]):
def build_read(ports, manifest_loc: int, table: Dict[int, AssignableSignal]):
"""Builds the read side of the MMIO service."""

# Instantiate the header and manifest ROM. Fill in the read_table with
# bundle wires to be assigned identically to the other MMIO clients.
header_bundle_wire = Wire(esi.MMIO.read.type)
read_table[0] = header_bundle_wire
table[0] = header_bundle_wire
HeaderMMIO(manifest_loc)(clk=ports.clk,
rst=ports.rst,
read=header_bundle_wire)

mani_bundle_wire = Wire(esi.MMIO.read.type)
read_table[manifest_loc] = mani_bundle_wire
table[manifest_loc] = mani_bundle_wire
ESI_Manifest_ROM_Wrapper(clk=ports.clk, read=mani_bundle_wire)

# Unpack the read bundle.
# Unpack the cmd bundle.
data_resp_channel = Wire(Channel(esi.MMIODataType))
counted_output = Wire(Channel(esi.MMIODataType))
read_addr_channel = ports.read.unpack(data=counted_output)["offset"]
cmd_channel = ports.cmd.unpack(data=counted_output)["cmd"]
counted_output.assign(data_resp_channel)

# Get the selection index and the address to hand off to the clients.
sel_bits, client_address_chan = ChannelMMIO.build_addr_read(
read_addr_channel)
sel_bits, client_cmd_chan = ChannelMMIO.build_addr_read(cmd_channel)

# Build the demux/mux and assign the results of each appropriately.
read_clients_clog2 = clog2(len(read_table))
client_addr_channels = esi.ChannelDemux(
read_clients_clog2 = clog2(len(table))
client_cmd_channels = esi.ChannelDemux(
sel=sel_bits.pad_or_truncate(read_clients_clog2),
input=client_address_chan,
num_outs=len(read_table))
input=client_cmd_chan,
num_outs=len(table))
client_data_channels = []
for (idx, offset) in enumerate(sorted(read_table.keys())):
bundle, bundle_froms = esi.MMIO.read.type.pack(
offset=client_addr_channels[idx])
for (idx, offset) in enumerate(sorted(table.keys())):
bundle_wire = table[offset]
bundle_type = bundle_wire.type
if bundle_type == esi.MMIO.read.type:
offset = client_cmd_channels[idx].transform(lambda cmd: cmd.offset)
bundle, bundle_froms = esi.MMIO.read.type.pack(offset=offset)
elif bundle_type == esi.MMIO.read_write.type:
bundle, bundle_froms = esi.MMIO.read_write.type.pack(
cmd=client_cmd_channels[idx])
else:
assert False, "Unrecognized bundle type."
bundle_wire.assign(bundle)
client_data_channels.append(bundle_froms["data"])
read_table[offset].assign(bundle)
resp_channel = esi.ChannelMux(client_data_channels)
data_resp_channel.assign(resp_channel)

Expand All @@ -218,18 +223,21 @@ def build_addr_read(
# change to support more flexibility in addressing. Not clear if what we're
# doing now it sufficient or not.

addr_ready_wire = Wire(Bits(1))
addr, addr_valid = read_addr_chan.unwrap(addr_ready_wire)
addr = addr.as_bits()
cmd_ready_wire = Wire(Bits(1))
cmd, cmd_valid = read_addr_chan.unwrap(cmd_ready_wire)
sel_bits = NamedWire(Bits(32 - ChannelMMIO.RegisterSpaceBits), "sel_bits")
sel_bits.assign(addr[ChannelMMIO.RegisterSpaceBits:])
client_addr = NamedWire(Bits(32), "client_addr")
client_addr.assign(addr & Bits(32)(ChannelMMIO.AddressMask))
client_addr_chan, client_addr_ready = Channel(UInt(32)).wrap(
client_addr.as_uint(), addr_valid)
addr_ready_wire.assign(client_addr_ready)
sel_bits.assign(cmd.offset.as_bits()[ChannelMMIO.RegisterSpaceBits:])
client_cmd = NamedWire(esi.MMIOReadWriteCmdType, "client_cmd")
client_cmd.assign(
esi.MMIOReadWriteCmdType({
"write":
cmd.write,
"offset": (cmd.offset.as_bits() &
Bits(32)(ChannelMMIO.AddressMask)).as_uint(),
"data":
cmd.data
}))
client_addr_chan, client_addr_ready = Channel(
esi.MMIOReadWriteCmdType).wrap(client_cmd, cmd_valid)
cmd_ready_wire.assign(client_addr_ready)
return sel_bits, client_addr_chan

def build_write(self, bundles):
# TODO: this.
pass
6 changes: 3 additions & 3 deletions frontends/PyCDE/src/pycde/bsp/cosim.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ class ESI_Cosim_UserTopWrapper(Module):
def build(ports):
user_module(clk=ports.clk, rst=ports.rst)

mmio_read = esi.FuncService.get_coerced(esi.AppID("__cosim_mmio_read"),
esi.MMIO.read.type)
mmio_read_write = esi.FuncService.get_coerced(
esi.AppID("__cosim_mmio_read_write"), esi.MMIO.read_write.type)
ChannelMMIO(esi.MMIO,
appid=esi.AppID("__cosim_mmio"),
clk=ports.clk,
rst=ports.rst,
read=mmio_read)
cmd=mmio_read_write)

class ESI_Cosim_Top(Module):
clk = Clock()
Expand Down
12 changes: 11 additions & 1 deletion frontends/PyCDE/src/pycde/esi.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from .support import get_user_loc
from .system import System
from .types import (Bits, Bundle, BundledChannel, Channel, ChannelDirection,
Type, UInt, types, _FromCirctType)
StructType, Type, UInt, types, _FromCirctType)

from .circt import ir
from .circt.dialects import esi as raw_esi, hw, msft
Expand Down Expand Up @@ -469,6 +469,11 @@ def param(name: str, type: Type = None):


MMIODataType = Bits(64)
MMIOReadWriteCmdType = StructType([
("write", Bits(1)),
("offset", UInt(32)),
("data", MMIODataType),
])


@ServiceDecl
Expand All @@ -482,6 +487,11 @@ class MMIO:
BundledChannel("data", ChannelDirection.FROM, MMIODataType)
])

read_write = Bundle([
BundledChannel("cmd", ChannelDirection.TO, MMIOReadWriteCmdType),
BundledChannel("data", ChannelDirection.FROM, MMIODataType)
])

@staticmethod
def _op(sym_name: ir.StringAttr):
return raw_esi.MMIOServiceDeclOp(sym_name)
Expand Down
22 changes: 17 additions & 5 deletions lib/Dialect/ESI/ESIStdServices.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,26 @@ void MMIOServiceDeclOp::getPortList(SmallVectorImpl<ServicePortInfo> &ports) {
BundledChannel{StringAttr::get(ctxt, "data"), ChannelDirection::from,
ChannelType::get(ctxt, IntegerType::get(ctxt, 64))}},
/*resettable=*/UnitAttr())});
// Write only port.
// Read-write port.
auto cmdType = hw::StructType::get(
ctxt, {
hw::StructType::FieldInfo{StringAttr::get(ctxt, "write"),
IntegerType::get(ctxt, 1)},
hw::StructType::FieldInfo{
StringAttr::get(ctxt, "offset"),
IntegerType::get(
ctxt, 32, IntegerType::SignednessSemantics::Unsigned)},
hw::StructType::FieldInfo{StringAttr::get(ctxt, "data"),
IntegerType::get(ctxt, 64)},
});
ports.push_back(ServicePortInfo{
hw::InnerRefAttr::get(getSymNameAttr(), StringAttr::get(ctxt, "write")),
hw::InnerRefAttr::get(getSymNameAttr(),
StringAttr::get(ctxt, "read_write")),
ChannelBundleType::get(
ctxt,
{BundledChannel{StringAttr::get(ctxt, "offset"), ChannelDirection::to,
ChannelType::get(ctxt, IntegerType::get(ctxt, 32))},
BundledChannel{StringAttr::get(ctxt, "data"), ChannelDirection::to,
{BundledChannel{StringAttr::get(ctxt, "cmd"), ChannelDirection::to,
ChannelType::get(ctxt, cmdType)},
BundledChannel{StringAttr::get(ctxt, "data"), ChannelDirection::from,
ChannelType::get(ctxt, IntegerType::get(ctxt, 64))}},
/*resettable=*/UnitAttr())});
}
Loading

0 comments on commit 05136f0

Please sign in to comment.