<commit_before>"""
http_client.py - asynchronous HTTP client library.

Copyright (c) 2008-2009 Mark Nottingham

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.
"""

import sys
import re
import errno
from urlparse import urlsplit, urlunsplit

import push_tcp

LWS = re.compile("\r?\n[ \t]+", re.M)
HDR_END = re.compile(r"\r?\n\r?\n", re.M)

# conn_modes
CLOSE = 0
COUNTED = 1
CHUNKED = 2

# states
WAITING = 0
HEADERS_DONE = 1
BODY_DONE = 2

# TODO: make object reusable for pipelining?
# TODO: expect/continue
# TODO: proxy support
# TODO: review HTTP requirements

class HttpClient:
    safe_methods = ['GET', 'HEAD']
    no_body_status = ['304', '204']
    retry_limit = 2

    def __init__(self, res_start_cb, res_body_write_cb, res_body_done_cb, timeout=None):
        self.res_start_cb = res_start_cb
        self.res_body_write_cb = res_body_write_cb
        self.res_body_done_cb = res_body_done_cb
        self.method = ""
        self.uri = ""
        self.req_headers = []
        self.tcp_conn = None
        self._conn_mode = None
        self._conn_reusable = False
        self._input_buffer = ""
        self._req_state = WAITING
        self._req_body_sent = 0
        self._res_state = WAITING
        self._res_body_left = 0
        self._paused = False
        self._retries = 0
        self._timeout_event = None

    def start_request(self, method, uri, req_headers={}):
        assert self._req_state == WAITING
        self.method = method
        self.uri = uri
        self.req_headers = req_headers
        (scheme, authority, path, query, fragment) = urlsplit(self.uri)
        assert scheme.lower() == 'http', "Only HTTP URLs are supported"
        if ":" in authority:
            host, port = authority.rsplit(":", 1)
            assert port.isdigit(), "Non-integer port"
            port = int(port)
        else:
            host, port = authority, 80
        self.req_headers.append(("Host", authority)) # FIXME: make sure it's the only one
        self.req_headers.append(("Connection", "keep-alive"))
        self.uri = urlunsplit(('', '', path, query, ''))
        pool.attach(host, port, self.handle_connect, self.handle_connect_error)
        return self.req_body_write, self.req_body_done

    def handle_connect(self, tcp_conn):
        self.tcp_conn = tcp_conn
        hdr_block = ["%s %s HTTP/1.1" % (self.method, self.uri)]
        [hdr_block.append("%s: %s" % (k,v)) for k, v in self.req_headers]
        hdr_block.append("")
        hdr_block.append("")
        self.tcp_conn.write("\r\n".join(hdr_block))
        self._req_state = HEADERS_DONE
        return self.handle_input, self.conn_closed, self.pause_request

    def handle_connect_error(self, host, port, err):
        import os
        self._http_error("504", "Gateway Timeout", os.strerror(err))

    def req_body_write(self, data):
        assert self._req_state == HEADERS_DONE
        assert self.req_headers.has_key('content-length'), "Request bodies require content-length"
        #TODO: deal with pausing
        self._req_body_sent += len(data)
        self.tcp_conn.write(data)
        assert self._req_body_sent <= int(self.req_headers['content-length'][0]), \
            "Too many request body bytes sent"

    def req_body_done(self, data):
        assert self._req_state == HEADERS_DONE
        self._req_state = BODY_DONE

    def handle_input(self, instr):
        if self._input_buffer:
            instr = self._input_buffer + instr # TODO: perf
            self._input_buffer = ""
        if self._res_state == WAITING:
            if re.search(HDR_END, instr): # found one
                self._res_state = HEADERS_DONE
                rest = self._parse_response(instr)
                self.handle_input(rest)
            else: # partial headers; store it and wait for more
                self._input_buffer = instr
        else:
            if self._conn_mode == CLOSE:
                self.res_body_write_cb(instr)
            elif self._conn_mode == CHUNKED:
                if self._res_body_left > 0:
                    if self._res_body_left < len(instr): # got more than the chunk
                        this_chunk = self._res_body_left
                        self.res_body_write_cb(instr[:this_chunk])
                        self._res_body_left = -1
                        return self.handle_input(instr[this_chunk+2:]) # +2 consumes the CRLF
                    elif self._res_body_left == len(instr): # got the whole chunk exactly
                        self.res_body_write_cb(instr)
                        self._res_body_left = -1
                    else: # got partial chunk
                        self.res_body_write_cb(instr)
                        self._res_body_left -= len(instr)
                elif self._res_body_left == 0: # done
                    self.res_body_done(True) # TODO: trailers, consume last CRLF
                else: # new chunk
                    try:
                        # they really need to use CRLF
                        chunk_size, instr = instr.split("\r\n", 1)
                    except ValueError:
                        # got a CRLF without anything behind it.. wait.
                        self._input_buffer += instr
                        return
                    if chunk_size.strip() == "": # ignore bare lines
                        return self.handle_input(instr)
                    if ";" in chunk_size: # ignore chunk extensions
                        chunk_size = chunk_size.split(";", 1)[0]
                    self._res_body_left = int(chunk_size, 16)
                    self.handle_input(instr)
            elif self._conn_mode == COUNTED:
                assert self._res_body_left >= 0, "response bytecounting problem! (%s)" % self._res_body_left
                # process response body
                if self._res_body_left <= len(instr): # got it all (and more?)
                    self.res_body_write_cb(instr[:self._res_body_left])
                    self.res_body_done(True)
                    # TODO: will we ever get anything on instr[self._res_body_left:]?
                else: # got some of it
                    self.res_body_write_cb(instr)
                    self._res_body_left -= len(instr)
            else:
                raise Exception, "Unknown conn_mode"

    def conn_closed(self):
        if self._input_buffer:
            self.handle_input("")
            if self._res_state == BODY_DONE:
                return # we've seen the whole body now.
        if self._conn_mode == CLOSE:
            self.res_body_done(True)
        else:
            if self.method in self.safe_methods and self._retries < self.retry_limit:
                # FIXME: this is probably dangerous past WAITING. Clean up state before we retry.
                self._retries += 1
                pool.attach(self.tcp_conn.host, self.tcp_conn.port,
                    self.handle_connect, self.handle_connect_error)
            else:
                self._http_error("504", "Gateway Timeout", "connection closed")

    def res_body_done(self, complete):
        assert self._res_state < BODY_DONE
        self._res_state = BODY_DONE
        if self.tcp_conn and self.tcp_conn.tcp_connected:
            def unexpected_read(data):
                if data:
                    self._conn_mode = CLOSE
                    self.res_body_write_cb(data)
            self.tcp_conn.read_cb = unexpected_read
            def idle_close():
                pass
            self.tcp_conn.close_cb = idle_close
            if self._conn_reusable:
                pool.release(self.tcp_conn)
            else:
                del self.tcp_conn
        self.res_body_done_cb(complete)

    def pause_request(self, paused):
        self._paused = paused

    def _parse_response(self, instr):
        top, rest = re.split(HDR_END, instr, 1)
        res_hdr_lines = LWS.sub(" ", top).splitlines()
        res_line = res_hdr_lines.pop(0)
        try:
            res_version, status_txt = res_line.split(None, 1)
            res_version = float(res_version.rsplit('/', 1)[1])
        except (ValueError, IndexError), why:
            return self._http_error("502", "Bad Gateway", why)
        try:
            res_code, res_phrase = status_txt.split(None, 1)
        except ValueError:
            res_code = status_txt
            res_phrase = ""
        res_headers = []
        connection_tokens = []
        transfer_codes = []
        content_length = None
        for line in res_hdr_lines:
            try:
                fn, fv = line.split(":", 1)
                fn = fn.strip()
                res_headers.append((fn, fv.strip()))
            except ValueError, why:
                return self._http_error("502", "Bad Gateway", why)
            f_name = fn.lower()
            if f_name == "connection":
                connection_tokens += [v.strip().lower() for v in fv.split(',')]
            elif f_name == "transfer-encoding":
                transfer_codes += [v.strip().lower() for v in fv.split(',')]
            elif f_name == "content-length":
                try:
                    content_length = int(fv)
                except ValueError, why:
                    return self._http_error("502" "Bad Gateway", why)

        if self.method == "HEAD" or res_code in self.no_body_status:
            self._conn_mode = COUNTED
            self._res_body_left = 0
        elif res_version > 1.0: # HTTP/1.1
            if len(transfer_codes) > 0:
                if 'chunked' in transfer_codes:
                    self._conn_mode = CHUNKED
                    self._res_body_left = -1 # flag that we don't know
                else:
                    self._conn_mode = CLOSE
            elif content_length != None:
                self._conn_mode = COUNTED
                self._res_body_left = content_length
            elif 'close' in connection_tokens:
                self._conn_mode = CLOSE
            else: # assume 0 length body
                self._conn_mode = COUNTED
                self._res_body_left = 0
        else: # HTTP/1.0
            if content_length != None and 'keep-alive' in connection_tokens:
                self._conn_mode = COUNTED
                self._res_body_left = content_length
            else:
                self._conn_mode = CLOSE
        if 'close' not in connection_tokens:
            if (res_version == 1.0 and 'keep-alive' in connection_tokens) or \
                res_version > 1.0:
                self._conn_reusable = True
        self.res_start_cb(res_version, res_code, res_phrase, res_headers)
        return rest

    def _http_error(self, status_code, status_phrase, body=""):
        self._conn_mode = CLOSE
        self.res_start_cb("1.1", status_code, status_phrase, [])
        self.res_body_write_cb(str(body))
        self.res_body_done(False)
        if hasattr(self, 'tcp_conn') and self.tcp_conn and self.tcp_conn.tcp_connected:
            self.tcp_conn.close()
        return ""

class HttpClientConnectionPool:
    connect_timeout = 3
    _conns = {} # TODO: clean up old conns

    def attach(self, host, port, handle_connect, handle_connect_error):
        while True:
            try:
                idle_conn = self._conns[(host, port)].pop()
            except (IndexError, KeyError):
                push_tcp.Client(host, port, handle_connect, handle_connect_error, self.connect_timeout)
                break
            if idle_conn.tcp_connected:
                idle_conn.read_cb, idle_conn.close_cb, idle_conn.pause_cb = handle_connect(idle_conn)
                break

    def release(self, tcp_conn):
        if tcp_conn.tcp_connected:
            if not self._conns.has_key((tcp_conn.host, tcp_conn.port)):
                self._conns[(tcp_conn.host, tcp_conn.port)] = [tcp_conn]
            else:
                self._conns[(tcp_conn.host, tcp_conn.port)].append(tcp_conn)

# create a singleton pool.
pool = HttpClientConnectionPool()

run = push_tcp.run
stop = push_tcp.stop

if __name__ == "__main__":
    request_uri = sys.argv[1]
    def printer(version, status, phrase, headers):
        print version, status, phrase
        print repr(headers)
    def done(self, complete):
        stop()
    c = HttpClient(printer, sys.stdout.write, done)
    req_body_write, req_body_done = c.start_request("GET", request_uri)
    run()
<commit_msg>[issue 17] Correct URLs with no path to '/'<commit_after>"""
http_client.py - asynchronous HTTP client library.

Copyright (c) 2008-2009 Mark Nottingham

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.
"""

import sys
import re
import errno
from urlparse import urlsplit, urlunsplit

import push_tcp

LWS = re.compile("\r?\n[ \t]+", re.M)
HDR_END = re.compile(r"\r?\n\r?\n", re.M)

# conn_modes
CLOSE = 0
COUNTED = 1
CHUNKED = 2

# states
WAITING = 0
HEADERS_DONE = 1
BODY_DONE = 2

# TODO: make object reusable for pipelining?
# TODO: expect/continue
# TODO: proxy support
# TODO: review HTTP requirements

