Skip to content

Commit

Permalink
Merge branch 'main' into print_sym_visibility
Browse files Browse the repository at this point in the history
  • Loading branch information
cepheus69 committed Jul 9, 2024
2 parents a2b0a8e + 917cde6 commit a17ad83
Show file tree
Hide file tree
Showing 104 changed files with 1,929 additions and 1,067 deletions.
10 changes: 2 additions & 8 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ tools/arcilator @fabianschuiki @maerhart
**/Dialect/ESI @teqdruid

# FIRRTL
**/FIRRTL* @darthscsi @dtzSiFive @seldridge
*/firtool @dtzSiFive
libs/Firtool @dtzSiFive
**/FIRRTL* @darthscsi @seldridge

# FSM
**/Dialect/FSM @mortbopet
Expand Down Expand Up @@ -56,15 +54,11 @@ lib/Target/ExportSMTLIB @maerhart
# Ibis
**/Dialect/Ibis @teqdruid @mortbopet @blakep-msft

## Conversions

lib/Conversion/ExportVerilog @dtzSiFive


## Infra

# CI
.github @teqdruid @dtzSiFive
.github @teqdruid

# PrettyPrinter
**/Support/Pretty* @dtzSiFive
10 changes: 6 additions & 4 deletions docs/Dialects/FIRRTL/FIRRTLIntrinsics.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,12 @@ by another `enable` to model a default value of results.
For clocked calls, a low enable means that its register state transfer function is
not called. Hence their values will not be modify in that clock.

| Parameter | Type | Description |
| ------------- | ------ | -------------------------------- |
| isClocked | int | Set 1 if the dpi call is clocked |
| functionName | string | Specify the function name |
| Parameter | Type | Description |
| ------------- | ------ | --------------------------------------------------- |
| isClocked | int | Set 1 if the dpi call is clocked. |
| functionName | string | Specify the function name. |
| inputNames | string | Semicolon-delimited list of input names. Optional. |
| outputName | string | Output name. Optional. |


| Port | Direction | Type | Description |
Expand Down
1 change: 1 addition & 0 deletions frontends/PyCDE/integration_test/esitester.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
# RUN: mkdir %t && cd %t
# RUN: %PYTHON% %s %t 2>&1
# RUN: esi-cosim.py --source %t -- esitester cosim env wait | FileCheck %s
# RUN: ESI_COSIM_MANIFEST_MMIO=1 esi-cosim.py --source %t -- esiquery cosim env info

import pycde
from pycde import AppID, Clock, Module, Reset, generator
Expand Down
1 change: 0 additions & 1 deletion frontends/PyCDE/integration_test/lit.site.cfg.py.in
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ config.questa_path = "@QUESTA_PATH@"
config.esi_capnp = "@ESI_CAPNP@"
config.esi_runtime = "@ESI_RUNTIME@"
config.esi_runtime_path = "@ESIRuntimePath@"
config.capnp_path = "@CapnProto_DIR@"
config.iverilog_path = "@IVERILOG_PATH@"
config.bindings_python_enabled = @CIRCT_BINDINGS_PYTHON_ENABLED@

Expand Down
11 changes: 6 additions & 5 deletions frontends/PyCDE/integration_test/test_software/esi_ram.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@

# Baseline
m = acc.manifest()
if (platform == "cosim"):
# MMIO method
acc.cpp_accel.set_manifest_method(esi.esiCppAccel.ManifestMMIO)
m_alt = acc.manifest()
assert len(m.type_table) == len(m_alt.type_table)
# TODO: I broke this. Need to fix it.
# if (platform == "cosim"):
# MMIO method
# acc.cpp_accel.set_manifest_method(esi.esiCppAccel.ManifestMMIO)
# m_alt = acc.manifest()
# assert len(m.type_table) == len(m_alt.type_table)

info = m.module_infos
assert len(info) == 3
Expand Down
5 changes: 5 additions & 0 deletions frontends/PyCDE/integration_test/test_software/esi_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
platform = sys.argv[1]
acc = esi.AcceleratorConnection(platform, sys.argv[2])

mmio = acc.get_service_mmio()
data = mmio.read(8)
print(f"mmio data@8: {data:X}")
assert data == 0x207D98E5E5100E51

assert acc.sysinfo().esi_version() == 1
m = acc.manifest()
assert m.api_version == 1
Expand Down
131 changes: 48 additions & 83 deletions frontends/PyCDE/src/pycde/bsp/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@
from .. import esi
from ..module import Module, generator
from ..signals import BundleSignal
from ..types import Array, Bits, ChannelDirection
from ..types import Array, Bits, Channel, ChannelDirection

