csr to micropython
This commit is contained in:
parent
d76c1f8ad1
commit
5717ef59df
|
@ -38,6 +38,7 @@ hardware-get:
|
|||
docker cp upsilon-hardware:/home/user/upsilon/gateware/build/digilent_arty/gateware/digilent_arty.bit ../boot/
|
||||
docker cp upsilon-hardware:/home/user/upsilon/gateware/arty.dtb ../boot/
|
||||
docker cp upsilon-hardware:/home/user/upsilon/gateware/csr.json ../boot/
|
||||
docker cp upsilon-hardware:/home/user/upsilon/gateware/csr_bitwidth.json ../boot/
|
||||
hardware-clean:
|
||||
-docker container stop upsilon-hardware
|
||||
-docker container rm upsilon-hardware
|
||||
|
|
|
@ -25,5 +25,5 @@ arty.dts: csr.json
|
|||
arty.dtb: arty.dts
|
||||
dtc -O dtb -o arty.dtb arty.dts
|
||||
|
||||
pin_io.c: csr.json generate_csr_locations.py
|
||||
python3 generate_csr_locations.py > pin_io.c
|
||||
mmio.py: csr2mp.py csr.json csr_bitwidth.json
|
||||
python3 csr2mp.py > mmio.py
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
#!/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 json
|
||||
import sys
|
||||
|
||||
class CSRGenerator:
|
||||
def __init__(self, csrjson, bitwidthjson, registers, outf):
|
||||
self.registers = registers
|
||||
self.csrs = json.load(open(csrjson))
|
||||
self.bws = json.load(open(bitwidthjson))
|
||||
self.file = outf
|
||||
|
||||
def get_reg(self, name, num=None):
|
||||
""" Get the base address of the register. """
|
||||
if num is None:
|
||||
regname = f"base_{name}"
|
||||
else:
|
||||
regname = f"base_{name}_{num}"
|
||||
return self.csrs["csr_registers"][regname]["addr"]
|
||||
|
||||
def get_accessor(self, name, num=None):
|
||||
""" Return a list of Micropython machine.mem accesses that can
|
||||
be used to read/write to a MMIO register.
|
||||
|
||||
Since the Micropython API only supports accesses up to the
|
||||
natural word size of the processor, multiple accesses must be made
|
||||
for 64 bit registers.
|
||||
"""
|
||||
b = self.bws[name]
|
||||
if b <= 8:
|
||||
return [f'machine.mem8[{self.get_reg(name,num)}]']
|
||||
elif b <= 16:
|
||||
return [f'machine.mem16[{self.get_reg(name,num)}]']
|
||||
elif b <= 32:
|
||||
return [f'machine.mem32[{self.get_reg(name,num)}]']
|
||||
elif b <= 64:
|
||||
return [f'machine.mem32[{self.get_reg(name,num)}]',
|
||||
f'machine.mem32[{self.get_reg(name,num) + 4}]']
|
||||
else:
|
||||
raise Exception('unsupported width', b)
|
||||
|
||||
def print(self, *args):
|
||||
print(*args, end='', file=self.file)
|
||||
|
||||
def print_write_register(self, indent, varname, name, num):
|
||||
acc = self.get_accessor(name,num)
|
||||
if len(acc) == 1:
|
||||
self.print(f'{indent}{acc[0]} = {varname}\n')
|
||||
else:
|
||||
assert len(acc) == 2
|
||||
self.print(f'{indent}{acc[0]} = {varname} & 0xFFFFFFFF\n')
|
||||
self.print(f'{indent}{acc[1]} = {varname} >> 32\n')
|
||||
|
||||
def print_read_register(self, indent, varname, name, num):
|
||||
acc = self.get_accessor(name,num)
|
||||
if len(acc) == 1:
|
||||
self.print(f'{indent}return {acc[0]}\n')
|
||||
else:
|
||||
assert len(acc) == 2
|
||||
self.print(f'{indent}return {acc[0]} | ({acc[1]} << 32)\n')
|
||||
|
||||
def print_fun(self, optype, name, regnum, pfun):
|
||||
"""Print out a read/write function for an MMIO register.
|
||||
* {optype} is set to "read" or "write" (the string).
|
||||
* {name} is set to the name of the MMIO register, without number suffix.
|
||||
* {regnum} is set to the amount of that type oF MMIO register exists.
|
||||
* {pfun} is set to {self.print_write_register} or {self.print_read_register}
|
||||
"""
|
||||
self.print(f'def {optype}_{name}(')
|
||||
|
||||
printed_argument = False
|
||||
if optype == 'write':
|
||||
self.print('val')
|
||||
printed_argument = True
|
||||
|
||||
if regnum != 1:
|
||||
if printed_argument:
|
||||
self.print(', ')
|
||||
self.print('num')
|
||||
self.print('):\n')
|
||||
|
||||
if regnum == 1:
|
||||
pfun('\t', 'val', name, None)
|
||||
else:
|
||||
for i in range(0,regnum):
|
||||
if i == 0:
|
||||
self.print(f'\tif ')
|
||||
else:
|
||||
self.print(f'\telif ')
|
||||
self.print(f'num == {i}:\n')
|
||||
pfun('\t\t', 'val', name, i)
|
||||
self.print(f'\telse:\n')
|
||||
self.print(f'\t\traise Exception("invalid {name}", regnum)\n')
|
||||
self.print('\n')
|
||||
|
||||
def print_file(self):
|
||||
self.print('import machine\n')
|
||||
for reg in self.registers:
|
||||
self.print_fun('read', reg['name'], reg['total'], self.print_read_register)
|
||||
if not reg['read_only']:
|
||||
self.print_fun('write', reg['name'], reg['total'], self.print_write_register)
|
||||
|
||||
if __name__ == "__main__":
|
||||
dac_num = 8
|
||||
adc_num = 8
|
||||
|
||||
registers = [
|
||||
{"read_only": False, "name": "dac_sel", "total": dac_num},
|
||||
{"read_only": True, "name": "dac_finished", "total": dac_num},
|
||||
{"read_only": False, "name": "dac_arm", "total": dac_num},
|
||||
{"read_only": True, "name": "from_dac", "total": dac_num},
|
||||
{"read_only": False, "name": "to_dac", "total": dac_num},
|
||||
# {"read_only": False, "name": "wf_arm", "total": dac_num},
|
||||
# {"read_only": False, "name": "wf_halt_on_finish", "total": dac_num},
|
||||
# {"read_only": True, "name": "wf_finished", "total": dac_num},
|
||||
# {"read_only": True, "name": "wf_running", "total": dac_num},
|
||||
# {"read_only": False, "name": "wf_time_to_wait", "total": dac_num},
|
||||
# {"read_only": False, "name": "wf_refresh_start", "total": dac_num},
|
||||
# {"read_only": True, "name": "wf_refresh_finished", "total": dac_num},
|
||||
# {"read_only": False, "name": "wf_start_addr", "total": dac_num},
|
||||
|
||||
{"read_only": True, "name": "adc_finished", "total": adc_num},
|
||||
{"read_only": False, "name": "adc_arm", "total": adc_num},
|
||||
{"read_only": True, "name": "from_adc", "total": adc_num},
|
||||
|
||||
{"read_only": False, "name": "adc_sel", "total": adc_num},
|
||||
{"read_only": True, "name": "cl_in_loop", "total": 1},
|
||||
{"read_only": False, "name": "cl_cmd", "total": 1},
|
||||
{"read_only": False, "name": "cl_word_in", "total": 1},
|
||||
{"read_only": False, "name": "cl_word_out", "total": 1},
|
||||
{"read_only": False, "name": "cl_start_cmd", "total": 1},
|
||||
{"read_only": True, "name": "cl_finish_cmd", "total": 1},
|
||||
]
|
||||
CSRGenerator("csr.json", "csr_bitwidth.json", registers, sys.stdout).print_file()
|
|
@ -1,144 +0,0 @@
|
|||
#!/usr/bin/python3
|
||||
import json
|
||||
import sys
|
||||
|
||||
"""
|
||||
This file uses csr.json and csr_bitwidth.json and writes functions
|
||||
that handle reads and writes to MMIO.
|
||||
"""
|
||||
|
||||
class CSRGenerator:
|
||||
def __init__(self, csrjson, bitwidthjson, registers, outf, dacmax, adcmax):
|
||||
self.registers = registers
|
||||
self.csrs = json.load(open(csrjson))
|
||||
self.bws = json.load(open(bitwidthjson))
|
||||
self.file = outf
|
||||
self.dacmax = dacmax
|
||||
self.adcmax = adcmax
|
||||
|
||||
def get_reg(self, name, num):
|
||||
if num is None:
|
||||
regname = f"base_{name}"
|
||||
else:
|
||||
regname = f"base_{name}_{num}"
|
||||
return self.csrs["csr_registers"][regname]["addr"]
|
||||
|
||||
def get_bitwidth_type(self, name):
|
||||
b = self.bws[name]
|
||||
if b <= 8:
|
||||
return 8
|
||||
elif b <= 16:
|
||||
return 16
|
||||
elif b <= 32:
|
||||
return 32
|
||||
elif b <= 64:
|
||||
return 64
|
||||
else:
|
||||
raise Exception('unsupported width', b)
|
||||
|
||||
def print(self, *args):
|
||||
print(*args, end='', file=self.file)
|
||||
|
||||
def print_write_fun(self, name, regnum):
|
||||
typ = self.get_bitwidth_type(name)
|
||||
self.print('static inline void\n')
|
||||
self.print(f'write_{name}(uint{typ}_t v')
|
||||
|
||||
if regnum != 1:
|
||||
self.print(f', int num')
|
||||
self.print(')\n{\n')
|
||||
|
||||
if regnum != 1:
|
||||
self.print('\t', f'static const uintptr_t loc[{regnum}]', '= {\n')
|
||||
self.print('\t\t', self.get_reg(name,0), '\n')
|
||||
for i in range(1,regnum):
|
||||
self.print('\t\t,', self.get_reg(name, i), '\n')
|
||||
self.print('\t};\n')
|
||||
self.print('''
|
||||
if (num < 0 || num >= ARRAY_SIZE(loc)) {
|
||||
LOG_ERR("invalid location %d", num);
|
||||
k_fatal_halt(K_ERR_KERNEL_OOPS);
|
||||
}
|
||||
''')
|
||||
self.print('\t', f'litex_write{typ}(v, {"loc[num]" if regnum != 1 else self.get_reg(name, None)});', '\n}\n\n')
|
||||
|
||||
def print_read_fun(self, name, regnum):
|
||||
typ = self.get_bitwidth_type(name)
|
||||
self.print(f'static inline uint{typ}_t\nread_{name}')
|
||||
|
||||
if regnum != 1:
|
||||
self.print(f'(int num)', '\n{\n')
|
||||
else:
|
||||
self.print('(void)\n{\n')
|
||||
|
||||
if regnum != 1:
|
||||
self.print('\t', f'static const uintptr_t loc[{regnum}]', '= {\n')
|
||||
self.print('\t\t', self.get_reg(name,0), '\n')
|
||||
for i in range(1,regnum):
|
||||
self.print('\t\t,', self.get_reg(name, i), '\n')
|
||||
self.print('\t};\n')
|
||||
self.print('''
|
||||
if (num < 0 || num >= ARRAY_SIZE(loc)) {
|
||||
LOG_ERR("invalid location %d", num);
|
||||
k_fatal_halt(K_ERR_KERNEL_OOPS);
|
||||
}
|
||||
''')
|
||||
self.print('\t', f'return litex_read{typ}({"loc[num]" if regnum != 1 else self.get_reg(name, None)}', ');\n}\n\n')
|
||||
|
||||
def print_file(self):
|
||||
self.print('''
|
||||
#pragma once
|
||||
|
||||
static inline void litex_write64(uint64_t value, unsigned long addr)
|
||||
{
|
||||
#if CONFIG_LITEX_CSR_DATA_WIDTH >= 32
|
||||
sys_write32(value >> 32, addr);
|
||||
sys_write32(value, addr + 0x4);
|
||||
#else
|
||||
# error Unsupported CSR data width
|
||||
#endif
|
||||
}
|
||||
|
||||
''')
|
||||
self.print('#define DAC_MAX', self.dacmax, '\n')
|
||||
self.print('#define ADC_MAX', self.adcmax, '\n')
|
||||
for reg in self.registers:
|
||||
self.print_read_fun(reg[1],reg[2])
|
||||
if not reg[0]: #read only
|
||||
self.print_write_fun(reg[1],reg[2])
|
||||
|
||||
if __name__ == "__main__":
|
||||
dac_num = 8
|
||||
adc_num = 8
|
||||
|
||||
registers = [
|
||||
# Read-only, name, number
|
||||
(False, "dac_sel", dac_num),
|
||||
(True, "dac_finished", dac_num),
|
||||
(False, "dac_arm", dac_num),
|
||||
(True, "from_dac", dac_num),
|
||||
(False, "to_dac", dac_num),
|
||||
# (False, "wf_arm", dac_num),
|
||||
# (False, "wf_halt_on_finish", dac_num),
|
||||
# (True, "wf_finished", dac_num),
|
||||
# (True, "wf_running", dac_num),
|
||||
# (False, "wf_time_to_wait", dac_num),
|
||||
# (False, "wf_refresh_start", dac_num),
|
||||
# (True, "wf_refresh_finished", dac_num),
|
||||
# (False, "wf_start_addr", dac_num),
|
||||
|
||||
(True, "adc_finished", adc_num),
|
||||
(False, "adc_arm", adc_num),
|
||||
(True, "from_adc", adc_num),
|
||||
|
||||
(False, "adc_sel", adc_num),
|
||||
(True, "cl_in_loop", 1),
|
||||
(False, "cl_cmd", 1),
|
||||
(False, "cl_word_in", 1),
|
||||
(False, "cl_word_out", 1),
|
||||
(False, "cl_start_cmd", 1),
|
||||
(True, "cl_finish_cmd", 1),
|
||||
]
|
||||
|
||||
CSRGenerator("csr.json", "csr_bitwidth.json", registers, sys.stdout, dac_num, adc_num).print_file()
|
||||
|
Loading…
Reference in New Issue