Newer
Older
mbed-os / hal / tests / TESTS / mbed_hal_fpga_ci_test_shield / i2c / main.cpp
@jeromecoutant jeromecoutant on 18 Mar 2021 15 KB [STD-PIN] update tests and components
/*
 * 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(TARGET_FF_ARDUINO) || defined(TARGET_FF_ARDUINO_UNO)) && !defined(MBED_CONF_TARGET_DEFAULT_FORM_FACTOR)
#error [NOT_SUPPORTED] Test not supported for this form factor
#else

#include "utest/utest.h"
#include "unity/unity.h"
#include "greentea-client/test_env.h"
#include "mbed.h"
#include "i2c_api.h"
#include "pinmap.h"
#include "hal/static_pinmap.h"
#include "test_utils.h"
#include "I2CTester.h"
#include "i2c_fpga_test.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 fpga_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);

    i2c_free(&obj);
}

template<bool init_direct>
void fpga_i2c_test_write(PinName sda, PinName scl)
{
    // Remap pins for test
    tester.reset();
    tester.pin_map_set(sda, MbedTester::LogicalPinI2CSda);
    tester.pin_map_set(scl, MbedTester::LogicalPinI2CScl);

    tester.pin_set_pull(sda, MbedTester::PullUp);
    tester.pin_set_pull(scl, MbedTester::PullUp);

    // Initialize mbed I2C pins
    i2c_t i2c;
    memset(&i2c, 0, sizeof(i2c));
    if (init_direct) {
#if STATIC_PINMAP_READY
        const i2c_pinmap_t pinmap = get_i2c_pinmap(sda, scl);
        i2c_init_direct(&i2c, &pinmap);
#else
        //skip this test case if static pinmap is not supported
        return;
#endif
    } else {
        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

    // 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;

    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;

    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());

    tester.reset();
    tester.pin_set_pull(sda, MbedTester::PullNone);
    tester.pin_set_pull(scl, MbedTester::PullNone);
    i2c_free(&i2c);
}

template<bool init_direct>
void fpga_i2c_test_read(PinName sda, PinName scl)
{
    // Remap pins for test
    tester.reset();
    tester.pin_map_set(sda, MbedTester::LogicalPinI2CSda);
    tester.pin_map_set(scl, MbedTester::LogicalPinI2CScl);

    tester.pin_set_pull(sda, MbedTester::PullUp);
    tester.pin_set_pull(scl, MbedTester::PullUp);

    // Initialize mbed I2C pins
    i2c_t i2c;
    memset(&i2c, 0, sizeof(i2c));
    if (init_direct) {
        const i2c_pinmap_t pinmap = get_i2c_pinmap(sda, scl);
        i2c_init_direct(&i2c, &pinmap);
    } else {
        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

    // Reset tester stats and select I2C
    tester.peripherals_reset();
    tester.select_peripheral(MbedTester::PeripheralI2C);

    // Read data for I2C complete transaction
    // Will read <TRANSFER_COUNT> 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;

    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_acks += TRANSFER_COUNT - 1;
    num_nacks += 1;
    num_dev_addr_matches += 1;

    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_send_checksum());
    TEST_ASSERT_EQUAL(0, tester.state_num());
    TEST_ASSERT_EQUAL(((TRANSFER_COUNT + 1) & 0xFF), tester.get_next_from_slave());
    TEST_ASSERT_EQUAL(num_writes, tester.num_writes());
    TEST_ASSERT_EQUAL(num_reads, tester.num_reads());

    tester.reset();
    tester.pin_set_pull(sda, MbedTester::PullNone);
    tester.pin_set_pull(scl, MbedTester::PullNone);
    i2c_free(&i2c);
}

void fpga_i2c_test_byte_write(PinName sda, PinName scl)
{
    // Remap pins for test
    tester.reset();
    tester.pin_map_set(sda, MbedTester::LogicalPinI2CSda);
    tester.pin_map_set(scl, MbedTester::LogicalPinI2CScl);

    tester.pin_set_pull(sda, MbedTester::PullUp);
    tester.pin_set_pull(scl, MbedTester::PullUp);

    // 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

    // 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;

    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;

    // 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());

    tester.reset();
    tester.pin_set_pull(sda, MbedTester::PullNone);
    tester.pin_set_pull(scl, MbedTester::PullNone);
    i2c_free(&i2c);
}

void fpga_i2c_test_byte_read(PinName sda, PinName scl)
{
    // Remap pins for test
    tester.reset();
    tester.pin_map_set(sda, MbedTester::LogicalPinI2CSda);
    tester.pin_map_set(scl, MbedTester::LogicalPinI2CScl);

    tester.pin_set_pull(sda, MbedTester::PullUp);
    tester.pin_set_pull(scl, MbedTester::PullUp);

    // 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

    // Reset tester stats and select I2C
    tester.peripherals_reset();
    tester.select_peripheral(MbedTester::PeripheralI2C);
    tester.set_next_from_slave(0);
    for (int i = 0; i < TRANSFER_COUNT; i++) {
        data_in[i] = 0;
    }

    // Read data for I2C single byte transfers
    // Will read <TRANSFER_COUNT> 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;

    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;
            num_nacks += 1;
        } else {
            data_in[i] = i2c_byte_read(&i2c, 0);//send ACK
            checksum += data_in[i];
            num_reads += 1;
            num_acks += 1;
        }
    }

    i2c_stop(&i2c);
    num_stops += 1;

    // 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_send_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());

    tester.reset();
    tester.pin_set_pull(sda, MbedTester::PullNone);
    tester.pin_set_pull(scl, MbedTester::PullNone);
    i2c_free(&i2c);
}

Case cases[] = {
    Case("i2c - init/free test all pins", all_ports<I2CPort, DefaultFormFactor, fpga_test_i2c_init_free>),
    Case("i2c - test write i2c API", all_peripherals<I2CPort, DefaultFormFactor, fpga_i2c_test_write<false>>),
    Case("i2c (direct init) - test write i2c API", all_peripherals<I2CPort, DefaultFormFactor, fpga_i2c_test_write<true>>),
    Case("i2c - test read i2c API", all_peripherals<I2CPort, DefaultFormFactor, fpga_i2c_test_read<false>>),
    Case("i2c (direct init) - test read i2c API", all_peripherals<I2CPort, DefaultFormFactor, fpga_i2c_test_read<true>>),
    Case("i2c - test single byte write i2c API", all_peripherals<I2CPort, DefaultFormFactor, fpga_i2c_test_byte_write>),
    Case("i2c - test single byte read i2c API", all_peripherals<I2CPort, DefaultFormFactor, fpga_i2c_test_byte_read>)
};

utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
{
    GREENTEA_SETUP(30, "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 */