75 """Construct the ESI header MMIO adhering to the MMIO layout specified in
76 the ChannelMMIO service implementation."""
80 read = Input(esi.MMIO.read_write.type)
83 reset_request = Output(Bits(1))
89 data_chan_wire = Wire(Channel(esi.MMIODataType))
90 input_bundles = ports.read.unpack(data=data_chan_wire)
91 cmd_chan = input_bundles[
'cmd']
96 cmd_ready = Wire(Bits(1))
97 s1_to_s2_xact = Wire(Bits(1))
98 cmd_raw, cmd_valid = cmd_chan.unwrap(cmd_ready)
101 s1_load = cmd_valid & cmd_ready
102 cmd = cmd_raw.reg(clk, rst, ce=s1_load, name=
"cmd")
103 s1_valid = ControlReg(clk,
106 resets=[s1_to_s2_xact],
109 cmd_ready.assign(~s1_valid)
111 address_words = cmd.offset.as_bits()[3:]
112 slot = address_words[:3]
114 cycles = Counter(64)(clk=ports.clk,
117 increment=Bits(1)(1),
118 instance_name=
"cycle_counter")
121 core_freq = System.current().core_freq
122 if core_freq
is None:
124 header = Array(Bits(64), 8)([
130 cycles.out.as_bits(),
134 header.name =
"header"
137 s2_valid = Wire(Bits(1))
138 data_chan_ready = Wire(Bits(1))
139 s2_xact = s2_valid & data_chan_ready
141 s1_to_s2_xact.assign(s1_valid & ~s2_valid)
143 header_out = header[slot].reg(clk=clk,
150 asserts=[s1_to_s2_xact],
152 name=
"header_out_valid"))
154 data_chan, data_chan_ready_sig = Channel(esi.MMIODataType).
wrap(
155 header_out, s2_valid)
156 data_chan_wire.assign(data_chan)
157 data_chan_ready.assign(data_chan_ready_sig)
163 reset_detect = (cmd.write & (slot == Bits(3)(7)) &
164 (cmd.data == Bits(64)(ResetMagicNumber))).as_bits()
165 ports.reset_request = (reset_detect & s1_to_s2_xact).as_bits()
172 data_type: Type, num_outs: int,
173 next_sel_width: int) -> type[
"ChannelDemuxNImpl"]:
174 """N-way channel demultiplexer for valid/ready signaling. Contains
175 valid/ready registers on the output channels. The selection signal is now
176 embedded in the input channel payload as a struct {sel, data}. Input
177 signals ready when the selected output register is empty."""
179 assert num_outs >= 1,
"num_outs must be at least 1."
181 class ChannelDemuxNImpl(Module):
186 InPayloadType = StructType([
187 (
"sel", Bits(clog2(num_outs))),
188 (
"next_sel", Bits(next_sel_width)),
191 inp = Input(Channel(InPayloadType))
192 OutPayloadType = StructType([
193 (
"next_sel", Bits(next_sel_width)),
197 for i
in range(num_outs):
198 locals()[f
"output_{i}"] = Output(Channel(OutPayloadType))
201 def generate(ports) -> None:
206 sel_width = clog2(num_outs)
209 input_ready = Wire(Bits(1), name=
"input_ready")
210 in_payload, in_valid = ports.inp.unwrap(input_ready)
211 in_sel = in_payload.sel
212 in_next_sel = in_payload.next_sel
213 in_data = in_payload.data
218 valid_regs: List[BitsSignal] = []
219 selected_valid_expr = Bits(1)(0)
221 for i
in range(num_outs):
223 will_write = Wire(Bits(1), name=f
"will_write_{i}")
224 write_cond = (in_valid & input_ready & (in_sel == Bits(sel_width)(i)))
225 will_write.assign(write_cond)
228 out_msg_reg = ChannelDemuxNImpl.OutPayloadType({
229 "next_sel": in_next_sel,
231 }).reg(clk=clk, rst=rst, ce=will_write, name=f
"out{i}_msg_reg")
234 consume = Wire(Bits(1), name=f
"consume_{i}")
235 valid_reg = ControlReg(
238 asserts=[will_write],
240 name=f
"out{i}_valid_reg",
242 valid_regs.append(valid_reg)
245 ch_sig, ch_ready = Channel(ChannelDemuxNImpl.OutPayloadType).
wrap(
246 out_msg_reg, valid_reg)
247 setattr(ports, f
"output_{i}", ch_sig)
248 consume.assign(valid_reg & ch_ready)
251 selected_valid_expr = (selected_valid_expr | (
252 (in_sel == Bits(sel_width)(i)) & valid_reg)).as_bits()
255 input_ready.assign((selected_valid_expr ^ Bits(1)(1)).as_bits())
257 def get_out(self, index: int) -> ChannelSignal:
258 return getattr(self, f
"output_{index}")
260 return ChannelDemuxNImpl
265 data_type: Type, num_outs: int,
266 branching_factor_log2: int) -> type[
"ChannelDemuxTree"]:
267 """Pipelined N-way channel demultiplexer for valid/ready signaling. This
268 implementation uses a tree structure of
269 ChannelDemuxN_HalfStage_ReadyBlocking modules to reduce fanout pressure.
270 Supports maximum half-throughput to save complexity and area.
273 root_sel_width = clog2(num_outs)
275 num_outs = 2**root_sel_width
276 sel_width = branching_factor_log2
277 fanout = 2**sel_width
279 class ChannelDemuxTree(Module):
283 InPayloadType = StructType([
284 (
"sel", Bits(clog2(num_outs))),
287 inp = Input(Channel(InPayloadType))
290 for i
in range(num_outs):
291 locals()[f
"output_{i}"] = Output(Channel(data_type))
294 def build(ports) -> None:
295 assert branching_factor_log2 > 0
298 setattr(ports,
"output_0", ports.inp.transform(
lambda p: p.data))
301 def payload_type(sel_width: int, next_sel_width: int) -> Type:
303 (
"sel", Bits(sel_width)),
304 (
"next_sel", Bits(next_sel_width)),
308 def next_sel_width_calc(curr_sel_width) -> int:
309 return max(curr_sel_width - sel_width, 0)
311 def payload_next(curr_msg: StructSignal) -> StructSignal:
312 """Given current level payload, produce next level payload by
313 stripping off the top selection bits."""
315 next_sel_width = next_sel_width_calc(curr_msg.next_sel.type.width)
316 curr_sel_width = curr_msg.next_sel.type.width
317 new_sel_width = min(curr_sel_width, sel_width)
323 "sel": (curr_msg.next_sel[next_sel_width:]
324 if curr_sel_width > 0
else Bits(0)(0)),
325 "next_sel": (curr_msg.next_sel[:next_sel_width]
326 if next_sel_width > 0
else Bits(0)(0)),
327 "data": curr_msg.data,
330 current_channels: List[ChannelSignal] = [
331 ports.inp.transform(
lambda m: payload_type(0, root_sel_width)({
338 curr_sel_width = root_sel_width
340 while len(current_channels) < num_outs:
341 next_level: List[ChannelSignal] = []
342 level_num_outs = min(2**curr_sel_width, fanout)
343 for i, c
in enumerate(current_channels):
346 num_outs=level_num_outs,
347 next_sel_width=next_sel_width_calc(curr_sel_width),
351 inp=c.transform(payload_next),
352 instance_name=f
"demux_l{level}_i{i}",
354 for j
in range(level_num_outs):
355 next_level.append(dmux.get_out(j))
356 current_channels = next_level
357 curr_sel_width -= sel_width
360 for i
in range(num_outs):
365 current_channels[i].transform(
lambda p: p.data),
368 def get_out(self, index: int) -> ChannelSignal:
369 return getattr(self, f
"output_{index}")
371 return ChannelDemuxTree
375def DesignResetController(
376 delay_cycles: int) -> type[
"DesignResetControllerImpl"]:
377 """Counts `delay_cycles` clock cycles after a reset request is observed, then
378 asserts `design_reset` for one cycle. This module must be driven by the
379 *external* reset only (not the reset it generates) so that the countdown is
380 not disturbed by the reset it produces.
382 `reset_pending` is asserted from the moment a reset is requested until it
383 fires. It is intended to be used to quiesce the design (e.g. stop accepting
384 new transactions) so that nothing is in flight when the reset is asserted."""
387 raise ValueError(
"'delay_cycles' must be at least 1.")
389 counter_width = max(clog2(delay_cycles), 1)
391 class DesignResetControllerImpl(Module):
394 reset_request = Input(Bits(1))
395 design_reset = Output(Bits(1))
398 reset_pending = Output(Bits(1))
404 pending = ControlReg(clk=ports.clk,
406 asserts=[ports.reset_request],
408 name=
"reset_pending")
410 count = Counter(counter_width)(clk=ports.clk,
412 clear=(fire | ~pending).as_bits(),
414 instance_name=
"reset_delay_counter")
417 (count.out == UInt(counter_width)(delay_cycles - 1))).as_bits())
418 ports.design_reset = fire
419 ports.reset_pending = pending
421 return DesignResetControllerImpl
721 output_bitwidth: int) -> type[
"TaggedReadGearboxImpl"]:
722 """Build a gearbox to convert the upstream data to the client data
723 type. Assumes a struct {tag, data} and only gearboxes the data. Tag is stored
724 separately and the struct is re-assembled later on."""
726 class TaggedReadGearboxImpl(Module):
731 (
"tag", esi.HostMem.TagType),
732 (
"data", Bits(input_bitwidth)),
736 (
"tag", esi.HostMem.TagType),
737 (
"data", Bits(output_bitwidth)),
742 ready_for_upstream = Wire(Bits(1), name=
"ready_for_upstream")
743 upstream_tag_and_data, upstream_valid = ports.in_.unwrap(
745 upstream_data = upstream_tag_and_data.data
746 upstream_xact = ready_for_upstream & upstream_valid
750 if output_bitwidth == input_bitwidth:
751 client_data_bits = upstream_data
752 client_valid = upstream_valid
753 elif output_bitwidth < input_bitwidth:
754 client_data_bits = upstream_data[:output_bitwidth]
755 client_valid = upstream_valid
760 chunks = ceil(output_bitwidth / input_bitwidth)
761 reg_ces = [Wire(Bits(1))
for _
in range(chunks)]
763 upstream_data.reg(ports.clk,
766 name=f
"chunk_reg_{idx}")
for idx
in range(chunks)
768 client_data_bits = BitsSignal.concat(reversed(regs))[:output_bitwidth]
772 clear_counter = Wire(Bits(1))
773 counter_width = clog2(chunks)
774 counter = Counter(counter_width)(clk=ports.clk,
777 increment=upstream_xact)
778 set_client_valid = counter.out == chunks - 1
779 client_xact = Wire(Bits(1))
780 client_valid = ControlReg(ports.clk, ports.rst,
781 [set_client_valid & upstream_xact],
783 client_xact.assign(client_valid & ready_for_upstream)
784 clear_counter.assign(client_xact)
785 for idx, reg_ce
in enumerate(reg_ces):
786 reg_ce.assign(upstream_xact &
787 (counter.out == UInt(counter_width)(idx)))
790 tag_reg = upstream_tag_and_data.tag.reg(ports.clk,
795 client_channel, client_ready = TaggedReadGearboxImpl.out.type.wrap(
798 "data": client_data_bits,
800 ready_for_upstream.assign(client_ready)
801 ports.out = client_channel
803 return TaggedReadGearboxImpl
807 reqs: List[esi._OutputBundleSetter]):
808 """Construct a host memory read request module to orchestrate the the read
809 connections. Responsible for both gearboxing the data, multiplexing the
810 requests, reassembling out-of-order responses and routing the responses to the
813 Generate this module dynamically to allow for multiple read clients of
814 multiple types to be directly accomodated."""
816 class HostmemReadProcessorImpl(Module):
821 reqPortMap: Dict[esi._OutputBundleSetter, str] = {}
823 name =
"client_" + req.client_name_str
824 locals()[name] = Output(req.type)
825 reqPortMap[req] = name
828 upstream = Output(hostmem_module.read.type)
832 """Build the read side of the HostMem service."""
836 upstream_req_channel, _ = Channel(hostmem_module.UpstreamReadReq).
wrap(
842 upstream_read_bundle, _ = hostmem_module.read.type.pack(
843 req=upstream_req_channel)
844 ports.upstream = upstream_read_bundle
851 assert len(reqs) <= 256,
"More than 256 read clients not supported."
854 upstream_req_channel = Wire(Channel(hostmem_module.UpstreamReadReq))
855 upstream_read_bundle, froms = hostmem_module.read.type.pack(
856 req=upstream_req_channel)
857 ports.upstream = upstream_read_bundle
858 upstream_resp_channel = froms[
"resp"]
860 demux = esi.TaggedDemux(len(reqs), upstream_resp_channel.type)(
861 clk=ports.clk, rst=ports.rst, in_=upstream_resp_channel)
863 tagged_client_reqs = []
864 for idx, client
in enumerate(reqs):
867 c.channel
for c
in client.type.channels
if c.name ==
'resp'
869 demuxed_upstream_channel = demux.get_out(idx)
878 client_type = resp_type.inner_type
879 if client_type.data.bitwidth == 0:
880 raise ValueError(
"Client data type cannot be zero-width. Use a "
881 "single-bit type if no data is needed.")
884 clk=ports.clk, rst=ports.rst, in_=demuxed_upstream_channel)
885 client_resp_channel = gearbox.out.transform(
lambda m: client_type({
887 "data": m.data.bitcast(client_type.data)
891 client_bundle, froms = client.type.pack(resp=client_resp_channel)
892 client_req = froms[
"req"]
893 tagged_client_req = client_req.transform(
894 lambda r: hostmem_module.UpstreamReadReq({
895 "address": r.address,
896 "length": (client_type.data.bitwidth + 7) // 8,
900 tagged_client_reqs.append(tagged_client_req)
903 setattr(ports, HostmemReadProcessorImpl.reqPortMap[client],
909 muxed_client_reqs = esi.ChannelMux(tagged_client_reqs)
910 upstream_req_channel.assign(muxed_client_reqs)
911 HostmemReadProcessorImpl.reqPortMap.clear()
913 return HostmemReadProcessorImpl
918 output_bitwidth: int) -> type[
"TaggedWriteGearboxImpl"]:
919 """Build a gearbox to convert the client data to upstream write chunks.
920 Assumes a struct {address, tag, data} and only gearboxes the data. Tag is
921 stored separately and the struct is re-assembled later on."""
923 if output_bitwidth % 8 != 0:
924 raise ValueError(
"Output bitwidth must be a multiple of 8.")
926 if input_bitwidth % 8 != 0:
927 input_pad_bits = 8 - (input_bitwidth % 8)
928 input_padded_bitwidth = input_bitwidth + input_pad_bits
930 class TaggedWriteGearboxImpl(Module):
935 (
"address", UInt(64)),
936 (
"tag", esi.HostMem.TagType),
937 (
"data", Bits(input_bitwidth)),
941 (
"address", UInt(64)),
942 (
"tag", esi.HostMem.TagType),
943 (
"data", Bits(output_bitwidth)),
944 (
"valid_bytes", Bits(8)),
947 num_chunks = ceil(input_padded_bitwidth / output_bitwidth)
951 upstream_ready = Wire(Bits(1))
952 ready_for_client = Wire(Bits(1))
953 client_tag_and_data, client_valid = ports.in_.unwrap(ready_for_client)
954 client_data = client_tag_and_data.data
955 if input_pad_bits > 0:
956 client_data = client_data.pad_or_truncate(input_padded_bitwidth)
957 client_xact = ready_for_client & client_valid
958 input_bitwidth_bytes = input_padded_bitwidth // 8
959 output_bitwidth_bytes = output_bitwidth // 8
963 if output_bitwidth == input_padded_bitwidth:
964 upstream_data_bits = client_data
965 upstream_valid = client_valid
966 ready_for_client.assign(upstream_ready)
967 tag = client_tag_and_data.tag
968 address = client_tag_and_data.address
969 valid_bytes = Bits(8)(input_bitwidth_bytes)
970 elif output_bitwidth > input_padded_bitwidth:
971 upstream_data_bits = client_data.as_bits(output_bitwidth)
972 upstream_valid = client_valid
973 ready_for_client.assign(upstream_ready)
974 tag = client_tag_and_data.tag
975 address = client_tag_and_data.address
976 valid_bytes = Bits(8)(input_bitwidth_bytes)
980 num_chunks = TaggedWriteGearboxImpl.num_chunks
981 num_chunks_idx_bitwidth = clog2(num_chunks)
982 if input_padded_bitwidth % output_bitwidth == 0:
985 padding_numbits = output_bitwidth - (input_padded_bitwidth %
987 client_data_padded = BitsSignal.concat(
988 [Bits(padding_numbits)(0), client_data])
990 client_data_padded[i * output_bitwidth:(i + 1) * output_bitwidth]
991 for i
in range(num_chunks)
993 chunk_regs = Array(Bits(output_bitwidth), num_chunks)([
994 c.reg(ports.clk, ce=client_xact, name=f
"chunk_{idx}")
995 for idx, c
in enumerate(chunks)
997 increment = Wire(Bits(1))
998 clear = Wire(Bits(1))
999 counter = Counter(num_chunks_idx_bitwidth)(clk=ports.clk,
1001 increment=increment,
1003 upstream_data_bits = chunk_regs[counter.out]
1004 upstream_valid = ControlReg(ports.clk, ports.rst, [client_xact],
1006 upstream_xact = upstream_valid & upstream_ready
1007 clear.assign(upstream_xact & (counter.out == (num_chunks - 1)))
1008 increment.assign(upstream_xact)
1009 ready_for_client.assign(~upstream_valid)
1010 address_padding_bits = clog2(output_bitwidth_bytes)
1011 counter_bytes = BitsSignal.concat(
1012 [counter.out.as_bits(),
1013 Bits(address_padding_bits)(0)]).as_uint()
1016 tag_reg = client_tag_and_data.tag.reg(ports.clk,
1019 addr_reg = client_tag_and_data.address.reg(ports.clk,
1022 address = (addr_reg + counter_bytes).as_uint(64)
1024 valid_bytes = Mux(counter.out == (num_chunks - 1),
1025 Bits(8)(output_bitwidth_bytes),
1026 Bits(8)((output_bitwidth - padding_numbits) // 8))
1028 upstream_channel, upstrm_ready_sig = TaggedWriteGearboxImpl.out.type.wrap(
1032 "data": upstream_data_bits,
1033 "valid_bytes": valid_bytes
1035 upstream_ready.assign(upstrm_ready_sig)
1036 ports.out = upstream_channel
1038 return TaggedWriteGearboxImpl
1042def EmitEveryN(message_type: Type, N: int) -> type[
'EmitEveryNImpl']:
1043 """Emit (forward) one message for every N input messages. The emitted message
1044 is the last one of the N received. N must be >= 1."""
1047 raise ValueError(
"N must be >= 1")
1049 class EmitEveryNImpl(Module):
1052 in_ = InputChannel(message_type)
1053 out = OutputChannel(message_type)
1057 ready_for_in = Wire(Bits(1))
1058 in_data, in_valid = ports.in_.unwrap(ready_for_in)
1059 xact = in_valid & ready_for_in
1063 out_chan, out_ready = EmitEveryNImpl.out.type.wrap(in_data, in_valid)
1064 ready_for_in.assign(out_ready)
1065 ports.out = out_chan
1068 counter_width = clog2(N)
1069 counter_clear = Wire(Bits(1))
1070 counter = Counter(counter_width)(clk=ports.clk,
1073 clear=counter_clear)
1076 last_msg = in_data.reg(ports.clk, ports.rst, ce=xact, name=
"last_msg")
1078 hit_last = (counter.out == UInt(counter_width)(N - 1)) & xact
1079 counter_clear.assign(hit_last)
1081 emit_accepted = Wire(Bits(1))
1082 out_valid = ControlReg(ports.clk, ports.rst, [hit_last], [emit_accepted])
1084 out_chan, out_ready = EmitEveryNImpl.out.type.wrap(last_msg, out_valid)
1086 ready_for_in.assign(~(out_valid & ~out_ready))
1087 emit_accepted.assign(out_valid & out_ready)
1089 ports.out = out_chan
1091 return EmitEveryNImpl
1095 write_width: int, hostmem_module,
1096 reqs: List[esi._OutputBundleSetter]) -> type[
"HostMemWriteProcessorImpl"]:
1097 """Construct a host memory write request module to orchestrate the the write
1098 connections. Responsible for both gearboxing the data, multiplexing the
1099 requests, reassembling out-of-order responses and routing the responses to the
1102 Generate this module dynamically to allow for multiple write clients of
1103 multiple types to be directly accomodated."""
1105 class HostMemWriteProcessorImpl(Module):
1111 reqPortMap: Dict[esi._OutputBundleSetter, str] = {}
1113 name =
"client_" + req.client_name_str
1114 locals()[name] = Output(req.type)
1115 reqPortMap[req] = name
1118 upstream = Output(hostmem_module.write.type)
1127 req, _ = Channel(hostmem_module.UpstreamWriteReq).
wrap(
1134 write_bundle, _ = hostmem_module.write.type.pack(req=req)
1135 ports.upstream = write_bundle
1138 assert len(reqs) <= 256,
"More than 256 write clients not supported."
1140 upstream_req_channel = Wire(Channel(hostmem_module.UpstreamWriteReq))
1141 upstream_write_bundle, froms = hostmem_module.write.type.pack(
1142 req=upstream_req_channel)
1143 ports.upstream = upstream_write_bundle
1144 upstream_ack_tag = froms[
"ackTag"]
1146 demuxed_acks = esi.TaggedDemux(len(reqs), upstream_ack_tag.type)(
1147 clk=ports.clk, rst=ports.rst, in_=upstream_ack_tag)
1152 write_channels: List[ChannelSignal] = []
1153 for idx, req
in enumerate(reqs):
1155 reqch = [c.channel
for c
in req.type.channels
if c.name ==
'req'][0]
1156 client_type = reqch.inner_type
1157 if isinstance(client_type.data, Window):
1158 client_type = client_type.lowered_type
1161 write_req_bundle_type = esi.HostMem.write_req_bundle_type(
1163 input_flit_ack = Wire(upstream_ack_tag.type)
1164 bundle_sig, froms = write_req_bundle_type.pack(ackTag=input_flit_ack)
1167 gearbox_in_type = gearbox_mod.in_.type.inner_type
1168 tagged_client_req = froms[
"req"]
1169 bitcast_client_req = tagged_client_req.transform(
1170 lambda m: gearbox_in_type({
1172 "address": m.address,
1173 "data": m.data.bitcast(gearbox_in_type.data)
1177 gearbox = gearbox_mod(clk=ports.clk,
1179 in_=bitcast_client_req)
1180 write_channels.append(
1181 gearbox.out.transform(
lambda m: m.type({
1182 "address": m.address,
1185 "valid_bytes": m.valid_bytes
1190 ack_every_n =
EmitEveryN(upstream_ack_tag.type, gearbox_mod.num_chunks)(
1191 clk=clk, rst=rst, in_=demuxed_acks.get_out(idx))
1192 input_flit_ack.assign(ack_every_n.out)
1195 setattr(ports, HostMemWriteProcessorImpl.reqPortMap[req], bundle_sig)
1198 muxed_write_channel = esi.ChannelMux(write_channels)
1199 upstream_req_channel.assign(muxed_write_channel)
1201 return HostMemWriteProcessorImpl
1205def ChannelHostMem(read_width: int,
1206 write_width: int) -> typing.Type[
'ChannelHostMemImpl']:
1208 class ChannelHostMemImpl(esi.ServiceImplementation):
1209 """Builds a HostMem service which multiplexes multiple HostMem clients into
1210 two (read and write) bundles of the given data width."""
1215 UpstreamReadReq = StructType([
1216 (
"address", UInt(64)),
1217 (
"length", UInt(32)),
1222 BundledChannel(
"req", ChannelDirection.TO, UpstreamReadReq),
1224 "resp", ChannelDirection.FROM,
1226 (
"tag", esi.HostMem.TagType),
1227 (
"data", Bits(read_width)),
1231 if write_width % 8 != 0:
1232 raise ValueError(
"Write width must be a multiple of 8.")
1233 UpstreamWriteReq = StructType([
1234 (
"address", UInt(64)),
1236 (
"data", Bits(write_width)),
1237 (
"valid_bytes", Bits(8)),
1241 BundledChannel(
"req", ChannelDirection.TO, UpstreamWriteReq),
1242 BundledChannel(
"ackTag", ChannelDirection.FROM, UInt(8)),
1246 def generate(ports, bundles: esi._ServiceGeneratorBundles):
1250 read_reqs = [req
for req
in bundles.to_client_reqs
if req.port ==
'read']
1253 read_proc = read_proc_module(clk=ports.clk, rst=ports.rst)
1254 ports.read = read_proc.upstream
1255 for req
in read_reqs:
1256 req.assign(getattr(read_proc, read_proc_module.reqPortMap[req]))
1260 req
for req
in bundles.to_client_reqs
if req.port ==
'write'
1264 write_proc = write_proc_module(clk=ports.clk, rst=ports.rst)
1265 ports.write = write_proc.upstream
1266 for req
in write_reqs:
1267 req.assign(getattr(write_proc, write_proc_module.reqPortMap[req]))
1269 return ChannelHostMemImpl
1354def ChannelEngineService(
1355 to_host_engine_gen: Callable,
1356 from_host_engine_gen: Callable) -> type[
'ChannelEngineService']:
1357 """Returns a channel service implementation which calls
1358 to_host_engine_gen(<client_type>) or from_host_engine_gen(<client_type>) to
1359 generate the to_host and from_host engines for each channel. Does not support
1360 engines which can service multiple clients at once.
1362 Individual service requests may override the default engine pair by passing
1363 `options={"engine": "pkg.mod.attr"}` at the service-request call site (e.g.
1364 `HostComms.some_bundle(AppID(...), options={"engine": "..."})`). The path
1365 is resolved by `_resolve_engine_pair` and must yield a
1366 `(to_host_engine_gen, from_host_engine_gen)` tuple with the same call shape
1367 as the defaults; the override applies to every channel of that request's
1371 class ChannelEngineService(esi.ServiceImplementation):
1372 """Service implementation which services the clients via a per-channel DMA
1379 def build(ports, bundles: esi._ServiceGeneratorBundles):
1383 def build_engine_appid(client_appid: List[
esi.AppID],
1384 channel_name: str) -> str:
1385 appid_strings = [str(appid)
for appid
in client_appid]
1386 return f
"{'_'.join(appid_strings)}.{channel_name}"
1388 def build_engine(bc: BundledChannel,
1389 bundle_to_host_gen: Callable,
1390 bundle_from_host_gen: Callable,
1391 input_channel=
None) -> Type:
1392 idbase = build_engine_appid(bundle.client_name, bc.name)
1397 engine_client_type = bc.channel.inner_type
1398 is_void = (engine_client_type.bitwidth == 0)
1400 engine_client_type = Bits(8)
1401 if bc.direction == ChannelDirection.FROM:
1402 engine_mod = bundle_to_host_gen(engine_client_type)
1404 engine_mod = bundle_from_host_gen(engine_client_type)
1409 eng_details: Dict[str, object] = {
"engine_inst": eng_appid}
1410 if input_channel
is not None:
1414 input_channel = input_channel.transform(
lambda _: Bits(8)(0))
1415 if (engine_mod.input_channel.type.signaling
1416 != input_channel.type.signaling):
1417 input_channel = input_channel.buffer(
1421 output_signaling=engine_mod.input_channel.type.signaling)
1422 eng_inputs[
"input_channel"] = input_channel
1423 if hasattr(engine_mod,
"mmio"):
1424 mmio_appid =
esi.AppID(idbase +
".mmio")
1425 eng_inputs[
"mmio"] = esi.MMIO.read_write(mmio_appid)
1426 eng_details[
"mmio"] = mmio_appid
1427 if hasattr(engine_mod,
"hostmem_write"):
1428 eng_inputs[
"hostmem_write"] = esi.HostMem.write_from_bundle(
1430 engine_mod.hostmem_write.type)
1431 if hasattr(engine_mod,
"hostmem_read"):
1432 eng_inputs[
"hostmem_read"] = esi.HostMem.read_from_bundle(
1433 esi.AppID(idbase +
".hostmem_read"), engine_mod.hostmem_read.type)
1434 engine = engine_mod(appid=eng_appid, **eng_inputs)
1435 engine_rec = bundles.emit_engine(engine, details=eng_details)
1436 engine_rec.add_record(bundle, {bc.name: {}})
1439 for bundle
in bundles.to_client_reqs:
1444 engine_override = bundle.options.get(
"engine")
1445 if engine_override
is None:
1446 bundle_to_host_gen = to_host_engine_gen
1447 bundle_from_host_gen = from_host_engine_gen
1452 bundle_type = bundle.type
1455 for bc
in bundle_type.channels:
1456 if bc.direction == ChannelDirection.TO:
1457 engine = build_engine(bc, bundle_to_host_gen, bundle_from_host_gen)
1458 out_chan = engine.output_channel
1460 if bc.channel.inner_type.bitwidth == 0:
1461 out_chan = out_chan.transform(
lambda _: Bits(0)(0))
1462 to_channels[bc.name] = out_chan
1464 client_bundle_sig, froms = bundle_type.pack(**to_channels)
1465 bundle.assign(client_bundle_sig)
1468 for bc
in bundle_type.channels:
1469 if bc.direction == ChannelDirection.FROM:
1470 build_engine(bc, bundle_to_host_gen, bundle_from_host_gen,
1473 return ChannelEngineService