upsilon/gateware/csr2mp.py

180 lines
5.1 KiB
Python
Raw Normal View History

2023-06-21 18:47:52 -04:00
#!/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?
2023-06-26 15:49:20 -04:00
import collections
2023-06-23 18:15:53 -04:00
import argparse
2023-06-21 18:47:52 -04:00
import json
import sys
2023-06-28 18:49:26 -04:00
import mmio_descr
2023-06-23 18:15:53 -04:00
2023-06-26 15:49:20 -04:00
class CSRHandler:
"""
Class that wraps the CSR file and fills in registers with information
from those files.
"""
2023-06-28 18:49:26 -04:00
def __init__(self, csrjson, registers):
2023-06-23 18:15:53 -04:00
"""
2023-06-26 15:49:20 -04:00
Reads in the CSR files.
2023-06-23 18:15:53 -04:00
:param csrjson: Filename of a LiteX "csr.json" file.
2023-06-28 18:49:26 -04:00
:param registers: A list of ``mmio_descr`` ``Descr``s.
2023-06-26 15:49:20 -04:00
:param outf: Output file.
2023-06-23 18:15:53 -04:00
"""
2023-06-21 18:47:52 -04:00
self.registers = registers
self.csrs = json.load(open(csrjson))
2023-06-26 15:49:20 -04:00
def update_reg(self, reg):
"""
Fill in size information from bitwidth json file.
2023-06-21 18:47:52 -04:00
2023-06-26 15:49:20 -04:00
:param reg: The register.
:raises Exception: When the bit width exceeds 64.
2023-06-21 18:47:52 -04:00
"""
2023-06-28 18:49:26 -04:00
regsize = None
b = reg.blen
2023-06-21 18:47:52 -04:00
if b <= 8:
2023-06-28 18:49:26 -04:00
regsize = 8
2023-06-21 18:47:52 -04:00
elif b <= 16:
2023-06-28 18:49:26 -04:00
regsize = 16
2023-06-21 18:47:52 -04:00
elif b <= 32:
2023-06-28 18:49:26 -04:00
regsize = 32
2023-06-21 18:47:52 -04:00
elif b <= 64:
2023-06-28 18:49:26 -04:00
regsize = 64
2023-06-21 18:47:52 -04:00
else:
2023-06-26 15:49:20 -04:00
raise Exception(f"unsupported width {b} in {reg.name}")
2023-06-28 18:49:26 -04:00
setattr(reg, "regsize", regsize)
2023-06-26 15:49:20 -04:00
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
2023-06-21 18:47:52 -04:00
def print(self, *args):
2023-06-26 15:49:20 -04:00
"""
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
2023-06-21 18:47:52 -04:00
2023-06-26 15:49:20 -04:00
def print_file(self):
self.print(self.header())
for r in self.csr.registers:
self.print(self.fun(r, "read"))
2023-06-28 18:49:26 -04:00
if not r.rwperm != "read-only":
2023-06-26 15:49:20 -04:00
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)
2023-06-28 18:49:26 -04:00
if reg.regsize in [8, 16, 32]:
return [f"machine.mem{reg.regsize}[{addr}]"]
return [f"machine.mem32[{addr + 4}]", f"machine.mem32[{addr}]"]
2023-06-26 15:49:20 -04:00
def print_write_register(self, indent, varname, reg, num):
acc = self.get_accessor(reg, num)
2023-06-21 18:47:52 -04:00
if len(acc) == 1:
2023-06-26 15:49:20 -04:00
return f'{indent}{acc[0]} = {varname}\n'
2023-06-21 18:47:52 -04:00
else:
assert len(acc) == 2
2023-06-27 17:50:55 -04:00
# Little Endian. See linux kernel, include/linux/litex.h
return f'{indent}{acc[1]} = {varname} & 0xFFFFFFFF\n' + \
f'{indent}{acc[0]} = {varname} >> 32\n'
2023-06-21 18:47:52 -04:00
2023-06-26 15:49:20 -04:00
def print_read_register(self, indent, varname, reg, num):
acc = self.get_accessor(reg, num)
2023-06-21 18:47:52 -04:00
if len(acc) == 1:
2023-06-26 15:49:20 -04:00
return f'{indent}return {acc[0]}\n'
2023-06-21 18:47:52 -04:00
else:
assert len(acc) == 2
2023-06-26 15:49:20 -04:00
return f'{indent}return {acc[0]} | ({acc[1]} << 32)\n'
2023-06-21 18:47:52 -04:00
2023-06-26 15:49:20 -04:00
def fun(self, reg, optype):
rs = ""
def a(s):
nonlocal rs
rs = rs + s
a(f'def {optype}_{reg.name}(')
2023-06-21 18:47:52 -04:00
printed_argument = False
if optype == 'write':
2023-06-26 15:49:20 -04:00
a('val')
2023-06-21 18:47:52 -04:00
printed_argument = True
2023-06-26 15:49:20 -04:00
pfun = self.print_write_register
else:
pfun = self.print_read_register
2023-06-21 18:47:52 -04:00
2023-06-28 18:49:26 -04:00
if reg.num != 1:
2023-06-21 18:47:52 -04:00
if printed_argument:
2023-06-26 15:49:20 -04:00
a(', ')
a('num')
a('):\n')
2023-06-21 18:47:52 -04:00
2023-06-28 18:49:26 -04:00
if reg.num == 1:
2023-06-26 15:49:20 -04:00
a(pfun('\t', 'val', reg, None))
2023-06-21 18:47:52 -04:00
else:
2023-06-28 18:49:26 -04:00
for i in range(0,reg.num):
2023-06-21 18:47:52 -04:00
if i == 0:
2023-06-26 15:49:20 -04:00
a(f'\tif ')
2023-06-21 18:47:52 -04:00
else:
2023-06-26 15:49:20 -04:00
a(f'\telif ')
a(f'num == {i}:\n')
a(pfun('\t\t', 'val', reg, i))
a(f'\telse:\n')
2023-06-28 18:49:26 -04:00
a(f'\t\traise Exception(regnum)\n')
2023-06-26 15:49:20 -04:00
a('\n')
return rs
def header(self):
2023-06-28 18:49:26 -04:00
return "import machine\n"
2023-06-21 18:47:52 -04:00
if __name__ == "__main__":
2023-06-28 18:49:26 -04:00
csrh = CSRHandler(sys.argv[1], mmio_descr.registers)
for r in mmio_descr.registers:
2023-06-26 15:49:20 -04:00
csrh.update_reg(r)
MicropythonGenerator(csrh, sys.stdout).print_file()