16def OneItemBuffersToHost(client_type: Type):
17 """Create a simple, non-performant DMA-based channel communication. Protocol:
19 1) Host sends address of buffer address via MMIO write.
20 2) Device writes data on channel with a byte '1' to said buffer address.
21 3) Host polls the last byte in buffer for '1'.
22 4) Data is copied out of buffer, last byte is set to '0', goto 1.
24 Future improvement: support more than one buffer at once."""
26 class OneItemBuffersToHost(esi.EngineModule):
30 return "OneItemBuffersToHost"
35 input_channel = InputChannel(client_type)
40 mmio = Input(esi.MMIO.read_write.type)
44 client_data_bitwidth = client_type.bitwidth
45 client_data_padded_bits = ((client_data_bitwidth + 7) // 8) * 8
46 client_data_needs_pad = (client_data_padded_bits != client_data_bitwidth)
49 client_data_wire_type = Bits(
50 client_data_padded_bits)
if client_data_needs_pad
else client_type
51 xfer_data_type = StructType([(
"valid", Bits(8)),
52 (
"client_data", client_data_wire_type)])
53 hostmem_write = Input(esi.HostMem.write_req_bundle_type(xfer_data_type))
61 mmio_resp_chan = Wire(Channel(Bits(64)))
63 mmio_cmd_chan_raw = mmio_rw.unpack(data=mmio_resp_chan)[
'cmd']
64 mmio_cmd_chan, mmio_cmd_fork_resp = mmio_cmd_chan_raw.fork(clk, rst)
67 mmio_resp_data = NamedWire(Bits(64)(0),
"mmio_resp_data")
68 mmio_resp_chan.assign(
69 mmio_cmd_fork_resp.transform(
lambda _: mmio_resp_data))
73 _, _, mmio_cmd = mmio_cmd_chan.snoop()
75 mmio_offset_words = NamedWire((mmio_cmd.offset.as_bits()[3:]).as_uint(),
77 addr_above = mmio_offset_words >= UInt(32)(num_sinks)
78 addr_is_zero = mmio_offset_words == UInt(32)(0)
79 force_to_null = NamedWire(addr_above | ~addr_is_zero | mmio_cmd.write,
81 cmd_sink_sel = Mux(force_to_null,
82 Bits(clog2(num_sinks))(0),
83 mmio_offset_words.as_bits()[:clog2(num_sinks)])
84 mmio_data_only_chan = mmio_cmd_chan.transform(
lambda m: m.data)
85 mailbox_names = [
"null",
"buffer_loc"]
86 demuxed = esi.ChannelDemux(mmio_data_only_chan, cmd_sink_sel, num_sinks)
87 mailbox_mod = esi.Mailbox(Bits(64))
92 instance_name=
"mailbox_" + name)
93 for name, c
in zip(mailbox_names, demuxed)
95 [_, buffer_loc] = mailboxes
99 next_buffer_loc_chan = buffer_loc.output
100 hostwr_type = esi.HostMem.write_req_channel_type(
101 OneItemBuffersToHost.xfer_data_type)
102 hostwr_joined = Channel.join(next_buffer_loc_chan, ports.input_channel)
104 def _pad_client_data(raw):
105 """Widen the client data to byte-aligned Bits for the DMA struct."""
106 if not OneItemBuffersToHost.client_data_needs_pad:
109 return raw.bitcast(Bits(
110 OneItemBuffersToHost.client_data_bitwidth)).pad_or_truncate(
111 OneItemBuffersToHost.client_data_padded_bits)
113 hostwr = hostwr_joined.transform(
lambda joined: hostwr_type({
114 "address": joined.a.as_uint(),
118 "client_data": _pad_client_data(joined.b)
121 ports.hostmem_write.unpack(req=hostwr)
123 return OneItemBuffersToHost
126def OneItemBuffersFromHost(client_type: Type):
127 """Create a simple, non-performant DMA-base from host communication channel.
129 1) Host sends address of buffer address via MMIO write to register 0x08.
130 2) Host sends address of completion address via MMIO write to register 0x10.
131 3) Device reads data from said buffer and sends down the channel. Only keeps
132 one transfer outstanding by buffering the read response locally before
134 4) Device writes '1' to the first byte of the completion buffer to signal that
135 the transfer is done.
138 class OneItemBuffersFromHost(esi.EngineModule):
142 return "OneItemBuffersFromHost"
148 output_channel = OutputChannel(client_type)
153 mmio = Input(esi.MMIO.read_write.type)
154 xfer_data_type = Bits(8)
155 hostmem_write = Input(esi.HostMem.write_req_bundle_type(xfer_data_type))
156 hostmem_read = Input(esi.HostMem.read_bundle_type(client_type))
164 mmio_resp_chan = Wire(Channel(Bits(64)))
166 mmio_cmd_chan_raw = mmio_rw.unpack(data=mmio_resp_chan)[
'cmd']
167 mmio_cmd_chan, mmio_cmd_fork_resp = mmio_cmd_chan_raw.fork(clk, rst)
170 mmio_resp_data = NamedWire(Bits(64)(0),
"mmio_resp_data")
171 mmio_resp_chan.assign(
172 mmio_cmd_fork_resp.transform(
lambda _: mmio_resp_data))
175 _, _, mmio_cmd = mmio_cmd_chan.snoop()
176 mailbox_names = [
"null",
"buffer_loc",
"completion_addr"]
177 num_sinks = len(mailbox_names)
178 mmio_offset_words = NamedWire((mmio_cmd.offset.as_bits()[3:]).as_uint(),
180 addr_above = mmio_offset_words >= UInt(32)(num_sinks)
181 addr_is_zero = mmio_offset_words == UInt(32)(0)
182 force_to_null = NamedWire(addr_above | ~addr_is_zero | mmio_cmd.write,
184 cmd_sink_sel = Mux(force_to_null,
185 Bits(clog2(num_sinks))(0),
186 mmio_offset_words.as_bits()[:clog2(num_sinks)])
187 mmio_data_only_chan = mmio_cmd_chan.transform(
lambda m: m.data)
188 demuxed = esi.ChannelDemux(mmio_data_only_chan, cmd_sink_sel, num_sinks)
192 mailbox_mod = esi.Mailbox(Bits(64))
197 instance_name=
"mailbox_" + name)
198 for name, c
in zip(mailbox_names, demuxed)
200 [_, buffer_loc, completion_loc] = mailboxes
201 buffer_loc_for_read = buffer_loc.output
203 output_chan = Wire(Channel(client_type))
204 ports.output_channel = output_chan
205 output_buf_ready = Wire(Bits(1), name=
"output_buf_ready")
210 read_req_accept = Wire(Bits(1), name=
"read_req_accept")
211 buffer_loc_data, buffer_loc_valid = buffer_loc_for_read.unwrap(
213 read_req_valid = NamedWire(buffer_loc_valid & output_buf_ready,
215 read_req, read_req_ready = Channel(esi.HostMem.ReadReqType).
wrap(
216 esi.HostMem.ReadReqType({
217 "address": buffer_loc_data.as_uint(),
220 read_req_xact = NamedWire(read_req_valid & read_req_ready,
222 read_req_accept.assign(read_req_xact)
227 read_resp = ports.hostmem_read.unpack(req=read_req)[
'resp']
228 output_ready = Wire(Bits(1), name=
"output_ready")
229 output_valid_reset = Wire(Bits(1), name=
"output_valid_reset")
230 read_resp_ready = Wire(Bits(1), name=
"read_resp_ready")
231 read_resp_msg, read_resp_valid = read_resp.unwrap(read_resp_ready)
232 read_resp_xact = NamedWire(read_resp_valid & read_resp_ready,
234 output_valid = ControlReg(
237 asserts=[read_resp_xact],
238 resets=[output_valid_reset],
241 output_data = read_resp_msg.data.reg(clk=clk,
245 output_buf_ready.assign((~output_valid | output_ready).as_bits())
246 read_resp_ready.assign(output_buf_ready)
247 output_valid_reset.assign(output_valid & output_ready)
248 output_buf_chan, output_chan_ready = Channel(client_type).
wrap(
249 output_data, output_valid)
250 output_chan.assign(output_buf_chan)
251 output_ready.assign(output_chan_ready)
255 completion_pending_reset = Wire(Bits(1), name=
"completion_pending_reset")
256 completion_pending = ControlReg(
259 asserts=[read_resp_xact],
260 resets=[completion_pending_reset],
261 name=
"completion_pending",
264 write_ch_type = esi.HostMem.write_req_channel_type(
265 OneItemBuffersFromHost.xfer_data_type)
266 completion_loc_ready = Wire(Bits(1), name=
"completion_loc_ready")
267 completion_loc_data, completion_loc_valid = completion_loc.output.unwrap(
268 completion_loc_ready)
269 completion_write_valid = NamedWire(
270 completion_loc_valid & completion_pending,
"completion_write_valid")
271 write_done_chan, completion_write_ready = Channel(write_ch_type).
wrap(
273 "address": completion_loc_data.as_uint(),
276 }), completion_write_valid)
277 completion_write_xact = NamedWire(
278 completion_write_valid & completion_write_ready,
279 "completion_write_xact")
280 completion_loc_ready.assign(completion_write_xact)
281 completion_pending_reset.assign(completion_write_xact)
284 ports.hostmem_write.unpack(req=write_done_chan)
286 return OneItemBuffersFromHost