""" Copyright (c) 2014-2016 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from myhdl import * import axis_ep import eth_ep import struct try: from queue import Queue except ImportError: from Queue import Queue class IPFrame(object): def __init__(self, payload=b'', eth_dest_mac=0, eth_src_mac=0, eth_type=0, ip_version=4, ip_ihl=5, ip_dscp=0, ip_ecn=0, ip_length=None, ip_identification=0, ip_flags=2, ip_fragment_offset=0, ip_ttl=64, ip_protocol=0x11, ip_header_checksum=None, ip_source_ip=0xc0a80164, ip_dest_ip=0xc0a80165): self._payload = axis_ep.AXIStreamFrame() self.eth_dest_mac = eth_dest_mac self.eth_src_mac = eth_src_mac self.eth_type = eth_type self.ip_version = ip_version self.ip_ihl = ip_ihl self.ip_dscp = ip_dscp self.ip_ecn = ip_ecn self.ip_length = ip_length self.ip_identification = ip_identification self.ip_flags = ip_flags self.ip_fragment_offset = ip_fragment_offset self.ip_ttl = ip_ttl self.ip_protocol = ip_protocol self.ip_header_checksum = ip_header_checksum self.ip_source_ip = ip_source_ip self.ip_dest_ip = ip_dest_ip if type(payload) is dict: self.payload = axis_ep.AXIStreamFrame(payload['ip_payload']) self.eth_dest_mac = payload['eth_dest_mac'] self.eth_src_mac = payload['eth_src_mac'] self.eth_type = payload['eth_type'] self.ip_version = payload['ip_version'] self.ip_ihl = payload['ip_ihl'] self.ip_dscp = payload['ip_dscp'] self.ip_ecn = payload['ip_ecn'] self.ip_length = payload['ip_length'] self.ip_identification = payload['ip_identification'] self.ip_flags = payload['ip_flags'] self.ip_fragment_offset = payload['ip_fragment_offset'] self.ip_ttl = payload['ip_ttl'] self.ip_protocol = payload['ip_protocol'] self.ip_header_checksum = payload['ip_header_checksum'] self.ip_source_ip = payload['ip_source_ip'] self.ip_dest_ip = payload['ip_dest_ip'] if type(payload) is bytes: payload = bytearray(payload) if type(payload) is bytearray or type(payload) is axis_ep.AXIStreamFrame: self.payload = axis_ep.AXIStreamFrame(payload) if type(payload) is IPFrame: self.payload = axis_ep.AXIStreamFrame(payload.payload) self.eth_dest_mac = payload.eth_dest_mac self.eth_src_mac = payload.eth_src_mac self.eth_type = payload.eth_type self.ip_version = payload.ip_version self.ip_ihl = payload.ip_ihl self.ip_dscp = payload.ip_dscp self.ip_ecn = payload.ip_ecn self.ip_length = payload.ip_length self.ip_identification = payload.ip_identification self.ip_flags = payload.ip_flags self.ip_fragment_offset = payload.ip_fragment_offset self.ip_ttl = payload.ip_ttl self.ip_protocol = payload.ip_protocol self.ip_header_checksum = payload.ip_header_checksum self.ip_source_ip = payload.ip_source_ip self.ip_dest_ip = payload.ip_dest_ip @property def payload(self): return self._payload @payload.setter def payload(self, value): self._payload = axis_ep.AXIStreamFrame(value) def update_length(self): self.ip_length = len(self.payload.data) + 20 def calc_checksum(self): cksum = self.ip_version << 12 | self.ip_ihl << 8 | self.ip_dscp << 2 | self.ip_ecn cksum += self.ip_length cksum += self.ip_identification cksum += self.ip_flags << 13 | self.ip_fragment_offset cksum += self.ip_ttl << 8 | self.ip_protocol cksum += self.ip_source_ip & 0xffff cksum += (self.ip_source_ip >> 16) & 0xffff cksum += self.ip_dest_ip & 0xffff cksum += (self.ip_dest_ip >> 16) & 0xffff cksum = (cksum & 0xffff) + (cksum >> 16) cksum = (cksum & 0xffff) + (cksum >> 16) return ~cksum & 0xffff def update_checksum(self): self.ip_header_checksum = self.calc_checksum() def build(self): if self.ip_length is None: self.update_length() if self.ip_header_checksum is None: self.update_checksum() def build_axis(self): return self.build_eth().build_axis() def build_eth(self): self.build() data = b'' data += struct.pack('B', self.ip_version << 4 | self.ip_ihl) data += struct.pack('B', self.ip_dscp << 2 | self.ip_ecn) data += struct.pack('>H', self.ip_length) data += struct.pack('>H', self.ip_identification) data += struct.pack('>H', self.ip_flags << 13 | self.ip_fragment_offset) data += struct.pack('B', self.ip_ttl) data += struct.pack('B', self.ip_protocol) data += struct.pack('>H', self.ip_header_checksum) data += struct.pack('>L', self.ip_source_ip) data += struct.pack('>L', self.ip_dest_ip) data += self.payload.data return eth_ep.EthFrame(data, self.eth_dest_mac, self.eth_src_mac, self.eth_type) def parse_axis(self, data): frame = eth_ep.EthFrame() frame.parse_axis(data) self.parse_eth(frame) def parse_eth(self, data): self.eth_src_mac = data.eth_src_mac self.eth_dest_mac = data.eth_dest_mac self.eth_type = data.eth_type v = struct.unpack('B', data.payload.data[0:1])[0] self.ip_version = (v >> 4) & 0xF self.ip_ihl = v & 0xF v = struct.unpack('B', data.payload.data[1:2])[0] self.ip_dscp = (v >> 2) & 0x3F self.ip_ecn = v & 0x3 self.ip_length = struct.unpack('>H', data.payload.data[2:4])[0] self.ip_identification = struct.unpack('>H', data.payload.data[4:6])[0] v = struct.unpack('>H', data.payload.data[6:8])[0] self.ip_flags = (v >> 13) & 0x7 self.ip_fragment_offset = v & 0x1FFF self.ip_ttl = struct.unpack('B', data.payload.data[8:9])[0] self.ip_protocol = struct.unpack('B', data.payload.data[9:10])[0] self.ip_header_checksum = struct.unpack('>H', data.payload.data[10:12])[0] self.ip_source_ip = struct.unpack('>L', data.payload.data[12:16])[0] self.ip_dest_ip = struct.unpack('>L', data.payload.data[16:20])[0] self.payload = axis_ep.AXIStreamFrame(data.payload.data[20:]) def __eq__(self, other): if type(other) is IPFrame: return (self.eth_src_mac == other.eth_src_mac and self.eth_dest_mac == other.eth_dest_mac and self.eth_type == other.eth_type and self.ip_version == other.ip_version and self.ip_ihl == other.ip_ihl and self.ip_dscp == other.ip_dscp and self.ip_ecn == other.ip_ecn and self.ip_length == other.ip_length and self.ip_identification == other.ip_identification and self.ip_flags == other.ip_flags and self.ip_fragment_offset == other.ip_fragment_offset and self.ip_ttl == other.ip_ttl and self.ip_protocol == other.ip_protocol and self.ip_header_checksum == other.ip_header_checksum and self.ip_source_ip == other.ip_source_ip and self.ip_dest_ip == other.ip_dest_ip and self.payload == other.payload) def __repr__(self): return (('IPFrame(payload=%s, ' % repr(self.payload)) + ('eth_dest_mac=0x%012x, ' % self.eth_dest_mac) + ('eth_src_mac=0x%012x, ' % self.eth_src_mac) + ('eth_type=0x%04x, ' % self.eth_type) + ('ip_version=%d, ' % self.ip_version) + ('ip_ihl=%d, ' % self.ip_ihl) + ('ip_dscp=%d, ' % self.ip_dscp) + ('ip_ecn=%d, ' % self.ip_ecn) + ('ip_length=%d, ' % self.ip_length) + ('ip_identification=%d, ' % self.ip_identification) + ('ip_flags=%d, ' % self.ip_flags) + ('ip_fragment_offset=%d, ' % self.ip_fragment_offset) + ('ip_ttl=%d, ' % self.ip_ttl) + ('ip_protocol=0x%02x, ' % self.ip_protocol) + ('ip_header_checksum=%x, ' % self.ip_header_checksum) + ('ip_source_ip=0x%08x, ' % self.ip_source_ip) + ('ip_dest_ip=0x%08x)' % self.ip_dest_ip)) def IPFrameSource(clk, rst, ip_hdr_valid=None, ip_hdr_ready=None, eth_dest_mac=Signal(intbv(0)[48:]), eth_src_mac=Signal(intbv(0)[48:]), eth_type=Signal(intbv(0)[16:]), ip_version=Signal(intbv(4)[4:]), ip_ihl=Signal(intbv(5)[4:]), ip_dscp=Signal(intbv(0)[6:]), ip_ecn=Signal(intbv(0)[2:]), ip_length=Signal(intbv(0)[16:]), ip_identification=Signal(intbv(0)[16:]), ip_flags=Signal(intbv(0)[3:]), ip_fragment_offset=Signal(intbv(0)[13:]), ip_ttl=Signal(intbv(0)[8:]), ip_protocol=Signal(intbv(0)[8:]), ip_header_checksum=Signal(intbv(0)[16:]), ip_source_ip=Signal(intbv(0)[32:]), ip_dest_ip=Signal(intbv(0)[32:]), ip_payload_tdata=None, ip_payload_tkeep=Signal(bool(True)), ip_payload_tvalid=Signal(bool(False)), ip_payload_tready=Signal(bool(True)), ip_payload_tlast=Signal(bool(False)), ip_payload_tuser=Signal(bool(False)), fifo=None, pause=0, name=None): ip_hdr_ready_int = Signal(bool(False)) ip_hdr_valid_int = Signal(bool(False)) ip_payload_pause = Signal(bool(False)) ip_payload_fifo = Queue() ip_payload_source = axis_ep.AXIStreamSource(clk, rst, tdata=ip_payload_tdata, tkeep=ip_payload_tkeep, tvalid=ip_payload_tvalid, tready=ip_payload_tready, tlast=ip_payload_tlast, tuser=ip_payload_tuser, fifo=ip_payload_fifo, pause=ip_payload_pause) @always_comb def pause_logic(): ip_hdr_ready_int.next = ip_hdr_ready and not pause ip_hdr_valid.next = ip_hdr_valid_int and not pause ip_payload_pause.next = pause # or ip_hdr_valid_int @instance def logic(): frame = dict() while True: yield clk.posedge, rst.posedge if rst: ip_hdr_valid_int.next = False else: if ip_hdr_ready_int: ip_hdr_valid_int.next = False if (ip_payload_tlast and ip_hdr_ready_int and ip_hdr_valid) or not ip_hdr_valid_int: if not fifo.empty(): frame = fifo.get() frame = IPFrame(frame) frame.build() eth_dest_mac.next = frame.eth_dest_mac eth_src_mac.next = frame.eth_src_mac eth_type.next = frame.eth_type ip_version.next = frame.ip_version ip_ihl.next = frame.ip_ihl ip_dscp.next = frame.ip_dscp ip_ecn.next = frame.ip_ecn ip_length.next = frame.ip_length ip_identification.next = frame.ip_identification ip_flags.next = frame.ip_flags ip_fragment_offset.next = frame.ip_fragment_offset ip_ttl.next = frame.ip_ttl ip_protocol.next = frame.ip_protocol ip_header_checksum.next = frame.ip_header_checksum ip_source_ip.next = frame.ip_source_ip ip_dest_ip.next = frame.ip_dest_ip ip_payload_fifo.put(frame.payload) if name is not None: print("[%s] Sending frame %s" % (name, repr(frame))) ip_hdr_valid_int.next = True return logic, pause_logic, ip_payload_source def IPFrameSink(clk, rst, ip_hdr_valid=None, ip_hdr_ready=None, eth_dest_mac=Signal(intbv(0)[48:]), eth_src_mac=Signal(intbv(0)[48:]), eth_type=Signal(intbv(0)[16:]), ip_version=Signal(intbv(4)[4:]), ip_ihl=Signal(intbv(5)[4:]), ip_dscp=Signal(intbv(0)[6:]), ip_ecn=Signal(intbv(0)[2:]), ip_length=Signal(intbv(0)[16:]), ip_identification=Signal(intbv(0)[16:]), ip_flags=Signal(intbv(0)[3:]), ip_fragment_offset=Signal(intbv(0)[13:]), ip_ttl=Signal(intbv(0)[8:]), ip_protocol=Signal(intbv(0)[8:]), ip_header_checksum=Signal(intbv(0)[16:]), ip_source_ip=Signal(intbv(0)[32:]), ip_dest_ip=Signal(intbv(0)[32:]), ip_payload_tdata=None, ip_payload_tkeep=Signal(bool(True)), ip_payload_tvalid=Signal(bool(True)), ip_payload_tready=Signal(bool(True)), ip_payload_tlast=Signal(bool(True)), ip_payload_tuser=Signal(bool(False)), fifo=None, pause=0, name=None): ip_hdr_ready_int = Signal(bool(False)) ip_hdr_valid_int = Signal(bool(False)) ip_payload_pause = Signal(bool(False)) ip_payload_fifo = Queue() ip_header_fifo = Queue() ip_payload_sink = axis_ep.AXIStreamSink(clk, rst, tdata=ip_payload_tdata, tkeep=ip_payload_tkeep, tvalid=ip_payload_tvalid, tready=ip_payload_tready, tlast=ip_payload_tlast, tuser=ip_payload_tuser, fifo=ip_payload_fifo, pause=ip_payload_pause) @always_comb def pause_logic(): ip_hdr_ready.next = ip_hdr_ready_int and not pause ip_hdr_valid_int.next = ip_hdr_valid and not pause ip_payload_pause.next = pause # or ip_hdr_valid_int @instance def logic(): frame = IPFrame() while True: yield clk.posedge, rst.posedge if rst: ip_hdr_ready_int.next = False frame = IPFrame() else: ip_hdr_ready_int.next = True if ip_hdr_ready_int and ip_hdr_valid_int: frame = IPFrame() frame.eth_dest_mac = int(eth_dest_mac) frame.eth_src_mac = int(eth_src_mac) frame.eth_type = int(eth_type) frame.ip_version = int(ip_version) frame.ip_ihl = int(ip_ihl) frame.ip_dscp = int(ip_dscp) frame.ip_ecn = int(ip_ecn) frame.ip_length = int(ip_length) frame.ip_identification = int(ip_identification) frame.ip_flags = int(ip_flags) frame.ip_fragment_offset = int(ip_fragment_offset) frame.ip_ttl = int(ip_ttl) frame.ip_protocol = int(ip_protocol) frame.ip_header_checksum = int(ip_header_checksum) frame.ip_source_ip = int(ip_source_ip) frame.ip_dest_ip = int(ip_dest_ip) ip_header_fifo.put(frame) if not ip_payload_fifo.empty() and not ip_header_fifo.empty(): frame = ip_header_fifo.get() frame.payload = ip_payload_fifo.get() fifo.put(frame) # ensure all payloads have been matched to headers if ip_header_fifo.empty(): assert ip_payload_fifo.empty() if name is not None: print("[%s] Got frame %s" % (name, repr(frame))) frame = dict() return logic, pause_logic, ip_payload_sink