/* flashimg/cpu/main.c - Main entry point and event loop for sbc-harness * * Copyright (C) 2024-2025 Luke T. Shumaker * SPDX-License-Identifier: AGPL-3.0-or-later * * Because of the runtime_init_clocks() function: * * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * SPDX-License-Identifier: BSD-3-Clause */ /* libc */ #include /* for exit() */ #include /* for strlen() */ /* pico-sdk */ #include #include /* pico-sdk:hardware_flash: for flash_get_unique_id() */ #include #include #include #include #include #include #include /* for the runtime_init_clocks() signature */ /* our OS */ #include #include /* so we can set `bootclock` */ #include #include #include /* our application libraries */ #include #include #include /* our utility libraries */ #include /* for __lm_putchar() and __lm_abort() */ #include #define LOG_NAME MAIN #include #include /* local headers */ #include "flashio.h" #include "static.h" #include "usb_keyboard.h" /* 9P files */ #include #include #include #include #include "flash_static.h" #include "fs_harness_flash_bin.h" /* configuration **************************************************************/ #include "config.h" #ifndef _CONFIG_9P_MAX_CONNS #error config.h must define _CONFIG_9P_MAX_CONNS #endif #ifndef _CONFIG_9P_MAX_REQS #error config.h must define _CONFIG_9P_MAX_REQS #endif /* file tree ******************************************************************/ enum { PATH_BASE = __COUNTER__ }; #define PATH_COUNTER __COUNTER__ - PATH_BASE #define STATIC_FILE(STRNAME, ...) UTIL9P_STATIC_FILE(PATH_COUNTER, STRNAME, __VA_ARGS__) #define STATIC_DIR(STRNAME, ...) UTIL9P_STATIC_DIR(PATH_COUNTER, STRNAME, __VA_ARGS__) #define STATIC_FLASH_FILE(STRNAME, ...) \ lo_box_flash_static_file_as_lib9p_srv_file(&((struct flash_static_file){ \ .c = UTIL9P_STATIC_COMMON(PATH_COUNTER, STRNAME, 0444), \ .io = &flashio, \ __VA_ARGS__ \ })) #define API_FILE(STRNAME, SYMNAME, ...) \ lo_box_##SYMNAME##_file_as_lib9p_srv_file(&((struct SYMNAME##_file){ \ .name = STRNAME, \ .pathnum = PATH_COUNTER \ __VA_OPT__(,) __VA_ARGS__ \ })) static struct flashio flashio = {}; static uint8_t framebuffer[NUT_FB_W*NUT_FB_H] = {}; static struct lib9p_srv_file root = STATIC_DIR("", STATIC_DIR("Documentation", STATIC_FLASH_FILE("YOUR_RIGHTS_AND_OBLIGATIONS.md", .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_md_start, .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_md_end), STATIC_DIR("YOUR_RIGHTS_AND_OBLIGATIONS", STATIC_FLASH_FILE("agpl-3.0.txt", .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_agpl_3_0_txt_start, .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_agpl_3_0_txt_end), STATIC_FLASH_FILE("dhcp.bsd3-mit.txt", .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_dhcp_bsd3_mit_txt_start, .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_dhcp_bsd3_mit_txt_end), STATIC_FLASH_FILE("newlib.txt", .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_newlib_txt_start, .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_newlib_txt_end), STATIC_FLASH_FILE("pico-sdk.bsd3.txt", .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_pico_sdk_bsd3_txt_start, .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_pico_sdk_bsd3_txt_end), STATIC_FLASH_FILE("tinyusb.mit.txt", .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_tinyusb_mit_txt_start, .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_tinyusb_mit_txt_end), ), STATIC_FLASH_FILE("harness_rom_bin.txt", .data_start = _binary_static_Documentation_harness_rom_bin_txt_start, .data_end = _binary_static_Documentation_harness_rom_bin_txt_end), STATIC_FLASH_FILE("harness_flash_bin.txt", .data_start = _binary_static_Documentation_harness_flash_bin_txt_start, .data_end = _binary_static_Documentation_harness_flash_bin_txt_end), STATIC_FLASH_FILE("harness_uptime_txt.txt", .data_start = _binary_static_Documentation_harness_uptime_txt_txt_start, .data_end = _binary_static_Documentation_harness_uptime_txt_txt_end), STATIC_FLASH_FILE("dut_dvi_video_nut.txt", .data_start = _binary_static_Documentation_dut_dvi_video_nut_txt_start, .data_end = _binary_static_Documentation_dut_dvi_video_nut_txt_end), ), STATIC_DIR("harness", API_FILE("flash.bin", flash, .io = &flashio), API_FILE("proc.txt", coroutine), STATIC_FILE("rom.bin", .data_start = (void*)0x00000000, .data_size = 16*1024), API_FILE("uptime.txt", uptime), // TODO: system.log // TODO: cpuinfo.txt // TODO: ctl ), STATIC_DIR("dut", API_FILE("dvi-video.nut", nut, .framebuffer = framebuffer), // TODO: uart.txt // TODO: usb-keyboard.txt ), ); static lib9p_srv_file_or_error get_root(struct lib9p_srv_ctx *LM_UNUSED(ctx), struct lib9p_s LM_UNUSED(treename)) { return ERROR_NEW_VAL(lib9p_srv_file, root); } /* Code ***********************************************************************/ #define GPIO_PIN_DBG_UART_TX 0 #define GPIO_PIN_DBG_UART_RX 1 #define GPIO_PIN_DUT_USB_DN 2 #define GPIO_PIN_DUT_USB_DP 3 #define GPIO_PIN_DUT_UART_TX 4 #define GPIO_PIN_DUT_UART_RX 5 #define GPIO_PIN_SD_EN_HARNESS 6 #define GPIO_PIN_SD_EN_DUT 7 #define GPIO_PIN_SD_DAT2 8 #define GPIO_PIN_SD_DAT3 9 #define GPIO_PIN_SD_CLK 10 #define GPIO_PIN_SD_CMD 11 #define GPIO_PIN_SD_DAT0 12 #define GPIO_PIN_SD_DAT1 13 #define GPIO_PIN_VPU_SWD_DAT 14 #define GPIO_PIN_VPU_SWD_CLK 15 #define GPIO_PIN_NET_SPI_MISO 16 #define GPIO_PIN_NET_SPI_CS 17 #define GPIO_PIN_NET_SPI_CLK 18 #define GPIO_PIN_NET_SPI_MOSI 19 #define GPIO_PIN_NET_RST 20 #define GPIO_PIN_NET_INT 21 #define GPIO_PIN_VPU_RUN 22 #define GPIO_PIN_DUT_PWR 23 #define GPIO_PIN_CLK25MHz 24 #define GPIO_PIN_LED 25 #define GPIO_PIN_NET_LNK 26 #define GPIO_PIN_UNUSED_C27 27 #define GPIO_PIN_UNUSED_C28 28 #define GPIO_PIN_UNUSED_C29 29 /* static COROUTINE hello_world_cr(void *_chan) { const char *msg = "Hello world!\n"; usb_keyboard_rpc_t *chan = _chan; cr_begin(); for (size_t i = 0;; i = (i+1) % strlen(msg)) { int result = usb_keyboard_rpc_send_req(chan, (uint32_t)msg[i]); if (result < 1) { log_errorln("error sending rune U+", msg[i]); break; } } cr_end(); } */ static COROUTINE dhcp_cr(void *_arg) { lo_interface net_iface netdev = *((lo_interface net_iface *)_arg); cr_begin(); dhcp_client_main(netdev, "harness"); cr_end(); } struct read9p_arg { lo_interface net_iface netdev; struct lib9p_srv *srv; }; static COROUTINE read9p_cr(void *__arg) { struct read9p_arg *_arg = __arg; lo_interface net_iface netdev = _arg->netdev; struct lib9p_srv *srv = _arg->srv; cr_begin(); lo_interface net_stream_listener listener = LO_CALL(netdev, tcp_listen, LIB9P_DEFAULT_PORT_9FS); lib9p_srv_accept_and_read_loop(srv, listener); cr_end(); } static COROUTINE write9p_cr(void *_arg) { struct lib9p_srv *srv = _arg; cr_begin(); lib9p_srv_worker_loop(srv); cr_end(); } static COROUTINE heartbeat_cr(void *) { cr_begin(); bi_decl(bi_1pin_with_name(GPIO_PIN_LED, "LED")); gpiopad_configure(GPIO_PIN_LED, GPIOPAD_CONF(NOIN, OUT(4MA, SLOW), PULL_DOWN)); gpio_init(GPIO_PIN_LED); gpio_set_dir(GPIO_PIN_LED, GPIO_OUT); bool on = false; for (;;) { gpio_put(GPIO_PIN_LED, on = !on); sleep_for_ms(500); } } static const char *const hexdig = "0123456789ABCDEF"; static_assert(_CONFIG_9P_MAX_REQS <= 16); COROUTINE init_cr(void *) { cr_begin(); cid_t cid; /* Launch debug coroutine *********************************************/ cid = coroutine_add("heartbeat", heartbeat_cr, NULL); assert(cid); /* Unique IDs *********************************************************/ /* NOR flash chips have a (bog-?)standard "RUID" "Read Unique * ID" instruction; use our flash chip's unique ID as the * basis for our serial numbers. */ uint64_t flash_id64; static_assert(sizeof(flash_id64) == FLASH_UNIQUE_ID_SIZE_BYTES); flash_get_unique_id((uint8_t *)&flash_id64); uint32_t flash_id32 = hash(&flash_id64, sizeof(flash_id64)); static_assert(sizeof(flash_id32) == sizeof(hash(NULL, 0))); uint8_t flash_id24[3] = { (uint8_t)((flash_id32 >> 16) & 0xFF), (uint8_t)((flash_id32 >> 8) & 0xFF), (uint8_t)((flash_id32 >> 0) & 0xFF), }; /* Object initialization **********************************************/ struct rp2040_hwspi dev_spi = {}; rp2040_hwspi_init(&dev_spi, "W5500", RP2040_HWSPI_0, SPI_MODE_0, /* the W5500 supports mode 0 or mode 3 */ 30000000, /* min(w5500, hwspi); w5500=80MHz; hwspi=clk_peri/2 */ 30, /* W5500 datasheet says min(T_CS = SCSn High Time) = 30ns */ 0, /* bogus write write data when doing a read */ GPIO_PIN_NET_SPI_MISO, /* PIN_MISO */ GPIO_PIN_NET_SPI_MOSI, /* PIN_MOSI */ GPIO_PIN_NET_SPI_CLK, /* PIN_CLK */ GPIO_PIN_NET_SPI_CS, /* PIN_CS */ 0, 1, 2, 3); /* DMA channels */ gpiopad_configure(GPIO_PIN_CLK25MHz, GPIOPAD_CONF(NOIN, OUT(4MA, SLOW), PULL_DOWN)); /* gpio_set_function(), not clock_gpio_init(), because we * already configured the clock in runtime_init_clocks(). */ gpio_set_function(GPIO_PIN_CLK25MHz, GPIO_FUNC_GPCK); struct w5500 dev_w5500 = {}; w5500_init(&dev_w5500, "W5500", LO_BOX(spi, &dev_spi), GPIO_PIN_NET_INT, /* PIN_INTR */ GPIO_PIN_NET_RST, /* PIN_RESET */ GPIO_PIN_NET_LNK, /* PIN_LINKLED */ ((struct net_eth_addr){{ /* vendor ID: "Wiznet" */ 0x00, 0x08, 0xDC, /* serial number */ flash_id24[0], flash_id24[1], flash_id24[2], }})); uint16_t usb_serial[sizeof(flash_id64)*2]; /* UTF-16 */ for (size_t i = 0; i < LM_ARRAY_LEN(usb_serial); i++) usb_serial[i] = hexdig[(flash_id64 >> ((sizeof(flash_id64)*8)-((i+1)*4))) & 0xF]; usb_common_earlyinit(usb_serial, sizeof(usb_serial)); usb_keyboard_init(); usb_common_lateinit(); usb_keyboard_rpc_t keyboard_chan = {}; struct lib9p_srv srv = {.rootdir = get_root}; /* Launch other coroutines ********************************************/ cid = coroutine_add("usb_common", usb_common_cr, NULL); assert(cid); cid = coroutine_add("usb_keyboard", usb_keyboard_cr, &keyboard_chan); assert(cid); //cid = coroutine_add("hello_world", hello_world_cr, &keyboard_chan); assert(cid); cid = coroutine_add("dhcp", dhcp_cr, &LO_BOX(net_iface, &dev_w5500)); assert(cid); for (int i = 0; i < _CONFIG_9P_MAX_CONNS; i++) { char name[] = {'r', 'e', 'a', 'd', '-', hexdig[i], '\0'}; cid = coroutine_add_with_stack(COROUTINE_STACK_read9p_cr[i], COROUTINE_STACK_read9p_cr_len, name, read9p_cr, &(struct read9p_arg){ .netdev=LO_BOX(net_iface, &dev_w5500), .srv=&srv, }); assert(cid); } for (int i = 0; i < _CONFIG_9P_MAX_REQS; i++) { char name[] = {'w', 'r', 'i', 't', 'e', '-', hexdig[i], '\0'}; cid = coroutine_add_with_stack(COROUTINE_STACK_write9p_cr[i], COROUTINE_STACK_write9p_cr_len, name, write9p_cr, &srv); assert(cid); } /* Sleep forever ******************************************************/ cr_pause_and_yield(); assert_notreached("init unpaused"); } /* Put __lm_putchar in RAM so that ab_flash_finalize() can use it. */ [[gnu::flatten]] void __no_inline_not_in_flash_func(__lm_putchar)(unsigned char c) { if (c == '\n') { while (!uart_is_writable(uart0)) tight_loop_contents(); uart_get_hw(uart0)->dr = '\r'; } while (!uart_is_writable(uart0)) tight_loop_contents(); uart_get_hw(uart0)->dr = c; } void __lm_abort(void) { exit(1); } void runtime_init_clocks(void) { /* Reset/initialize things to a clean/safe state. */ clocks_hw->resus.ctrl = 0; xosc_init(); hw_clear_bits(&clocks_hw->clk[clk_sys].ctrl, CLOCKS_CLK_SYS_CTRL_SRC_BITS); /* clk_sys.src=CLK_REF */ while (clocks_hw->clk[clk_sys].selected != 0x1) tight_loop_contents(); hw_clear_bits(&clocks_hw->clk[clk_ref].ctrl, CLOCKS_CLK_REF_CTRL_SRC_BITS); /* clk_ref.src=ROSC_CLKSRC_PH */ while (clocks_hw->clk[clk_sys].selected != 0x1) tight_loop_contents(); /* OK, now actually configure things to how we want them. */ /* By having clk_sys be a multiple of 12MHz, we can share * pll_sys between clk_sys and clk_usb, and use pll_usb for * other purposes (like the 25MHz Ethernet clock). */ #define xosc_hz 12000000 #define pll_sys_hz 120000000 #define pll_usb_hz 25000000 /* Use * `./3rd-party/pico-sdk/src/rp2_common/hardware_clocks/scripts/vcocalc.py` * to help calculate these values. */ pll_init(pll_sys, 1, 1440000000, 6, 2); /* (12MHz/1)*(1.44GHz/12MHz)/(6*2) = 120MHz */ pll_init(pll_usb, 2, 1050000000, 7, 6); /* (12MHz/1)*(1.05GHz/12MHz)/(7*6) = 25MHz */ /* Configure DVDD based on how fast we plan to run clk_sys. */ #define clk_sys_hz 120000000 #if clk_sys_hz/1000000 <= 135 vreg_set_voltage(VREG_VOLTAGE_1_10); #elif clk_sys_hz/1000000 <= 225 /* threshold might need fine-tuned; >200, <252 */ vreg_set_voltage(VREG_VOLTAGE_1_15); #elif clk_sys_hz/1000000 <= 325 /* threshold might need fine-tuned; >292, <372 */ vreg_set_voltage(VREG_VOLTAGE_1_20); #elif clk_sys_hz/1000000 <= 385 /* threshold might need fine-tuned; >372, <400 */ vreg_set_voltage(VREG_VOLTAGE_1_25); #else vreg_set_voltage(VREG_VOLTAGE_1_30); #endif /* 10 clocks to configure... */ #define NA 0 #define CLOCK(dst, src, aux, src_hz, dst_hz) do { \ clock_configure(dst, src, aux, src_hz, dst_hz); \ assert(clock_get_hz(dst) == dst_hz); \ } while (0) CLOCK(clk_ref, 0x2, NA, xosc_hz, 12000000); /* 1. clk_ref=XOSC ; 12 MHz */ CLOCK(clk_sys, 0x1, 0x0, pll_sys_hz, clk_sys_hz); /* 2. clk_sys=AUX.PLL_SYS ; 120 MHz */ CLOCK(clk_usb, NA, 0x1, pll_sys_hz, 48000000); /* 3. clk_usb=AUX.PLL_SYS/2.5 ; 48 MHz */ CLOCK(clk_adc, NA, 0x1, pll_sys_hz, 48000000); /* 4. clk_adc=AUX.PLL_SYS/2.5 ; 48 MHz */ CLOCK(clk_rtc, NA, 0x1, pll_sys_hz, 46875); /* 5. clk_rtc=AUX.PLL_SYS/2560 ; 0.047 MHz */ CLOCK(clk_peri, NA, 0x0, clk_sys_hz, 120000000); /* 6. clk_peri=AUX.CLK_SYS ; 120 MHz */ /*OCK(clk_gpout0, NA, ???, ??????????, ?????????); 7. clk_gpout0=??? ; ??? MHz */ /*OCK(clk_gpout1, NA, ???, ??????????, ?????????); 8. clk_gpout1=??? ; ??? MHz */ CLOCK(clk_gpout2, NA, 0x3, pll_usb_hz, 25000000); /* 9. clk_gpout2=AU?.PLL_USB ; 25 MHz */ /*OCK(clk_gpout3, NA, ???, ??????????, ?????????); 10. clk_gpout3=??? ; ??? MHz */ /* Start the watchdog tick generator (the RP2040's only tick generator). */ tick_start(TICK_WATCHDOG, clock_get_hz(clk_ref) / MHZ); } int main() { bootclock = rp2040_hwtimer(0); for (uint gpio = 0; gpio < 30; gpio++) gpiopad_configure(gpio, GPIOPAD_CONF(NOIN, NOOUT, NO_PULL)); gpiopad_configure(GPIO_PIN_DBG_UART_TX, GPIOPAD_CONF(NOIN, OUT(4MA, SLOW), PULL_DOWN)); gpiopad_configure(GPIO_PIN_DBG_UART_RX, GPIOPAD_CONF(IN(SCHMITT), NOOUT, PULL_DOWN)); uart_init(uart0, 115200); gpio_set_function(GPIO_PIN_DBG_UART_TX, GPIO_FUNC_UART); /* tx */ gpio_set_function(GPIO_PIN_DBG_UART_RX, GPIO_FUNC_UART); /* rx */ bi_decl(bi_2pins_with_names(GPIO_PIN_DBG_UART_TX, "UART TX", GPIO_PIN_DBG_UART_RX, "UART RX")); /* char *hdr = "=" * (80-strlen("info : MAIN: ")); */ log_infoln("==================================================================="); cid_t cid = coroutine_add("init", init_cr, NULL); assert(cid); coroutine_main(); assert_notreached("all coroutines exited"); }