class HttpClient:
    safe_methods = ['GET', 'HEAD']
    no_body_status = ['304', '204']
    retry_limit = 2

    def __init__(self, res_start_cb, res_body_write_cb, res_body_done_cb, timeout=None):
        self.res_start_cb = res_start_cb
        self.res_body_write_cb = res_body_write_cb
        self.res_body_done_cb = res_body_done_cb
        self.method = ""
        self.uri = ""
        self.req_headers = []
        self.tcp_conn = None
        self._conn_mode = None
        self._conn_reusable = False
        self._input_buffer = ""
        self._req_state = WAITING
        self._req_body_sent = 0
        self._res_state = WAITING
        self._res_body_left = 0
        self._paused = False
        self._retries = 0
        self._timeout_event = None

    def start_request(self, method, uri, req_headers={}):
        assert self._req_state == WAITING
        self.method = method
        self.uri = uri
        self.req_headers = req_headers
        (scheme, authority, path, query, fragment) = urlsplit(self.uri)
        assert scheme.lower() == 'http', "Only HTTP URLs are supported"
        if ":" in authority:
            host, port = authority.rsplit(":", 1)
            assert port.isdigit(), "Non-integer port"
            port = int(port)
        else:
            host, port = authority, 80
        if path == "":
            path = "/"
        self.req_headers.append(("Host", authority)) # FIXME: make sure it's the only one
        self.req_headers.append(("Connection", "keep-alive"))
        self.uri = urlunsplit(('', '', path, query, ''))
        pool.attach(host, port, self.handle_connect, self.handle_connect_error)
        return self.req_body_write, self.req_body_done

    def handle_connect(self, tcp_conn):
        self.tcp_conn = tcp_conn
        hdr_block = ["%s %s HTTP/1.1" % (self.method, self.uri)]
        [hdr_block.append("%s: %s" % (k,v)) for k, v in self.req_headers]
        hdr_block.append("")
        hdr_block.append("")
        self.tcp_conn.write("\r\n".join(hdr_block))
        self._req_state = HEADERS_DONE
        return self.handle_input, self.conn_closed, self.pause_request

    def handle_connect_error(self, host, port, err):
        import os
        self._http_error("504", "Gateway Timeout", os.strerror(err))

    def req_body_write(self, data):
        assert self._req_state == HEADERS_DONE
        assert self.req_headers.has_key('content-length'), "Request bodies require content-length"
        #TODO: deal with pausing
        self._req_body_sent += len(data)
        self.tcp_conn.write(data)
        assert self._req_body_sent <= int(self.req_headers['content-length'][0]), \
            "Too many request body bytes sent"

    def req_body_done(self, data):
        assert self._req_state == HEADERS_DONE
        self._req_state = BODY_DONE

    def handle_input(self, instr):
        if self._input_buffer:
            instr = self._input_buffer + instr # TODO: perf
            self._input_buffer = ""
        if self._res_state == WAITING:
            if re.search(HDR_END, instr): # found one
                self._res_state = HEADERS_DONE
                rest = self._parse_response(instr)
                self.handle_input(rest)
            else: # partial headers; store it and wait for more
                self._input_buffer = instr
        else:
            if self._conn_mode == CLOSE:
                self.res_body_write_cb(instr)
            elif self._conn_mode == CHUNKED:
                if self._res_body_left > 0:
                    if self._res_body_left < len(instr): # got more than the chunk
                        this_chunk = self._res_body_left
                        self.res_body_write_cb(instr[:this_chunk])
                        self._res_body_left = -1
                        return self.handle_input(instr[this_chunk+2:]) # +2 consumes the CRLF
                    elif self._res_body_left == len(instr): # got the whole chunk exactly
                        self.res_body_write_cb(instr)
                        self._res_body_left = -1
                    else: # got partial chunk
                        self.res_body_write_cb(instr)
                        self._res_body_left -= len(instr)
                elif self._res_body_left == 0: # done
                    self.res_body_done(True) # TODO: trailers, consume last CRLF
                else: # new chunk
                    try:
                        # they really need to use CRLF
                        chunk_size, instr = instr.split("\r\n", 1)
                    except ValueError:
                        # got a CRLF without anything behind it.. wait.
                        self._input_buffer += instr
                        return
                    if chunk_size.strip() == "": # ignore bare lines
                        return self.handle_input(instr)
                    if ";" in chunk_size: # ignore chunk extensions
                        chunk_size = chunk_size.split(";", 1)[0]
                    self._res_body_left = int(chunk_size, 16)
                    self.handle_input(instr)
            elif self._conn_mode == COUNTED:
                assert self._res_body_left >= 0, "response bytecounting problem! (%s)" % self._res_body_left
                # process response body
                if self._res_body_left <= len(instr): # got it all (and more?)
                    self.res_body_write_cb(instr[:self._res_body_left])
                    self.res_body_done(True)
                    # TODO: will we ever get anything on instr[self._res_body_left:]?
                else: # got some of it
                    self.res_body_write_cb(instr)
                    self._res_body_left -= len(instr)
            else:
                raise Exception, "Unknown conn_mode"

    def conn_closed(self):
        if self._input_buffer:
            self.handle_input("")
            if self._res_state == BODY_DONE:
                return # we've seen the whole body now.
        if self._conn_mode == CLOSE:
            self.res_body_done(True)
        else:
            if self.method in self.safe_methods and self._retries < self.retry_limit:
                # FIXME: this is probably dangerous past WAITING. Clean up state before we retry.
                self._retries += 1
                pool.attach(self.tcp_conn.host, self.tcp_conn.port,
                    self.handle_connect, self.handle_connect_error)
            else:
                self._http_error("504", "Gateway Timeout", "connection closed")

    def res_body_done(self, complete):
        assert self._res_state < BODY_DONE
        self._res_state = BODY_DONE
        if self.tcp_conn and self.tcp_conn.tcp_connected:
            def unexpected_read(data):
                if data:
                    self._conn_mode = CLOSE
                    self.res_body_write_cb(data)
            self.tcp_conn.read_cb = unexpected_read
            def idle_close():
                pass
            self.tcp_conn.close_cb = idle_close
            if self._conn_reusable:
                pool.release(self.tcp_conn)
            else:
                del self.tcp_conn
        self.res_body_done_cb(complete)

    def pause_request(self, paused):
        self._paused = paused

    def _parse_response(self, instr):
        top, rest = re.split(HDR_END, instr, 1)
        res_hdr_lines = LWS.sub(" ", top).splitlines()
        res_line = res_hdr_lines.pop(0)
        try:
            res_version, status_txt = res_line.split(None, 1)
            res_version = float(res_version.rsplit('/', 1)[1])
        except (ValueError, IndexError), why:
            return self._http_error("502", "Bad Gateway", why)
        try:
            res_code, res_phrase = status_txt.split(None, 1)
        except ValueError:
            res_code = status_txt
            res_phrase = ""
        res_headers = []
        connection_tokens = []
        transfer_codes = []
        content_length = None
        for line in res_hdr_lines:
            try:
                fn, fv = line.split(":", 1)
                fn = fn.strip()
                res_headers.append((fn, fv.strip()))
            except ValueError, why:
                return self._http_error("502", "Bad Gateway", why)
            f_name = fn.lower()
            if f_name == "connection":
                connection_tokens += [v.strip().lower() for v in fv.split(',')]
            elif f_name == "transfer-encoding":
                transfer_codes += [v.strip().lower() for v in fv.split(',')]
            elif f_name == "content-length":
                try:
                    content_length = int(fv)
                except ValueError, why:
                    return self._http_error("502" "Bad Gateway", why)

        if self.method == "HEAD" or res_code in self.no_body_status:
            self._conn_mode = COUNTED
            self._res_body_left = 0
        elif res_version > 1.0: # HTTP/1.1
            if len(transfer_codes) > 0:
                if 'chunked' in transfer_codes:
                    self._conn_mode = CHUNKED
                    self._res_body_left = -1 # flag that we don't know
                else:
                    self._conn_mode = CLOSE
            elif content_length != None:
                self._conn_mode = COUNTED
                self._res_body_left = content_length
            elif 'close' in connection_tokens:
                self._conn_mode = CLOSE
            else: # assume 0 length body
                self._conn_mode = COUNTED
                self._res_body_left = 0
        else: # HTTP/1.0
            if content_length != None and 'keep-alive' in connection_tokens:
                self._conn_mode = COUNTED
                self._res_body_left = content_length
            else:
                self._conn_mode = CLOSE
        if 'close' not in connection_tokens:
            if (res_version == 1.0 and 'keep-alive' in connection_tokens) or \
                res_version > 1.0:
                self._conn_reusable = True
        self.res_start_cb(res_version, res_code, res_phrase, res_headers)
        return rest

    def _http_error(self, status_code, status_phrase, body=""):
        self._conn_mode = CLOSE
        self.res_start_cb("1.1", status_code, status_phrase, [])
        self.res_body_write_cb(str(body))
        self.res_body_done(False)
        if hasattr(self, 'tcp_conn') and self.tcp_conn and self.tcp_conn.tcp_connected:
            self.tcp_conn.close()
        return ""

class HttpClientConnectionPool:
    connect_timeout = 3
    _conns = {} # TODO: clean up old conns

    def attach(self, host, port, handle_connect, handle_connect_error):
        while True:
            try:
                idle_conn = self._conns[(host, port)].pop()
            except (IndexError, KeyError):
                push_tcp.Client(host, port, handle_connect, handle_connect_error, self.connect_timeout)
                break
            if idle_conn.tcp_connected:
                idle_conn.read_cb, idle_conn.close_cb, idle_conn.pause_cb = handle_connect(idle_conn)
                break

    def release(self, tcp_conn):
        if tcp_conn.tcp_connected:
            if not self._conns.has_key((tcp_conn.host, tcp_conn.port)):
                self._conns[(tcp_conn.host, tcp_conn.port)] = [tcp_conn]
            else:
                self._conns[(tcp_conn.host, tcp_conn.port)].append(tcp_conn)

# create a singleton pool.
pool = HttpClientConnectionPool()

run = push_tcp.run
stop = push_tcp.stop

if __name__ == "__main__":
    request_uri = sys.argv[1]
    def printer(version, status, phrase, headers):
        print version, status, phrase
        print repr(headers)
    def done(self, complete):
        stop()
    c = HttpClient(printer, sys.stdout.write, done)
    req_body_write, req_body_done = c.start_request("GET", request_uri)
    run()
<|endoftext|>


<commit_before>#
# Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
#

#
# Sandesh UVE
#

import importlib
from sandesh_logger import SandeshLogger

logger = SandeshLogger('SandeshUVE').logger()

class SandeshUVETypeMaps(object):

    def __init__(self):
        self._uve_global_map = {}
        self._uve_data_type_map = {}
    #end __init__

    def get_uve_global_map(self):
        return self._uve_global_map
    #end get_uve_global_map

    def get_uve_type_name(self, uve_data_type_name):
        try:
            uve_name = self._uve_data_type_map[uve_data_type_name]
        except KeyError:
            logger.error('UVE data "%s" not present in the UVE Data Type map' %
                (uve_data_type_name))
            return None
        else:
            return uve_name
    #end get_uve_type_name

    def add_uve_data_type_mapping(self, uve_data_type_name, uve_type_name):
        try:
            uve_type = self._uve_data_type_map[uve_data_type_name]
        except KeyError:
            self._uve_data_type_map[uve_data_type_name] = uve_type_name
        else:
            logger.error('UVE data type "%s" to UVE "%s" already added' %
                (uve_data_type_name, uve_type_name))
            assert 0
    #end add_uve_data_type_mapping

    def register_uve_type_map(self, uve_type_key, uve_type_map):
        try:
            uve_map = self._uve_global_map[uve_type_key]
        except KeyError:
            self._uve_global_map[uve_type_key] = uve_type_map
        else:
            logger.error('UVE type "%s" already added' % (uve_type_key))
            assert 0
    #end register_uve_type_map

    def get_uve_type_map(self, uve_type_key):
        try:
            uve_map = self._uve_global_map[uve_type_key]
        except KeyError:
            logger.error('UVE type "%s" not present in the UVE global map' % (uve_type_key))
            return None
        else:
            return uve_map
    #end get_uve_type_map

    def update_uve_type_map(self, uve_type_key, uve_type_map):
        try:
            uve_map = self._uve_global_map[uve_type_key]
        except KeyError:
            logger.error('UVE type "%s" not present in the UVE global map' % (uve_type_key))
            assert 0
        else:
            self._uve_global_map[uve_type_key] = uve_type_map
    #end update_uve_type_map

    def sync_all_uve_types(self, inmap, sandesh_instance):
        for uve_type, uve_type_map in self._uve_global_map.iteritems():
            try:
                in_seqno = inmap[uve_type]
            except KeyError:
                uve_type_map.sync_uve(0, '', False, sandesh_instance)
            else:
                uve_type_map.sync_uve(in_seqno, '', False, sandesh_instance)
    #end sync_all_uve_types

