From d352b8c89de241e5b1a92ea11105ae9e91d0cdcf Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Sun, 3 Mar 2024 13:29:55 -0800 Subject: [PATCH] PWM setup & diagnostics (#5) This PR adds a library for controlling the am335x ehrpwm module. This is a work-in-progress, having demonstrated how to configure the basic settings and see a functional PWM output. This PR also demonstrates a failure to make PWM interrupt generation work for the PRU. Will post a forum question using this content. See comments in lib/pwm/am335x/pwm-am335x.c. --- .bazelrc | 7 +- .gitmodules | 3 + README.md | 19 +- arch/ti-pruss-intr.csv | 14 +- examples/BUILD | 46 ---- examples/example.c | 158 ------------- examples/led_outin/BUILD | 45 ++++ examples/led_outin/README.md | 1 + examples/led_outin/led_outin.c | 73 ++++++ examples/local.c | 26 --- examples/pwm_outin/BUILD | 47 ++++ examples/pwm_outin/README.md | 2 + examples/pwm_outin/pwm_outin.c | 91 ++++++++ examples/two_leds/BUILD | 45 ++++ examples/two_leds/README.md | 1 + examples/two_leds/two_leds.c | 79 +++++++ kernel/build.sh | 3 + kernel/compile.sh | 11 + kernel/docker/Dockerfile | 25 ++ kernel/run.sh | 15 ++ kernel/system.sh | 46 ++++ kernel/ti-linux-kernel-dev | 1 + lib/coroutine/coroutine.c | 87 ++++--- lib/coroutine/coroutine.h | 3 +- lib/debug/debug.c | 5 +- lib/gpio/am335x/BUILD | 4 + lib/gpio/am335x/gpio.c | 11 + lib/gpio/am335x/gpio.h | 3 + lib/gpio/gpio-defs.h | 6 +- lib/gpio/gpio.h | 21 +- lib/gpio/test32/BUILD | 2 +- lib/gpio/test32/gpio.cc | 3 + lib/gpio/test32/gpio_test.cc | 4 - lib/initproc/BUILD | 22 +- lib/initproc/initproc.c | 34 ++- lib/initproc/initproc.h | 5 +- lib/initproc/initproc_test.cc | 91 ++++++++ lib/intc/BUILD | 14 ++ lib/intc/am335x/README.md | 88 -------- lib/intc/am335x/intc.c | 52 +++-- lib/intc/intc-defs.h | 1 + lib/intc/service.c | 29 +++ lib/intc/service.h | 17 ++ lib/intc/test32/BUILD | 2 +- lib/intc/test32/intc.cc | 4 + lib/list/BUILD | 4 +- lib/log/daemon/BUILD | 2 + lib/log/daemon/daemon.c | 19 +- lib/log/daemon/daemon.h | 6 +- lib/log/daemon/daemon_test.cc | 21 +- lib/log/fmt/fmt.cc | 4 +- lib/log/journal/journal.c | 17 +- lib/log/journal/journal.h | 92 +++++++- lib/log/journal/journal_test.cc | 26 ++- lib/pwm/BUILD | 25 ++ lib/pwm/am335x/BUILD | 37 +++ lib/pwm/am335x/README.md | 88 ++++++++ lib/pwm/am335x/epwm0_intr_pend.yaml | 4 + lib/pwm/am335x/epwm1_intr_pend.yaml | 4 + lib/pwm/am335x/epwm2_intr_pend.yaml | 4 + lib/pwm/am335x/pwm-am335x.c | 213 ++++++++++++++++++ lib/pwm/am335x/pwm-am335x.h | 148 ++++++++++++ lib/pwm/pwm-defs.h | 21 ++ lib/pwm/pwm.h | 27 +++ lib/pwm/test32/BUILD | 24 ++ lib/pwm/test32/pwm-test32.cc | 12 + lib/pwm/test32/pwm-test32.h | 20 ++ lib/pwm/test32/pwm_test.cc | 13 ++ lib/resource/BUILD | 19 ++ lib/resource/table.c | 67 ++++++ lib/resource/table.h | 23 ++ lib/rpmsg/am335x/BUILD | 30 +++ .../toolchain => lib/rpmsg}/am335x/pru0.yaml | 0 .../toolchain => lib/rpmsg}/am335x/pru1.yaml | 2 +- lib/rpmsg/am335x/rpmsg.c | 8 +- lib/rpmsg/test32/BUILD | 1 + lib/rpmsg/test32/chan.h | 30 +-- lib/soc/BUILD | 3 + lib/soc/am335x/BUILD | 1 + lib/soc/am335x/sleep.s | 5 + lib/soc/am335x/soc.c | 13 +- lib/soc/am335x/soc.h | 10 +- lib/soc/soc-defs.h | 26 ++- lib/soc/soc.c | 11 + lib/soc/test32/BUILD | 2 +- lib/soc/test32/soc.cc | 11 +- lib/soc/test32/soc.h | 2 - lib/thread/thread.h | 9 +- lib/time/BUILD | 5 +- lib/time/am335x/BUILD | 10 +- lib/time/am335x/clock-am335x.c | 32 ++- lib/time/am335x/clock-am335x.h | 2 + lib/time/am335x/iep_tim_cap_cmp_pend.yaml | 3 +- lib/time/clock-defs.h | 2 + lib/time/clock.c | 11 +- lib/time/clock.h | 12 +- lib/time/clock_test.cc | 11 +- lib/time/process.c | 4 +- lib/time/test32/BUILD | 2 +- lib/time/test32/clock-test32.cc | 14 +- lib/time/test32/clock-test32.h | 2 + local.sh | 11 +- remote.sh | 21 +- save/cap/BUILD | 25 ++ save/cap/am335x/#cap-am335x.c# | 8 + save/cap/am335x/.#cap-am335x.c | 1 + save/cap/am335x/BUILD | 16 ++ save/cap/am335x/cap-am335x.c | 9 + save/cap/am335x/cap-am335x.h | 17 ++ save/cap/cap-defs.h | 19 ++ save/cap/cap.h | 27 +++ save/cap/test32/BUILD | 24 ++ save/cap/test32/cap-test32.cc | 9 + save/cap/test32/cap-test32.h | 20 ++ save/cap/test32/cap_test.cc | 13 ++ tools/cmd/irqgen/irqgen.go | 16 +- tools/cmd/supructl/main.go | 8 +- tools/internal/arch/irq.go | 2 +- tools/internal/arch/sysevt.go | 2 +- tools/internal/elfdata/elfdata.go | 39 +++- tools/internal/fwstate/BUILD | 8 + tools/internal/fwstate/fwstate.go | 98 ++++++++ tools/internal/remoteproc/remoteproc.go | 4 + tools/internal/rpmsghost/BUILD | 6 +- tools/internal/rpmsghost/rpmsghost.go | 108 ++++++--- tools/toolchain/BUILD | 26 ++- tools/toolchain/am335x/BUILD | 28 +-- 127 files changed, 2436 insertions(+), 609 deletions(-) create mode 100644 .gitmodules delete mode 100644 examples/BUILD delete mode 100644 examples/example.c create mode 100644 examples/led_outin/BUILD create mode 100644 examples/led_outin/README.md create mode 100644 examples/led_outin/led_outin.c delete mode 100644 examples/local.c create mode 100644 examples/pwm_outin/BUILD create mode 100644 examples/pwm_outin/README.md create mode 100644 examples/pwm_outin/pwm_outin.c create mode 100644 examples/two_leds/BUILD create mode 100644 examples/two_leds/README.md create mode 100644 examples/two_leds/two_leds.c create mode 100755 kernel/build.sh create mode 100755 kernel/compile.sh create mode 100644 kernel/docker/Dockerfile create mode 100755 kernel/run.sh create mode 100644 kernel/system.sh create mode 160000 kernel/ti-linux-kernel-dev create mode 100644 lib/gpio/am335x/gpio.c create mode 100644 lib/initproc/initproc_test.cc create mode 100644 lib/intc/service.c create mode 100644 lib/intc/service.h create mode 100644 lib/pwm/BUILD create mode 100644 lib/pwm/am335x/BUILD create mode 100644 lib/pwm/am335x/README.md create mode 100644 lib/pwm/am335x/epwm0_intr_pend.yaml create mode 100644 lib/pwm/am335x/epwm1_intr_pend.yaml create mode 100644 lib/pwm/am335x/epwm2_intr_pend.yaml create mode 100644 lib/pwm/am335x/pwm-am335x.c create mode 100644 lib/pwm/am335x/pwm-am335x.h create mode 100644 lib/pwm/pwm-defs.h create mode 100644 lib/pwm/pwm.h create mode 100644 lib/pwm/test32/BUILD create mode 100644 lib/pwm/test32/pwm-test32.cc create mode 100644 lib/pwm/test32/pwm-test32.h create mode 100644 lib/pwm/test32/pwm_test.cc create mode 100644 lib/resource/BUILD create mode 100644 lib/resource/table.c create mode 100644 lib/resource/table.h rename {tools/toolchain => lib/rpmsg}/am335x/pru0.yaml (100%) rename {tools/toolchain => lib/rpmsg}/am335x/pru1.yaml (89%) create mode 100644 lib/soc/am335x/sleep.s create mode 100644 lib/soc/soc.c create mode 100644 save/cap/BUILD create mode 100644 save/cap/am335x/#cap-am335x.c# create mode 120000 save/cap/am335x/.#cap-am335x.c create mode 100644 save/cap/am335x/BUILD create mode 100644 save/cap/am335x/cap-am335x.c create mode 100644 save/cap/am335x/cap-am335x.h create mode 100644 save/cap/cap-defs.h create mode 100644 save/cap/cap.h create mode 100644 save/cap/test32/BUILD create mode 100644 save/cap/test32/cap-test32.cc create mode 100644 save/cap/test32/cap-test32.h create mode 100644 save/cap/test32/cap_test.cc create mode 100644 tools/internal/fwstate/BUILD create mode 100644 tools/internal/fwstate/fwstate.go diff --git a/.bazelrc b/.bazelrc index e0d5181..9b0238a 100644 --- a/.bazelrc +++ b/.bazelrc @@ -13,16 +13,17 @@ build --spawn_strategy=standalone build:pru0 --crosstool_top=//tools/toolchain:clpru_suite build:pru0 --cpu=pru build:pru0 --host_crosstool_top=@bazel_tools//tools/cpp:toolchain -build:pru0 --platforms //tools/toolchain:real_pru +build:pru0 --platforms //tools/toolchain:real_pru0 build:pru0 --copt='-DSUPRUGLUE_AM335X' -build:pru1 --copt='-DSUPRUGLUE_PRU_NUM=0' +build:pru0 --copt='-DSUPRUGLUE_PRU_NUM=0' build:pru0 --copt='--opt_level=4' build:pru0 --copt='--opt_for_speed=0' + build:pru1 --crosstool_top=//tools/toolchain:clpru_suite build:pru1 --cpu=pru build:pru1 --host_crosstool_top=@bazel_tools//tools/cpp:toolchain -build:pru1 --platforms //tools/toolchain:real_pru +build:pru1 --platforms //tools/toolchain:real_pru1 build:pru1 --copt='-DSUPRUGLUE_AM335X' build:pru1 --copt='-DSUPRUGLUE_PRU_NUM=1' build:pru1 --copt='--opt_level=4' diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ce0d7aa --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "kernel/ti-linux-kernel-dev"] + path = kernel/ti-linux-kernel-dev + url = git@github.com:RobertCNelson/ti-linux-kernel-dev.git diff --git a/README.md b/README.md index 1ee7438..ba6ea78 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,8 @@ achieve success. In 2021, after nearly twenty years working for Big Tech companies I moved to the Mendocino coast and became the owner/operator of the Caspar, California community water system. With a background in -open-source software and specializing in telemetry systems, you -guessed it-- - -I'm writing Supruglue as the foundation for an open-source SCADA -system. +open-source software and specializing in telemetry systems, I'm +writing Supruglue as the foundation for an open-source SCADA system. ### WORK IN PROGRESS @@ -34,10 +31,8 @@ This repository is under development. The next steps are: -- Time support: timestamps, sleep & timer functions - Carveout support: setup regions for bulk data transfer -- Interrupt controller logic -- Drivers: eCAP, PWM, DMA, etc. +- Drivers: i2c, eCAP, PWM, DMA, etc. - Multi-PRU coordination. Sample application goals: @@ -70,15 +65,17 @@ To cross-compile for the Beaglebone ARM: bazel build --config=arm ... ``` -### Example +### Examples + +#### Two LEDs The example will alternatingly flash two LEDs on pins `P9_23` and `P9_25`. ``` -bazel build --config=pru0 //examples:example_pru0 +bazel build --config=pru0 //examples/two_leds:example_pru0 ``` -The firmware is located in `bazel-out/pru-fastbuild/bin/examples/example_pru0` +The firmware is located in `bazel-out/pru-fastbuild/bin/examples/two_leds/example_pru0` To build the Supruglue command-line interface, diff --git a/arch/ti-pruss-intr.csv b/arch/ti-pruss-intr.csv index 04a569d..7f9cef6 100644 --- a/arch/ti-pruss-intr.csv +++ b/arch/ti-pruss-intr.csv @@ -14,7 +14,7 @@ Event Number,Signal Name,Source,MII RT Signal Name 12,latch0_in,PRU-ICSS IEP,PRU-ICSS0 Internal Interrupts 13,sync1_out_pend,PRU-ICSS IEP,PRU-ICSS0 Internal Interrupts 14,sync0_out_pend,PRU-ICSS IEP,PRU-ICSS0 Internal Interrupts -15,pr1_ecap_intr_req,PRU-ICSS eCAP,PRU-ICSS0 Internal Interrupts +15,pr1_pru_ecap_intr_req,PRU-ICSS eCAP,PRU-ICSS0 Internal Interrupts 16,pr1_pru_mst_intr[0]_intr_req,pru0 or pru1,PRU-ICSS0 Internal Interrupts 17,pr1_pru_mst_intr[1]_intr_req,pru0 or pru1,PRU-ICSS0 Internal Interrupts 18,pr1_pru_mst_intr[2]_intr_req,pru0 or pru1,PRU-ICSS0 Internal Interrupts @@ -34,18 +34,18 @@ Event Number,Signal Name,Source,MII RT Signal Name 32,nirq,UART1,PRU-ICSS0 PRU0_RX_ERR 33,mcasp_x_intr[0]_pend,McASP1,Tx PRU-ICSS0 PRU0_RX_ERR32 34,mcasp_r_intr[0]_pend,McASP1,Rx PRU-ICSS0 PRU0_RX_SFD -35,ecap_intr[0]_intr_pend,eCAP1,PRU-ICSS0 PRU0_RX_SOF -36,ecap_intr[1]_intr_pend,eCAP2,PRU-ICSS0 PRU0_RX_CRC -37,epwm_intr[0]_intr_pend,eHRPWM2,PRU-ICSS0 PRU0_RX_NIBBLE_ODD +35,ecap1_intr_pend,eCAP1,PRU-ICSS0 PRU0_RX_SOF +36,ecap2_intr_pend,eCAP2,PRU-ICSS0 PRU0_RX_CRC +37,epwm2_intr_pend,eHRPWM2,PRU-ICSS0 PRU0_RX_NIBBLE_ODD 38,dcan_uerr,DCAN0,PRU-ICSS0 PRU0_RX_OVERFLOW 39,dcan_int1,DCAN0,PRU-ICSS0 PORT0_TX_UNDERFLOW 40,dcan_intr,DCAN0,PRU-ICSS0 PORT0_TX_OVERFLOW 41,POINTRPEND,I2C0,PRU-ICSS0 MDIO_MII_LINK[0] -42,ecap_intr[2]_intr_pend,eCAP0,PRU-ICSS0 PRU0_RX_EOF -43,epwm_intr[1]_intr_pend,eHRPWM0,(pr1_mii0_col & pr1_mii0_txen) (external) +42,ecap0_intr_pend,eCAP0,PRU-ICSS0 PRU0_RX_EOF +43,epwm0_intr_pend,eHRPWM0,(pr1_mii0_col & pr1_mii0_txen) (external) 44,SINTERRUPTN,McSPI0,PRU-ICSS0 PRU1_RX_ERR 45,eqep_intr_intr_pend,eQEP0,PRU-ICSS0 PRU1_RX_ERR32 -46,epwm_intr[2]_intr_pend,eHRPWM1,PRU-ICSS0 PRU1_RX_SFD +46,epwm1_intr_pend,eHRPWM1,PRU-ICSS0 PRU1_RX_SFD 47,c0_misc_pend,3PGSW (GEMAC),PRU-ICSS0 PRU1_RX_SOF 48,c0_tx_pend,3PGSW (GEMAC),PRU-ICSS0 PRU1_RX_CRC 49,c0_rx_pend,3PGSW (GEMAC),PRU-ICSS0 PRU1_RX_NIBBLE_ODD diff --git a/examples/BUILD b/examples/BUILD deleted file mode 100644 index b336b34..0000000 --- a/examples/BUILD +++ /dev/null @@ -1,46 +0,0 @@ -# -*- Mode: Python -*- -cc_binary( - name = "example_pru0", - srcs = [ - "example.c", - ":irqgen", - ], - additional_linker_inputs = [ - "//tools/toolchain/am335x:pru0_cmd", - ], - linkopts = [ - "$(location //tools/toolchain/am335x:pru0_cmd)", - "--stack_size=0x1000", - ], - deps = [ - "//lib/coroutine", - "//lib/gpio", - "//lib/initproc", - "//lib/log/daemon", - "//lib/rpmsg", - "//lib/time:process", - "//tools/toolchain/am335x", - ], -) - -# cc_binary( -# name = "local", -# srcs = ["local.c"], -# deps = [ -# "//lib/coroutine", -# ], -# ) - -genrule( - name = "irqgen", - srcs = [ - "//tools/toolchain/am335x:pru0_irq", - "//lib/time/am335x:iep_tim_cap_cmp_pend", - "//arch:sysevts", - ], - outs = [ - "rsc.c", - ], - cmd = "./$(location //tools/cmd/irqgen:irqgen) $(location //arch:sysevts) $(location //tools/toolchain/am335x:pru0_irq) $(location //lib/time/am335x:iep_tim_cap_cmp_pend) > $@", - tools = ["//tools/cmd/irqgen"], -) diff --git a/examples/example.c b/examples/example.c deleted file mode 100644 index 6ed09d2..0000000 --- a/examples/example.c +++ /dev/null @@ -1,158 +0,0 @@ -#include -#include -#include - -#include "external/ti-pru-support/include/am335x/pru_cfg.h" -#include "external/ti-pru-support/include/pru_virtio_ids.h" -#include "lib/args/args.h" -#include "lib/coroutine/coroutine.h" -#include "lib/debug/debug.h" -#include "lib/gpio/gpio.h" -#include "lib/initproc/initproc.h" -#include "lib/log/daemon/daemon.h" -#include "lib/pinmap/pinmap.h" -#include "lib/rpmsg/rpmsg.h" -#include "lib/time/clock.h" -#include "lib/time/process.h" - -#define NUM_RESOURCES 1 - -// my_resource_table describes the custom hardware settings used by -// this program. -struct my_resource_table { - struct resource_table base; - - uint32_t offset[NUM_RESOURCES]; // Should match 'num' in actual definition - - struct fw_rsc_vdev rpmsg_vdev; // Resource 0 - struct fw_rsc_vdev_vring rpmsg_vring0; // (cont) - struct fw_rsc_vdev_vring rpmsg_vring1; // (cont) -}; - -// Set in resourceTable.rpmsg_vdev.status when the kernel is ready. -#define VIRTIO_CONFIG_S_DRIVER_OK ((uint32_t)1 << 2) - -// Sizes of the virtqueues (expressed in number of buffers supported, -// and must be power of 2) -#define PRU_RPMSG_VQ0_SIZE 16 -#define PRU_RPMSG_VQ1_SIZE 16 - -// The feature bitmap for virtio rpmsg -#define VIRTIO_RPMSG_F_NS 0 // name service notifications - -// This firmware supports name service notifications as one of its features. -#define RPMSG_PRU_C0_FEATURES (1 << VIRTIO_RPMSG_F_NS) - -#pragma DATA_SECTION(resourceTable, ".resource_table") -#pragma RETAIN(resourceTable) -struct my_resource_table resourceTable = { - // resource_table base - { - 1, // Resource table version: only version 1 is supported - NUM_RESOURCES, // Number of entries in the table (equals length of offset field). - 0, 0, // Reserved zero fields - }, - // Entry offsets - { - offsetof(struct my_resource_table, rpmsg_vdev), - }, - // RPMsg virtual device - { - (uint32_t)TYPE_VDEV, // type - (uint32_t)VIRTIO_ID_RPMSG, // id - (uint32_t)0, // notifyid - (uint32_t)RPMSG_PRU_C0_FEATURES, // dfeatures - (uint32_t)0, // gfeatures - (uint32_t)0, // config_len - (uint8_t)0, // status - (uint8_t)2, // num_of_vrings, only two is supported - {(uint8_t)0, (uint8_t)0}, // reserved - }, - // The two vring structs must be packed after the vdev entry. - { - FW_RSC_ADDR_ANY, // da, will be populated by host, can't pass it in - 16, // align (bytes), - PRU_RPMSG_VQ0_SIZE, // num of descriptors - 0, // notifyid, will be populated, can't pass right now - 0 // reserved - }, - { - FW_RSC_ADDR_ANY, // da, will be populated by host, can't pass it in - 16, // align (bytes), - PRU_RPMSG_VQ1_SIZE, // num of descriptors - 0, // notifyid, will be populated, can't pass right now - 0 // reserved - }, -}; - -// Note: the argument is cycles / 5 because 5 ns cycle -#define BLUE_PERIOD (2000000000U / 5) -#define YELLOW_PERIOD (1000000000U / 5) - -void toggle_blue(ThreadID tid, Args args) { - gpio_pin pin = GPIO_PIN(P9_23); - PRULOG_2U(INFO, "starting blue half-cycle %uns", BLUE_PERIOD, 0); - while (1) { - PRULOG_2U(INFO, "blue on", 0, 0); - - GPIO_SetPin(pin, 1); - Sleep(BLUE_PERIOD); - - PRULOG_2U(INFO, "blue off", 0, 0); - - GPIO_SetPin(pin, 0); - Sleep(BLUE_PERIOD); - } -} - -void toggle_yellow(ThreadID tid, Args args) { - gpio_pin pin = GPIO_PIN(P9_25); - PRULOG_2U(INFO, "starting yellow half-cycle %uns", YELLOW_PERIOD, 0); - while (1) { - PRULOG_2U(INFO, "yellow on", 0, 0); - - GPIO_SetPin(pin, 1); - Sleep(YELLOW_PERIOD); - - PRULOG_2U(INFO, "yellow off", 0, 0); - - GPIO_SetPin(pin, 0); - Sleep(YELLOW_PERIOD); - } -} - -SUPRUGLUE_DEFINE_THREAD(syslog, 256); -SUPRUGLUE_DEFINE_THREAD(init, 256); -SUPRUGLUE_DEFINE_THREAD(blue, 256); -SUPRUGLUE_DEFINE_THREAD(yellow, 256); - -int main(void) { - - Args args1; - Args args2; - int err = 0; - - Init(NewSystemConfig()); - - ClockInit(); - - err = RpmsgInit(&__transport, &resourceTable.rpmsg_vdev, &resourceTable.rpmsg_vring0, &resourceTable.rpmsg_vring1); - if (err != 0) { - // @@@ this has failed b/c wrong event number... what would we do? - // (q: why compile-in such checks? is there a way to panic?) - } - - TimeStart(); - - args1.ptr = "1"; - args2.ptr = "0"; - - err = Create(&init.thread, InitProcess, args1, "init", sizeof(init.space)); - err = Create(&syslog.thread, SyslogProcess, args2, "syslog", sizeof(syslog.space)); - - err = Create(&blue.thread, toggle_blue, args2, "blue", sizeof(blue.space)); - err = Create(&yellow.thread, toggle_yellow, args2, "yellow", sizeof(yellow.space)); - - err = Run(); - return err; -} diff --git a/examples/led_outin/BUILD b/examples/led_outin/BUILD new file mode 100644 index 0000000..1338ffc --- /dev/null +++ b/examples/led_outin/BUILD @@ -0,0 +1,45 @@ +# -*- Mode: Python -*- +cc_binary( + name = "led_outin", + srcs = [ + "led_outin.c", + ":irqgen", + ], + additional_linker_inputs = [ + "//tools/toolchain/am335x:linker_cmd", + ], + linkopts = [ + "$(location //tools/toolchain/am335x:linker_cmd)", + "--stack_size=0x1000", + ], + deps = [ + "//lib/coroutine", + "//lib/gpio", + "//lib/initproc", + "//lib/intc:service", + "//lib/log/daemon", + "//lib/rpmsg", + "//lib/time:process", + "//lib/resource", + "//tools/toolchain/am335x", + ], +) + +genrule( + name = "irqgen", + srcs = [ + # Order is important: args[0] is all events + # Remaining args define arriving system events. + # (Only) one should set the host. + "//arch:sysevts", + "//lib/rpmsg/am335x:irq", + "//lib/time/am335x:iep_tim_cap_cmp_pend", + ], + outs = [ + "irq.c", + ], + cmd = "./$(location //tools/cmd/irqgen:irqgen) $(SRCS) > $@", + tools = [ + "//tools/cmd/irqgen", + ], +) diff --git a/examples/led_outin/README.md b/examples/led_outin/README.md new file mode 100644 index 0000000..de7ff07 --- /dev/null +++ b/examples/led_outin/README.md @@ -0,0 +1 @@ +This example flashes a LED on pin P9_25 and reads the value on P9_23. diff --git a/examples/led_outin/led_outin.c b/examples/led_outin/led_outin.c new file mode 100644 index 0000000..18a6db7 --- /dev/null +++ b/examples/led_outin/led_outin.c @@ -0,0 +1,73 @@ +#include +#include +#include + +#include "lib/args/args.h" +#include "lib/coroutine/coroutine.h" +#include "lib/debug/debug.h" +#include "lib/gpio/gpio.h" +#include "lib/initproc/initproc.h" +#include "lib/intc/service.h" +#include "lib/log/daemon/daemon.h" +#include "lib/pinmap/pinmap.h" +#include "lib/resource/table.h" +#include "lib/rpmsg/rpmsg.h" +#include "lib/time/clock.h" +#include "lib/time/process.h" + +// Note: the argument is cycles / 5 because 5 ns cycle +#define PERIOD (2000000000U / 5) + +void toggle_blue(ThreadID tid, Args args) { + gpio_pin pin = GPIO_PIN(P9_23); + PRULOG_1u32(INFO, "starting blue half-cycle %uns", PERIOD); + + Timestamp clock; + ReadClock(&clock); + while (1) { + PRULOG_0(INFO, "on"); + + GPIO_SetPin(pin, 1); + SleepUntil(&clock, PERIOD); + + PRULOG_0(INFO_NOYIELD, "off"); + GPIO_SetPin(pin, 0); + SleepUntil(&clock, PERIOD); + } +} + +void read_yellow(ThreadID tid, Args args) { + gpio_pin pin = GPIO_PIN(P9_25); + + PRULOG_1u32(INFO, "starting yellow reader %uns", PERIOD / 2); + + Timestamp clock; + ReadClock(&clock); + while (1) { + uint32_t value = GPIO_GetPin(pin); + PRULOG_1u32(INFO_NOYIELD, "yellow is %u", value); + SleepUntil(&clock, PERIOD / 2); + } +} + +SUPRUGLUE_DEFINE_THREAD(blue, 256); +SUPRUGLUE_DEFINE_THREAD(yellow, 256); + +int main(void) { + Args args; + + Init(NewSystemConfig()); + InterruptServiceInit(); + ClockInit(); + RpmsgInit(&__transport, &resourceTable.rpmsg_vdev, &resourceTable.rpmsg_vring0, &resourceTable.rpmsg_vring1); + GPIO_Init(); + SyslogInit(); + ProcessInit(); + + args.ptr = "0"; + + Create(&blue.thread, toggle_blue, args, "blue", sizeof(blue.space)); + Create(&yellow.thread, read_yellow, args, "yellow", sizeof(yellow.space)); + + return Run(); +} diff --git a/examples/local.c b/examples/local.c deleted file mode 100644 index f516c5b..0000000 --- a/examples/local.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "lib/coroutine/coroutine.h" -#include -#include -#include - -void Thread1(ThreadID thid, Args args) { - while (1) { - printf("arg %s\n", args.ptr); - Yield(); - } -} - -SUPRUGLUE_DEFINE_THREAD(th1, 256); -SUPRUGLUE_DEFINE_THREAD(th2, 256); - -int main() { - Args args1 = {.ptr = "1"}; - Args args2 = {.ptr = "2"}; - - Init(NewSystemConfig()); - - Create(&th1.thread, Thread1, args1, "th1", sizeof(th1.space)); - Create(&th2.thread, Thread1, args2, "th2", sizeof(th2.space)); - - return Run(); -} diff --git a/examples/pwm_outin/BUILD b/examples/pwm_outin/BUILD new file mode 100644 index 0000000..cc77b07 --- /dev/null +++ b/examples/pwm_outin/BUILD @@ -0,0 +1,47 @@ +# -*- Mode: Python -*- +cc_binary( + name = "pwm_outin", + srcs = [ + "pwm_outin.c", + ":irqgen", + ], + additional_linker_inputs = [ + "//tools/toolchain/am335x:linker_cmd", + ], + linkopts = [ + "$(location //tools/toolchain/am335x:linker_cmd)", + "--stack_size=0x1000", + ], + deps = [ + "//lib/coroutine", + "//lib/gpio", + "//lib/initproc", + "//lib/intc:service", + "//lib/log/daemon", + "//lib/rpmsg", + "//lib/pwm", + "//lib/time:process", + "//lib/resource", + "//tools/toolchain/am335x", + ], +) + +genrule( + name = "irqgen", + srcs = [ + # Order is important: args[0] is all events + # Remaining args define arriving system events. + # (Only) one should set the host. + "//arch:sysevts", + "//lib/rpmsg/am335x:irq", + "//lib/time/am335x:iep_tim_cap_cmp_pend", + "//lib/pwm/am335x:epwm1_intr_pend", + ], + outs = [ + "irq.c", + ], + cmd = "./$(location //tools/cmd/irqgen:irqgen) $(SRCS) > $@", + tools = [ + "//tools/cmd/irqgen", + ], +) diff --git a/examples/pwm_outin/README.md b/examples/pwm_outin/README.md new file mode 100644 index 0000000..baba287 --- /dev/null +++ b/examples/pwm_outin/README.md @@ -0,0 +1,2 @@ +This example configures a 50% duty cycle output on EPWM1 on EPWMxA +(`P9_14`) and reads the value via GPIO `P9_12` at 75% of the period. diff --git a/examples/pwm_outin/pwm_outin.c b/examples/pwm_outin/pwm_outin.c new file mode 100644 index 0000000..642873c --- /dev/null +++ b/examples/pwm_outin/pwm_outin.c @@ -0,0 +1,91 @@ +#include +#include +#include + +#include "external/ti-pru-support/include/am335x/pru_intc.h" // @@@ +#include "external/ti-pru-support/include/am335x/sys_pwmss.h" + +#include "lib/args/args.h" +#include "lib/coroutine/coroutine.h" +#include "lib/debug/debug.h" +#include "lib/gpio/gpio.h" +#include "lib/initproc/initproc.h" +#include "lib/intc/intc.h" +#include "lib/intc/service.h" +#include "lib/log/daemon/daemon.h" +#include "lib/log/journal/journal.h" +#include "lib/pinmap/pinmap.h" +#include "lib/pwm/pwm.h" +#include "lib/resource/table.h" +#include "lib/rpmsg/rpmsg.h" +#include "lib/soc/sysevts.h" +#include "lib/time/clock.h" +#include "lib/time/process.h" + +#define PERIOD (2000000000U / 5) + +void pwmHandler(void) { + gpio_pin pin = GPIO_PIN(P9_12); + uint32_t value = GPIO_GetPin(pin); + PRULOG_1u32(INFO, "interrupt EPWM1 output A", value); + PWM_ClearInterrupt(); +} + +void runBlue(ThreadID tid, Args args) { + gpio_pin pin = GPIO_PIN(P9_12); + + PRULOG_1u32(INFO, "starting reader %uns", PERIOD / 2); + PWM_ClearInterrupt(); + + Timestamp clock; + ReadClock(&clock); + while (1) { + uint32_t value = GPIO_GetPin(pin); + + // uint32_t val1 = PWMSS1.EPWM_TBCNT; + // uint32_t val2 = PWMSS1.EPWM_ETPS; + // uint32_t val2 = PWMSS1.EPWM_ETFLG; + // uint32_t val2 = PWMSS1.EPWM_ETSEL; + // uint32_t val2 = PWMSS1.EPWM_CMPB; + // uint32_t val2 = EDMA_BASE[SHADOW1(EDMAREG_SERH)]; + uint32_t val1 = EDMA_BASE[SHADOW1(EDMAREG_IER)]; + uint32_t val2 = EDMA_BASE[SHADOW1(EDMAREG_IERH)]; + + val1 = CT_INTC.ESR0; + val2 = CT_INTC.ESR1; + + PRULOG_2u32(INFO_NOYIELD, "val1 %u val2 %u", val1, val2); + + PWM_ClearInterrupt(); + + SleepUntil(&clock, PERIOD / 2); + } +} + +SUPRUGLUE_DEFINE_THREAD(blue, 256); + +int main(void) { + Args args; + + Init(NewSystemConfig()); + PWM_Init(); + InterruptServiceInit(); + ClockInit(); + RpmsgInit(&__transport, &resourceTable.rpmsg_vdev, &resourceTable.rpmsg_vring0, &resourceTable.rpmsg_vring1); + GPIO_Init(); + SyslogInit(); + ProcessInit(); + + args.ptr = "0"; + + Create(&blue.thread, runBlue, args, "blue", sizeof(blue.space)); + + InterruptHandlerInit(SYSEVT_EPWM1_INTR_PEND, pwmHandler); + + PWM_Enable(); + + // @@@ TODO need to add this call in other tests, examples... + ControllerEnable(); + + return Run(); +} diff --git a/examples/two_leds/BUILD b/examples/two_leds/BUILD new file mode 100644 index 0000000..2080298 --- /dev/null +++ b/examples/two_leds/BUILD @@ -0,0 +1,45 @@ +# -*- Mode: Python -*- +cc_binary( + name = "two_leds", + srcs = [ + "two_leds.c", + ":irqgen", + ], + additional_linker_inputs = [ + "//tools/toolchain/am335x:linker_cmd", + ], + linkopts = [ + "$(location //tools/toolchain/am335x:linker_cmd)", + "--stack_size=0x1000", + ], + deps = [ + "//lib/coroutine", + "//lib/gpio", + "//lib/initproc", + "//lib/intc:service", + "//lib/log/daemon", + "//lib/rpmsg", + "//lib/time:process", + "//lib/resource", + "//tools/toolchain/am335x", + ], +) + +genrule( + name = "irqgen", + srcs = [ + # Order is important: args[0] is all events + # Remaining args define arriving system events. + # (Only) one should set the host. + "//arch:sysevts", + "//lib/rpmsg/am335x:irq", + "//lib/time/am335x:iep_tim_cap_cmp_pend", + ], + outs = [ + "irq.c", + ], + cmd = "./$(location //tools/cmd/irqgen:irqgen) $(SRCS) > $@", + tools = [ + "//tools/cmd/irqgen", + ], +) diff --git a/examples/two_leds/README.md b/examples/two_leds/README.md new file mode 100644 index 0000000..da2dda5 --- /dev/null +++ b/examples/two_leds/README.md @@ -0,0 +1 @@ +This example flashes two LEDs on pins P9_25 and P9_23. diff --git a/examples/two_leds/two_leds.c b/examples/two_leds/two_leds.c new file mode 100644 index 0000000..91e8e81 --- /dev/null +++ b/examples/two_leds/two_leds.c @@ -0,0 +1,79 @@ +#include +#include +#include + +#include "lib/args/args.h" +#include "lib/coroutine/coroutine.h" +#include "lib/debug/debug.h" +#include "lib/gpio/gpio.h" +#include "lib/initproc/initproc.h" +#include "lib/intc/service.h" +#include "lib/log/daemon/daemon.h" +#include "lib/pinmap/pinmap.h" +#include "lib/resource/table.h" +#include "lib/rpmsg/rpmsg.h" +#include "lib/time/clock.h" +#include "lib/time/process.h" + +// Note: the argument is cycles / 5 because 5 ns cycle +#define BLUE_PERIOD (2000000000U / 5) +#define YELLOW_PERIOD (1000000000U / 5) + +void toggle_blue(ThreadID tid, Args args) { + gpio_pin pin = GPIO_PIN(P9_23); + PRULOG_1u32(INFO, "starting blue half-cycle %uns", BLUE_PERIOD); + + Timestamp clock; + ReadClock(&clock); + while (1) { + PRULOG_0(INFO, "on"); + + GPIO_SetPin(pin, 1); + SleepUntil(&clock, BLUE_PERIOD); + + PRULOG_0(INFO, "off"); + + GPIO_SetPin(pin, 0); + SleepUntil(&clock, BLUE_PERIOD); + } +} + +void toggle_yellow(ThreadID tid, Args args) { + gpio_pin pin = GPIO_PIN(P9_25); + PRULOG_1u32(INFO, "starting yellow half-cycle %uns", YELLOW_PERIOD); + + Timestamp clock; + ReadClock(&clock); + while (1) { + PRULOG_0(INFO, "on"); + + GPIO_SetPin(pin, 1); + SleepUntil(&clock, YELLOW_PERIOD); + + PRULOG_0(INFO, "off"); + + GPIO_SetPin(pin, 0); + SleepUntil(&clock, YELLOW_PERIOD); + } +} + +SUPRUGLUE_DEFINE_THREAD(blue, 256); +SUPRUGLUE_DEFINE_THREAD(yellow, 256); + +int main(void) { + Args args; + + Init(NewSystemConfig()); + InterruptServiceInit(); + ClockInit(); + RpmsgInit(&__transport, &resourceTable.rpmsg_vdev, &resourceTable.rpmsg_vring0, &resourceTable.rpmsg_vring1); + SyslogInit(); + ProcessInit(); + + args.ptr = "0"; + + Create(&blue.thread, toggle_blue, args, "blue", sizeof(blue.space)); + Create(&yellow.thread, toggle_yellow, args, "yellow", sizeof(yellow.space)); + + return Run(); +} diff --git a/kernel/build.sh b/kernel/build.sh new file mode 100755 index 0000000..a520119 --- /dev/null +++ b/kernel/build.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +docker build -t supruglue docker diff --git a/kernel/compile.sh b/kernel/compile.sh new file mode 100755 index 0000000..9c8c534 --- /dev/null +++ b/kernel/compile.sh @@ -0,0 +1,11 @@ +#!/bin/bash -e + +cd /supruglue/kernel/ti-linux-kernel-dev + +echo Running build_deb.sh in `pwd` + +#./build_kernel.sh +#./build_deb.sh +./tools/rebuild_deb.sh + +echo Exited: build_deb.sh: $? diff --git a/kernel/docker/Dockerfile b/kernel/docker/Dockerfile new file mode 100644 index 0000000..c8d605a --- /dev/null +++ b/kernel/docker/Dockerfile @@ -0,0 +1,25 @@ +FROM debian:bullseye-slim + +RUN apt-get -y update +RUN apt-get -y install gcc-arm-linux-gnueabihf +RUN apt-get -y install git +RUN apt-get -y install cpio +RUN apt-get -y install lz4 +RUN apt-get -y install bc +RUN apt-get -y install build-essential +RUN apt-get -y install fakeroot +RUN apt-get -y install lsb-release +RUN apt-get -y install lzma +RUN apt-get -y install man-db +RUN apt-get -y install gettext +RUN apt-get -y install bison +RUN apt-get -y install flex +RUN apt-get -y install pkg-config +RUN apt-get -y install libmpc-dev +RUN apt-get -y install u-boot-tools +RUN apt-get -y install libncurses-dev:amd64 +RUN apt-get -y install libssl-dev:amd64 +RUN apt-get -y install wget +RUN apt-get -y install rsync +RUN apt-get -y install kmod + diff --git a/kernel/run.sh b/kernel/run.sh new file mode 100755 index 0000000..6c94f78 --- /dev/null +++ b/kernel/run.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +docker run \ + -v ${HOME}/src/supruglue:/supruglue \ + -ti \ + supruglue \ + /supruglue/kernel/compile.sh + + +# docker run \ ✔ 21:10:34 +# -v ${HOME}/src/supruglue/kernel:/supruglue \ +# -ti \ +# supruglue \ +# bash + diff --git a/kernel/system.sh b/kernel/system.sh new file mode 100644 index 0000000..b47c8bc --- /dev/null +++ b/kernel/system.sh @@ -0,0 +1,46 @@ +#!/bin/sh +#copy as "system.sh" and tweak for your system + +ARCH=$(uname -m) + +#ARM Native gcc compiler (running gcc on arm target) +if [ "x${ARCH}" = "xarmv7l" ] || [ "x${ARCH}" = "xaarch64" ] ; then + #Native arm gcc compiler + CC= +fi + +###REQUIRED: + +#ARM GCC CROSS Compiler: +#if CC is not set, a known working linaro based gcc compiler will be downloaded and utilized. +#CC=/bin/arm-none-eabi- +#CC=/bin/arm-linux-gnueabi- +CC=/usr/bin/arm-linux-gnueabihf- + +###OPTIONAL: + +###OPTIONAL: CORES: number of CPU cores to use for compilation +#CORES=4 + +###OPTIONAL: LINUX_GIT: specify location of locally cloned git tree. +# +#LINUX_GIT=/supruglue/kernel/linux/ + +###OPTIONAL: MMC: (REQUIRED FOR RUNNING: tools/install_kernel.sh) +#Note: This operates on raw disks, NOT PARTITIONS.. +# +#WRONG: MMC=/dev/mmcblk0p1 +#CORRECT: MMC=/dev/mmcblk0 +# +#WRONG: MMC=/dev/sde1 +#CORRECT: MMC=/dev/sde +# +#MMC=/dev/sde + +###ADVANCED: RUN_BISECT: used with ./scripts/bisect.sh +# +#RUN_BISECT=1 + +###ADVANCED: AUTO_BUILD: Easier integration with Jenkins/Buildbot/etc.. +# +#AUTO_BUILD=1 diff --git a/kernel/ti-linux-kernel-dev b/kernel/ti-linux-kernel-dev new file mode 160000 index 0000000..c68b848 --- /dev/null +++ b/kernel/ti-linux-kernel-dev @@ -0,0 +1 @@ +Subproject commit c68b848f0d2f702ba8e91a87efaa678b80c7101d diff --git a/lib/coroutine/coroutine.c b/lib/coroutine/coroutine.c index ca6f327..81c0b21 100755 --- a/lib/coroutine/coroutine.c +++ b/lib/coroutine/coroutine.c @@ -8,13 +8,13 @@ #include "lib/coroutine/coroutine.h" #include "lib/debug/debug.h" -#include "lib/intc/intc.h" +#include "lib/time/clock.h" System __system; SystemConfig NewSystemConfig(void) { return (SystemConfig){ - .unused = 0, + .export_interval = 10 * TIME_SECOND, }; } @@ -25,7 +25,7 @@ int Init(SystemConfig cfg) { SystemOnChipSetup(); ThreadListInit(&__system_runnable); JournalInit(&sys->journal); - ControllerInit(); + __system_shutdown = 0; return 0; } @@ -36,30 +36,27 @@ int Create(Thread *thread, ThreadFunc *func, Args args, const char *name, size_t thread->exec.call.func = func; thread->exec.call.args = args; thread->state = TS_STARTING; + thread->allthreads = __system.allthreads; + __system.allthreads = thread; ThreadListPushBack(&__system_runnable, thread); return 0; } int __run(void) { - while (!SystemOnChipIsShutdown()) { - // TODO: use a blocking call when there are no runnables. Requires assembly - // to use the block-on-R31 instruction. - ServiceInterrupts(); + while (!__system_shutdown && !ThreadListEmpty(&__system_runnable)) { + Thread *volatile run = ThreadListPopFront(&__system_runnable); - if (ThreadListEmpty(&__system_runnable)) { - continue; - } + __system.run_stack_pos = (void *)&run; - Thread *volatile run = ThreadListPopFront(&__system_runnable); + TimedSwitch(); __system_current = run; - __system.run_stack_pos = (void *)&run; + // Control flow: + // 1. setjmp() returns JC_SETJUMP + // 2. switch (run->state) starts, resumes, or exits a thread + // 3. return here for non-JC_SETJUMP codes switch (setjmp(__system.return_jump)) { - case JC_SETJUMP: - // The return jump was prepared, break to the second switch with - // the current runnable thread. - break; case JC_SUSPEND: // Running thread yielded. ThreadListPushBack(&__system_runnable, run); @@ -70,26 +67,22 @@ int __run(void) { case JC_OVERFLOW: case JC_INTERNAL: // Running thread had an error. - run->state = TS_FINISHED; - break; - default: - break; - } - switch (run->state) { - case TS_STARTING: - // Run a new thread. - run->state = TS_RUNNING; - run->exec.call.func(TID(run), run->exec.call.args); - run->state = TS_FINISHED; - break; - case TS_RUNNING: - // Re-enter the yield call. - longjmp(run->exec.run_jump, JC_RESUME); - break; - // Function call returned - case TS_FINISHED: - // Error cases exit here. - break; + continue; + default: // e.g., JC_SETJUMP + switch (run->state) { + case TS_STARTING: + // Run a new thread. + run->state = TS_RUNNING; + run->exec.call.func(TID(run), run->exec.call.args); + continue; + case TS_RUNNING: + // Re-enter the yield call. + longjmp(run->exec.run_jump, JC_RESUME); + // Function call returned + continue; + default: + continue; + } } } return 0; @@ -102,23 +95,22 @@ void yieldInternal(JumpCode jc) { // Check for thread-stack overflow. if (size > __system_current->stack_size) { - PRULOG_2U(FATAL, "stack overflow: %u exceeds %u", size, __system_current->stack_size); + PRULOG_2u32(FATAL, "stack overflow: %u exceeds %u", size, __system_current->stack_size); longjmp(__system.return_jump, JC_OVERFLOW); } memcpy(__system_current->stack, yield_stack, size); - switch (setjmp(__system_current->exec.run_jump)) { - case JC_SETJUMP: + // Set return point. + if (setjmp(__system_current->exec.run_jump) == JC_SETJUMP) { + // Return control. longjmp(__system.return_jump, jc); - case JC_RESUME: - break; - default: - // assert(0); - break; } + // Resume control. - // size and yield_stack are not volatile, recompute: + // size and yield_stack are not volatile, but they have to be + // recomputed for other reasons. TODO: not clear why--is the stack + // copy off in size? yield_stack = (void *)&unused; size = (size_t)__system.run_stack_pos - (size_t)yield_stack; @@ -127,6 +119,11 @@ void yieldInternal(JumpCode jc) { int Run(void) { __system_yield = &yieldInternal; + + // set current for TimedSwitch() to avoid extra branches + __system_current = ThreadListFront(&__system_runnable); + TimeStart(); + int err = __run(); __system_yield = NULL; __system_current = NULL; diff --git a/lib/coroutine/coroutine.h b/lib/coroutine/coroutine.h index 9da66c5..bff4353 100755 --- a/lib/coroutine/coroutine.h +++ b/lib/coroutine/coroutine.h @@ -21,13 +21,14 @@ typedef struct _System System; typedef struct _SystemConfig SystemConfig; struct _SystemConfig { - int32_t unused; + int32_t export_interval; }; struct _System { SystemConfig cfg; jmp_buf return_jump; void *run_stack_pos; + Thread *allthreads; Journal journal; }; diff --git a/lib/debug/debug.c b/lib/debug/debug.c index f1a5be4..db56ab9 100644 --- a/lib/debug/debug.c +++ b/lib/debug/debug.c @@ -44,7 +44,6 @@ void flash(int n) { SystemOnChipDelay(30000000); uled1(1); uled2(1); - uled3(1); uled4(1); yellow(1); @@ -61,5 +60,9 @@ void solid(int n) { uled3(1); uled4(1); SystemOnChipDelay(200000000); + uled1(0); + uled2(0); + uled3(0); + uled4(0); } } diff --git a/lib/gpio/am335x/BUILD b/lib/gpio/am335x/BUILD index 5ad5787..b8cc475 100644 --- a/lib/gpio/am335x/BUILD +++ b/lib/gpio/am335x/BUILD @@ -6,7 +6,11 @@ cc_library( hdrs = [ "gpio.h", ], + srcs = [ + "gpio.c", + ], deps = [ "//lib/gpio:defs", + "//tools/toolchain/am335x", ], ) diff --git a/lib/gpio/am335x/gpio.c b/lib/gpio/am335x/gpio.c new file mode 100644 index 0000000..a4d687c --- /dev/null +++ b/lib/gpio/am335x/gpio.c @@ -0,0 +1,11 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#include "gpio.h" + +// void GPIO_Init(void) { +// GPIO_SetRegister(GPIO_BANK0, GPIOREG_SYSCONFIG, 2); // SOFTRESET +// GPIO_SetRegister(GPIO_BANK1, GPIOREG_SYSCONFIG, 2); // SOFTRESET +// GPIO_SetRegister(GPIO_BANK2, GPIOREG_SYSCONFIG, 2); // SOFTRESET +// GPIO_SetRegister(GPIO_BANK3, GPIOREG_SYSCONFIG, 2); // SOFTRESET +// } diff --git a/lib/gpio/am335x/gpio.h b/lib/gpio/am335x/gpio.h index d204ca8..4c5e62d 100644 --- a/lib/gpio/am335x/gpio.h +++ b/lib/gpio/am335x/gpio.h @@ -24,6 +24,9 @@ inline uint32_t GPIO_GetRegister(gpio_bank *g, int r) { return (uint32_t)g[r]; } +inline void GPIO_Init(void) { +} + #ifdef __cplusplus } #endif diff --git a/lib/gpio/gpio-defs.h b/lib/gpio/gpio-defs.h index f623fab..0d41e19 100644 --- a/lib/gpio/gpio-defs.h +++ b/lib/gpio/gpio-defs.h @@ -16,7 +16,7 @@ extern "C" { // generally here because they are just integers. Let's hope they're // the same in other platforms. -enum _GpioBankNumber { +enum _GpioFieldNumber { GPIOREG_REVISION = NUM_WORDS(0x0), // Section 25.4.1.1 GPIOREG_SYSCONFIG = NUM_WORDS(0x10), // Section 25.4.1.2 GPIOREG_EOI = NUM_WORDS(0x20), // Section 25.4.1.3 @@ -45,7 +45,7 @@ enum _GpioBankNumber { GPIOREG_SETDATAOUT = NUM_WORDS(0x194), // Section 25.4.1.26 }; -typedef enum _GpioBankNumber GpioBankNumber; +typedef enum _GpioFieldNumber GpioFieldNumber; typedef uint32_t gpio_bank; @@ -79,6 +79,8 @@ void GPIO_SetRegister(gpio_bank *g, int r, uint32_t v); uint32_t GPIO_GetRegister(gpio_bank *g, int r); +void GPIO_Init(void); + #ifdef __cplusplus } #endif diff --git a/lib/gpio/gpio.h b/lib/gpio/gpio.h index a19ccae..a3e5112 100644 --- a/lib/gpio/gpio.h +++ b/lib/gpio/gpio.h @@ -19,14 +19,29 @@ #ifdef __cplusplus extern "C" { #endif -static inline int GPIO_GetPin(gpio_pin pin) { - return (GPIO_GetRegister(pin.bank, GPIOREG_DATAIN) & (1 << pin.bit)) != 0; + +static inline uint32_t GPIO_GetPin(gpio_pin pin) { + // TODO: which is smaller? + // return (GPIO_GetRegister(pin.bank, GPIOREG_DATAIN) & (1 << pin.bit)) != 0; + return (GPIO_GetRegister(pin.bank, GPIOREG_DATAIN) & (1 << pin.bit)) == 0 ? 0 : 1; } -static inline void GPIO_SetPin(gpio_pin pin, int value) { +static inline void GPIO_SetPin(gpio_pin pin, uint32_t value) { GPIO_SetRegister(pin.bank, value ? GPIOREG_SETDATAOUT : GPIOREG_CLEARDATAOUT, 1 << pin.bit); } +// static inline void GPIO_EnableOutput(gpio_pin pin) { +// uint32_t current = GPIO_GetRegister(pin.bank, GPIOREG_OE); +// uint32_t mask = ~(1 << pin.bit); +// GPIO_SetRegister(pin.bank, GPIOREG_OE, current & mask); +// } + +// static inline void GPIO_EnableInput(gpio_pin pin) { +// uint32_t current = GPIO_GetRegister(pin.bank, GPIOREG_OE); +// uint32_t mask = 1 << pin.bit; +// GPIO_SetRegister(pin.bank, GPIOREG_OE, current & mask); +// } + #ifdef __cplusplus } #endif diff --git a/lib/gpio/test32/BUILD b/lib/gpio/test32/BUILD index f5d1265..4646950 100644 --- a/lib/gpio/test32/BUILD +++ b/lib/gpio/test32/BUILD @@ -15,7 +15,7 @@ cc_library( ) cc_test( - name = "gpio_test", + name = "test", srcs = ["gpio_test.cc"], deps = [ "//lib/gpio", diff --git a/lib/gpio/test32/gpio.cc b/lib/gpio/test32/gpio.cc index cebb986..5126b28 100644 --- a/lib/gpio/test32/gpio.cc +++ b/lib/gpio/test32/gpio.cc @@ -7,6 +7,9 @@ static uint32_t __gpio_banks[4]; +void GPIO_Init(void) { +} + void GPIO_SetRegister(gpio_bank *as_int, int r, uint32_t v) { intptr_t num = ((intptr_t)as_int) - 1; gpio_bank *g = &__gpio_banks[num]; diff --git a/lib/gpio/test32/gpio_test.cc b/lib/gpio/test32/gpio_test.cc index 6a276a2..319fcae 100644 --- a/lib/gpio/test32/gpio_test.cc +++ b/lib/gpio/test32/gpio_test.cc @@ -11,10 +11,6 @@ class GpioTest : public testing::Test { void SetUp() override { SystemOnChipSetup(); } - - void TearDown() override { - SystemOnChipShutdown(); - } }; TEST_F(GpioTest, Register) { diff --git a/lib/initproc/BUILD b/lib/initproc/BUILD index af6dc68..1e09bb7 100644 --- a/lib/initproc/BUILD +++ b/lib/initproc/BUILD @@ -10,9 +10,29 @@ cc_library( "initproc.h", ], deps = [ + "//lib/coroutine", "//lib/debug", "//lib/intc", + "//lib/log/journal", "//lib/rpmsg", - "//lib/thread", + "//lib/time", + ], +) + +cc_test( + name = "test", + srcs = [ + "initproc_test.cc", + ], + deps = [ + ":initproc", + "//lib/intc", + "//lib/intc:service", + "//lib/log/daemon", + "//lib/log/fmt", + "//lib/rpmsg/test32", + "//lib/time:process", + "@com_google_absl//absl/log", + "@com_google_googletest//:gtest_main", ], ) diff --git a/lib/initproc/initproc.c b/lib/initproc/initproc.c index 684c613..a82dcc5 100644 --- a/lib/initproc/initproc.c +++ b/lib/initproc/initproc.c @@ -2,24 +2,42 @@ // SPDX-License-Identifier: MIT #include "initproc.h" +#include "lib/coroutine/coroutine.h" #include "lib/debug/debug.h" #include "lib/intc/intc.h" #include "lib/rpmsg/rpmsg.h" #include "lib/soc/sysevts.h" +#include "lib/time/clock.h" -// EH? Unclear purpose. Is it important to call ClientRecv? Or does -// knowing the ARM process is ready to talk matter? - -void InitProcess(ThreadID thid, Args args) { +void initProcessThread(ThreadID thid, Args args) { for (;;) { - int err; + // Try to receive until it succeeds. This is necessary to get the + // destination ID for the ARM host. char buf[32]; uint16_t sz = sizeof(buf); - if ((err = ClientRecv(&__transport, buf, &sz)) != 0) { - Sleep(1000000); + if (ClientRecv(&__transport, buf, &sz) != 0) { + Sleep(TIME_SECOND / 2); + continue; } - Yield(); + // Note we do not exit this loop. We needed to do the above once + // per firmware start because there is only one host. + for (;;) { + Sleep(__system.cfg.export_interval); + + Thread *th; + for (th = __system.allthreads; th != NULL; th = th->allthreads) { + METLOG_2u64(th, "cpu.run.cycles=%u cpu.stall.cycles=%u", th->usage.run.CYCLES, th->usage.stall.CYCLES); + } + } } } + +SUPRUGLUE_DEFINE_THREAD(init, 512); + +int ProcessInit(void) { + Args args; + args.ptr = ""; + return Create(&init.thread, initProcessThread, args, "init", sizeof(init.space)); +} diff --git a/lib/initproc/initproc.h b/lib/initproc/initproc.h index a416c6d..dce9aa6 100644 --- a/lib/initproc/initproc.h +++ b/lib/initproc/initproc.h @@ -11,10 +11,7 @@ extern "C" { #endif -// Thread function that reads input messages from the ARM host, at a -// minimum handles RPMsg interrupts. - -void InitProcess(ThreadID thid, Args args); +int ProcessInit(void); #ifdef __cplusplus } diff --git a/lib/initproc/initproc_test.cc b/lib/initproc/initproc_test.cc new file mode 100644 index 0000000..dd372d1 --- /dev/null +++ b/lib/initproc/initproc_test.cc @@ -0,0 +1,91 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#include "initproc.h" +#include "lib/coroutine/coroutine.h" +#include "lib/intc/service.h" +#include "lib/log/daemon/daemon.h" +#include "lib/log/journal/journal.h" +#include "lib/rpmsg/rpmsg.h" +#include "lib/time/process.h" +#include "gtest/gtest.h" + +#include +#include +#include +#include + +using std::make_pair; +using std::pair; +using std::thread; +using std::unordered_map; +using std::vector; + +TEST(InitProc, Simple) { + auto tt = NewTestTransport(); + + EXPECT_EQ(0, Init(NewSystemConfig())); + + InterruptServiceInit(); + ClockInit(); + SyslogInit(); + ProcessInit(); + + // Count all threads + int thread_count = 0; + for (Thread *th = __system.allthreads; th != NULL; th = th->allthreads) { + thread_count++; + } + + unordered_map>> data; + + const int expectRounds = 3; + + thread client([tt, thread_count, &data] { + // Force it to sleep once. + HostTransientReceiveError(tt); + // Wake it up. + const char *msg = "wakeup"; + EXPECT_EQ(0, HostSend(tt, msg, strlen(msg))); + + int round; + int n; + + for (round = 0; round < expectRounds; round++) { + // expect one metric event per thread. + for (n = 0; n < thread_count;) { + Entry entry; + uint16_t blen = sizeof(entry); + EXPECT_EQ(0, HostRecv(tt, &entry, &blen)); + EXPECT_EQ(blen, sizeof(entry)); + + if ((entry.flags & JW_METRICS) == 0) { + continue; + } + + data[entry.tid].emplace_back(make_pair(entry.int1.U64, entry.int2.U64)); + n++; + } + } + + Shutdown(); + }); + + EXPECT_EQ(0, ::Run()); + + client.join(); + + for (auto &kv : data) { + auto &v = kv.second; + + // Less-than because every thread has to run for this test to make progress. + // The count is nanoseconds: the constant ensures the count starts from 0. + EXPECT_LT(v[0].first, 1000000000); + EXPECT_LT(v[0].first, v[1].first); + EXPECT_LT(v[1].first, v[2].first); + + EXPECT_EQ(v[0].first, v[0].second); + EXPECT_EQ(v[1].first, v[1].second); + EXPECT_EQ(v[2].first, v[2].second); + } +} diff --git a/lib/intc/BUILD b/lib/intc/BUILD index 5a796db..4798920 100644 --- a/lib/intc/BUILD +++ b/lib/intc/BUILD @@ -23,3 +23,17 @@ cc_library( "//conditions:default": ["//lib/intc/test32"], }), ) + +cc_library( + name = "service", + srcs = [ + "service.c", + ], + hdrs = [ + "service.h", + ], + deps = [ + ":intc", + "//lib/coroutine", + ], +) diff --git a/lib/intc/am335x/README.md b/lib/intc/am335x/README.md index fb7f904..ede73e0 100644 --- a/lib/intc/am335x/README.md +++ b/lib/intc/am335x/README.md @@ -245,91 +245,3 @@ one bit per host interrupt. These bits are updated when writing to the Host Interrupt Enable Index Set and Host Interrupt Enable Index Clear registers. - -Cheatsheet on PWMSS - -0h IDVER IP Revision Register Section 15.1.3.1 - -Version - -4h SYSCONFIG System Configuration Register Section 15.1.3.2 - -Reset, standby, idle, emulation stuff. - -8h CLKCONFIG Clock Configuration Register Section 15.1.3.3 - -Enable and stop the clock to the ePWM, eCAP, and eQEP. - -Ch CLKSTATUS Clock Status Register Section 15.1.3.4 - -Shows the above config changes (which are async?) - -Cheatsheet on PWM - - -0h TBCTL Time-Base Control Register Section 15.2.4.1 - -Clock divisor (rate 1/1 to 1/128); Phase direction (in up/down mode); -High-speed clock divisor; Software force sync; Sync output select; -Counter and Shadow modes - -2h TBSTS Time-Base Status Register Section 15.2.4.2 - -Latch for synchronization events; overflow; counter direction. - -4h TBPHSHR Extension for HRPWM Phase Register Section 15.2.4.3 - -High-res stuff. - -6h TBPHS Time-Base Phase Register Section 15.2.4.4 - -High-res phase. - -8h TBCNT Time-Base Counter Register Section 15.2.4.5 - -Time-based count (16 bits) - -Ah TBPRD Time-Base Period Register Section 15.2.4.6 - -The PERIOD of the coutner. When zero or max, ... - -Eh CMPCTL Counter-Compare Control Register Section 15.2.4.7 - -Shadowing, and when to load/latch. - -10h CMPAHR Extension for HRPWM Counter-Compare A Register Section 15.2.4.8 - -Compare-A high-res - -12h CMPA Counter-Compare A Register Section 15.2.4.9 - -Value for compare-A reg - -14h CMPB Counter-Compare B Register Section 15.2.4.10 - -Value for compare-A reg - -16h AQCTLA Action-Qualifier Control Register for Output A (EPWMxA) Section 15.2.4.11 -18h AQCTLB Action-Qualifier Control Register for Output B (EPWMxB) Section 15.2.4.12 -1Ah AQSFRC Action-Qualifier Software Force Register Section 15.2.4.13 -1Ch AQCSFRC Action-Qualifier Continuous S/W Force Register Set Section 15.2.4.14 - -1Eh DBCTL Dead-Band Generator Control Register Section 15.2.4.15 -20h DBRED Dead-Band Generator Rising Edge Delay Count Register Section 15.2.4.16 -22h DBFED Dead-Band Generator Falling Edge Delay Count Register Section 15.2.4.17 - -24h TZSEL Trip-Zone Select Register Section 15.2.4.18 -28h TZCTL Trip-Zone Control Register Section 15.2.4.19 -2Ah TZEINT Trip-Zone Enable Interrupt Register Section 15.2.4.20 -2Ch TZFLG Trip-Zone Flag Register Section 15.2.4.21 -2Eh TZCLR Trip-Zone Clear Register Section 15.2.4.22 -30h TZFRC Trip-Zone Force Register Section 15.2.4.23 - -32h ETSEL Event-Trigger Selection Register Section 15.2.4.24 -34h ETPS Event-Trigger Pre-Scale Register Section 15.2.4.25 -36h ETFLG Event-Trigger Flag Register Section 15.2.4.26 -38h ETCLR Event-Trigger Clear Register Section 15.2.4.27 -3Ah ETFRC Event-Trigger Force Register Section 15.2.4.28 - -3Ch PCCTL PWM-Chopper Control Register Section 15.2.4.29 -C0h HRCNFG HRPWM configuration register (HRCNFG) Section 15.2.4.30 diff --git a/lib/intc/am335x/intc.c b/lib/intc/am335x/intc.c index 4bec8dc..77261c2 100644 --- a/lib/intc/am335x/intc.c +++ b/lib/intc/am335x/intc.c @@ -8,44 +8,39 @@ #include "lib/intc/am335x/intc.h" #include "lib/soc/sysevts.h" -// Interrupt inputs set bits 30 and 31 in register R31. -#define PRU_HOST0_INTERRUPT 0x40000000 -#define PRU_HOST1_INTERRUPT 0x80000000 -#define PRU_HOST_ANY_INTERRUPT 0xc0000000 - volatile register uint32_t __R31; InterruptController __controller; +#if SUPRUGLUE_PRU_NUM == 0 +#define ARM_TO_PRU_EVT SYSEVT_PR1_PRU_MST_INTR1_INTR_REQ +#define ARM_TO_PRU_IRQ PRU_HOST0_INTERRUPT +#define HIPRIO_EVT CT_INTC.HIPIR0 +#else +#define ARM_TO_PRU_EVT SYSEVT_PR1_PRU_MST_INTR3_INTR_REQ +#define ARM_TO_PRU_IRQ PRU_HOST1_INTERRUPT +#define HIPRIO_EVT CT_INTC.HIPIR1 +#endif + void ControllerInit(void) { uint8_t evt; for (evt = 0; evt < NUM_SYSEVTS; evt++) { __controller.handler[evt] = NULL; } - // Disable interrupts until configured. + // Disable interrupts until enabled in ControllerEnable(). CT_INTC.GER_bit.EN_HINT_ANY = 0; // Clear pending system event enabled. All system events are disabled. + // TODO: this will clobber another PRU. should only handle events + // this PRU will use. CT_INTC.SECR0 = 0xffffffff; CT_INTC.SECR1 = 0xffffffff; - // Use EISR (indexed) or ESR (32bit) to enable system events. - // - // Note: fewer instructions, maybe, if we assemble a bit map and - // assign to ESR0/ESR1. - CT_INTC.EISR_bit.EN_SET_IDX = SYSEVT_PR1_PRU_MST_INTR1_INTR_REQ; - CT_INTC.EISR_bit.EN_SET_IDX = SYSEVT_PR1_IEP_TIM_CAP_CMP_PEND; - - // Enable host interrupt 0 - // CT_INTC.HIER_bit.EN_HINT = 0x1; - // Unset the raw events - CT_INTC.SICR_bit.STS_CLR_IDX = SYSEVT_PR1_PRU_MST_INTR1_INTR_REQ; + CT_INTC.SICR_bit.STS_CLR_IDX = ARM_TO_PRU_EVT; CT_INTC.SICR_bit.STS_CLR_IDX = SYSEVT_PR1_IEP_TIM_CAP_CMP_PEND; - - // Re-enable events - CT_INTC.GER_bit.EN_HINT_ANY = 1; + CT_INTC.SICR_bit.STS_CLR_IDX = SYSEVT_EPWM1_INTR_PEND; } void InterruptHandlerInit(uint8_t evt, InterruptHandler *handler) { @@ -53,8 +48,16 @@ void InterruptHandlerInit(uint8_t evt, InterruptHandler *handler) { } void ServiceInterrupts(void) { - while ((__R31 & PRU_HOST0_INTERRUPT) != 0) { - uint8_t evt = CT_INTC.HIPIR0; + while ((__R31 & ARM_TO_PRU_IRQ) != 0) { + uint8_t evt = HIPRIO_EVT; + + // TODO: Can't use debug.h helpers here because they run longer + // than one IEP cycle and lead to an endless interrupt cycle + // because presently, the IEP interrupt has highest priority. + // (Here, 46 is the EPWM1EVT event, which is not working.) + // if (evt == 46) { + // flash(1); + // } // Unblock all and prioritize to run immediately. if (__controller.handler[evt] != NULL) { @@ -64,3 +67,8 @@ void ServiceInterrupts(void) { CT_INTC.SICR_bit.STS_CLR_IDX = evt; } } + +// ControllerEnable starts the interrupt controller. +void ControllerEnable(void) { + CT_INTC.GER_bit.EN_HINT_ANY = 1; +} diff --git a/lib/intc/intc-defs.h b/lib/intc/intc-defs.h index 64abcef..b62bb23 100644 --- a/lib/intc/intc-defs.h +++ b/lib/intc/intc-defs.h @@ -17,6 +17,7 @@ typedef void(InterruptHandler)(void); extern InterruptController __controller; void ControllerInit(void); +void ControllerEnable(void); void InterruptHandlerInit(uint8_t evt, InterruptHandler *handler); void ServiceInterrupts(void); diff --git a/lib/intc/service.c b/lib/intc/service.c new file mode 100644 index 0000000..7c101c3 --- /dev/null +++ b/lib/intc/service.c @@ -0,0 +1,29 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#include + +#include "intc.h" +#include "service.h" + +#include "lib/coroutine/coroutine.h" + +SUPRUGLUE_DEFINE_THREAD(serviceproc, 256); + +void serviceProcess(ThreadID thid, Args args) { + for (;;) { + if (ThreadListEmpty(&__system_runnable)) { + SystemOnChipSuspend(); + } + ServiceInterrupts(); + Yield(); + } +} + +int InterruptServiceInit(void) { + ControllerInit(); + + Args args; + args.ptr = ""; + return Create(&serviceproc.thread, serviceProcess, args, "intc", sizeof(serviceproc.space)); +} diff --git a/lib/intc/service.h b/lib/intc/service.h new file mode 100644 index 0000000..01fda61 --- /dev/null +++ b/lib/intc/service.h @@ -0,0 +1,17 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#ifndef LIB_TIME_INTC_SERVICE_H +#define LIB_TIME_INTC_SERVICE_H + +#ifdef __cplusplus +extern "C" { +#endif + +int InterruptServiceInit(void); + +#ifdef __cplusplus +} +#endif + +#endif // LIB_TIME_INTC_SERVICE_H diff --git a/lib/intc/test32/BUILD b/lib/intc/test32/BUILD index 90474c2..0872918 100644 --- a/lib/intc/test32/BUILD +++ b/lib/intc/test32/BUILD @@ -10,7 +10,7 @@ cc_library( "intc.h", ], deps = [ - "@com_google_absl//absl/log", "//lib/intc:defs", + "@com_google_absl//absl/log", ], ) diff --git a/lib/intc/test32/intc.cc b/lib/intc/test32/intc.cc index ba3bffc..d4e4566 100644 --- a/lib/intc/test32/intc.cc +++ b/lib/intc/test32/intc.cc @@ -56,3 +56,7 @@ void InterruptHandlerInit(uint8_t evt, InterruptHandler *handler) { void RaiseInterrupt(uint8_t evt) { __controller.test->Raise(evt); } + +void ControllerEnable(void) { + // @@@ +} diff --git a/lib/list/BUILD b/lib/list/BUILD index 325fa2c..f6f7989 100644 --- a/lib/list/BUILD +++ b/lib/list/BUILD @@ -5,11 +5,11 @@ package(default_visibility = ["//visibility:public"]) # Re-evaluate. See ./save/list-old.h. cc_library( name = "list", + srcs = [ + ], hdrs = [ "list.h", ], - srcs = [ - ], ) cc_test( diff --git a/lib/log/daemon/BUILD b/lib/log/daemon/BUILD index 3f65091..6ef644c 100644 --- a/lib/log/daemon/BUILD +++ b/lib/log/daemon/BUILD @@ -24,8 +24,10 @@ cc_test( deps = [ ":daemon", "//lib/intc", + "//lib/intc:service", "//lib/log/fmt", "//lib/rpmsg/test32", + "//lib/time:process", "@com_google_absl//absl/log", "@com_google_googletest//:gtest_main", ], diff --git a/lib/log/daemon/daemon.c b/lib/log/daemon/daemon.c index 07f6839..c213604 100644 --- a/lib/log/daemon/daemon.c +++ b/lib/log/daemon/daemon.c @@ -1,25 +1,36 @@ // Copyright Joshua MacDonald // SPDX-License-Identifier: MIT +#include + #include "daemon.h" #include "lib/coroutine/coroutine.h" #include "lib/debug/debug.h" #include "lib/intc/intc.h" #include "lib/rpmsg/rpmsg.h" #include "lib/soc/sysevts.h" +#include "lib/time/clock.h" -void SyslogProcess(ThreadID thid, Args args) { +void syslogProcess(ThreadID thid, Args args) { for (;;) { Entry entry; JournalRead(&__system.journal, &entry, JR_BLOCKING); - int err; - while ((err = ClientSend(&__transport, &entry, sizeof(entry))) != 0) { + while (ClientSend(&__transport, &entry, sizeof(entry)) != 0) { // TODO: Flash user LEDs? - Sleep(1000000); + Sleep(TIME_SECOND / 2); } Yield(); } } + +// The 512 byte stack is needed for host testing. TODO: for on-PRU too? +SUPRUGLUE_DEFINE_THREAD(syslog, 512); + +int SyslogInit(void) { + Args args; + args.ptr = ""; + return Create(&syslog.thread, syslogProcess, args, "syslog", sizeof(syslog.space)); +} diff --git a/lib/log/daemon/daemon.h b/lib/log/daemon/daemon.h index 2338b6e..66a1371 100644 --- a/lib/log/daemon/daemon.h +++ b/lib/log/daemon/daemon.h @@ -5,16 +5,14 @@ #define LIB_SYSLOG_PROC_H #include "lib/args/args.h" +#include "lib/log/journal/journal.h" #include "lib/thread/thread.h" #ifdef __cplusplus extern "C" { #endif -// Thread function that reads log messages from other threads via the -// journal and conveys them over RPMsg to the host. - -void SyslogProcess(ThreadID thid, Args args); +int SyslogInit(void); #ifdef __cplusplus } diff --git a/lib/log/daemon/daemon_test.cc b/lib/log/daemon/daemon_test.cc index 9f93054..b2c1493 100644 --- a/lib/log/daemon/daemon_test.cc +++ b/lib/log/daemon/daemon_test.cc @@ -6,9 +6,11 @@ #include "absl/log/log.h" #include "absl/strings/str_format.h" #include "lib/coroutine/coroutine.h" +#include "lib/intc/service.h" #include "lib/log/fmt/fmt.h" #include "lib/log/journal/journal.h" #include "lib/rpmsg/rpmsg.h" +#include "lib/time/process.h" #include "gtest/gtest.h" #include @@ -17,22 +19,24 @@ void test_write_func(ThreadID tid, Args args) { int32_t cnt = Atoi(args.ptr); for (int32_t i = 0; i < cnt; i++) { - PRULOG_2U(INFO, "write %u", i, 0); // Logs always yield + PRULOG_1u32(INFO, "write %u", i); // Logs always yield } } SUPRUGLUE_DEFINE_THREAD(writer0, 500); SUPRUGLUE_DEFINE_THREAD(writer1, 500); -SUPRUGLUE_DEFINE_THREAD(syslog, 500); TEST(Syslog, Simple) { auto tt = NewTestTransport(); EXPECT_EQ(0, Init(NewSystemConfig())); + InterruptServiceInit(); + ClockInit(); + SyslogInit(); + EXPECT_EQ(0, Create(&writer0.thread, test_write_func, Args{.ptr = "100"}, "writer0", sizeof(writer0.space))); EXPECT_EQ(0, Create(&writer1.thread, test_write_func, Args{.ptr = "100"}, "writer1", sizeof(writer1.space))); - EXPECT_EQ(0, Create(&syslog.thread, SyslogProcess, Args{.ptr = ""}, "syslog", sizeof(syslog.space))); std::unordered_set res; std::unordered_set expect; @@ -56,13 +60,14 @@ TEST(Syslog, Simple) { howmany++; if (entry.msg == overflowMessage) { overflows++; - overflowed += entry.arg1; - got += entry.arg1; + overflowed += entry.int1.U32.LOW; + got += entry.int1.U32.LOW; } else { res.insert(Format(&entry)); got += 1; } } + Shutdown(); }); EXPECT_EQ(0, ::Run()); @@ -82,9 +87,11 @@ TEST(Syslog, WithTransients) { auto tt = NewTestTransport(); EXPECT_EQ(0, Init(NewSystemConfig())); + InterruptServiceInit(); + ClockInit(); + SyslogInit(); EXPECT_EQ(0, Create(&writer0.thread, test_write_func, Args{.ptr = "1"}, "writer0", sizeof(writer0.space))); - EXPECT_EQ(0, Create(&syslog.thread, SyslogProcess, Args{.ptr = ""}, "syslog", sizeof(writer0.space))); std::thread client([tt] { // Some transients. @@ -98,6 +105,8 @@ TEST(Syslog, WithTransients) { EXPECT_EQ(0, HostRecv(tt, &entry, &blen)); EXPECT_EQ(blen, sizeof(entry)); EXPECT_EQ("[writer0] write 0", Format(&entry)); + + Shutdown(); }); EXPECT_EQ(0, ::Run()); diff --git a/lib/log/fmt/fmt.cc b/lib/log/fmt/fmt.cc index eaa6005..6d86175 100644 --- a/lib/log/fmt/fmt.cc +++ b/lib/log/fmt/fmt.cc @@ -15,12 +15,12 @@ std::string Format(Entry *entry) { auto fmt2str = absl::ParsedFormat<'u', 'u'>::New(entry->msg); if (fmt2str) { - std::string res = absl::StrFormat(*fmt2str, entry->arg1, entry->arg2); + std::string res = absl::StrFormat(*fmt2str, entry->int1.U32.LOW, entry->int2.U32.LOW); return hdr + res; } auto fmt1str = absl::ParsedFormat<'u'>::New(entry->msg); if (fmt1str) { - std::string res = absl::StrFormat(*fmt1str, entry->arg1); + std::string res = absl::StrFormat(*fmt1str, entry->int1.U32.LOW); return hdr + res; } // TODO shrug. diff --git a/lib/log/journal/journal.c b/lib/log/journal/journal.c index 2576e34..09ad081 100644 --- a/lib/log/journal/journal.c +++ b/lib/log/journal/journal.c @@ -44,7 +44,7 @@ int JournalRead(Journal *jl, Entry *record, JournalReadFlags flags) { return 0; } -static Entry *getEntry(Journal *jl) { +Entry *getEntry(Journal *jl) { Block *block; // Empty case: add the first block. if (BlockListEmpty(&jl->data)) { @@ -73,7 +73,7 @@ static Entry *getEntry(Journal *jl) { int32_t loss = NUM_PER_BLOCK + 1; // If an overflow record is being lost, retain its loss count. if (swap->entries[0].msg == overflowMessage) { - loss += swap->entries[0].arg1 - 1; + loss += swap->entries[0].int1.U32.LOW - 1; } // Block is now the second-newest record, and its entry[0] is the @@ -81,8 +81,8 @@ static Entry *getEntry(Journal *jl) { Entry *entry = &block->entries[0]; entry->tid = OVERFLOW_THREAD_ID; entry->msg = overflowMessage; - entry->arg1 = loss; - entry->arg2 = 0; + entry->int1.U32.LOW = loss; + entry->int2.U32.LOW = 0; // Swap is the newest record. swap->count = 1; @@ -90,17 +90,12 @@ static Entry *getEntry(Journal *jl) { return &swap->entries[0]; } -void JournalWrite(Journal *jl, ThreadID tid, const char *msg, int32_t arg1, int32_t arg2, JournalWriteFlags flags) { - Entry *entry = getEntry(jl); - entry->tid = tid; - entry->msg = msg; - entry->arg1 = arg1; - entry->arg2 = arg2; +void setEntry(Journal *jl, Entry *entry) { ReadClock(&entry->time); SemaUp(&jl->lock); - if ((flags & JW_YIELD) != 0) { + if ((entry->flags & JW_YIELD) != 0) { Yield(); } } diff --git a/lib/log/journal/journal.h b/lib/log/journal/journal.h index cd9e371..d6063b9 100644 --- a/lib/log/journal/journal.h +++ b/lib/log/journal/journal.h @@ -21,6 +21,7 @@ extern const char *const overflowMessage; typedef struct _Journal Journal; typedef struct _Entry Entry; typedef struct _Block Block; +typedef struct _Integer Integer; #define NUM_PER_BLOCK 8 #define NUM_BLOCKS 4 @@ -35,22 +36,42 @@ enum _JournalReadFlags { enum _JournalWriteFlags { JW_NONE = 0, JW_YIELD = 0x1, - JW_INFO_BLOCK = 0x10, // INFO will Block - JW_INFO = 0x10 | JW_YIELD, // INFO will Yield - JW_WARNING_BLOCK = 0x20, // WARNING will Block - JW_WARNING = 0x20 | JW_YIELD, // WARNING will Yield - JW_FATAL = 0x40, // FATAL will Block + + JW_METRICS = 0x10, // Metrics do not yield + JW_INFO_NOYIELD = 0x20, + JW_WARNING_NOYIELD = 0x40, + JW_FATAL = 0x80, // Fatal does not yield + + JW_INFO = JW_INFO_NOYIELD | JW_YIELD, // INFO will Yield + JW_WARNING = JW_WARNING_NOYIELD | JW_YIELD, // WARNING will Yield + + JW_FMT_Au32 = 0x1000, + JW_FMT_Au64 = 0x2000, + JW_FMT_Bu32 = 0x4000, + JW_FMT_Bu64 = 0x8000, }; typedef enum _JournalReadFlags JournalReadFlags; typedef enum _JournalWriteFlags JournalWriteFlags; +struct _Integer { + union { + struct { + uint32_t LOW; + uint32_t HIGH; + } U32; + + uint64_t U64; + }; +}; + struct _Entry { ThreadID tid; + uint32_t flags; Timestamp time; const char *msg; - int32_t arg1; - int32_t arg2; + Integer int1; + Integer int2; }; struct _Block { @@ -71,10 +92,61 @@ SUPRUGLUE_DEFINE_LIST_INLINE(BlockList, Block, list); void JournalInit(Journal *jl); int JournalRead(Journal *jl, Entry *record, JournalReadFlags flags); -void JournalWrite(Journal *jl, ThreadID tid, const char *fmt, int32_t arg1, int32_t arg2, JournalWriteFlags flags); -#define PRULOG_2U(level, fmt, arg1, arg2) \ - JournalWrite(&__system.journal, TID(__system_current), (fmt), (arg1), (arg2), JW_##level) +Entry *getEntry(Journal *jl); +void setEntry(Journal *jl, Entry *entry); + +#define PRULOG_2u32(level, fmt, arg1, arg2) \ + do { \ + Entry *entry = getEntry(&__system.journal); \ + entry->tid = TID(__system_current); \ + entry->flags = JW_##level | JW_FMT_Au32 | JW_FMT_Bu32; \ + entry->msg = (fmt); \ + entry->int1.U32.LOW = (arg1); \ + entry->int2.U32.LOW = (arg2); \ + setEntry(&__system.journal, entry); \ + } while (0) + +#define PRULOG_1u32(level, fmt, arg1) \ + do { \ + Entry *entry = getEntry(&__system.journal); \ + entry->tid = TID(__system_current); \ + entry->flags = JW_##level | JW_FMT_Au32; \ + entry->msg = (fmt); \ + entry->int1.U32.LOW = (arg1); \ + setEntry(&__system.journal, entry); \ + } while (0) + +#define PRULOG_2u64(level, fmt, arg1, arg2) \ + do { \ + Entry *entry = getEntry(&__system.journal); \ + entry->tid = TID(__system_current); \ + entry->flags = JW_##level | JW_FMT_Au64 | JW_FMT_Bu64; \ + entry->msg = (fmt); \ + entry->int1.U64 = (arg1); \ + entry->int2.U64 = (arg2); \ + setEntry(&__system.journal, entry); \ + } while (0) + +#define PRULOG_0(level, fmt) \ + do { \ + Entry *entry = getEntry(&__system.journal); \ + entry->tid = TID(__system_current); \ + entry->flags = JW_##level; \ + entry->msg = (fmt); \ + setEntry(&__system.journal, entry); \ + } while (0) + +#define METLOG_2u64(th, fmt, arg1, arg2) \ + do { \ + Entry *entry = getEntry(&__system.journal); \ + entry->tid = TID(th); \ + entry->flags = JW_METRICS | JW_FMT_Au64 | JW_FMT_Bu64; \ + entry->msg = (fmt); \ + entry->int1.U64 = (arg1); \ + entry->int2.U64 = (arg2); \ + setEntry(&__system.journal, entry); \ + } while (0) #ifdef __cplusplus } diff --git a/lib/log/journal/journal_test.cc b/lib/log/journal/journal_test.cc index 27dcb10..19c5201 100644 --- a/lib/log/journal/journal_test.cc +++ b/lib/log/journal/journal_test.cc @@ -8,9 +8,19 @@ #define HALF ((NUM_BLOCKS * NUM_PER_BLOCK) >> 1) #define QUARTER ((NUM_BLOCKS * NUM_PER_BLOCK) >> 2) +void journalWrite(Journal *jl, ThreadID tid, const char *msg, int32_t arg1, int32_t arg2, int32_t flags) { + Entry *entry = getEntry(jl); + entry->tid = tid; + entry->flags = flags; + entry->msg = msg; + entry->int1.U32.LOW = arg1; + entry->int2.U32.LOW = arg2; + setEntry(jl, entry); +} + void testWrite(Journal *jl, int32_t o, int32_t n) { for (int32_t i = o; i < n; i++) { - JournalWrite(jl, i, "nice journal", i, i + 1, JW_NONE); + journalWrite(jl, i, "nice journal", i, i + 1, JW_NONE); } } @@ -19,8 +29,8 @@ void testRead(Journal *jl, int32_t i) { EXPECT_EQ(0, JournalRead(jl, &entry, JR_NONE)); EXPECT_EQ(i, entry.tid); EXPECT_STREQ("nice journal", entry.msg); - EXPECT_EQ(i, entry.arg1); - EXPECT_EQ(i + 1, entry.arg2); + EXPECT_EQ(i, entry.int1.U32.LOW); + EXPECT_EQ(i + 1, entry.int2.U32.LOW); } void testReadOverflow(Journal *jl, int32_t i) { @@ -28,8 +38,8 @@ void testReadOverflow(Journal *jl, int32_t i) { EXPECT_EQ(0, JournalRead(jl, &entry, JR_NONE)); EXPECT_EQ(OVERFLOW_THREAD_ID, entry.tid); EXPECT_STREQ(overflowMessage, entry.msg); - EXPECT_EQ(i, entry.arg1); - EXPECT_EQ(0, entry.arg2); + EXPECT_EQ(i, entry.int1.U32.LOW); + EXPECT_EQ(0, entry.int2.U32.LOW); } TEST(Journal, EmptyRead) { @@ -84,14 +94,14 @@ TEST(Journal, RepeatOverflow) { for (int i = 0; i < TOTAL * repeat; i++) { // write so many for (int w = 0; w < writes; w++) { - JournalWrite(&jl, 2 * i, "repeat", 0, 0, JW_NONE); + journalWrite(&jl, 2 * i, "repeat", 0, 0, JW_NONE); } // read so many (fewer) for (int r = 0; r < reads; r++) { Entry entry; EXPECT_EQ(0, JournalRead(&jl, &entry, JR_NONE)); if (entry.msg == overflowMessage) { - counted += entry.arg1; + counted += entry.int1.U32.LOW; } else { counted += 1; } @@ -106,7 +116,7 @@ TEST(Journal, RepeatOverflow) { } if (entry.msg == overflowMessage) { - counted += entry.arg1; + counted += entry.int1.U32.LOW; } else { counted += 1; } diff --git a/lib/pwm/BUILD b/lib/pwm/BUILD new file mode 100644 index 0000000..d70bf82 --- /dev/null +++ b/lib/pwm/BUILD @@ -0,0 +1,25 @@ +# -*- Mode: Python -*- +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "defs", + hdrs = [ + "pwm-defs.h", + ], + deps = [ + "//lib/pinmap", + ], +) + +cc_library( + name = "pwm", + hdrs = [ + "pwm.h", + ], + deps = [ + ":defs", + ] + select({ + "//tools/toolchain:am335x": ["//lib/pwm/am335x"], + "//conditions:default": ["//lib/pwm/test32"], + }), +) diff --git a/lib/pwm/am335x/BUILD b/lib/pwm/am335x/BUILD new file mode 100644 index 0000000..8123058 --- /dev/null +++ b/lib/pwm/am335x/BUILD @@ -0,0 +1,37 @@ +# -*- Mode: Python -*- +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "am335x", + hdrs = [ + "pwm-am335x.h", + ], + srcs = [ + "pwm-am335x.c", + ], + deps = [ + "//lib/pwm:defs", + "//tools/toolchain/am335x", + ], +) + +filegroup( + name = "epwm0_intr_pend", + srcs = [ + "epwm0_intr_pend.yaml", + ], +) + +filegroup( + name = "epwm1_intr_pend", + srcs = [ + "epwm1_intr_pend.yaml", + ], +) + +filegroup( + name = "epwm2_intr_pend", + srcs = [ + "epwm2_intr_pend.yaml", + ], +) diff --git a/lib/pwm/am335x/README.md b/lib/pwm/am335x/README.md new file mode 100644 index 0000000..088f5c4 --- /dev/null +++ b/lib/pwm/am335x/README.md @@ -0,0 +1,88 @@ + +Cheatsheet on PWMSS + +0h IDVER IP Revision Register Section 15.1.3.1 + +Version + +4h SYSCONFIG System Configuration Register Section 15.1.3.2 + +Reset, standby, idle, emulation stuff. + +8h CLKCONFIG Clock Configuration Register Section 15.1.3.3 + +Enable and stop the clock to the ePWM, eCAP, and eQEP. + +Ch CLKSTATUS Clock Status Register Section 15.1.3.4 + +Shows the above config changes (which are async?) + +Cheatsheet on PWM + + +0h TBCTL Time-Base Control Register Section 15.2.4.1 + +Clock divisor (rate 1/1 to 1/128); Phase direction (in up/down mode); +High-speed clock divisor; Software force sync; Sync output select; +Counter and Shadow modes + +2h TBSTS Time-Base Status Register Section 15.2.4.2 + +Latch for synchronization events; overflow; counter direction. + +4h TBPHSHR Extension for HRPWM Phase Register Section 15.2.4.3 + +High-res stuff. + +6h TBPHS Time-Base Phase Register Section 15.2.4.4 + +High-res phase. + +8h TBCNT Time-Base Counter Register Section 15.2.4.5 + +Time-based count (16 bits) + +Ah TBPRD Time-Base Period Register Section 15.2.4.6 + +The PERIOD of the coutner. When zero or max, ... + +Eh CMPCTL Counter-Compare Control Register Section 15.2.4.7 + +Shadowing, and when to load/latch. + +10h CMPAHR Extension for HRPWM Counter-Compare A Register Section 15.2.4.8 + +Compare-A high-res + +12h CMPA Counter-Compare A Register Section 15.2.4.9 + +Value for compare-A reg + +14h CMPB Counter-Compare B Register Section 15.2.4.10 + +Value for compare-A reg + +16h AQCTLA Action-Qualifier Control Register for Output A (EPWMxA) Section 15.2.4.11 +18h AQCTLB Action-Qualifier Control Register for Output B (EPWMxB) Section 15.2.4.12 +1Ah AQSFRC Action-Qualifier Software Force Register Section 15.2.4.13 +1Ch AQCSFRC Action-Qualifier Continuous S/W Force Register Set Section 15.2.4.14 + +1Eh DBCTL Dead-Band Generator Control Register Section 15.2.4.15 +20h DBRED Dead-Band Generator Rising Edge Delay Count Register Section 15.2.4.16 +22h DBFED Dead-Band Generator Falling Edge Delay Count Register Section 15.2.4.17 + +24h TZSEL Trip-Zone Select Register Section 15.2.4.18 +28h TZCTL Trip-Zone Control Register Section 15.2.4.19 +2Ah TZEINT Trip-Zone Enable Interrupt Register Section 15.2.4.20 +2Ch TZFLG Trip-Zone Flag Register Section 15.2.4.21 +2Eh TZCLR Trip-Zone Clear Register Section 15.2.4.22 +30h TZFRC Trip-Zone Force Register Section 15.2.4.23 + +32h ETSEL Event-Trigger Selection Register Section 15.2.4.24 +34h ETPS Event-Trigger Pre-Scale Register Section 15.2.4.25 +36h ETFLG Event-Trigger Flag Register Section 15.2.4.26 +38h ETCLR Event-Trigger Clear Register Section 15.2.4.27 +3Ah ETFRC Event-Trigger Force Register Section 15.2.4.28 + +3Ch PCCTL PWM-Chopper Control Register Section 15.2.4.29 +C0h HRCNFG HRPWM configuration register (HRCNFG) Section 15.2.4.30 diff --git a/lib/pwm/am335x/epwm0_intr_pend.yaml b/lib/pwm/am335x/epwm0_intr_pend.yaml new file mode 100644 index 0000000..69fbf6c --- /dev/null +++ b/lib/pwm/am335x/epwm0_intr_pend.yaml @@ -0,0 +1,4 @@ +incoming: +- event: SYSEVT_EPWM0_INTR_PEND + channel: 3 + desc: eHRPWM interrupt from ePWM module 0 diff --git a/lib/pwm/am335x/epwm1_intr_pend.yaml b/lib/pwm/am335x/epwm1_intr_pend.yaml new file mode 100644 index 0000000..c879c67 --- /dev/null +++ b/lib/pwm/am335x/epwm1_intr_pend.yaml @@ -0,0 +1,4 @@ +incoming: +- event: SYSEVT_EPWM1_INTR_PEND + channel: 4 + desc: eHRPWM interrupt from ePWM module 1 diff --git a/lib/pwm/am335x/epwm2_intr_pend.yaml b/lib/pwm/am335x/epwm2_intr_pend.yaml new file mode 100644 index 0000000..cb40e5e --- /dev/null +++ b/lib/pwm/am335x/epwm2_intr_pend.yaml @@ -0,0 +1,4 @@ +incoming: +- event: SYSEVT_EPWM2_INTR_PEND + channel: 5 + desc: eHRPWM interrupt from ePWM module 2 diff --git a/lib/pwm/am335x/pwm-am335x.c b/lib/pwm/am335x/pwm-am335x.c new file mode 100644 index 0000000..ffd8721 --- /dev/null +++ b/lib/pwm/am335x/pwm-am335x.c @@ -0,0 +1,213 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#include "pwm-am335x.h" +#include "external/ti-pru-support/include/am335x/sys_pwmss.h" + +// Status: the code in this directory is a work-in-progress, and not +// completely functional. Here, epwmss1 is initialized with a simple +// counting-up waveform, with: +// - Prescaler at 1/128 +// - Period 10000 +// - CMPA 5000 +// - CMPB 7500 +// - AQ: output low on TBCNT==0, high on TBCNT=CMPA (i.e., 50% duty) + +// PWM initialization procedure, from the am335x TRM 15.2.2.2: +// +// 1. Disable global interrupts(CPUINTMflag) +// 2. Disable PWM interrupts +// 3. Initialize peripheral registers +// 4. Clear any spurious PWM flags +// 5. Enable ePWM interrupts +// 6. Enable global interrupts + +// Paragraph from the bottom of am335x TRM 15.2.2.9.3: +// +// Note that the interrupts coming from the ePWM module are also used +// as DMA events. The interrupt registers should be used to enable and +// clear the current DMA event in order for the ePWM module to +// generate subsequent DMA events. + +// Various other things we know: +// +// 1. The paragraph from 15.2.2.9.3 indicates a connection with the +// DMA controller, but it is unclear whether one needs to be +// concerned with the DMA controller. EDMA channel 35 == ePWMEVT1. +// Do we need a dummy PaRaM entry so that the DMA controller is +// satisfied and will allow interrupts to be generated? +// +// 2. Need to enable the TBCLK for EPWM1 using the PWMSS_CTRL +// register (TRM 9.3.1.31) (offset = 664h) +// +// 3. Need to pinmux the EPWMxA output. +// +// 4. According to some forum threads, the device tree needs to be +// configured to give the PRU control over it. +// See https://e2e.ti.com/support/processors-group/processors/f/processors-forum/918237/am4378-arm-to-pru-event +// See https://forum.beagleboard.org/t/pwmss-control-by-pru-with-kernel-4-19/31246/19 +// Using a device-tree with `status = "disabled"` to prevent the kernel from +// adopting the PWM hardware. +// +// ehrpwm1: pwm@200 { +// compatible = "ti,am3352-ehrpwm"; +// #pwm-cells = <3>; +// reg = <0x200 0x80>; +// clocks = <&ehrpwm1_tbclk>, <&l4ls_gclk>; +// clock-names = "tbclk", "fck"; +// status = "disabled"; +// }; +// +// 5. Searched and searched for any examples or forum discussions on this +// topic, found very little. I assume that anyone trying to the eQEP +// interrupts to work is in the same situation, so here's one: +// +// https://e2e.ti.com/support/processors-group/processors/f/processors-forum/478720/beagle-bone-black-pru-not-able-to-initialize-pwmss0-or-pwmss1 +// https://e2e.ti.com/support/processors-group/processors/f/processors-forum/362435/am335x-unable-to-receive-pwm-interrupt-on-pru +// +// Current state: the PWM is configured and running correctly -- the PWM +// output can be observed by looping back to a GPIO pin, which the example +// `examples/pwm_outin` does. + +// PWM_ClearInterrupt is called from the interrupt servicing routine. +// +void PWM_ClearInterrupt(void) { + // Experimental: do we need to clear a DMA interrupt, too? + // EDMA_BASE[SHADOW1(EDMAREG_ICRH)] = EDMA_dmaChannelMask; + + // Clear the event. + PWMSS1.EPWM_ETCLR = 1; +} + +// PWM_Init initializes but does not start the PWM. +void PWM_Init(void) { + ////////////////////////////////////////////////////////////////////// + // Time-Base + + PWMSS1.EPWM_TBCTL = (3 << 14) | // FREE_SOFT: Free run + (0 << 13) | // PHSDIR: n/a in up-count mode + (7 << 10) | // CLKDIV: Time-base clock prescale bits (/128) + (1 << 7) | // HSPCLKDIV: (Default) high-speed clock prescale bits + (0 << 6) | // SWFSYNC: No force sync pulse + (0 << 4) | // SYNCOSEL: Sync output from EPWMxSYNC + (0 << 3) | // PRDLD: Load active period from shadow + (0 << 2) | // PHSEN: Do not load from time-base phase register + (0 << 0); // CTRMODE: Up-count mode + + // TBSTS: All defaults + // TBPHSHR: Not used + // TBPHS: Not used + // TBCNT: Not used + + ////////////////////////////////////////////////////////////////////// + // Counter compare + + // CMPA: Not used + // CMPAHR: Not used + + PWMSS1.EPWM_TBPRD = 10000; // TBPRD: Time-base period (16 bits) + + // CMPCTL: All defaults + // CMPAHR: Not used + + PWMSS1.EPWM_CMPA = 5000; // CMPB: Compare value for clearing EPWMxA + PWMSS1.EPWM_CMPB = 7500; // CMPB: Compare value for sample interrupt + + ////////////////////////////////////////////////////////////////////// + // Action qualifier + + PWMSS1.EPWM_AQCTLA = (2 << 0) | // ZRO: force EPWMxA low when TBCNT == 0 + (1 << 4); // CAU: force EPWMxA high when TBCNT == CMPA + // AQCTLB: Not used + + // PWMSS1.EPWM_AQSFRC = (3 << 6); // Immediate mode + // PWMSS1.EPWM_AQCSFRC = (1 << 0); // Force low on EPWMxA + + ////////////////////////////////////////////////////////////////////// + // Chopper: Not used + // Deadband: Not used + + ////////////////////////////////////////////////////////////////////// + // Event trigger + + PWMSS1.EPWM_ETSEL = (6 << 0); // INTSEL: CMPB incrementing + PWMSS1.EPWM_ETPS = (1 << 0); // INTPRD: Every event + + // Clear pending interrupt! + PWMSS1.EPWM_ETCLR = (1 << 0); + + ////////////////////////////////////////////////////////////////////// + // Control module + CONTROL_MODULE[0x664 / WORDSZ] = (1 << 1); // Enable TBCLK for EPWM1 + + // Note: The pinmix step has been accomplished, for now, using + // `config-pin P9_14 pwm`. In theory, we can do this with the + // control module: + // + // CONTROL_MODULE[0x848 / WORDSZ] = (6 << 0); // P9_14 == Mode 6 + + // Enable the PWM clock. + PWMSS1.CLKCONFIG_bit.EPWMCLK_EN = 1; + + // The following experimental code attempts to create a dummy PaRaM + // entry, attempting to see if helps with the issue described in + // 15.2.2.9.3. It does not appear to help. + +#if 0 + // Setup EDMA region access for Shadow Region 1 + // Note the Linux kernel uses Shadow Region 0. + // + // DRAE1 == DMA Region Access Enable shadow region 1. + // + // We enable a single channel in this region. + EDMA_BASE[EDMA_DRAEH1] = EDMA_dmaChannelMask; + EDMA_BASE[EDMA_DRAEH1] = EDMA_paramNumberMask; + + // // Map DMA Channel to PaRAM w/ same number. + EDMA_BASE[EDMA_DCHMAP_N(EDMA_dmaChannel)] = EDMA_paramNumber << 5; + + // // Setup channel to submit to EDMA TC0 (highest priority). + // // + // // Note DMAQNUM_5 configures the channel controller for channels + // // 32-39. This is specific to channel 35. + EDMA_BASE[EDMA_DMAQNUM_4] &= 0xFFFF8FFF; + + // // Enable channel for an event trigger. + // EDMA_BASE[SHADOW1(EDMAREG_EESRH)] = EDMA_dmaChannelMask; + + EDMA_BASE[SHADOW1(EDMAREG_IESRH)] = EDMA_dmaChannelMask; + + // // Clear interrupt and secondary event registers. + EDMA_BASE[SHADOW1(EDMAREG_SECRH)] = EDMA_dmaChannelMask; + + // // Clear event missed register. + // EDMA_BASE[EDMA_EMCRH] = EDMA_dmaChannelMask; + + // // Setup and store Dummy PaRAM. + uint16_t paramOffset = EDMA_PARAM_OFFSET; + paramOffset += ((EDMA_paramNumber * EDMA_PARAM_SIZE) / WORDSZ); + + volatile edmaParam *edma_param_entry = (volatile edmaParam *)(EDMA_BASE + paramOffset); + + edma_param_entry->lnkrld.link = 0xFFFF; + edma_param_entry->lnkrld.bcntrld = 0x0000; + edma_param_entry->opt.static_set = 1; + + // Transfer complete interrupt enable. + edma_param_entry->opt.tcinten = 1; + + // Intermediate transfer completion chaining enable. + // not needed, used for splitting the transfer + // edma_param_entry->opt.itcchen = 1; + edma_param_entry->opt.tcc = EDMA_dmaChannel; + + edma_param_entry->ccnt.ccnt = 1; + edma_param_entry->abcnt.acnt = 0; + edma_param_entry->abcnt.bcnt = 1; +#endif +} + +// PWM_Enable enables PWM interrupts. +void PWM_Enable(void) { + PWMSS1.EPWM_ETSEL |= (1 << 3); +} diff --git a/lib/pwm/am335x/pwm-am335x.h b/lib/pwm/am335x/pwm-am335x.h new file mode 100644 index 0000000..af27537 --- /dev/null +++ b/lib/pwm/am335x/pwm-am335x.h @@ -0,0 +1,148 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#include "lib/pwm/pwm-defs.h" + +#ifndef LIB_PWM_AM335X_PWM_H +#define LIB_PWM_AM335X_PWM_H + +#ifdef __cplusplus +extern "C" { +#endif + +// Note! All of these constants belong in a better place and should +// probably be auto-generated. I've placed them here to see if +// configuring a dummy PaRaM entry on the ehrpwm1 DMA channel would +// help with interrupt generation. + +// This DMA code was copied from another project, which succeeded at +// receiving an interrupt on the PRU from the DMA controller. See: +// +// https://forum.beagleboard.org/t/kernel-5-10-device-tree-overlay-for-using-dma-from-bbb-pru/35665 + +#define CONTROL_MODULE ((uint32_t *)0x44E10000) + +#define EDMA_BASE ((volatile uint32_t *)(0x49000000)) +#define EDMA_DRAE1 (0x348 / WORDSZ) // DMA Region Access Enable Register for Region 1 Section 11.4.1.20 +#define EDMA_DRAEH1 (0x34C / WORDSZ) // DMA Region Access Enable Register High for Region 1 Section 11.4.1.21 + +#define EDMA_EMR (0x300 / WORDSZ) // Event Missed Register Section 11.4.1.9 +#define EDMA_EMRH (0x304 / WORDSZ) // Event Missed Register High Section 11.4.1.10 +#define EDMA_EMCR (0x308 / WORDSZ) // Event Missed Clear Register Section 11.4.1.11 +#define EDMA_EMCRH (0x30C / WORDSZ) // Event Missed Clear Register High Section 11.4.1.12 + +#define EDMA_CCERR (0x318 / WORDSZ) // EDMA3CC Error Register Section 11.4.1.15 +#define EDMA_EEVAL (0x320 / WORDSZ) // EDMA3CC EEVAL +#define EDMA_CCERRCLR (0x31C / WORDSZ) // EDMA3CC Error Clear Register Section 11.4.1.16 + +#define EDMAREG_ER 0x00 // Event Register Section 11.4.1.91 +#define EDMAREG_ERH 0x04 // Event Register High Section 11.4.1.92 +#define EDMAREG_ECR 0x08 // Event Clear Register Section 11.4.1.93 +#define EDMAREG_ECRH 0x0C // Event Clear Register High Section 11.4.1.94 +#define EDMAREG_ESR 0x10 // Event Set Register Section 11.4.1.95 +#define EDMAREG_ESRH 0x14 // Event Set Register High Section 11.4.1.96 +#define EDMAREG_CER 0x18 // Chained Event Register Section 11.4.1.97 +#define EDMAREG_CERH 0x1C // Chained Event Register High Section 11.4.1.98 +#define EDMAREG_EER 0x20 // Event Enable Register Section 11.4.1.99 +#define EDMAREG_EERH 0x24 // Event Enable Register High Section 11.4.1.100 +#define EDMAREG_EECR 0x28 // Event Enable Clear Register Section 11.4.1.101 +#define EDMAREG_EECRH 0x2C // Event Enable Clear Register High Section 11.4.1.102 +#define EDMAREG_EESR 0x30 // Event Enable Set Register Section 11.4.1.103 +#define EDMAREG_EESRH 0x34 // Event Enable Set Register High Section 11.4.1.104 +#define EDMAREG_SER 0x38 // Secondary Event Register Section 11.4.1.105 +#define EDMAREG_SERH 0x3C // Secondary Event Register High Section 11.4.1.106 +#define EDMAREG_SECR 0x40 // Secondary Event Clear Register Section 11.4.1.107 +#define EDMAREG_SECRH 0x44 // Secondary Event Clear Register High Section 11.4.1.108 +#define EDMAREG_IER 0x50 // Interrupt Enable Register Section 11.4.1.109 +#define EDMAREG_IERH 0x54 // Interrupt Enable Register High Section 11.4.1.110 +#define EDMAREG_IECR 0x58 // Interrupt Enable Clear Register Section 11.4.1.111 +#define EDMAREG_IECRH 0x5C // Interrupt Enable Clear Register High Section 11.4.1.112 +#define EDMAREG_IESR 0x60 // Interrupt Enable Set Register Section 11.4.1.113 +#define EDMAREG_IESRH 0x64 // Interrupt Enable Set Register High Section 11.4.1.114 +#define EDMAREG_IPR 0x68 // Interrupt Pending Register Section 11.4.1.115 +#define EDMAREG_IPRH 0x6C // Interrupt Pending Register High Section 11.4.1.116 +#define EDMAREG_ICR 0x70 // Interrupt Clear Register Section 11.4.1.117 +#define EDMAREG_ICRH 0x74 // Interrupt Clear Register High Section 11.4.1.118 +#define EDMAREG_IEVAL 0x78 // Interrupt Evaluate Register Section 11.4.1.119 +#define EDMAREG_QER 0x80 // QDMA Event Register Section 11.4.1.120 +#define EDMAREG_QEER 0x84 // QDMA Event Enable Register Section 11.4.1.121 +#define EDMAREG_QEECR 0x88 // QDMA Event Enable Clear Register Section 11.4.1.122 +#define EDMAREG_QEESR 0x8C // QDMA Event Enable Set Register Section 11.4.1.123 +#define EDMAREG_QSER 0x90 // QDMA Secondary Event Register Section 11.4.1.124 +#define EDMAREG_QSECR 0x94 // QDMA Secondary Event Clear Register Section 11.4.1.125 + +#define EDMA_DCHMAP_N(N) ((0x100 + (N << 2)) / WORDSZ) +#define EDMA_DMAQNUM_4 (0x250 / WORDSZ) // DMA Queue Number Register 4 +#define SHADOW1(reg) ((0x2200 + reg) / WORDSZ) + +#define EDMA_dmaChannel 35 +#define EDMA_dmaChannelMask (1 << (EDMA_dmaChannel - 32)) +#define EDMA_paramNumber EDMA_dmaChannel +#define EDMA_paramNumberMask (1 << (EDMA_paramNumber - 32)) + +// (TRM 11.3.3.1) +#define EDMA_PARAM_OFFSET (0x4000 / WORDSZ) +#define EDMA_PARAM_SIZE sizeof(edmaParam) // 32 bytes +#define EDMA_PARAM_NUM 256 + +// EDMA PARAM registers +typedef struct { + uint32_t sam : 1; + uint32_t dam : 1; + uint32_t syncdim : 1; + uint32_t static_set : 1; + uint32_t : 4; + uint32_t fwid : 3; + uint32_t tccmode : 1; + uint32_t tcc : 6; + uint32_t : 2; + uint32_t tcinten : 1; + uint32_t itcinten : 1; + uint32_t tcchen : 1; + uint32_t itcchen : 1; + uint32_t privid : 4; + uint32_t : 3; + uint32_t priv : 1; +} edmaParamOpt; + +typedef struct { + uint32_t acnt : 16; + uint32_t bcnt : 16; +} edmaParamABcnt; + +typedef struct { + uint32_t srcbidx : 16; + uint32_t dstbidx : 16; +} edmaParamBidx; + +typedef struct { + uint32_t link : 16; + uint32_t bcntrld : 16; +} edmaParamLnkRld; + +typedef struct { + uint32_t srccidx : 16; + uint32_t dstcidx : 16; +} edmaParamCidx; + +typedef struct { + uint32_t ccnt : 16; + uint32_t : 16; +} edmaParamCcnt; + +typedef struct { + edmaParamOpt opt; + uint32_t src; + edmaParamABcnt abcnt; + uint32_t dst; + edmaParamBidx bidx; + edmaParamLnkRld lnkrld; + edmaParamCidx cidx; + edmaParamCcnt ccnt; +} edmaParam; + +#ifdef __cplusplus +} +#endif + +#endif // LIB_PWM_AM335X_PWM_H diff --git a/lib/pwm/pwm-defs.h b/lib/pwm/pwm-defs.h new file mode 100644 index 0000000..20858e0 --- /dev/null +++ b/lib/pwm/pwm-defs.h @@ -0,0 +1,21 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#ifndef LIB_PWM_PWM_DEFS_H +#define LIB_PWM_PWM_DEFS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lib/soc/soc.h" + +void PWM_Init(void); +void PWM_ClearInterrupt(void); +void PWM_Enable(void); + +#ifdef __cplusplus +} +#endif + +#endif // LIB_PWM_PWM_DEFS_H diff --git a/lib/pwm/pwm.h b/lib/pwm/pwm.h new file mode 100644 index 0000000..24e85c7 --- /dev/null +++ b/lib/pwm/pwm.h @@ -0,0 +1,27 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#ifndef LIB_PWM_PWM_H +#define LIB_PWM_PWM_H + +#include "lib/pinmap/pinmap.h" +#include "lib/soc/soc.h" +#include + +#include "lib/pwm/pwm-defs.h" + +#if defined(SUPRUGLUE_AM335X) +#include "lib/pwm/am335x/pwm-am335x.h" +#elif defined(SUPRUGLUE_TEST32) +#include "lib/pwm/test32/pwm-test32.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif // LIB_GPIO_GPIO_H diff --git a/lib/pwm/test32/BUILD b/lib/pwm/test32/BUILD new file mode 100644 index 0000000..841f91a --- /dev/null +++ b/lib/pwm/test32/BUILD @@ -0,0 +1,24 @@ +# -*- Mode: Python -*- +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "test32", + srcs = [ + "pwm-test32.cc", + ], + hdrs = [ + "pwm-test32.h", + ], + deps = [ + "//lib/pwm:defs", + ], +) + +cc_test( + name = "test", + srcs = ["pwm_test.cc"], + deps = [ + "//lib/pwm", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/lib/pwm/test32/pwm-test32.cc b/lib/pwm/test32/pwm-test32.cc new file mode 100644 index 0000000..63de6e0 --- /dev/null +++ b/lib/pwm/test32/pwm-test32.cc @@ -0,0 +1,12 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#include + +#include "pwm-test32.h" + +void PWM_Init(void) { +} + +void PWM_ClearInterrupt(void) { +} diff --git a/lib/pwm/test32/pwm-test32.h b/lib/pwm/test32/pwm-test32.h new file mode 100644 index 0000000..0f5c6e9 --- /dev/null +++ b/lib/pwm/test32/pwm-test32.h @@ -0,0 +1,20 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#ifndef LIB_PWM_TEST32_PWM_H +#define LIB_PWM_TEST32_PWM_H + +#include + +#include "lib/pwm/pwm-defs.h" +#include "lib/soc/soc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif // LIB_PWM_TEST32_PWM_H diff --git a/lib/pwm/test32/pwm_test.cc b/lib/pwm/test32/pwm_test.cc new file mode 100644 index 0000000..e703b8f --- /dev/null +++ b/lib/pwm/test32/pwm_test.cc @@ -0,0 +1,13 @@ +#include "lib/pinmap/pinmap.h" +#include "lib/pwm/pwm.h" +#include "lib/soc/soc.h" +#include "gtest/gtest.h" + +class PwmTest : public testing::Test { +protected: + void SetUp() override { + } +}; + +TEST_F(PwmTest, Basic) { +} diff --git a/lib/resource/BUILD b/lib/resource/BUILD new file mode 100644 index 0000000..8b27c8a --- /dev/null +++ b/lib/resource/BUILD @@ -0,0 +1,19 @@ +# -*- Mode: Python -*- +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "resource", + srcs = [ + "table.c", + ], + hdrs = [ + "table.h", + ], + deps = [ + "@ti-pru-support//include", + ], + target_compatible_with = select({ + "//tools/toolchain:am335x": [], + "//conditions:default": ["@platforms//:incompatible"], + }), +) diff --git a/lib/resource/table.c b/lib/resource/table.c new file mode 100644 index 0000000..c9f3a32 --- /dev/null +++ b/lib/resource/table.c @@ -0,0 +1,67 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +// TODO: This is a placeholder. Presently there is nothing +// configurable here--all the examples use the same resource table. + +#include + +#include "external/ti-pru-support/include/pru_virtio_ids.h" +#include "external/ti-pru-support/include/rsc_types.h" + +#include "table.h" + +#define NUM_RESOURCES 1 + +// Sizes of the virtqueues (expressed in number of buffers supported, +// and must be power of 2) +#define PRU_RPMSG_VQ0_SIZE 16 +#define PRU_RPMSG_VQ1_SIZE 16 + +// The feature bitmap for virtio rpmsg +#define VIRTIO_RPMSG_F_NS 0 // name service notifications + +// This firmware supports name service notifications as one of its features. +#define RPMSG_PRU_C0_FEATURES (1 << VIRTIO_RPMSG_F_NS) + +#pragma DATA_SECTION(resourceTable, ".resource_table") +#pragma RETAIN(resourceTable) +struct my_resource_table resourceTable = { + // resource_table base + { + 1, // Resource table version: only version 1 is supported + NUM_RESOURCES, // Number of entries in the table (equals length of offset field). + 0, 0, // Reserved zero fields + }, + // Entry offsets + { + offsetof(struct my_resource_table, rpmsg_vdev), + }, + // RPMsg virtual device + { + (uint32_t)TYPE_VDEV, // type + (uint32_t)VIRTIO_ID_RPMSG, // id + (uint32_t)0, // notifyid + (uint32_t)RPMSG_PRU_C0_FEATURES, // dfeatures + (uint32_t)0, // gfeatures + (uint32_t)0, // config_len + (uint8_t)0, // status + (uint8_t)2, // num_of_vrings, only two is supported + {(uint8_t)0, (uint8_t)0}, // reserved + }, + // The two vring structs must be packed after the vdev entry. + { + FW_RSC_ADDR_ANY, // da, will be populated by host, can't pass it in + 16, // align (bytes), + PRU_RPMSG_VQ0_SIZE, // num of descriptors + 0, // notifyid, will be populated, can't pass right now + 0 // reserved + }, + { + FW_RSC_ADDR_ANY, // da, will be populated by host, can't pass it in + 16, // align (bytes), + PRU_RPMSG_VQ1_SIZE, // num of descriptors + 0, // notifyid, will be populated, can't pass right now + 0 // reserved + }, +}; diff --git a/lib/resource/table.h b/lib/resource/table.h new file mode 100644 index 0000000..216f489 --- /dev/null +++ b/lib/resource/table.h @@ -0,0 +1,23 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +// TODO: This is a placeholder. Presently there is nothing +// configurable here--all the examples use the same resource table. + +#include "external/ti-pru-support/include/rsc_types.h" + +#define NUM_RESOURCES 1 + +// my_resource_table describes the custom hardware settings used by +// this program. +struct my_resource_table { + struct resource_table base; + + uint32_t offset[NUM_RESOURCES]; // Should match 'num' in actual definition + + struct fw_rsc_vdev rpmsg_vdev; // Resource 0 + struct fw_rsc_vdev_vring rpmsg_vring0; // (cont) + struct fw_rsc_vdev_vring rpmsg_vring1; // (cont) +}; + +extern struct my_resource_table resourceTable; diff --git a/lib/rpmsg/am335x/BUILD b/lib/rpmsg/am335x/BUILD index 3964200..de5a5e4 100644 --- a/lib/rpmsg/am335x/BUILD +++ b/lib/rpmsg/am335x/BUILD @@ -20,3 +20,33 @@ cc_library( "@ti-pru-support//lib/src/rpmsg_lib", ], ) + +filegroup( + name = "pru0_irq", + srcs = [ + "pru0.yaml", + ], +) + +filegroup( + name = "pru1_irq", + srcs = [ + "pru1.yaml", + ], +) + +filegroup( + name = "irq", + srcs = select({ + "//tools/toolchain:pru0": [ + "pru0.yaml", + ], + "//tools/toolchain:pru1": [ + "pru1.yaml", + ], + }), + target_compatible_with = select({ + "//tools/toolchain:am335x": [], + "//conditions:default": ["@platforms//:incompatible"], + }), +) diff --git a/tools/toolchain/am335x/pru0.yaml b/lib/rpmsg/am335x/pru0.yaml similarity index 100% rename from tools/toolchain/am335x/pru0.yaml rename to lib/rpmsg/am335x/pru0.yaml diff --git a/tools/toolchain/am335x/pru1.yaml b/lib/rpmsg/am335x/pru1.yaml similarity index 89% rename from tools/toolchain/am335x/pru1.yaml rename to lib/rpmsg/am335x/pru1.yaml index bfea0ab..0082706 100644 --- a/tools/toolchain/am335x/pru1.yaml +++ b/lib/rpmsg/am335x/pru1.yaml @@ -1,5 +1,5 @@ incoming: - event: SYSEVT_PR1_PRU_MST_INTR3_INTR_REQ - channel: 1 + channel: 2 host: 1 desc: Rpmsg driver from the ARM to the PRU1 diff --git a/lib/rpmsg/am335x/rpmsg.c b/lib/rpmsg/am335x/rpmsg.c index 25a38d6..46aa511 100644 --- a/lib/rpmsg/am335x/rpmsg.c +++ b/lib/rpmsg/am335x/rpmsg.c @@ -70,14 +70,16 @@ int RpmsgInit(ClientTransport *transport, struct fw_rsc_vdev *vdev, struct fw_rs while (!(*status & VIRTIO_CONFIG_S_DRIVER_OK)) { } - InterruptHandlerInit(SYSEVT_PR1_PRU_MST_INTR1_INTR_REQ, &RpmsgKick); - // The system events and port are core-specific. #if SUPRUGLUE_PRU_NUM == 0 + InterruptHandlerInit(SYSEVT_PR1_PRU_MST_INTR1_INTR_REQ, &RpmsgKick); + // @@@ transport->channel_port = RPMSG_CHANNEL_PORT_0; sysevt_pru_to_arm = SYSEVT_PR1_PRU_MST_INTR0_INTR_REQ; sysevt_arm_to_pru = SYSEVT_PR1_PRU_MST_INTR1_INTR_REQ; #elif SUPRUGLUE_PRU_NUM == 1 + InterruptHandlerInit(SYSEVT_PR1_PRU_MST_INTR3_INTR_REQ, &RpmsgKick); + // @@@ transport->channel_port = RPMSG_CHANNEL_PORT_1; sysevt_pru_to_arm = SYSEVT_PR1_PRU_MST_INTR2_INTR_REQ; sysevt_arm_to_pru = SYSEVT_PR1_PRU_MST_INTR3_INTR_REQ; @@ -107,6 +109,8 @@ int ClientSend(ClientTransport *transport, const void *data, uint16_t len) { int err = pru_rpmsg_send(&transport->channel, transport->peer_dst_addr, transport->peer_src_addr, (void *)data, len); if (err != 0) { + // IS THIS HAPPENING? OR THE ABOVE? We are copying from an address on the stack + // and what ... can it move? @@@ Hmm, nope. SemaDown(&transport->kick_lock); } return err; diff --git a/lib/rpmsg/test32/BUILD b/lib/rpmsg/test32/BUILD index f65a656..56ff252 100644 --- a/lib/rpmsg/test32/BUILD +++ b/lib/rpmsg/test32/BUILD @@ -11,6 +11,7 @@ cc_library( "rpmsg.h", ], deps = [ + "//lib/coroutine", "//lib/rpmsg:defs", "@com_google_absl//absl/synchronization", ], diff --git a/lib/rpmsg/test32/chan.h b/lib/rpmsg/test32/chan.h index d050236..857d4ec 100644 --- a/lib/rpmsg/test32/chan.h +++ b/lib/rpmsg/test32/chan.h @@ -5,8 +5,11 @@ #define LIB_RPMSG_TEST32_RPMSG_TEST32_CHAN_H #include "absl/synchronization/mutex.h" +#include "lib/soc/soc.h" #include +extern absl::Mutex __system_lock; + template class Channel { public: Channel(){}; @@ -19,10 +22,10 @@ template class Channel { public: // causes opposite send() to get a value std::optional receive() { - absl::MutexLock lock(&_lock); + absl::MutexLock lock(&__system_lock); _has_receiver = true; - _lock.Await(absl::Condition( - +[](Channel *ch) { return ch->_has_receiver && ch->_has_value; }, this)); + __system_lock.Await(absl::Condition( + +[](Channel *ch) { return __system_shutdown || (ch->_has_receiver && ch->_has_value); }, this)); _has_receiver = false; _has_value = false; return std::move(_val); @@ -30,11 +33,11 @@ template class Channel { // causes opposite send() to get an error void sender_transient() { - absl::MutexLock lock(&_lock); + absl::MutexLock lock(&__system_lock); _has_receiver = true; _has_error = true; - _lock.Await(absl::Condition( - +[](Channel *ch) { return ch->_has_receiver && ch->_has_value; }, this)); + __system_lock.Await(absl::Condition( + +[](Channel *ch) { return __system_shutdown || (ch->_has_receiver && ch->_has_value); }, this)); _has_receiver = false; _has_error = false; _has_value = false; @@ -42,11 +45,11 @@ template class Channel { // causes opposite receive() to get a value int send(T &&val) { - absl::MutexLock lock(&_lock); - _lock.Await(absl::Condition( - +[](Channel *ch) { return (ch->_has_receiver && !ch->_has_value); }, this)); + absl::MutexLock lock(&__system_lock); + __system_lock.Await(absl::Condition( + +[](Channel *ch) { return __system_shutdown || (ch->_has_receiver && !ch->_has_value); }, this)); _has_value = true; - if (_has_error) { + if (__system_shutdown || _has_error) { _val.reset(); return -1; } @@ -56,15 +59,14 @@ template class Channel { // causes opposite receive() to get an error void receiver_transient() { - absl::MutexLock lock(&_lock); - _lock.Await(absl::Condition( - +[](Channel *ch) { return (ch->_has_receiver && !ch->_has_value); }, this)); + absl::MutexLock lock(&__system_lock); + __system_lock.Await(absl::Condition( + +[](Channel *ch) { return __system_shutdown || (ch->_has_receiver && !ch->_has_value); }, this)); _val.reset(); _has_value = true; } protected: - absl::Mutex _lock; std::optional _val; bool _has_value{false}; bool _has_receiver{false}; diff --git a/lib/soc/BUILD b/lib/soc/BUILD index 2845bca..4cd9a2c 100644 --- a/lib/soc/BUILD +++ b/lib/soc/BUILD @@ -12,6 +12,9 @@ cc_library( cc_library( name = "soc", + srcs = [ + "soc.c", + ], hdrs = [ "soc.h", ":sysevts_gen", diff --git a/lib/soc/am335x/BUILD b/lib/soc/am335x/BUILD index 2060cde..fc4d44e 100644 --- a/lib/soc/am335x/BUILD +++ b/lib/soc/am335x/BUILD @@ -5,6 +5,7 @@ cc_library( name = "am335x", srcs = [ "delay.s", + "sleep.s", "soc.c", ], hdrs = [ diff --git a/lib/soc/am335x/sleep.s b/lib/soc/am335x/sleep.s new file mode 100644 index 0000000..2d5c9f0 --- /dev/null +++ b/lib/soc/am335x/sleep.s @@ -0,0 +1,5 @@ +;; SystemOnChipSuspend calls SLP + .global SystemOnChipSuspend +SystemOnChipSuspend: + SLP 1 + JMP r3.w2 diff --git a/lib/soc/am335x/soc.c b/lib/soc/am335x/soc.c index c28bebf..6012658 100644 --- a/lib/soc/am335x/soc.c +++ b/lib/soc/am335x/soc.c @@ -3,10 +3,19 @@ #include "soc.h" #include "external/ti-pru-support/include/am335x/pru_cfg.h" - -void *__dummy; +#include "external/ti-pru-support/include/am335x/pru_ctrl.h" void SystemOnChipSetup(void) { // Allow OCP master port access by the PRU. CT_CFG.SYSCFG_bit.STANDBY_INIT = 0; + +#if SUPRUGLUE_PRU_NUM == 0 // @@@ + PRU0_CTRL.WAKEUP_EN = PRU_HOST0_INTERRUPT; +#elif SUPRUGLUE_PRU_NUM == 1 + PRU1_CTRL.WAKEUP_EN = PRU_HOST1_INTERRUPT; +#endif +} + +void Shutdown(void) { + __system_shutdown = 1; } diff --git a/lib/soc/am335x/soc.h b/lib/soc/am335x/soc.h index b5c359c..e577728 100644 --- a/lib/soc/am335x/soc.h +++ b/lib/soc/am335x/soc.h @@ -11,12 +11,16 @@ extern "C" { #endif +// Interrupt inputs set bits 30 and 31 in register R31. +#define PRU_HOST0_INTERRUPT 0x40000000 +#define PRU_HOST1_INTERRUPT 0x80000000 +#define PRU_HOST_ANY_INTERRUPT 0xc0000000 + // Defined in delay.s. extern void SystemOnChipDelay(int cycles); -inline int SystemOnChipIsShutdown(void) { - return 0; -} +// Defined in sleep.s. +extern void SystemOnChipSuspend(void); #define ARCH_NUM_SYSEVTS 64 diff --git a/lib/soc/soc-defs.h b/lib/soc/soc-defs.h index 77b45ab..0d94e29 100644 --- a/lib/soc/soc-defs.h +++ b/lib/soc/soc-defs.h @@ -21,8 +21,15 @@ extern "C" { #define SUPRUGLUE_DEFINE_SIZED(name, type1, thing1, type2, size) \ SUPRUGLUE_DECLARE_SIZED(name, type1, thing1, type2, size); \ + _PRAGMA(DATA_SECTION(name, ".thread." #name)) \ static SUPRUGLUE_SIZED_TYPENAME(name, type1, type2) name +#ifdef SUPRUGLUE_TEST32 +#define _PRAGMA(x) +#else +#define _PRAGMA(x) _Pragma(#x) +#endif + #define _JOIN(thing, field) thing##field #define WORDSZ sizeof(uint32_t) @@ -37,10 +44,12 @@ extern "C" { // test helpers, reset functions, etc void SystemOnChipSetup(void); -int SystemOnChipIsShutdown(void); // like a dynamic __delay_cycles void SystemOnChipDelay(int32_t cycles); +void SystemOnChipSuspend(void); + +void Shutdown(void); // Counts 5ns cycles. typedef struct _Timestamp Timestamp; @@ -54,9 +63,24 @@ struct _Timestamp { unsigned MIDDLE : 16; unsigned HIGH : 32; } CYCLES_bit; + + struct { + unsigned LOW32 : 32; + unsigned HIGH32 : 32; + } CYCLES_32bit; }; }; +// Pair of usage/stall cycle count +typedef struct _CPUCounter CPUCounter; + +struct _CPUCounter { + Timestamp run; + Timestamp stall; +}; + +extern int __system_shutdown; + #ifdef __cplusplus } #endif diff --git a/lib/soc/soc.c b/lib/soc/soc.c new file mode 100644 index 0000000..82a2373 --- /dev/null +++ b/lib/soc/soc.c @@ -0,0 +1,11 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#include "soc.h" + +#if SUPRUGLUE_PRU_NUM == 0 +#pragma DATA_SECTION(__system_shutdown, ".pru_num.0") +#elif SUPRUGLUE_PRU_NUM == 1 +#pragma DATA_SECTION(__system_shutdown, ".pru_num.1") +#endif +int __system_shutdown; diff --git a/lib/soc/test32/BUILD b/lib/soc/test32/BUILD index 27078b0..eaf11d6 100644 --- a/lib/soc/test32/BUILD +++ b/lib/soc/test32/BUILD @@ -11,8 +11,8 @@ cc_library( ], deps = [ "//lib/soc:defs", - "@com_google_absl//absl/synchronization", "//tools/toolchain/am335x:incompatible", + "@com_google_absl//absl/synchronization", "@ti-pru-support//include", ], ) diff --git a/lib/soc/test32/soc.cc b/lib/soc/test32/soc.cc index 8fddcd2..d61ea07 100644 --- a/lib/soc/test32/soc.cc +++ b/lib/soc/test32/soc.cc @@ -5,22 +5,17 @@ #include "absl/synchronization/mutex.h" absl::Mutex __system_lock; -int __system_enabled; void SystemOnChipSetup() { - absl::MutexLock lock(&__system_lock); - __system_enabled = 1; } void SystemOnChipDelay(int32_t cycles) { } -int SystemOnChipIsShutdown(void) { - absl::MutexLock lock(&__system_lock); - return !__system_enabled; +void SystemOnChipSuspend(void) { } -void SystemOnChipShutdown(void) { +void Shutdown(void) { absl::MutexLock lock(&__system_lock); - __system_enabled = 0; + __system_shutdown = 1; } diff --git a/lib/soc/test32/soc.h b/lib/soc/test32/soc.h index 6792e25..9ea1ef0 100644 --- a/lib/soc/test32/soc.h +++ b/lib/soc/test32/soc.h @@ -12,8 +12,6 @@ extern "C" { #endif -void SystemOnChipShutdown(void); - #define ARCH_NUM_SYSEVTS 64 #ifdef __cplusplus diff --git a/lib/thread/thread.h b/lib/thread/thread.h index 9c528d9..0aac204 100644 --- a/lib/thread/thread.h +++ b/lib/thread/thread.h @@ -49,7 +49,12 @@ struct _Thread { const char *name; int32_t stack_size; ThreadState state; - Timestamp when; + + // when is used by Sleep() to calculate the wakeup + Timestamp when; + + // usage counts run/stall time in cycles. + CPUCounter usage; union { jmp_buf run_jump; @@ -59,6 +64,8 @@ struct _Thread { } call; } exec; + Thread *allthreads; + uint8_t stack[0]; }; diff --git a/lib/time/BUILD b/lib/time/BUILD index 6952f34..6224083 100644 --- a/lib/time/BUILD +++ b/lib/time/BUILD @@ -46,10 +46,11 @@ cc_test( name = "test", srcs = ["clock_test.cc"], deps = [ - "//lib/time:process", - "//lib/log/fmt", + "//lib/intc:service", "//lib/log/daemon", + "//lib/log/fmt", "//lib/rpmsg/test32", + "//lib/time:process", "@com_google_absl//absl/log", "@com_google_googletest//:gtest_main", ], diff --git a/lib/time/am335x/BUILD b/lib/time/am335x/BUILD index 267f068..eab7872 100644 --- a/lib/time/am335x/BUILD +++ b/lib/time/am335x/BUILD @@ -3,16 +3,16 @@ package(default_visibility = ["//visibility:public"]) cc_library( name = "am335x", - hdrs = [ - "clock-am335x.h", - ], srcs = [ "clock-am335x.c", ], + hdrs = [ + "clock-am335x.h", + ], deps = [ - "//lib/thread", - "//lib/sync", "//lib/intc", + "//lib/sync", + "//lib/thread", "//lib/time:defs", "//tools/toolchain/am335x", ], diff --git a/lib/time/am335x/clock-am335x.c b/lib/time/am335x/clock-am335x.c index e371a65..5aa8259 100644 --- a/lib/time/am335x/clock-am335x.c +++ b/lib/time/am335x/clock-am335x.c @@ -3,15 +3,13 @@ #include +#include "external/ti-pru-support/include/am335x/pru_ctrl.h" #include "external/ti-pru-support/include/am335x/pru_iep.h" #include "lib/intc/intc.h" #include "lib/soc/sysevts.h" #include "lib/time/clock-defs.h" -// Device-Specific PRU Read Latency Values Appendix A.1 says 12 cycles -// to read IEP_TIMER. - // This is power-of-two to simplify the logic. // #define RESOLUTION (1 << 30) #define RESOLUTION (1 << 16) @@ -19,6 +17,8 @@ Timestamp __clock; void UpdateClock(void) { + // Device-Specific PRU Read Latency Values Appendix A.1 indicates + // 12 cycles to read IEP_TIMER. uint32_t ts = CT_IEP.TMR_CNT; if (ts < __clock.CYCLES_bit.LOW) { @@ -53,9 +53,35 @@ void TimeInit(void) { } void TimeStart(void) { +#if SUPRUGLUE_PRU_NUM == 0 + PRU0_CTRL.CTRL_bit.CTR_EN = 1; +#elif SUPRUGLUE_PRU_NUM == 1 + PRU1_CTRL.CTRL_bit.CTR_EN = 1; +#endif + CT_IEP.TMR_GLB_CFG_bit.CNT_EN = 1; } +void TimedSwitch(void) { + uint32_t run; + uint32_t stall; +#if SUPRUGLUE_PRU_NUM == 0 + run = PRU0_CTRL.CYCLE; + stall = PRU0_CTRL.STALL; + // PRU0_CTRL.CTRL_bit.CTR_EN = 0; + PRU0_CTRL.CYCLE = 0xffffffff; + // PRU0_CTRL.CTRL_bit.CTR_EN = 1; +#elif SUPRUGLUE_PRU_NUM == 1 + run = PRU1_CTRL.CYCLE; + stall = PRU1_CTRL.STALL; + // PRU1_CTRL.CTRL_bit.CTR_EN = 0; + PRU1_CTRL.CYCLE = 0xffffffff; + // PRU1_CTRL.CTRL_bit.CTR_EN = 1; +#endif + __system_current->usage.run.CYCLES += run; + __system_current->usage.stall.CYCLES += stall; +} + void ReadClock(Timestamp *clock) { UpdateClock(); *clock = __clock; diff --git a/lib/time/am335x/clock-am335x.h b/lib/time/am335x/clock-am335x.h index 197ace5..bed6943 100644 --- a/lib/time/am335x/clock-am335x.h +++ b/lib/time/am335x/clock-am335x.h @@ -10,6 +10,8 @@ extern "C" { #endif +#define TIME_SECOND 200000000 + extern Timestamp __clock; #ifdef __cplusplus diff --git a/lib/time/am335x/iep_tim_cap_cmp_pend.yaml b/lib/time/am335x/iep_tim_cap_cmp_pend.yaml index 57fa6bb..0b7edc1 100644 --- a/lib/time/am335x/iep_tim_cap_cmp_pend.yaml +++ b/lib/time/am335x/iep_tim_cap_cmp_pend.yaml @@ -1,5 +1,4 @@ incoming: - event: SYSEVT_PR1_IEP_TIM_CAP_CMP_PEND channel: 0 - host: 0 - desc: From EIP to PRU channel 0 on host 0 + desc: EIP timer counter diff --git a/lib/time/clock-defs.h b/lib/time/clock-defs.h index 077578f..a00e13f 100644 --- a/lib/time/clock-defs.h +++ b/lib/time/clock-defs.h @@ -17,6 +17,8 @@ void TimeInit(void); void TimeStart(void); +void TimedSwitch(void); + // Read the clock. void ReadClock(Timestamp *ts); diff --git a/lib/time/clock.c b/lib/time/clock.c index 748bd8c..83253f1 100644 --- a/lib/time/clock.c +++ b/lib/time/clock.c @@ -9,16 +9,15 @@ LockWord __clock_lock; ThreadList __asleep; -void Sleep(uint32_t cycles) { +void SleepUntil(Timestamp *when, uint32_t cycles) { Thread *self = __system_current; - ReadClock(&self->when); - TimeAddCycles(&self->when, cycles); + TimeAddCycles(when, cycles); + self->when = *when; ThreadListPushBack(&__asleep, self); YieldBlocked(); -} -void TimeAddCycles(Timestamp *clock, uint32_t cycles) { - clock->CYCLES += cycles; + // Note: the clock process could set the wakeup time as self->when + // and the difference could be use to detect falling behind, maybe. } diff --git a/lib/time/clock.h b/lib/time/clock.h index a708e78..c65082b 100644 --- a/lib/time/clock.h +++ b/lib/time/clock.h @@ -18,9 +18,17 @@ extern "C" { #include "lib/time/test32/clock-test32.h" #endif -void Sleep(uint32_t nanos); - +void Sleep(uint32_t cycles); void TimeAddCycles(Timestamp *clock, uint32_t cycles); +void SleepUntil(Timestamp *when, uint32_t cycles); + +inline void Sleep(uint32_t cycles) { + SleepUntil(&__system_current->when, cycles); +} + +inline void TimeAddCycles(Timestamp *clock, uint32_t cycles) { + clock->CYCLES += cycles; +} #ifdef __cplusplus } diff --git a/lib/time/clock_test.cc b/lib/time/clock_test.cc index 3ec4399..a081833 100644 --- a/lib/time/clock_test.cc +++ b/lib/time/clock_test.cc @@ -4,6 +4,7 @@ #include "absl/log/log.h" #include "absl/strings/str_format.h" #include "lib/coroutine/coroutine.h" +#include "lib/intc/service.h" #include "lib/log/daemon/daemon.h" #include "lib/log/fmt/fmt.h" #include "lib/rpmsg/rpmsg.h" @@ -17,25 +18,23 @@ void test_write_func(ThreadID tid, Args args) { int32_t cnt = Atoi(args.ptr); for (int32_t i = 0; i < cnt; i++) { - PRULOG_2U(INFO_BLOCK, "write %u", i, 0); // Logs always yield + PRULOG_1u32(INFO_NOYIELD, "write %u", i); // Logs always yield Sleep(10); } } SUPRUGLUE_DEFINE_THREAD(writer, 500); -SUPRUGLUE_DEFINE_THREAD(syslog, 500); TEST(ClockTest, SleepWake) { auto tt = NewTestTransport(); EXPECT_EQ(0, Init(NewSystemConfig())); - - EXPECT_EQ(0, SystemOnChipIsShutdown()); + EXPECT_EQ(0, InterruptServiceInit()); ClockInit(); + SyslogInit(); EXPECT_EQ(0, Create(&writer.thread, test_write_func, Args{.ptr = "100"}, "writer", sizeof(writer.space))); - EXPECT_EQ(0, Create(&syslog.thread, SyslogProcess, Args{.ptr = ""}, "syslog", sizeof(syslog.space))); std::unordered_set res; std::unordered_set expect; @@ -58,7 +57,7 @@ TEST(ClockTest, SleepWake) { res.insert(Format(&entry)); got += 1; } - SystemOnChipShutdown(); + Shutdown(); }); EXPECT_EQ(0, ::Run()); diff --git a/lib/time/process.c b/lib/time/process.c index 7ead6b7..a84dd06 100644 --- a/lib/time/process.c +++ b/lib/time/process.c @@ -23,12 +23,10 @@ void clockProcess(ThreadID thid, Args args) { while (p != &__asleep) { Thread *th = ThreadListEntry(p); + p = p->next; int runnable = clk.CYCLES >= th->when.CYCLES; if (runnable) { ThreadListRemove(th); - } - p = p->next; - if (runnable) { ThreadListPushFront(&__system_runnable, th); } } diff --git a/lib/time/test32/BUILD b/lib/time/test32/BUILD index cd65b96..b271965 100644 --- a/lib/time/test32/BUILD +++ b/lib/time/test32/BUILD @@ -10,8 +10,8 @@ cc_library( "clock-test32.h", ], deps = [ - "//lib/thread", "//lib/intc", + "//lib/thread", "//lib/time:defs", "@com_google_absl//absl/log", ], diff --git a/lib/time/test32/clock-test32.cc b/lib/time/test32/clock-test32.cc index 45e714c..351a3d9 100644 --- a/lib/time/test32/clock-test32.cc +++ b/lib/time/test32/clock-test32.cc @@ -14,6 +14,7 @@ using std::chrono::high_resolution_clock; using std::chrono::nanoseconds; high_resolution_clock::time_point started; +high_resolution_clock::time_point switched; thread *source; void Tick(void) { @@ -32,10 +33,21 @@ void TimeInit(void) { void TimeStart(void) { started = high_resolution_clock::now(); + switched = started; } void ReadClock(Timestamp *ts) { auto now = high_resolution_clock::now(); - ts->CYCLES = duration_cast(now - started).count() / 5; + ts->CYCLES = duration_cast(now - started).count(); +} + +void TimedSwitch(void) { + auto now = high_resolution_clock::now(); + auto run = duration_cast(now - switched).count(); + + __system_current->usage.run.CYCLES += run; + __system_current->usage.stall.CYCLES += run; + + switched = now; } diff --git a/lib/time/test32/clock-test32.h b/lib/time/test32/clock-test32.h index 889eacb..db47bd8 100644 --- a/lib/time/test32/clock-test32.h +++ b/lib/time/test32/clock-test32.h @@ -10,6 +10,8 @@ extern "C" { #include "lib/time/clock-defs.h" +#define TIME_SECOND 10000000 + #ifdef __cplusplus } #endif diff --git a/local.sh b/local.sh index 9e3758a..7e31bca 100755 --- a/local.sh +++ b/local.sh @@ -1,12 +1,15 @@ #!/bin/sh +PRU=pru0 +EXAMPLE=pwm_outin +REMOTEPROC=/sys/class/remoteproc/remoteproc1 + REMOTE=./remote.sh -FIRMWARE=bazel-out/pru-fastbuild/bin/examples/example_pru0 +FIRMWARE=bazel-out/pru-fastbuild/bin/examples/${EXAMPLE}/${EXAMPLE} SUPRUCTL=bazel-bin/tools/cmd/supructl/supructl_/supructl - BONE=${BONE:-beaglebone.local} -bazel build --config=pru0 //examples:example_pru0 || exit 1 +bazel build --config=${PRU} //examples/${EXAMPLE} || exit 1 bazel build --config=arm //tools/cmd/supructl || exit 1 echo "Copying" @@ -15,4 +18,4 @@ scp -q -r -p ${REMOTE} ${FIRMWARE} ${SUPRUCTL} debian@${BONE}: echo "Running" -ssh -q debian@${BONE} ${REMOTE} +ssh -q debian@${BONE} "${REMOTE} ${REMOTEPROC} ${EXAMPLE}" diff --git a/remote.sh b/remote.sh index 714837b..eafb185 100755 --- a/remote.sh +++ b/remote.sh @@ -5,12 +5,13 @@ trap cleanup 1 2 3 6 PID="" # TODO this is ... -ALL_GPIOs="gpio10 gpio11 gpio110 gpio111 gpio112 gpio113 gpio114 gpio115 gpio116 gpio117 gpio12 gpio13 gpio14 gpio15 gpio2 gpio20 gpio22 gpio23 gpio26 gpio27 gpio3 gpio30 gpio31 gpio32 gpio33 gpio34 gpio35 gpio36 gpio37 gpio38 gpio39 gpio4 gpio44 gpio45 gpio46 gpio47 gpio48 gpio49 gpio5 gpio50 gpio51 gpio60 gpio61 gpio62 gpio63 gpio65 gpio66 gpio67 gpio68 gpio69 gpio7 gpio70 gpio71 gpio72 gpio73 gpio74 gpio75 gpio76 gpio77 gpio78 gpio79 gpio8 gpio80 gpio81 gpio86 gpio87 gpio88 gpio89 gpio9" +#ALL_GPIOs="gpio10 gpio11 gpio110 gpio111 gpio112 gpio113 gpio114 gpio115 gpio116 gpio12 gpio13 gpio14 gpio15 gpio2 gpio20 gpio22 gpio23 gpio26 gpio27 gpio3 gpio30 gpio31 gpio32 gpio33 gpio34 gpio35 gpio36 gpio37 gpio38 gpio39 gpio4 gpio44 gpio45 gpio46 gpio47 gpio48 gpio49 gpio5 gpio50 gpio51 gpio60 gpio61 gpio62 gpio63 gpio65 gpio66 gpio67 gpio68 gpio69 gpio7 gpio70 gpio71 gpio72 gpio73 gpio74 gpio75 gpio76 gpio77 gpio78 gpio79 gpio8 gpio80 gpio81 gpio86 gpio87 gpio88 gpio89 gpio9" -#OUT_GPIOs="gpio117 gpio115" -OUT_GPIOs=${ALL_GPIOs} +#OUT_GPIOs="gpio17 gpio115" +#OUT_GPIOs=${ALL_GPIOs} -IN_GPIOs="" +OUT_GPIOs="gpio49" # P9.23 +IN_GPIOs="gpio117 gpio60" # P9.25 P9.12 LEDs="beaglebone:green:usr0 beaglebone:green:usr1 beaglebone:green:usr2 beaglebone:green:usr3" @@ -36,6 +37,10 @@ configPins() { configPins +# Works! +config-pin P9_14 pwm + + # echo "Stopping ..." # echo stop > /sys/class/remoteproc/remoteproc1/state # echo stop > /sys/class/remoteproc/remoteproc2/state @@ -49,8 +54,10 @@ configPins # echo "State: ..." # cat /sys/class/remoteproc/remoteproc1/state +RP=$1 +FW=$2 + mv supructl ./bin -mv example_pru0 ./fw +mv ${FW} ./fw -./bin/supructl start --firmware ./fw/example_pru0 -#./bin/supructl rodata --firmware ./fw/example_pru0 +./bin/supructl start --remoteproc ${RP} --firmware ./fw/${FW} diff --git a/save/cap/BUILD b/save/cap/BUILD new file mode 100644 index 0000000..64c518e --- /dev/null +++ b/save/cap/BUILD @@ -0,0 +1,25 @@ +# -*- Mode: Python -*- +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "defs", + hdrs = [ + "cap-defs.h", + ], + deps = [ + "//lib/pinmap", + ], +) + +cc_library( + name = "cap", + hdrs = [ + "cap.h", + ], + deps = [ + ":defs", + ] + select({ + "//tools/toolchain:am335x": ["//lib/cap/am335x"], + "//conditions:default": ["//lib/cap/test32"], + }), +) diff --git a/save/cap/am335x/#cap-am335x.c# b/save/cap/am335x/#cap-am335x.c# new file mode 100644 index 0000000..8236574 --- /dev/null +++ b/save/cap/am335x/#cap-am335x.c# @@ -0,0 +1,8 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#include "cap-am335x.h" +#include "external/ti-pru-support/include/am335x/pru_ecap.h" + +void CAP_Init(void) { +} diff --git a/save/cap/am335x/.#cap-am335x.c b/save/cap/am335x/.#cap-am335x.c new file mode 120000 index 0000000..37c8d2f --- /dev/null +++ b/save/cap/am335x/.#cap-am335x.c @@ -0,0 +1 @@ +josh.macdonald@Joshuas-MacBook-Pro.local.35299 \ No newline at end of file diff --git a/save/cap/am335x/BUILD b/save/cap/am335x/BUILD new file mode 100644 index 0000000..54ab2a4 --- /dev/null +++ b/save/cap/am335x/BUILD @@ -0,0 +1,16 @@ +# -*- Mode: Python -*- +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "am335x", + hdrs = [ + "cap-am335x.h", + ], + srcs = [ + "cap-am335x.c", + ], + deps = [ + "//lib/cap:defs", + "//tools/toolchain/am335x", + ], +) diff --git a/save/cap/am335x/cap-am335x.c b/save/cap/am335x/cap-am335x.c new file mode 100644 index 0000000..72ba64d --- /dev/null +++ b/save/cap/am335x/cap-am335x.c @@ -0,0 +1,9 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#include "cap-am335x.h" +#include "external/ti-pru-support/include/am335x/pru_ecap.h" + +void CAP_Init(void) { + CT_ECAP. +} diff --git a/save/cap/am335x/cap-am335x.h b/save/cap/am335x/cap-am335x.h new file mode 100644 index 0000000..f4de9e4 --- /dev/null +++ b/save/cap/am335x/cap-am335x.h @@ -0,0 +1,17 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#include "lib/cap/cap-defs.h" + +#ifndef LIB_CAP_AM335X_CAP_H +#define LIB_CAP_AM335X_CAP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif // LIB_CAP_AM335X_CAP_H diff --git a/save/cap/cap-defs.h b/save/cap/cap-defs.h new file mode 100644 index 0000000..48229c9 --- /dev/null +++ b/save/cap/cap-defs.h @@ -0,0 +1,19 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#ifndef LIB_CAP_CAP_DEFS_H +#define LIB_CAP_CAP_DEFS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lib/soc/soc.h" + +void CAP_Init(void); + +#ifdef __cplusplus +} +#endif + +#endif // LIB_CAP_CAP_DEFS_H diff --git a/save/cap/cap.h b/save/cap/cap.h new file mode 100644 index 0000000..f71d030 --- /dev/null +++ b/save/cap/cap.h @@ -0,0 +1,27 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#ifndef LIB_CAP_CAP_H +#define LIB_CAP_CAP_H + +#include "lib/pinmap/pinmap.h" +#include "lib/soc/soc.h" +#include + +#include "lib/cap/cap-defs.h" + +#if defined(SUPRUGLUE_AM335X) +#include "lib/cap/am335x/cap-am335x.h" +#elif defined(SUPRUGLUE_TEST32) +#include "lib/cap/test32/cap-test32.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif // LIB_GPIO_GPIO_H diff --git a/save/cap/test32/BUILD b/save/cap/test32/BUILD new file mode 100644 index 0000000..e8c22d5 --- /dev/null +++ b/save/cap/test32/BUILD @@ -0,0 +1,24 @@ +# -*- Mode: Python -*- +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "test32", + srcs = [ + "cap-test32.cc", + ], + hdrs = [ + "cap-test32.h", + ], + deps = [ + "//lib/cap:defs", + ], +) + +cc_test( + name = "test", + srcs = ["cap_test.cc"], + deps = [ + "//lib/cap", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/save/cap/test32/cap-test32.cc b/save/cap/test32/cap-test32.cc new file mode 100644 index 0000000..af35d75 --- /dev/null +++ b/save/cap/test32/cap-test32.cc @@ -0,0 +1,9 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#include + +#include "cap-test32.h" + +void CAP_Init(void) { +} diff --git a/save/cap/test32/cap-test32.h b/save/cap/test32/cap-test32.h new file mode 100644 index 0000000..41b9f1b --- /dev/null +++ b/save/cap/test32/cap-test32.h @@ -0,0 +1,20 @@ +// Copyright Joshua MacDonald +// SPDX-License-Identifier: MIT + +#ifndef LIB_CAP_TEST32_CAP_H +#define LIB_CAP_TEST32_CAP_H + +#include + +#include "lib/cap/cap-defs.h" +#include "lib/soc/soc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif // LIB_CAP_TEST32_CAP_H diff --git a/save/cap/test32/cap_test.cc b/save/cap/test32/cap_test.cc new file mode 100644 index 0000000..ad6db2c --- /dev/null +++ b/save/cap/test32/cap_test.cc @@ -0,0 +1,13 @@ +#include "lib/cap/cap.h" +#include "lib/pinmap/pinmap.h" +#include "lib/soc/soc.h" +#include "gtest/gtest.h" + +class CapTest : public testing::Test { +protected: + void SetUp() override { + } +}; + +TEST_F(CapTest, Basic) { +} diff --git a/tools/cmd/irqgen/irqgen.go b/tools/cmd/irqgen/irqgen.go index 3ad58bd..23ac8cf 100644 --- a/tools/cmd/irqgen/irqgen.go +++ b/tools/cmd/irqgen/irqgen.go @@ -20,11 +20,12 @@ func main() { } sevts, err := csv.ReadFile[arch.SystemEvent](os.Args[1]) if err != nil { - log.Fatalf("unmarshal sysevts.csv: %v\n", err) + log.Fatalf("unmarshal %s: %v\n", os.Args[1], err) } sysevtMap := arch.SystemEventMap(sevts) var combined arch.IRQs + hostIRQ := -1 for _, arg := range os.Args[2:] { obj := map[string]interface{}{} @@ -55,10 +56,20 @@ func main() { if !ok { log.Fatalf("cannot find system event %s\n", result.Incoming[i].Event) } + if result.Incoming[i].Host != nil { + if hostIRQ >= 0 { + log.Fatalf("multiple host interrupts set\n") + } + hostIRQ = *result.Incoming[i].Host + } } combined.Incoming = append(combined.Incoming, result.Incoming...) } + if hostIRQ < 0 { + log.Fatalf("missing host interrupt\n") + } + guard := strings.ToUpper("supruglue_include_irqgen_h") fmt.Printf(`// Copyright Joshua MacDonald @@ -86,13 +97,14 @@ struct pru_irq_rsc supruglue_incoming_irq_rsc = { `, os.Args[2], guard, guard, len(combined.Incoming), func() string { var sb strings.Builder sb.WriteString(" {\n") + sb.WriteString(" // { Event, Channel, Host }\n") for _, irq := range combined.Incoming { sb.WriteString(" { ") sb.WriteString(irq.Event) sb.WriteString(", ") sb.WriteString(fmt.Sprint(irq.Channel)) sb.WriteString(", ") - sb.WriteString(fmt.Sprint(irq.Host)) + sb.WriteString(fmt.Sprint(hostIRQ)) sb.WriteString("},\n") } sb.WriteString(" },\n") diff --git a/tools/cmd/supructl/main.go b/tools/cmd/supructl/main.go index 52db5d6..8eab949 100644 --- a/tools/cmd/supructl/main.go +++ b/tools/cmd/supructl/main.go @@ -88,7 +88,7 @@ func runStart(cmd *cobra.Command, _ []string) error { return fmt.Errorf("start firmware: %w", err) } - app, err := rpmsghost.New(fw) + app, err := rpmsghost.New(fw, rp) if err != nil { return fmt.Errorf("rpmsg host: %w", err) } @@ -113,7 +113,11 @@ func runJournal(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("firmware: %w", err) } - app, err := rpmsghost.New(fw) + rp, err := remoteproc.Open(*flagRemoteProcDir, *flagFirmwareDir) + if err != nil { + return fmt.Errorf("remoteproc: %w", err) + } + app, err := rpmsghost.New(fw, rp) if err != nil { return fmt.Errorf("rpmsg host: %w", err) } diff --git a/tools/internal/arch/irq.go b/tools/internal/arch/irq.go index 477b6ec..26d4cdb 100644 --- a/tools/internal/arch/irq.go +++ b/tools/internal/arch/irq.go @@ -3,7 +3,7 @@ package arch type IRQ struct { Event string `mapstructure:"event"` Channel int `mapstructure:"channel"` - Host int `mapstructure:"host"` + Host *int `mapstructure:"host"` Desc string `mapstructure:"desc"` } diff --git a/tools/internal/arch/sysevt.go b/tools/internal/arch/sysevt.go index 370df99..c4be143 100644 --- a/tools/internal/arch/sysevt.go +++ b/tools/internal/arch/sysevt.go @@ -15,7 +15,7 @@ type SystemEvent struct { func (se SystemEvent) Validate() error { if se.SignalName == "" { - return fmt.Errorf("system event empty name: %s", se.Number) + return fmt.Errorf("system event empty name: %d", se.Number()) } if _, err := strconv.ParseInt(se.EventNumber, 10, 64); err != nil { return fmt.Errorf("system event range: %w", err) diff --git a/tools/internal/elfdata/elfdata.go b/tools/internal/elfdata/elfdata.go index 40d256d..36e5e2c 100644 --- a/tools/internal/elfdata/elfdata.go +++ b/tools/internal/elfdata/elfdata.go @@ -13,6 +13,8 @@ import ( type ELF struct { addr uint64 rodata []byte + + threads map[uint64]string } func Open(fw string) (*ELF, error) { @@ -22,10 +24,32 @@ func Open(fw string) (*ELF, error) { return nil, err } + threads := map[uint64]string{} + for _, sect := range f.Sections { - if sect.Type != elf.SHT_PROGBITS || sect.Size == 0 || strings.HasPrefix(sect.Name, ".debug") { + switch { + case sect.Size == 0: + continue + case strings.HasPrefix(sect.Name, ".debug_"): + continue + case strings.HasPrefix(sect.Name, ".TI."): + continue + case strings.HasPrefix(sect.Name, ".creg."): + continue + case strings.HasPrefix(sect.Name, "__TI_"): + continue + case map[string]bool{ + ".strtab": true, + ".symtab": true, + ".shstrtab": true, + }[sect.Name]: continue } + + if strings.HasPrefix(sect.Name, ".thread.") { + threads[sect.Addr] = sect.Name[len(".thread."):] + } + fmt.Printf("%s: %s: %d bytes\n", base, sect.Name, sect.Size) } @@ -40,8 +64,9 @@ func Open(fw string) (*ELF, error) { } return &ELF{ - addr: sect.Addr, - rodata: rodata, + addr: sect.Addr, + rodata: rodata, + threads: threads, }, nil } @@ -93,3 +118,11 @@ func (elf *ELF) CStringAt(addr uint64) (string, error) { } return string(b), nil } + +func (elf *ELF) ThreadNameAt(addr uint64) (string, error) { + name, ok := elf.threads[addr] + if !ok { + return "", fmt.Errorf("no thread at this address: %u", addr) + } + return name, nil +} diff --git a/tools/internal/fwstate/BUILD b/tools/internal/fwstate/BUILD new file mode 100644 index 0000000..a0ec4b1 --- /dev/null +++ b/tools/internal/fwstate/BUILD @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "fwstate", + srcs = ["fwstate.go"], + importpath = "github.com/jmacd/supruglue/tools/internal/fwstate", + visibility = ["//tools:__subpackages__"], +) diff --git a/tools/internal/fwstate/fwstate.go b/tools/internal/fwstate/fwstate.go new file mode 100644 index 0000000..444c672 --- /dev/null +++ b/tools/internal/fwstate/fwstate.go @@ -0,0 +1,98 @@ +package fwstate + +import ( + "fmt" + "regexp" + "sort" + "sync" + "time" +) + +type metricEntry struct { + ts time.Duration + val uint64 +} + +type Metrics struct { + lock sync.Mutex + state map[string]map[string]*[2]metricEntry +} + +type noopFmt struct{} + +func (noopFmt) Format(f fmt.State, verb rune) { + _, _ = f.Write([]byte("{}")) +} + +func NewMetrics() *Metrics { + return &Metrics{ + state: map[string]map[string]*[2]metricEntry{}, + } +} + +var mre = regexp.MustCompile(`[a-zA-Z0-9_\.]+={}`) + +func (m *Metrics) Update(ts time.Duration, name, msg string, flags uint32, a, b uint64) { + m.lock.Lock() + defer m.lock.Unlock() + + cnt := 0 + if (flags & 0x3000) != 0 { + cnt++ + } + if (flags & 0x3000) != 0 { + cnt++ + } + var tfmt string + if cnt == 1 { + tfmt = fmt.Sprintf(msg, noopFmt{}) + } else { + tfmt = fmt.Sprintf(msg, noopFmt{}, noopFmt{}) + } + matches := mre.FindAllString(tfmt, -1) + if len(matches) != cnt { + panic("unhandled") + } + if 0 < cnt { + m.update1(ts, name, matches[0][:len(matches[0])-3], a) + } + if 1 < cnt { + m.update1(ts, name, matches[1][:len(matches[1])-3], b) + } +} + +func (m *Metrics) update1(ts time.Duration, tname, mname string, val uint64) { + tm, ok := m.state[tname] + if !ok { + tm = map[string]*[2]metricEntry{} + m.state[tname] = tm + } + mm, ok := tm[mname] + if !ok { + mm = new([2]metricEntry) + tm[mname] = mm + } + mm[0] = mm[1] + mm[1].ts = ts + mm[1].val = val +} + +func (m *Metrics) Show() { + m.lock.Lock() + defer m.lock.Unlock() + + var tnames []string + for tname := range m.state { + tnames = append(tnames, tname) + } + sort.Strings(tnames) + + for _, tname := range tnames { + fmt.Printf(" %s\n", tname) + + for mname, vals := range m.state[tname] { + fmt.Printf(" %s: %f%%\n", mname, 100*float64(time.Second)*float64(vals[1].val-vals[0].val)/float64(vals[1].ts-vals[0].ts)/200e9) + } + } + +} diff --git a/tools/internal/remoteproc/remoteproc.go b/tools/internal/remoteproc/remoteproc.go index b6282eb..e3d48fd 100644 --- a/tools/internal/remoteproc/remoteproc.go +++ b/tools/internal/remoteproc/remoteproc.go @@ -62,6 +62,10 @@ func openDir(dir string) error { return nil } +func (rp *RemoteProc) Directory() string { + return rp.rpDir +} + func Open(rpDir, fwDir string) (*RemoteProc, error) { rp := &RemoteProc{ rpDir: rpDir, diff --git a/tools/internal/rpmsghost/BUILD b/tools/internal/rpmsghost/BUILD index 857058d..a84548a 100644 --- a/tools/internal/rpmsghost/BUILD +++ b/tools/internal/rpmsghost/BUILD @@ -5,5 +5,9 @@ go_library( srcs = ["rpmsghost.go"], importpath = "github.com/jmacd/supruglue/tools/internal/rpmsghost", visibility = ["//tools:__subpackages__"], - deps = ["//tools/internal/firmware"], + deps = [ + "//tools/internal/firmware", + "//tools/internal/fwstate", + "//tools/internal/remoteproc", + ], ) diff --git a/tools/internal/rpmsghost/rpmsghost.go b/tools/internal/rpmsghost/rpmsghost.go index 7005c6f..d422aee 100644 --- a/tools/internal/rpmsghost/rpmsghost.go +++ b/tools/internal/rpmsghost/rpmsghost.go @@ -5,13 +5,16 @@ import ( "fmt" "log" "os" + "path" "strings" "time" "github.com/jmacd/supruglue/tools/internal/firmware" + "github.com/jmacd/supruglue/tools/internal/fwstate" + "github.com/jmacd/supruglue/tools/internal/remoteproc" ) -const deviceName = "/dev/rpmsg_pru30" +const logEntrySize = 36 type RPMsgDevice struct { file *os.File @@ -20,10 +23,18 @@ type RPMsgDevice struct { type Host struct { rpm *RPMsgDevice fw *firmware.Firmware + ms *fwstate.Metrics } -func New(fw *firmware.Firmware) (*Host, error) { - rpm, err := openRPMsgDevice() +func deviceName(rp *remoteproc.RemoteProc) string { + if path.Base(rp.Directory()) == "remoteproc1" { + return "/dev/rpmsg_pru30" + } + return "/dev/rpmsg_pru31" +} + +func New(fw *firmware.Firmware, rp *remoteproc.RemoteProc) (*Host, error) { + rpm, err := openRPMsgDevice(rp) if err != nil { return nil, err } @@ -36,59 +47,98 @@ func New(fw *firmware.Firmware) (*Host, error) { return &Host{ rpm: rpm, fw: fw, + ms: fwstate.NewMetrics(), }, nil } -const logEntrySize = 24 - func (host *Host) Run() error { - buf := make([]byte, logEntrySize) + buf := make([]byte, logEntrySize*2) // TODO: clock correction, or similar fmt.Println("rpmsg: channel open") + go func() { + t := time.NewTicker(time.Second * 5) + defer t.Stop() + for { + select { + case <-t.C: + host.ms.Show() + } + } + }() + for { dat, err := host.rpm.Read(buf) if err != nil { log.Print(err) continue } + // Interpret 4 32-bit words - if len(dat) != cap(buf) { + if len(dat) != logEntrySize { log.Print(fmt.Errorf("data should be %d bytes, was %d", logEntrySize, len(dat))) continue } - u0 := binary.LittleEndian.Uint32(dat[0:4]) - u1 := binary.LittleEndian.Uint32(dat[4:8]) - u2 := binary.LittleEndian.Uint32(dat[8:12]) - u3 := binary.LittleEndian.Uint32(dat[12:16]) - u4 := binary.LittleEndian.Uint32(dat[16:20]) - u5 := binary.LittleEndian.Uint32(dat[20:24]) - - msg, err := host.fw.ELF.CStringAt(uint64(u3)) + tid := binary.LittleEndian.Uint32(dat[0:4]) + flags := binary.LittleEndian.Uint32(dat[4:8]) + tslow := binary.LittleEndian.Uint32(dat[8:12]) + tshigh := binary.LittleEndian.Uint32(dat[12:16]) + msgptr := binary.LittleEndian.Uint32(dat[16:20]) + + var a, b uint64 + + switch { + case flags&0x1000 != 0: + a = uint64(binary.LittleEndian.Uint32(dat[20:24])) + case flags&0x2000 != 0: + a = uint64(binary.LittleEndian.Uint32(dat[20:24])) + a |= uint64(binary.LittleEndian.Uint32(dat[24:28])) << 32 + } + switch { + case flags&0x4000 != 0: + b = uint64(binary.LittleEndian.Uint32(dat[28:32])) + case flags&0x8000 != 0: + b = uint64(binary.LittleEndian.Uint32(dat[28:32])) + b |= uint64(binary.LittleEndian.Uint32(dat[32:36])) << 32 + } + + msg, err := host.fw.ELF.CStringAt(uint64(msgptr)) if err != nil || msg == "" { - msg = fmt.Sprintf("", u3) + msg = fmt.Sprintf("", msg) + } + + elapsed := 5 * time.Duration(uint64(tshigh)<<32|uint64(tslow)) + tname := "" + if name, err := host.fw.ELF.ThreadNameAt(uint64(tid)); err == nil { + tname = name } else { - // TODO Should let %d coerce uint->int - msg = strings.Replace(msg, "%u", "%d", -1) - print := fmt.Sprintf(msg, u4, u5) - if strings.Contains(print, "%!(EXTRA") { - print = fmt.Sprintf(msg, u4) - } - if !strings.Contains(print, "%!(EXTRA") { - msg = print - } + tname = fmt.Sprintf("%05x", tid) } - elapsed := 5 * time.Duration(uint64(u2)<<32|uint64(u1)) + + if (flags & 0x10) != 0 { + host.ms.Update(elapsed, tname, msg, flags, a, b) + continue + } + + msg = strings.Replace(msg, "%u", "%d", -1) + print := fmt.Sprintf(msg, a, b) + if strings.Contains(print, "%!(EXTRA") { + print = fmt.Sprintf(msg, a) + } + if !strings.Contains(print, "%!(EXTRA") { + msg = print + } + ts := elapsed.String() - fmt.Printf("%s [%05x] %s\n", ts, u0, msg) + fmt.Printf("%s [%s] %s\n", ts, tname, msg) } } const retryDelay = 100 * time.Millisecond -func openRPMsgDevice() (*RPMsgDevice, error) { +func openRPMsgDevice(rp *remoteproc.RemoteProc) (*RPMsgDevice, error) { const numTries = 10 var err error for tries := 0; tries < numTries; tries++ { @@ -96,7 +146,7 @@ func openRPMsgDevice() (*RPMsgDevice, error) { fmt.Println("rpmsg:", err) } var file *os.File - file, err = os.OpenFile(deviceName, os.O_RDWR, 0666) + file, err = os.OpenFile(deviceName(rp), os.O_RDWR, 0666) if err != nil { time.Sleep(retryDelay) continue diff --git a/tools/toolchain/BUILD b/tools/toolchain/BUILD index bde84a0..5122262 100644 --- a/tools/toolchain/BUILD +++ b/tools/toolchain/BUILD @@ -52,12 +52,36 @@ constraint_value( constraint_setting = ":pru_soc", ) +constraint_setting(name = "pru_num") + +constraint_value( + name = "pru0", + constraint_setting = ":pru_num", +) + +constraint_value( + name = "pru1", + constraint_setting = ":pru_num", +) + # See https://github.com/bazelbuild/rules_go/issues/2591 # why for Go toolchain selection we list host platform os/cpu. platform( - name = "real_pru", + name = "real_pru0", + constraint_values = [ + ":am335x", + ":pru0", + "@platforms//os:osx", + "@platforms//cpu:arm64", + "@io_bazel_rules_go//go/toolchain:cgo_off", + ], +) + +platform( + name = "real_pru1", constraint_values = [ ":am335x", + ":pru1", "@platforms//os:osx", "@platforms//cpu:arm64", "@io_bazel_rules_go//go/toolchain:cgo_off", diff --git a/tools/toolchain/am335x/BUILD b/tools/toolchain/am335x/BUILD index 1f0a243..83211c7 100644 --- a/tools/toolchain/am335x/BUILD +++ b/tools/toolchain/am335x/BUILD @@ -2,31 +2,33 @@ package(default_visibility = ["//visibility:public"]) filegroup( - name = "pru0_cmd", + name = "pru0_linker_cmd", srcs = [ "pru0.cmd", ], ) filegroup( - name = "pru1_cmd", + name = "pru1_linker_cmd", srcs = [ "pru1.cmd", ], ) filegroup( - name = "pru0_irq", - srcs = [ - "pru0.yaml", - ], -) - -filegroup( - name = "pru1_irq", - srcs = [ - "pru1.yaml", - ], + name = "linker_cmd", + srcs = select({ + "//tools/toolchain:pru0": [ + "pru0.cmd", + ], + "//tools/toolchain:pru1": [ + "pru1.cmd", + ], + }), + target_compatible_with = select({ + "//tools/toolchain:am335x": [], + "//conditions:default": ["@platforms//:incompatible"], + }), ) cc_library(