diff --git a/dv/verilator/top_verilator.sv b/dv/verilator/top_verilator.sv index 1bce3a8e..3def9203 100644 --- a/dv/verilator/top_verilator.sv +++ b/dv/verilator/top_verilator.sv @@ -275,19 +275,21 @@ module top_verilator (input logic clk_i, rst_ni); // these signals are re-timed through a single register stage simply to prevent Verilator // warnings about circular combinational logic which assesses circularity at the net level // (i.e. `in_from_pins` and `out_to_pins`) rather than the bit level. - reg [4:0] loopback_q; + reg [5:0] loopback_q; always @(posedge clk_i) begin loopback_q <= {inout_to_pins[INOUT_PIN_AH_TMPIO8], inout_to_pins[INOUT_PIN_AH_TMPIO1], out_to_pins[OUT_PIN_MB10], // mikroBUS CLick PWM -> PMOD0.1; PWM loopback out_to_pins[OUT_PIN_MB7], // mikroBUS Click TX -> RX; UART loopback. - out_to_pins[OUT_PIN_MB4]}; // mikroBUS Click COPI -> CIPO; SPI loopback. + out_to_pins[OUT_PIN_MB4], // mikroBUS Click COPI -> CIPO; SPI loopback. + inout_to_pins[INOUT_PIN_PMOD0_8]}; // PMOD0 8->10; PWM loopback end assign {inout_from_pins[INOUT_PIN_AH_TMPIO9], inout_from_pins[INOUT_PIN_AH_TMPIO0], inout_from_pins[INOUT_PIN_PMOD0_1], in_from_pins[IN_PIN_MB8], - in_from_pins[IN_PIN_MB3]} = loopback_q; + in_from_pins[IN_PIN_MB3], + inout_from_pins[INOUT_PIN_PMOD0_10]} = loopback_q; // Switch inputs have pull-ups and switches pull to ground when on, but in `top_sonata` // they are inverted, so 0 here means 'not pressed' or 'off'. diff --git a/sw/cheri/common/sonata-devices.hh b/sw/cheri/common/sonata-devices.hh index caf3ddb0..b71f91aa 100644 --- a/sw/cheri/common/sonata-devices.hh +++ b/sw/cheri/common/sonata-devices.hh @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -22,6 +23,7 @@ typedef CHERI::Capability CapRoot; typedef volatile SonataGpioBoard *GpioPtr; +typedef volatile SonataPwm *PwmPtr; typedef volatile OpenTitanUart *UartPtr; typedef volatile OpenTitanUsbdev *UsbdevPtr; typedef volatile OpenTitanI2c *I2cPtr; @@ -40,6 +42,13 @@ using PinmuxPtrs = std::pair; return gpio; } +[[maybe_unused]] static PwmPtr pwm_ptr(CapRoot root) { + CHERI::Capability pwm = root.cast(); + pwm.address() = PWM_ADDRESS; + pwm.bounds() = PWM_BOUNDS * PWM_NUM; + return pwm; +} + [[maybe_unused]] static UartPtr uart_ptr(CapRoot root, uint32_t idx = 0) { CHERI::Capability uart = root.cast(); assert(idx < UART_NUM); diff --git a/sw/cheri/tests/pinmux_tests.hh b/sw/cheri/tests/pinmux_tests.hh index 151d95d6..ee7c67b6 100644 --- a/sw/cheri/tests/pinmux_tests.hh +++ b/sw/cheri/tests/pinmux_tests.hh @@ -12,6 +12,7 @@ #include "../common/block_tests.hh" #include "test_runner.hh" #include "i2c_tests.hh" +#include "pwm_tests.hh" #include #include #include @@ -32,6 +33,7 @@ using namespace SonataPinmux; * - mikroBus (P7) RX & TX * - Arduino Shield (P4) D0 & D1 * - Arduino Shield (P4) D8 & D9 + * - PMOD0 8 & 10 * This can be overriden via a compilation flag. */ #ifndef PINMUX_CABLE_CONNECTIONS_AVAILABLE @@ -310,6 +312,55 @@ static int pinmux_gpio_test(PinmuxPtrs sinks, SonataGpioFull *gpio) { return failures; } +static int pinmux_pwm_test(PinmuxPtrs sinks, PwmPtr pwm, SonataGpioFull *gpio, Log &log) { + constexpr uint8_t PmxPmod0_8ToPwmOut2 = 2; + constexpr uint8_t PmxPmod0Gpio7ToPmod0_10 = 1; + + constexpr GpioPin GpioPinInput = {GpioInstance::Pmod0, 7}; + constexpr uint8_t PwmInstance = 2; + + int failures = 0; + auto pmod0_8 = std::get(sinks)->get(PinSink::pmod0_8); + auto pmod0_gpio7 = std::get(sinks)->get(BlockSink::gpio_2_ios_7); + + constexpr size_t NumLoopbackTests = 3; + constexpr uint8_t Periods[NumLoopbackTests] = {255, 255, 128}; + constexpr uint8_t DutyCycles[NumLoopbackTests] = {213, 128, 40}; + + // Configure the PMOD 0 Pin 10 as GPIO input. + set_gpio_output_enable(gpio, GpioPinInput, false); + + // Ensure that the GPIO (PMOD 0 Pin 10) and PWM (PMOD 0 Pin 8) are enabled via Pinmux + failures += !pmod0_8.select(PmxPmod0_8ToPwmOut2); + failures += !pmod0_gpio7.select(PmxPmod0Gpio7ToPmod0_10); + + // Check that the PWM works as expected in loopback tests + for (uint8_t i = 0; i < NumLoopbackTests; i++) { + failures += pwm_loopback_test(gpio->pmod0, GpioPinInput.bit, pwm, PwmInstance, Periods[i], DutyCycles[i], + NumPwmCyclesObserved, AllowedCycleDeviation, log); + } + + // Disable the PWM via pinmux, and check that the test now fails: + pmod0_8.disable(); + for (uint8_t i = 0; i < NumLoopbackTests; i++) { + failures += !pwm_loopback_test(gpio->pmod0, GpioPinInput.bit, pwm, PwmInstance, Periods[i], DutyCycles[i], + NumPwmCyclesObserved, AllowedCycleDeviation, log); + } + + // Re-enable the PWM via pinmux, and check that the test now passes again + failures += !pmod0_8.select(PmxPmod0_8ToPwmOut2); + for (uint8_t i = 0; i < NumLoopbackTests; i++) { + failures += pwm_loopback_test(gpio->pmod0, GpioPinInput.bit, pwm, PwmInstance, Periods[i], DutyCycles[i], + NumPwmCyclesObserved, AllowedCycleDeviation, log); + } + + // Reset muxed pins to not interfere with future tests + pmod0_8.default_selection(); + pmod0_gpio7.default_selection(); + + return failures; +} + /** * Test the muxing capability of pinmux, by dynamically switching between using * (and testing) UART and pinmux on the same two pins - specifically the Arduino @@ -387,6 +438,8 @@ void pinmux_tests(CapRoot root, Log &log) { I2cPtr i2c0 = i2c_ptr(root, 0); I2cPtr i2c1 = i2c_ptr(root, 1); + PwmPtr pwm = pwm_ptr(root); + // Create bounded capabilities for the full range of GPIO SonataGpioFull gpio_full = get_full_gpio_ptrs(root); @@ -424,6 +477,11 @@ void pinmux_tests(CapRoot root, Log &log) { test_failed |= (failures > 0); write_test_result(log, failures); + log.print(" Running PWM Pinmux test... "); + failures = pinmux_pwm_test(sinks, pwm, &gpio_full, log); + test_failed |= (failures > 0); + write_test_result(log, failures); + log.print(" Running UART Pinmux test... "); failures = pinmux_uart_test(sinks, prng, uart1); test_failed |= (failures > 0); diff --git a/sw/cheri/tests/pwm_tests.hh b/sw/cheri/tests/pwm_tests.hh index 638c7496..5d265d27 100644 --- a/sw/cheri/tests/pwm_tests.hh +++ b/sw/cheri/tests/pwm_tests.hh @@ -9,7 +9,6 @@ #include "test_runner.hh" #include #include -#include using namespace CHERI; @@ -54,11 +53,9 @@ constexpr bool LogPwmTests = false; * * @returns The integer number of failures during the test */ -int pwm_loopback_test(Capability gpio_pmod0, Capability pwm, - uint8_t period, uint8_t duty_cycle, uint8_t cycles, uint8_t error_delta, Log &log) { - constexpr uint8_t PwmInstance = 0; - constexpr uint8_t InputPin = 0; - +int pwm_loopback_test(Capability gpio_pmod0, uint8_t input_pin, PwmPtr pwm, + uint8_t pwm_instance, uint8_t period, uint8_t duty_cycle, uint8_t cycles, uint8_t error_delta, + Log &log) { // Calculate error bounds & timeouts for use in testing const uint32_t period_lower = period - (error_delta > period ? period : error_delta); const uint32_t period_upper = period + error_delta; @@ -69,34 +66,34 @@ int pwm_loopback_test(Capability gpio_pmod0, Capabilit int failures = 0; // Configure GPIO, timer and PWM respectively. - gpio_pmod0->set_output_enable(InputPin, false); + gpio_pmod0->set_output_enable(input_pin, false); reset_mcycle(); - pwm->output_set(PwmInstance, period, duty_cycle); + pwm->output_set(pwm_instance, period, duty_cycle); uint32_t cycles_observed = 0; - const bool inputState = gpio_pmod0->read_input(0); + const bool inputState = gpio_pmod0->read_input(input_pin); while (cycles_observed < cycles) { // Wait for the next cycle to avoid missing measurements due to arithmetic/logging. uint32_t start_mcycle = get_mcycle(); uint32_t timeout_cycle = start_mcycle + timeout; - while (gpio_pmod0->read_input(0) == inputState && get_mcycle() < timeout_cycle) { + while (gpio_pmod0->read_input(input_pin) == inputState && get_mcycle() < timeout_cycle) { asm volatile(""); } start_mcycle = get_mcycle(); timeout_cycle = start_mcycle + timeout; - while (gpio_pmod0->read_input(0) != inputState && get_mcycle() < timeout_cycle) { + while (gpio_pmod0->read_input(input_pin) != inputState && get_mcycle() < timeout_cycle) { asm volatile(""); } // Measure PWM period & duty cycle times start_mcycle = get_mcycle(); timeout_cycle = start_mcycle + timeout; - while (gpio_pmod0->read_input(0) == inputState && get_mcycle() < timeout_cycle) { + while (gpio_pmod0->read_input(input_pin) == inputState && get_mcycle() < timeout_cycle) { asm volatile(""); } uint32_t changed_mcycle = get_mcycle(); timeout_cycle = changed_mcycle + timeout; - while (gpio_pmod0->read_input(0) != inputState && get_mcycle() < timeout_cycle) { + while (gpio_pmod0->read_input(input_pin) != inputState && get_mcycle() < timeout_cycle) { asm volatile(""); } uint32_t end_mcycle = get_mcycle(); @@ -204,9 +201,7 @@ void pwm_tests(CapRoot root, Log &log) { gpio_pmod0.bounds() = GPIO_BOUNDS; // Create bounded capability for PWM - Capability pwm = root.cast(); - pwm.address() = PWM_ADDRESS; - pwm.bounds() = PWM_BOUNDS; + PwmPtr pwm = pwm_ptr(root); // Execute the specified number of iterations of each test. for (size_t i = 0; i < PWM_TEST_ITERATIONS; i++) { @@ -226,8 +221,8 @@ void pwm_tests(CapRoot root, Log &log) { uint8_t period = periods[i]; uint8_t duty_cycle = duty_cycles[i]; log.print(" Running PWM Loopback ({}/{}) test... ", duty_cycle, period); - failures = - pwm_loopback_test(gpio_pmod0, pwm, period, duty_cycle, NumPwmCyclesObserved, AllowedCycleDeviation, log); + failures = pwm_loopback_test(gpio_pmod0, 0, pwm, 0, period, duty_cycle, NumPwmCyclesObserved, + AllowedCycleDeviation, log); test_failed |= (failures > 0); write_test_result(log, failures); }