Skip to content

Commit

Permalink
Merge pull request #30 from Peter-van-Tol/29-default-state-of-output-…
Browse files Browse the repository at this point in the history
…pins

29 default state of output pins
  • Loading branch information
Peter-van-Tol authored Aug 27, 2023
2 parents 9960cfc + 503bad8 commit 2396285
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 64 deletions.
16 changes: 14 additions & 2 deletions docs/src/modules/gpio.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ a second 32-bit wide word is automatically used to send or retrieve the informat
Configuration
=============

The configuration of the GPIO consists of separate blocks for GPIO In and GPIO Out.
The configuration of the GPIO of a list of pins. Per pin it can be indicated whether it
is an input or an output pin. The name of the GPIO is optional. The name will be exported
to `alias.hal` when the firmware is build using the `-a` directive.

.. code-block:: json
Expand All @@ -31,12 +33,22 @@ The configuration of the GPIO consists of separate blocks for GPIO In and GPIO O
{
"module_type": "gpio",
"instances": [
{"direction": "out", "pin":"j1:7" , "name":"j1:8" },
{"direction": "out", "pin":"j1:7" , "name":"Optional_name" },
{"direction": "out", "pin":"j1:7" , "safe_state":true },
{"direction": "in", "pin":"j9:7" }
]
},
...
]
.. info::
By default the safe state is False, meaning the output is LOW on start up of
the FPGA or after reset. When logic negates the output, it can be required to
set the pin to True, meaning when the FPGA starts with the output HIGH. When
LinuxCNC is running, the behavior of the pin is governed by the `invert_output`
parameter (See below); the `safe_State` setting does not alter behavior when
running LinuxCNC.

Defining the pin is required in the configuration. Optionally one can give the pin a name which
will be used as an alias in HAL. When no name is given, no entry in the file containnig the
aliases will be generated.
Expand Down
9 changes: 9 additions & 0 deletions src/litexcnc/config/modules/gpio.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ class GPIO_PinIn(GPIO_PinBase):

class GPIO_PinOut(GPIO_PinBase):
direction: Literal['out']
safe_state: bool = Field(
False,
description="The safe state of the pin. By default the safe state is "
"False, meaning the output is LOW. When logic negates the output, it can "
"be required to set the pin to True, meaning when the FPGA starts with "
"the output HIGH. When LinuxCNC is running, the behavior of the pin is "
"governed by the `invert_output` parameter; this setting does not alter "
"behavior when running LinuxCNC."
)
pins: ClassVar[List[str]] = [
'out',
]
Expand Down
139 changes: 84 additions & 55 deletions src/litexcnc/driver/litexcnc.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ struct rtapi_list_head litexcnc_drivers;
void *loaded_modules[32];
size_t loaded_modules_count;

// This keeps track of all default modules, so they can be unloaded later
// This keeps track of all default drivers (i.e. ethernet, USB, UART), so they can
// be unloaded later. NOTE: only ethernet is supported at this moment.
void *loaded_drivers[1];
size_t loaded_drivers_count;

Expand Down Expand Up @@ -223,6 +224,72 @@ size_t retrieve_driver_from_registration(litexcnc_driver_registration_t **regist
return -1;
}


