Add pio-based flux capturing for rp2040

This commit is contained in:
Jeff Epler 2024-03-25 11:12:04 -05:00
parent 1be36bd7cb
commit 9efb034cba
7 changed files with 221 additions and 11 deletions

View file

@ -508,6 +508,7 @@ msgstr ""
msgid "All event channels in use"
msgstr ""
#: ports/raspberrypi/common-hal/floppyio/__init__.c
#: ports/raspberrypi/common-hal/picodvi/Framebuffer.c
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
#: ports/raspberrypi/common-hal/usb_host/Port.c
@ -4029,6 +4030,15 @@ msgstr ""
msgid "timeout must be < 655.35 secs"
msgstr ""
#: ports/raspberrypi/common-hal/floppyio/__init__.c
msgid "timeout waiting for flux"
msgstr ""
#: ports/raspberrypi/common-hal/floppyio/__init__.c
#: shared-module/floppyio/__init__.c
msgid "timeout waiting for index pulse"
msgstr ""
#: shared-module/sdcardio/SDCard.c
msgid "timeout waiting for v1 card"
msgstr ""

View file

@ -7,3 +7,6 @@ CHIP_VARIANT = RP2040
CHIP_FAMILY = rp2
EXTERNAL_FLASH_DEVICES = "GD25Q64C,W25Q64JVxQ,W25Q128JV"
CIRCUITPY_USB_HOST = 0
CIRCUITPY_PICODVI = 0

View file

