From 35d56072013ad505b73cba50c758d4e916faa227 Mon Sep 17 00:00:00 2001 From: Peter van Tol Date: Tue, 20 Feb 2024 16:08:20 +0100 Subject: [PATCH 1/5] Fix max frequency --- src/litexcnc/config/modules/stepgen.py | 36 +++++++- .../driver/modules/litexcnc_stepgen.c | 92 ++++++------------- .../driver/modules/litexcnc_stepgen.h | 11 ++- src/litexcnc/firmware/modules/stepgen.py | 7 +- 4 files changed, 69 insertions(+), 77 deletions(-) diff --git a/src/litexcnc/config/modules/stepgen.py b/src/litexcnc/config/modules/stepgen.py index d6f887a..e2edc31 100644 --- a/src/litexcnc/config/modules/stepgen.py +++ b/src/litexcnc/config/modules/stepgen.py @@ -1,4 +1,5 @@ # Imports for creating a json-definition +import math import os try: from typing import ClassVar, List, Literal, Union @@ -128,6 +129,14 @@ class StepgenInstanceConfig(BaseModel): ..., description="The configuration of the stepper type and pin-out." ) + max_frequency: bool = Field( + 400e3, + description="The guaranteed maximum frequency the stepgen can generate in Hz. " + "The actual value can be larger then this value, as this is dependent on " + "the clock-frequency and scaling. Choosing a smaller value, close to the " + "limits of your drivers, gives a higher resolution in the velocity. Default " + "value is 400,000 Hz (400 kHz)." + ) soft_stop: bool = Field( False, description="When False, the stepgen will directly stop when the stepgen is " @@ -158,6 +167,13 @@ class StepgenInstanceConfig(BaseModel): 'dir-hold-time', ] + def calculate_shift(self, mmio): + clock_frequency = mmio.clock_frequency.status.reset.value + shift = 0 + while (clock_frequency / (1 << (shift+2)) > self.max_frequency): + shift += 1 + return shift + class StepgenModuleConfig(ModuleBaseModel): """ @@ -197,14 +213,30 @@ def add_mmio_read_registers(self, mmio): @property def config_size(self): - return 4 + """Calculates the number DWORDS required to store the shift data + of the stepgen instances. The first byte is the number of instances, + followed by the shifts required for each instance to get the desired + maximum frequency. + """ + return math.ceil((1 + len(self.instances)) / 4) * 4 def store_config(self, mmio): + """Calculates the shift for each instance and stores them in the + MMIO. To calculate the shift, the clock-frequency is required. This + is taken from the MMIO register, not to break with current design + of this module. + """ # Deferred imports to prevent importing Litex while installing the driver from litex.soc.interconnect.csr import CSRStatus + # - store the number of instances + config = len(self.instances) << (self.config_size * 8 - 8) + # - calculate and store the shift for each instance + clock_frequency = mmio.clock_frequency.status.reset.value + for index, instance in enumerate(self.instances): + config += instance.calculate_shift(mmio) << (self.config_size * 8 - (2 + index) * 8) mmio.stepgen_config_data = CSRStatus( size=self.config_size*8, - reset=len(self.instances), + reset=config, description=f"The config of the Stepgen module." ) diff --git a/src/litexcnc/driver/modules/litexcnc_stepgen.c b/src/litexcnc/driver/modules/litexcnc_stepgen.c index bcfbe59..9ecf819 100644 --- a/src/litexcnc/driver/modules/litexcnc_stepgen.c +++ b/src/litexcnc/driver/modules/litexcnc_stepgen.c @@ -69,36 +69,6 @@ int register_stepgen_module(void) { EXPORT_SYMBOL_GPL(register_stepgen_module); -int rtapi_app_main(void) { - // Show some information on the module being loaded - LITEXCNC_PRINT_NO_DEVICE( - "Loading Litex Stepgen module driver version %u.%u.%u\n", - LITEXCNC_STEPGEN_VERSION_MAJOR, - LITEXCNC_STEPGEN_VERSION_MINOR, - LITEXCNC_STEPGEN_VERSION_PATCH - ); - - // Initialize the module - comp_id = hal_init(LITEXCNC_STEPGEN_NAME); - if(comp_id < 0) return comp_id; - - // Register the module with LitexCNC (NOTE: LitexCNC should be loaded first) - int result = register_stepgen_module(); - if (result<0) return result; - - // Report GPIO is ready to be used - hal_ready(comp_id); - - return 0; -} - - -void rtapi_app_exit(void) { - hal_exit(comp_id); - LITEXCNC_PRINT_NO_DEVICE("LitexCNC Stepgen module driver unloaded \n"); -} - - size_t required_config_buffer(void *module) { static litexcnc_stepgen_t *stepgen_module; stepgen_module = (litexcnc_stepgen_t *) module; @@ -139,16 +109,6 @@ int litexcnc_stepgen_config(void *module, uint8_t **data, int period) { stepgen->data.period_s_recip = 1.0f / stepgen->data.period_s; stepgen->data.cycles_per_period = stepgen->data.period_s * (*(stepgen->data.clock_frequency)); - // Set the pick-offs. At this moment it is fixed, but is easy to make it configurable - int8_t shift = 0; - while (*(stepgen->data.clock_frequency) / (1 << (shift + 1)) > stepgen->hal.param.max_driver_freq) { - shift++; - } - stepgen->data.pick_off_pos = 32; - stepgen->data.pick_off_vel = stepgen->data.pick_off_pos + shift; - stepgen->data.pick_off_acc = stepgen->data.pick_off_vel + 8; - stepgen->data.max_frequency = (float) *(stepgen->data.clock_frequency) / (1 << (shift + 1)); - // Timings // =============== // All stepgens will use the same values for steplen, dir_hold_time and dir_setup_time. @@ -190,7 +150,8 @@ int litexcnc_stepgen_config(void *module, uint8_t **data, int period) { // Calculate the maximum frequency for stepgen (in if statement to prevent division) if ((stepgen->memo.stepspace_cycles != stepspace_cycles) || (stepgen->memo.steplen_cycles != steplen_cycles)) { - stepgen->data.max_frequency = fmin(stepgen->data.max_frequency, (double) (*(stepgen->data.clock_frequency)) / (steplen_cycles + stepspace_cycles)); + // Temporary removed, as aboce will also be individual timings + // stepgen->data.max_frequency = fmin(stepgen->data.max_frequency, (double) (*(stepgen->data.clock_frequency)) / (steplen_cycles + stepspace_cycles)); stepgen->memo.steplen_cycles = steplen_cycles; stepgen->memo.stepspace_cycles = stepspace_cycles; } @@ -291,10 +252,10 @@ int litexcnc_stepgen_prepare_write(void *module, uint8_t **data, int period) { instance->data.scale_recip = 1.0 / instance->hal.param.position_scale; instance->memo.position_scale = instance->hal.param.position_scale; // Calculate the scales for speed and acceleration - instance->data.fpga_speed_scale = (float) (instance->hal.param.position_scale * (*(stepgen->data.clock_frequency_recip))) * (1LL << stepgen->data.pick_off_vel); - instance->data.fpga_speed_scale_inv = (float) (*(stepgen->data.clock_frequency)) * instance->data.scale_recip / (1LL << stepgen->data.pick_off_vel); - instance->data.fpga_acc_scale = (float) (instance->hal.param.position_scale * (*(stepgen->data.clock_frequency_recip)) * (*(stepgen->data.clock_frequency_recip))) * (1LL << (stepgen->data.pick_off_acc)); - instance->data.fpga_acc_scale_inv = (float) instance->data.scale_recip * (*(stepgen->data.clock_frequency)) * (*(stepgen->data.clock_frequency)) / (1LL << stepgen->data.pick_off_acc);; + instance->data.fpga_speed_scale = (float) (instance->hal.param.position_scale * (*(stepgen->data.clock_frequency_recip))) * (1LL << instance->data.pick_off_vel); + instance->data.fpga_speed_scale_inv = (float) (*(stepgen->data.clock_frequency)) * instance->data.scale_recip / (1LL << instance->data.pick_off_vel); + instance->data.fpga_acc_scale = (float) (instance->hal.param.position_scale * (*(stepgen->data.clock_frequency_recip)) * (*(stepgen->data.clock_frequency_recip))) * (1LL << (instance->data.pick_off_acc)); + instance->data.fpga_acc_scale_inv = (float) instance->data.scale_recip * (*(stepgen->data.clock_frequency)) * (*(stepgen->data.clock_frequency)) / (1LL << instance->data.pick_off_acc);; } // Check the limits on the speed of the stepgen @@ -303,10 +264,10 @@ int litexcnc_stepgen_prepare_write(void *module, uint8_t **data, int period) { instance->hal.param.max_velocity = 0.0; } else { // Maximum speed is positive and no zero, compare with maximum frequency - if (instance->hal.param.max_velocity > (stepgen->data.max_frequency * fabs(instance->data.scale_recip))) { + if (instance->hal.param.max_velocity > (instance->hal.param.max_frequency * fabs(instance->data.scale_recip))) { // Limit speed to the maximum. This will lead to joint follow error when the higher speeds are commanded float max_speed_desired = instance->hal.param.max_velocity; - instance->hal.param.max_velocity = stepgen->data.max_frequency * fabs(instance->data.scale_recip); + instance->hal.param.max_velocity = instance->hal.param.max_frequency * fabs(instance->data.scale_recip); // Maximum speed is too high, complain about it and modify the value if (!instance->memo.error_max_speed_printed) { LITEXCNC_ERR_NO_DEVICE( @@ -459,11 +420,11 @@ int litexcnc_stepgen_process_read(void *module, uint8_t **data, int period) { instance->data.scale_recip = 1.0 / instance->hal.param.position_scale; instance->memo.position_scale = instance->hal.param.position_scale; // Calculate the scales for speed and acceleration - instance->data.fpga_pos_scale_inv = (float) instance->data.scale_recip / (1LL << stepgen->data.pick_off_pos); - instance->data.fpga_speed_scale = (float) (instance->hal.param.position_scale * (*(stepgen->data.clock_frequency_recip))) * (1LL << stepgen->data.pick_off_vel); + instance->data.fpga_pos_scale_inv = (float) instance->data.scale_recip / (1LL << instance->data.pick_off_pos); + instance->data.fpga_speed_scale = (float) (instance->hal.param.position_scale * (*(stepgen->data.clock_frequency_recip))) * (1LL << instance->data.pick_off_vel); instance->data.fpga_speed_scale_inv = 1.0f / instance->data.fpga_speed_scale; - instance->data.fpga_acc_scale = (float) (instance->hal.param.position_scale * (*(stepgen->data.clock_frequency_recip)) * (*(stepgen->data.clock_frequency_recip))) * (1LL << (stepgen->data.pick_off_acc)); - instance->data.fpga_acc_scale_inv = (float) instance->data.scale_recip * (*(stepgen->data.clock_frequency)) * (*(stepgen->data.clock_frequency)) / (1LL << stepgen->data.pick_off_acc);; + instance->data.fpga_acc_scale = (float) (instance->hal.param.position_scale * (*(stepgen->data.clock_frequency_recip)) * (*(stepgen->data.clock_frequency_recip))) * (1LL << (instance->data.pick_off_acc)); + instance->data.fpga_acc_scale_inv = (float) instance->data.scale_recip * (*(stepgen->data.clock_frequency)) * (*(stepgen->data.clock_frequency)) / (1LL << instance->data.pick_off_acc);; } // Store the old data @@ -476,10 +437,10 @@ int litexcnc_stepgen_process_read(void *module, uint8_t **data, int period) { instance->data.speed = (int64_t) be32toh(speed) - 0x80000000; *data += 4; // The data read is 32 bit-wide. The buffer is 8-bit wide // Convert the received position to HAL pins for counts and floating-point position - *(instance->hal.pin.counts) = instance->data.position >> stepgen->data.pick_off_pos; + *(instance->hal.pin.counts) = instance->data.position >> instance->data.pick_off_pos; // Check: why is a half step subtracted from the position. Will case a possible problem // when the power is cycled -> will lead to a moving reference frame - // *(instance->hal.pin.position_fb) = (double)(instance->data.position-(1LL<<(stepgen->data.pick_off_pos-1))) * instance->data.scale_recip / (1LL << stepgen->data.pick_off_pos); + // *(instance->hal.pin.position_fb) = (double)(instance->data.position-(1LL<<(instance->data.pick_off_pos-1))) * instance->data.scale_recip / (1LL << instance->data.pick_off_pos); *(instance->hal.pin.position_fb) = (double) instance->data.position * instance->data.fpga_pos_scale_inv; *(instance->hal.pin.speed_fb) = (double) instance->data.speed * instance->data.fpga_speed_scale_inv; @@ -576,27 +537,26 @@ size_t litexcnc_stepgen_init(litexcnc_module_instance_t **module, litexcnc_t *li stepgen->data.clock_frequency_recip = &(litexcnc->clock_frequency_recip); stepgen->data.wallclock_ticks = &(litexcnc->wallclock->memo.wallclock_ticks); - // Create shared HAL pins - rtapi_snprintf(base_name, sizeof(base_name), "%s.stepgen", litexcnc->fpga->name); - // NOTE: This parameter is disabled at this moment, because the pick-off is determined - // in the formware based on a fixed value of 400 kHz and this information is not being - // relayed back to the driver. This parameter will be REMOVED in a later stage. The - // pick-off will be sent as config data from the FPGA to LinuxCNC. - //LITEXCNC_CREATE_HAL_PARAM("max-driver-freq", float, HAL_RW, &(stepgen->hal.param.max_driver_freq)); - stepgen->hal.param.max_driver_freq = 400e3; - // Store the amount of stepgen instances on this board and allocate HAL shared memory - stepgen->num_instances = be32toh(*(uint32_t*)*config); + stepgen->num_instances = *(*config); stepgen->instances = (litexcnc_stepgen_instance_t *)hal_malloc(stepgen->num_instances * sizeof(litexcnc_stepgen_instance_t)); if (stepgen->instances == NULL) { LITEXCNC_ERR_NO_DEVICE("Out of memory!\n"); return -ENOMEM; } - (*config) += 4; + (*config)++; // Create the pins and params in the HAL for (size_t i=0; inum_instances; i++) { litexcnc_stepgen_instance_t *instance = &(stepgen->instances[i]); + + // Set the pick-offs + int8_t shift = *(*config); + instance->data.pick_off_pos = 32; + instance->data.pick_off_vel = instance->data.pick_off_pos + shift; + instance->data.pick_off_acc = instance->data.pick_off_vel + 8; + instance->hal.param.max_frequency = (float) *(stepgen->data.clock_frequency) / (1 << (shift + 1)); + (*config)++; // Create the basename LITEXCNC_CREATE_BASENAME("stepgen", i); @@ -610,6 +570,7 @@ size_t litexcnc_stepgen_init(litexcnc_module_instance_t **module, litexcnc_t *li LITEXCNC_CREATE_HAL_PARAM("stepspace", u32, HAL_RW, &(instance->hal.param.stepspace)); LITEXCNC_CREATE_HAL_PARAM("dir-setup-time", u32, HAL_RW, &(instance->hal.param.dir_setup_time)); LITEXCNC_CREATE_HAL_PARAM("dir-hold-time", u32, HAL_RW, &(instance->hal.param.dir_hold_time)); + LITEXCNC_CREATE_HAL_PARAM("max-frequency", float, HAL_RO, &(instance->hal.param.max_frequency)); // Create the pins LITEXCNC_CREATE_HAL_PIN("counts", u32, HAL_OUT, &(instance->hal.pin.counts)); @@ -625,5 +586,8 @@ size_t litexcnc_stepgen_init(litexcnc_module_instance_t **module, litexcnc_t *li LITEXCNC_CREATE_HAL_PIN("debug", bit, HAL_IN, &(instance->hal.pin.debug)); } + // Align config at DWORD boundary + (*config) += 4 - ((1 + stepgen->num_instances) & 0x03); + return 0; } diff --git a/src/litexcnc/driver/modules/litexcnc_stepgen.h b/src/litexcnc/driver/modules/litexcnc_stepgen.h index 536905a..76f2742 100644 --- a/src/litexcnc/driver/modules/litexcnc_stepgen.h +++ b/src/litexcnc/driver/modules/litexcnc_stepgen.h @@ -79,6 +79,7 @@ typedef struct { hal_u32_t stepspace; /* The minimum space between step pulses, in nanoseconds. Measured from falling edge to rising edge. The actual time depends on the step rate and can be much longer. Is used to calculate the maximum stepping frequency */ hal_u32_t dir_setup_time; /* The minimum setup time from direction to step, in nanoseconds. Measured from change of direction to rising edge of step. */ hal_u32_t dir_hold_time; /* The minimum hold time of direction after step, in nanoseconds. Measured from falling edge of step to change of direction */ + hal_float_t max_frequency; /* The maximum frequency of the driver in Hz */ } param; } hal; @@ -125,6 +126,10 @@ typedef struct { float fpga_speed_scale_inv; float fpga_acc_scale; float fpga_acc_scale_inv; + // Pick-off for fixed point math + size_t pick_off_pos; + size_t pick_off_vel; + size_t pick_off_acc; } data; } litexcnc_stepgen_instance_t; @@ -141,7 +146,7 @@ typedef struct { } pin; struct{ - hal_float_t max_driver_freq; /* The maximum frequency of the driver in Hz. Default value is 400 kHz. */ + } param; } hal; @@ -161,10 +166,6 @@ typedef struct { float period_s; float period_s_recip; float cycles_per_period; - size_t pick_off_pos; - size_t pick_off_vel; - size_t pick_off_acc; - float max_frequency; } data; } litexcnc_stepgen_t; diff --git a/src/litexcnc/firmware/modules/stepgen.py b/src/litexcnc/firmware/modules/stepgen.py index 03b6c1e..af8b8da 100644 --- a/src/litexcnc/firmware/modules/stepgen.py +++ b/src/litexcnc/firmware/modules/stepgen.py @@ -304,12 +304,6 @@ def create_from_config(cls, soc: SoC, watchdog, config: StepgenModuleConfig): if not config: return - # Determine the pick-off for the velocity. This one is based on the clock-frequency - # and the step frequency to be obtained - shift = 0 - while (soc.clock_frequency / (1 << (shift + 1)) > 400e3): - shift += 1 - for index, stepgen_config in enumerate(config.instances): soc.platform.add_extension([ ("stepgen", index, @@ -317,6 +311,7 @@ def create_from_config(cls, soc: SoC, watchdog, config: StepgenModuleConfig): ) ]) # Create the stepgen and add to the system + shift = stepgen_config.calculate_shift(soc.MMIO_inst) stepgen = cls( pads=soc.platform.request('stepgen', index), pick_off=(32, 32 + shift, 32 + shift + 8), From 923432e98df857dc88a2734614fbe46e515caf5f Mon Sep 17 00:00:00 2001 From: Peter van Tol Date: Tue, 20 Feb 2024 22:47:03 +0100 Subject: [PATCH 2/5] Indiviual timings per stepgen --- .../driver/modules/litexcnc_stepgen.c | 65 ++++++++----------- src/litexcnc/firmware/modules/stepgen.py | 31 +++++---- 2 files changed, 45 insertions(+), 51 deletions(-) diff --git a/src/litexcnc/driver/modules/litexcnc_stepgen.c b/src/litexcnc/driver/modules/litexcnc_stepgen.c index 9ecf819..067523a 100644 --- a/src/litexcnc/driver/modules/litexcnc_stepgen.c +++ b/src/litexcnc/driver/modules/litexcnc_stepgen.c @@ -72,11 +72,7 @@ EXPORT_SYMBOL_GPL(register_stepgen_module); size_t required_config_buffer(void *module) { static litexcnc_stepgen_t *stepgen_module; stepgen_module = (litexcnc_stepgen_t *) module; - // Safeguard for empty modules - if (stepgen_module->num_instances == 0) { - return 0; - } - return sizeof(litexcnc_stepgen_config_data_t); + return sizeof(litexcnc_stepgen_config_data_t) * stepgen_module->num_instances; } @@ -133,49 +129,42 @@ int litexcnc_stepgen_config(void *module, uint8_t **data, int period) { // - steplen instance->data.steplen_cycles = ceil((float) instance->hal.param.steplen * (*(stepgen->data.clock_frequency)) * 1e-9); instance->memo.steplen = instance->hal.param.steplen; - if (instance->data.steplen_cycles > steplen_cycles) {steplen_cycles = instance->data.steplen_cycles;}; // - stepspace instance->data.stepspace_cycles = ceil((float) instance->hal.param.stepspace * (*(stepgen->data.clock_frequency)) * 1e-9); instance->memo.stepspace = instance->hal.param.stepspace; - if (instance->data.stepspace_cycles > stepspace_cycles) {stepspace_cycles = instance->data.stepspace_cycles;}; // - dir_hold_time instance->data.dirhold_cycles = ceil((float) instance->hal.param.dir_hold_time * (*(stepgen->data.clock_frequency)) * 1e-9); instance->memo.dir_hold_time = instance->hal.param.dir_hold_time; - if (instance->data.dirhold_cycles > dirhold_cycles) {dirhold_cycles = instance->data.dirhold_cycles;}; // - dir_setup_time instance->data.dirsetup_cycles = ceil((float) instance->hal.param.dir_setup_time * (*(stepgen->data.clock_frequency)) * 1e-9); - instance->memo.dir_setup_time = instance->hal.param.dir_setup_time; - if (instance->data.dirsetup_cycles > dirsetup_cycles) {dirsetup_cycles = instance->data.dirsetup_cycles;}; - } - - // Calculate the maximum frequency for stepgen (in if statement to prevent division) - if ((stepgen->memo.stepspace_cycles != stepspace_cycles) || (stepgen->memo.steplen_cycles != steplen_cycles)) { - // Temporary removed, as aboce will also be individual timings - // stepgen->data.max_frequency = fmin(stepgen->data.max_frequency, (double) (*(stepgen->data.clock_frequency)) / (steplen_cycles + stepspace_cycles)); - stepgen->memo.steplen_cycles = steplen_cycles; - stepgen->memo.stepspace_cycles = stepspace_cycles; - } + instance->memo.dir_setup_time = instance->hal.param.dir_setup_time; + + // Convert the general data to the correct byte order + // - check whether the parameters fits in the space + if (instance->data.steplen_cycles >= 1 << 11) { + LITEXCNC_ERR("Stepgen channel %zu: Parameter `steplen` too large and is clipped. Consider lowering the frequency of the FPGA.\n", i, stepgen->data.fpga_name); + instance->data.steplen_cycles = (1 << 11) - 1; + } + if (instance->data.dirhold_cycles >= 1 << 11) { + LITEXCNC_ERR("Stepgen channel %zu: Parameter `dir_hold_time` too large and is clipped. Consider lowering the frequency of the FPGA.\n", i, stepgen->data.fpga_name); + instance->data.dirhold_cycles = (1 << 11) - 1; + } + if (instance->data.dirsetup_cycles >= 1 << 13) { + LITEXCNC_ERR("Stepgen channel %zu: Parameter `dir_setup_time` too large and is clipped. Consider lowering the frequency of the FPGA.\n", i, stepgen->data.fpga_name); + instance->data.dirsetup_cycles = (1 << 13) - 1; + } - // Convert the general data to the correct byte order - // - check whether the parameters fits in the space - if (steplen_cycles >= 1 << 11) { - LITEXCNC_ERR("Parameter `steplen` too large and is clipped. Consider lowering the frequency of the FPGA.\n", stepgen->data.fpga_name); - steplen_cycles = (1 << 11) - 1; - } - if (dirhold_cycles >= 1 << 11) { - LITEXCNC_ERR("Parameter `dir_hold_time` too large and is clipped. Consider lowering the frequency of the FPGA.\n", stepgen->data.fpga_name); - dirhold_cycles = (1 << 11) - 1; - } - if (dirsetup_cycles >= 1 << 13) { - LITEXCNC_ERR("Parameter `dir_setup_time` too large and is clipped. Consider lowering the frequency of the FPGA.\n", stepgen->data.fpga_name); - dirsetup_cycles = (1 << 13) - 1; + // Put the data on the data-stream and advance the pointer + // - convert the timings to the data to be sent to the FPGA + config_data.timings = htobe32((dirsetup_cycles << 20) + (dirhold_cycles << 10) + (steplen_cycles << 0)); + // - send the data + memcpy(*data, &config_data, sizeof(litexcnc_stepgen_config_data_t)); + // - proceed to the next data + *data += sizeof(litexcnc_stepgen_config_data_t); + + // Calculate the maximum frequency + instance->hal.param.max_frequency = fmin(instance->hal.param.max_frequency, (double) (*(stepgen->data.clock_frequency)) / (instance->data.steplen_cycles + instance->data.stepspace_cycles)); } - // - convert the timings to the data to be sent to the FPGA - config_data.timings = htobe32((dirsetup_cycles << 20) + (dirhold_cycles << 10) + (steplen_cycles << 0)); - - // Put the data on the data-stream and advance the pointer - memcpy(*data, &config_data, required_config_buffer(stepgen)); - *data += required_config_buffer(stepgen); return 0; } diff --git a/src/litexcnc/firmware/modules/stepgen.py b/src/litexcnc/firmware/modules/stepgen.py index af8b8da..565f4ce 100644 --- a/src/litexcnc/firmware/modules/stepgen.py +++ b/src/litexcnc/firmware/modules/stepgen.py @@ -201,16 +201,21 @@ def add_mmio_config_registers(cls, mmio, config: StepgenModuleConfig): TODO: in the next iteration of the stepgen timing configs should be for each stepgen individually. """ - mmio.stepgen_stepdata = CSRStorage( - fields=[ - CSRField("steplen", size=10, offset=0, description="The length of the step pulse in clock cycles"), - CSRField("dir_hold_time", size=10, offset=10, description="The minimum delay (in clock cycles) after a step pulse before "), - CSRField("dir_setup_time", size=12, offset=20, description="The minimum delay (in clock cycles) after a direction change and before the next step - may be longer"), - ], - name=f'stepgen_stepdata', - description=f'The length of the step pulse in clock cycles', - write_from_dev=False - ) + for index, _ in enumerate(config.instances): + setattr( + mmio, + f'stepgen_{index}_stepdata', + CSRStorage( + fields=[ + CSRField("steplen", size=10, offset=0, description="The length of the step pulse in clock cycles"), + CSRField("dir_hold_time", size=10, offset=10, description="The minimum delay (in clock cycles) after a step pulse before "), + CSRField("dir_setup_time", size=12, offset=20, description="The minimum delay (in clock cycles) after a direction change and before the next step - may be longer"), + ], + name=f'stepgen_{index}_stepdata', + description=f'The length of the step pulse in clock cycles', + write_from_dev=False + ) + ) @classmethod def add_mmio_read_registers(cls, mmio, config: StepgenModuleConfig): @@ -324,9 +329,9 @@ def create_from_config(cls, soc: SoC, watchdog, config: StepgenModuleConfig): # Data from MMIO to stepgen stepgen.reset.eq(soc.MMIO_inst.reset.storage), stepgen.enable.eq(~watchdog.has_bitten), - stepgen.steplen.eq(soc.MMIO_inst.stepgen_stepdata.fields.steplen), - stepgen.dir_hold_time.eq(soc.MMIO_inst.stepgen_stepdata.fields.dir_hold_time), - stepgen.dir_setup_time.eq(soc.MMIO_inst.stepgen_stepdata.fields.dir_setup_time), + stepgen.steplen.eq(getattr(soc.MMIO_inst, f"stepgen_{index}_stepdata").fields.steplen), + stepgen.dir_hold_time.eq(getattr(soc.MMIO_inst, f"stepgen_{index}_stepdata").fields.dir_hold_time), + stepgen.dir_setup_time.eq(getattr(soc.MMIO_inst, f"stepgen_{index}_stepdata").fields.dir_setup_time), ] soc.sync += [ # Position and feedback from stepgen to MMIO From 867f3747b77d08e9b7cda18fd4ecb1991729c420 Mon Sep 17 00:00:00 2001 From: Peter van Tol Date: Tue, 20 Feb 2024 22:54:46 +0100 Subject: [PATCH 3/5] Reduce max frequency by 1 - prevent overflow --- src/litexcnc/driver/modules/litexcnc_stepgen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/litexcnc/driver/modules/litexcnc_stepgen.c b/src/litexcnc/driver/modules/litexcnc_stepgen.c index 067523a..91ca328 100644 --- a/src/litexcnc/driver/modules/litexcnc_stepgen.c +++ b/src/litexcnc/driver/modules/litexcnc_stepgen.c @@ -544,7 +544,7 @@ size_t litexcnc_stepgen_init(litexcnc_module_instance_t **module, litexcnc_t *li instance->data.pick_off_pos = 32; instance->data.pick_off_vel = instance->data.pick_off_pos + shift; instance->data.pick_off_acc = instance->data.pick_off_vel + 8; - instance->hal.param.max_frequency = (float) *(stepgen->data.clock_frequency) / (1 << (shift + 1)); + instance->hal.param.max_frequency = (float) *(stepgen->data.clock_frequency) / (1 << (shift + 1)) - 1; (*config)++; // Create the basename From 959421f0db30a884f5233141c4e5ea58d7b9b07d Mon Sep 17 00:00:00 2001 From: Peter van Tol Date: Tue, 20 Feb 2024 23:08:52 +0100 Subject: [PATCH 4/5] Update docs with timing --- docs/src/modules/stepgen.rst | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/src/modules/stepgen.rst b/docs/src/modules/stepgen.rst index 9036b02..bd7c84e 100644 --- a/docs/src/modules/stepgen.rst +++ b/docs/src/modules/stepgen.rst @@ -19,12 +19,6 @@ and velocity mode during operations. position to veloctiy, you can keep your current setup when you explicitly set te pin ``velocity-mode`` to TRUE. -.. note:: - At this moment the timings can be set for each stepgen channel. At start up these timings are - aggregated to a single timing which is applied to the whole stepgen. This means that the slowest - drive will determine the maximum speed of the machine. In future release of LitexCNC this behavior - will be changed and timings will be applied independently. - Step types ========== @@ -63,6 +57,7 @@ The code-block belows gives an example for the configuration of ``StepGen`` for "step_pin": "j9:0", "dir_pin": "j9:1" }, + "max_frequency": 400000, "soft_stop": true }, ... @@ -89,6 +84,7 @@ The code-block belows gives an example for the configuration of ``StepGen`` for "dir_pos_pin": "j9:2", "dir_neg_pin": "j9:4" }, + "max_frequency": 400000 "soft_stop": true }, ... @@ -98,6 +94,17 @@ The code-block belows gives an example for the configuration of ``StepGen`` for ] ... +.. info:: + The maximum frequency in the configuration is the guaranteed maximum frequency the + stepgen can reach. The actual maximum frequency depends on the clock speed of the + FPGA and the scaling of this clock powers with a power of 2. + + The maximum frequency should be chosen to be as close as possible to the maximum + frequency supported by the drive. Setting this value to a high value would lead + to reduction in resolution of the speed of the stepgen. + + The field ``max_frequency`` is optional. When not set, it will default to 400 kHz. + HAL === @@ -191,6 +198,11 @@ The relevant parameters which are exported to the HAL are: .stepgen..dir-setup-time (FLOAT) The minimum setup time from direction to step, in nanoseconds periods. Measured from change of direction to rising edge of step. +.stepgen..max_frequency (FLOAT) + The maximum frequency the FPGA can generate pulses. This maximum frequency is determined + based on the ``steplen`` and ``stepspace`` parameters and the fixed point math in the + FPGA (i.e. protection against rollovers). This frequency can be higher then the maximum + frequency requested in the JSON configuration file. Timing parameters - up/down ^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 05c8e0fecff8b5ae1dac6dca187ed1b9936b6094 Mon Sep 17 00:00:00 2001 From: Peter van Tol Date: Wed, 21 Feb 2024 14:03:47 +0100 Subject: [PATCH 5/5] Fix empty config --- src/litexcnc/driver/modules/litexcnc_stepgen.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/litexcnc/driver/modules/litexcnc_stepgen.c b/src/litexcnc/driver/modules/litexcnc_stepgen.c index 91ca328..b93f496 100644 --- a/src/litexcnc/driver/modules/litexcnc_stepgen.c +++ b/src/litexcnc/driver/modules/litexcnc_stepgen.c @@ -106,20 +106,11 @@ int litexcnc_stepgen_config(void *module, uint8_t **data, int period) { stepgen->data.cycles_per_period = stepgen->data.period_s * (*(stepgen->data.clock_frequency)); // Timings - // =============== - // All stepgens will use the same values for steplen, dir_hold_time and dir_setup_time. - // The maximum value is governing, so we start with the value 0. For each instance - // it is checked whether the value has changed. If it has changed, the time is - // converted to cycles. + // ======= // NOTE: all timings are in nano-seconds (1E-9), so the timing is multiplied with // the clock-frequency and divided by 1E9. However, this might lead to issues // with roll-over of the 32-bit integer. - // TODO: Make the timings settings per stepgen unit litexcnc_stepgen_config_data_t config_data = {0}; - uint32_t stepspace_cycles = 0; - uint32_t steplen_cycles = 0; - uint32_t dirhold_cycles = 0; - uint32_t dirsetup_cycles = 0; for (size_t i=0; inum_instances; i++) { // Get pointer to the stepgen instance @@ -156,7 +147,7 @@ int litexcnc_stepgen_config(void *module, uint8_t **data, int period) { // Put the data on the data-stream and advance the pointer // - convert the timings to the data to be sent to the FPGA - config_data.timings = htobe32((dirsetup_cycles << 20) + (dirhold_cycles << 10) + (steplen_cycles << 0)); + config_data.timings = htobe32((instance->data.dirsetup_cycles << 20) + (instance->data.dirhold_cycles << 10) + (instance->data.steplen_cycles << 0)); // - send the data memcpy(*data, &config_data, sizeof(litexcnc_stepgen_config_data_t)); // - proceed to the next data