#end class SandeshUVETypeMaps

class SandeshUVEPerTypeMap(object):

    class UVEMapEntry(object):
        def __init__(self, data, seqno):
            self.data = data
            self.seqno = seqno
            self.update_count = 0
        #end __init__

    #end class UVEMapEntry

    def __init__(self, sandesh, uve_type_name, uve_data_type_name, uve_module):
        self._sandesh = sandesh
        self._uve_type = uve_type_name
        self._uve_data_type = uve_data_type_name
        self._uve_module = uve_module
        self._uve_map = {}
        sandesh._uve_type_maps.register_uve_type_map(uve_type_name, self)
        sandesh._uve_type_maps.add_uve_data_type_mapping(uve_data_type_name,
            uve_type_name)
    #end __init__

    def uve_data_type(self):
        return self._uve_data_type
    #end uve_data_type

    def uve_type_seqnum(self):
        seqnum = 0
        try:
            imp_module = importlib.import_module(self._uve_module)
        except ImportError:
            logger.error('Failed to import Module "%s"' % (self._uve_module))
        else:
            seqnum = getattr(imp_module, self._uve_type).lseqnum()
        return seqnum
    #end uve_type_seqnum

    def update_uve(self, uve_sandesh):
        uve_name = uve_sandesh.data.name
        try:
            uve_entry = self._uve_map[uve_name]
        except KeyError:
            logger.debug('Add uve <%s, %s> in the [%s] map' \
                % (uve_name, uve_sandesh.seqnum(), self._uve_type))
            self._uve_map[uve_name] = \
                SandeshUVEPerTypeMap.UVEMapEntry(uve_sandesh.data, uve_sandesh.seqnum())
        else:
            if uve_entry.data.deleted is True:
                if uve_sandesh.data.deleted is not True:
                    # The uve entry in the cache has been marked for deletion and
                    # a new uve entry with the same key has been created. Replace the
                    # deleted uve entry in the cache with this new entry.
                    logger.debug('Re-add uve <%s, %s> in the [%s] map' \
                        % (uve_name, uve_sandesh.seqnum(), self._uve_type))
                    self._uve_map[uve_name] = \
                        SandeshUVEPerTypeMap.UVEMapEntry(uve_sandesh.data, uve_sandesh.seqnum())
                else:
                    # Duplicate uve delete. Do we need to update the seqnum here?
                    logger.error('Duplicate uve delete <%s>' % (uve_name))
            else:
                uve_entry.data = uve_sandesh.update_uve(uve_entry.data)
                uve_entry.seqno = uve_sandesh.seqnum()
                uve_entry.update_count = uve_entry.update_count + 1
                self._uve_map[uve_name] = uve_entry
        # Now, update the uve_global_map
        self._sandesh._uve_type_maps.update_uve_type_map(self._uve_type, self)
    #end update_uve

    def sync_uve(self, seqno, ctx, more, sandesh_instance):
        count = 0
        try:
            imp_module = importlib.import_module(self._uve_module)
        except ImportError:
            logger.error('Failed to import Module "%s"' % (self._uve_module))
        else:
            for uve_name, uve_entry in self._uve_map.iteritems():
                if seqno == 0 or seqno < uve_entry.seqno:
                    try:
                        sandesh_uve = \
                            getattr(imp_module, self._uve_type)(sandesh=sandesh_instance)
                    except AttributeError:
                        logger.error('Failed to create sandesh UVE "%s"' % (self._uve_type))
                        break
                    else:
                        sandesh_uve.data = uve_entry.data
                        logger.debug('send sync_uve <%s: %s> in the [%s] map)' \
                            % (uve_entry.data.name, uve_entry.seqno, self._uve_type))
                        sandesh_uve.send(True, uve_entry.seqno, ctx, more, sandesh_instance)
                        count += 1
        return count
    #end sync_uve

#end class SandeshUVEPerTypeMap
<commit_msg>Need to do deepcopy when writing to UVE cache - otherwise newly-enqued messages can overwrite older messages<commit_after>#
# Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
#

#
# Sandesh UVE
#

import importlib
import copy
from sandesh_logger import SandeshLogger

logger = SandeshLogger('SandeshUVE').logger()

class SandeshUVETypeMaps(object):

    def __init__(self):
        self._uve_global_map = {}
        self._uve_data_type_map = {}
    #end __init__

    def get_uve_global_map(self):
        return self._uve_global_map
    #end get_uve_global_map

    def get_uve_type_name(self, uve_data_type_name):
        try:
            uve_name = self._uve_data_type_map[uve_data_type_name]
        except KeyError:
            logger.error('UVE data "%s" not present in the UVE Data Type map' %
                (uve_data_type_name))
            return None
        else:
            return uve_name
    #end get_uve_type_name

    def add_uve_data_type_mapping(self, uve_data_type_name, uve_type_name):
        try:
            uve_type = self._uve_data_type_map[uve_data_type_name]
        except KeyError:
            self._uve_data_type_map[uve_data_type_name] = uve_type_name
        else:
            logger.error('UVE data type "%s" to UVE "%s" already added' %
                (uve_data_type_name, uve_type_name))
            assert 0
    #end add_uve_data_type_mapping

    def register_uve_type_map(self, uve_type_key, uve_type_map):
        try:
            uve_map = self._uve_global_map[uve_type_key]
        except KeyError:
            self._uve_global_map[uve_type_key] = uve_type_map
        else:
            logger.error('UVE type "%s" already added' % (uve_type_key))
            assert 0
    #end register_uve_type_map

    def get_uve_type_map(self, uve_type_key):
        try:
            uve_map = self._uve_global_map[uve_type_key]
        except KeyError:
            logger.error('UVE type "%s" not present in the UVE global map' % (uve_type_key))
            return None
        else:
            return uve_map
    #end get_uve_type_map

    def update_uve_type_map(self, uve_type_key, uve_type_map):
        try:
            uve_map = self._uve_global_map[uve_type_key]
        except KeyError:
            logger.error('UVE type "%s" not present in the UVE global map' % (uve_type_key))
            assert 0
        else:
            self._uve_global_map[uve_type_key] = uve_type_map
    #end update_uve_type_map

    def sync_all_uve_types(self, inmap, sandesh_instance):
        for uve_type, uve_type_map in self._uve_global_map.iteritems():
            try:
                in_seqno = inmap[uve_type]
            except KeyError:
                uve_type_map.sync_uve(0, '', False, sandesh_instance)
            else:
                uve_type_map.sync_uve(in_seqno, '', False, sandesh_instance)
    #end sync_all_uve_types

#end class SandeshUVETypeMaps

class SandeshUVEPerTypeMap(object):

    class UVEMapEntry(object):
        def __init__(self, data, seqno):
            self.data = data
            self.seqno = seqno
            self.update_count = 0
        #end __init__

    #end class UVEMapEntry

    def __init__(self, sandesh, uve_type_name, uve_data_type_name, uve_module):
        self._sandesh = sandesh
        self._uve_type = uve_type_name
        self._uve_data_type = uve_data_type_name
        self._uve_module = uve_module
        self._uve_map = {}
        sandesh._uve_type_maps.register_uve_type_map(uve_type_name, self)
        sandesh._uve_type_maps.add_uve_data_type_mapping(uve_data_type_name,
            uve_type_name)
    #end __init__

    def uve_data_type(self):
        return self._uve_data_type
    #end uve_data_type

    def uve_type_seqnum(self):
        seqnum = 0
        try:
            imp_module = importlib.import_module(self._uve_module)
        except ImportError:
            logger.error('Failed to import Module "%s"' % (self._uve_module))
        else:
            seqnum = getattr(imp_module, self._uve_type).lseqnum()
        return seqnum
    #end uve_type_seqnum

    def update_uve(self, uve_sandesh):
        uve_name = uve_sandesh.data.name
        try:
            uve_entry = self._uve_map[uve_name]
        except KeyError:
            logger.debug('Add uve <%s, %s> in the [%s] map' \
                % (uve_name, uve_sandesh.seqnum(), self._uve_type))
            self._uve_map[uve_name] = \
                SandeshUVEPerTypeMap.UVEMapEntry( \
                    copy.deepcopy(uve_sandesh.data), uve_sandesh.seqnum())
        else:
            if uve_entry.data.deleted is True:
                if uve_sandesh.data.deleted is not True:
                    # The uve entry in the cache has been marked for deletion and
                    # a new uve entry with the same key has been created. Replace the
                    # deleted uve entry in the cache with this new entry.
                    logger.debug('Re-add uve <%s, %s> in the [%s] map' \
                        % (uve_name, uve_sandesh.seqnum(), self._uve_type))
                    self._uve_map[uve_name] = \
                        SandeshUVEPerTypeMap.UVEMapEntry(uve_sandesh.data, uve_sandesh.seqnum())
                else:
                    # Duplicate uve delete. Do we need to update the seqnum here?
                    logger.error('Duplicate uve delete <%s>' % (uve_name))
            else:
                uve_entry.data = uve_sandesh.update_uve(uve_entry.data)
                uve_entry.seqno = uve_sandesh.seqnum()
                uve_entry.update_count = uve_entry.update_count + 1
                self._uve_map[uve_name] = uve_entry
        # Now, update the uve_global_map
        self._sandesh._uve_type_maps.update_uve_type_map(self._uve_type, self)
    #end update_uve

    def sync_uve(self, seqno, ctx, more, sandesh_instance):
        count = 0
        try:
            imp_module = importlib.import_module(self._uve_module)
        except ImportError:
            logger.error('Failed to import Module "%s"' % (self._uve_module))
        else:
            for uve_name, uve_entry in self._uve_map.iteritems():
                if seqno == 0 or seqno < uve_entry.seqno:
                    try:
                        sandesh_uve = \
                            getattr(imp_module, self._uve_type)(sandesh=sandesh_instance)
                    except AttributeError:
                        logger.error('Failed to create sandesh UVE "%s"' % (self._uve_type))
                        break
                    else:
                        sandesh_uve.data = uve_entry.data
                        logger.debug('send sync_uve <%s: %s> in the [%s] map)' \
                            % (uve_entry.data.name, uve_entry.seqno, self._uve_type))
                        sandesh_uve.send(True, uve_entry.seqno, ctx, more, sandesh_instance)
                        count += 1
        return count
    #end sync_uve

#end class SandeshUVEPerTypeMap
<|endoftext|>


<commit_before># SPDX-License-Identifier: Apache-2.0
#
# Copyright (C) 2015, ARM Limited and contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

""" Latency Analysis Module """

import matplotlib.gridspec as gridspec
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import pylab as pl
import re

from collections import namedtuple
from analysis_module import AnalysisModule
from devlib.utils.misc import memoized
from trappy.utils import listify

