diff --git a/sw/device/tests/BUILD b/sw/device/tests/BUILD index 008023a2bd0670..4d4f7cf4f5e5f2 100644 --- a/sw/device/tests/BUILD +++ b/sw/device/tests/BUILD @@ -2836,17 +2836,34 @@ opentitan_test( { "//hw/top_earlgrey:fpga_cw340_rom_with_fake_keys": None, "//hw/top_earlgrey:silicon_owner_sival_rom_ext": None, + "//hw/top_earlgrey:fpga_cw310_sival": None, } ), + fpga = fpga_params( + test_cmd = """ + --bootstrap="{firmware}" + "{firmware:elf}" + """, + test_harness = "//sw/host/tests/chip/pwm_smoketest", + ), + silicon = silicon_params( + test_cmd = """ + --bootstrap="{firmware}" + "{firmware:elf}" + """, + test_harness = "//sw/host/tests/chip/pwm_smoketest", + ), deps = [ "//hw/top_earlgrey/sw/autogen:top_earlgrey", "//sw/device/lib/dif:pinmux", "//sw/device/lib/dif:pwm", + "//sw/device/lib/dif:gpio", "//sw/device/lib/runtime:hart", "//sw/device/lib/runtime:ibex", "//sw/device/lib/runtime:irq", "//sw/device/lib/runtime:log", "//sw/device/lib/testing/test_framework:ottf_main", + "//sw/device/lib/testing/test_framework:ottf_utils", ], ) diff --git a/sw/device/tests/pwm_smoketest.c b/sw/device/tests/pwm_smoketest.c index 512f4c03208f74..06955570c2ba08 100644 --- a/sw/device/tests/pwm_smoketest.c +++ b/sw/device/tests/pwm_smoketest.c @@ -5,77 +5,107 @@ #include #include "sw/device/lib/base/mmio.h" +#include "sw/device/lib/dif/dif_gpio.h" #include "sw/device/lib/dif/dif_pinmux.h" #include "sw/device/lib/dif/dif_pwm.h" #include "sw/device/lib/runtime/hart.h" #include "sw/device/lib/runtime/log.h" #include "sw/device/lib/testing/test_framework/check.h" #include "sw/device/lib/testing/test_framework/ottf_main.h" +#include "sw/device/lib/testing/test_framework/ottf_utils.h" #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" -OTTF_DEFINE_TEST_CONFIG(); - -static const uint32_t kClocksKhz[] = [1, 5, 10]; -static const uint32_t kDutyCicles[] = [0, 50, 100]; - -// Test harness will backdoor write to this variable. -static volatile uint8_t backdoor_start = false; - +enum { + kDefaultTimeoutMicros = 50000, +}; -static status_t pwm_pinmux(void) { - - dif_pinmux_t pinmux; - mmio_region_t base_addr = - mmio_region_from_addr(TOP_EARLGREY_PINMUX_AON_BASE_ADDR); - CHECK_DIF_OK(dif_pinmux_init(base_addr, &pinmux)); - - TRY(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIoa8, - kTopEarlgreyPinmuxOutselPwmAonPwm0)); +OTTF_DEFINE_TEST_CONFIG(); - return OK_STATUS(); -} +static volatile const uint8_t kClocksHz[] = {20, 50, 100, 200}; +static volatile const uint8_t kDutyCycles[] = {10, 30, 50, 70, 90}; // This function assumes that DEC_RESN = 0. -static uint32_t compute_clk_div(uint32_t pwm_clk) { - return kClockFreqAonHz / (2 * pwm_clk) +static dif_pwm_config_t compute_clk_config(uint32_t pwm_clk) { + enum { + // Theres a trade off on the precision of the clock and the precision of the + // dutycycle, the higher the kDutyCycleResulution the more precise is the + // the dutycycle and less precise is the pwm frequency. + // By experimentation kDutyCycleResulution would generate an error of 2.5% + // on the frequency and 6% on the dutycycle. + kDutyCycleResulution = 4, + kBeatsPerCycle = 2 << (kDutyCycleResulution + 1), // 2 ^ (DC_RESN + 1) + }; + return (dif_pwm_config_t){ + .beats_per_pulse_cycle = kBeatsPerCycle, + .clock_divisor = + ((uint32_t)kClockFreqAonHz / (kBeatsPerCycle * pwm_clk)) - 1}; } bool test_main(void) { dif_pwm_t pwm; - mmio_region_t base_addr = mmio_region_from_addr(TOP_EARLGREY_PWM_AON_BASE_ADDR); - CHECK_DIF_OK(dif_pwm_init(base_addr, &pwm)); - CHECK_STATUS_OK(pwm_pinmux()); - - for (size_t i = 0; i < ARRAYSIZE(kClocksKhz);++i){ - - dif_pwm_config_t pwm_config = { - .clock_divisor = compute_clk_div(kClocksKhz[i] * 1000), - .beats_per_pulse_cycle = 100, - }; - for (size_t j = 0; j < ARRAYSIZE(kDutyCicles);++j){ + mmio_region_t addr = mmio_region_from_addr(TOP_EARLGREY_PWM_AON_BASE_ADDR); + CHECK_DIF_OK(dif_pwm_init(addr, &pwm)); + dif_pinmux_t pinmux; + addr = mmio_region_from_addr(TOP_EARLGREY_PINMUX_AON_BASE_ADDR); + CHECK_DIF_OK(dif_pinmux_init(addr, &pinmux)); + CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIoa8, + kTopEarlgreyPinmuxOutselPwmAonPwm0)); + + dif_gpio_t gpio; + addr = mmio_region_from_addr(TOP_EARLGREY_GPIO_BASE_ADDR); + CHECK_DIF_OK(dif_gpio_init(addr, &gpio)); + CHECK_DIF_OK(dif_gpio_output_set_enabled_all(&gpio, 0x1)); + + LOG_INFO("Use IOA7 to let host know when sleep is active."); + CHECK_DIF_OK(dif_pinmux_input_select(&pinmux, + kTopEarlgreyPinmuxPeripheralInGpioGpio0, + kTopEarlgreyPinmuxInselIoa7)); + + for (size_t i = 0; i < ARRAYSIZE(kClocksHz); ++i) { + dif_pwm_config_t pwm_config = compute_clk_config(kClocksHz[i]); + LOG_INFO("div: %d", pwm_config.clock_divisor); + for (size_t j = 0; j < ARRAYSIZE(kDutyCycles); ++j) { dif_pwm_channel_config_t channel_config = { - .duty_cycle_a = pwm_config.beats_per_pulse_cycle / (100 / kDutyCicles[j]) , - .duty_cycle_b = 0, - .phase_delay = 0, - .mode = kDifPwmModeFirmware, - .polarity = kDifPwmPolarityActiveHigh, - .blink_parameter_x = 0, - .blink_parameter_y = 0, + .duty_cycle_a = + pwm_config.beats_per_pulse_cycle * kDutyCycles[j] / 100, + .duty_cycle_b = 0, + .phase_delay = 0, + .mode = kDifPwmModeFirmware, + .polarity = kDifPwmPolarityActiveHigh, + .blink_parameter_x = 0, + .blink_parameter_y = 0, }; - - OTTF_WAIT_FOR(backdoor_start, kDefaultTimeoutMicros); + LOG_INFO("dity_cycle: %d", channel_config.duty_cycle_a); CHECK_DIF_OK(dif_pwm_configure(&pwm, pwm_config)); - CHECK_DIF_OK(dif_pwm_configure_channel(&pwm, kDifPwmChannel0, channel_config)); + CHECK_DIF_OK( + dif_pwm_configure_channel(&pwm, kDifPwmChannel0, channel_config)); + + // The goes low when the host is sampling. + bool not_sampling = true; + do { + CHECK_DIF_OK(dif_gpio_read(&gpio, 0, ¬_sampling)); + if (not_sampling) { // Debauce + busy_spin_micros(500); + CHECK_DIF_OK(dif_gpio_read(&gpio, 0, ¬_sampling)); + } + } while (not_sampling); + CHECK_DIF_OK(dif_pwm_phase_cntr_set_enabled(&pwm, kDifToggleEnabled)); - CHECK_DIF_OK(dif_pwm_channel_set_enabled(&pwm, kDifPwmChannel0, kDifToggleEnabled)); + CHECK_DIF_OK(dif_pwm_channel_set_enabled(&pwm, kDifPwmChannel0, + kDifToggleEnabled)); + + // The goes high when the host stop sampling. + do { + CHECK_DIF_OK(dif_gpio_read(&gpio, 0, ¬_sampling)); + } while (!not_sampling); - busy_spin_micros(50 * 1000); - backdoor_start = false; - CHECK_DIF_OK(dif_pwm_channel_set_enabled(&pwm, kDifPwmChannel0, kDifToggleDisable)); + CHECK_DIF_OK(dif_pwm_channel_set_enabled(&pwm, kDifPwmChannel0, + kDifToggleDisabled)); CHECK_DIF_OK(dif_pwm_phase_cntr_set_enabled(&pwm, kDifToggleDisabled)); + LOG_INFO("Sync: Disabled pwm"); } } return true;