from typing import Dict, Tuple

MagicNumberLo = 0xE5100E51 # ESI__ESI
MagicNumberHi = 0x207D98E5 # Random
MagicNumber = 0x207D98E5_E5100E51 # random + ESI__ESI
VersionNumber = 0 # Version 0: format subject to change


Expand All @@ -23,33 +22,30 @@ class ESI_Manifest_ROM(Module):
module_name = "__ESI_Manifest_ROM"

clk = Clock()
address = Input(Bits(30))
address = Input(Bits(29))
# Data is two cycles delayed after address changes.
data = Output(Bits(32))
data = Output(Bits(64))


class AxiMMIO(esi.ServiceImplementation):
"""MMIO service implementation with an AXI-lite protocol. This assumes a 20
bit address bus for 1MB of addressable MMIO space. Which should be fine for
now, though nothing should assume this limit. It also only supports 32-bit
aligned accesses and just throws awary the lower two bits of address.
class ChannelMMIO(esi.ServiceImplementation):
"""MMIO service implementation with an AXI-lite protocol. This assumes a 32
bit address bus. It also only supports 64-bit aligned accesses and just throws
away the lower three bits of address.
Only allows for one outstanding request at a time. If a client doesn't return
a response, the MMIO service will hang. TODO: add some kind of timeout.
Implementation-defined MMIO layout:
- 0x0: 0 constanst
- 0x4: 0 constanst
- 0x8: Magic number low (0xE5100E51)
- 0xC: Magic number high (random constant: 0x207D98E5)
- 0x10: ESI version number (0)
- 0x14: Location of the manifest ROM (absolute address)
- 0x0: 0 constant
- 0x8: Magic number (0x207D98E5_E5100E51)
- 0x12: ESI version number (0)
- 0x18: Location of the manifest ROM (absolute address)
- 0x100: Start of MMIO space for requests. Mapping is contained in the
manifest so can be dynamically queried.
- addr(Manifest ROM) + 0: Size of compressed manifest
- addr(Manifest ROM) + 4: Start of compressed manifest
- addr(Manifest ROM) + 8: Start of compressed manifest
This layout _should_ be pretty standard, but different BSPs may have various
different restrictions.
Expand All @@ -58,56 +54,33 @@ class AxiMMIO(esi.ServiceImplementation):
clk = Clock()
rst = Input(Bits(1))

# MMIO read: address channel.
arvalid = Input(Bits(1))
arready = Output(Bits(1))
araddr = Input(Bits(20))

# MMIO read: data response channel.
rvalid = Output(Bits(1))
rready = Input(Bits(1))
rdata = Output(Bits(32))
rresp = Output(Bits(2))

# MMIO write: address channel.
awvalid = Input(Bits(1))
awready = Output(Bits(1))
awaddr = Input(Bits(20))

# MMIO write: data channel.
wvalid = Input(Bits(1))
wready = Output(Bits(1))
wdata = Input(Bits(32))

# MMIO write: write response channel.
bvalid = Output(Bits(1))
bready = Input(Bits(1))
bresp = Output(Bits(2))
read = Input(esi.MMIO.read.type)

# Start at this address for assigning MMIO addresses to service requests.
initial_offset: int = 0x100

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

def build_table(
self,
bundles) -> Tuple[Dict[int, BundleSignal], Dict[int, BundleSignal], int]:
"""Build a table of read and write addresses to BundleSignals."""
offset = AxiMMIO.initial_offset
offset = ChannelMMIO.initial_offset
read_table = {}
write_table = {}
for bundle in bundles.to_client_reqs:
if bundle.direction == ChannelDirection.Input:
read_table[offset] = bundle
offset += 4
offset += 8
elif bundle.direction == ChannelDirection.Output:
write_table[offset] = bundle
offset += 4
offset += 8

manifest_loc = 1 << offset.bit_length()
return read_table, write_table, manifest_loc
Expand All @@ -118,10 +91,16 @@ def build_read(self, manifest_loc: int, bundles):
# Currently just exposes the header and manifest. Not any of the possible
# service requests.

i32 = Bits(32)
i64 = Bits(64)
i2 = Bits(2)
i1 = Bits(1)

read_data_channel = Wire(Channel(esi.MMIOReadDataResponse),
"resp_data_channel")
read_addr_channel = self.read.unpack(data=read_data_channel)["offset"]
arready = Wire(i1)
(araddr, arvalid) = read_addr_channel.unwrap(arready)

address_written = NamedWire(i1, "address_written")
response_written = NamedWire(i1, "response_written")