# Tuple representing all IDs data of a Task
TaskData = namedtuple('TaskData', ['pid', 'names', 'label'])

class LatencyAnalysis(AnalysisModule):
    """
    Support for plotting Latency Analysis data

    :param trace: input Trace object
    :type trace: :mod:`libs.utils.Trace`
    """

    def __init__(self, trace):
        super(LatencyAnalysis, self).__init__(trace)

###############################################################################
# DataFrame Getter Methods
###############################################################################

    @memoized
    def _dfg_latency_df(self, task):

        if not self._trace.hasEvents('sched_wakeup'):
            self._log.warning('Events [sched_wakeup] not found, '
                              'cannot compute CPU active signal!')
            return None
        if not self._trace.hasEvents('sched_switch'):
            self._log.warning('Events [sched_switch] not found, '
                              'cannot compute CPU active signal!')
            return None

        # Get task data
        td = self._getTaskData(task)
        if not td:
            return None

        wk_df = self._dfg_trace_event('sched_wakeup')
        sw_df = self._dfg_trace_event('sched_switch')

        # Filter Task's WAKEUP events
        task_wakeup = wk_df[wk_df.pid == td.pid][['success', 'target_cpu', 'pid']]

        # Filter Task's START events
        task_events = (sw_df.prev_pid == td.pid) | (sw_df.next_pid == td.pid)
        task_switches_df = sw_df[task_events]\
            [['__cpu', 'prev_pid', 'next_pid', 'prev_state']]

        # Unset prev_state for switch_in events, i.e.
        # we don't care about the status of a task we are replacing
        task_switches_df.prev_state = task_switches_df.apply(
            lambda r : np.nan if r['prev_pid'] != td.pid
                              else self._taskState(r['prev_state']),
            axis=1)

        # Rename prev_state
        task_switches_df.rename(columns={'prev_state' : 'curr_state'}, inplace=True)

        # Fill in Running status
        task_switches_df.curr_state = task_switches_df.curr_state.fillna(value='A')

        # Join Wakeup and SchedSwitch events
        task_latency_df = task_wakeup.join(task_switches_df, how='outer',
                                             lsuffix='_wkp', rsuffix='_slp')
        # Remove not required columns
        task_latency_df = task_latency_df[['target_cpu', '__cpu', 'curr_state']]
        # Set Wakeup state on each Wakeup event
        task_latency_df.curr_state = task_latency_df.curr_state.fillna(value='W')

        # Sanity check for all task states to be mapped to a char
        numbers = 0
        for value in task_switches_df.curr_state.unique():
            if type(value) is not str:
                self._log.warning('The [sched_switch] events contain "prev_state" value [%s]',
                                  value)
                numbers += 1
        if numbers:
            verb = 'is' if numbers == 1 else 'are'
            self._log.warning('  which %s not currently mapped into a task state.',
                              verb)
            self._log.warning('Check mappings in:')
            self._log.warning(' %s::%s _taskState()',
                              __file__, self.__class__.__name__)

        # Forward annotate task state
        task_latency_df['next_state'] = task_latency_df.curr_state.shift(-1)

        # Forward account for previous state duration
        task_latency_df['t_start'] =  task_latency_df.index
        task_latency_df['t_delta'] = (
              task_latency_df['t_start'].shift(-1)
            - task_latency_df['t_start']
        )

        return task_latency_df

    # Select Wakeup latency
    def _dfg_latency_wakeup_df(self, task):
        task_latency_df = self._dfg_latency_df(task)
        if task_latency_df is None:
            return None
        df = task_latency_df[
                    (task_latency_df.curr_state == 'W') &
                    (task_latency_df.next_state == 'A')][['t_delta']]
        df.rename(columns={'t_delta' : 'wakeup_latency'}, inplace=True)
        return df

    # Select Wakeup latency
    def _dfg_latency_preemption_df(self, task):
        task_latency_df = self._dfg_latency_df(task)
        if task_latency_df is None:
            return None
        df = task_latency_df[
                    (task_latency_df.curr_state.isin([0, 'R', 'R+'])) &
                    (task_latency_df.next_state == 'A')][['t_delta']]
        df.rename(columns={'t_delta' : 'preempt_latency'}, inplace=True)
        return df

###############################################################################
# Plotting Methods
###############################################################################

    def plotLatency(self, task, kind='all', tag=None, threshold_ms=1):
        """
        Generate a set of plots to report the WAKEUP and PREEMPT latencies the
        specified task has been subject to. A WAKEUP latencies is the time from
        when a task becomes RUNNABLE till the first time it gets a CPU.
        A PREEMPT latencies is the time from when a RUNNABLE task is suspended
        because of the CPU is assigned to another task till when the task
        enters the CPU again.

        :param task: the task to report latencies for
        :type task: int or list(str)

        :param kind: the kind of latencies to report (WAKEUP and/or PREEMPT")
        :type kind: str

        :param tag: a string to add to the plot title
        :type tag: str

        :param threshold_ms: the minimum acceptable [ms] value to report
                             graphically in the generated plots
        :type threshold_ms: int or float
        """

        if not self._trace.hasEvents('sched_switch'):
            self._log.warning('Event [sched_switch] not found, '
                              'plot DISABLED!')
            return
        if not self._trace.hasEvents('sched_wakeup'):
            self._log.warning('Event [sched_wakeup] not found, '
                              'plot DISABLED!')
            return

        # Get task data
        td = self._getTaskData(task)
        if not td:
            return None

        # Load wakeup latencies (if required)
        wkp_df = None
        if 'all' in kind or 'wakeup' in kind:
            wkp_df = self._dfg_latency_wakeup_df(td.pid)
        if wkp_df is not None:
            wkp_df.rename(columns={'wakeup_latency' : 'latency'}, inplace=True)
            self._log.info('Found: %5d WAKEUP latencies', len(wkp_df))

        # Load preempt latencies (if required)
        prt_df = None
        if 'all' in kind or 'preempt' in kind:
            prt_df = self._dfg_latency_preemption_df(td.pid)
        if prt_df is not None:
            prt_df.rename(columns={'preempt_latency' : 'latency'}, inplace=True)
            self._log.info('Found: %5d PREEMPT latencies', len(prt_df))

        if wkp_df is None and prt_df is None:
            self._log.warning('No Latency info for task [%s]', td.label)
            return

        # Join the two data frames
        df = wkp_df.append(prt_df)
        ymax = 1.1 * df.latency.max()
        self._log.info('Total: %5d latency events', len(df))

        df = pd.DataFrame(sorted(df.latency), columns=['latency'])

        # Setup plots
        gs = gridspec.GridSpec(2, 2, height_ratios=[2,1], width_ratios=[1,1])
        plt.figure(figsize=(16, 8))

        plot_title = "Task [{}] latencies".format(kind.upper())
        if tag:
            plot_title = "{} [{}]".format(plot_title, tag)
        plot_title = "{}, threshold @ {} [ms]".format(plot_title, threshold_ms)

        # Latency events duration over time
        axes = plt.subplot(gs[0,0:2])
        axes.set_title(plot_title)
        try:
            wkp_df.rename(columns={'latency': 'wakeup'}, inplace=True)
            wkp_df.plot(style='b+', logy=True, ax=axes)
        except: pass
        try:
            prt_df.rename(columns={'latency' : 'preempt'}, inplace=True)
            prt_df.plot(style='r+', logy=True, ax=axes)
        except: pass
        axes.axhline(threshold_ms / 1000., linestyle='--', color='g')
        self._trace.analysis.status.plotOverutilized(axes)
        axes.legend(loc='lower center', ncol=2)

        # Cumulative distribution of all latencies
        axes = plt.subplot(gs[1,0])
        df.latency.plot(ax=axes, logy=True, legend=False,
                        title='Latencies cumulative distribution [{}]'\
                              .format(td.label))
        axes.axhline(y=threshold_ms / 1000., linewidth=2,
                     color='r', linestyle='--')

        # Histogram of all latencies
        axes = plt.subplot(gs[1,1])
        df.latency.plot(kind='hist', bins=64, ax=axes,
                        xlim=(0,ymax), legend=False,
                        title='Latency histogram (64 bins, {} [ms] green threshold)'\
                        .format(threshold_ms));
        axes.axvspan(0, threshold_ms / 1000., facecolor='g', alpha=0.5);

        # Save generated plots into datadir
        task_name = re.sub('[\ :/]', '_', td.label)
        figname = '{}/{}task_latencies_{}_{}.png'\
                  .format(self._trace.plots_dir, self._trace.plots_prefix,
                          td.pid, task_name)
        pl.savefig(figname, bbox_inches='tight')

        # Return statistics
        return df.describe(percentiles=[0.95, 0.99])

    def plotLatencyBands(self, task, axes=None):
        """
        Draw a plot that shows intervals of time when the execution of a
        RUNNABLE task has been delayed. The plot reports:
          WAKEUP     lantecies as RED colored bands
          PREEMPTION lantecies as BLUE colored bands

        The optional axes parameter allows to plot the signal on an existing
        graph.

        :param task: the task to report latencies for
        :type task: str

        :param axes: axes on which to plot the signal
        :type axes: :mod:`matplotlib.axes.Axes`
        """
        if not self._trace.hasEvents('sched_switch'):
            self._log.warning('Event [sched_switch] not found, '
                              'plot DISABLED!')
            return
        if not self._trace.hasEvents('sched_wakeup'):
            self._log.warning('Event [sched_wakeup] not found, '
                              'plot DISABLED!')
            return

        # Get task PID
        td = self._getTaskData(task)
        if not td:
            return None

        wkl_df = self._dfg_latency_wakeup_df(td.pid)
        prt_df = self._dfg_latency_preemption_df(td.pid)

        if wkl_df is None and prt_df is None:
            self._log.warning('No task with name [%s]', td.label)
            return

        # If not axis provided: generate a standalone plot
        if not axes:
            gs = gridspec.GridSpec(1, 1)
            plt.figure(figsize=(16, 2))
            axes = plt.subplot(gs[0, 0])
            axes.set_title('Latencies on [{}] '
                           '(red: WAKEUP, blue: PREEMPT)'\
                          .format(td.label))
            axes.set_xlim(self._trace.x_min, self._trace.x_max)
            axes.set_yticklabels([])
            axes.set_xlabel('Time [s]')
            axes.grid(True)

        # Draw WAKEUP latencies
        try:
            bands = [(t, wkl_df['wakeup_latency'][t]) for t in wkl_df.index]
            for (start, duration) in bands:
                end = start + duration
                axes.axvspan(start, end, facecolor='r', alpha=0.1)
        except: pass

        # Draw PREEMPTION latencies
        try:
            bands = [(t, prt_df['preempt_latency'][t]) for t in prt_df.index]
            for (start, duration) in bands:
                end = start + duration
                axes.axvspan(start, end, facecolor='b', alpha=0.1)
        except: pass

