def __init__(self, clock): """ The EMesh interface on the external ELinks is defined as having three EMeshPacket conduits. These conduits are used to send write and read requests over the Elink. """ self.clock = clock # the interface clock self.txwr = EMeshPacket() # TX write, send write commands self.txrd = EMeshPacket() # TX read, send read commands self.txrr = EMeshPacket() # TX read response, acknowledge external read commands self.rxwr = EMeshPacket() # RX write, receive external write commands self.rxrd = EMeshPacket() # RX read, receive external read commands self.rxrr = EMeshPacket() # RX read response, receive read acknowledge # transmit and receive FIFOs - simulation only # @todo: want these to be private (_) but then a bunch of # @todo: methods need to be added to wait on packet events, etc. self.packet_types = ('wr', 'rd', 'rr',) self.txwr_fifo = FIFO() self.txrd_fifo = FIFO() self.txrr_fifo = FIFO() self.rxwr_fifo = FIFO() self.rxrd_fifo = FIFO() self.rxrr_fifo = FIFO() self._outstanding_reads = {}
def __init__(self): self._tx = ELinkChannel() self._rx = ELinkChannel() self._tx_fifo = FIFO() self._rx_fifo = FIFO() # Keep track how this interface is connected, only an east-west or # north-south connections can be established (not both). # The east-west and north-south are redundant but commonly used. self.connections = {"east": False, "west": False, "north": False, "south": False}
def __init__(self): self._tx = ELinkChannel() self._rx = ELinkChannel() self._tx_fifo = FIFO() self._rx_fifo = FIFO() # Keep track how this interface is connected, only an east-west or # north-south connections can be established (not both). # The east-west and north-south are redundant but commonly used. self.connections = {'east': False, 'west': False, 'north': False, 'south': False}
def elink_asic_model(elink): """ Model a simple ELink device (something like Epiphany) Arguments: elink: Interface to the external ELink enabled device myhdl not convertible. """ assert isinstance(elink, ELink) # get the tx and rx links based on this logical location tx, rx = elink.connect('south') gclk = tx.instances() pkt_i_fifo = FIFO(depth=128) pkt_o_fifo = FIFO(depth=128) @instance def p_rx_packets(): """ receive packets and push onto processing FIFO """ while True: yield rx.frame.posedge bytes = [] yield rx.lclk.posedge while rx.frame: bytes.append(intbv(int(rx.data))[8:]) if len(bytes) == 13: pkt = EMeshPacket() pkt.frombytes(bytes) yield delay(1) pkt_i_fifo.write(pkt) # print("[easic] RX packet {} {}".format(pkt, pkt_i_fifo)) # @todo: if FIFO full assert wait bytes = [] yield rx.lclk.posedge # @todo: if len(bytes) != 13 report error - partial packet # @todo: simulate EMesh routing @instance def p_proc_packets(): """ process packets """ idelay = False while True: pkt = pkt_i_fifo.read() if pkt is not None and pkt.access: if not idelay: yield delay(17) idelay = True pkt_o_fifo.write(pkt) # print("[easic] PROC packet {} {}".format(pkt, pkt_o_fifo)) if pkt_i_fifo.is_empty(): idelay = False yield pkt_i_fifo.empty.negedge @instance def p_tx_packets(): """ transmit processed packets """ while True: if not pkt_o_fifo.is_empty(): pkt = pkt_o_fifo.read() # print("[easic] TX packet {} {}".format(pkt, pkt_o_fifo)) # @todo: if len of FIFO > 2, shuffle order bytes = pkt.tobytes() for bb in bytes: tx.frame.next = True tx.data.next = bb yield tx.lclk.posedge # packet complete clear frame tx.frame.next = False if pkt_o_fifo.is_empty(): yield pkt_o_fifo.empty.negedge else: yield tx.lclk.posedge return myhdl.instances()
class ELink(object): """ The ELink interface is the external interface between devices (typically the Adapteva Epiphany and an FPGA). @todo: more description ... The Epiphany datasheet (has a description of the chip-to-chip (ELink) interfaces): http://www.adapteva.com/docs/e16g301_datasheet.pdf The Parallella open-hardware (oh) repository: https://github.com/parallella/oh/tree/master/elink """ def __init__(self): self._tx = ELinkChannel() self._rx = ELinkChannel() self._tx_fifo = FIFO() self._rx_fifo = FIFO() # Keep track how this interface is connected, only an east-west or # north-south connections can be established (not both). # The east-west and north-south are redundant but commonly used. self.connections = { 'east': False, 'west': False, 'north': False, 'south': False } def connect(self, pos): """ Return relative relation In this implementation the TX and RX are from the perspective of the external link (e.g. the FPGA link). This function is used to get local perspective. :param pos: where the component is logically positioned (located) :return: tx link, rx link """ pos = pos.lower() assert pos in self.connections assert not self.connections[pos], "{} connection exists".format(pos) self.connections[pos] = True if pos in ('east', 'north'): links = self._tx, self._rx elif pos in ('west', 'south'): links = self._rx, self._tx return links @myhdl.block def instances(self): return self._tx.instances(), self._rx.instances() def write(self, dstaddr, data, srcaddr=0, block=True): """A single ELink write transaction """ packet = EMeshPacket(access=1, write=1, datamode=2, dstaddr=dstaddr, data=data, srcaddr=srcaddr) bytes = packet.tobytes() bpkt = _ELinkTransaction(bytes) self._tx_fifo.write(bpkt) if block: yield bpkt.finished.posedge def read(self, dstaddr, data, srcaddr=0, block=True): """ A single ELink read transaction """ packet = EMeshPacket(access=1, write=0, datamode=2, dstaddr=dstaddr, data=data, srcaddr=srcaddr) bytes = packet.tobytes() tpkt = _ELinkTransaction(bytes) self._tx_fifo.append(tpkt) if block: yield tpkt.finished.posedge def send_packet(self, emesh, block=True): bytes = emesh.tobytes() tpkt = _ELinkTransaction(bytes) self._tx_fifo.write(tpkt) if block: yield tpkt.finished.posedge def receive_packet(self, emesh, block=True): if self._rx_fifo.is_empty() and block: yield self._rx_fifo.empty.negedge # if blocked will not be empty, if not block maybe if not self._rx_fifo.is_empty(): tpkt = self._rx_fifo.read() emesh.frombytes(tpkt.bytes) # allow emesh to update yield self._rx.lclk.posedge def write_bytes(self, bytes, block=True): assert isinstance(bytes, (list, tuple)) def read_bytes(self, bytes, block=True): assert isinstance(bytes, list) @myhdl.block def process(self): """ Drive the ELink signals This process mimics the behavior of the external ELink logic. @todo: this really needs to exist in a specific module model??? myhdl not convertible """ @instance def tx_bytes(): while True: if not self._tx_fifo.is_empty(): pkt = self._tx_fifo.read() assert isinstance(pkt, _ELinkTransaction) yield self._send_bytes(pkt.bytes) pkt.finished.next = True else: yield self._tx_fifo.empty.negedge @instance def rx_bytes(): while True: bytes = [None for _ in range(13)] yield self._receive_bytes(bytes) self._rx_fifo.write(_ELinkTransaction(bytes)) return tx_bytes, rx_bytes def _send_bytes(self, bytes): yield self._tx.lclk.posedge self._tx.frame.next = True for ii in range(13): self._tx.data.next = bytes[ii] yield self._tx.lclk.posedge self._tx.frame.next = False yield self._tx.lclk.posedge def _receive_bytes(self, bytes): ri = 0 while ri < 13: yield self._rx.lclk.posedge if self._rx.frame: bytes[ri] = intbv(int(self._rx.data))[8:0] ri += 1
class EMesh(object): def __init__(self, clock): """ The EMesh interface on the external ELinks is defined as having three EMeshPacket conduits. These conduits are used to send write and read requests over the Elink. """ self.clock = clock # the interface clock self.txwr = EMeshPacket() # TX write, send write commands self.txrd = EMeshPacket() # TX read, send read commands self.txrr = EMeshPacket() # TX read response, acknowledge external read commands self.rxwr = EMeshPacket() # RX write, receive external write commands self.rxrd = EMeshPacket() # RX read, receive external read commands self.rxrr = EMeshPacket() # RX read response, receive read acknowledge # transmit and receive FIFOs - simulation only # @todo: want these to be private (_) but then a bunch of # @todo: methods need to be added to wait on packet events, etc. self.packet_types = ('wr', 'rd', 'rr',) self.txwr_fifo = FIFO() self.txrd_fifo = FIFO() self.txrr_fifo = FIFO() self.rxwr_fifo = FIFO() self.rxrd_fifo = FIFO() self.rxrr_fifo = FIFO() self._outstanding_reads = {} def __str__(self): s = "txwr: {}, txrd: {}, txrr: {}, rxwr: {}, rxrd: {}, rxrr: {}".format( self.txwr_fifo.count, self.txrd_fifo.count, self.txrr_fifo.count, self.rxwr_fifo.count, self.rxrd_fifo.count, self.rxrr_fifo.count) return s def set_clock(self, clock): self.clock = clock def write(self, dstaddr, data, datau=0): """ send a write packet :param dstaddr: destination address for the write :param data: 32bit data for the write :param datau: upper 32bit data for the write (64bit write) :return: @todo: add explaination why a separate packet is used to push @todo: onto the transaction FIFOs (needs copies on the FIFOs @todo: and not actual bus interface). not convertible. """ # get a new packet for the transaction emulation pkt = EMeshPacket(access=True, write=True, dstaddr=dstaddr, data=data, srcaddr=datau) # push the packet onto the TX write FIFO # also assign the txwr packet to the new packet, the txwr # will mirror the transaction packet self.txwr.assign(pkt) self.txwr_fifo.write(pkt) yield self.clock.posedge self.txwr.clear() def read(self, dstaddr, data, srcaddr): """ send a read packet :param dstaddr: :param data: :param srcaddr: :return: not convertible. """ # sent a read packet through the txrd fifo pkt = EMeshPacket(access=True, write=False, dstaddr=dstaddr, data=data, srcaddr=srcaddr) # push the packet onto the TX read FIFO self.txrd_fifo.write(pkt) self._outstanding_reads[dstaddr] = pkt yield self.clock.posedge def read_response(self, read_packet): """ send a read response :param read_packet: :return: @todo: complete not convertible. """ pass def route_to_fifo(self, pkt): """ take a freshly recieved packet from the ELink interface Take a freshly received packet from the ELink interface and route it to the correct RX fifo. :param pkt: :return: not convertible """ # if the write bit is set pass it to RX write FIFO # @todo: how to determine the other packets?? if pkt.write: self.rxwr_fifo.write(pkt) else: # determine if this is a read-request or read-response. pass def get_packet(self, pkt_type): """ Get a packet from one of the channels :param pkt_type: :return: """ assert pkt_type in self.packet_types pkt = None if pkt_type == 'wr': pkt = self.rxwr_fifo.read() elif pkt_type == 'rd': pkt = self.rxrd_fifo.read() elif pkt_type == 'rr': pkt = self.rxrr_fifo.read() return pkt
def __init__(self, clock): """ The EMesh interface on the external ELinks is defined as having three EMeshPacket conduits. These conduits are used to send write and read requests over the Elink. """ self.clock = clock # the interface clock self.txwr = EMeshPacket() # TX write, send write commands self.txrd = EMeshPacket() # TX read, send read commands self.txrr = EMeshPacket( ) # TX read response, acknowledge external read commands self.rxwr = EMeshPacket() # RX write, receive external write commands self.rxrd = EMeshPacket() # RX read, receive external read commands self.rxrr = EMeshPacket() # RX read response, receive read acknowledge # transmit and receive FIFOs - simulation only # @todo: want these to be private (_) but then a bunch of # @todo: methods need to be added to wait on packet events, etc. self.packet_types = ( 'wr', 'rd', 'rr', ) self.txwr_fifo = FIFO() self.txrd_fifo = FIFO() self.txrr_fifo = FIFO() self.rxwr_fifo = FIFO() self.rxrd_fifo = FIFO() self.rxrr_fifo = FIFO() self._outstanding_reads = {}
class EMesh(object): def __init__(self, clock): """ The EMesh interface on the external ELinks is defined as having three EMeshPacket conduits. These conduits are used to send write and read requests over the Elink. """ self.clock = clock # the interface clock self.txwr = EMeshPacket() # TX write, send write commands self.txrd = EMeshPacket() # TX read, send read commands self.txrr = EMeshPacket( ) # TX read response, acknowledge external read commands self.rxwr = EMeshPacket() # RX write, receive external write commands self.rxrd = EMeshPacket() # RX read, receive external read commands self.rxrr = EMeshPacket() # RX read response, receive read acknowledge # transmit and receive FIFOs - simulation only # @todo: want these to be private (_) but then a bunch of # @todo: methods need to be added to wait on packet events, etc. self.packet_types = ( 'wr', 'rd', 'rr', ) self.txwr_fifo = FIFO() self.txrd_fifo = FIFO() self.txrr_fifo = FIFO() self.rxwr_fifo = FIFO() self.rxrd_fifo = FIFO() self.rxrr_fifo = FIFO() self._outstanding_reads = {} def __str__(self): s = "txwr: {}, txrd: {}, txrr: {}, rxwr: {}, rxrd: {}, rxrr: {}".format( self.txwr_fifo.count, self.txrd_fifo.count, self.txrr_fifo.count, self.rxwr_fifo.count, self.rxrd_fifo.count, self.rxrr_fifo.count) return s def set_clock(self, clock): self.clock = clock def write(self, dstaddr, data, datau=0): """ send a write packet Arguments: dstaddr: destination address for the write data: 32bit data for the write datau: upper 32bit data for the write (64bit write) @todo: add explanation why a separate packet is used to push onto the transaction FIFOs (needs copies on the FIFOs and not actual bus interface). myhdl not convertible. """ # get a new packet for the transaction emulation pkt = EMeshPacket(access=True, write=True, dstaddr=dstaddr, data=data, srcaddr=datau) # push the packet onto the TX write FIFO # also assign the txwr packet to the new packet, the txwr # will mirror the transaction packet self.txwr.assign(pkt) self.txwr_fifo.write(pkt) yield self.clock.posedge self.txwr.clear() def read(self, dstaddr, data, srcaddr): """ send a read packet Arguments: dstaddr: data: srcaddr: myhdl not convertible. """ # sent a read packet through the txrd fifo pkt = EMeshPacket(access=True, write=False, dstaddr=dstaddr, data=data, srcaddr=srcaddr) # push the packet onto the TX read FIFO self.txrd_fifo.write(pkt) self._outstanding_reads[dstaddr] = pkt yield self.clock.posedge def read_response(self, read_packet): """ send a read response Arguments: read_packet: @todo: complete myhdl not convertible. """ pass def route_to_fifo(self, pkt): """ take a freshly received packet from the ELink interface Take a freshly received packet from the ELink interface and route it to the correct RX fifo. Arguments: pkt: myhdl not convertible """ # if the write bit is set pass it to RX write FIFO # @todo: how to determine the other packets?? if pkt.write: self.rxwr_fifo.write(pkt) else: # determine if this is a read-request or read-response. pass def get_packet(self, pkt_type): """ Get a packet from one of the channels :param pkt_type: :return: """ assert pkt_type in self.packet_types pkt = None if pkt_type == 'wr': pkt = self.rxwr_fifo.read() elif pkt_type == 'rd': pkt = self.rxrd_fifo.read() elif pkt_type == 'rr': pkt = self.rxrr_fifo.read() return pkt
class ELink(object): """ The ELink interface is the external interface between devices (typically the Adapteva Epiphany and an FPGA). @todo: more description ... The Epiphany datasheet (has a description of the chip-to-chip (ELink) interfaces): http://www.adapteva.com/docs/e16g301_datasheet.pdf The Parallella open-hardware (oh) repository: https://github.com/parallella/oh/tree/master/elink """ def __init__(self): self._tx = ELinkChannel() self._rx = ELinkChannel() self._tx_fifo = FIFO() self._rx_fifo = FIFO() # Keep track how this interface is connected, only an east-west or # north-south connections can be established (not both). # The east-west and north-south are redundant but commonly used. self.connections = {'east': False, 'west': False, 'north': False, 'south': False} def connect(self, pos): """ Return relative relation In this implementation the TX and RX are from the perspective of the external link (e.g. the FPGA link). This function is used to get local perspective. :param pos: where the component is logically positioned (located) :return: tx link, rx link """ pos = pos.lower() assert pos in self.connections assert not self.connections[pos], "{} connection exists".format(pos) self.connections[pos] = True if pos in ('east', 'north'): links = self._tx, self._rx elif pos in ('west', 'south'): links = self._rx, self._tx return links @myhdl.block def instances(self): return self._tx.instances(), self._rx.instances() def write(self, dstaddr, data, srcaddr=0, block=True): """A single ELink write transaction """ packet = EMeshPacket(access=1, write=1, datamode=2, dstaddr=dstaddr, data=data, srcaddr=srcaddr) bytes = packet.tobytes() bpkt = _ELinkTransaction(bytes) self._tx_fifo.write(bpkt) if block: yield bpkt.finished.posedge def read(self, dstaddr, data, srcaddr=0, block=True): """ A single ELink read transaction """ packet = EMeshPacket(access=1, write=0, datamode=2, dstaddr=dstaddr, data=data, srcaddr=srcaddr) bytes = packet.tobytes() tpkt = _ELinkTransaction(bytes) self._tx_fifo.append(tpkt) if block: yield tpkt.finished.posedge def send_packet(self, emesh, block=True): bytes = emesh.tobytes() tpkt = _ELinkTransaction(bytes) self._tx_fifo.write(tpkt) if block: yield tpkt.finished.posedge def receive_packet(self, emesh, block=True): if self._rx_fifo.is_empty() and block: yield self._rx_fifo.empty.negedge # if blocked will not be empty, if not block maybe if not self._rx_fifo.is_empty(): tpkt = self._rx_fifo.read() emesh.frombytes(tpkt.bytes) # allow emesh to update yield self._rx.lclk.posedge def write_bytes(self, bytes, block=True): assert isinstance(bytes, (list, tuple)) def read_bytes(self, bytes, block=True): assert isinstance(bytes, list) @myhdl.block def process(self): """ Drive the ELink signals This process mimics the behavior of the external ELink logic. @todo: this really needs to exist in a specific module model??? myhdl not convertible """ @instance def tx_bytes(): while True: if not self._tx_fifo.is_empty(): pkt = self._tx_fifo.read() assert isinstance(pkt, _ELinkTransaction) yield self._send_bytes(pkt.bytes) pkt.finished.next = True else: yield self._tx_fifo.empty.negedge @instance def rx_bytes(): while True: bytes = [None for _ in range(13)] yield self._receive_bytes(bytes) self._rx_fifo.write(_ELinkTransaction(bytes)) return tx_bytes, rx_bytes def _send_bytes(self, bytes): yield self._tx.lclk.posedge self._tx.frame.next = True for ii in range(13): self._tx.data.next = bytes[ii] yield self._tx.lclk.posedge self._tx.frame.next = False yield self._tx.lclk.posedge def _receive_bytes(self, bytes): ri = 0 while ri < 13: yield self._rx.lclk.posedge if self._rx.frame: bytes[ri] = intbv(int(self._rx.data))[8:0] ri += 1