From a15ba940f29d1bbcf79477ef5eed8746858b6581 Mon Sep 17 00:00:00 2001 From: sys64738 Date: Tue, 2 Nov 2021 23:06:01 +0100 Subject: [PATCH] lots of stuff i should back up now --- CMakeLists.txt | 20 +++- src/main.c | 50 +++++++-- src/{ => msp430}/msp430dbg.c | 0 src/{ => msp430}/msp430dbg.h | 0 src/{ => msp430}/pio_sbw.c | 0 src/{ => msp430}/pio_sbw.h | 0 src/{ => msp430}/sbw.pio | 0 src/{ => msp430}/tap.c | 0 src/{ => msp430}/tap.h | 0 src/swim/swim.pio | 160 +++++++++++++++++++++++++++ src/swim/swim_hw.c | 156 +++++++++++++++++++++++++++ src/swim/swim_hw.h | 28 +++++ src/test/piotest.c | 45 ++++++++ src/test/test.pio | 70 ++++++++++++ src/tool78/tool78.pio | 174 ++++++++++++++++++++++++++++++ src/tool78/tool78_cmds.h | 3 + src/tool78/tool78_defs.h | 159 +++++++++++++++++++++++++++ src/tool78/tool78_hw.h | 43 ++++++++ src/tool78/tool78_hw_helpers.c | 164 ++++++++++++++++++++++++++++ src/tool78/tool78_hw_helpers.h | 39 +++++++ src/tool78/tool78_hw_test_uart2.c | 67 ++++++++++++ 21 files changed, 1162 insertions(+), 16 deletions(-) rename src/{ => msp430}/msp430dbg.c (100%) rename src/{ => msp430}/msp430dbg.h (100%) rename src/{ => msp430}/pio_sbw.c (100%) rename src/{ => msp430}/pio_sbw.h (100%) rename src/{ => msp430}/sbw.pio (100%) rename src/{ => msp430}/tap.c (100%) rename src/{ => msp430}/tap.h (100%) create mode 100644 src/swim/swim.pio create mode 100644 src/swim/swim_hw.c create mode 100644 src/swim/swim_hw.h create mode 100644 src/test/piotest.c create mode 100644 src/test/test.pio create mode 100644 src/tool78/tool78.pio create mode 100644 src/tool78/tool78_cmds.h create mode 100644 src/tool78/tool78_defs.h create mode 100644 src/tool78/tool78_hw.h create mode 100644 src/tool78/tool78_hw_helpers.c create mode 100644 src/tool78/tool78_hw_helpers.h create mode 100644 src/tool78/tool78_hw_test_uart2.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 79fde3a..832bab1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,14 +31,21 @@ if(FAMILY STREQUAL "rp2040") # Example source target_sources(${PROJECT} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/pio_sbw.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/tap.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/msp430dbg.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/msp430/pio_sbw.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/msp430/tap.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/msp430/msp430dbg.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/swim/swim_hw.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/tool78/tool78_hw_helpers.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/tool78/tool78_hw_test_uart2.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/test/piotest.c ) - # Example include + # Example include target_include_directories(${PROJECT} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/ + ${CMAKE_CURRENT_SOURCE_DIR}/src/msp430/ + ${CMAKE_CURRENT_SOURCE_DIR}/src/swim/ + ${CMAKE_CURRENT_SOURCE_DIR}/src/tool78/ ) # Example defines @@ -48,7 +55,10 @@ if(FAMILY STREQUAL "rp2040") target_link_libraries(${PROJECT} pico_stdlib pico_unique_id hardware_pio hardware_dma hardware_pwm) - pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/src/sbw.pio) + pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/src/msp430/sbw.pio) + pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/src/swim/swim.pio) + pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/src/tool78/tool78.pio) + pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/src/test/test.pio) pico_add_extra_outputs(${PROJECT}) else() diff --git a/src/main.c b/src/main.c index b468e31..1a902ca 100644 --- a/src/main.c +++ b/src/main.c @@ -8,8 +8,11 @@ #include "pio_sbw.h" #include "tap.h" #include "msp430dbg.h" +#include "tool78_hw.h" -static uint16_t DATA_text[0x24] = { +void piotest(void); + +/*static uint16_t DATA_text[0x24] = { 0xc232,0x43c2,0x0000,0x4031,0x02fe,0x40f2,0x00ff,0x002a,0x40b2,0x5a10,0x0120, 0x43d2,0x0000,0xd232,0xd032,0x0018,0x3ffd,0xc3d2,0x0002,0xd3d2,0x0000,0xe3d2, 0x0029,0x4130,0x1300,0x1300,0x1300,0x1300,0x1300,0x1300,0x12b0,0xf822,0x1300, @@ -22,21 +25,19 @@ static uint16_t DATA_vectors[0x10] = { // 2k/8k flash, 256b RAM static uint16_t dumpmem_ram[256>>1]; -static uint16_t dumpmem_flash[8192>>1]; +static uint16_t dumpmem_flash[8192>>1];*/ -int main() { +static void test_sbw(void) { gpio_init(PINOUT_SBW_TCK); gpio_set_function(PINOUT_SBW_TCK, GPIO_FUNC_SIO); gpio_set_dir(PINOUT_SBW_TCK, true); gpio_put(PINOUT_SBW_TCK, true); - stdio_init_all(); - - /*sbw_preinit(); + sbw_preinit(); printf("%s\n", "preinited"); bool s = sbw_init(); - printf("%s\n", s ? "inited" : "failure");*/ + printf("%s\n", s ? "inited" : "failure"); /*uint8_t tdi = 0x0f, tdo = 0; sbw_sequence(8, true, &tdi, &tdo); @@ -60,9 +61,9 @@ int main() { printf("done.\n");*/ /*sbw_tap_reset(); - sbw_check_fuse(); + sbw_check_fuse();*/ - uint8_t id = sbw_tap_shift_ir(msp430_ir_bypass); + /*uint8_t id = sbw_tap_shift_ir(msp430_ir_bypass); printf("JTAG ID=%02x\n", id); sbw_tap_shift_ir(msp430_ir_ctrl_sig_16bit); @@ -150,6 +151,33 @@ int main() { msp430_memory_write8(0x0029, 0x01); // P2OUT=0x01 (P2.0 hi)*/ msp430_device_release(0xfffe/*0xf800*/); - - return 0; +} + +static uint8_t buf[32]; + +static void tool78_testtest() { + struct tool78_hw* thw = &tool78_hw_test_uart2; + + printf("initing...\n"); + bool r = thw->init(); + printf("init: %s\n", r ? "ok" : "nope"); + + size_t rrr = thw->send(7, (const uint8_t*)"hello\r\n", 0); + printf("sent %zu\n", rrr); + + while (true) { + size_t n = thw->recv(31, buf, 0); + buf[n] = 0; + if (n) { + printf("got %zu: '%s'\n", n, (const char*)buf); + thw->send(n, buf, 0); + } + } +} + +int main() { + stdio_init_all(); + tool78_testtest(); + //piotest(); + return 0; } diff --git a/src/msp430dbg.c b/src/msp430/msp430dbg.c similarity index 100% rename from src/msp430dbg.c rename to src/msp430/msp430dbg.c diff --git a/src/msp430dbg.h b/src/msp430/msp430dbg.h similarity index 100% rename from src/msp430dbg.h rename to src/msp430/msp430dbg.h diff --git a/src/pio_sbw.c b/src/msp430/pio_sbw.c similarity index 100% rename from src/pio_sbw.c rename to src/msp430/pio_sbw.c diff --git a/src/pio_sbw.h b/src/msp430/pio_sbw.h similarity index 100% rename from src/pio_sbw.h rename to src/msp430/pio_sbw.h diff --git a/src/sbw.pio b/src/msp430/sbw.pio similarity index 100% rename from src/sbw.pio rename to src/msp430/sbw.pio diff --git a/src/tap.c b/src/msp430/tap.c similarity index 100% rename from src/tap.c rename to src/msp430/tap.c diff --git a/src/tap.h b/src/msp430/tap.h similarity index 100% rename from src/tap.h rename to src/msp430/tap.h diff --git a/src/swim/swim.pio b/src/swim/swim.pio new file mode 100644 index 0000000..512168b --- /dev/null +++ b/src/swim/swim.pio @@ -0,0 +1,160 @@ +.program swim_tx +.side_set 1 pindirs + +; Pin assignments: +; - SWIM is out/set/side pin 0 +; NOTE: pin needs pullup, either input(+pullup) or output+drivezero => use set pindirs AND sideset is direction! +; +; Autopush and autopull must be enabled, set to 8 + +start: + pull side 0 + out x, 32 side 0 + + ; TODO: can we somehow sleep for a variable amount of cycles in an easier + ; way? +iter: + out y, 1 side 0 ; get next bit + jmp !y, bit_0 side 0 + +bit_1: + nop side 1 [2] +PUBLIC bit_1_delay_long: + nop side 0 [10] ; or 4 + nop side 0 [10] ; or 4 + + jmp iter_next side 0 + +bit_0: +PUBLIC bit_0_delay_long: + nop side 1 [10] ; or 4 + nop side 1 [10] ; or 4 + nop side 0 [2] + +iter_next: + jmp x--, iter side 0 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +.program swim_rx + +; Pin assignments: +; - SWIM is in pin 0 +; NOTE: pin needs pullup, either input(+pullup) or output+drivezero +; +; Autopush and autopull must be enabled, set to 8 + + +start: + pull + out x, 32 + +iter: + ; wait for pin to go low (device tx), and wait until the middle of the + ; interval (5 or 11 cyc). note that the WAIT insn has a one cycle delay + ; (and sometimes up to two), so we subtract a cycle here + + ; WAIT PIN +PUBLIC wait_delay: + wait 0 pin 0 [10] ; or 4 + ; now sample the line in the middle (UM0470 recommends this) + in pins, 1 + + wait 1 pin 0 ; wait until pin goes high again (to not auto-fallthru to + jmp x--, iter ; multiple iterations per low phase) + + push + + +;;;; 19 instructions total + + +% c-sdk { + +// SWIM defaults to low-speed mode +static inline void swim_tx_pio_init(PIO pio, uint sm, uint prog_offs, + float swim_clock_freq, uint pin_swim) { + pio_sm_set_enabled(pio, sm, false); + + pio_sm_config c = swim_tx_program_get_default_config(prog_offs); + sm_config_set_out_pins(&c, pin_swim, 1); + sm_config_set_set_pins(&c, pin_swim, 1); + sm_config_set_sideset_pins(&c, pin_swim); + sm_config_set_out_shift(&c, false, true, 8); + sm_config_set_clkdiv(&c, (float)clock_get_hz(clk_sys) / swim_clock_freq); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + pio_sm_init(pio, sm, prog_offs, &c); + + // initially set to input (i.e. pullup) as default bus state + pio_sm_set_pindirs_with_mask(pio, sm, 0, (1u << pin_swim)); + + // output should drive zero, sideset sets direction to select between + // drive-zero and pullup + pio_sm_exec(pio, sm, pio_encode_set(pio_pins, 0) | pio_encode_sideset(1, 0)); + + // SWIM is asynchronous! + hw_clear_bits(&pio->input_sync_bypass, 1u << pin_swim); + + gpio_set_pulls(pin_swim, true, false); // SWIM is pullup if input + + pio_sm_set_enabled(pio, sm, true); + + // set padsbank func to PIO *after* initing PIO, otherwise a glitch occurs + pio_gpio_init(pio, pin_swim); +} + +static inline void swim_tx_set_speed(PIO pio, uint sm, uint prog_offs, + bool en, float swim_clock_freq, bool fast) { + pio_sm_set_enabled(pio, sm, false); + + pio_sm_set_clkdiv(pio, sm, (float)clock_get_hz(clk_sys) / swim_clock_freq); + + pio->instr_mem[prog_offs + swim_tx_offset_bit_1_delay_long + 0] = pio_encode_nop() + | pio_encode_sideset(1, 0) | pio_encode_delay(fast ? 4 : 10); + pio->instr_mem[prog_offs + swim_tx_offset_bit_1_delay_long + 1] = pio_encode_nop() + | pio_encode_sideset(1, 0) | pio_encode_delay(fast ? 4 : 10); + + pio->instr_mem[prog_offs + swim_tx_offset_bit_0_delay_long + 0] = pio_encode_nop() + | pio_encode_sideset(1, 1) | pio_encode_delay(fast ? 4 : 10); + pio->instr_mem[prog_offs + swim_tx_offset_bit_0_delay_long + 1] = pio_encode_nop() + | pio_encode_sideset(1, 1) | pio_encode_delay(fast ? 4 : 10); + + pio_sm_set_enabled(pio, sm, en); +} + +// SWIM defaults to low-speed mode +static inline void swim_rx_pio_init(PIO pio, uint sm, uint prog_offs, + float swim_clock_freq, uint pin_swim) { + pio_sm_set_enabled(pio, sm, false); + + pio_sm_config c = swim_rx_program_get_default_config(prog_offs); + sm_config_set_in_pins(&c, pin_swim); + sm_config_set_clkdiv(&c, (float)clock_get_hz(clk_sys) / swim_clock_freq); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); + pio_sm_init(pio, sm, prog_offs, &c); + + // SWIM pin is pure input + pio_sm_set_pindirs_with_mask(pio, sm, 0, (1u << pin_swim)); + + // SWIM is asynchronous! + hw_clear_bits(&pio->input_sync_bypass, 1u << pin_swim); + + pio_sm_set_enabled(pio, sm, true); + + pio_gpio_init(pio, pin_swim); +} + +static inline void swim_rx_set_speed(PIO pio, uint sm, uint prog_offs, + bool en, float swim_clock_freq, bool fast) { + pio_sm_set_enabled(pio, sm, false); + + pio_sm_set_clkdiv(pio, sm, (float)clock_get_hz(clk_sys) / swim_clock_freq); + + pio->instr_mem[prog_offs + swim_rx_offset_wait_delay] + = pio_encode_wait_pin(false, 0) | pio_encode_delay(fast ? 4 : 10); + + pio_sm_set_enabled(pio, sm, en); +} + +%} + diff --git a/src/swim/swim_hw.c b/src/swim/swim_hw.c new file mode 100644 index 0000000..c553420 --- /dev/null +++ b/src/swim/swim_hw.c @@ -0,0 +1,156 @@ + +#include +#include +#include +#include + +#include "swim_hw.h" + +#include "swim.pio.h" + +#define PINOUT_SWIM_PIO pio0 +#define PINOUT_SWIM 22 + +int swim_tx_sm = -1, swim_rx_sm = -1, swim_tx_off = -1, swim_rx_off = -1; + +void swim_preinit(void) { + // TODO: + // * init GPIO (pulls, dir, out, function) + // * do entry sequence (~8 phases?) +} + +bool swim_init(void) { + // TODO: + // for each rx, tx: + // * allocate PIO program space + // * allocate PIO SMs + // * init PIO SM program & stuff + return false; +} + +void swim_deinit(void) { + // TODO: + // * disable PIO SMs + // * deallocate PIO SMs + // * deallocate PIO program space +} + +void swim_set_speed(float swim_clock_freq, bool fast) { + swim_tx_set_speed(PINOUT_SWIM_PIO, swim_tx_sm, swim_tx_off, true, + swim_clock_freq, fast); + swim_tx_set_speed(PINOUT_SWIM_PIO, swim_rx_sm, swim_rx_off, true, + swim_clock_freq, fast); +} + +// SWIM only sends up to 10 bits at once before changing direction (for an ack) +void swim_tx_bits(uint32_t nbits, uint16_t bits) { + // TODO: + // * init PIO SM stuff + // * send length + // * send data + // * wait for tx fifo empty + // * wait until finished? (use IRQ?? prolly best sync mechanism) +} +uint16_t swim_rx_bits(uint32_t nbits) { + uint16_t r = 0; + + // TODO: + // * init PIO SM stuff + // * send length + // * read data (incl. wait until finished) + + return r; +} + +static uint8_t bitswap(uint8_t in) { + static const uint8_t lut[16] = { + 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, + 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf + }; + + return (lut[in&0xf] << 4) | lut[in>>4]; +} + +bool swim_tx_command(uint8_t cmd, size_t datasize, const uint8_t* data) { + // cmd is 0 bit, 3 data bits, parity bit, responded by an ack bit + uint16_t r = 0, v = (0<<15) | ((cmd&1)<<14) | ((cmd&2)<<12) | ((cmd&4)<<10); + // add parity bit + v |= (((cmd>>0)^(cmd>>1)^(cmd>>2)) & 1) << 11; + + for (size_t i = 0; i < 10; ++i) { + swim_tx_bits(5, v); + + r = swim_rx_bits(1); // ack bit + if (r) break; + } + + if (!r) { + // oops, no ack! + return false; + } + + // start sending data bits + for (size_t i = 0; i < datasize; ++i) { + // calculate parity + for (size_t j = 0; j < 8; ++j) v ^= data[i] >> j; + v = (v & 1) << 6; + // and value bits + v |= (0 << 15) | ((uint16_t)bitswap(data[i]) << 7); + + for (size_t j = 0; j < 10; ++j) { + swim_tx_bits(10, v); + + r = swim_rx_bits(1); // ack bit + if (r) break; + } + + if (!r) { + // oops, no ack! + return false; + } + } + + return true; +} + +bool swim_rx_data(size_t datasize, uint8_t* data) { + uint16_t v = 0, r = 0; + + for (size_t i = 0; i < datasize; ++i) { + for (size_t j = 0; j < 10; ++j) { + v = swim_rx_bits(10); + + if (!(v & 0x8000)) { // should be a device->host cmd + swim_tx_bits(1, 0); // send NAK + continue; + } + + // calculate parity + r = 0; + for (size_t k = 7; k < 15; ++k) r ^= v >> k; + + if (((v >> 6) & 1) != (r & 1)) { + // parity mismatch! + r = 0; + swim_tx_bits(1, 0); // send NAK + continue; + } + + // send ACK + swim_tx_bits(1, 0x8000); + + data[i] = bitswap(v >> 7); + + r = 1; + break; + } + + if (!r) { + // oops, something went wrong + return false; + } + } + + return true; +} + diff --git a/src/swim/swim_hw.h b/src/swim/swim_hw.h new file mode 100644 index 0000000..98e20d6 --- /dev/null +++ b/src/swim/swim_hw.h @@ -0,0 +1,28 @@ + +#ifndef SWIM_HW_H_ +#define SWIM_HW_H_ + +#include +#include + +// perform entry sequence +void swim_preinit(void); + +bool swim_init(void); +void swim_deinit(void); + +// 'swim_clock_freq' is the frequency of the underlying SWIM clock, +// *NOT* the baudrate! in the STM8 device being debugged, this is usually +// HSI/2 = 8 MHz +void swim_set_speed(float swim_clock_freq, bool fast); + +// SWIM only sends up to 10 bits at once before changing direction (for an ack) +// bits are transferred most-significant-bit first (for both functions here) +void swim_tx_bits(uint32_t nbits, uint16_t bits); +uint16_t swim_rx_bits(uint32_t nbits); + +bool swim_tx_command(uint8_t cmd, size_t datasize, const uint8_t* data); +bool swim_rx_data(size_t datasize, uint8_t* data); + +#endif + diff --git a/src/test/piotest.c b/src/test/piotest.c new file mode 100644 index 0000000..f8eabda --- /dev/null +++ b/src/test/piotest.c @@ -0,0 +1,45 @@ + +#include +#include +#include + +#include "test.pio.h" + +#define PIN_T2_OUT 2 +#define PIN_T1_IN 3 +#define PIN_T1_OUT 4 +#define PIN_T1_SIDE 5 + +static volatile uint delay; + +void piotest(void) { + uint off1 = pio_add_program(pio0, &test1_program), + off2 = pio_add_program(pio0, &test2_program); + + float freq = 1/*0.05f*/ * 1000 * 1000; + + uint sm1 = 0, sm2 = 1; + uint smmask = (1u << sm1) | (1u << sm2); + + test1_program_init(pio0, sm1, off1, PIN_T1_IN, PIN_T1_OUT, PIN_T1_SIDE, freq, false); + test2_program_init(pio0, sm2, off2, PIN_T2_OUT, freq, false); + + delay = (uint)((0.85/*phase percentage*/ * 1000*1000/*microseconds*/) / freq); + asm volatile("":::"memory"); // make sure delay is computed *before* enabling both SMs + + // reset and enable both at once + pio_restart_sm_mask(pio0, smmask); + + pio_clkdiv_restart_sm_mask(pio0, smmask); + + //pio_sm_clkdiv_restart(pio0, sm2); + ///*busy_wait_us_32(0);*/busy_wait_us_32(delay); + //pio_sm_clkdiv_restart(pio0, sm1); + + /*pio_sm_set_enabled(pio0, sm1, true); + pio_sm_set_enabled(pio0, sm2, true);*/ + pio_set_sm_mask_enabled(pio0, smmask, true); + + while(1); +} + diff --git a/src/test/test.pio b/src/test/test.pio new file mode 100644 index 0000000..337ccc1 --- /dev/null +++ b/src/test/test.pio @@ -0,0 +1,70 @@ + +.program test1 +.side_set 1 + +_start: + wait 1 pin 0 side 1 ; input pin + set pins, 1 side 0 ; set pin + set pins, 0 side 0 + set pins, 1 side 0 + set pins, 0 side 0 + set pins, 1 side 0 + set pins, 0 side 0 + set pins, 1 side 0 + set pins, 0 side 0 + set pins, 1 side 0 [15] ; set pin + set pins, 0 side 0 [15] + +.program test2 + +_start: + nop [31] + set pins, 1 [16] ; set pin + set pins, 0 [16] ; set pin + +% c-sdk { +#include + +static inline void test1_program_init(PIO pio, uint sm, uint offset, uint pinin, + uint pinout, uint pinside, float freq, bool en) { + pio_gpio_init(pio, pinin ); + pio_gpio_init(pio, pinout); + pio_gpio_init(pio, pinside); + + pio_sm_set_consecutive_pindirs(pio, sm, pinin , 1, false); + pio_sm_set_consecutive_pindirs(pio, sm, pinout, 1, true ); + pio_sm_set_consecutive_pindirs(pio, sm, pinside, 1, true ); + + pio_sm_config c = test1_program_get_default_config(offset); + sm_config_set_in_pins(&c, pinin); + sm_config_set_out_pins(&c, pinout, 1); + sm_config_set_set_pins(&c, pinout, 1); + sm_config_set_sideset_pins(&c, pinside); + + float div = clock_get_hz(clk_sys) / freq; + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset, &c); + pio->input_sync_bypass = (1u< + +static inline void tool78_uart_tx_program_init(PIO pio, uint sm, uint offset, + uint pin, uint baudrate, bool en) { + // needs to work on a one-wire half-duplex line: + // * use internal pullup + // * drive output low, always + // * use pin direction setting to send data + // * default to hiZ/pullup after initc + pio_sm_set_pins_with_mask(pio, sm, 0u << pin, 1u << pin); + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false); // pin is input by default (i.e. pullup) + pio_gpio_init(pio, pin); + gpio_pull_up(pin); + gpio_set_oeover(pin, GPIO_OVERRIDE_INVERT); // see note in PIO code + + pio_sm_config c = tool78_uart_tx_program_get_default_config(offset); + + // no autopull, shifting out MSb first + sm_config_set_out_shift(&c, false, false, 8); + // only need an output FIFO + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + + sm_config_set_out_pins(&c, pin, 1); + sm_config_set_sideset_pins(&c, pin); + + float div = (float)clock_get_hz(clk_sys) / (8*baudrate); + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, en); +} + +%} + +.program tool78_uart_rx + +; 8n1 +; no autopush +; uses in and jmp pin for rx +; bit period = 1/8 PIO cycle period + +start: + wait 0 pin 0 ; wait for start bit + set x, 7 [10] ; 8 data bits. delay until middle of data bit period +loop: + in pins, 1 ; read the bit + jmp x-- loop [ 6] + + jmp pin stop_ok ; if pin high (stop bit): all is ok + + irq 4 rel ; set sticky flag due to missing stop bit + mov isr, null ; clear shift reg as the data is nonsense + wait 1 pin 0 ; wait until line goes idle/high again + jmp start ; restart + +stop_ok: + push + +% c-sdk { + +static inline void tool78_uart_rx_program_init(PIO pio, uint sm, uint offset, + uint pin, uint baudrate, bool en) { + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false); // pin is input now + pio_gpio_init(pio, pin); + gpio_pull_up(pin); + + pio_sm_config c = tool78_uart_rx_program_get_default_config(offset); + sm_config_set_in_pins(&c, pin); + sm_config_set_jmp_pin(&c, pin); + + // no autopull, MSb first + sm_config_set_in_shift(&c, false, false, 8); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); + + float div = (float)clock_get_hz(clk_sys) / (8*baudrate); + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, en); +} + +// TODO: IRQ handling! + +%} + +.program tool78_spi +.side_set 1 + +; nSCK: side-set pin +; MOSI: out pin +; MISO: in pin + +; CPHA1: set on falling edge, read on rising edge. clk high when idle +; CPOL1: can do this in IO muxing controls: GPIOx_CTRL.OUTOVER = 1 +; => gpio_set_outover(pin, GPIO_OVERRIDE_INVERT) +; this means we have to write out code such that it reads/samples on the +; "falling" edge, i.e. code it as if we're doing CPHA1 CPOL0 + + out x, 1 side 0 + mov pins, x side 1 [1] + in pins, 1 side 0 + +% c-sdk { + +static inline void tool78_spi_program_init(PIO pio, uint sm, uint offset, + uint pin_nsck, uint pin_mosi, uint pin_miso, uint freq, bool en) { + // clock deafults to high, let's not care about the rest yet + pio_sm_set_pins_with_mask(pio, sm, 0u << pin_nsck, 1u << pin_nsck); + gpio_set_outover(pin_nsck, GPIO_OVERRIDE_INVERT); // see note in PIO code + // SPI is clocked & synchronous, so we can bypass this + hw_set_bits(&pio->input_sync_bypass, 1u << pin_miso); + + pio_gpio_init(pio, pin_nsck); + pio_gpio_init(pio, pin_mosi); + pio_gpio_init(pio, pin_miso); + + pio_sm_config c = tool78_spi_program_get_default_config(offset); + + sm_config_set_out_pins(&c, pin_mosi, 1); + sm_config_set_in_pins (&c, pin_miso); + sm_config_set_sideset_pins(&c, pin_nsck); + + // MSb first, 8 bit, autopush & autopull + sm_config_set_out_shift(&c, false, true, 8); + sm_config_set_in_shift (&c, false, true, 8); + + float div = (float)clock_get_hz(clk_sys) / freq; + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, en); +} + +%} + diff --git a/src/tool78/tool78_cmds.h b/src/tool78/tool78_cmds.h new file mode 100644 index 0000000..5fef3ee --- /dev/null +++ b/src/tool78/tool78_cmds.h @@ -0,0 +1,3 @@ + +// big TODO (implement separate commands & handle MCU differences) + diff --git a/src/tool78/tool78_defs.h b/src/tool78/tool78_defs.h new file mode 100644 index 0000000..73fb365 --- /dev/null +++ b/src/tool78/tool78_defs.h @@ -0,0 +1,159 @@ + +#ifndef TOOL78_DEFS_H_ +#define TOOL78_DEFS_H_ + +#include +#include + +#define PINOUT_TOOL78_PIO pio1 + +#define PINOUT_TOOL78_nRESET 16 + +#define PINOUT_TOOL78_78K0_FLMD0 17 +#define PINOUT_TOOL78_78K0_TXMOSI 18 +#define PINOUT_TOOL78_78K0_RXMISO 19 +#define PINOUT_TOOL78_78K0_CLOCK 20 + +#define PINOUT_TOOL78_78K0R_FLMD0 17 +#define PINOUT_TOOL78_78K0R_TOOL0 18 + +#define PINOUT_TOOL78_RL78_TOOL0 17 +#define PINOUT_TOOL78_RL78_TX 18 +#define PINOUT_TOOL78_RL78_RX 19 + +enum tool78_target { + tool78k0_kx2_uart2 = 0x12, + tool78k0_kx2_spi = 0x13, + tool78k0_kx2_uart2_extclk = 0x14, + + tool78k0r_kx3l_uart1 = 0x21, + + tool78rl_uart1 = 0x31, + tool78rl_uart2 = 0x32, + tool78rl_ocd = 0x38, // FIXME: do this in a better way + + tool78_mcu_mask = 0xf0, + tool78_mcu_78k0_kx2 = 0x10, + tool78_mcu_78k0r_kx3l = 0x20, + tool78_mcu_rl78 = 0x30, + + tool78_phy_mask = 0x0f, + tool78_phy_uart1 = 0x01, + tool78_phy_uart2 = 0x02, + tool78_phy_spi = 0x03, + tool78_phy_uart2_extclk = 0x04, + tool78_phy_rlocd = 0x08, // FIXME: do this in a better way +}; + +enum tool78_entry { + // FLMD0 pulse numbers + tool78_entry_78k0_uart2 = 0, + tool78_entry_78k0_uart2_extclk = 3, + tool78_entry_78k0_spi = 8, + + // FLMD0 pulse numbers + // TODO: or is it the byte sent over TOOL0? + tool78_entry_78k0r_uart1 = 0, + + // TOOL0 initial byte values + tool78_entry_rl78_uart1 = 0x3a, + tool78_entry_rl78_uart2 = 0x00, + tool78_entry_rl78_ocd = 0xc5 +}; + +// TODO: initial baudrates? 9600 for 78k, 115.2k for RL78 + +enum tool78_cmd { + // only used in UART comms (i.e. not in 78k0/Kx2 SPI) + tool78_cmd_reset = 0x00, + tool78_cmd_verify = 0x13, + // undocumented, RL78 only? + tool78_cmd_verify_internal = 0x19, + tool78_cmd_chip_erase = 0x20, + // not available on RL78 + tool78_cmd_block_erase = 0x22, + tool78_cmd_block_blank_check = 0x32, + tool78_cmd_programming = 0x40, + // 78k0/Kx2 SPI only + tool78_cmd_status = 0x70, + // 78k0/Kx2 only + tool78_cmd_osc_freq_set = 0x90, + // not on 78k0/Kx2. arguments are different for 78K0R/Kx3-L and RL78! + tool78_cmd_baud_rate_set = 0x9a, + // 78K0R/Kx3-L and RL78: also sets flash shield window + tool78_cmd_security_set = 0xa0, + // RL78 only + tool78_cmd_security_get = 0xa1, + tool78_cmd_checksum = 0xb0, + // response format & length differ between all MCU versions! + // length & first few bytes can be used to discern between MCUs + // EXCEPT: osc freq set/baudrate set cmd required as first after reset?? + tool78_cmd_silicon_signature = 0xc0, + // not on RL78. same response for 78k0/Kx2 and 78K0R/Kx3-L, so kinda useless + tool78_cmd_version_get = 0xc5, +}; + +enum tool78_stat { + tool78_stat_cmd_not_supported = 0x04, + tool78_stat_parameter_error = 0x05, + tool78_stat_ack = 0x06, + tool78_stat_checksum_error = 0x07, + tool78_stat_verify_error = 0x0f, + // can't do command due to protection settings + tool78_stat_protect_error = 0x10, + tool78_stat_nak = 0x15, + tool78_stat_mrg10_erase_verif_error = 0x1a, + tool78_stat_mrg11_internal_verif_error = 0x1b, + tool78_stat_write_error = 0x1c, + // 78k0/Kx2 only. security related? + tool78_stat_read_error = 0x20, + // 78k0/Kx2 SPI only + tool78_stat_busy = 0xff +}; + +// TODO: only known for RL78 from fail0overflow. verify! +// these commands use a checksum that is the bitwise complement from the usual +// checksum (used in the flash programming protocol) +enum tool78ocd_cmd { + tool78ocd_cmd_reset = 0x00, + tool78ocd_cmd_version = 0x90, // also called "ping" + // also called "unlock", as it *always* required a 10-byte passphrase (but + // not always checked, depending on protection status) + tool78ocd_cmd_connect = 0x91, + // read from memory? + tool78ocd_cmd_read = 0x92, + // write to memory (2 bytes addr, 1 byte len, data) + tool78ocd_cmd_write = 0x93, + // execute code at 0xf07e0 (TODO: verify addr across versions) + tool78ocd_cmd_exec = 0x94, + tool78ocd_cmd_exit_reti = 0x95, // TODO: what is this? + tool78ocd_cmd_exit_ram = 0x97, // TODO: what is this? +}; + +enum tool78_frame { + // first byte of command + tool78_frame_soh = 0x01, + tool78_frame_cmd = 0x01, + // first byte of data or response status + tool78_frame_stx = 0x02, + tool78_frame_dat = 0x02, + // end of command or final data/resp commandframe + tool78_frame_etx = 0x03, + tool78_frame_end = 0x03, + // end of data/resp for non-final commandframe + tool78_frame_etb = 0x17, + tool78_frame_eot = 0x17, +}; + +static inline uint8_t tool78_calc_checksum8(size_t len, const uint8_t* data) { + uint8_t r = 0; + for (size_t i = 0; i < len; ++i) r = r - data[i]; + return r; +} +static inline uint8_t tool78_calc_ocd_checksum8(size_t len, const uint8_t* data) { + return ~tool78_calc_checksum8(len, data); +} +// TODO: 16bit checksum (cf. command B0 Checksum) + +#endif + diff --git a/src/tool78/tool78_hw.h b/src/tool78/tool78_hw.h new file mode 100644 index 0000000..e663c6a --- /dev/null +++ b/src/tool78/tool78_hw.h @@ -0,0 +1,43 @@ + +#ifndef TOOL78_HW_H_ +#define TOOL78_HW_H_ + +#include +#include +#include + +#include "tool78_defs.h" + +struct tool78_hw { + const enum tool78_target target; + + // performs entry sequence + initial handshake (RESET or similar stuff, eg. + // UART needs extra null bytes in the beginning) + bool (*init)(void); + void (*deinit)(void); + + // returns -1 if an rx overrun occurred + int (*has_available)(void); + + // timeout: + // * infinitely blocking: < 0 + // * not blocking at all: = 0 + // retval: negative if an rx overrun occurred + int (*recv)(int len, uint8_t* data, int32_t timeout_ms); + int (*send)(int len, const uint8_t* data, int32_t timeout_ms); +}; + +extern struct tool78_hw + tool78_hw_test_uart2, // for testing DMA buffer logic & PIO stuff + + tool78_hw_78k0_uart2, + tool78_hw_78k0_uart2_extclk, + tool78_hw_78k0_spi, + + tool78_hw_78k0r_uart1, + + tool78_hw_rl78_uart1, + tool78_hw_rl78_uart2; + +#endif + diff --git a/src/tool78/tool78_hw_helpers.c b/src/tool78/tool78_hw_helpers.c new file mode 100644 index 0000000..9933b95 --- /dev/null +++ b/src/tool78/tool78_hw_helpers.c @@ -0,0 +1,164 @@ + +#include +#include +#include +#include + +#include "tool78_defs.h" +#include "tool78_hw.h" + +#include "tool78_hw_helpers.h" + +static uint8_t bitswap(uint8_t in) { + static const uint8_t lut[16] = { + 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, + 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf + }; + + return (lut[in&0xf] << 4) | lut[in>>4]; +} + +bool tool78_hw_init_help(const pio_program_t* prgm_tx, + const pio_program_t* prgm_rx, struct tool78_pio_vars* vars) { + uint txoff = ~(uint)0, rxoff = ~(uint)0; + int smtx = -1, smrx = -1; + + if (!prgm_tx && !prgm_rx) return false; + + if (prgm_rx) { + if (!pio_can_add_program(PINOUT_TOOL78_PIO, prgm_rx)) + goto error; + rxoff = pio_add_program(PINOUT_TOOL78_PIO, prgm_rx); + } + if (prgm_tx) { + if (!pio_can_add_program(PINOUT_TOOL78_PIO, prgm_tx)) + goto error; + txoff = pio_add_program(PINOUT_TOOL78_PIO, prgm_tx); + } + + if (prgm_tx) { + smtx = pio_claim_unused_sm(PINOUT_TOOL78_PIO, false); + if (smtx == -1) goto error; + } + if (prgm_rx) { + smrx = pio_claim_unused_sm(PINOUT_TOOL78_PIO, false); + if (smrx == -1) goto error; + } + + vars->txoff = txoff; + vars->rxoff = rxoff; + vars->smtx = smtx; + vars->smrx = smrx; + + return true; // all is well! + +error: + if (!~txoff) pio_remove_program(PINOUT_TOOL78_PIO, prgm_tx, txoff); + if (!~rxoff) pio_remove_program(PINOUT_TOOL78_PIO, prgm_rx, rxoff); + if (smtx >= 0) pio_sm_unclaim(PINOUT_TOOL78_PIO, smtx); + if (smrx >= 0) pio_sm_unclaim(PINOUT_TOOL78_PIO, smrx); + return false; +} +void tool78_hw_deinit_help(const pio_program_t* prgm_tx, + const pio_program_t* prgm_rx, const struct tool78_pio_vars* vars) { + if (vars->smrx >= 0) { + pio_sm_set_enabled(PINOUT_TOOL78_PIO, vars->smrx, false); + pio_sm_unclaim(PINOUT_TOOL78_PIO, vars->smrx); + } + if (vars->smtx >= 0) { + pio_sm_set_enabled(PINOUT_TOOL78_PIO, vars->smtx, false); + pio_sm_unclaim(PINOUT_TOOL78_PIO, vars->smtx); + } + if (~vars->rxoff) pio_remove_program(PINOUT_TOOL78_PIO, prgm_rx, vars->rxoff); + if (~vars->txoff) pio_remove_program(PINOUT_TOOL78_PIO, prgm_tx, vars->txoff); +} + +bool tool78_hw_help_check_overrun(const struct tool78_pio_vars* vars) { + if (vars->smrx < 0 || !~vars->rxoff) return false; + + // check if an RX overrun happened. if so, reset some stuff + uint rxstall_flg = ((1u << vars->smrx) << PIO_FDEBUG_RXSTALL_LSB); + if (PINOUT_TOOL78_PIO->fdebug & rxstall_flg) { + pio_sm_set_enabled(PINOUT_TOOL78_PIO, vars->smrx, false); + pio_sm_clear_fifos(PINOUT_TOOL78_PIO, vars->smrx); + PINOUT_TOOL78_PIO->fdebug = rxstall_flg; + pio_sm_restart(PINOUT_TOOL78_PIO, vars->smrx); + pio_sm_exec(PINOUT_TOOL78_PIO, vars->smrx, pio_encode_jmp(vars->rxoff)); + pio_sm_set_enabled(PINOUT_TOOL78_PIO, vars->smrx, true); + + return true; + } + + return false; +} + +int tool78_hw_has_available_help(const struct tool78_pio_vars* vars) { + if (tool78_hw_help_check_overrun(vars)) return -1; + + return pio_sm_get_rx_fifo_level(PINOUT_TOOL78_PIO, vars->smrx); +} + +int tool78_hw_help_recv(const struct tool78_pio_vars* vars, + int len, uint8_t* data, int32_t timeout_us) { + bool overrun = tool78_hw_help_check_overrun(vars); + + bool blockinf = timeout_us < 0; + int i = 0; + + absolute_time_t at = make_timeout_time_us(timeout_us); + timeout_state_t ts = {0}; + check_timeout_fn ct = NULL; + if (!blockinf) ct = init_single_timeout_until(&ts, at); + + for (; i < len && (!pio_sm_is_rx_fifo_empty(PINOUT_TOOL78_PIO, vars->smrx) + || blockinf); ++i) { + while (pio_sm_is_rx_fifo_empty(PINOUT_TOOL78_PIO, vars->smrx)) { + if (!blockinf && ct(&ts)) goto end; // whoops, timeout + + // TODO: sleep a bit? idk + } + + data[i] = bitswap(*(volatile uint8_t*)&PINOUT_TOOL78_PIO->rxf[vars->smrx]); + } + +end: + return overrun ? -i : i; +} + +int tool78_hw_help_send(const struct tool78_pio_vars* vars, uint32_t sleep_us_between_bytes, + int len, const uint8_t* data, int32_t timeout_us) { + + if (vars->exclusive) + pio_sm_set_enabled(PINOUT_TOOL78_PIO, vars->smrx, false); + + bool blockinf = timeout_us < 0; + absolute_time_t at = make_timeout_time_us(timeout_us); + timeout_state_t ts = {0}; + check_timeout_fn ct = NULL; + if (!blockinf) ct = init_single_timeout_until(&ts, at); + + int i = 0; + for (; i < len; ++i) { + while (pio_sm_is_tx_fifo_full(PINOUT_TOOL78_PIO, vars->smtx)) { + if (!blockinf && ct(&ts)) goto end; // whoops, timeout + } + + *(volatile uint8_t*)&PINOUT_TOOL78_PIO->txf[vars->smtx] = bitswap(data[i]); + + if (sleep_us_between_bytes) busy_wait_us_32(sleep_us_between_bytes); + } + +end: + if (vars->exclusive) { + // wait until everything is sent before reenabling the RX SM again + while (!pio_sm_is_tx_fifo_empty(PINOUT_TOOL78_PIO, vars->smtx)) + ; // wait until FIFO is clear + while (!(PINOUT_TOOL78_PIO->fdebug & ((1u << vars.smtx) << PIO_FDEBUG_TXSTALL_LSB))) + ; // now wait for the SM to hang on the next 'pull' + + pio_sm_set_enabled(PINOUT_TOOL78_PIO, vars->smrx, true); + } + + return i; +} + diff --git a/src/tool78/tool78_hw_helpers.h b/src/tool78/tool78_hw_helpers.h new file mode 100644 index 0000000..3c1087e --- /dev/null +++ b/src/tool78/tool78_hw_helpers.h @@ -0,0 +1,39 @@ + +#ifndef TOOL78_HW_HELPERS_H_ +#define TOOL78_HW_HELPERS_H_ + +#include +#include + +#include + +struct tool78_pio_vars { + uint txoff, rxoff; + int smtx, smrx; + // temporarily disable rx when doing tx (typically desired when doing + // single-wire UART, as otherwise the data would be echoing back) + bool exclusive; +}; + +// claims and inits program space & state machines, saves the offsets & SMs +// in a struct. returns true if successful. +// still needs to be done: initing & enabling the PIO SMs +bool tool78_hw_init_help(const pio_program_t* prgm_tx, + const pio_program_t* prgm_rx, struct tool78_pio_vars* vars); + +// disables the SM, deallocates the program & SM +// still nees to be done: changing the SM pin functions to something/NULL +void tool78_hw_deinit_help(const pio_program_t* prgm_tx, + const pio_program_t* prgm_rx, const struct tool78_pio_vars* vars); + +bool tool78_hw_help_check_overrun(const struct tool78_pio_vars* vars); + +int tool78_hw_has_available_help(const struct tool78_pio_vars* vars); +int tool78_hw_help_recv(const struct tool78_pio_vars* vars, + int len, uint8_t* data, int32_t timeout_us); +int tool78_hw_help_send(const struct tool78_pio_vars* vars, uint32_t sleep_us_between_bytes, + int len, const uint8_t* data, int32_t timeout_us); + +#endif + + diff --git a/src/tool78/tool78_hw_test_uart2.c b/src/tool78/tool78_hw_test_uart2.c new file mode 100644 index 0000000..a89c3cd --- /dev/null +++ b/src/tool78/tool78_hw_test_uart2.c @@ -0,0 +1,67 @@ + +#include +#include +#include +#include +#include +#include + +#include "tool78_defs.h" +#include "tool78_hw_helpers.h" + +#include "tool78.pio.h" + +#include "tool78_hw.h" + +static struct tool78_pio_vars vars; + +static bool test_init(void) { + if (!tool78_hw_init_help(&tool78_uart_tx_program, &tool78_uart_rx_program, + &vars)) return false; + + vars.exclusive = false; + + // TODO: entry sequence here + + tool78_uart_tx_program_init(PINOUT_TOOL78_PIO, vars.smtx, vars.txoff, + PINOUT_TOOL78_RL78_TX, 115200, true); + tool78_uart_rx_program_init(PINOUT_TOOL78_PIO, vars.smrx, vars.rxoff, + PINOUT_TOOL78_RL78_RX, 115200, true); + + // TODO: magic initial not-really-commands + + return true; // all is well! +} +static void test_deinit(void) { + tool78_hw_deinit_help(&tool78_uart_tx_program, &tool78_uart_rx_program, + &vars); + + // keep pullups + gpio_set_function(PINOUT_TOOL78_78K0_TXMOSI, GPIO_FUNC_NULL); + gpio_set_function(PINOUT_TOOL78_78K0_RXMISO, GPIO_FUNC_NULL); +} + +static int test_has_available(void) { + return tool78_hw_has_available_help(&vars); +} + +static int test_recv(int len, uint8_t* data, int32_t timeout_us) { + return tool78_hw_help_recv(&vars, len, data, timeout_us); +} +static int test_send(int len, const uint8_t* data, int32_t timeout_us) { + return tool78_hw_help_send(&vars, 0/*inter-byte delay*/, + len, data, timeout_us); +} + +struct tool78_hw tool78_hw_test_uart2 = { + .target = tool78k0_kx2_uart2, + + .init = test_init, + .deinit = test_deinit, + + .has_available = test_has_available, + + .recv = test_recv, + .send = test_send +}; +