###############################################################################
# Utility Methods
###############################################################################

    @memoized
    def _getTaskData(self, task):

        # Get task PID
        if isinstance(task, str):
            task_pids = self._trace.getTaskByName(task)
            if len(task_pids) == 0:
                self._log.warning('No tasks found with name [%s]', task)
                return None

            task_pid = task_pids[0]
            if len(task_pids) > 1:
                self._log.warning('Multiple PIDs for task named [%s]', task)
                for pid in task_pids:
                    self._log.warning('  %5d :  %s', pid,
                                      ','.join(self._trace.getTaskByPid(pid)))
                self._log.warning('Returning stats only for PID: %d',
                                  task_pid)
            task_names = self._trace.getTaskByPid(task_pid)

        # Get task name
        elif isinstance(task, int):
            task_pid = task
            task_names = self._trace.getTaskByPid(task_pid)
            if len(task_names) == 0:
                self._log.warning('No tasks found with name [%s]', task)
                return None

        else:
            raise ValueError("Task must be either an int or str")

        task_label = "{}: {}".format(task_pid, ', '.join(task_names))
        return TaskData(task_pid, task_names, task_label)

    @memoized
    def _taskState(self, state):
        try:
            state = int(state)
        except ValueError:
            # State already converted to symbol
            return state

        # Tasks STATE flags (Linux 4.8)
        TASK_STATES = {
              0: "R", # TASK_RUNNING
              1: "S", # TASK_INTERRUPTIBLE
              2: "D", # TASK_UNINTERRUPTIBLE
              4: "T", # __TASK_STOPPED
              8: "t", # __TASK_TRACED
             16: "X", # EXIT_DEAD
             32: "Z", # EXIT_ZOMBIE
             64: "x", # TASK_DEAD
            128: "K", # TASK_WAKEKILL
            256: "W", # TASK_WAKING
            512: "P", # TASK_PARKED
           1024: "N", # TASK_NOLOAD
           2048: "n", # TASK_NEW
        }
        TASK_MAX_STATE = 4096

        res = "R"
        if state & (TASK_MAX_STATE - 1) != 0:
            res = ""
        for key in TASK_STATES.keys():
            if key & state:
                res += TASK_STATES[key]
        if state & TASK_MAX_STATE:
            res += "+"
        else:
            res = '|'.join(res)
        return res

# vim :set tabstop=4 shiftwidth=4 expandtab
<commit_msg>utils/analysis/latency: remove unused columns<commit_after># SPDX-License-Identifier: Apache-2.0
#
# Copyright (C) 2015, ARM Limited and contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

""" Latency Analysis Module """

import matplotlib.gridspec as gridspec
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import pylab as pl
import re

from collections import namedtuple
from analysis_module import AnalysisModule
from devlib.utils.misc import memoized
from trappy.utils import listify

# Tuple representing all IDs data of a Task
TaskData = namedtuple('TaskData', ['pid', 'names', 'label'])

class LatencyAnalysis(AnalysisModule):
    """
    Support for plotting Latency Analysis data

    :param trace: input Trace object
    :type trace: :mod:`libs.utils.Trace`
    """

    def __init__(self, trace):
        super(LatencyAnalysis, self).__init__(trace)

###############################################################################
# DataFrame Getter Methods
###############################################################################

    @memoized
    def _dfg_latency_df(self, task):

        if not self._trace.hasEvents('sched_wakeup'):
            self._log.warning('Events [sched_wakeup] not found, '
                              'cannot compute CPU active signal!')
            return None
        if not self._trace.hasEvents('sched_switch'):
            self._log.warning('Events [sched_switch] not found, '
                              'cannot compute CPU active signal!')
            return None

        # Get task data
        td = self._getTaskData(task)
        if not td:
            return None

        wk_df = self._dfg_trace_event('sched_wakeup')
        sw_df = self._dfg_trace_event('sched_switch')

        # Filter Task's WAKEUP events
        task_wakeup = wk_df[wk_df.pid == td.pid][['target_cpu', 'pid']]

        # Filter Task's START events
        task_events = (sw_df.prev_pid == td.pid) | (sw_df.next_pid == td.pid)
        task_switches_df = sw_df[task_events]\
            [['__cpu', 'prev_pid', 'next_pid', 'prev_state']]

        # Unset prev_state for switch_in events, i.e.
        # we don't care about the status of a task we are replacing
        task_switches_df.prev_state = task_switches_df.apply(
            lambda r : np.nan if r['prev_pid'] != td.pid
                              else self._taskState(r['prev_state']),
            axis=1)

        # Rename prev_state
        task_switches_df.rename(columns={'prev_state' : 'curr_state'}, inplace=True)

        # Fill in Running status
        task_switches_df.curr_state = task_switches_df.curr_state.fillna(value='A')

        # Join Wakeup and SchedSwitch events
        task_latency_df = task_wakeup.join(task_switches_df, how='outer',
                                             lsuffix='_wkp', rsuffix='_slp')
        # Remove not required columns
        task_latency_df = task_latency_df[['target_cpu', '__cpu', 'curr_state']]
        # Set Wakeup state on each Wakeup event
        task_latency_df.curr_state = task_latency_df.curr_state.fillna(value='W')

        # Sanity check for all task states to be mapped to a char
        numbers = 0
        for value in task_switches_df.curr_state.unique():
            if type(value) is not str:
                self._log.warning('The [sched_switch] events contain "prev_state" value [%s]',
                                  value)
                numbers += 1
        if numbers:
            verb = 'is' if numbers == 1 else 'are'
            self._log.warning('  which %s not currently mapped into a task state.',
                              verb)
            self._log.warning('Check mappings in:')
            self._log.warning(' %s::%s _taskState()',
                              __file__, self.__class__.__name__)

        # Forward annotate task state
        task_latency_df['next_state'] = task_latency_df.curr_state.shift(-1)

        # Forward account for previous state duration
        task_latency_df['t_start'] =  task_latency_df.index
        task_latency_df['t_delta'] = (
              task_latency_df['t_start'].shift(-1)
            - task_latency_df['t_start']
        )

        return task_latency_df

    # Select Wakeup latency
    def _dfg_latency_wakeup_df(self, task):
        task_latency_df = self._dfg_latency_df(task)
        if task_latency_df is None:
            return None
        df = task_latency_df[
                    (task_latency_df.curr_state == 'W') &
                    (task_latency_df.next_state == 'A')][['t_delta']]
        df.rename(columns={'t_delta' : 'wakeup_latency'}, inplace=True)
        return df

    # Select Wakeup latency
    def _dfg_latency_preemption_df(self, task):
        task_latency_df = self._dfg_latency_df(task)
        if task_latency_df is None:
            return None
        df = task_latency_df[
                    (task_latency_df.curr_state.isin([0, 'R', 'R+'])) &
                    (task_latency_df.next_state == 'A')][['t_delta']]
        df.rename(columns={'t_delta' : 'preempt_latency'}, inplace=True)
        return df

###############################################################################
# Plotting Methods
###############################################################################

    def plotLatency(self, task, kind='all', tag=None, threshold_ms=1):
        """
        Generate a set of plots to report the WAKEUP and PREEMPT latencies the
        specified task has been subject to. A WAKEUP latencies is the time from
        when a task becomes RUNNABLE till the first time it gets a CPU.
        A PREEMPT latencies is the time from when a RUNNABLE task is suspended
        because of the CPU is assigned to another task till when the task
        enters the CPU again.

        :param task: the task to report latencies for
        :type task: int or list(str)

        :param kind: the kind of latencies to report (WAKEUP and/or PREEMPT")
        :type kind: str

        :param tag: a string to add to the plot title
        :type tag: str

        :param threshold_ms: the minimum acceptable [ms] value to report
                             graphically in the generated plots
        :type threshold_ms: int or float
        """

        if not self._trace.hasEvents('sched_switch'):
            self._log.warning('Event [sched_switch] not found, '
                              'plot DISABLED!')
            return
        if not self._trace.hasEvents('sched_wakeup'):
            self._log.warning('Event [sched_wakeup] not found, '
                              'plot DISABLED!')
            return

        # Get task data
        td = self._getTaskData(task)
        if not td:
            return None

        # Load wakeup latencies (if required)
        wkp_df = None
        if 'all' in kind or 'wakeup' in kind:
            wkp_df = self._dfg_latency_wakeup_df(td.pid)
        if wkp_df is not None:
            wkp_df.rename(columns={'wakeup_latency' : 'latency'}, inplace=True)
            self._log.info('Found: %5d WAKEUP latencies', len(wkp_df))

        # Load preempt latencies (if required)
        prt_df = None
        if 'all' in kind or 'preempt' in kind:
            prt_df = self._dfg_latency_preemption_df(td.pid)
        if prt_df is not None:
            prt_df.rename(columns={'preempt_latency' : 'latency'}, inplace=True)
            self._log.info('Found: %5d PREEMPT latencies', len(prt_df))

        if wkp_df is None and prt_df is None:
            self._log.warning('No Latency info for task [%s]', td.label)
            return

        # Join the two data frames
        df = wkp_df.append(prt_df)
        ymax = 1.1 * df.latency.max()
        self._log.info('Total: %5d latency events', len(df))

        df = pd.DataFrame(sorted(df.latency), columns=['latency'])

        # Setup plots
        gs = gridspec.GridSpec(2, 2, height_ratios=[2,1], width_ratios=[1,1])
        plt.figure(figsize=(16, 8))

        plot_title = "Task [{}] latencies".format(kind.upper())
        if tag:
            plot_title = "{} [{}]".format(plot_title, tag)
        plot_title = "{}, threshold @ {} [ms]".format(plot_title, threshold_ms)

        # Latency events duration over time
        axes = plt.subplot(gs[0,0:2])
        axes.set_title(plot_title)
        try:
            wkp_df.rename(columns={'latency': 'wakeup'}, inplace=True)
            wkp_df.plot(style='b+', logy=True, ax=axes)
        except: pass
        try:
            prt_df.rename(columns={'latency' : 'preempt'}, inplace=True)
            prt_df.plot(style='r+', logy=True, ax=axes)
        except: pass
        axes.axhline(threshold_ms / 1000., linestyle='--', color='g')
        self._trace.analysis.status.plotOverutilized(axes)
        axes.legend(loc='lower center', ncol=2)

        # Cumulative distribution of all latencies
        axes = plt.subplot(gs[1,0])
        df.latency.plot(ax=axes, logy=True, legend=False,
                        title='Latencies cumulative distribution [{}]'\
                              .format(td.label))
        axes.axhline(y=threshold_ms / 1000., linewidth=2,
                     color='r', linestyle='--')

        # Histogram of all latencies
        axes = plt.subplot(gs[1,1])
        df.latency.plot(kind='hist', bins=64, ax=axes,
                        xlim=(0,ymax), legend=False,
                        title='Latency histogram (64 bins, {} [ms] green threshold)'\
                        .format(threshold_ms));
        axes.axvspan(0, threshold_ms / 1000., facecolor='g', alpha=0.5);

        # Save generated plots into datadir
        task_name = re.sub('[\ :/]', '_', td.label)
        figname = '{}/{}task_latencies_{}_{}.png'\
                  .format(self._trace.plots_dir, self._trace.plots_prefix,
                          td.pid, task_name)
        pl.savefig(figname, bbox_inches='tight')

        # Return statistics
        return df.describe(percentiles=[0.95, 0.99])

    def plotLatencyBands(self, task, axes=None):
        """
        Draw a plot that shows intervals of time when the execution of a
        RUNNABLE task has been delayed. The plot reports:
          WAKEUP     lantecies as RED colored bands
          PREEMPTION lantecies as BLUE colored bands

        The optional axes parameter allows to plot the signal on an existing
        graph.

        :param task: the task to report latencies for
        :type task: str

        :param axes: axes on which to plot the signal
        :type axes: :mod:`matplotlib.axes.Axes`
        """
        if not self._trace.hasEvents('sched_switch'):
            self._log.warning('Event [sched_switch] not found, '
                              'plot DISABLED!')
            return
        if not self._trace.hasEvents('sched_wakeup'):
            self._log.warning('Event [sched_wakeup] not found, '
                              'plot DISABLED!')
            return

        # Get task PID
        td = self._getTaskData(task)
        if not td:
            return None

        wkl_df = self._dfg_latency_wakeup_df(td.pid)
        prt_df = self._dfg_latency_preemption_df(td.pid)

        if wkl_df is None and prt_df is None:
            self._log.warning('No task with name [%s]', td.label)
            return

        # If not axis provided: generate a standalone plot
        if not axes:
            gs = gridspec.GridSpec(1, 1)
            plt.figure(figsize=(16, 2))
            axes = plt.subplot(gs[0, 0])
            axes.set_title('Latencies on [{}] '
                           '(red: WAKEUP, blue: PREEMPT)'\
                          .format(td.label))
            axes.set_xlim(self._trace.x_min, self._trace.x_max)
            axes.set_yticklabels([])
            axes.set_xlabel('Time [s]')
            axes.grid(True)

        # Draw WAKEUP latencies
        try:
            bands = [(t, wkl_df['wakeup_latency'][t]) for t in wkl_df.index]
            for (start, duration) in bands:
                end = start + duration
                axes.axvspan(start, end, facecolor='r', alpha=0.1)
        except: pass

        # Draw PREEMPTION latencies
        try:
            bands = [(t, prt_df['preempt_latency'][t]) for t in prt_df.index]
            for (start, duration) in bands:
                end = start + duration
                axes.axvspan(start, end, facecolor='b', alpha=0.1)
        except: pass

