From 5717ef59dfa7a7b99bef15180f9ad2b43d5a6f76 Mon Sep 17 00:00:00 2001 From: Peter McGoron Date: Wed, 21 Jun 2023 18:47:52 -0400 Subject: [PATCH] csr to micropython --- build/Makefile | 1 + gateware/Makefile | 4 +- gateware/csr2mp.py | 145 +++++++++++++++++++++++++++++ gateware/generate_csr_locations.py | 144 ---------------------------- 4 files changed, 148 insertions(+), 146 deletions(-) create mode 100644 gateware/csr2mp.py delete mode 100644 gateware/generate_csr_locations.py diff --git a/build/Makefile b/build/Makefile index 9f3adc9..24626c0 100644 --- a/build/Makefile +++ b/build/Makefile @@ -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 diff --git a/gateware/Makefile b/gateware/Makefile index 255a297..266bac2 100644 --- a/gateware/Makefile +++ b/gateware/Makefile @@ -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 diff --git a/gateware/csr2mp.py b/gateware/csr2mp.py new file mode 100644 index 0000000..cb6d80e --- /dev/null +++ b/gateware/csr2mp.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() diff --git a/gateware/generate_csr_locations.py b/gateware/generate_csr_locations.py deleted file mode 100644 index e740d34..0000000 --- a/gateware/generate_csr_locations.py +++ /dev/null @@ -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() -