@ -0,0 +1,195 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2024 Jeff Epler for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "bindings/rp2pio/StateMachine.h"
#include "py/runtime.h"
#include "shared-bindings/digitalio/DigitalInOut.h"
#include "shared-bindings/floppyio/__init__.h"
#include "common-hal/floppyio/__init__.h"
#include "shared-bindings/time/__init__.h"
#include "supervisor/shared/tick.h"
static const uint16_t fluxread_program[] = {
// ; Count flux pulses and watch for index pin
// ; flux input is the 'jmp pin'. index is "pin zero".
// ; Counts are in units 3 / F_pio, so e.g., at 30MHz 1 count = 0.1us
// ; Count down while waiting for the counter to go HIGH
// ; The only counting is down, so C code will just have to negate the
// count!
// ; Each 'wait one' loop takes 3 instruction-times
// wait_one:
0x0041, // jmp x--, wait_one_next ; acts as a non-conditional decrement
// of x
// wait_one_next:
0x00c3, // jmp pin wait_zero
0x0000, // jmp wait_one
// ; Each 'wait zero' loop takes 3 instruction-times, needing one
// instruction delay
// ; (it has to match the 'wait one' timing exactly)
// wait_zero:
0x0044, // jmp x--, wait_zero_next ; acts as a non-conditional decrement
// of x
// wait_zero_next:
0x01c3, // jmp pin wait_zero [1]
// ; Top bit is index status, bottom 15 bits are inverse of counts
// ; Combined FIFO gives 16 entries (8 32-bit entries) so with the
// ; smallest plausible pulse of 2us there are 250 CPU cycles available
// @125MHz
0x4001, // in pins, 1
0x402f, // in x, 15
// ; Three cycles for the end of loop, so we need to decrement x to make
// everything
// ; come out right. This has constant timing whether we actually jump back
// vs wrapping.
0x0040, // jmp x--, wait_one
};
typedef struct {
PIO pio;
uint8_t sm;
bool word_available;
uint16_t half;
} floppy_reader;
static bool data_available(floppy_reader *reader) {
return reader->word_available || !pio_sm_is_rx_fifo_empty(reader->pio, reader->sm);
}
static uint16_t read_fifo(floppy_reader *reader) {
if (reader->word_available) {
reader->word_available = false;
return reader->half;
}
uint32_t value = pio_sm_get_blocking(reader->pio, reader->sm);
reader->half = value >> 16;
reader->word_available = true;
return value & 0xffff;
}
int common_hal_floppyio_flux_readinto(void *buf, size_t len, digitalio_digitalinout_obj_t *data, digitalio_digitalinout_obj_t *index, mp_int_t index_wait_ms) {
#define READ_INDEX() (!!(*index_port & index_mask))
uint32_t index_mask;
volatile uint32_t *index_port = common_hal_digitalio_digitalinout_get_reg(index, DIGITALINOUT_REG_READ, &index_mask);
memset(buf, 0, len);
uint32_t pins_we_use = 1 << data->pin->number;
rp2pio_statemachine_obj_t state_machine;
bool ok = rp2pio_statemachine_construct(&state_machine,
fluxread_program, MP_ARRAY_SIZE(fluxread_program),
FLOPPYIO_SAMPLERATE * 3, // 3 PIO cycles per sample count
NULL, 0, // init program
NULL, 0, // out
index->pin, 1, // in
1, 0, // in pull up/down
NULL, 0, // set
NULL, 0, // sideset
0, 0, // initial pin state
data->pin, // jump pin
pins_we_use, false, true,
true, 32, false, // TX setting we don't use
true, // Wait for txstall. If we don't, then we'll deinit too quickly.
true, 32, true, // move 32 bits at a time
false, // claim pins
false, // Not user-interruptible.
false, // No sideset enable
0, -1, // wrap
PIO_ANY_OFFSET // offset
);
if (!ok) {
mp_raise_RuntimeError(MP_ERROR_TEXT("All state machines in use"));
}
floppy_reader reader = { .pio = state_machine.pio, .sm = state_machine.state_machine, };
uint8_t *ptr = buf, *end = ptr + len;
uint64_t index_deadline_us = time_us_64() + index_wait_ms * 1000;
common_hal_mcu_disable_interrupts();
// check if flux is arriving
uint64_t flux_deadline_us = time_us_64() + 20;
while (pio_sm_is_rx_fifo_empty(reader.pio, reader.sm)) {
if (time_us_64() > flux_deadline_us) {
common_hal_mcu_enable_interrupts();
common_hal_rp2pio_statemachine_deinit(&state_machine);
mp_raise_RuntimeError(MP_ERROR_TEXT("timeout waiting for flux"));
}
}
// wait for index pulse low
while (READ_INDEX()) {
if (time_us_64() > index_deadline_us) {
common_hal_mcu_enable_interrupts();
common_hal_rp2pio_statemachine_deinit(&state_machine);
mp_raise_RuntimeError(MP_ERROR_TEXT("timeout waiting for index pulse"));
}
}
pio_sm_clear_fifos(reader.pio, reader.sm);
// if another index doesn't show up ...
index_deadline_us = time_us_64() + index_wait_ms * 1000;
int last = read_fifo(&reader);
bool last_index = READ_INDEX();
while (ptr != end) {
/* Handle index */
bool now_index = READ_INDEX();
if (!now_index && last_index) {
break;
}
last_index = now_index;
if (!data_available(&reader)) {
// no flux is arriving? is ANY flux arriving or has a full revoulution gone by?
if (time_us_64() > index_deadline_us) {
break;
}
continue;
}
int timestamp = read_fifo(&reader);
int delta = last - timestamp;
if (delta < 0) {
delta += 65536;
}
delta /= 2;
last = timestamp;
*ptr++ = delta > 255 ? 255 : delta;
}
common_hal_mcu_enable_interrupts();
common_hal_rp2pio_statemachine_deinit(&state_machine);
return ptr - (uint8_t *)buf;
}

View file

@ -478,6 +478,7 @@ SRC_COMMON_HAL_ALL = \
dotclockframebuffer/DotClockFramebuffer.c \
dotclockframebuffer/__init__.c \
dualbank/__init__.c \
floppyio/__init__.c \
frequencyio/FrequencyIn.c \
frequencyio/__init__.c \
imagecapture/ParallelImageCapture.c \

View file