###############################################################################
# Utility Methods
###############################################################################

    @memoized
    def _getTaskData(self, task):

        # Get task PID
        if isinstance(task, str):
            task_pids = self._trace.getTaskByName(task)
            if len(task_pids) == 0:
                self._log.warning('No tasks found with name [%s]', task)
                return None

            task_pid = task_pids[0]
            if len(task_pids) > 1:
                self._log.warning('Multiple PIDs for task named [%s]', task)
                for pid in task_pids:
                    self._log.warning('  %5d :  %s', pid,
                                      ','.join(self._trace.getTaskByPid(pid)))
                self._log.warning('Returning stats only for PID: %d',
                                  task_pid)
            task_names = self._trace.getTaskByPid(task_pid)

        # Get task name
        elif isinstance(task, int):
            task_pid = task
            task_names = self._trace.getTaskByPid(task_pid)
            if len(task_names) == 0:
                self._log.warning('No tasks found with name [%s]', task)
                return None

        else:
            raise ValueError("Task must be either an int or str")

        task_label = "{}: {}".format(task_pid, ', '.join(task_names))
        return TaskData(task_pid, task_names, task_label)

    @memoized
    def _taskState(self, state):
        try:
            state = int(state)
        except ValueError:
            # State already converted to symbol
            return state

        # Tasks STATE flags (Linux 4.8)
        TASK_STATES = {
              0: "R", # TASK_RUNNING
              1: "S", # TASK_INTERRUPTIBLE
              2: "D", # TASK_UNINTERRUPTIBLE
              4: "T", # __TASK_STOPPED
              8: "t", # __TASK_TRACED
             16: "X", # EXIT_DEAD
             32: "Z", # EXIT_ZOMBIE
             64: "x", # TASK_DEAD
            128: "K", # TASK_WAKEKILL
            256: "W", # TASK_WAKING
            512: "P", # TASK_PARKED
           1024: "N", # TASK_NOLOAD
           2048: "n", # TASK_NEW
        }
        TASK_MAX_STATE = 4096

        res = "R"
        if state & (TASK_MAX_STATE - 1) != 0:
            res = ""
        for key in TASK_STATES.keys():
            if key & state:
                res += TASK_STATES[key]
        if state & TASK_MAX_STATE:
            res += "+"
        else:
            res = '|'.join(res)
        return res

# vim :set tabstop=4 shiftwidth=4 expandtab
<|endoftext|>


<commit_before>#
# This file is part of LiteX-Boards.
#
# Copyright (c) 2022 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
# SPDX-License-Identifier: BSD-2-Clause

from litex.build.generic_platform import *
from litex.build.xilinx import XilinxPlatform
from litex.build.openfpgaloader import OpenFPGALoader

# IOs ----------------------------------------------------------------------------------------------

_io = [
    # Clk / Rst
    ("clk25", 0, Pins("AB11"), IOStandard("LVCMOS33")),

    # Leds
    ("user_led", 0, Pins("W13"),  IOStandard("LVCMOS33")),
    ("user_led", 1, Pins("Y12"),  IOStandard("LVCMOS33")),
    ("user_led", 2, Pins("AA12"), IOStandard("LVCMOS33")),
    ("user_led", 3, Pins("AB13"), IOStandard("LVCMOS33")),

    # Buttons
    ("user_btn", 0, Pins("AA13"), IOStandard("LVCMOS33")),
    ("user_btn", 1, Pins("AE14"), IOStandard("LVCMOS33")),
    ("user_btn", 2, Pins("AE15"), IOStandard("LVCMOS33")),
    ("user_btn", 3, Pins("AG14"), IOStandard("LVCMOS33")),

    # Serial (no UART by default -> use J15 3 & 5)
    ("serial", 0,
        Subsignal("tx", Pins("A11")),
        Subsignal("rx", Pins("A13")),
        IOStandard("LVCMOS33")
    ),

    # MIPI 0
    ("camera", 0,
        Subsignal("mclk",    Pins("AG13"),IOStandard("LVCMOS33")),
        Subsignal("clkp",    Pins("AC9")),
        Subsignal("clkn",    Pins("AD9")),
        Subsignal("dp",      Pins("AE9 AB8")),
        Subsignal("dn",      Pins("AE8 AC8")),
        IOStandard("MIPI_DPHY")
    ),
    ("mipi_gpio", 0,
        Subsignal("gpio",    Pins("AH14")),
        IOStandard("LVCMOS33")
    ),
    ("mipi_i2c", 0,
        Subsignal("scl",     Pins("AH13")),
        Subsignal("sda",     Pins("AE13")),
        IOStandard("LVCMOS33")
    ),

    # MIPI 1
    ("camera", 1,
        Subsignal("mclk",    Pins("AC14"), IOStandard("LVCMOS33")),
        Subsignal("clkp",    Pins("U9")),
        Subsignal("clkn",    Pins("V9")),
        Subsignal("dp",      Pins("U8 W8")),
        Subsignal("dn",      Pins("V8 Y8")),
        IOStandard("MIPI_DPHY")
    ),
    ("mipi_gpio", 1,
        Subsignal("gpio",    Pins("AD15")),
        IOStandard("LVCMOS33")
    ),
    ("mipi_i2c", 1,
        Subsignal("scl",     Pins("AD14")),
        Subsignal("sda",     Pins("AC13")),
        IOStandard("LVCMOS33")
    )

]

# Connectors ---------------------------------------------------------------------------------------

_connectors = [
    ("J12", {
         3: "F7",  4: "G8",
         5: "F6",  6: "G6",
         7: "D9",  8: "E9",
         9: "F5", 10: "G5",
        11: "E8", 12: "F8",
        13: "D5", 14: "E5",
        15: "C4", 16: "D4",
        17: "E3", 18: "E4",
        19: "F1", 20: "G1",
        21: "E2", 22: "F2",
        23: "D6", 24: "D7",
        25: "B9", 26: "C9",
        27: "A4", 28: "B4",
        29: "B6", 30: "C6",
        31: "A6", 32: "A7",
        33: "B8", 34: "C8",
        35: "A8", 36: "A9",
    }),
    ("j15", {
         3: "A11",  4: "A12",
         5: "A13",  6: "B13",
         7: "A14",  8: "B14",
         9: "E13", 10: "E14",
        11: "A15", 12: "B15",
        13: "C13", 14: "C14",
        15: "B10", 16: "C11",
        17: "D14", 18: "D15",
        19: "F11", 20: "F12",
        21: "H13", 22: "H14",
        23: "G14", 24: "G15",
        25: "F10", 26: "G11",
        27: "H12", 28: "J12",
        29: "J14", 30: "K14",
        31: "K12", 32: "K13",
        33: "L13", 34: "L14",
        35: "G10", 36: "H11",
    })
]

# Platform -----------------------------------------------------------------------------------------

class Platform(XilinxPlatform):
    default_clk_name   = "clk25"
    default_clk_period = 1e9/25e6

    def __init__(self):
        XilinxPlatform.__init__(self, "xczu2cg-sfvc784-1-e", _io, _connectors, toolchain="vivado")

    def create_programmer(self, cable):
        return OpenFPGALoader("axu2cga", cable)

    def do_finalize(self, fragment):
        XilinxPlatform.do_finalize(self, fragment)
        self.add_period_constraint(self.lookup_request("clk25", loose=True), 1e9/25e6)
<commit_msg>platforms/alinx_axu2cga: add zynqmp PS configuration params<commit_after>#
# This file is part of LiteX-Boards.
#
# Copyright (c) 2022 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
# SPDX-License-Identifier: BSD-2-Clause

from litex.build.generic_platform import *
from litex.build.xilinx import XilinxPlatform
from litex.build.openfpgaloader import OpenFPGALoader

# IOs ----------------------------------------------------------------------------------------------

_io = [
    # Clk / Rst
    ("clk25", 0, Pins("AB11"), IOStandard("LVCMOS33")),

    # Leds
    ("user_led", 0, Pins("W13"),  IOStandard("LVCMOS33")),
    ("user_led", 1, Pins("Y12"),  IOStandard("LVCMOS33")),
    ("user_led", 2, Pins("AA12"), IOStandard("LVCMOS33")),
    ("user_led", 3, Pins("AB13"), IOStandard("LVCMOS33")),

    # Buttons
    ("user_btn", 0, Pins("AA13"), IOStandard("LVCMOS33")),
    ("user_btn", 1, Pins("AE14"), IOStandard("LVCMOS33")),
    ("user_btn", 2, Pins("AE15"), IOStandard("LVCMOS33")),
    ("user_btn", 3, Pins("AG14"), IOStandard("LVCMOS33")),

    # Serial (no UART by default -> use J15 3 & 5)
    ("serial", 0,
        Subsignal("tx", Pins("A11")),
        Subsignal("rx", Pins("A13")),
        IOStandard("LVCMOS33")
    ),

    # MIPI 0
    ("camera", 0,
        Subsignal("mclk",    Pins("AG13"),IOStandard("LVCMOS33")),
        Subsignal("clkp",    Pins("AC9")),
        Subsignal("clkn",    Pins("AD9")),
        Subsignal("dp",      Pins("AE9 AB8")),
        Subsignal("dn",      Pins("AE8 AC8")),
        IOStandard("MIPI_DPHY")
    ),
    ("mipi_gpio", 0,
        Subsignal("gpio",    Pins("AH14")),
        IOStandard("LVCMOS33")
    ),
    ("mipi_i2c", 0,
        Subsignal("scl",     Pins("AH13")),
        Subsignal("sda",     Pins("AE13")),
        IOStandard("LVCMOS33")
    ),

    # MIPI 1
    ("camera", 1,
        Subsignal("mclk",    Pins("AC14"), IOStandard("LVCMOS33")),
        Subsignal("clkp",    Pins("U9")),
        Subsignal("clkn",    Pins("V9")),
        Subsignal("dp",      Pins("U8 W8")),
        Subsignal("dn",      Pins("V8 Y8")),
        IOStandard("MIPI_DPHY")
    ),
    ("mipi_gpio", 1,
        Subsignal("gpio",    Pins("AD15")),
        IOStandard("LVCMOS33")
    ),
    ("mipi_i2c", 1,
        Subsignal("scl",     Pins("AD14")),
        Subsignal("sda",     Pins("AC13")),
        IOStandard("LVCMOS33")
    )

]

# Connectors ---------------------------------------------------------------------------------------

_connectors = [
    ("J12", {
         3: "F7",  4: "G8",
         5: "F6",  6: "G6",
         7: "D9",  8: "E9",
         9: "F5", 10: "G5",
        11: "E8", 12: "F8",
        13: "D5", 14: "E5",
        15: "C4", 16: "D4",
        17: "E3", 18: "E4",
        19: "F1", 20: "G1",
        21: "E2", 22: "F2",
        23: "D6", 24: "D7",
        25: "B9", 26: "C9",
        27: "A4", 28: "B4",
        29: "B6", 30: "C6",
        31: "A6", 32: "A7",
        33: "B8", 34: "C8",
        35: "A8", 36: "A9",
    }),
    ("j15", {
         3: "A11",  4: "A12",
         5: "A13",  6: "B13",
         7: "A14",  8: "B14",
         9: "E13", 10: "E14",
        11: "A15", 12: "B15",
        13: "C13", 14: "C14",
        15: "B10", 16: "C11",
        17: "D14", 18: "D15",
        19: "F11", 20: "F12",
        21: "H13", 22: "H14",
        23: "G14", 24: "G15",
        25: "F10", 26: "G11",
        27: "H12", 28: "J12",
        29: "J14", 30: "K14",
        31: "K12", 32: "K13",
        33: "L13", 34: "L14",
        35: "G10", 36: "H11",
    })
]

