180 lines
5.1 KiB
Python
180 lines
5.1 KiB
Python
#!/usr/bin/python3
|
|
# Copyright 2023 (C) Peter McGoron
|
|
#
|
|
# This file is a part of Upsilon, a free and open source software project.
|
|
# For license terms, refer to the files in `doc/copying` in the Upsilon
|
|
# source distribution.
|
|
#######################################################################
|
|
#
|
|
# This file generates a Micropython module "mmio" with functions that
|
|
# do raw reads and writes to MMIO registers.
|
|
#
|
|
# TODO: Devicetree?
|
|
|
|
import collections
|
|
import argparse
|
|
import json
|
|
import sys
|
|
import mmio_descr
|
|
|
|
class CSRHandler:
|
|
"""
|
|
Class that wraps the CSR file and fills in registers with information
|
|
from those files.
|
|
"""
|
|
def __init__(self, csrjson, registers):
|
|
"""
|
|
Reads in the CSR files.
|
|
|
|
:param csrjson: Filename of a LiteX "csr.json" file.
|
|
:param registers: A list of ``mmio_descr`` ``Descr``s.
|
|
:param outf: Output file.
|
|
"""
|
|
self.registers = registers
|
|
self.csrs = json.load(open(csrjson))
|
|
|
|
def update_reg(self, reg):
|
|
"""
|
|
Fill in size information from bitwidth json file.
|
|
|
|
:param reg: The register.
|
|
:raises Exception: When the bit width exceeds 64.
|
|
"""
|
|
regsize = None
|
|
b = reg.blen
|
|
if b <= 8:
|
|
regsize = 8
|
|
elif b <= 16:
|
|
regsize = 16
|
|
elif b <= 32:
|
|
regsize = 32
|
|
elif b <= 64:
|
|
regsize = 64
|
|
else:
|
|
raise Exception(f"unsupported width {b} in {reg.name}")
|
|
setattr(reg, "regsize", regsize)
|
|
|
|
def get_reg_addr(self, reg, num=None):
|
|
"""
|
|
Get address of register.
|
|
|
|
:param reg: The register.
|
|
:param num: Select which register number. Registers without
|
|
numerical suffixes require ``None``.
|
|
:return: The address.
|
|
"""
|
|
if num is None:
|
|
regname = f"base_{reg.name}"
|
|
else:
|
|
regname = f"base_{reg.name}_{num}"
|
|
return self.csrs["csr_registers"][regname]["addr"]
|
|
|
|
class InterfaceGenerator:
|
|
"""
|
|
Interface for file generation. Implement the unimplemented functions
|
|
to generate a CSR interface for another language.
|
|
"""
|
|
|
|
def __init__(self, csr, outf):
|
|
"""
|
|
:param CSRHandler csr:
|
|
:param FileIO outf:
|
|
"""
|
|
self.outf = outf
|
|
self.csr = csr
|
|
|
|
def print(self, *args):
|
|
"""
|
|
Print to the file specified in the initializer and without
|
|
newlines.
|
|
"""
|
|
print(*args, end='', file=self.outf)
|
|
|
|
def fun(self, reg, optype):
|
|
""" Print function for reads/writes to register. """
|
|
pass
|
|
def header(self):
|
|
""" Print header of file. """
|
|
pass
|
|
|
|
def print_file(self):
|
|
self.print(self.header())
|
|
for r in self.csr.registers:
|
|
self.print(self.fun(r, "read"))
|
|
if not r.rwperm != "read-only":
|
|
self.print(self.fun(r, "write"))
|
|
|
|
class MicropythonGenerator(InterfaceGenerator):
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
def get_accessor(self, reg, num):
|
|
addr = self.csr.get_reg_addr(reg, num)
|
|
if reg.regsize in [8, 16, 32]:
|
|
return [f"machine.mem{reg.regsize}[{addr}]"]
|
|
return [f"machine.mem32[{addr + 4}]", f"machine.mem32[{addr}]"]
|
|
|
|
def print_write_register(self, indent, varname, reg, num):
|
|
acc = self.get_accessor(reg, num)
|
|
if len(acc) == 1:
|
|
return f'{indent}{acc[0]} = {varname}\n'
|
|
else:
|
|
assert len(acc) == 2
|
|
# Little Endian. See linux kernel, include/linux/litex.h
|
|
return f'{indent}{acc[1]} = {varname} & 0xFFFFFFFF\n' + \
|
|
f'{indent}{acc[0]} = {varname} >> 32\n'
|
|
|
|
def print_read_register(self, indent, varname, reg, num):
|
|
acc = self.get_accessor(reg, num)
|
|
if len(acc) == 1:
|
|
return f'{indent}return {acc[0]}\n'
|
|
else:
|
|
assert len(acc) == 2
|
|
return f'{indent}return {acc[0]} | ({acc[1]} << 32)\n'
|
|
|
|
def fun(self, reg, optype):
|
|
rs = ""
|
|
def a(s):
|
|
nonlocal rs
|
|
rs = rs + s
|
|
a(f'def {optype}_{reg.name}(')
|
|
|
|
printed_argument = False
|
|
if optype == 'write':
|
|
a('val')
|
|
printed_argument = True
|
|
pfun = self.print_write_register
|
|
else:
|
|
pfun = self.print_read_register
|
|
|
|
if reg.num != 1:
|
|
if printed_argument:
|
|
a(', ')
|
|
a('num')
|
|
a('):\n')
|
|
|
|
if reg.num == 1:
|
|
a(pfun('\t', 'val', reg, None))
|
|
else:
|
|
for i in range(0,reg.num):
|
|
if i == 0:
|
|
a(f'\tif ')
|
|
else:
|
|
a(f'\telif ')
|
|
a(f'num == {i}:\n')
|
|
a(pfun('\t\t', 'val', reg, i))
|
|
a(f'\telse:\n')
|
|
a(f'\t\traise Exception(regnum)\n')
|
|
a('\n')
|
|
|
|
return rs
|
|
|
|
def header(self):
|
|
return "import machine\n"
|
|
|
|
if __name__ == "__main__":
|
|
csrh = CSRHandler(sys.argv[1], mmio_descr.registers)
|
|
for r in mmio_descr.registers:
|
|
csrh.update_reg(r)
|
|
MicropythonGenerator(csrh, sys.stdout).print_file()
|