from pynq import MMIO
from pynq import PL
LEDS_OFFSET0 = 0
class MyLED(object):
"""This class controls the onboard LEDs vi axi_gpio_0. """
_mmio = None
_leds_value = 0
def __init__(self):
"""Create a new MyLED object. """
if MyLED._mmio is None:
MyLED._mmio = MMIO(int(PL.ip_dict["SEG_axi_gpio_0_Reg"][0],16),16)
MyLED._mmio.write(LEDS_OFFSET0, 0x0)
def set(self, value):
"""Turn on a LED.
Parameters
----------
Value = GPIO out data
Returns
-------
None
"""
MyLED._mmio.write(LEDS_OFFSET0, value)
PYNQ-Z1はZYBOとよく似たZYNQ SoCを使ったFPGAボードですが、Pythonを使ってLinuxからFPGAのリソースにアクセスできることが特徴です。PYNQではFPGAのConfiguration Data (bitsteam) をOverlayと呼んでおり、標準でPYNQのI/Oやビデオ関係の処理ができるOverlayが提供されているのですが、ドキュメントを読んでいると、カスタムOverlayも作成できるとあります。
class Bitstream(PL):
"""This class instantiates a programmable logic bitstream.
Attributes
----------
bitfile_name : str
The absolute path of the bitstream.
timestamp : str
Timestamp when loading the bitstream. Format:
year, month, day, hour, minute, second, microsecond
"""
def __init__(self, bitfile_name):
"""Return a new Bitstream object.
Users can either specify an absolute path to the bitstream file
(e.g. '/home/xilinx/src/pynq/bitstream/base.bit'),
or only a relative path.
(e.g. 'base.bit').
Note
----
self.bitstream always stores the absolute path of the bitstream.
Parameters
----------
bitfile_name : str
The bitstream absolute path or name as a string.
"""
super().__init__()
if not isinstance(bitfile_name, str):
raise TypeError("Bitstream name has to be a string.")
if os.path.isfile(bitfile_name):
self.bitfile_name = bitfile_name
elif os.path.isfile(general_const.BS_SEARCH_PATH + bitfile_name):
self.bitfile_name = general_const.BS_SEARCH_PATH + bitfile_name
else:
raise IOError('Bitstream file {} does not exist.'\
.format(bitfile_name))
self.timestamp = ''
def download(self):
"""The method to download the bitstream onto PL.
Note
----
The class variables held by the singleton PL will also be updated.
Parameters
----------
None
Returns
-------
None
"""
# Compose bitfile name, open bitfile
with open(self.bitfile_name, 'rb') as f:
buf = f.read()
# Set is_partial_bitfile device attribute to 0
with open(general_const.BS_IS_PARTIAL, 'w') as fd:
fd.write('0')
# Write bitfile to xdevcfg device
with open(general_const.BS_XDEVCFG, 'wb') as f:
f.write(buf)
t = datetime.now()
self.timestamp = "{}/{}/{} {}:{}:{} +{}".format(t.year,t.month,t.day,\
t.hour,t.minute,t.second,t.microsecond)
# Update PL information
PL._client_request()
PL._bitfile_name = self.bitfile_name
PL._timestamp = self.timestamp
PL._ip_dict = {}
PL._gpio_dict = {}
PL._server_update()
class MMIO:
""" This class exposes API for MMIO read and write.
Attributes
----------
virt_base : int
The address of the page for the MMIO base address.
virt_offset : int
The offset of the MMIO base address from the virt_base.
base_addr : int
The base address, not necessarily page aligned.
length : int
The length in bytes of the address range.
debug : bool
Turn on debug mode if it is True.
mmap_file : file
Underlying file object for MMIO mapping
mem : mmap
An mmap object created when mapping files to memory.
array : numpy.ndarray
A numpy view of the mapped range for efficient assignment
"""
def __init__(self, base_addr, length=4, debug=False):
"""Return a new MMIO object.
Parameters
----------
base_addr : int
The base address of the MMIO.
length : int
The length in bytes; default is 4.
debug : bool
Turn on debug mode if it is True; default is False.
"""
if base_addr < 0 or length < 0:
raise ValueError("Negative offset or negative length.")
euid = os.geteuid()
if euid != 0:
raise EnvironmentError('Root permissions required.')
# Align the base address with the pages
self.virt_base = base_addr & ~(mmap.PAGESIZE - 1)
# Calculate base address offset w.r.t the base address
self.virt_offset = base_addr - self.virt_base
# Storing the base address and length
self.base_addr = base_addr
self.length = length
self.debug = debug
self._debug('MMIO(address, size) = ({0:x}, {1:x} bytes).',
self.base_addr, self.length)
# Open file and mmap
self.mmap_file = os.open(general_const.MMIO_FILE_NAME,
os.O_RDWR | os.O_SYNC)
self.mem = mmap.mmap(self.mmap_file, (self.length + self.virt_offset),
mmap.MAP_SHARED,
mmap.PROT_READ | mmap.PROT_WRITE,
offset=self.virt_base)
self.array = np.frombuffer(self.mem, np.uint32,
length >> 2, self.virt_offset)
def __del__(self):
"""Destructor to ensure mmap file is closed
"""
os.close(self.mmap_file)
def read(self, offset=0, length=4):
"""The method to read data from MMIO.
Parameters
----------
offset : int
The read offset from the MMIO base address.
length : int
The length of the data in bytes.
Returns
-------
list
A list of data read out from MMIO
"""
if not length == 4:
raise ValueError("MMIO currently only supports 4-byte reads.")
if offset < 0 or length < 0:
raise ValueError("Negative offset or negative length.")
idx = offset >> 2
if idx << 2 != offset:
raise MemoryError('Read operation unaligned.')
self._debug('Reading {0} bytes from offset {1:x}',
length, offset)
# Read data out
return int(self.array[idx])
def write(self, offset, data):
"""The method to write data to MMIO.
Parameters
----------
offset : int
The write offset from the MMIO base address.
data : int / bytes
The integer(s) to be written into MMIO.
Returns
-------
None
"""
if offset < 0:
raise ValueError("Negative offset.")
idx = offset >> 2
if idx << 2 != offset:
raise MemoryError('Write operation not aligned.')
if type(data) is int:
self._debug('Writing 4 bytes to offset {0:x}: {1:x}',
offset, data)
self.array[idx] = np.uint32(data)
elif type(data) is bytes:
length = len(data)
num_words = length >> 2
if num_words << 2 != length:
raise MemoryError('Need an integer number of words')
buf = np.frombuffer(data, np.uint32, num_words, 0)
self.array[offset:offset + num_words] = buf
else:
raise ValueError("Data type must be int or bytes.")
最近のコメント