From 8b8e14bc7fdf4a58bd8ab2c5b55181e6c338b32a Mon Sep 17 00:00:00 2001 From: Peter McGoron Date: Tue, 27 Jun 2023 17:50:55 -0400 Subject: [PATCH] z output reading --- client/control_loop_test.py | 26 +++++ client/util.py | 107 ++++++++++++++++++++ gateware/Makefile | 2 +- gateware/csr2mp.py | 7 +- gateware/rtl/base/base.v.m4 | 11 +- gateware/rtl/control_loop/control_loop.v.m4 | 5 +- gateware/soc.py | 3 +- linux/control_loop_test.py | 31 ++++++ linux/noise_test.py | 2 + 9 files changed, 182 insertions(+), 12 deletions(-) create mode 100644 client/control_loop_test.py create mode 100644 client/util.py create mode 100644 linux/control_loop_test.py diff --git a/client/control_loop_test.py b/client/control_loop_test.py new file mode 100644 index 0000000..4b7c2cd --- /dev/null +++ b/client/control_loop_test.py @@ -0,0 +1,26 @@ +""" +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. +""" + +import numpy as np +import matplotlib.pyplot as plt +import pandas as pd +import sys +import signal +from util import * + +Pval = string_to_fixed_point('0.0006', 43) +Ival = string_to_fixed_point('0.01', 43) + +out = connect_execute("control_loop_test.py", Pval, Ival, 10000, 100) + +################ +# Script Handler +################ + +for line in out.stdout: + print(line) diff --git a/client/util.py b/client/util.py new file mode 100644 index 0000000..81d1834 --- /dev/null +++ b/client/util.py @@ -0,0 +1,107 @@ +""" +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. +""" +from math import log10, floor +from decimal import * + +def sign_extend(value, bits): + """ + Interpret ``value`` as a twos-complement integer of ``bits`` length. + + :param value: Twos-complement integer with finite bit width. + :param bits: Bit length of ``value``. + :return: ``value`` converted to a Python integer. + """ + + # Check the sign bit of the integer. + is_signed = (value >> (bits - 1)) & 1 == 1 + # If not signed, just return the integer. + if not is_signed: + return value + # Otherwise, + # 1. Do an explicit twos-complement negation + # 2. Mask all the non-sign bits + # This returns the positive value as a standard Python integer. + # Then the function negates the positive integer to get the negative + # one back. + return -((~value + 1) & ((1 << (bits - 1)) - 1)) + +def connect_execute(f, *arg): + from pssh.clients import SSHClient # require parallel-ssh + + print('connecting') + client = SSHClient('192.168.1.50', user='root', pkey='~/.ssh/upsilon_key') + # Upload the script. + print('connected') + client.scp_send(f'../linux/{f}', '/root/') + # Run the script. + args = f'micropython {f} {" ".join([str(s) for s in arg])}' + print(f"running {args}") + return client.run_command(args) + +# Functions for converting to and from fixed point in Python. + +def string_to_fixed_point(s, fracnum): + l = s.split('.') + if len(l) == 1: + return int(s) << fracnum + elif len(l) != 2: + return None + + dec = 10 + frac = 0 + + frac_decimal = Decimal(f'0.{l[1]}') + # get the smallest power of ten higher then frac_decimal + frac = 0 + + # Example: + # 0.4567 = 0.abcdefgh... + # where abcdefgh are binary digits. + # multiply both sides by two: + # 0.9134 = a.bcdefgh ... + # therefore a = 0. Then remove the most significant digit. + # Then multiply by 2 again. Then + # 1.8268 = b.cdefgh ... + # therefore b = 1. Then take 8268, and so on. + for i in range(0,fracnum): + frac_decimal = frac_decimal * 2 + div = floor(frac_decimal) + + frac = div | (frac << 1) + frac_decimal = frac_decimal - div + + whole = int(l[0]) + if whole < 0: + return -((-whole) << fracnum | frac) + else: + return whole << fracnum | frac + +def fixed_point_to_string(fxp, fracnum): + whole = str(fxp >> fracnum) + mask = (1 << fracnum) - 1 + fracbit = fxp & mask + n = 1 + frac = "" + + if fracbit == 0: + return whole + + # The same method can be applied backwards. + # 0.1110101 = 0.abcdefgh ... + # where abcdefgh... are decimal digits. Then multiply by 10 to + # get + # 1001.0010010 = a.bcdefgh ... + # therefore a = 0b1001 = 9. Then use a bitmask to get + # 0.0010010 = 0.bcdefgh ... + # etc. + + for i in range(0, fracnum): + fracbit = fracbit * 10 + frac = frac + str(fracbit >> fracnum) + fracbit = fracbit & mask + return whole + "." + frac diff --git a/gateware/Makefile b/gateware/Makefile index 1c3f32a..fc91f34 100644 --- a/gateware/Makefile +++ b/gateware/Makefile @@ -26,4 +26,4 @@ arty.dtb: arty.dts dtc -O dtb -o arty.dtb arty.dts mmio.py: csr2mp.py csr.json csr_bitwidth.json - python3 csr2mp.py > mmio.py + python3 csr2mp.py csr.json csr_bitwidth.json > mmio.py diff --git a/gateware/csr2mp.py b/gateware/csr2mp.py index e3d4997..9f9e86e 100644 --- a/gateware/csr2mp.py +++ b/gateware/csr2mp.py @@ -154,8 +154,9 @@ class MicropythonGenerator(InterfaceGenerator): return f'{indent}{acc[0]} = {varname}\n' else: assert len(acc) == 2 - return f'{indent}{acc[0]} = {varname} & 0xFFFFFFFF\n' + \ - f'{indent}{acc[1]} = {varname} >> 32\n' + # 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) @@ -231,8 +232,10 @@ if __name__ == "__main__": MMIORegister("cl_in_loop", read_only=True), MMIORegister("cl_cmd"), MMIORegister("cl_word_in"), + MMIORegister("cl_word_out", read_only=True), MMIORegister("cl_start_cmd"), MMIORegister("cl_finish_cmd", read_only=True), + MMIORegister("cl_z_report", read_only=True), ] csrh = CSRHandler(sys.argv[1], sys.argv[2], registers) for r in registers: diff --git a/gateware/rtl/base/base.v.m4 b/gateware/rtl/base/base.v.m4 index 26f4f45..e46305b 100644 --- a/gateware/rtl/base/base.v.m4 +++ b/gateware/rtl/base/base.v.m4 @@ -296,9 +296,9 @@ m4_define(CL_DATA_WID, CL_CONSTS_WID) input [CL_DATA_WID-1:0] cl_word_in, output reg [CL_DATA_WID-1:0] cl_word_out, input cl_start_cmd, - output cl_finish_cmd + output cl_finish_cmd, - ,output reg test_clock + output [DAC_DATA_WID-1:0] cl_z_report ); assign set_low = 0; @@ -315,8 +315,6 @@ m4_dac_switch(DAC_PORTS, 5); m4_dac_switch(DAC_PORTS, 6); m4_dac_switch(DAC_PORTS, 7); -initial test_clock <= 0; - `define MAKE_TEST_CLOCK `ifdef MAKE_TEST_CLOCK reg [8-1:0] counter = 0; @@ -333,8 +331,6 @@ always @ (posedge clk) begin end end end -`else -assign test_clock = 0; `endif m4_adc_switch(ADC_TYPE1_WID, 0, ADC_PORTS_CONTROL_LOOP); @@ -385,7 +381,8 @@ control_loop #( .word_in(cl_word_in), .word_out(cl_word_out), .start_cmd(cl_start_cmd), - .finish_cmd(cl_finish_cmd) + .finish_cmd(cl_finish_cmd), + .z_report(cl_z_report) ); endmodule diff --git a/gateware/rtl/control_loop/control_loop.v.m4 b/gateware/rtl/control_loop/control_loop.v.m4 index 841e038..fba092d 100644 --- a/gateware/rtl/control_loop/control_loop.v.m4 +++ b/gateware/rtl/control_loop/control_loop.v.m4 @@ -63,7 +63,9 @@ m4_define(M4_E_WID, (DAC_DATA_WID + 1)) input [M4_DATA_WID-1:0] word_in, output reg [M4_DATA_WID-1:0] word_out, input start_cmd, - output reg finish_cmd + output reg finish_cmd, + + output [DAC_DATA_WID-1:0] z_report ); /************ ADC and DAC modules ***************/ @@ -156,6 +158,7 @@ reg [DELAY_WID-1:0] dely_buffer = 0; reg running = 0; reg signed [DAC_DATA_WID-1:0] stored_dac_val = 0; +assign z_report = stored_dac_val; reg [CYCLE_COUNT_WID-1:0] last_timer = 0; reg [CYCLE_COUNT_WID-1:0] counting_timer = 0; reg [M4_CONSTS_WID-1:0] adjval_prev = 0; diff --git a/gateware/soc.py b/gateware/soc.py index a54bcce..8e1299d 100644 --- a/gateware/soc.py +++ b/gateware/soc.py @@ -198,6 +198,7 @@ class Base(Module, AutoCSR): self._make_csr("cl_word_out", CSRStatus, 64, "Control Loop Data Output") self._make_csr("cl_start_cmd", CSRStorage, 1, "Control Loop Command Start Flag") self._make_csr("cl_finish_cmd", CSRStatus, 1, "Control Loop Command Finished Flag") + self._make_csr("cl_z_report", CSRStatus, 1, "Control Loop Z Setting") self.kwargs["i_clk"] = clk self.kwargs["i_rst_L"] = ~platform.request("module_reset") @@ -208,7 +209,7 @@ class Base(Module, AutoCSR): self.kwargs["o_adc_conv"] = platform.request("adc_conv") self.kwargs["i_adc_sdo"] = platform.request("adc_sdo") self.kwargs["o_adc_sck"] = platform.request("adc_sck") - self.kwargs["o_test_clock"] = platform.request("test_clock") +# self.kwargs["o_test_clock"] = platform.request("test_clock") self.kwargs["o_set_low"] = platform.request("differntial_output_low") """ Dump all MMIO pins to a JSON file with their exact bit widths. """ diff --git a/linux/control_loop_test.py b/linux/control_loop_test.py new file mode 100644 index 0000000..b13961d --- /dev/null +++ b/linux/control_loop_test.py @@ -0,0 +1,31 @@ +from mmio import * +from comm import * +from sys import argv + +# The DAC always have to be init and reset +dac_init(0) + +write_dac_sel(1 << 1, 0) +write_adc_sel(2 << 1, 0) + +def cl_cmd_write(cmd, val): + write_cl_word_in(val) + write_cl_cmd(1 << 7 | cmd) + write_cl_start_cmd(1) + while not read_cl_finish_cmd(): + print('aa') + write_cl_start_cmd(0) + +def cl_cmd_read(cmd): + write_cl_cmd(cmd) + write_cl_start_cmd(1) + while not read_cl_finish_cmd(): + print('aa') + write_cl_start_cmd(0) + return read_cl_word_out() + +cl_cmd_write(2, int(argv[1])) # Setpoint +cl_cmd_write(3, int(argv[2])) # P +cl_cmd_write(4, int(argv[3])) # I +cl_cmd_write(8, int(argv[4])) # Delay +cl_cmd_write(1, 1) diff --git a/linux/noise_test.py b/linux/noise_test.py index b77994a..c969dfc 100644 --- a/linux/noise_test.py +++ b/linux/noise_test.py @@ -1,6 +1,8 @@ from comm import * from time import sleep_ms +dac_init(0) +write_adc_sel(0,0) for i in range(-300,300): dac_write_volt(i, 0) for j in range(0,20):