73 """Construct the ESI header MMIO adhering to the MMIO layout specified in
74 the ChannelMMIO service implementation."""
78 read = Input(esi.MMIO.read_write.type)
81 reset_request = Output(Bits(1))
87 data_chan_wire = Wire(Channel(esi.MMIODataType))
88 input_bundles = ports.read.unpack(data=data_chan_wire)
89 cmd_chan = input_bundles[
'cmd']
94 cmd_ready = Wire(Bits(1))
95 s1_to_s2_xact = Wire(Bits(1))
96 cmd_raw, cmd_valid = cmd_chan.unwrap(cmd_ready)
99 s1_load = cmd_valid & cmd_ready
100 cmd = cmd_raw.reg(clk, rst, ce=s1_load, name=
"cmd")
101 s1_valid = ControlReg(clk,
104 resets=[s1_to_s2_xact],
107 cmd_ready.assign(~s1_valid)
109 address_words = cmd.offset.as_bits()[3:]
110 slot = address_words[:3]
112 cycles = Counter(64)(clk=ports.clk,
115 increment=Bits(1)(1),
116 instance_name=
"cycle_counter")
119 core_freq = System.current().core_freq
120 if core_freq
is None:
122 header = Array(Bits(64), 8)([
128 cycles.out.as_bits(),
132 header.name =
"header"
135 s2_valid = Wire(Bits(1))
136 data_chan_ready = Wire(Bits(1))
137 s2_xact = s2_valid & data_chan_ready
139 s1_to_s2_xact.assign(s1_valid & ~s2_valid)
141 header_out = header[slot].reg(clk=clk,
148 asserts=[s1_to_s2_xact],
150 name=
"header_out_valid"))
152 data_chan, data_chan_ready_sig = Channel(esi.MMIODataType).
wrap(
153 header_out, s2_valid)
154 data_chan_wire.assign(data_chan)
155 data_chan_ready.assign(data_chan_ready_sig)
161 reset_detect = (cmd.write & (slot == Bits(3)(7)) &
162 (cmd.data == Bits(64)(ResetMagicNumber))).as_bits()
163 ports.reset_request = (reset_detect & s1_to_s2_xact).as_bits()
170 data_type: Type, num_outs: int,
171 next_sel_width: int) -> type[
"ChannelDemuxNImpl"]:
172 """N-way channel demultiplexer for valid/ready signaling. Contains
173 valid/ready registers on the output channels. The selection signal is now
174 embedded in the input channel payload as a struct {sel, data}. Input
175 signals ready when the selected output register is empty."""
177 assert num_outs >= 1,
"num_outs must be at least 1."
179 class ChannelDemuxNImpl(Module):
184 InPayloadType = StructType([
185 (
"sel", Bits(clog2(num_outs))),
186 (
"next_sel", Bits(next_sel_width)),
189 inp = Input(Channel(InPayloadType))
190 OutPayloadType = StructType([
191 (
"next_sel", Bits(next_sel_width)),
195 for i
in range(num_outs):
196 locals()[f
"output_{i}"] = Output(Channel(OutPayloadType))
199 def generate(ports) -> None:
204 sel_width = clog2(num_outs)
207 input_ready = Wire(Bits(1), name=
"input_ready")
208 in_payload, in_valid = ports.inp.unwrap(input_ready)
209 in_sel = in_payload.sel
210 in_next_sel = in_payload.next_sel
211 in_data = in_payload.data
216 valid_regs: List[BitsSignal] = []
217 selected_valid_expr = Bits(1)(0)
219 for i
in range(num_outs):
221 will_write = Wire(Bits(1), name=f
"will_write_{i}")
222 write_cond = (in_valid & input_ready & (in_sel == Bits(sel_width)(i)))
223 will_write.assign(write_cond)
226 out_msg_reg = ChannelDemuxNImpl.OutPayloadType({
227 "next_sel": in_next_sel,
229 }).reg(clk=clk, rst=rst, ce=will_write, name=f
"out{i}_msg_reg")
232 consume = Wire(Bits(1), name=f
"consume_{i}")
233 valid_reg = ControlReg(
236 asserts=[will_write],
238 name=f
"out{i}_valid_reg",
240 valid_regs.append(valid_reg)
243 ch_sig, ch_ready = Channel(ChannelDemuxNImpl.OutPayloadType).
wrap(
244 out_msg_reg, valid_reg)
245 setattr(ports, f
"output_{i}", ch_sig)
246 consume.assign(valid_reg & ch_ready)
249 selected_valid_expr = (selected_valid_expr | (
250 (in_sel == Bits(sel_width)(i)) & valid_reg)).as_bits()
253 input_ready.assign((selected_valid_expr ^ Bits(1)(1)).as_bits())
255 def get_out(self, index: int) -> ChannelSignal:
256 return getattr(self, f
"output_{index}")
258 return ChannelDemuxNImpl
263 data_type: Type, num_outs: int,
264 branching_factor_log2: int) -> type[
"ChannelDemuxTree"]:
265 """Pipelined N-way channel demultiplexer for valid/ready signaling. This
266 implementation uses a tree structure of
267 ChannelDemuxN_HalfStage_ReadyBlocking modules to reduce fanout pressure.
268 Supports maximum half-throughput to save complexity and area.
271 root_sel_width = clog2(num_outs)
273 num_outs = 2**root_sel_width
274 sel_width = branching_factor_log2
275 fanout = 2**sel_width
277 class ChannelDemuxTree(Module):
281 InPayloadType = StructType([
282 (
"sel", Bits(clog2(num_outs))),
285 inp = Input(Channel(InPayloadType))
288 for i
in range(num_outs):
289 locals()[f
"output_{i}"] = Output(Channel(data_type))
292 def build(ports) -> None:
293 assert branching_factor_log2 > 0
296 setattr(ports,
"output_0", ports.inp.transform(
lambda p: p.data))
299 def payload_type(sel_width: int, next_sel_width: int) -> Type:
301 (
"sel", Bits(sel_width)),
302 (
"next_sel", Bits(next_sel_width)),
306 def next_sel_width_calc(curr_sel_width) -> int:
307 return max(curr_sel_width - sel_width, 0)
309 def payload_next(curr_msg: StructSignal) -> StructSignal:
310 """Given current level payload, produce next level payload by
311 stripping off the top selection bits."""
313 next_sel_width = next_sel_width_calc(curr_msg.next_sel.type.width)
314 curr_sel_width = curr_msg.next_sel.type.width
315 new_sel_width = min(curr_sel_width, sel_width)
321 "sel": (curr_msg.next_sel[next_sel_width:]
322 if curr_sel_width > 0
else Bits(0)(0)),
323 "next_sel": (curr_msg.next_sel[:next_sel_width]
324 if next_sel_width > 0
else Bits(0)(0)),
325 "data": curr_msg.data,
328 current_channels: List[ChannelSignal] = [
329 ports.inp.transform(
lambda m: payload_type(0, root_sel_width)({
336 curr_sel_width = root_sel_width
338 while len(current_channels) < num_outs:
339 next_level: List[ChannelSignal] = []
340 level_num_outs = min(2**curr_sel_width, fanout)
341 for i, c
in enumerate(current_channels):
344 num_outs=level_num_outs,
345 next_sel_width=next_sel_width_calc(curr_sel_width),
349 inp=c.transform(payload_next),
350 instance_name=f
"demux_l{level}_i{i}",
352 for j
in range(level_num_outs):
353 next_level.append(dmux.get_out(j))
354 current_channels = next_level
355 curr_sel_width -= sel_width
358 for i
in range(num_outs):
363 current_channels[i].transform(
lambda p: p.data),
366 def get_out(self, index: int) -> ChannelSignal:
367 return getattr(self, f
"output_{index}")
369 return ChannelDemuxTree
373def DesignResetController(
374 delay_cycles: int) -> type[
"DesignResetControllerImpl"]:
375 """Counts `delay_cycles` clock cycles after a reset request is observed, then
376 asserts `design_reset` for one cycle. This module must be driven by the
377 *external* reset only (not the reset it generates) so that the countdown is
378 not disturbed by the reset it produces.
380 `reset_pending` is asserted from the moment a reset is requested until it
381 fires. It is intended to be used to quiesce the design (e.g. stop accepting
382 new transactions) so that nothing is in flight when the reset is asserted."""
385 raise ValueError(
"'delay_cycles' must be at least 1.")
387 counter_width = max(clog2(delay_cycles), 1)
389 class DesignResetControllerImpl(Module):
392 reset_request = Input(Bits(1))
393 design_reset = Output(Bits(1))
396 reset_pending = Output(Bits(1))
402 pending = ControlReg(clk=ports.clk,
404 asserts=[ports.reset_request],
406 name=
"reset_pending")
408 count = Counter(counter_width)(clk=ports.clk,
410 clear=(fire | ~pending).as_bits(),
412 instance_name=
"reset_delay_counter")
415 (count.out == UInt(counter_width)(delay_cycles - 1))).as_bits())
416 ports.design_reset = fire
417 ports.reset_pending = pending
419 return DesignResetControllerImpl
704 output_bitwidth: int) -> type[
"TaggedReadGearboxImpl"]:
705 """Build a gearbox to convert the upstream data to the client data
706 type. Assumes a struct {tag, data} and only gearboxes the data. Tag is stored
707 separately and the struct is re-assembled later on."""
709 class TaggedReadGearboxImpl(Module):
714 (
"tag", esi.HostMem.TagType),
715 (
"data", Bits(input_bitwidth)),
719 (
"tag", esi.HostMem.TagType),
720 (
"data", Bits(output_bitwidth)),
725 ready_for_upstream = Wire(Bits(1), name=
"ready_for_upstream")
726 upstream_tag_and_data, upstream_valid = ports.in_.unwrap(
728 upstream_data = upstream_tag_and_data.data
729 upstream_xact = ready_for_upstream & upstream_valid
733 if output_bitwidth == input_bitwidth:
734 client_data_bits = upstream_data
735 client_valid = upstream_valid
736 elif output_bitwidth < input_bitwidth:
737 client_data_bits = upstream_data[:output_bitwidth]
738 client_valid = upstream_valid
743 chunks = ceil(output_bitwidth / input_bitwidth)
744 reg_ces = [Wire(Bits(1))
for _
in range(chunks)]
746 upstream_data.reg(ports.clk,
749 name=f
"chunk_reg_{idx}")
for idx
in range(chunks)
751 client_data_bits = BitsSignal.concat(reversed(regs))[:output_bitwidth]
755 clear_counter = Wire(Bits(1))
756 counter_width = clog2(chunks)
757 counter = Counter(counter_width)(clk=ports.clk,
760 increment=upstream_xact)
761 set_client_valid = counter.out == chunks - 1
762 client_xact = Wire(Bits(1))
763 client_valid = ControlReg(ports.clk, ports.rst,
764 [set_client_valid & upstream_xact],
766 client_xact.assign(client_valid & ready_for_upstream)
767 clear_counter.assign(client_xact)
768 for idx, reg_ce
in enumerate(reg_ces):
769 reg_ce.assign(upstream_xact &
770 (counter.out == UInt(counter_width)(idx)))
773 tag_reg = upstream_tag_and_data.tag.reg(ports.clk,
778 client_channel, client_ready = TaggedReadGearboxImpl.out.type.wrap(
781 "data": client_data_bits,
783 ready_for_upstream.assign(client_ready)
784 ports.out = client_channel
786 return TaggedReadGearboxImpl
790 reqs: List[esi._OutputBundleSetter]):
791 """Construct a host memory read request module to orchestrate the the read
792 connections. Responsible for both gearboxing the data, multiplexing the
793 requests, reassembling out-of-order responses and routing the responses to the
796 Generate this module dynamically to allow for multiple read clients of
797 multiple types to be directly accomodated."""
799 class HostmemReadProcessorImpl(Module):
804 reqPortMap: Dict[esi._OutputBundleSetter, str] = {}
806 name =
"client_" + req.client_name_str
807 locals()[name] = Output(req.type)
808 reqPortMap[req] = name
811 upstream = Output(hostmem_module.read.type)
815 """Build the read side of the HostMem service."""
819 upstream_req_channel, _ = Channel(hostmem_module.UpstreamReadReq).
wrap(
825 upstream_read_bundle, _ = hostmem_module.read.type.pack(
826 req=upstream_req_channel)
827 ports.upstream = upstream_read_bundle
834 assert len(reqs) <= 256,
"More than 256 read clients not supported."
837 upstream_req_channel = Wire(Channel(hostmem_module.UpstreamReadReq))
838 upstream_read_bundle, froms = hostmem_module.read.type.pack(
839 req=upstream_req_channel)
840 ports.upstream = upstream_read_bundle
841 upstream_resp_channel = froms[
"resp"]
843 demux = esi.TaggedDemux(len(reqs), upstream_resp_channel.type)(
844 clk=ports.clk, rst=ports.rst, in_=upstream_resp_channel)
846 tagged_client_reqs = []
847 for idx, client
in enumerate(reqs):
850 c.channel
for c
in client.type.channels
if c.name ==
'resp'
852 demuxed_upstream_channel = demux.get_out(idx)
861 client_type = resp_type.inner_type
862 if client_type.data.bitwidth == 0:
863 raise ValueError(
"Client data type cannot be zero-width. Use a "
864 "single-bit type if no data is needed.")
867 clk=ports.clk, rst=ports.rst, in_=demuxed_upstream_channel)
868 client_resp_channel = gearbox.out.transform(
lambda m: client_type({
870 "data": m.data.bitcast(client_type.data)
874 client_bundle, froms = client.type.pack(resp=client_resp_channel)
875 client_req = froms[
"req"]
876 tagged_client_req = client_req.transform(
877 lambda r: hostmem_module.UpstreamReadReq({
878 "address": r.address,
879 "length": (client_type.data.bitwidth + 7) // 8,
883 tagged_client_reqs.append(tagged_client_req)
886 setattr(ports, HostmemReadProcessorImpl.reqPortMap[client],
892 muxed_client_reqs = esi.ChannelMux(tagged_client_reqs)
893 upstream_req_channel.assign(muxed_client_reqs)
894 HostmemReadProcessorImpl.reqPortMap.clear()
896 return HostmemReadProcessorImpl
901 output_bitwidth: int) -> type[
"TaggedWriteGearboxImpl"]:
902 """Build a gearbox to convert the client data to upstream write chunks.
903 Assumes a struct {address, tag, data} and only gearboxes the data. Tag is
904 stored separately and the struct is re-assembled later on."""
906 if output_bitwidth % 8 != 0:
907 raise ValueError(
"Output bitwidth must be a multiple of 8.")
909 if input_bitwidth % 8 != 0:
910 input_pad_bits = 8 - (input_bitwidth % 8)
911 input_padded_bitwidth = input_bitwidth + input_pad_bits
913 class TaggedWriteGearboxImpl(Module):
918 (
"address", UInt(64)),
919 (
"tag", esi.HostMem.TagType),
920 (
"data", Bits(input_bitwidth)),
924 (
"address", UInt(64)),
925 (
"tag", esi.HostMem.TagType),
926 (
"data", Bits(output_bitwidth)),
927 (
"valid_bytes", Bits(8)),
930 num_chunks = ceil(input_padded_bitwidth / output_bitwidth)
934 upstream_ready = Wire(Bits(1))
935 ready_for_client = Wire(Bits(1))
936 client_tag_and_data, client_valid = ports.in_.unwrap(ready_for_client)
937 client_data = client_tag_and_data.data
938 if input_pad_bits > 0:
939 client_data = client_data.pad_or_truncate(input_padded_bitwidth)
940 client_xact = ready_for_client & client_valid
941 input_bitwidth_bytes = input_padded_bitwidth // 8
942 output_bitwidth_bytes = output_bitwidth // 8
946 if output_bitwidth == input_padded_bitwidth:
947 upstream_data_bits = client_data
948 upstream_valid = client_valid
949 ready_for_client.assign(upstream_ready)
950 tag = client_tag_and_data.tag
951 address = client_tag_and_data.address
952 valid_bytes = Bits(8)(input_bitwidth_bytes)
953 elif output_bitwidth > input_padded_bitwidth:
954 upstream_data_bits = client_data.as_bits(output_bitwidth)
955 upstream_valid = client_valid
956 ready_for_client.assign(upstream_ready)
957 tag = client_tag_and_data.tag
958 address = client_tag_and_data.address
959 valid_bytes = Bits(8)(input_bitwidth_bytes)
963 num_chunks = TaggedWriteGearboxImpl.num_chunks
964 num_chunks_idx_bitwidth = clog2(num_chunks)
965 if input_padded_bitwidth % output_bitwidth == 0:
968 padding_numbits = output_bitwidth - (input_padded_bitwidth %
970 client_data_padded = BitsSignal.concat(
971 [Bits(padding_numbits)(0), client_data])
973 client_data_padded[i * output_bitwidth:(i + 1) * output_bitwidth]
974 for i
in range(num_chunks)
976 chunk_regs = Array(Bits(output_bitwidth), num_chunks)([
977 c.reg(ports.clk, ce=client_xact, name=f
"chunk_{idx}")
978 for idx, c
in enumerate(chunks)
980 increment = Wire(Bits(1))
981 clear = Wire(Bits(1))
982 counter = Counter(num_chunks_idx_bitwidth)(clk=ports.clk,
986 upstream_data_bits = chunk_regs[counter.out]
987 upstream_valid = ControlReg(ports.clk, ports.rst, [client_xact],
989 upstream_xact = upstream_valid & upstream_ready
990 clear.assign(upstream_xact & (counter.out == (num_chunks - 1)))
991 increment.assign(upstream_xact)
992 ready_for_client.assign(~upstream_valid)
993 address_padding_bits = clog2(output_bitwidth_bytes)
994 counter_bytes = BitsSignal.concat(
995 [counter.out.as_bits(),
996 Bits(address_padding_bits)(0)]).as_uint()
999 tag_reg = client_tag_and_data.tag.reg(ports.clk,
1002 addr_reg = client_tag_and_data.address.reg(ports.clk,
1005 address = (addr_reg + counter_bytes).as_uint(64)
1007 valid_bytes = Mux(counter.out == (num_chunks - 1),
1008 Bits(8)(output_bitwidth_bytes),
1009 Bits(8)((output_bitwidth - padding_numbits) // 8))
1011 upstream_channel, upstrm_ready_sig = TaggedWriteGearboxImpl.out.type.wrap(
1015 "data": upstream_data_bits,
1016 "valid_bytes": valid_bytes
1018 upstream_ready.assign(upstrm_ready_sig)
1019 ports.out = upstream_channel
1021 return TaggedWriteGearboxImpl
1025def EmitEveryN(message_type: Type, N: int) -> type[
'EmitEveryNImpl']:
1026 """Emit (forward) one message for every N input messages. The emitted message
1027 is the last one of the N received. N must be >= 1."""
1030 raise ValueError(
"N must be >= 1")
1032 class EmitEveryNImpl(Module):
1035 in_ = InputChannel(message_type)
1036 out = OutputChannel(message_type)
1040 ready_for_in = Wire(Bits(1))
1041 in_data, in_valid = ports.in_.unwrap(ready_for_in)
1042 xact = in_valid & ready_for_in
1046 out_chan, out_ready = EmitEveryNImpl.out.type.wrap(in_data, in_valid)
1047 ready_for_in.assign(out_ready)
1048 ports.out = out_chan
1051 counter_width = clog2(N)
1052 counter_clear = Wire(Bits(1))
1053 counter = Counter(counter_width)(clk=ports.clk,
1056 clear=counter_clear)
1059 last_msg = in_data.reg(ports.clk, ports.rst, ce=xact, name=
"last_msg")
1061 hit_last = (counter.out == UInt(counter_width)(N - 1)) & xact
1062 counter_clear.assign(hit_last)
1064 emit_accepted = Wire(Bits(1))
1065 out_valid = ControlReg(ports.clk, ports.rst, [hit_last], [emit_accepted])
1067 out_chan, out_ready = EmitEveryNImpl.out.type.wrap(last_msg, out_valid)
1069 ready_for_in.assign(~(out_valid & ~out_ready))
1070 emit_accepted.assign(out_valid & out_ready)
1072 ports.out = out_chan
1074 return EmitEveryNImpl
1078 write_width: int, hostmem_module,
1079 reqs: List[esi._OutputBundleSetter]) -> type[
"HostMemWriteProcessorImpl"]:
1080 """Construct a host memory write request module to orchestrate the the write
1081 connections. Responsible for both gearboxing the data, multiplexing the
1082 requests, reassembling out-of-order responses and routing the responses to the
1085 Generate this module dynamically to allow for multiple write clients of
1086 multiple types to be directly accomodated."""
1088 class HostMemWriteProcessorImpl(Module):
1094 reqPortMap: Dict[esi._OutputBundleSetter, str] = {}
1096 name =
"client_" + req.client_name_str
1097 locals()[name] = Output(req.type)
1098 reqPortMap[req] = name
1101 upstream = Output(hostmem_module.write.type)
1110 req, _ = Channel(hostmem_module.UpstreamWriteReq).
wrap(
1117 write_bundle, _ = hostmem_module.write.type.pack(req=req)
1118 ports.upstream = write_bundle
1121 assert len(reqs) <= 256,
"More than 256 write clients not supported."
1123 upstream_req_channel = Wire(Channel(hostmem_module.UpstreamWriteReq))
1124 upstream_write_bundle, froms = hostmem_module.write.type.pack(
1125 req=upstream_req_channel)
1126 ports.upstream = upstream_write_bundle
1127 upstream_ack_tag = froms[
"ackTag"]
1129 demuxed_acks = esi.TaggedDemux(len(reqs), upstream_ack_tag.type)(
1130 clk=ports.clk, rst=ports.rst, in_=upstream_ack_tag)
1135 write_channels: List[ChannelSignal] = []
1136 for idx, req
in enumerate(reqs):
1138 reqch = [c.channel
for c
in req.type.channels
if c.name ==
'req'][0]
1139 client_type = reqch.inner_type
1140 if isinstance(client_type.data, Window):
1141 client_type = client_type.lowered_type
1144 write_req_bundle_type = esi.HostMem.write_req_bundle_type(
1146 input_flit_ack = Wire(upstream_ack_tag.type)
1147 bundle_sig, froms = write_req_bundle_type.pack(ackTag=input_flit_ack)
1150 gearbox_in_type = gearbox_mod.in_.type.inner_type
1151 tagged_client_req = froms[
"req"]
1152 bitcast_client_req = tagged_client_req.transform(
1153 lambda m: gearbox_in_type({
1155 "address": m.address,
1156 "data": m.data.bitcast(gearbox_in_type.data)
1160 gearbox = gearbox_mod(clk=ports.clk,
1162 in_=bitcast_client_req)
1163 write_channels.append(
1164 gearbox.out.transform(
lambda m: m.type({
1165 "address": m.address,
1168 "valid_bytes": m.valid_bytes
1173 ack_every_n =
EmitEveryN(upstream_ack_tag.type, gearbox_mod.num_chunks)(
1174 clk=clk, rst=rst, in_=demuxed_acks.get_out(idx))
1175 input_flit_ack.assign(ack_every_n.out)
1178 setattr(ports, HostMemWriteProcessorImpl.reqPortMap[req], bundle_sig)
1181 muxed_write_channel = esi.ChannelMux(write_channels)
1182 upstream_req_channel.assign(muxed_write_channel)
1184 return HostMemWriteProcessorImpl
1188def ChannelHostMem(read_width: int,
1189 write_width: int) -> typing.Type[
'ChannelHostMemImpl']:
1191 class ChannelHostMemImpl(esi.ServiceImplementation):
1192 """Builds a HostMem service which multiplexes multiple HostMem clients into
1193 two (read and write) bundles of the given data width."""
1198 UpstreamReadReq = StructType([
1199 (
"address", UInt(64)),
1200 (
"length", UInt(32)),
1205 BundledChannel(
"req", ChannelDirection.TO, UpstreamReadReq),
1207 "resp", ChannelDirection.FROM,
1209 (
"tag", esi.HostMem.TagType),
1210 (
"data", Bits(read_width)),
1214 if write_width % 8 != 0:
1215 raise ValueError(
"Write width must be a multiple of 8.")
1216 UpstreamWriteReq = StructType([
1217 (
"address", UInt(64)),
1219 (
"data", Bits(write_width)),
1220 (
"valid_bytes", Bits(8)),
1224 BundledChannel(
"req", ChannelDirection.TO, UpstreamWriteReq),
1225 BundledChannel(
"ackTag", ChannelDirection.FROM, UInt(8)),
1229 def generate(ports, bundles: esi._ServiceGeneratorBundles):
1233 read_reqs = [req
for req
in bundles.to_client_reqs
if req.port ==
'read']
1236 read_proc = read_proc_module(clk=ports.clk, rst=ports.rst)
1237 ports.read = read_proc.upstream
1238 for req
in read_reqs:
1239 req.assign(getattr(read_proc, read_proc_module.reqPortMap[req]))
1243 req
for req
in bundles.to_client_reqs
if req.port ==
'write'
1247 write_proc = write_proc_module(clk=ports.clk, rst=ports.rst)
1248 ports.write = write_proc.upstream
1249 for req
in write_reqs:
1250 req.assign(getattr(write_proc, write_proc_module.reqPortMap[req]))
1252 return ChannelHostMemImpl
1300def ChannelEngineService(
1301 to_host_engine_gen: Callable,
1302 from_host_engine_gen: Callable) -> type[
'ChannelEngineService']:
1303 """Returns a channel service implementation which calls
1304 to_host_engine_gen(<client_type>) or from_host_engine_gen(<client_type>) to
1305 generate the to_host and from_host engines for each channel. Does not support
1306 engines which can service multiple clients at once."""
1308 class ChannelEngineService(esi.ServiceImplementation):
1309 """Service implementation which services the clients via a per-channel DMA
1316 def build(ports, bundles: esi._ServiceGeneratorBundles):
1320 def build_engine_appid(client_appid: List[
esi.AppID],
1321 channel_name: str) -> str:
1322 appid_strings = [str(appid)
for appid
in client_appid]
1323 return f
"{'_'.join(appid_strings)}.{channel_name}"
1325 def build_engine(bc: BundledChannel, input_channel=
None) -> Type:
1326 idbase = build_engine_appid(bundle.client_name, bc.name)
1331 engine_client_type = bc.channel.inner_type
1332 is_void = (engine_client_type.bitwidth == 0)
1334 engine_client_type = Bits(8)
1335 if bc.direction == ChannelDirection.FROM:
1336 engine_mod = to_host_engine_gen(engine_client_type)
1338 engine_mod = from_host_engine_gen(engine_client_type)
1343 eng_details: Dict[str, object] = {
"engine_inst": eng_appid}
1344 if input_channel
is not None:
1348 input_channel = input_channel.transform(
lambda _: Bits(8)(0))
1349 if (engine_mod.input_channel.type.signaling
1350 != input_channel.type.signaling):
1351 input_channel = input_channel.buffer(
1355 output_signaling=engine_mod.input_channel.type.signaling)
1356 eng_inputs[
"input_channel"] = input_channel
1357 if hasattr(engine_mod,
"mmio"):
1358 mmio_appid =
esi.AppID(idbase +
".mmio")
1359 eng_inputs[
"mmio"] = esi.MMIO.read_write(mmio_appid)
1360 eng_details[
"mmio"] = mmio_appid
1361 if hasattr(engine_mod,
"hostmem_write"):
1362 eng_inputs[
"hostmem_write"] = esi.HostMem.write_from_bundle(
1364 engine_mod.hostmem_write.type)
1365 if hasattr(engine_mod,
"hostmem_read"):
1366 eng_inputs[
"hostmem_read"] = esi.HostMem.read_from_bundle(
1367 esi.AppID(idbase +
".hostmem_read"), engine_mod.hostmem_read.type)
1368 engine = engine_mod(appid=eng_appid, **eng_inputs)
1369 engine_rec = bundles.emit_engine(engine, details=eng_details)
1370 engine_rec.add_record(bundle, {bc.name: {}})
1373 for bundle
in bundles.to_client_reqs:
1374 bundle_type = bundle.type
1377 for bc
in bundle_type.channels:
1378 if bc.direction == ChannelDirection.TO:
1379 engine = build_engine(bc)
1380 out_chan = engine.output_channel
1382 if bc.channel.inner_type.bitwidth == 0:
1383 out_chan = out_chan.transform(
lambda _: Bits(0)(0))
1384 to_channels[bc.name] = out_chan
1386 client_bundle_sig, froms = bundle_type.pack(**to_channels)
1387 bundle.assign(client_bundle_sig)
1390 for bc
in bundle_type.channels:
1391 if bc.direction == ChannelDirection.FROM:
1392 build_engine(bc, froms[bc.name])
1394 return ChannelEngineService