@ -40,7 +40,10 @@
#include "py/runtime.h"
//| def flux_readinto(
//| buffer: WriteableBuffer, data: digitalio.DigitalInOut, index: digitalio.DigitalInOut
//| buffer: WriteableBuffer,
//| data: digitalio.DigitalInOut,
//| index: digitalio.DigitalInOut,
//| index_wait=0.220,
//| ) -> int:
//| """Read flux transition information into the buffer.
//|
@ -52,6 +55,7 @@
//| :param buffer: Read data into this buffer. Each element represents the time between successive zero-to-one transitions.
//| :param data: Pin on which the flux data appears
//| :param index: Pin on which the index pulse appears
//| :param index_wait: Time to wait, in seconds, for the index pulse
//| :return: The actual number of bytes of read
//| """
//| ...
@ -76,7 +80,7 @@ STATIC mp_obj_t floppyio_flux_readinto(size_t n_args, const mp_obj_t *pos_args,
mp_int_t index_wait_ms = args[ARG_index_wait].u_obj ?
MICROPY_FLOAT_C_FUN(round)(mp_arg_validate_type_float(args[ARG_index_wait].u_obj, MP_QSTR_index_wait) * 1000) :
MICROPY_FLOAT_CONST(.220);
220;
return MP_OBJ_NEW_SMALL_INT(common_hal_floppyio_flux_readinto(bufinfo.buf, bufinfo.len, data, index, index_wait_ms));
#else
@ -88,7 +92,8 @@ MP_DEFINE_CONST_FUN_OBJ_KW(floppyio_flux_readinto_obj, 0, floppyio_flux_readinto
//| def mfm_readinto(
//| buffer: WriteableBuffer,
//| flux: ReadableBuffer,
//| flux_t1_nominal: int,
//| flux_t2_max: int,
//| flux_t3_max: int,
//| validity: bytearray | None = None,
//| clear_validity: bool = True,
//| ) -> int:

View file

@ -30,6 +30,7 @@
#include <stdint.h>
#include <stdbool.h>
#include "py/obj.h"
#include "shared/timeutils/timeutils.h"
extern mp_obj_t struct_time_from_tm(timeutils_struct_time_t *tm);

View file

@ -34,26 +34,20 @@
#include "shared-bindings/digitalio/DigitalInOut.h"
#endif
#ifndef T2_5
#define T2_5 (FLOPPYIO_SAMPLERATE * 5 / 2 / 1000000)
#endif
#ifndef T3_5
#define T3_5 (FLOPPYIO_SAMPLERATE * 7 / 2 / 1000000)
#endif
#include "lib/adafruit_floppy/src/mfm_impl.h"
#if CIRCUITPY_DIGITALIO
MP_WEAK
__attribute__((optimize("O3")))
int common_hal_floppyio_flux_readinto(void *buf, size_t len, digitalio_digitalinout_obj_t *data, digitalio_digitalinout_obj_t *index, mp_int_t index_wait_ms) {
mp_printf(&mp_plat_print, "common_hal_floppyio_flux_readinto in %s\n", __FILE__);
uint32_t index_mask;
volatile uint32_t *index_port = common_hal_digitalio_digitalinout_get_reg(index, DIGITALINOUT_REG_READ, &index_mask);
uint32_t data_mask;
volatile uint32_t *data_port = common_hal_digitalio_digitalinout_get_reg(data, DIGITALINOUT_REG_READ, &data_mask);
uint32_t index_deadline_ms = mp_hal_ticks_ms() + index_wait_ms;
uint32_t index_deadline_ms = supervisor_ticks_ms32() + index_wait_ms;
#undef READ_INDEX
#undef READ_DATA
#define READ_INDEX() (!!(*index_port & index_mask))
@ -69,6 +63,7 @@ int common_hal_floppyio_flux_readinto(void *buf, size_t len, digitalio_digitalin
while (READ_INDEX()) { /* NOTHING */
if (supervisor_ticks_ms32() > index_deadline_ms) {
common_hal_mcu_enable_interrupts();
mp_raise_RuntimeError(MP_ERROR_TEXT("timeout waiting for index pulse"));
return 0;
}
}