# PSU config ---------------------------------------------------------------------------------------
psu_config = {
    "PSU__DPAUX__PERIPHERAL__IO":              "MIO 27 .. 30",
    "PSU__ENET3__PERIPHERAL__ENABLE":          "1",
    "PSU__ENET3__GRP_MDIO__ENABLE":            "1",
    "PSU__I2C1__PERIPHERAL__ENABLE":           "1",
    "PSU__I2C1__PERIPHERAL__IO":               "MIO 32 .. 33",
    "PSU__PCIE__PERIPHERAL__ENABLE":           "1",
    "PSU__PCIE__PERIPHERAL__ROOTPORT_IO":      "MIO 37",
    "PSU__USB0__REF_CLK_SEL":                  "Ref Clk1",
    "PSU__PCIE__DEVICE_PORT_TYPE":             "Root Port",
    "PSU__PCIE__CLASS_CODE_SUB":               "0x04",
    "SUBPRESET1":                              "DDR4_MICRON_MT40A256M16GE_083E",
    "PSU__QSPI__PERIPHERAL__ENABLE":           "1",
    "PSU__QSPI__PERIPHERAL__DATA_MODE":        "x4",
    "PSU__QSPI__GRP_FBCLK__ENABLE":            "1",
    "PSU__SD1__PERIPHERAL__ENABLE":            "1",
    "PSU__SD1__PERIPHERAL__IO":                "MIO 46 .. 51",
    "PSU__SD1__GRP_CD__ENABLE":                "1",
    "PSU__SD1__SLOT_TYPE":                     "SD 2.0",
    "PSU__TTC0__PERIPHERAL__ENABLE":           "1",
    "PSU__TTC1__PERIPHERAL__ENABLE":           "1",
    "PSU__TTC2__PERIPHERAL__ENABLE":           "1",
    "PSU__TTC3__PERIPHERAL__ENABLE":           "1",
    "PSU__DDRC__BUS_WIDTH":                    "32 Bit",
    "PSU__UART1__PERIPHERAL__ENABLE":          "1",
    "PSU__UART1__PERIPHERAL__IO":              "MIO 24 .. 25",
    "PSU__USB0__PERIPHERAL__ENABLE":           "1",
    "PSU__USB0__RESET__ENABLE":                "1",
    "PSU__USB0__RESET__IO":                    "MIO 44",
    "PSU__USB__RESET__MODE":                   "Shared MIO Pin",
    "PSU__USB3_0__PERIPHERAL__ENABLE":         "1",
    "PSU__USB3_0__PERIPHERAL__IO":             "GT Lane1",
    "PSU_BANK_0_IO_STANDARD":                  "LVCMOS18",
    "PSU_BANK_1_IO_STANDARD":                  "LVCMOS18",
    "PSU_BANK_2_IO_STANDARD":                  "LVCMOS18",
    "PSU__DISPLAYPORT__PERIPHERAL__ENABLE":    "1",
    "PSU__CRF_APB__DP_VIDEO_REF_CTRL__SRCSEL": "VPLL",
    "PSU__CRF_APB__DP_AUDIO_REF_CTRL__SRCSEL": "RPLL",
    "PSU__CRF_APB__DP_STC_REF_CTRL__SRCSEL":   "RPLL",
    "PSU__CRF_APB__TOPSW_MAIN_CTRL__SRCSEL":   "APLL",
}

# Platform -----------------------------------------------------------------------------------------

class Platform(XilinxPlatform):
    default_clk_name   = "clk25"
    default_clk_period = 1e9/25e6

    def __init__(self):
        XilinxPlatform.__init__(self, "xczu2cg-sfvc784-1-e", _io, _connectors, toolchain="vivado")
        self.psu_config = psu_config

    def create_programmer(self, cable):
        return OpenFPGALoader("axu2cga", cable)

    def do_finalize(self, fragment):
        XilinxPlatform.do_finalize(self, fragment)
        self.add_period_constraint(self.lookup_request("clk25", loose=True), 1e9/25e6)
<|endoftext|>


<commit_before>#
# This file is part of LiteX-Boards.
#
# Copyright (c) 2018-2019 Florent Kermarrec <florent@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause

from litex.build.generic_platform import *
from litex.build.lattice import LatticePlatform
from litex.build.lattice.programmer import UJProg

# IOs ----------------------------------------------------------------------------------------------