int litexcnc_reset(litexcnc_fpga_t *fpga) {

size_t i;
int r;
uint32_t reset_flag;
uint32_t reset_status;
uint8_t *reset_buffer = rtapi_kmalloc(LITEXCNC_RESET_HEADER_SIZE, RTAPI_GFP_KERNEL);

// Raise flag
reset_flag = htobe32(0x01);
reset_status = htobe32(0x00); // Make sure the loop below is run at least once
i = 0;
while (reset_flag != reset_status) {
if (i > MAX_RESET_RETRIES) {
LITEXCNC_ERR_NO_DEVICE("Reset of the card failed after %d times\n", MAX_RESET_RETRIES);
return -1;
}
// Write the reset flag to the FPGA
memcpy(reset_buffer, &reset_flag, LITEXCNC_RESET_HEADER_SIZE);
r = fpga->write_n_bits(
fpga,
fpga->reset_base_address,
reset_buffer,
sizeof(reset_flag)
);
r = fpga->read_n_bits(
fpga,
fpga->reset_base_address,
reset_buffer,
sizeof(reset_flag)
);
reset_status = *(uint32_t *)reset_buffer;
i++;
}

// Lower flag
reset_flag = htobe32(0x00);
i = 0;
while (reset_flag != reset_status) {
if (i > MAX_RESET_RETRIES) {
LITEXCNC_ERR_NO_DEVICE("Reset of the card failed after %d times\n", MAX_RESET_RETRIES);
return -1;
}
// Write the reset flag to the FPGA
memcpy(reset_buffer, &reset_flag, LITEXCNC_RESET_HEADER_SIZE);
r = fpga->write_n_bits(
fpga,
fpga->reset_base_address,
reset_buffer,
sizeof(reset_flag)
);
r = fpga->read_n_bits(
fpga,
fpga->reset_base_address,
reset_buffer,
sizeof(reset_flag)
);
reset_status = *(uint32_t *)reset_buffer;
i++;
}

return 0;
}


