diff --git a/nmigen/.gitignore b/nmigen/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/nmigen/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/nmigen/CMakeLists.txt b/nmigen/CMakeLists.txt new file mode 100644 index 0000000..194aca3 --- /dev/null +++ b/nmigen/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.13) +# initialize the SDK based on PICO_SDK_PATH +# note: this must happen before project() +include(pico_sdk_import.cmake) +set(PROJECT nmigen) +project(${PROJECT}) + +pico_sdk_init() + +add_executable(${PROJECT}) + +pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/trigctl.pio) +pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/uart_rx.pio) + + +target_sources(${PROJECT} PRIVATE + main.c + ) + +target_link_libraries(${PROJECT} PRIVATE pico_stdlib hardware_pwm hardware_pio + hardware_dma pico_multicore cmsis_core + pico_fix_rp2040_usb_device_enumeration) +pico_add_extra_outputs(${PROJECT}) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror=implicit-function-declaration -Werror=return-type") + +# enable usb output, disable uart output +pico_enable_stdio_usb(${PROJECT} 1) +pico_enable_stdio_uart(${PROJECT} 0) diff --git a/nmigen/main.c b/nmigen/main.c new file mode 100644 index 0000000..2f0117a --- /dev/null +++ b/nmigen/main.c @@ -0,0 +1,149 @@ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "trigctl.pio.h" +#include "uart_rx.pio.h" + +#define PIN_TRIGGER 14 +#define PIN_TRIGGER_POL glitch_positive + +#define PIN_NMI 15 +#define PIN_NMI_POL glitch_negative + +#define PIN_ACK 16 +#define PIN_UART 17 + +#define PIO_UNIT pio0 +#define PIOSM_UART 0 +#define PIOSM_TRIG 1 + +#define UART_BAUD 9600 + +static void init_gpio(void) { + const int func = GPIO_FUNC_PIO0; + + // init trigger pin + sio_hw->gpio_oe_clr = 1u << PIN_TRIGGER; + hw_write_masked(&padsbank0_hw->io[PIN_TRIGGER] + , (PADS_BANK0_GPIO0_IE_BITS) + , (PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS) + | (PADS_BANK0_GPIO0_SCHMITT_BITS) + ); + hw_write_masked(&iobank0_hw->io[PIN_TRIGGER].ctrl + , (func << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB) + | ((uint)(PIN_TRIGGER_POL ? GPIO_OVERRIDE_INVERT + : GPIO_OVERRIDE_NORMAL) << IO_BANK0_GPIO0_CTRL_INOVER_LSB) + , (IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS) + | (IO_BANK0_GPIO0_CTRL_INOVER_BITS) + ); + + sio_hw->gpio_clr = 1u << PIN_NMI; + sio_hw->gpio_oe_set = 1u << PIN_NMI; + hw_write_masked(&padsbank0_hw->io[PIN_NMI] + , PADS_BANK0_GPIO0_IE_BITS + | ((uint)GPIO_SLEW_RATE_FAST << PADS_BANK0_GPIO0_SLEWFAST_LSB) + | (PADS_BANK0_GPIO0_DRIVE_VALUE_12MA << PADS_BANK0_GPIO0_DRIVE_LSB) + , PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS + | PADS_BANK0_GPIO0_SLEWFAST_BITS | PADS_BANK0_GPIO0_DRIVE_BITS + ); + hw_write_masked(&iobank0_hw->io[PIN_NMI].ctrl + , (func << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB) + | ((uint)(PIN_NMI_POL ? GPIO_OVERRIDE_INVERT + : GPIO_OVERRIDE_NORMAL) << IO_BANK0_GPIO0_CTRL_OUTOVER_LSB) + , IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS | IO_BANK0_GPIO0_CTRL_OUTOVER_BITS + ); + + sio_hw->gpio_clr = 1u << PIN_ACK; + sio_hw->gpio_oe_set = 1u << PIN_ACK; + gpio_init(PIN_ACK); + gpio_set_dir(PIN_ACK, GPIO_OUT); + gpio_set_function(PIN_ACK, GPIO_FUNC_SIO); +} + +static uint off_uart, off_trig; +static void init_pio(void) { + off_uart = pio_add_program(PIO_UNIT, &uart_rx_program); + uart_rx_program_init(PIO_UNIT, PIOSM_UART, off_uart, PIN_UART, UART_BAUD); + + off_trig = pio_add_program(PIO_UNIT, &trigctl_program); + trigctl_pio_init(PIO_UNIT, PIOSM_TRIG, off_trig, + trig_source_pin, PIN_NMI, PIN_TRIGGER, PIN_TRIGGER_POL, + PIN_NMI_POL, true, 125); // 1 MHz + + const int irq = PIO0_IRQ_1; + irq_set_enabled(irq, false); + trigctl_ack_glitch_irq(PIO_UNIT, PIOSM_TRIG); + trigctl_set_glitch_irq_enabled(PIO_UNIT, PIOSM_TRIG, 1, true); + irq_set_priority(irq, PICO_HIGHEST_IRQ_PRIORITY); + irq_set_enabled(irq, true); +} + +static bool uart_poll_ch(uint8_t* ch) { + if (pio_sm_is_rx_fifo_empty(PIO_UNIT, PIOSM_UART)) return false; + + // 8-bit read from the uppermost byte of the FIFO, as data is left-justified + io_rw_8 *rxfifo_shift = (io_rw_8*)&PIO_UNIT->rxf[PIOSM_UART] + 3; + *ch = *rxfifo_shift; + return true; +} + +int main() { + stdio_init_all(); + + init_gpio(); + init_pio(); + + while (!stdio_usb_connected()); + + uint32_t delay = 1; + const uint32_t len = 2; + + while (true) { + if (pio_sm_is_tx_fifo_empty(PIO_UNIT, PIOSM_TRIG)) { + printf("push offlen\n"); + trigctl_push_off_len(PIO_UNIT, PIOSM_TRIG, delay, len); + } + + uint8_t ch; + if (uart_poll_ch(&ch)) { + printf("got cmd: %c\n", ch); + switch (ch) { + case '0': delay = 1; break; + case '+': ++delay; break; + case '-': --delay; break; + default: goto noack; + } + + printf("send ack\n"); + sio_hw->gpio_set = 1u << PIN_ACK; + busy_wait_us_32(50); + sio_hw->gpio_clr = 1u << PIN_ACK; + printf("sent ack\n"); + + noack:; + } + + if (PIO_UNIT->irq & (1u << PIOSM_TRIG)) { + printf("sent nmi\n"); + trigctl_ack_glitch_irq(PIO_UNIT, PIOSM_TRIG); + } + } +} + diff --git a/nmigen/pico_sdk_import.cmake b/nmigen/pico_sdk_import.cmake new file mode 100644 index 0000000..28efe9e --- /dev/null +++ b/nmigen/pico_sdk_import.cmake @@ -0,0 +1,62 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + FetchContent_Populate(pico_sdk) + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/nmigen/trigctl.pio b/nmigen/trigctl.pio new file mode 100644 index 0000000..cadbd99 --- /dev/null +++ b/nmigen/trigctl.pio @@ -0,0 +1,118 @@ + +.program trigctl + +; autopull should be on, depth 32 +; +; input pin 0: trigger input +; +; set pin 0: glitch output +; +; irq 0 rel: PIO->CM0 "has glitched" signal +; irq 1 rel: CM0->PIO "do a glitch now" signal + +public _start: + out x, 32 ; offset + out y, 32 ; length + + irq clear 0 rel + +public waitinsn: + wait 1 pin 0 ; or "wait irq 1 rel" + +offloop: + jmp x-- offloop + + set pins, 1 + +lenloop: + jmp y-- lenloop + + set pins, 0 + + irq set 0 rel + +public wait2: + wait 0 pin 0 ; or "nop" + +% c-sdk { + +enum glitch_polarity { + glitch_positive = 0, + glitch_negative = 1 +}; +enum trigctl_source { + trig_source_pin, + trig_source_irq +}; + +static inline void trigctl_pio_init(PIO pio, uint sm, uint prog_offs, + enum trigctl_source trigsrc, uint glitch_pin, uint trig_pin, + enum glitch_polarity trig_in_pol, enum glitch_polarity glitch_out_pol + , bool enable, int divider) { + pio_sm_set_enabled(pio, sm, false); + + if (trigsrc == trig_source_irq) { + pio->instr_mem[prog_offs + trigctl_offset_waitinsn] = + pio_encode_wait_irq(true, true, 1); + pio->instr_mem[prog_offs + trigctl_offset_wait2] = + pio_encode_nop(); + } else { + pio->instr_mem[prog_offs + trigctl_offset_waitinsn] = + pio_encode_wait_pin(true, 0); + pio->instr_mem[prog_offs + trigctl_offset_wait2] = + pio_encode_wait_pin(false, 0); + } + + pio_sm_config c = trigctl_program_get_default_config(prog_offs); + sm_config_set_set_pins(&c, glitch_pin, 1); + sm_config_set_in_pins(&c, trig_pin/*, 1*/); + sm_config_set_out_shift(&c, false, true, 32); + sm_config_set_in_shift(&c, false, true, 32); + sm_config_set_clkdiv(&c, divider); + + if (trigsrc == trig_source_irq) { + sm_config_set_wrap(&c, prog_offs + trigctl_wrap_target, + prog_offs + trigctl_wrap - 1); + } + + pio_sm_init(pio, sm, prog_offs, &c); + + pio_sm_set_consecutive_pindirs(pio, sm, glitch_pin, 1, true ); + pio_sm_set_consecutive_pindirs(pio, sm, trig_pin , 1, false); + pio_sm_set_pins_with_mask(pio, sm, 0, 1u << glitch_pin); + + pio_sm_set_enabled(pio, sm, enable); + pio_gpio_init(pio, glitch_pin); + pio_gpio_init(pio, trig_pin); +} + +// ony use "use_wfi" when the corresponding IRQ is enabled in the NVIC +static inline void trigctl_wait_glitch_irq(PIO pio, uint sm, bool use_wfi) { + uint mask = 1u << ((sm + 0) & 3); + if (use_wfi) { + while (!(pio->irq & mask)) __WFE(); + } else { + while (!(pio->irq & mask)) ; + } +} +static inline void trigctl_ack_glitch_irq(PIO pio, uint sm) { + hw_set_bits(&pio->irq, 1 << ((sm + 0) & 3)); +} +// nvic_irqno: corresponding NVIC IRQ will be PIO${pio}_IRQ_${nvic_irqno} +static inline void trigctl_set_glitch_irq_enabled(PIO pio, uint sm, uint nvic_irqno, bool en) { + pio_set_irqn_source_enabled(pio, nvic_irqno, (sm + 0) & 3, en); +} + +/*static inline void trigctl_send_trig_irq(PIO pio, uint sm) { + pio->irq_force = 1 << ((sm + 1) & 3); +}*/ + +static inline void trigctl_push_off_len(PIO pio, uint sm, uint32_t off, uint32_t len) { + while (pio_sm_is_tx_fifo_full(pio, sm)) ; + pio->txf[sm] = off; + while (pio_sm_is_tx_fifo_full(pio, sm)) ; + pio->txf[sm] = len; +} + +%} + diff --git a/nmigen/uart_rx.pio b/nmigen/uart_rx.pio new file mode 100644 index 0000000..54a6577 --- /dev/null +++ b/nmigen/uart_rx.pio @@ -0,0 +1,94 @@ +; +; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. +; +; SPDX-License-Identifier: BSD-3-Clause +; + +.program uart_rx_mini + +; Minimum viable 8n1 UART receiver. Wait for the start bit, then sample 8 bits +; with the correct timing. +; IN pin 0 is mapped to the GPIO used as UART RX. +; Autopush must be enabled, with a threshold of 8. + + wait 0 pin 0 ; Wait for start bit + set x, 7 [10] ; Preload bit counter, delay until eye of first data bit +bitloop: ; Loop 8 times + in pins, 1 ; Sample data + jmp x-- bitloop [6] ; Each iteration is 8 cycles + +% c-sdk { +#include "hardware/clocks.h" +#include "hardware/gpio.h" + +static inline void uart_rx_mini_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) { + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false); + pio_gpio_init(pio, pin); + gpio_pull_up(pin); + + pio_sm_config c = uart_rx_mini_program_get_default_config(offset); + sm_config_set_in_pins(&c, pin); // for WAIT, IN + // Shift to right, autopush enabled + sm_config_set_in_shift(&c, true, true, 8); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); + // SM transmits 1 bit per 8 execution cycles. + float div = (float)clock_get_hz(clk_sys) / (8 * baud); + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} +%} + +.program uart_rx + +; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and +; break conditions more gracefully. +; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX. + +start: + wait 0 pin 0 ; Stall until start bit is asserted + set x, 7 [10] ; Preload bit counter, then delay until halfway through +bitloop: ; the first data bit (12 cycles incl wait, set). + in pins, 1 ; Shift data bit into ISR + jmp x-- bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles + jmp pin good_stop ; Check stop bit (should be high) + + irq 4 rel ; Either a framing error or a break. Set a sticky flag, + wait 1 pin 0 ; and wait for line to return to idle state. + jmp start ; Don't push data if we didn't see good framing. + +good_stop: ; No delay before returning to start; a little slack is + push ; important in case the TX clock is slightly too fast. + + +% c-sdk { +static inline void uart_rx_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) { + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false); + pio_gpio_init(pio, pin); + gpio_pull_up(pin); + + pio_sm_config c = uart_rx_program_get_default_config(offset); + sm_config_set_in_pins(&c, pin); // for WAIT, IN + sm_config_set_jmp_pin(&c, pin); // for JMP + // Shift to right, autopush disabled + sm_config_set_in_shift(&c, true, false, 32); + // Deeper FIFO as we're not doing any TX + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); + // SM transmits 1 bit per 8 execution cycles. + float div = (float)clock_get_hz(clk_sys) / (8 * baud); + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} + +static inline char uart_rx_program_getc(PIO pio, uint sm) { + // 8-bit read from the uppermost byte of the FIFO, as data is left-justified + io_rw_8 *rxfifo_shift = (io_rw_8*)&pio->rxf[sm] + 3; + while (pio_sm_is_rx_fifo_empty(pio, sm)) + tight_loop_contents(); + return (char)*rxfifo_shift; +} + +%} diff --git a/src/main.c b/src/main.c index caa9069..4d8c950 100644 --- a/src/main.c +++ b/src/main.c @@ -24,11 +24,41 @@ static void setup_io(void) { P2SEL0 &= ~(BIT0 | BIT1); // port 2.[01] to eUSCI_A0 P2SEL1 |= BIT0 | BIT1; - P1OUT &= ~(BIT0|BIT1|BIT2|BIT3); // P1.[0-3]: status LED and LA trigger debug stuff - P1DIR |= (BIT0|BIT1|BIT2|BIT3); + // BIT4: trigger for pico-controlled NMI + P1OUT &= ~(BIT0|BIT1|BIT2|BIT3|BIT4); // P1.[0-3]: status LED and LA trigger debug stuff + P1DIR |= (BIT0|BIT1|BIT2|BIT3|BIT4); + // BIT5: ack from pico + P1OUT &= ~BIT5; + P1DIR &= ~BIT5; + P1SEL0 &= ~BIT5; + P2SEL0 &= ~BIT5; + + // uart to pico (eUSCI_A3) + P6OUT = BIT0; + P6REN = BIT0; + P6DIR = 0xff; + P6SEL1 = ~(BIT0|BIT1); + P6SEL0 = BIT0|BIT1 ; PM5CTL0 &= ~LOCKLPM5; // Disable the GPIO power-on default high-impedance mode // to activate previously configured port settings + + // uart A3 to pico + UCA3CTLW0 = UCSWRST; + UCA3CTLW0 |= UCSSEL__SMCLK | (0<<9/*UART*/) | (0<<3/*dormant rx*/); + UCA3BRW = 6; // 9600ish baud + UCA3MCTLW |= UCOS16 | UCBRF_1 | 0x2080; + UCA3IE &= ~(UCTXIE|UCRXIE); + UCA3CTLW0 &= ~UCSWRST; +} + +static void pico_send_cmd(char cmd) { + while (!(UCA3IFG & UCTXIFG)) ; + UCA3TXBUF = cmd; +} +static void pico_wait_ack(void) { + while (!(P1IN & BIT5)) ; + while ( (P1IN & BIT5)) ; } // --- @@ -288,15 +318,17 @@ __attribute__((__interrupt__(UNMI_VECTOR))) void NMI_ISR(void) { SFRIFG1 &= ~NMIIE; SYSUNIV = 0; - ++P1OUT; + //++P1OUT; + P1OUT = ((P1OUT+1) & 15) | (P1OUT & 0xF0); } int main(void) { setup_io(); setup_clocks(); stdio_msp_init(); - //SFRIE1 = NMIIE; - //SFRRPCR = SYSRSTRE__ENABLE | SYSRSTUP__PULLUP | SYSNMIIES__FALLING | SYSNMI__NMI; + SFRIE1 = NMIIE; + SFRRPCR = SYSRSTRE__ENABLE | SYSRSTUP__PULLUP | SYSNMIIES__FALLING | SYSNMI__NMI; + // NOTE: RST/#NMI == SBWTDIO memset(regbak, 0, sizeof regbak); @@ -319,7 +351,19 @@ int main(void) { puts("hello world!\r\n"); done_irq = 0; - do_trace(); - __builtin_unreachable(); + + pico_send_cmd(0); + while (true) { + P1OUT |= BIT4; // send trig + __delay_cycles(10); + P1OUT &= ~BIT4; + + pico_send_cmd('+'); + pico_wait_ack(); + __delay_cycles(10000); + } + + /*do_trace(); + __builtin_unreachable();*/ }