diff --git a/TESTS/mbed_hal_fpga_ci_test_shield/analogin/main.cpp b/TESTS/mbed_hal_fpga_ci_test_shield/analogin/main.cpp new file mode 100644 index 0000000000..1016c045c3 --- /dev/null +++ b/TESTS/mbed_hal_fpga_ci_test_shield/analogin/main.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2019, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !DEVICE_ANALOGIN +#error [NOT_SUPPORTED] Analog in not supported for this target +#elif !COMPONENT_FPGA_CI_TEST_SHIELD +#error [NOT_SUPPORTED] FPGA CI Test Shield is needed to run this test +#else + +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" + +#include "mbed.h" +#include "pinmap.h" +#include "test_utils.h" +#include "MbedTester.h" + +using namespace utest::v1; + +#define analogin_debug_printf(...) + +#define DELTA_FLOAT 0.01f // 1% +#define DELTA_U16 655 // 1% + +const PinList *form_factor = pinmap_ff_default_pins(); +const PinList *restricted = pinmap_restricted_pins(); + +MbedTester tester(form_factor, restricted); + +void analogin_init(PinName pin) +{ + analogin_t analogin; + + analogin_init(&analogin, pin); +} + +void analogin_test(PinName pin) +{ + tester.reset(); + tester.pin_map_set(pin, MbedTester::LogicalPinGPIO0); + tester.select_peripheral(MbedTester::PeripheralGPIO); + + /* Test analog input */ + + analogin_t analogin; + analogin_init(&analogin, pin); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + TEST_ASSERT_FLOAT_WITHIN(DELTA_FLOAT, 1.0f, analogin_read(&analogin)); + TEST_ASSERT_UINT16_WITHIN(DELTA_U16, 65535, analogin_read_u16(&analogin)); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + TEST_ASSERT_FLOAT_WITHIN(DELTA_FLOAT, 0.0f, analogin_read(&analogin)); + TEST_ASSERT_UINT16_WITHIN(DELTA_U16, 0, analogin_read_u16(&analogin)); + + /* Set gpio back to Hi-Z */ + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, false); +} + +void analogin_full_test(PinName pin) +{ + /* Test analog input */ + + printf("Testing AnalogIn\r\n"); + + // Remap pins for test + tester.reset(); + + // Reset tester stats and select GPIO + tester.peripherals_reset(); + tester.select_peripheral(MbedTester::PeripheralGPIO); + + analogin_t analogin; + analogin_init(&analogin, pin); + + //enable analog MUX + tester.set_mux_enable(true); + + //set MUX address + tester.set_mux_addr(pin); + + float voltage = 0.0; + bool enable = true; + uint32_t period = 100; + uint32_t duty_cycle = 0; + //enable FPGA system PWM + tester.set_analog_out(voltage, enable); + + TEST_ASSERT_EQUAL(true, tester.get_pwm_enable()); + TEST_ASSERT_EQUAL(period, tester.get_pwm_period()); + TEST_ASSERT_EQUAL(duty_cycle, tester.get_pwm_cycles_high()); + // test duty cycles 0-100% at 1000ns period + for (int i = 0; i < 11; i += 1) { + voltage = (float)i * 0.1f; + duty_cycle = 10 * i; + tester.set_analog_out(voltage, enable); + wait_us(10); + //read analog input + //assert pwm duty_cycle is correct based on set analog voltage + TEST_ASSERT_EQUAL(duty_cycle, tester.get_pwm_cycles_high()); + TEST_ASSERT_FLOAT_WITHIN(DELTA_FLOAT, 0.01f * (float)duty_cycle, analogin_read(&analogin)); + } + tester.set_pwm_enable(false); + TEST_ASSERT_EQUAL(false, tester.get_pwm_enable()); + wait_us(10); + + // assert Mbed pin correctly drives AnalogMuxIn + tester.pin_map_set(pin, MbedTester::LogicalPinGPIO0); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + TEST_ASSERT_EQUAL(0, tester.sys_pin_read(MbedTester::AnalogMuxIn)); + wait_us(10); + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + TEST_ASSERT_EQUAL(1, tester.sys_pin_read(MbedTester::AnalogMuxIn)); + wait_us(10); + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + TEST_ASSERT_EQUAL(0, tester.sys_pin_read(MbedTester::AnalogMuxIn)); + wait_us(10); + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, false); + wait_us(10); + tester.set_mux_enable(false); + wait_us(10); + + TEST_ASSERT_EQUAL(1, tester.self_test_control_current());//assert control channel still functioning properly +} + +Case cases[] = { + // This will be run for all pins + Case("AnalogIn - init test", all_ports), +#if defined(FULL_TEST_SHIELD) + Case("AnalogIn - full test", all_ports), +#endif + + // This will be run for single pin + Case("AnalogIn - read test", all_ports), +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(120, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + Harness::run(specification); +} + +#endif /* !DEVICE_ANALOGIN */ diff --git a/TESTS/mbed_hal_fpga_ci_test_shield/analogout/main.cpp b/TESTS/mbed_hal_fpga_ci_test_shield/analogout/main.cpp new file mode 100644 index 0000000000..d677e1cda8 --- /dev/null +++ b/TESTS/mbed_hal_fpga_ci_test_shield/analogout/main.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2019, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !DEVICE_ANALOGOUT +#error [NOT_SUPPORTED] Analog out not supported for this target +#elif !COMPONENT_FPGA_CI_TEST_SHIELD +#error [NOT_SUPPORTED] FPGA CI Test Shield is needed to run this test +#else + +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include +#include "mbed.h" + +using namespace utest::v1; + +#include "MbedTester.h" +#include "pinmap.h" +#include "test_utils.h" + +#if !DEVICE_ANALOGOUT +#error [NOT_SUPPORTED] Analog out not supported for this target +#endif + +#define analogout_debug_printf(...) + +#define DELTA_FLOAT 0.01f // 1% + +const PinList *form_factor = pinmap_ff_default_pins(); +const PinList *restricted = pinmap_restricted_pins(); + +MbedTester tester(form_factor, restricted); + +void analogout_init_free(PinName pin) +{ + dac_t analogout; + + analogout_debug_printf("Analog output init/free test on pin=%s (%i)\r\n", pinmap_ff_default_pin_to_string(pin), pin); + + analogout_init(&analogout, pin); + analogout_free(&analogout); +} + +void analogout_test(PinName pin) +{ + analogout_debug_printf("Analog input test on pin %s (%i)\r\n", pinmap_ff_default_pin_to_string(pin), pin); + + tester.reset(); + tester.peripherals_reset(); + + /* Test analog input */ + + dac_t analogout; + analogout_init(&analogout, pin); + + tester.set_sample_adc(true);//begin ADC sampling on the FPGA + + float anin; + float i; + for (i = 0.0f; i < 0.304f; i += 0.0303f) {//0V-1V, float + analogout_write(&analogout, i); + anin = tester.get_analog_in(); + TEST_ASSERT_FLOAT_WITHIN(DELTA_FLOAT, (i * 3.3f), anin); + } + + i = 0.0f; + for (uint16_t i_d16 = 0; i_d16 < 19851; i_d16 += 1985) {//0V-1V, 16-bit + analogout_write_u16(&analogout, i_d16); + anin = tester.get_analog_in(); + TEST_ASSERT_FLOAT_WITHIN(DELTA_FLOAT, (i * 3.3f), anin); + i += 0.0303f; + } + + analogout_free(&analogout); + + tester.set_sample_adc(false);//stop ADC sampling on the FPGA + + // power analysis + // uint64_t sum; + // uint32_t samples; + // uint64_t cycles; + // tester.get_anin_sum_samples_cycles(0, &sum, &samples, &cycles); + // printf("ANIN0\r\n"); + // printf("Sum: %llu\r\n", sum); + // printf("Num power samples: %d\r\n", samples); + // printf("Num power cycles: %llu\r\n", cycles); + // printf("ANIN0 voltage: %.6fV\r\n", tester.get_anin_voltage(0)); +} + +Case cases[] = { + // This will be run for all pins + Case("Analogout - init test", all_ports), + + // This will be run for single pin + Case("Analogout - write/read test", all_ports), +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(120, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + Harness::run(specification); +} + +#endif /* !DEVICE_ANALOGOUT */ diff --git a/TESTS/mbed_hal_fpga_ci_test_shield/gpio/main.cpp b/TESTS/mbed_hal_fpga_ci_test_shield/gpio/main.cpp new file mode 100644 index 0000000000..b90c39c42f --- /dev/null +++ b/TESTS/mbed_hal_fpga_ci_test_shield/gpio/main.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2019, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !COMPONENT_FPGA_CI_TEST_SHIELD +#error [NOT_SUPPORTED] FPGA CI Test Shield is needed to run this test +#else + +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "mbed.h" + +using namespace utest::v1; + +#include "MbedTester.h" +#include "pinmap.h" + +const PinList *form_factor = pinmap_ff_default_pins(); +const PinList *restricted = pinmap_restricted_pins(); +MbedTester tester(form_factor, restricted); + +void gpio_inout_test(PinName pin) +{ + gpio_t gpio; + gpio_init_inout(&gpio, pin, PIN_OUTPUT, PullNone, 0); + TEST_ASSERT_EQUAL(0, tester.gpio_read(MbedTester::LogicalPinGPIO0)); + + /* Test GPIO output */ + + gpio_write(&gpio, 1); + TEST_ASSERT_EQUAL(1, tester.gpio_read(MbedTester::LogicalPinGPIO0)); + + gpio_write(&gpio, 0); + TEST_ASSERT_EQUAL(0, tester.gpio_read(MbedTester::LogicalPinGPIO0)); + + gpio_dir(&gpio, PIN_INPUT); + + /* Test GPIO input */ + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + TEST_ASSERT_EQUAL(0, gpio_read(&gpio)); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + TEST_ASSERT_EQUAL(1, gpio_read(&gpio)); + + /* Set gpio back to Hi-Z */ + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, false); +} + +void gpio_inout_test() +{ + for (int i = 0; i < form_factor->count; i++) { + const PinName test_pin = form_factor->pins[i]; + if (pinmap_list_has_pin(restricted, test_pin)) { + printf("Skipping gpio pin %s (%i)\r\n", pinmap_ff_default_pin_to_string(test_pin), test_pin); + continue; + } + tester.pin_map_reset(); + tester.pin_map_set(test_pin, MbedTester::LogicalPinGPIO0); + + printf("GPIO test on pin %s (%i)\r\n", pinmap_ff_default_pin_to_string(test_pin), test_pin); + gpio_inout_test(test_pin); + } +} + +utest::v1::status_t setup(const Case *const source, const size_t index_of_case) +{ + tester.reset(); + tester.select_peripheral(MbedTester::PeripheralGPIO); + + return greentea_case_setup_handler(source, index_of_case); +} + +utest::v1::status_t teardown(const Case *const source, const size_t passed, const size_t failed, + const failure_t reason) +{ + return greentea_case_teardown_handler(source, passed, failed, reason); +} + +Case cases[] = { + Case("GPIO - inout", setup, gpio_inout_test, teardown), +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(60, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + Harness::run(specification); +} + +#endif /* !COMPONENT_FPGA_CI_TEST_SHIELD */ diff --git a/TESTS/mbed_hal_fpga_ci_test_shield/gpio_irq/main.cpp b/TESTS/mbed_hal_fpga_ci_test_shield/gpio_irq/main.cpp new file mode 100644 index 0000000000..f2f685c714 --- /dev/null +++ b/TESTS/mbed_hal_fpga_ci_test_shield/gpio_irq/main.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2019, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !DEVICE_INTERRUPTIN +#error [NOT_SUPPORTED] test not supported +#elif !COMPONENT_FPGA_CI_TEST_SHIELD +#error [NOT_SUPPORTED] FPGA CI Test Shield is needed to run this test +#else + +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "mbed.h" + +using namespace utest::v1; + +#include "mbed.h" +#include "MbedTester.h" +#include "pinmap.h" + +static uint32_t call_counter; +void test_gpio_irq_handler(uint32_t id, gpio_irq_event event) +{ + call_counter++; +} + +const PinList *form_factor = pinmap_ff_default_pins(); +const PinList *restricted = pinmap_restricted_pins(); +MbedTester tester(form_factor, restricted); + +#define WAIT() wait_us(10) + + +void gpio_irq_test(PinName pin) +{ + gpio_t gpio; + // configure pin as input + gpio_init_in(&gpio, pin); + + gpio_irq_t gpio_irq; + uint32_t id = 123; + gpio_irq_init(&gpio_irq, pin, test_gpio_irq_handler, id); + + gpio_irq_set(&gpio_irq, IRQ_RISE, true); + gpio_irq_enable(&gpio_irq); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + + // test irq on rising edge + call_counter = 0; + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(1, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(1, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(2, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(2, call_counter); + + gpio_irq_disable(&gpio_irq); + + call_counter = 0; + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(0, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(0, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(0, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(0, call_counter); + + gpio_irq_enable(&gpio_irq); + + call_counter = 0; + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(1, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(1, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(2, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(2, call_counter); + + // test irq on both rising and falling edge + gpio_irq_set(&gpio_irq, IRQ_FALL, true); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + + call_counter = 0; + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(1, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(2, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(3, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(4, call_counter); + + gpio_irq_disable(&gpio_irq); + + call_counter = 0; + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(0, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(0, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(0, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(0, call_counter); + + gpio_irq_enable(&gpio_irq); + + call_counter = 0; + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(1, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(2, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(3, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(4, call_counter); + + // test irq on falling edge + gpio_irq_set(&gpio_irq, IRQ_RISE, false); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + + call_counter = 0; + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(1, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(1, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(2, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(2, call_counter); + + gpio_irq_disable(&gpio_irq); + + call_counter = 0; + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(0, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(0, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(0, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(0, call_counter); + + gpio_irq_enable(&gpio_irq); + + call_counter = 0; + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(1, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(1, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true); + WAIT(); + TEST_ASSERT_EQUAL(2, call_counter); + + tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true); + WAIT(); + TEST_ASSERT_EQUAL(2, call_counter); + + + gpio_irq_free(&gpio_irq); +} + + +void gpio_irq_test() +{ + for (uint32_t i = 0; i < form_factor->count; i++) { + const PinName test_pin = form_factor->pins[i]; + if (pinmap_list_has_pin(restricted, test_pin)) { + printf("Skipping gpio pin %s (%i)\r\n", pinmap_ff_default_pin_to_string(test_pin), test_pin); + continue; + } + tester.pin_map_reset(); + tester.pin_map_set(test_pin, MbedTester::LogicalPinGPIO0); + + printf("GPIO irq test on pin %3s (%3i)\r\n", pinmap_ff_default_pin_to_string(test_pin), test_pin); + gpio_irq_test(test_pin); + } +} + +utest::v1::status_t setup(const Case *const source, const size_t index_of_case) +{ + tester.reset(); + tester.select_peripheral(MbedTester::PeripheralGPIO); + + return greentea_case_setup_handler(source, index_of_case); +} + +utest::v1::status_t teardown(const Case *const source, const size_t passed, const size_t failed, + const failure_t reason) +{ + return greentea_case_teardown_handler(source, passed, failed, reason); +} + +Case cases[] = { + Case("GPIO - irq test", setup, gpio_irq_test, teardown) +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(10, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + Harness::run(specification); +} + +#endif /* !DEVICE_INTERRUPTIN */ diff --git a/TESTS/mbed_hal_fpga_ci_test_shield/i2c/main.cpp b/TESTS/mbed_hal_fpga_ci_test_shield/i2c/main.cpp new file mode 100644 index 0000000000..ec3e09e4ac --- /dev/null +++ b/TESTS/mbed_hal_fpga_ci_test_shield/i2c/main.cpp @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2019, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !DEVICE_I2C +#error [NOT_SUPPORTED] I2C not supported for this target +#elif !COMPONENT_FPGA_CI_TEST_SHIELD +#error [NOT_SUPPORTED] FPGA CI Test Shield is needed to run this test +#elif !defined(FULL_TEST_SHIELD) +#error [NOT_SUPPORTED] The I2C test does not run on prototype hardware +#else + +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "mbed.h" + +#include "mbed.h" +#include "i2c_api.h" +#include "pinmap.h" +#include "test_utils.h" +#include "I2CTester.h" + + +using namespace utest::v1; + +#define NACK 0 +#define ACK 1 +#define TIMEOUT 2 +#define I2C_DEV_ADDR 0x98//default i2c slave address on FPGA is 0x98 until modified +const int TRANSFER_COUNT = 300; + +I2CTester tester(DefaultFormFactor::pins(), DefaultFormFactor::restricted_pins()); + +void test_i2c_init_free(PinName sda, PinName scl) +{ + i2c_t obj = {}; + memset(&obj, 0, sizeof(obj)); + i2c_init(&obj, sda, scl); + i2c_frequency(&obj, 100000); +} + +void i2c_test_all(PinName sda, PinName scl) +{ + // Remap pins for test + tester.reset(); + tester.pin_map_set(sda, MbedTester::LogicalPinI2CSda); + tester.pin_map_set(scl, MbedTester::LogicalPinI2CScl); + + // Initialize mbed I2C pins + i2c_t i2c; + memset(&i2c, 0, sizeof(i2c)); + i2c_init(&i2c, sda, scl); + i2c_frequency(&i2c, 100000); + + // Reset tester stats and select I2C + tester.peripherals_reset(); + tester.select_peripheral(I2CTester::PeripheralI2C); + + // Data out and in buffers and initialization + uint8_t data_out[TRANSFER_COUNT]; + for (int i = 0; i < TRANSFER_COUNT; i++) { + data_out[i] = i & 0xFF; + } + uint8_t data_in[TRANSFER_COUNT]; + for (int i = 0; i < TRANSFER_COUNT; i++) { + data_in[i] = 0; + } + + int num_writes; + int num_reads; + int num_acks; + int num_nacks; + int num_starts; + int num_stops; + uint32_t checksum; + int num_dev_addr_matches; + int ack_nack;//0 if NACK was received, 1 if ACK was received, 2 for timeout + + printf("I2C complete write transaction test on sda=%s (%i), scl=%s (%i)\r\n", + pinmap_ff_default_pin_to_string(sda), sda, + pinmap_ff_default_pin_to_string(scl), scl); + + // Reset tester stats and select I2C + tester.peripherals_reset(); + tester.select_peripheral(MbedTester::PeripheralI2C); + + // Write data for I2C complete transaction + // Will write 0-(TRANSFER_COUNT-1) to FPGA, checksum must match checksum calculated in parallel on FPGA + num_dev_addr_matches = 0; + num_writes = 0; + num_reads = 0; + num_starts = 0; + num_stops = 0; + num_acks = 0; + num_nacks = 0; + checksum = 0; + tester.io_metrics_start(); + num_writes = i2c_write(&i2c, I2C_DEV_ADDR, (char *)data_out, TRANSFER_COUNT, true); //transaction ends with a stop condition + num_acks = num_writes + 1; + num_starts += 1; + num_stops += 1; + num_dev_addr_matches += 1; + tester.io_metrics_stop(); + for (int i = 0; i < TRANSFER_COUNT; i++) { + checksum += data_out[i]; + } + + // Verify that the transfer was successful + TEST_ASSERT_EQUAL(num_dev_addr_matches, tester.num_dev_addr_matches()); + TEST_ASSERT_EQUAL(TRANSFER_COUNT, num_writes); + TEST_ASSERT_EQUAL(num_writes + 1, tester.transfer_count()); + TEST_ASSERT_EQUAL(num_starts, tester.num_starts()); + TEST_ASSERT_EQUAL(num_stops, tester.num_stops()); + TEST_ASSERT_EQUAL(num_acks, tester.num_acks()); + TEST_ASSERT_EQUAL(num_nacks, tester.num_nacks()); + TEST_ASSERT_EQUAL(checksum, tester.get_receive_checksum()); + TEST_ASSERT_EQUAL(0, tester.state_num()); + TEST_ASSERT_EQUAL(data_out[TRANSFER_COUNT - 4], tester.get_prev_to_slave_4()); + TEST_ASSERT_EQUAL(data_out[TRANSFER_COUNT - 3], tester.get_prev_to_slave_3()); + TEST_ASSERT_EQUAL(data_out[TRANSFER_COUNT - 2], tester.get_prev_to_slave_2()); + TEST_ASSERT_EQUAL(data_out[TRANSFER_COUNT - 1], tester.get_prev_to_slave_1()); + TEST_ASSERT_EQUAL(num_writes, tester.num_writes()); + TEST_ASSERT_EQUAL(num_reads, tester.num_reads()); + + printf(" Pin combination works\r\n"); + + printf("I2C complete read transaction test on sda=%s (%i), scl=%s (%i)\r\n", + pinmap_ff_default_pin_to_string(sda), sda, + pinmap_ff_default_pin_to_string(scl), scl); + + // Reset tester stats and select I2C + tester.peripherals_reset(); + tester.select_peripheral(MbedTester::PeripheralI2C); + + // Read data for I2C complete transaction + // Will read bytes, checksum must match checksum calculated in parallel on FPGA + num_dev_addr_matches = 0; + num_writes = 0; + num_reads = 0; + num_starts = 0; + num_stops = 0; + num_acks = 0; + num_nacks = 0; + checksum = 0; + tester.io_metrics_start(); + num_reads = i2c_read(&i2c, (I2C_DEV_ADDR | 1), (char *)data_in, TRANSFER_COUNT, true); //transaction ends with a stop condition + num_starts += 1; + num_stops += 1; + num_acks += 1; + num_dev_addr_matches += 1; + tester.io_metrics_stop(); + for (int i = 0; i < TRANSFER_COUNT; i++) { + checksum += data_in[i]; + } + + // Verify that the transfer was successful + TEST_ASSERT_EQUAL(num_dev_addr_matches, tester.num_dev_addr_matches()); + TEST_ASSERT_EQUAL(TRANSFER_COUNT, num_reads); + TEST_ASSERT_EQUAL(num_reads + 1, tester.transfer_count()); + TEST_ASSERT_EQUAL(num_starts, tester.num_starts()); + TEST_ASSERT_EQUAL(num_stops, tester.num_stops()); + TEST_ASSERT_EQUAL(num_acks, tester.num_acks()); + TEST_ASSERT_EQUAL(num_nacks, tester.num_nacks()); + TEST_ASSERT_EQUAL(checksum, tester.get_receive_checksum()); + TEST_ASSERT_EQUAL(0, tester.state_num()); + TEST_ASSERT_EQUAL((TRANSFER_COUNT & 0xFF), tester.get_next_from_slave()); + TEST_ASSERT_EQUAL(num_writes, tester.num_writes()); + TEST_ASSERT_EQUAL(num_reads, tester.num_reads()); + + printf(" Pin combination works\r\n"); + + printf("I2C write single bytes test on sda=%s (%i), scl=%s (%i)\r\n", + pinmap_ff_default_pin_to_string(sda), sda, + pinmap_ff_default_pin_to_string(scl), scl); + + // Reset tester stats and select I2C + tester.peripherals_reset(); + tester.select_peripheral(MbedTester::PeripheralI2C); + + // Write data for I2C single byte transfers + // Will write 0-(TRANSFER_COUNT-1) to FPGA, checksum must match checksum calculated in parallel on FPGA + num_dev_addr_matches = 0; + num_writes = 0; + num_reads = 0; + num_starts = 0; + num_stops = 0; + num_acks = 0; + num_nacks = 0; + checksum = 0; + tester.io_metrics_start(); + i2c_start(&i2c);//start condition + num_starts += 1; + i2c_byte_write(&i2c, I2C_DEV_ADDR);//send device address + num_dev_addr_matches += 1; + num_acks += 1; + for (int i = 0; i < TRANSFER_COUNT; i++) { + ack_nack = i2c_byte_write(&i2c, data_out[i]);//send data + if (ack_nack == ACK) { + num_acks += 1; + } else if (ack_nack == NACK) { + num_nacks += 1; + } else { + printf("Timeout error\n\r"); + } + checksum += data_out[i]; + num_writes += 1; + } + i2c_stop(&i2c); + num_stops += 1; + tester.io_metrics_stop(); + + // Verify that the transfer was successful + TEST_ASSERT_EQUAL(num_dev_addr_matches, tester.num_dev_addr_matches()); + TEST_ASSERT_EQUAL(TRANSFER_COUNT, num_writes); + TEST_ASSERT_EQUAL(num_writes + 1, tester.transfer_count()); + TEST_ASSERT_EQUAL(num_starts, tester.num_starts()); + TEST_ASSERT_EQUAL(num_stops, tester.num_stops()); + TEST_ASSERT_EQUAL(num_acks, tester.num_acks()); + TEST_ASSERT_EQUAL(num_nacks, tester.num_nacks()); + TEST_ASSERT_EQUAL(checksum, tester.get_receive_checksum()); + TEST_ASSERT_EQUAL(0, tester.state_num()); + TEST_ASSERT_EQUAL(data_out[TRANSFER_COUNT - 4], tester.get_prev_to_slave_4()); + TEST_ASSERT_EQUAL(data_out[TRANSFER_COUNT - 3], tester.get_prev_to_slave_3()); + TEST_ASSERT_EQUAL(data_out[TRANSFER_COUNT - 2], tester.get_prev_to_slave_2()); + TEST_ASSERT_EQUAL(data_out[TRANSFER_COUNT - 1], tester.get_prev_to_slave_1()); + TEST_ASSERT_EQUAL(num_writes, tester.num_writes()); + TEST_ASSERT_EQUAL(num_reads, tester.num_reads()); + + printf(" Pin combination works\r\n"); + + printf("I2C read single bytes test on sda=%s (%i), scl=%s (%i)\r\n", + pinmap_ff_default_pin_to_string(sda), sda, + pinmap_ff_default_pin_to_string(scl), scl); + + // Reset tester stats and select I2C + tester.peripherals_reset(); + tester.select_peripheral(MbedTester::PeripheralI2C); + for (int i = 0; i < TRANSFER_COUNT; i++) { + data_in[i] = 0; + } + + // Read data for I2C single byte transfers + // Will read bytes, checksum must match checksum calculated in parallel on FPGA + num_dev_addr_matches = 0; + num_writes = 0; + num_reads = 0; + num_starts = 0; + num_stops = 0; + num_acks = 0; + num_nacks = 0; + checksum = 0; + tester.io_metrics_start(); + i2c_start(&i2c);//start condition + num_starts += 1; + i2c_byte_write(&i2c, (I2C_DEV_ADDR | 1));//send device address for reading + num_dev_addr_matches += 1; + num_acks += 1; + for (int i = 0; i < TRANSFER_COUNT; i++) { + if (num_reads == (TRANSFER_COUNT - 1)) { + data_in[i] = i2c_byte_read(&i2c, 1);//send NACK + checksum += data_in[i]; + num_reads += 1; + } else { + data_in[i] = i2c_byte_read(&i2c, 0);//send ACK + checksum += data_in[i]; + num_reads += 1; + } + } + + i2c_stop(&i2c); + num_stops += 1; + tester.io_metrics_stop(); + + // Verify that the transfer was successful + TEST_ASSERT_EQUAL(num_dev_addr_matches, tester.num_dev_addr_matches()); + TEST_ASSERT_EQUAL(TRANSFER_COUNT, num_reads); + TEST_ASSERT_EQUAL(num_reads + 1, tester.transfer_count()); + TEST_ASSERT_EQUAL(num_starts, tester.num_starts()); + TEST_ASSERT_EQUAL(num_stops, tester.num_stops()); + TEST_ASSERT_EQUAL(num_acks, tester.num_acks()); + TEST_ASSERT_EQUAL(num_nacks, tester.num_nacks()); + TEST_ASSERT_EQUAL(checksum, tester.get_receive_checksum()); + TEST_ASSERT_EQUAL(0, tester.state_num()); + TEST_ASSERT_EQUAL((TRANSFER_COUNT & 0xFF), tester.get_next_from_slave()); + TEST_ASSERT_EQUAL(num_writes, tester.num_writes()); + TEST_ASSERT_EQUAL(num_reads, tester.num_reads()); + + printf(" Pin combination works\r\n"); + + tester.reset(); +} + +Case cases[] = { + Case("i2c - init/free test all pins", all_ports), + Case("i2c - test all i2c APIs", all_peripherals) + // Case("i2c - simple write test all peripherals", all_peripherals), + // Case("i2c - simple read test all peripherals", all_peripherals), + // Case("i2c - XXXXXXX test single pin", one_peripheral) +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(15, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + Harness::run(specification); +} + +#endif /* !DEVICE_I2C */ diff --git a/TESTS/mbed_hal_fpga_ci_test_shield/pwm/main.cpp b/TESTS/mbed_hal_fpga_ci_test_shield/pwm/main.cpp new file mode 100644 index 0000000000..1d89f832d6 --- /dev/null +++ b/TESTS/mbed_hal_fpga_ci_test_shield/pwm/main.cpp @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2019, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !DEVICE_PWMOUT +#error [NOT_SUPPORTED] PWM not supported for this target +#elif !COMPONENT_FPGA_CI_TEST_SHIELD +#error [NOT_SUPPORTED] FPGA CI Test Shield is needed to run this test +#else + +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "mbed.h" + +using namespace utest::v1; + +#include "MbedTester.h" +#include "pinmap.h" +#include "test_utils.h" + + +#define pwm_debug_printf(...) + +typedef enum { + PERIOD_WRITE, + PERIOD_MS_WRITE, + PERIOD_US_WRITE, + PERIOD_PULSEWIDTH, + PERIOD_PULSEWIDTH_MS, + PERIOD_PULSEWIDTH_US +} pwm_api_test_t; + +#define NUM_OF_PERIODS 10 +#define US_PER_SEC 1000000 +#define US_PER_MS 1000 +#define MS_PER_SEC 1000 + +#define DELTA_FACTOR 20 // 5% delta + +#define PERIOD_US(PERIOD_MS) (((PERIOD_MS) * US_PER_MS)) +#define PERIOD_FLOAT(PERIOD_MS) (((float)(PERIOD_MS) / US_PER_MS)) +#define FILL_FLOAT(PRC) ((float)(PRC) / 100) +#define PULSE_HIGH_US(PERIOD_US, PRC) ((uint32_t)((PERIOD_US) * FILL_FLOAT(PRC))) +#define PULSE_LOW_US(PERIOD_US, PRC) ((uint32_t)((PERIOD_US) * (1.0f - FILL_FLOAT(PRC)))) + + +MbedTester tester(DefaultFormFactor::pins(), DefaultFormFactor::restricted_pins()); + + +void pwm_init_free(PinName pin) +{ + pwmout_t pwm_out; + + pwm_debug_printf("PWM init/free test on pin=%s (%i)\r\n", pinmap_ff_default_pin_to_string(pin), pin); + + pwmout_init(&pwm_out, pin); + pwmout_free(&pwm_out); +} + + +void pwm_period_fill_test(PinName pin, uint32_t period_ms, uint32_t fill_prc, pwm_api_test_t api_test) +{ + pwm_debug_printf("PWM test on pin = %s (%i)\r\n", pinmap_ff_default_pin_to_string(pin), pin); + pwm_debug_printf("Testing period = %lu ms, duty-cycle = %lu %%\r\n", period_ms, fill_prc); + pwm_debug_printf("Testing APIs = %d\r\n", (int)api_test); + + tester.reset(); + MbedTester::LogicalPin logical_pin = (MbedTester::LogicalPin)(MbedTester::LogicalPinIOMetrics0); + tester.pin_map_set(pin, logical_pin); + + pwmout_t pwm_out; + + pwmout_init(&pwm_out, pin); + + core_util_critical_section_enter(); + + switch (api_test) { + case PERIOD_WRITE: + pwmout_period(&pwm_out, PERIOD_FLOAT(period_ms)); + pwmout_write(&pwm_out, FILL_FLOAT(fill_prc)); + break; + + case PERIOD_MS_WRITE: + pwmout_period_ms(&pwm_out, (int)period_ms); + pwmout_write(&pwm_out, FILL_FLOAT(fill_prc)); + break; + + case PERIOD_US_WRITE: + pwmout_period_us(&pwm_out, PERIOD_US(period_ms)); + pwmout_write(&pwm_out, FILL_FLOAT(fill_prc)); + break; + + case PERIOD_PULSEWIDTH: + pwmout_period(&pwm_out, PERIOD_FLOAT(period_ms)); + pwmout_pulsewidth(&pwm_out, (float)PULSE_HIGH_US(PERIOD_US(period_ms), fill_prc) / US_PER_SEC); + break; + + case PERIOD_PULSEWIDTH_MS: + pwmout_period(&pwm_out, PERIOD_FLOAT(period_ms)); + pwmout_pulsewidth_ms(&pwm_out, (int)PULSE_HIGH_US(PERIOD_US(period_ms), fill_prc) / MS_PER_SEC); + break; + + case PERIOD_PULSEWIDTH_US: + pwmout_period(&pwm_out, PERIOD_FLOAT(period_ms)); + pwmout_pulsewidth_us(&pwm_out, (int)PULSE_HIGH_US(PERIOD_US(period_ms), fill_prc)); + break; + } + + tester.io_metrics_start(); + + wait(NUM_OF_PERIODS * PERIOD_FLOAT(period_ms)); + + tester.io_metrics_stop(); + core_util_critical_section_exit(); + + const uint32_t expected_low_pulse_us = PULSE_LOW_US(PERIOD_US(period_ms), fill_prc); + const uint32_t expected_high_pulse_us = PULSE_HIGH_US(PERIOD_US(period_ms), fill_prc); + const uint32_t delta_low_pulse = (expected_low_pulse_us / DELTA_FACTOR); + const uint32_t delta_high_pulse = (expected_high_pulse_us / DELTA_FACTOR); + + pwm_debug_printf("Minimum pulse low %lu us\r\n", tester.io_metrics_min_pulse_low(logical_pin) / 100); + pwm_debug_printf("Minimum pulse high %lu us\r\n", tester.io_metrics_min_pulse_high(logical_pin) / 100); + pwm_debug_printf("Maximum pulse low %lu us\r\n", tester.io_metrics_max_pulse_low(logical_pin) / 100); + pwm_debug_printf("Maximum pulse high %lu us\r\n", tester.io_metrics_max_pulse_high(logical_pin) / 100); + pwm_debug_printf("Rising edges %lu\r\n", tester.io_metrics_rising_edges(logical_pin)); + pwm_debug_printf("Falling edges %lu\r\n", tester.io_metrics_falling_edges(logical_pin)); + + TEST_ASSERT_FLOAT_WITHIN(FILL_FLOAT(fill_prc) / DELTA_FACTOR, FILL_FLOAT(fill_prc), pwmout_read(&pwm_out)); + + TEST_ASSERT_UINT32_WITHIN(delta_low_pulse, expected_low_pulse_us, tester.io_metrics_min_pulse_low(logical_pin) / 100); + TEST_ASSERT_UINT32_WITHIN(delta_low_pulse, expected_low_pulse_us, tester.io_metrics_max_pulse_low(logical_pin) / 100); + TEST_ASSERT_UINT32_WITHIN(delta_high_pulse, expected_high_pulse_us, tester.io_metrics_min_pulse_high(logical_pin) / 100); + TEST_ASSERT_UINT32_WITHIN(delta_high_pulse, expected_high_pulse_us, tester.io_metrics_max_pulse_high(logical_pin) / 100); + + TEST_ASSERT_UINT32_WITHIN(1, NUM_OF_PERIODS, tester.io_metrics_rising_edges(logical_pin)); + TEST_ASSERT_UINT32_WITHIN(1, NUM_OF_PERIODS, tester.io_metrics_falling_edges(logical_pin)); + + pwmout_free(&pwm_out); +} + +template +void pwm_period_fill_test(PinName pin) +{ + pwm_period_fill_test(pin, period_ms, fill_prc, api_test); +} + + +Case cases[] = { + // This will be run for all pins + Case("PWM - init/free test", all_ports), + + // This will be run for single pin + Case("PWM - period: 10 ms, fill: 10%, api: period/write", one_peripheral >), + + Case("PWM - period: 10 ms, fill: 10%, api: period_ms/write", one_peripheral >), + Case("PWM - period: 10 ms, fill: 10%, api: period_us/write", one_peripheral >), + Case("PWM - period: 10 ms, fill: 10%, api: period/pulse_width", one_peripheral >), + Case("PWM - period: 10 ms, fill: 10%, api: period/pulse_width_ms", one_peripheral >), + Case("PWM - period: 10 ms, fill: 10%, api: period/pulse_width_us", one_peripheral >), + + Case("PWM - period: 10 ms, fill: 50%, api: period/write", one_peripheral >), + Case("PWM - period: 10 ms, fill: 50%, api: period_ms/write", one_peripheral >), + Case("PWM - period: 10 ms, fill: 50%, api: period_us/write", one_peripheral >), + Case("PWM - period: 10 ms, fill: 50%, api: period/pulse_width", one_peripheral >), + Case("PWM - period: 10 ms, fill: 50%, api: period/pulse_width_ms", one_peripheral >), + Case("PWM - period: 10 ms, fill: 50%, api: period/pulse_width_us", one_peripheral >), + + Case("PWM - period: 10 ms, fill: 90%, api: period/write", one_peripheral >), + Case("PWM - period: 10 ms, fill: 90%, api: period_ms/write", one_peripheral >), + Case("PWM - period: 10 ms, fill: 90%, api: period_us/write", one_peripheral >), + Case("PWM - period: 10 ms, fill: 90%, api: period/pulse_width", one_peripheral >), + Case("PWM - period: 10 ms, fill: 90%, api: period/pulse_width_ms", one_peripheral >), + Case("PWM - period: 10 ms, fill: 90%, api: period/pulse_width_us", one_peripheral >), + + Case("PWM - period: 50 ms, fill: 10%, api: period/write", one_peripheral >), + Case("PWM - period: 50 ms, fill: 10%, api: period_ms/write", one_peripheral >), + Case("PWM - period: 50 ms, fill: 10%, api: period_us/write", one_peripheral >), + Case("PWM - period: 50 ms, fill: 10%, api: period/pulse_width", one_peripheral >), + Case("PWM - period: 50 ms, fill: 10%, api: period/pulse_width_ms", one_peripheral >), + Case("PWM - period: 50 ms, fill: 10%, api: period/pulse_width_us", one_peripheral >), + + Case("PWM - period: 50 ms, fill: 50%, api: period/write", one_peripheral >), + Case("PWM - period: 50 ms, fill: 50%, api: period_ms/write", one_peripheral >), + Case("PWM - period: 50 ms, fill: 50%, api: period_us/write", one_peripheral >), + Case("PWM - period: 50 ms, fill: 50%, api: period/pulse_width", one_peripheral >), + Case("PWM - period: 50 ms, fill: 50%, api: period/pulse_width_ms", one_peripheral >), + Case("PWM - period: 50 ms, fill: 50%, api: period/pulse_width_us", one_peripheral >), + + Case("PWM - period: 50 ms, fill: 90%, api: period/write", one_peripheral >), + Case("PWM - period: 50 ms, fill: 90%, api: period_ms/write", one_peripheral >), + Case("PWM - period: 50 ms, fill: 90%, api: period_us/write", one_peripheral >), + Case("PWM - period: 50 ms, fill: 90%, api: period/pulse_width", one_peripheral >), + Case("PWM - period: 50 ms, fill: 90%, api: period/pulse_width_ms", one_peripheral >), + Case("PWM - period: 50 ms, fill: 90%, api: period/pulse_width_us", one_peripheral >) +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(120, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + Harness::run(specification); +} + +#endif /* !DEVICE_PWMOUT */ diff --git a/TESTS/mbed_hal_fpga_ci_test_shield/spi/main.cpp b/TESTS/mbed_hal_fpga_ci_test_shield/spi/main.cpp new file mode 100644 index 0000000000..d3c491077c --- /dev/null +++ b/TESTS/mbed_hal_fpga_ci_test_shield/spi/main.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2019, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !DEVICE_SPI +#error [NOT_SUPPORTED] SPI not supported for this target +#elif !COMPONENT_FPGA_CI_TEST_SHIELD +#error [NOT_SUPPORTED] FPGA CI Test Shield is needed to run this test +#else + +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "mbed.h" + +using namespace utest::v1; + +#include "SPIMasterTester.h" +#include "pinmap.h" +#include "test_utils.h" + + +const int TRANSFER_COUNT = 300; +SPIMasterTester tester(DefaultFormFactor::pins(), DefaultFormFactor::restricted_pins()); + + +void spi_test_init_free(PinName mosi, PinName miso, PinName sclk, PinName ssel) +{ + spi_t spi; + spi_init(&spi, mosi, miso, sclk, ssel); + spi_format(&spi, 8, SPITester::Mode0, 0); + spi_frequency(&spi, 1000000); + spi_free(&spi); +} + +void spi_test_common(PinName mosi, PinName miso, PinName sclk, PinName ssel, SPITester::SpiMode spi_mode, uint32_t sym_size) +{ + uint32_t sym_mask = ((1 << sym_size) - 1); + // Remap pins for test + tester.reset(); + tester.pin_map_set(mosi, MbedTester::LogicalPinSPIMosi); + tester.pin_map_set(miso, MbedTester::LogicalPinSPIMiso); + tester.pin_map_set(sclk, MbedTester::LogicalPinSPISclk); + tester.pin_map_set(ssel, MbedTester::LogicalPinSPISsel); + + // Initialize mbed SPI pins + spi_t spi; + spi_init(&spi, mosi, miso, sclk, ssel); + spi_format(&spi, sym_size, spi_mode, 0); + spi_frequency(&spi, 1000000); + + // Configure spi_slave module + tester.set_mode(spi_mode); + tester.set_bit_order(SPITester::MSBFirst); + tester.set_sym_size(sym_size); + + // Reset tester stats and select SPI + tester.peripherals_reset(); + tester.select_peripheral(SPITester::PeripheralSPI); + + // Send and receive test data + uint32_t checksum = 0; + for (int i = 0; i < TRANSFER_COUNT; i++) { + uint32_t data = spi_master_write(&spi, (0 - i) & sym_mask); + TEST_ASSERT_EQUAL(i & sym_mask, data); + + checksum += (0 - i) & sym_mask; + } + + // Verify that the transfer was successful + TEST_ASSERT_EQUAL(TRANSFER_COUNT, tester.get_transfer_count()); + TEST_ASSERT_EQUAL(checksum, tester.get_receive_checksum()); + + spi_free(&spi); + tester.reset(); +} + +template +void spi_test_common(PinName mosi, PinName miso, PinName sclk, PinName ssel) +{ + spi_test_common(mosi, miso, sclk, ssel, spi_mode, sym_size); +} + +Case cases[] = { + // This will be run for all pins + Case("SPI - init/free test all pins", all_ports), + + // This will be run for all peripherals + Case("SPI - basic test", all_peripherals >), + + // This will be run for single pin configuration + Case("SPI - mode testing (MODE_1)", one_peripheral >), + Case("SPI - mode testing (MODE_2)", one_peripheral >), + Case("SPI - mode testing (MODE_3)", one_peripheral >), + + Case("SPI - symbol size testing (4)", one_peripheral >), + Case("SPI - symbol size testing (12)", one_peripheral >), + Case("SPI - symbol size testing (16)", one_peripheral >) +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(60, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + Harness::run(specification); +} + +#endif /* !DEVICE_SPI */ diff --git a/TESTS/mbed_hal_fpga_ci_test_shield/uart/main.cpp b/TESTS/mbed_hal_fpga_ci_test_shield/uart/main.cpp new file mode 100644 index 0000000000..eedb2726eb --- /dev/null +++ b/TESTS/mbed_hal_fpga_ci_test_shield/uart/main.cpp @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2019, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !DEVICE_SERIAL +#error [NOT_SUPPORTED] SERIAL not supported for this target +#elif !COMPONENT_FPGA_CI_TEST_SHIELD +#error [NOT_SUPPORTED] FPGA CI Test Shield is needed to run this test +#else + +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" + +using namespace utest::v1; + +#include +#include "UARTTester.h" +#include "pinmap.h" +#include "test_utils.h" +#include "serial_api.h" +#include "us_ticker_api.h" + +#define PUTC_REPS 16 +#define GETC_REPS 16 + +// In the UART RX test, the request for the FPGA to start sending data is sent +// first. Then the execution is blocked at serial_getc() call. Since the DUT +// is not ready to receive UART data instantly after the request, the start of +// the actual transmission has to be dalyed. +// A measured delay for NUCLEO_F070RB is 193 us. +#define TX_START_DELAY_NS 250000 + +UARTTester tester(DefaultFormFactor::pins(), DefaultFormFactor::restricted_pins()); + +typedef struct { + serial_t *ser; + int *rx_buff; + uint32_t rx_cnt; + int *tx_buff; + uint32_t tx_cnt; +} serial_test_data_t; + +static void test_irq_handler(uint32_t id, SerialIrq event) +{ + serial_test_data_t *td = (serial_test_data_t *)id; + int c = 0x01; // arbitrary, non-zero value + if (event == RxIrq) { + c = serial_getc(td->ser); + core_util_critical_section_enter(); + if (td->rx_cnt < GETC_REPS) { + td->rx_buff[td->rx_cnt] = c; + td->rx_cnt++; + } + core_util_critical_section_exit(); + } else if (event == TxIrq) { + core_util_critical_section_enter(); + if (td->tx_cnt < PUTC_REPS) { + c = td->tx_buff[td->tx_cnt]; + td->tx_cnt++; + } + core_util_critical_section_exit(); + // Send either one of tx_buff[] values or 0x01. + serial_putc(td->ser, c); + } +} + +static void uart_test_common(int baudrate, int data_bits, SerialParity parity, int stop_bits, PinName tx, PinName rx, PinName cts = NC, PinName rts = NC) +{ + // The FPGA CI shield only supports None, Odd & Even. + // Forced parity is not supported on Atmel, Freescale, Nordic & STM targets. + MBED_ASSERT(parity != ParityForced1 && parity != ParityForced0); + + // STM-specific constraints + // Only 7, 8 & 9 data bits. + MBED_ASSERT(data_bits >= 7 && data_bits <= 9); + // Only Odd or Even parity for 7 data bits. + if (data_bits == 7) { + MBED_ASSERT(parity != ParityNone); + } + + // Limit the actual TX & RX chars to 8 bits for this test. + int test_buff_bits = data_bits < 8 ? data_bits : 8; + + // start_bit + data_bits + parity_bit + stop_bits + int packet_bits = 1 + data_bits + stop_bits + (parity == ParityNone ? 0 : 1); + us_timestamp_t packet_tx_time = 1000000 * packet_bits / baudrate; + const ticker_data_t *const us_ticker = get_us_ticker_data(); + + bool use_flow_control = (cts != NC && rts != NC) ? true : false; + + // Remap pins for test + tester.reset(); + tester.pin_map_set(tx, MbedTester::LogicalPinUARTRx); + tester.pin_map_set(rx, MbedTester::LogicalPinUARTTx); + if (use_flow_control) { + tester.pin_map_set(cts, MbedTester::LogicalPinUARTRts); + tester.pin_map_set(rts, MbedTester::LogicalPinUARTCts); + } + + // Initialize mbed UART pins + serial_t serial; + serial_init(&serial, tx, rx); + serial_baud(&serial, baudrate); + serial_format(&serial, data_bits, parity, stop_bits); + if (use_flow_control) { + serial_set_flow_control(&serial, FlowControlRTSCTS, rts, cts); + } else { + serial_set_flow_control(&serial, FlowControlNone, NC, NC); + } + + // Reset tester stats and select UART + tester.peripherals_reset(); + tester.select_peripheral(MbedTester::PeripheralUART); + + // Configure UART module + tester.set_baud((uint32_t)baudrate); + tester.set_bits((uint8_t)data_bits); + tester.set_stops((uint8_t)stop_bits); + switch (parity) { + case ParityOdd: + tester.set_parity(true, true); + break; + case ParityEven: + tester.set_parity(true, false); + break; + case ParityNone: + default: + tester.set_parity(false, false); + break; + } + if (use_flow_control) { + tester.cts_deassert_delay(0); + } + + int rx_buff[GETC_REPS] = {}; + int tx_buff[PUTC_REPS] = {}; + volatile serial_test_data_t td = { + &serial, + rx_buff, + 0, + tx_buff, + 0 + }; + uint32_t checksum = 0; + + // DUT TX / FPGA RX + int tx_val; + tester.rx_start(); + for (uint32_t reps = 1; reps <= PUTC_REPS; reps++) { + tx_val = rand() % (1 << test_buff_bits); + checksum += tx_val; + serial_putc(&serial, tx_val); + us_timestamp_t end_ts = ticker_read_us(us_ticker) + 2 * packet_tx_time; + while (tester.rx_get_count() != reps && ticker_read_us(us_ticker) <= end_ts) { + // Wait (no longer than twice the time of one packet transfer) for + // the FPGA to receive data and update the byte counter. + } + TEST_ASSERT_EQUAL_UINT32(reps, tester.rx_get_count()); + TEST_ASSERT_EQUAL(0, tester.rx_get_parity_errors()); + TEST_ASSERT_EQUAL(0, tester.rx_get_stop_errors()); + TEST_ASSERT_EQUAL(0, tester.rx_get_framing_errors()); + TEST_ASSERT_EQUAL_UINT32(checksum, tester.rx_get_checksum()); + TEST_ASSERT_EQUAL(tx_val, tester.rx_get_data()); + } + tester.rx_stop(); + + // DUT RX / FPGA TX + // serial_getc() may return 16-bit as well as 8-bit value cast to an int. + // Use a random initial value, but make sure it is low enouth, + // so the FPGA will not overflow 8 bits when incrementing it. + uint16_t tester_buff = rand() % ((1 << test_buff_bits) - GETC_REPS); + tester.tx_set_next(tester_buff); + tester.tx_set_count(GETC_REPS); + if (!use_flow_control) { + tester.tx_set_delay(TX_START_DELAY_NS); + } + tester.tx_start(use_flow_control); + for (int i = 0; i < GETC_REPS; i++) { + rx_buff[i] = serial_getc(&serial); + } + tester.tx_stop(); + for (int i = 0; i < GETC_REPS; tester_buff++, i++) { + TEST_ASSERT_EQUAL(tester_buff, rx_buff[i]); + } + + serial_irq_handler(&serial, test_irq_handler, (uint32_t) &td); + + // DUT TX (IRQ) / FPGA RX + tx_val = rand() % ((1 << test_buff_bits) - PUTC_REPS); + for (size_t i = 0; i < PUTC_REPS; tx_val++, i++) { + td.tx_buff[i] = tx_val; + checksum += tx_val; + } + + tester.rx_start(); + core_util_critical_section_enter(); + td.tx_cnt = 0; + // Enable only the TX IRQ. + serial_irq_set(&serial, TxIrq, 1); + core_util_critical_section_exit(); + while (core_util_atomic_load_u32(&td.tx_cnt) != PUTC_REPS) { + // Wait until the last byte is written to UART TX reg. + }; + core_util_critical_section_enter(); + serial_irq_set(&serial, TxIrq, 0); + core_util_critical_section_exit(); + us_timestamp_t end_ts = ticker_read_us(us_ticker) + 2 * packet_tx_time; + while (ticker_read_us(us_ticker) <= end_ts) { + // Wait twice the time of one packet transfer for the FPGA + // to receive and process data. + }; + tester.rx_stop(); + TEST_ASSERT_EQUAL_UINT32(2 * PUTC_REPS, tester.rx_get_count()); + TEST_ASSERT_EQUAL(0, tester.rx_get_parity_errors()); + TEST_ASSERT_EQUAL(0, tester.rx_get_stop_errors()); + TEST_ASSERT_EQUAL(0, tester.rx_get_framing_errors()); + TEST_ASSERT_EQUAL_UINT32(checksum, tester.rx_get_checksum()); + TEST_ASSERT_EQUAL(tx_val - 1, tester.rx_get_data()); + + // DUT RX (IRQ) / FPGA TX + // serial_getc() may return 16-bit as well as 8-bit value cast to an int. + // Use a random initial value, but make sure it is low enouth, + // so the FPGA will not overflow 8 bits when incrementing it. + tester_buff = rand() % ((1 << test_buff_bits) - GETC_REPS); + tester.tx_set_next(tester_buff); + tester.tx_set_count(GETC_REPS); + if (!use_flow_control) { + tester.tx_set_delay(TX_START_DELAY_NS); + } + core_util_critical_section_enter(); + // Enable only the RX IRQ. + serial_irq_set(&serial, RxIrq, 1); + core_util_critical_section_exit(); + tester.rx_start(); + tester.tx_start(use_flow_control); + while (core_util_atomic_load_u32(&td.rx_cnt) != GETC_REPS) { + // Wait until the last byte is received to UART RX reg. + }; + core_util_critical_section_enter(); + serial_irq_set(&serial, RxIrq, 0); + core_util_critical_section_exit(); + tester.tx_stop(); + tester.rx_stop(); + for (int i = 0; i < GETC_REPS; tester_buff++, i++) { + TEST_ASSERT_EQUAL(tester_buff, td.rx_buff[i]); + } + + // Make sure TX IRQ was disabled during the last RX test. + TEST_ASSERT_EQUAL_UINT32(checksum, tester.rx_get_checksum()); + TEST_ASSERT_EQUAL_UINT32(2 * PUTC_REPS, tester.rx_get_count()); + + // Cleanup + serial_free(&serial); + tester.reset(); +} + +void test_init_free(PinName tx, PinName rx, PinName cts = NC, PinName rts = NC) +{ + bool use_flow_control = (cts != NC && rts != NC) ? true : false; + serial_t serial; + serial_init(&serial, tx, rx); + serial_baud(&serial, 9600); + serial_format(&serial, 8, ParityNone, 1); + if (use_flow_control) { + serial_set_flow_control(&serial, FlowControlRTSCTS, rts, cts); + } + serial_free(&serial); +} + +void test_init_free_no_fc(PinName tx, PinName rx) +{ + test_init_free(tx, rx); +} + +template +void test_common(PinName tx, PinName rx, PinName cts, PinName rts) +{ + uart_test_common(BAUDRATE, DATA_BITS, PARITY, STOP_BITS, tx, rx, cts, rts); +} + +template +void test_common_no_fc(PinName tx, PinName rx) +{ + uart_test_common(BAUDRATE, DATA_BITS, PARITY, STOP_BITS, tx, rx); +} + +Case cases[] = { + // Every set of pins from every peripheral. + Case("init/free, FC on", all_ports), + Case("init/free, FC off", all_ports), + + // One set of pins from every peripheral. + Case("basic, 9600, 8N1, FC on", all_peripherals >), + Case("basic, 9600, 8N1, FC off", all_peripherals >), + + // One set of pins from one peripheral. + // baudrate + Case("19200, 8N1, FC on", one_peripheral >), + Case("19200, 8N1, FC off", one_peripheral >), + Case("38400, 8N1, FC on", one_peripheral >), + Case("38400, 8N1, FC off", one_peripheral >), + Case("115200, 8N1, FC on", one_peripheral >), + Case("115200, 8N1, FC off", one_peripheral >), // DISCO_L475VG_IOT01A + // data bits + /* FIXME not supported: K64F + Case("9600, 7O1, FC on", one_peripheral >), + Case("9600, 7O1, FC off", one_peripheral >), + Case("9600, 9N1, FC on", one_peripheral >), + Case("9600, 9N1, FC off", one_peripheral >), + */ + // parity + Case("9600, 8O1, FC on", one_peripheral >), + /* FIXME not supported: NUCLEO_F429ZI, NUCLEO_F746ZG, NUCLEO_F207ZG + Case("9600, 8O1, FC off", one_peripheral >), + */ + Case("9600, 8E1, FC on", one_peripheral >), + /* FIXME not supported: NUCLEO_F429ZI, NUCLEO_F746ZG, NUCLEO_F207ZG + Case("9600, 8E1, FC off", one_peripheral >), + */ + // stop bits + Case("9600, 8N2, FC on", one_peripheral >), + Case("9600, 8N2, FC off", one_peripheral >), +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(240, "default_auto"); + srand((unsigned) ticker_read_us(get_us_ticker_data())); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + Harness::run(specification); +} + +#endif /* !DEVICE_SERIAL */ \ No newline at end of file