_io_common = [
    # Clk / Rst
    ("clk25", 0, Pins("G2"), IOStandard("LVCMOS33")),
    ("rst",   0, Pins("R1"), IOStandard("LVCMOS33")),

    # Buttons
    ("user_button", 0, Pins("D6"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")), # PWR
    ("user_button", 1, Pins("R1"), IOStandard("LVCMOS33"), Misc("PULLMODE=DOWN")), # F1
    ("user_button", 2, Pins("T1"), IOStandard("LVCMOS33"), Misc("PULLMODE=DOWN")), # F2
    ("user_button", 3, Pins("R18"), IOStandard("LVCMOS33"), Misc("PULLMODE=DOWN")), # UP
    ("user_button", 4, Pins("V1"), IOStandard("LVCMOS33"), Misc("PULLMODE=DOWN")), # DOWN
    ("user_button", 5, Pins("U1"), IOStandard("LVCMOS33"), Misc("PULLMODE=DOWN")), # LEFT
    ("user_button", 6, Pins("H16"), IOStandard("LVCMOS33"), Misc("PULLMODE=DOWN")), # RIGHT

    # Leds
    ("user_led", 0, Pins("B2"), IOStandard("LVCMOS33")),
    ("user_led", 1, Pins("C2"), IOStandard("LVCMOS33")),
    ("user_led", 2, Pins("C1"), IOStandard("LVCMOS33")),
    ("user_led", 3, Pins("D2"), IOStandard("LVCMOS33")),
    ("user_led", 4, Pins("D1"), IOStandard("LVCMOS33")),
    ("user_led", 5, Pins("E2"), IOStandard("LVCMOS33")),
    ("user_led", 6, Pins("E1"), IOStandard("LVCMOS33")),
    ("user_led", 7, Pins("H3"), IOStandard("LVCMOS33")),

    # Serial
    ("serial", 0,
        Subsignal("tx", Pins("L4"), IOStandard("LVCMOS33")),
        Subsignal("rx", Pins("M1"), IOStandard("LVCMOS33"))
    ),

    # SDR SDRAM
    ("sdram_clock", 0, Pins("F19"), IOStandard("LVCMOS33")),
    ("sdram", 0,
        Subsignal("a",     Pins(
            "M20 M19 L20 L19 K20 K19 K18 J20",
            "J19 H20 N19 G20 G19")),
        Subsignal("dq",    Pins(
            "J16 L18 M18 N18 P18 T18 T17 U20",
            "E19 D20 D19 C20 E18 F18 J18 J17")),
        Subsignal("we_n",  Pins("T20")),
        Subsignal("ras_n", Pins("R20")),
        Subsignal("cas_n", Pins("T19")),
        Subsignal("cs_n",  Pins("P20")),
        Subsignal("cke",   Pins("F20")),
        Subsignal("ba",    Pins("P19 N20")),
        Subsignal("dm",    Pins("U19 E20")),
        IOStandard("LVCMOS33"),
        Misc("SLEWRATE=FAST"),
    ),

    # GPIOs
    ("gpio", 0,
        Subsignal("p", Pins("B11")),
        Subsignal("n", Pins("C11")),
        IOStandard("LVCMOS33")
    ),
    ("gpio", 1,
        Subsignal("p", Pins("A10")),
        Subsignal("n", Pins("A11")),
        IOStandard("LVCMOS33")
    ),
    ("gpio", 2,
        Subsignal("p", Pins("A9")),
        Subsignal("n", Pins("B10")),
        IOStandard("LVCMOS33")
    ),
    ("gpio", 3,
        Subsignal("p", Pins("B9")),
        Subsignal("n", Pins("C10")),
        IOStandard("LVCMOS33")
    ),

    # USB
    ("usb", 0,
        Subsignal("d_p", Pins("D15")),
        Subsignal("d_n", Pins("E15")),
        Subsignal("pullup", Pins("B12 C12")),
        IOStandard("LVCMOS33")
    ),

    # SPIFlash
    ("spiflash", 0,
        Subsignal("cs_n", Pins("R2")),
        Subsignal("miso", Pins("V2")),
        Subsignal("mosi", Pins("W2")),
        Subsignal("wp", Pins("Y2")),
        Subsignal("hold", Pins("W1")),
        IOStandard("LVCMOS33")
    ),
    ("spiflash4x", 0,
        Subsignal("cs_n", Pins("R2")),
        Subsignal("dq", Pins("W2", "V2", "Y2", "W1")),
        IOStandard("LVCMOS33")
    ),

    # OLED
    ("oled_spi", 0,
        Subsignal("clk",  Pins("P4")),
        Subsignal("mosi", Pins("P3")),
        IOStandard("LVCMOS33"),
    ),
    ("oled_ctl", 0,
        Subsignal("dc",   Pins("P1")),
        Subsignal("resn", Pins("P2")),
        Subsignal("csn",  Pins("N2")),
        IOStandard("LVCMOS33"),
    ),

    # Others
    ("ext0p", 0, Pins("B11"), IOStandard("LVCMOS33")),
    ("ext1p", 0, Pins("A10"), IOStandard("LVCMOS33")),
]

_io_1_7 = [
    # SDCard
    ("spisdcard", 0,
        Subsignal("clk",  Pins("J1")),
        Subsignal("mosi", Pins("J3"), Misc("PULLMODE=UP")),
        Subsignal("cs_n", Pins("H1"), Misc("PULLMODE=UP")),
        Subsignal("miso", Pins("K2"), Misc("PULLMODE=UP")),
        Misc("SLEWRATE=FAST"),
        IOStandard("LVCMOS33"),
    ),
    ("sdcard", 0,
        Subsignal("clk",  Pins("J1")),
        Subsignal("cmd",  Pins("J3"), Misc("PULLMODE=UP")),
        Subsignal("data", Pins("K2 K1 H2 H1"), Misc("PULLMODE=UP")),
        Misc("SLEWRATE=FAST"),
        IOStandard("LVCMOS33"),
    ),

    # GPDI
    ("gpdi", 0,
        Subsignal("clk_p",    Pins("A17"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("clk_n",   Pins("B18"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        Subsignal("data0_p",  Pins("A12"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("data0_n", Pins("A13"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        Subsignal("data1_p",  Pins("A14"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("data1_n", Pins("C14"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        Subsignal("data2_p",  Pins("A16"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("data2_n", Pins("B16"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("cec",     Pins("A18"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")),
        #Subsignal("scl",     Pins("E19"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")),
        #Subsignal("sda",     Pins("B19"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP"))
    ),
    # Wifi power
    ("wifi_gpio0", 0, Pins("L2"), IOStandard("LVCMOS33")),
]

_io_2_0 = [
    # SDCard
    ("spisdcard", 0,
        Subsignal("clk",  Pins("H2")),
        Subsignal("mosi", Pins("J1"), Misc("PULLMODE=UP")),
        Subsignal("cs_n", Pins("K2"), Misc("PULLMODE=UP")),
        Subsignal("miso", Pins("J3"), Misc("PULLMODE=UP")),
        Misc("SLEWRATE=FAST"),
        IOStandard("LVCMOS33"),
    ),
    ("sdcard", 0,
        Subsignal("clk",  Pins("H2")),
        Subsignal("cmd",  Pins("J1"), Misc("PULLMODE=UP")),
        Subsignal("data", Pins("J3 H1 K1 K2"), Misc("PULLMODE=UP")),
        #Subsignal("cd", Pins("N5")), # Not connected
        #Subsignal("wp", Pins("P5")), # Not connected
        Misc("SLEWRATE=FAST"),
        IOStandard("LVCMOS33"),
    ),

    # GPDI
    ("gpdi", 0,
        Subsignal("clk_p",    Pins("A17"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("clk_n",   Pins("B18"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        Subsignal("data0_p",  Pins("A16"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("data0_n", Pins("B16"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        Subsignal("data1_p",  Pins("A14"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("data1_n", Pins("C14"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        Subsignal("data2_p",  Pins("A12"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("data2_n", Pins("A13"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("cec",     Pins("A18"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")),
        #Subsignal("scl",     Pins("E19"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")),
        #Subsignal("sda",     Pins("B19"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP"))
    ),
    # Wifi power
    ("wifi_gpio0", 0, Pins("F1"), IOStandard("LVCMOS33")),
]

# Platform -----------------------------------------------------------------------------------------

class Platform(LatticePlatform):
    default_clk_name   = "clk25"
    default_clk_period = 1e9/25e6

    def __init__(self, device="LFE5U-45F", revision="2.0", toolchain="trellis", **kwargs):
        assert device in ["LFE5U-12F", "LFE5U-25F", "LFE5U-45F", "LFE5U-85F"]
        assert revision in ["1.7", "2.0"]
        _io = _io_common + {"1.7": _io_1_7, "2.0": _io_2_0}[revision]
        LatticePlatform.__init__(self, device + "-6BG381C", _io, toolchain=toolchain, **kwargs)

    def create_programmer(self):
        return UJProg()

    def do_finalize(self, fragment):
        LatticePlatform.do_finalize(self, fragment)
        self.add_period_constraint(self.lookup_request("clk25", loose=True), 1e9/25e6)
<commit_msg>radiona/ulx3s: Rename user_button to user_btn (consistency with other boards) and fix ident.<commit_after>#
# This file is part of LiteX-Boards.
#
# Copyright (c) 2018-2019 Florent Kermarrec <florent@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause

from litex.build.generic_platform import *
from litex.build.lattice import LatticePlatform
from litex.build.lattice.programmer import UJProg

# IOs ----------------------------------------------------------------------------------------------

_io_common = [
    # Clk / Rst
    ("clk25", 0, Pins("G2"), IOStandard("LVCMOS33")),
    ("rst",   0, Pins("R1"), IOStandard("LVCMOS33")),

    # Buttons
    ("user_btn", 0, Pins( "D6"), IOStandard("LVCMOS33"), Misc("PULLMODE=DOWN")), # PWR
    ("user_btn", 1, Pins( "R1"), IOStandard("LVCMOS33"), Misc("PULLMODE=DOWN")), # F1
    ("user_btn", 2, Pins( "T1"), IOStandard("LVCMOS33"), Misc("PULLMODE=DOWN")), # F2
    ("user_btn", 3, Pins("R18"), IOStandard("LVCMOS33"), Misc("PULLMODE=DOWN")), # UP
    ("user_btn", 4, Pins( "V1"), IOStandard("LVCMOS33"), Misc("PULLMODE=DOWN")), # DOWN
    ("user_btn", 5, Pins( "U1"), IOStandard("LVCMOS33"), Misc("PULLMODE=DOWN")), # LEFT
    ("user_btn", 6, Pins("H16"), IOStandard("LVCMOS33"), Misc("PULLMODE=DOWN")), # RIGHT

    # Leds
    ("user_led", 0, Pins("B2"), IOStandard("LVCMOS33")),
    ("user_led", 1, Pins("C2"), IOStandard("LVCMOS33")),
    ("user_led", 2, Pins("C1"), IOStandard("LVCMOS33")),
    ("user_led", 3, Pins("D2"), IOStandard("LVCMOS33")),
    ("user_led", 4, Pins("D1"), IOStandard("LVCMOS33")),
    ("user_led", 5, Pins("E2"), IOStandard("LVCMOS33")),
    ("user_led", 6, Pins("E1"), IOStandard("LVCMOS33")),
    ("user_led", 7, Pins("H3"), IOStandard("LVCMOS33")),

    # Serial
    ("serial", 0,
        Subsignal("tx", Pins("L4"), IOStandard("LVCMOS33")),
        Subsignal("rx", Pins("M1"), IOStandard("LVCMOS33"))
    ),

    # SDR SDRAM
    ("sdram_clock", 0, Pins("F19"), IOStandard("LVCMOS33")),
    ("sdram", 0,
        Subsignal("a",     Pins(
            "M20 M19 L20 L19 K20 K19 K18 J20",
            "J19 H20 N19 G20 G19")),
        Subsignal("dq",    Pins(
            "J16 L18 M18 N18 P18 T18 T17 U20",
            "E19 D20 D19 C20 E18 F18 J18 J17")),
        Subsignal("we_n",  Pins("T20")),
        Subsignal("ras_n", Pins("R20")),
        Subsignal("cas_n", Pins("T19")),
        Subsignal("cs_n",  Pins("P20")),
        Subsignal("cke",   Pins("F20")),
        Subsignal("ba",    Pins("P19 N20")),
        Subsignal("dm",    Pins("U19 E20")),
        IOStandard("LVCMOS33"),
        Misc("SLEWRATE=FAST"),
    ),

    # GPIOs
    ("gpio", 0,
        Subsignal("p", Pins("B11")),
        Subsignal("n", Pins("C11")),
        IOStandard("LVCMOS33")
    ),
    ("gpio", 1,
        Subsignal("p", Pins("A10")),
        Subsignal("n", Pins("A11")),
        IOStandard("LVCMOS33")
    ),
    ("gpio", 2,
        Subsignal("p", Pins("A9")),
        Subsignal("n", Pins("B10")),
        IOStandard("LVCMOS33")
    ),
    ("gpio", 3,
        Subsignal("p", Pins("B9")),
        Subsignal("n", Pins("C10")),
        IOStandard("LVCMOS33")
    ),

    # USB
    ("usb", 0,
        Subsignal("d_p", Pins("D15")),
        Subsignal("d_n", Pins("E15")),
        Subsignal("pullup", Pins("B12 C12")),
        IOStandard("LVCMOS33")
    ),

    # SPIFlash
    ("spiflash", 0,
        Subsignal("cs_n", Pins("R2")),
        Subsignal("miso", Pins("V2")),
        Subsignal("mosi", Pins("W2")),
        Subsignal("wp", Pins("Y2")),
        Subsignal("hold", Pins("W1")),
        IOStandard("LVCMOS33")
    ),
    ("spiflash4x", 0,
        Subsignal("cs_n", Pins("R2")),
        Subsignal("dq", Pins("W2", "V2", "Y2", "W1")),
        IOStandard("LVCMOS33")
    ),

    # OLED
    ("oled_spi", 0,
        Subsignal("clk",  Pins("P4")),
        Subsignal("mosi", Pins("P3")),
        IOStandard("LVCMOS33"),
    ),
    ("oled_ctl", 0,
        Subsignal("dc",   Pins("P1")),
        Subsignal("resn", Pins("P2")),
        Subsignal("csn",  Pins("N2")),
        IOStandard("LVCMOS33"),
    ),

    # Others
    ("ext0p", 0, Pins("B11"), IOStandard("LVCMOS33")),
    ("ext1p", 0, Pins("A10"), IOStandard("LVCMOS33")),
]

_io_1_7 = [
    # SDCard
    ("spisdcard", 0,
        Subsignal("clk",  Pins("J1")),
        Subsignal("mosi", Pins("J3"), Misc("PULLMODE=UP")),
        Subsignal("cs_n", Pins("H1"), Misc("PULLMODE=UP")),
        Subsignal("miso", Pins("K2"), Misc("PULLMODE=UP")),
        Misc("SLEWRATE=FAST"),
        IOStandard("LVCMOS33"),
    ),
    ("sdcard", 0,
        Subsignal("clk",  Pins("J1")),
        Subsignal("cmd",  Pins("J3"), Misc("PULLMODE=UP")),
        Subsignal("data", Pins("K2 K1 H2 H1"), Misc("PULLMODE=UP")),
        Misc("SLEWRATE=FAST"),
        IOStandard("LVCMOS33"),
    ),

    # GPDI
    ("gpdi", 0,
        Subsignal("clk_p",    Pins("A17"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("clk_n",   Pins("B18"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        Subsignal("data0_p",  Pins("A12"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("data0_n", Pins("A13"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        Subsignal("data1_p",  Pins("A14"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("data1_n", Pins("C14"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        Subsignal("data2_p",  Pins("A16"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("data2_n", Pins("B16"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("cec",     Pins("A18"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")),
        #Subsignal("scl",     Pins("E19"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")),
        #Subsignal("sda",     Pins("B19"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP"))
    ),
    # Wifi power
    ("wifi_gpio0", 0, Pins("L2"), IOStandard("LVCMOS33")),
]

_io_2_0 = [
    # SDCard
    ("spisdcard", 0,
        Subsignal("clk",  Pins("H2")),
        Subsignal("mosi", Pins("J1"), Misc("PULLMODE=UP")),
        Subsignal("cs_n", Pins("K2"), Misc("PULLMODE=UP")),
        Subsignal("miso", Pins("J3"), Misc("PULLMODE=UP")),
        Misc("SLEWRATE=FAST"),
        IOStandard("LVCMOS33"),
    ),
    ("sdcard", 0,
        Subsignal("clk",  Pins("H2")),
        Subsignal("cmd",  Pins("J1"), Misc("PULLMODE=UP")),
        Subsignal("data", Pins("J3 H1 K1 K2"), Misc("PULLMODE=UP")),
        #Subsignal("cd", Pins("N5")), # Not connected
        #Subsignal("wp", Pins("P5")), # Not connected
        Misc("SLEWRATE=FAST"),
        IOStandard("LVCMOS33"),
    ),

    # GPDI
    ("gpdi", 0,
        Subsignal("clk_p",    Pins("A17"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("clk_n",   Pins("B18"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        Subsignal("data0_p",  Pins("A16"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("data0_n", Pins("B16"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        Subsignal("data1_p",  Pins("A14"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("data1_n", Pins("C14"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        Subsignal("data2_p",  Pins("A12"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("data2_n", Pins("A13"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
        #Subsignal("cec",     Pins("A18"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")),
        #Subsignal("scl",     Pins("E19"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")),
        #Subsignal("sda",     Pins("B19"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP"))
    ),
    # Wifi power
    ("wifi_gpio0", 0, Pins("F1"), IOStandard("LVCMOS33")),
]

# Platform -----------------------------------------------------------------------------------------

class Platform(LatticePlatform):
    default_clk_name   = "clk25"
    default_clk_period = 1e9/25e6

    def __init__(self, device="LFE5U-45F", revision="2.0", toolchain="trellis", **kwargs):
        assert device in ["LFE5U-12F", "LFE5U-25F", "LFE5U-45F", "LFE5U-85F"]
        assert revision in ["1.7", "2.0"]
        _io = _io_common + {"1.7": _io_1_7, "2.0": _io_2_0}[revision]
        LatticePlatform.__init__(self, device + "-6BG381C", _io, toolchain=toolchain, **kwargs)

    def create_programmer(self):
        return UJProg()

    def do_finalize(self, fragment):
        LatticePlatform.do_finalize(self, fragment)
        self.add_period_constraint(self.lookup_request("clk25", loose=True), 1e9/25e6)
<|endoftext|>