diff --git a/.gitignore b/.gitignore index 6ab7f77..ff8fdd2 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ firmware/litex_json2dts_zephyr.py firmware/overlay.config firmware/overlay.cmake firmware/overlay.dts -firmware/pin_io.h +firmware/pin_io.c misc/ software/build/ *.fst diff --git a/doc/programmers_manual.md b/doc/programmers_manual.md index 609e9e7..d027dd7 100644 --- a/doc/programmers_manual.md +++ b/doc/programmers_manual.md @@ -149,6 +149,12 @@ The kernel is `/software/build/zephyr/zephyr.bin` If you make a change to `CMakeLists.txt` or to `prj.conf`, run `make clean` before `make`. +Make can run in parallel using `-j${NUMBER_OF_PROCESSORS}`. Add this to the +`buidl/zephyr/zephyr.bin` in `/software/Makefile` to makeyour builds faster. +Remove this argument when you are attemping to fix compile errors and warnings +(it will make the build output easier to read) but put it back when you fix +them. + # Loading the Software and Firmware ## Network Setup @@ -438,6 +444,12 @@ TODO: Ethernet debugging output. ## Control and Status Registers in Software +CSR read and write functions are generated by `/firmware/generate_csr_locations.py`. +You should not need to directly call `write` and `read` on raw addresses. +If you add a new CSR, add it to the generator script. + +### Implementation Information + CSRs can be used in software by using `litex_write8`, `litex_read16`, etc. In the Zephyr source, look at `soc/riscv/litex-vexriscv/soc.h` for the complete implementation. @@ -449,7 +461,6 @@ Do not directly write to CSR ports without using `litex_writeN` and not careful you will not access the registers correctly and you will crash the software. - # Controlling Computer ## Creole diff --git a/firmware/Makefile b/firmware/Makefile index 5383780..c5f66e6 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -2,11 +2,11 @@ DEVICETREE_GEN_DIR=. -all: rtl_codegen build/digilent_arty/digilent_arty.bit overlay.dts overlay.cmake pin_io.h +all: rtl_codegen build/digilent_arty/digilent_arty.bit overlay.dts overlay.cmake pin_io.c rtl_codegen: cd rtl && make -build/digilent_arty/digilent_arty.bit: rtl_codegen soc.py +build/digilent_arty/digilent_arty.bit: soc.py python3 soc.py clean: rm -rf build csr.json overlay.config overlay.dts pin_io.h @@ -14,5 +14,5 @@ overlay.dts overlay.cmake: csr.json litex_json2dts_zephyr.py # NOTE: Broken in LiteX 2022.4. $(DEVICETREE_GEN_DIR)/litex_json2dts_zephyr.py --dts overlay.dts --config overlay.cmake csr.json -pin_io.h: csr.json generate_csr_locations.py - python3 generate_csr_locations.py > pin_io.h +pin_io.c: csr.json generate_csr_locations.py + python3 generate_csr_locations.py > pin_io.c diff --git a/firmware/generate_csr_locations.py b/firmware/generate_csr_locations.py index 2956b7b..79fc902 100644 --- a/firmware/generate_csr_locations.py +++ b/firmware/generate_csr_locations.py @@ -3,76 +3,142 @@ import json import sys """ -This file takes the csr.json file output by LiteX and extracts all -CSRs that are handled by Upsilon directly. See the output file csr.json -for layout. +This file uses csr.json and csr_bitwidth.json and writes functions +that handle reads and writes to MMIO. """ class CSRGenerator: - def __init__(self, json_file, registers, f): + def __init__(self, csrjson, bitwidthjson, registers, outf, dacmax, adcmax): self.registers = registers - self.j = json.load(open(json_file)) - self.file = f + 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 f'{self.j["csr_registers"][regname]["addr"]}' + 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_array(self, name, num): - if num == 1: - self.print(f'static const uintptr_t {name} = {self.get_reg(name, None)};\n') - else: - self.print(f'static const uintptr_t {name}[{num}] = {{', self.get_reg(name, 0)) - for i in range(1,num): - self.print(',', self.get_reg(name, i)) - self.print('};\n\n') + 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') - def print_registers(self): - for name,num in self.registers: - self.print_array(name, num) - def print_file(self): - self.print(f'''#pragma once -#define ADC_MAX {adc_num} -#define DAC_MAX {dac_num} + 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_registers() + 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 = [ - ("dac_sel", dac_num), - ("dac_finished", dac_num), - ("dac_arm", dac_num), - ("from_dac", dac_num), - ("to_dac", dac_num), - ("wf_arm", dac_num), - ("wf_halt_on_finish", dac_num), - ("wf_finished", dac_num), - ("wf_running", dac_num), - ("wf_time_to_wait", dac_num), - ("wf_refresh_start", dac_num), - ("wf_refresh_finished", dac_num), - ("wf_start_addr", dac_num), + # 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), - ("adc_finished", adc_num), - ("adc_arm", adc_num), - ("from_adc", adc_num), + (True, "adc_finished", adc_num), + (False, "adc_arm", adc_num), + (True, "from_adc", adc_num), - ("adc_sel_0", 1), - ("cl_in_loop", 1), - ("cl_cmd", 1), - ("cl_word_in", 1), - ("cl_word_out", 1), - ("cl_start_cmd", 1), - ("cl_finish_cmd", 1), + (False, "adc_sel_0", 1), + (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", registers, sys.stdout).print_file() + CSRGenerator("csr.json", "csr_bitwidth.json", registers, sys.stdout, dac_num, adc_num).print_file() diff --git a/firmware/soc.py b/firmware/soc.py index b6e52c5..7611368 100644 --- a/firmware/soc.py +++ b/firmware/soc.py @@ -128,10 +128,9 @@ class Base(Module, AutoCSR): self.kwargs["i_adc_sdo"] = platform.request("adc_sdo") self.kwargs["o_adc_sck"] = platform.request("adc_sck") - with open("io_widths.h", mode='w') as f: - print('#pragma once', file=f) - for key in self.csrdict: - print(f'#define {key.upper()}_LEN {self.csrdict[key]}', file=f) + with open("csr_bitwidth.json", mode='w') as f: + import json + json.dump(self.csrdict, f) self.specials += Instance("base", **self.kwargs) diff --git a/software/Makefile b/software/Makefile index cd854c5..5b878e7 100644 --- a/software/Makefile +++ b/software/Makefile @@ -3,9 +3,10 @@ unexport CFLAGS unexport CPPFLAGS unexport LDFLAGS +# TODO: Number of processors build/zephyr/zephyr.bin: build/Makefile cd build && make -j7 -build/Makefile: ../creole/creole.c src/*.c ../firmware/overlay.dts ../firmware/pin_io.h prj.conf CMakeLists.txt +build/Makefile: ../creole/creole.c src/*.c ../firmware/overlay.dts ../firmware/pin_io.c prj.conf CMakeLists.txt mkdir -p build cd build && cmake .. clean: diff --git a/software/src/access.c b/software/src/access.c index 5a65974..c2d1723 100644 --- a/software/src/access.c +++ b/software/src/access.c @@ -14,12 +14,12 @@ #include #include "upsilon.h" #include "access.h" -#include "pin_io.h" #include "control_loop_cmds.h" LOG_MODULE_REGISTER(access); +#include "pin_io.c" -/* The values from converters are not aligned to an 8-bit byte. +/* The values from converters are not aligned to 32 bits. * These values are still in twos compliment and have to be * manually sign-extended. */ @@ -61,8 +61,8 @@ dac_release(int dac) return -EFAULT; if (dac_locked[dac] == 1) { - *dac_arm[dac] = 0; - while (!*dac_finished[dac]); + write_dac_arm(0, dac); + while (!read_dac_finished(dac)); } int e = k_mutex_unlock(dac_mutex + dac); @@ -81,20 +81,20 @@ dac_read_write(int dac, creole_word send, k_timeout_t timeout, return e; dac_switch(dac, DAC_SPI_PORT, K_NO_WAIT); - litex_write32(send, to_dac[dac]); - litex_write8(1, dac_arm[dac]); + write_to_dac(send, dac); + write_dac_arm(1, dac); - /* Recursive locks should busy wait. */ + /* Non-recursive locks should busy wait. */ /* 10ns * (2 * 10 cycles per half DAC cycle) * 24 bits */ if (dac_locked[dac] > 1) k_sleep(K_NSEC(10*2*10*24)); - while (!litex_read8(dac_finished[dac])); + while (!read_dac_finished(dac)); if (recv) - *recv = sign_extend(litex_read32(from_dac[dac]), 20); - litex_write8(0, dac_arm[dac]); + *recv = sign_extend(read_from_dac(dac), 20); + write_dac_arm(0, dac); dac_release(dac); return 0; @@ -107,7 +107,7 @@ dac_switch(int dac, int setting, k_timeout_t timeout) if (e != 0) return e; - litex_write8(setting, dac_sel[dac]); + write_dac_sel(setting, dac); dac_release(dac); return 0; @@ -140,8 +140,8 @@ adc_release(int adc) return -EFAULT; if (adc_locked[adc] == 1) { - *adc_arm[adc] = 0; - while (!*adc_finished[adc]); + write_adc_arm(0, adc); + while (!read_adc_finished(adc)); } int e = k_mutex_unlock(adc_mutex + adc); @@ -164,7 +164,7 @@ adc_switch(int adc, int setting, k_timeout_t timeout) if (e != 0) return e; - litex_write8(setting, adc_sel_0); + write_adc_sel_0(setting); adc_release(adc); return 0; @@ -178,15 +178,15 @@ adc_read(int adc, k_timeout_t timeout, creole_word *wrd) return e; adc_switch(adc, ADC_SPI_PORT, K_NO_WAIT); - litex_write8(1, adc_arm[adc]); + write_adc_arm(1, adc); /* Recursive locks should busy wait. */ if (adc_locked[adc] > 1) k_sleep(K_NSEC(550 + 24*2*10*10)); - while (!litex_read8(adc_finished[adc])); + while (!read_adc_finished(adc)); - *wrd = sign_extend(litex_read32(from_adc[adc]), 20); - litex_write8(0, adc_arm[adc]); + *wrd = sign_extend(read_from_adc(adc), 20); + write_adc_arm(0, adc); adc_release(adc); return 0; @@ -231,11 +231,11 @@ cloop_read(int code, uint32_t *high_reg, uint32_t *low_reg, if (cloop_take(timeout) != 0) return 0; - litex_write8(code, cl_cmd); - litex_write8(1, cl_start_cmd); - while (!litex_read8(cl_finish_cmd)); - v = litex_read64(cl_word_out); - litex_write8(0, cl_start_cmd); + write_cl_cmd(code); + write_cl_start_cmd(1); + while (!read_cl_finish_cmd()); + v = read_cl_word_out(); + write_cl_start_cmd(0); *high_reg = v >> 32; *low_reg = v & 0xFFFFFFFF; @@ -251,11 +251,11 @@ cloop_write(int code, uint32_t high_val, uint32_t low_val, if (cloop_take(timeout) != 0) return 0; - litex_write8(code, cl_cmd); - litex_write64((uint64_t) high_val << 32 | low_val, cl_word_in); - litex_write8(1, cl_start_cmd); - while (!litex_read8(cl_finish_cmd)); - litex_write8(0, cl_start_cmd); + write_cl_cmd(code); + write_cl_word_in((uint64_t) high_val << 32 | low_val); + write_cl_start_cmd(1); + while (!read_cl_finish_cmd()); + write_cl_start_cmd(0); cloop_release(); return 1; @@ -284,11 +284,9 @@ waveform_take(int waveform, k_timeout_t timeout) static void waveform_disarm_wait(int wf) { - litex_write8(0, wf_arm[wf]); - if (*wf_running[wf]) { - // k_sleep(K_NSEC(10* *wf_time_to_wait[wf])); - while (litex_read8(wf_running[wf])); - } + write_wf_arm(0, wf); + /* TODO: add wait */ + while (read_wf_running(wf)); } int @@ -328,10 +326,10 @@ waveform_load(uint32_t buf[MAX_WL_SIZE], int slot, k_timeout_t timeout) if (waveform_take(slot, timeout) != 0) return 0; - litex_write32((uint32_t) buf, wf_start_addr[slot]); - litex_write8(1, wf_refresh_start[slot]); - while (!litex_read8(wf_refresh_finished[slot])); - litex_write8(0, wf_refresh_start[slot]); + write_wf_start_addr((uint32_t) buf, slot); + write_wf_refresh_start(1, slot); + while (!read_wf_refresh_finished(slot)); + write_wf_refresh_start(0, slot); waveform_release(slot); return 1; @@ -340,8 +338,8 @@ waveform_load(uint32_t buf[MAX_WL_SIZE], int slot, k_timeout_t timeout) int waveform_halt_until_finished(int slot) { - litex_write8(1, wf_halt_on_finish[slot]); - while (!litex_read(wf_finished[slot])); + write_wf_halt_on_finish(1, slot); + while (!read_wf_finished(slot)); return 1; } @@ -357,9 +355,9 @@ waveform_arm(int slot, bool halt_on_finish, uint32_t wait, k_timeout_t timeout) dac_switch(slot, DAC_WF_PORT, K_NO_WAIT); - litex_write8(halt_on_finish, wf_halt_on_finish[slot]); - litex_write16(wait, wf_time_to_wait[slot]); - litex_write8(1, wf_arm[slot]); + write_wf_halt_on_finish(halt_on_finish, slot); + write_wf_time_to_wait(wait, slot); + write_wf_arm(1, slot); return 1; } @@ -403,9 +401,6 @@ access_release_thread(void) } } - for (int i = 0; i < DAC_MAX; i++) { - } - for (int i = 0; i < ADC_MAX; i++) { while (adc_release(i) == 0) adc_locked[i]--;