EXPORT_SYMBOL_GPL(litexcnc_register);
int litexcnc_register(litexcnc_fpga_t *fpga) {
int r;
Expand Down Expand Up @@ -456,62 +523,13 @@ int litexcnc_register(litexcnc_fpga_t *fpga) {
// ==========
// RESET FPGA
// ==========
uint32_t reset_flag;
uint32_t reset_status;
uint8_t *reset_buffer = rtapi_kmalloc(LITEXCNC_RESET_HEADER_SIZE, RTAPI_GFP_KERNEL);
// Raise flag
reset_flag = htobe32(0x01);
reset_status = htobe32(0x00); // Make sure the loop below is run at least once
i = 0;
while (reset_flag != reset_status) {
if (i > MAX_RESET_RETRIES) {
LITEXCNC_ERR_NO_DEVICE("Reset of the card failed after %d times\n", MAX_RESET_RETRIES);
return -1;
}
// Write the reset flag to the FPGA
memcpy(reset_buffer, &reset_flag, LITEXCNC_RESET_HEADER_SIZE);
r = litexcnc->fpga->write_n_bits(
litexcnc->fpga,
litexcnc->fpga->reset_base_address,
reset_buffer,
sizeof(reset_flag)
);
r = litexcnc->fpga->read_n_bits(
litexcnc->fpga,
litexcnc->fpga->reset_base_address,
reset_buffer,
sizeof(reset_flag)
);
reset_status = *(uint32_t *)reset_buffer;
i++;
}
// Lower flag
reset_flag = htobe32(0x00);
i = 0;
while (reset_flag != reset_status) {
if (i > MAX_RESET_RETRIES) {
LITEXCNC_ERR_NO_DEVICE("Reset of the card failed after %d times\n", MAX_RESET_RETRIES);
return -1;
}
// Write the reset flag to the FPGA
memcpy(reset_buffer, &reset_flag, LITEXCNC_RESET_HEADER_SIZE);
r = litexcnc->fpga->write_n_bits(
litexcnc->fpga,
litexcnc->fpga->reset_base_address,
reset_buffer,
sizeof(reset_flag)
);
r = litexcnc->fpga->read_n_bits(
litexcnc->fpga,
litexcnc->fpga->reset_base_address,
reset_buffer,
sizeof(reset_flag)
);
reset_status = *(uint32_t *)reset_buffer;
i++;
r = litexcnc_reset(litexcnc->fpga);
if (r != 0) {
goto fail1;
}

return 0;
// Succes
return 0;

fail1:
litexcnc_cleanup(litexcnc); // undoes the rtapi_kmallocs from hm2_parse_module_descriptors()
Expand Down Expand Up @@ -678,6 +696,17 @@ int rtapi_app_main(void) {


void rtapi_app_exit(void) {

// Reset the FPGA to its known state (stop coolant, turn of LED's, etc).
// It is assumed that when the card is reset, it will be reset to a safe
// state.
struct rtapi_list_head *ptr;
rtapi_list_for_each(ptr, &litexcnc_list) {
litexcnc_t* board = rtapi_list_entry(ptr, litexcnc_t, list);
litexcnc_reset(board->fpga);
}

// Exit the component
hal_exit(comp_id);
LITEXCNC_PRINT_NO_DEVICE("LitexCNC driver unloaded \n");
}
Expand Down
31 changes: 28 additions & 3 deletions src/litexcnc/firmware/modules/gpio.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ def __init__(self, mmio, pads_out=None, pads_in=None) -> None:
pads_in = self._to_signal(pads_in)
self.specials += MultiReg(pads_in, mmio.gpio_in.status)

@staticmethod
def gpio_out_safe_state(config) -> int:
"""
Gets the safe state of the GPIO
"""
return sum(
[
(1*instance.safe_state << index )
for index, instance
in enumerate([instance for instance in config.instances if instance.direction == "out"])
]
)

@classmethod
def create_from_config(cls, soc, config: 'GPIO_ModuleConfig'):
"""
Expand Down Expand Up @@ -58,13 +71,25 @@ def create_from_config(cls, soc, config: 'GPIO_ModuleConfig'):
])
pads_in = soc.platform.request_all("gpio_in")

# Connect to the reset mechanism
soc.sync += [
soc.MMIO_inst.gpio_out.we.eq(0),
If(
soc.MMIO_inst.reset.storage | soc.MMIO_inst.watchdog_has_bitten.status,
soc.MMIO_inst.gpio_out.dat_w.eq(
cls.gpio_out_safe_state(config)
),
soc.MMIO_inst.gpio_out.we.eq(1)
)
]

# Create the GPIO module
gpio = cls(
soc.MMIO_inst,
pads_out,
pads_in
)
soc.submodules += gpio
soc.submodules += gpio

@classmethod
def add_mmio_write_registers(cls, mmio, config: 'GPIO_ModuleConfig'):
Expand All @@ -81,12 +106,12 @@ def add_mmio_write_registers(cls, mmio, config: 'GPIO_ModuleConfig'):

mmio.gpio_out = CSRStorage(
size=int(math.ceil(float(sum(1 for instance in config.instances if instance.direction == "out"))/32))*32,
reset=cls.gpio_out_safe_state(config),
name='gpio_out',
description="Register containing the bits to be written to the GPIO out pins.",
write_from_dev=False
write_from_dev=True
)


@classmethod
def add_mmio_read_registers(cls, mmio, config: 'GPIO_ModuleConfig'):
"""
Expand Down
17 changes: 13 additions & 4 deletions src/litexcnc/firmware/modules/pwm.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


# Default imports
import math

Expand Down Expand Up @@ -108,7 +106,7 @@ def add_mmio_write_registers(cls, mmio, pwm_config: 'PWM_ModuleConfig'):
NOTE: Storage registers are meant to be written by LinuxCNC and contain
the flags and configuration for the module.
"""
# Don't create the registers when the config is empty (no encoders
# Don't create the registers when the config is empty (no PWM/PDM
# defined in this case)
if not pwm_config:
return
Expand All @@ -117,7 +115,7 @@ def add_mmio_write_registers(cls, mmio, pwm_config: 'PWM_ModuleConfig'):
size=int(math.ceil(float(len(pwm_config.instances))/32))*32,
name='gpio_out',
description="Register containing the bits to be written to the GPIO out pins.",
write_from_dev=False
write_from_dev=True
)

# Speed and acceleration settings for the next movement segment
Expand Down Expand Up @@ -163,6 +161,17 @@ def create_from_config(cls, soc: SoC, watchdog, pwm_config: 'PWM_ModuleConfig'):
])
soc.pwm_outputs = [pad for pad in soc.platform.request_all("pwm").l]

# Turn the PWM off when the card is reset or watchdog has bitten
# Connect to the reset mechanism
soc.sync += [
soc.MMIO_inst.pwm_enable.we.eq(0),
If(
soc.MMIO_inst.reset.storage | soc.MMIO_inst.watchdog_has_bitten.status,
soc.MMIO_inst.pwm_enable.dat_w.eq(0x0),
soc.MMIO_inst.pwm_enable.we.eq(1)
)
]

# Create the generators
for index in range(len(pwm_config.instances)):
# Add the PWM-module to the platform
Expand Down

0 comments on commit 2396285

Please sign in to comment.