Expand All @@ -132,52 +111,49 @@ def build_read(self, manifest_loc: int, bundles):
self.rst, [address_written],
[response_written],
name="req_outstanding")
self.arready = ~req_outstanding
arready.assign(~req_outstanding)

# Capture the address if a the bus transaction occured.
address_written.assign(self.arvalid & ~req_outstanding)
address = self.araddr.reg(self.clk, ce=address_written, name="address")
address_written.assign(arvalid & ~req_outstanding)
address = araddr.reg(self.clk, ce=address_written, name="address")
address_valid = address_written.reg(name="address_valid")
address_words = address[2:] # Lop off the lower two bits.
address_words = address[3:] # Lop off the lower three bits.

# Set up the output of the data response pipeline. `data_pipeline*` are to
# be connected below.
data_pipeline_valid = NamedWire(i1, "data_pipeline_valid")
data_pipeline = NamedWire(i32, "data_pipeline")
data_pipeline = NamedWire(i64, "data_pipeline")
data_pipeline_rresp = NamedWire(i2, "data_pipeline_rresp")
data_out_valid = ControlReg(self.clk,
self.rst, [data_pipeline_valid],
[response_written],
name="data_out_valid")
self.rvalid = data_out_valid
self.rdata = data_pipeline.reg(self.clk,
self.rst,
ce=data_pipeline_valid,
name="data_pipeline_reg")
self.rresp = data_pipeline_rresp.reg(self.clk,
self.rst,
ce=data_pipeline_valid,
name="data_pipeline_rresp_reg")
rvalid = data_out_valid
rdata = data_pipeline.reg(self.clk,
self.rst,
ce=data_pipeline_valid,
name="data_pipeline_reg")
read_resp_ch, rready = Channel(esi.MMIOReadDataResponse).wrap(rdata, rvalid)
read_data_channel.assign(read_resp_ch)
# Clear the `req_outstanding` flag when the response has been transmitted.
response_written.assign(data_out_valid & self.rready)
response_written.assign(data_out_valid & rready)

# Handle reads from the header (< 0x100).
header_upper = address_words[AxiMMIO.initial_offset.bit_length() - 2:]
header_upper = address_words[ChannelMMIO.initial_offset.bit_length() - 2:]
# Is the address in the header?
header_sel = (header_upper == header_upper.type(0))
header_sel.name = "header_sel"
# Layout the header as an array.
header = Array(Bits(32), 6)(
[0, 0, MagicNumberLo, MagicNumberHi, VersionNumber, manifest_loc])
header = Array(Bits(64), 4)([0, MagicNumber, VersionNumber, manifest_loc])
header.name = "header"
header_response_valid = address_valid # Zero latency read.
header_out = header[address[2:5]]
header_out = header[address_words[:2]]
header_out.name = "header_out"
header_rresp = i2(0)

# Handle reads from the manifest.
rom_address = NamedWire(
(address_words.as_uint() - (manifest_loc >> 2)).as_bits(30),
(address_words.as_uint() - (manifest_loc >> 3)).as_bits(29),
"rom_address")
mani_rom = ESI_Manifest_ROM(clk=self.clk, address=rom_address)
mani_valid = address_valid.reg(
Expand All @@ -187,10 +163,10 @@ def build_read(self, manifest_loc: int, bundles):
cycles=2, # Two cycle read to match the ROM latency.
name="mani_valid_reg")
mani_rresp = i2(0)
# mani_sel = (address.as_uint() >= manifest_loc)
mani_sel = (address.as_uint() >= manifest_loc).as_bits(1)

# Mux the output depending on whether or not the address is in the header.
sel = NamedWire(~header_sel, "sel")
sel = NamedWire(mani_sel, "sel")
data_mux_inputs = [header_out, mani_rom.data]
data_pipeline.assign(Mux(sel, *data_mux_inputs))
data_valid_mux_inputs = [header_response_valid, mani_valid]
Expand All @@ -200,15 +176,4 @@ def build_read(self, manifest_loc: int, bundles):

def build_write(self, bundles):
# TODO: this.

# So that we don't wedge the AXI-lite for writes, just ack all of them.
write_happened = Wire(Bits(1))
latched_aw = ControlReg(self.clk, self.rst, [self.awvalid],
[write_happened])
latched_w = ControlReg(self.clk, self.rst, [self.wvalid], [write_happened])
write_happened.assign(latched_aw & latched_w)

self.awready = 1
self.wready = 1
self.bvalid = write_happened
self.bresp = 0
pass
Loading

0 comments on commit a17ad83

Please sign in to comment.