From b3332129b2a72a47f77eea1e51ca71921f4e20e1 Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Tue, 7 May 2019 15:10:47 -0500 Subject: [PATCH] Check in files for the FPGA CI Test Shield Bring all the FPGA CI Test Shield C and C++ driver files into mbed-os as the component FPGA_CI_TEST_SHIELD. When this component is enabled all the files that are needed to communicate with, update firmware on and run testing with the FPGA are built. --- .../DynamicPinList.cpp | 75 + .../DynamicPinList.h | 93 + .../I2CTester.cpp | 144 + .../COMPONENT_FPGA_CI_TEST_SHIELD/I2CTester.h | 173 ++ .../MbedTester.cpp | 2341 +++++++++++++++++ .../MbedTester.h | 878 +++++++ .../SPIMasterTester.cpp | 70 + .../SPIMasterTester.h | 109 + .../SPISlaveTester.cpp | 84 + .../SPISlaveTester.h | 121 + .../SPITester.cpp | 77 + .../COMPONENT_FPGA_CI_TEST_SHIELD/SPITester.h | 127 + .../UARTTester.cpp | 131 + .../UARTTester.h | 158 ++ .../fpga_config.h | 190 ++ .../test_utils.h | 465 ++++ 16 files changed, 5236 insertions(+) create mode 100644 components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/DynamicPinList.cpp create mode 100644 components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/DynamicPinList.h create mode 100644 components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/I2CTester.cpp create mode 100644 components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/I2CTester.h create mode 100644 components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/MbedTester.cpp create mode 100644 components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/MbedTester.h create mode 100644 components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPIMasterTester.cpp create mode 100644 components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPIMasterTester.h create mode 100644 components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPISlaveTester.cpp create mode 100644 components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPISlaveTester.h create mode 100644 components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPITester.cpp create mode 100644 components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPITester.h create mode 100644 components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/UARTTester.cpp create mode 100644 components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/UARTTester.h create mode 100644 components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/fpga_config.h create mode 100644 components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/test_utils.h diff --git a/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/DynamicPinList.cpp b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/DynamicPinList.cpp new file mode 100644 index 0000000000..a9db031c64 --- /dev/null +++ b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/DynamicPinList.cpp @@ -0,0 +1,75 @@ +/* + * 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. + */ + +#include "DynamicPinList.h" + +DynamicPinList::DynamicPinList() +{ + +} + +DynamicPinList::DynamicPinList(const PinList *pin_list) +{ + for (uint32_t i = 0; i < pin_list->count; i++) { + _pins.push_back(pin_list->pins[i]); + } +} + +DynamicPinList::DynamicPinList(const DynamicPinList &other) +{ + _pins = other._pins; +} + +void DynamicPinList::add(PinName pin) +{ + _pins.push_back(pin); +} + +bool DynamicPinList::has_pin(PinName pin) const +{ + for (uint32_t i = 0; i < _pins.size(); i++) { + if (pin == _pins[i]) { + return true; + } + } + return false; +} + +void DynamicPinList::clear() +{ + _pins.clear(); +} + +uint32_t DynamicPinList::count() const +{ + return _pins.size(); +} + +PinName DynamicPinList::get(uint32_t index) const +{ + return index < _pins.size() ? _pins[index] : NC; +} + +int DynamicPinList::index(PinName pin) const +{ + for (uint32_t i = 0; i < _pins.size(); i++) { + if (pin == _pins[i]) { + return i; + } + } + return -1; +} diff --git a/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/DynamicPinList.h b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/DynamicPinList.h new file mode 100644 index 0000000000..81029b4b33 --- /dev/null +++ b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/DynamicPinList.h @@ -0,0 +1,93 @@ +/* + * 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. + */ + +#ifndef DYNAMIC_PIN_LIST_H +#define DYNAMIC_PIN_LIST_H + +#include "mbed.h" +#include "pinmap.h" +#include + +class DynamicPinList { +public: + + /** + * Create an empty pin list + */ + DynamicPinList(); + + /** + * Create a pin list with the given contents + * + * @param pin_list List of pins to create this list from + */ + DynamicPinList(const PinList *pin_list); + + /** + * Create a copy of another list + * + * @param other Other object to copy contruct this from + */ + DynamicPinList(const DynamicPinList &other); + + /** + * Add a pin to the pin list + * + * @param pin Pin to add to this pin list + */ + void add(PinName pin); + + /** + * Check if the given pin is in this list + * + * @param pin Pin to check for in the list + * @return true if the pin is in the list, false otherwise + */ + bool has_pin(PinName pin) const; + + /** + * Empty this pin list + */ + void clear(); + + /** + * Return the number of pins in this list + * + * @return Elements in this list + */ + uint32_t count() const; + + /** + * Get the pin at the given index + * + * @return Pin at this position + */ + PinName get(uint32_t index) const; + + /** + * Get the location of the given pin + * + * @param pin Pin to get the index of + * @return pin index or -1 if pin is not in the list + */ + int index(PinName pin) const; + +private: + vector _pins; +}; + +#endif diff --git a/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/I2CTester.cpp b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/I2CTester.cpp new file mode 100644 index 0000000000..d3a84a3c5d --- /dev/null +++ b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/I2CTester.cpp @@ -0,0 +1,144 @@ +/* + * 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. + */ + +#include "I2CTester.h" +#include "fpga_config.h" + +uint8_t I2CTester::num_starts() +{ + uint8_t num_starts = 0; + read(TESTER_I2C_STARTS, &num_starts, sizeof(num_starts)); + return num_starts; +} + +uint8_t I2CTester::num_stops() +{ + uint8_t num_stops = 0; + read(TESTER_I2C_STOPS, &num_stops, sizeof(num_stops)); + return num_stops; +} + +uint16_t I2CTester::num_acks() +{ + uint16_t num_acks = 0; + read(TESTER_I2C_ACKS, (uint8_t*)&num_acks, sizeof(num_acks)); + return num_acks; +} + +uint16_t I2CTester::num_nacks() +{ + uint16_t num_nacks = 0; + read(TESTER_I2C_NACKS, (uint8_t*)&num_nacks, sizeof(num_nacks)); + return num_nacks; +} + +uint16_t I2CTester::transfer_count() +{ + uint16_t transfers = 0; + MBED_ASSERT(sizeof(transfers) == TESTER_I2C_TRANSFERS_SIZE); + read(TESTER_I2C_TRANSFERS, (uint8_t*)&transfers, sizeof(transfers)); + return transfers; +} + +uint32_t I2CTester::get_receive_checksum() +{ + uint32_t to_slave_checksum = 0; + MBED_ASSERT(sizeof(to_slave_checksum) == TESTER_I2C_TO_SLAVE_CHECKSUM_SIZE); + read(TESTER_I2C_TO_SLAVE_CHECKSUM, (uint8_t*)&to_slave_checksum, sizeof(to_slave_checksum)); + return to_slave_checksum; +} + +uint8_t I2CTester::state_num() +{ + uint8_t state_num = 0; + read(TESTER_I2C_STATE_NUM, &state_num, sizeof(state_num)); + return state_num; +} + +uint8_t I2CTester::num_dev_addr_matches() +{ + uint8_t num_correct = 0; + read(TESTER_I2C_NUMBER_DEV_ADDR_MATCHES, &num_correct, sizeof(num_correct)); + return num_correct; +} + +void I2CTester::set_device_address(uint16_t addr) +{ + uint16_t data = addr; + write(TESTER_I2C_DEVICE_ADDRESS, (uint8_t*)&data, sizeof(data)); +} + +uint16_t I2CTester::get_device_address() +{ + uint16_t addr = 0; + read(TESTER_I2C_DEVICE_ADDRESS, (uint8_t*)&addr, sizeof(addr)); + return addr; +} + +void I2CTester::set_sda(uint8_t value) +{ + uint8_t val = value; + write(TESTER_I2C_SET_SDA, &val, sizeof(val)); +} + +uint8_t I2CTester::get_prev_to_slave_4() +{ + uint8_t prev_to_slave_4 = 0; + read(TESTER_I2C_PREV_TO_SLAVE_4, &prev_to_slave_4, sizeof(prev_to_slave_4)); + return prev_to_slave_4; +} +uint8_t I2CTester::get_prev_to_slave_3() +{ + uint8_t prev_to_slave_3 = 0; + read(TESTER_I2C_PREV_TO_SLAVE_3, &prev_to_slave_3, sizeof(prev_to_slave_3)); + return prev_to_slave_3; +} +uint8_t I2CTester::get_prev_to_slave_2() +{ + uint8_t prev_to_slave_2 = 0; + read(TESTER_I2C_PREV_TO_SLAVE_2, &prev_to_slave_2, sizeof(prev_to_slave_2)); + return prev_to_slave_2; +} +uint8_t I2CTester::get_prev_to_slave_1() +{ + uint8_t prev_to_slave_1 = 0; + read(TESTER_I2C_PREV_TO_SLAVE_1, &prev_to_slave_1, sizeof(prev_to_slave_1)); + return prev_to_slave_1; +} +void I2CTester::set_next_from_slave(uint8_t value) +{ + uint8_t val = value; + write(TESTER_I2C_NEXT_FROM_SLAVE, &val, sizeof(val)); +} +uint8_t I2CTester::get_next_from_slave() +{ + uint8_t next_from_slave = 0; + read(TESTER_I2C_NEXT_FROM_SLAVE, &next_from_slave, sizeof(next_from_slave)); + return next_from_slave; +} +uint16_t I2CTester::num_writes() +{ + uint16_t num_writes = 0; + read(TESTER_I2C_NUM_WRITES, (uint8_t*)&num_writes, sizeof(num_writes)); + return num_writes; +} +uint16_t I2CTester::num_reads() +{ + uint16_t num_reads = 0; + read(TESTER_I2C_NUM_READS, (uint8_t*)&num_reads, sizeof(num_reads)); + return num_reads; +} diff --git a/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/I2CTester.h b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/I2CTester.h new file mode 100644 index 0000000000..6d33e2116a --- /dev/null +++ b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/I2CTester.h @@ -0,0 +1,173 @@ +/* + * 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. + */ + +#ifndef I2C_TESTER_H +#define I2C_TESTER_H + +#include "MbedTester.h" + + +class I2CTester: public MbedTester { +public: + + I2CTester(const PinList *form_factor, const PinList *exclude_pins) + : MbedTester(form_factor, exclude_pins) + { + + } + + /* **I2C peripheral functions** */ + + /** + * Get the number of start conditions since last I2C reset + * + * @return The number of start conditions + */ + uint8_t num_starts(); + + /** + * Get the number of stop conditions since last I2C reset + * + * @return The number of stop conditions + */ + uint8_t num_stops(); + + /** + * Get the number of ACKs since last I2C reset + * + * @return The number of ACKs + */ + uint16_t num_acks(); + + /** + * Get the number of NACKs since last I2C reset + * + * @return The number of NACKs + */ + uint16_t num_nacks(); + + /** + * Read the number of transfers which have occurred, not including the device address byte + * + * @return The number of I2C transfers that have completed since + * i2c was reset, not including the device address byte + */ + uint16_t transfer_count(); + + /** + * Read a checksum of data send to the tester + * + * @return The sum of all bytes sent to the tester since reset + */ + uint32_t get_receive_checksum(); + + /** + * Get the I2C slave state number + * + * @return The state number + */ + uint8_t state_num(); + + /** + * Get the number of times the device address has been sent correctly + * + * @return The number of times the device address has been sent correctly + */ + uint8_t num_dev_addr_matches(); + + /** + * Set the I2C slave device address + * + * @param addr New address for slave device + */ + void set_device_address(uint16_t addr); + + /** + * Get the I2C slave device address + * + * @return The slave device address + */ + uint16_t get_device_address(); + + /** + * Set SDA (test mode) + * + * @param value Test value for SDA + */ + void set_sda(uint8_t value); + + /** + * Get the value written to slave in the fourth to last transaction + * + * @return value written to slave in the fourth to last transaction + */ + uint8_t get_prev_to_slave_4(); + + /** + * Get the value written to slave in the third to last transaction + * + * @return value written to slave in the third to last transaction + */ + uint8_t get_prev_to_slave_3(); + + /** + * Get the value written to slave in the second to last transaction + * + * @return value written to slave in the second to last transaction + */ + uint8_t get_prev_to_slave_2(); + + /** + * Get the value written to slave in the last transaction + * + * @return value written to slave in the last transaction + */ + uint8_t get_prev_to_slave_1(); + + /** + * Set the value to be read from slave in next read transaction + * + * @param value Value to be read from slave in next read transaction + */ + void set_next_from_slave(uint8_t value); + + /** + * Get the value to be read from slave in next read transaction + * + * @return Value to be read from slave in next read transaction + */ + uint8_t get_next_from_slave(); + + /** + * Read the number of writes which have occurred, not including the device address byte + * + * @return The number of I2C writes that have completed since + * i2c was reset, not including the device address byte + */ + uint16_t num_writes(); + + /** + * Read the number of reads which have occurred + * + * @return The number of I2C reads that have completed since + * i2c was reset + */ + uint16_t num_reads(); + +}; + +#endif diff --git a/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/MbedTester.cpp b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/MbedTester.cpp new file mode 100644 index 0000000000..b658da435a --- /dev/null +++ b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/MbedTester.cpp @@ -0,0 +1,2341 @@ +/* + * 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. + */ + +#include "MbedTester.h" +#include "fpga_config.h" +#include "BlockDevice.h" + +#define mbed_tester_printf(...) + +#define PHYSICAL_PINS 128 +#define LOGICAL_PINS 8 +#define FIRMWARE_SIZE 2192012 +#define FIRMWARE_REGION_SIZE 0x220000 +#define FIRMWARE_HEADER_SIZE 0x10000 +#define FLASH_SECTOR_SIZE 0x1000 +#define LENGTH_SIZE 0x4 +#define CRC_SIZE 0x4 +#define FLASH_SPI_FREQ_HZ 2000000 +#define ANALOG_COUNT 4 + +#define PHYSICAL_NC ((MbedTester::PhysicalIndex)0xFF) + +static const uint8_t KEY[8] = { + 0x92, 0x9d, 0x9a, 0x9b, + 0x29, 0x35, 0xa2, 0x65 +}; + +template +class MbedTesterBitMap { +public: + + MbedTesterBitMap() { + for (size_t i = 0; i < _count; i++) { + _bitmap[i] = 0; + } + } + + bool get(size_t index) { + if (index >= width) { + return false; + } + return _bitmap[index / 32] & (1 << (index % 32)) ? true : false; + } + + void set(size_t index) { + if (index >= width) { + return; + } + _bitmap[index / 32] |= 1 << (index % 32); + } + + void clear(size_t index) { + if (index >= width) { + return; + } + _bitmap[index / 32] &= ~(1 << (index % 32)); + } + +private: + + static const size_t _count = (width + 31) / 32; + uint32_t _bitmap[(width + 31) / 32]; +}; + +static uint8_t spi_transfer(DigitalInOut *clk, DigitalInOut *mosi, DigitalInOut *miso, uint8_t data) +{ + uint8_t ret = 0; + for (int i = 0; i < 8; i++) { + *clk = 0; + *mosi = (data >> (7 - i)) & 1; + wait_ns(100); + *clk = 1; + ret |= *miso ? 1 << (7 - i) : 0; + wait_ns(100); + } + return ret; +} + +static void mbed_tester_command(DigitalInOut *clk, DigitalInOut *mosi, DigitalInOut *miso, uint8_t miso_index, uint32_t addr, bool write_n_read, uint8_t *data, uint8_t size) +{ + // 8 - Start Key + for (uint32_t i = 0; i < sizeof(KEY); i++) { + spi_transfer(clk, mosi, miso, KEY[i]); + } + + // 1 - Physical pin index for MISO + spi_transfer(clk, mosi, miso, miso_index); + + // 1 - Number of SPI transfers which follow (= N + 5) + spi_transfer(clk, mosi, miso, size + 5); + + // 4 - Little endian address for transfer + spi_transfer(clk, mosi, miso, (addr >> (8 * 0)) & 0xFF); + spi_transfer(clk, mosi, miso, (addr >> (8 * 1)) & 0xFF); + spi_transfer(clk, mosi, miso, (addr >> (8 * 2)) & 0xFF); + spi_transfer(clk, mosi, miso, (addr >> (8 * 3)) & 0xFF); + + // 1 - direction + spi_transfer(clk, mosi, miso, write_n_read ? 1 : 0); + + // N - Data to read or write + if (write_n_read) {//read: false, write: true + for (int i = 0; i < size; i++) { + spi_transfer(clk, mosi, miso, data[i]); + } + } else { + for (int i = 0; i < size; i++) { + data[i] = spi_transfer(clk, mosi, miso, 0); + } + } + *clk = 0; + +} + +static bool mbed_tester_test(DigitalInOut *clk, DigitalInOut *mosi, DigitalInOut *miso, uint8_t miso_index) +{ + uint8_t buf[4]; + memset(buf, 0, sizeof(buf)); + mbed_tester_command(clk, mosi, miso, miso_index, TESTER_CONTROL, false, buf, sizeof(buf)); + return memcmp(buf, "mbed", sizeof(buf)) == 0; +} + + +class MbedTesterBlockDevice : public BlockDevice { +public: + + MbedTesterBlockDevice(DigitalInOut &mosi, DigitalInOut &miso, DigitalInOut &clk, DigitalInOut &cs, uint32_t frequency) + : _mosi(mosi), _miso(miso), _clk(clk), _cs(cs), _wait_ns(1000000000 / frequency / 2), _init(false) { + + // Set initial values + _cs.write(1); + _clk.write(0); + + // Set direction + _mosi.output(); + _miso.input(); + _clk.output(); + _cs.output(); + } + + + virtual int init() + { + if (_check_id()) { + _init = true; + } + return _init ? BD_ERROR_OK : BD_ERROR_DEVICE_ERROR; + } + + virtual int deinit() + { + _init = false; + return BD_ERROR_OK; + } + + virtual int read(void *buffer, bd_addr_t addr, bd_size_t size) + { + if (!is_valid_read(addr, size) || !_init) { + return BD_ERROR_DEVICE_ERROR; + } + + _assert_cs(true); + + uint8_t cmd[] = { + 0x0B, // Fast read + (addr >> (2 * 8)) & 0xFF, // Address + (addr >> (1 * 8)) & 0xFF, + (addr >> (0 * 8)) & 0xFF, + 0x00 // Dummy + }; + _write((char *)cmd, sizeof(cmd), NULL, 0); + _write(NULL, 0, (char *)buffer, size); + + _assert_cs(false); + + return BD_ERROR_OK; + } + + virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size) + { + if (!is_valid_program(addr, size) || !_init) { + return BD_ERROR_DEVICE_ERROR; + } + + const bd_size_t max_program_size = 256; + bd_size_t programmed = 0; + while (programmed < size) { + const bd_size_t size_left = size - programmed; + const bd_size_t program_size = size_left < max_program_size ? size_left : max_program_size; + + _write_enable(); + _page_program(addr + programmed, (const uint8_t *)buffer, program_size); + _wait_ready(); + programmed += program_size; + } + + return BD_ERROR_OK; + } + + virtual int erase(bd_addr_t addr, bd_size_t size) + { + if (!is_valid_erase(addr, size) || !_init) { + return BD_ERROR_DEVICE_ERROR; + } + + if ((addr == 0) && (size == FLASH_SECTOR_SIZE)) { + // Allow 4K erase only on the first sector. The flash on the basys3 does + // not allow sector erases at the higher addresses. + _write_enable(); + _sector_erase(addr); + _wait_ready(); + return BD_ERROR_OK; + } + + if (!is_valid_erase(addr, size)) { + return BD_ERROR_DEVICE_ERROR; + } + + const uint32_t erase_size = get_erase_size(); + bd_size_t erased = 0; + while (erased < erase_size) { + _write_enable(); + _block_erase(addr + erased); + _wait_ready(); + erased += erase_size; + } + return BD_ERROR_OK; + } + + virtual bd_size_t get_read_size() const + { + return 1; + } + + virtual bd_size_t get_program_size() const + { + return 1; + } + + virtual bd_size_t get_erase_size() const + { + return 0x10000; + } + + virtual bd_size_t get_erase_size(bd_addr_t addr) const + { + return get_erase_size(); + } + + virtual bd_size_t size() const + { + return 8 * 1024 * 1024; + } + + virtual const char *get_type() const + { + return "MbedTesterBlockDevice"; + } + +protected: + + void _write_enable() + { + uint8_t command[1]; + + _assert_cs(true); + + command[0] = 0x06; + _write((char *)command, 1, NULL, 0); + + _assert_cs(false); + } + + void _sector_erase(uint32_t addr) + { + uint8_t command[4]; + + _assert_cs(true); + + command[0] = 0x20; + command[1] = (addr >> (2 * 8)) & 0xFF; + command[2] = (addr >> (1 * 8)) & 0xFF; + command[3] = (addr >> (0 * 8)) & 0xFF; + _write((char *)command, 4, NULL, 0); + + _assert_cs(false); + } + + void _block_erase(uint32_t addr) + { + uint8_t command[4]; + + _assert_cs(true); + + command[0] = 0xD8; + command[1] = (addr >> (2 * 8)) & 0xFF; + command[2] = (addr >> (1 * 8)) & 0xFF; + command[3] = (addr >> (0 * 8)) & 0xFF; + _write((char *)command, 4, NULL, 0); + + _assert_cs(false); + } + + void _page_program(uint32_t addr, const uint8_t *data, uint32_t size) + { + uint8_t command[4]; + + _assert_cs(true); + + command[0] = 0x02; + command[1] = (addr >> (2 * 8)) & 0xFF; + command[2] = (addr >> (1 * 8)) & 0xFF; + command[3] = (addr >> (0 * 8)) & 0xFF; + _write((char *)command, 4, NULL, 0); + _write((char *)data, size, NULL, 0); + + _assert_cs(false); + } + + void _wait_ready() + { + uint8_t command[2]; + uint8_t response[2]; + + // Wait for ready + response[1] = 0xFF; + do { + _assert_cs(true); + + command[0] = 0x05; + command[1] = 0; + _write((char *)command, 2, (char *)response, 2); + + _assert_cs(false); + + } while (response[1] & (1 << 0)); + } + + bool _check_id() + { + uint8_t command[1]; + char id0[3]; + char id1[3]; + + // Read ID twice and verify it is the same + + _assert_cs(true); + + command[0] = 0x9F; + _write((char *)command, 1, NULL, 0); + _write(NULL, 0, id0, sizeof(id0)); + + _assert_cs(false); + + _assert_cs(true); + + command[0] = 0x9F; + _write((char *)command, 1, NULL, 0); + _write(NULL, 0, id1, sizeof(id1)); + + _assert_cs(false); + + // Return failure if IDs are not the same + for (size_t i = 0; i < sizeof(id0); i++) { + if (id0[i] != id1[i]) { + return false; + } + } + + // If all 0xFF return failure + if ((id0[0] == 0xFF) && (id0[1] == 0xFF) && (id0[2] == 0xFF)) { + return false; + } + + // If all 0x00 return failure + if ((id0[0] == 0x00) && (id0[1] == 0x00) && (id0[2] == 0x00)) { + return false; + } + + return true; + } + + void _write(const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length) { + int transfers = 0; + if (tx_length > transfers) { + transfers = tx_length; + } + if (rx_length > transfers) { + transfers = rx_length; + } + + for (int i = 0; i < transfers; i++) { + uint8_t out = i < tx_length ? tx_buffer[i] : 0; + uint8_t in = 0; + for (int j = 0; j < 8; j++) { + _mosi.write((out >> 7) & 1); + out = out << 1; + wait_ns(_wait_ns); + + _clk.write(1); + in = (in << 1) | (_miso.read() ? 1 : 0); + wait_ns(_wait_ns); + + _clk.write(0); + } + if (i < rx_length) { + rx_buffer[i] = in; + } + } + } + + void _assert_cs(bool asserted) { + _clk = 0; + wait_ns(_wait_ns); + _cs = asserted ? 0 : 1; + wait_ns(_wait_ns); + } + + DigitalInOut &_mosi; + DigitalInOut &_miso; + DigitalInOut &_clk; + DigitalInOut &_cs; + uint32_t _wait_ns; + bool _init; +}; + +static void dummy_progress(uint8_t) +{ + // Stub progress handler for firmware update/dump +} + +// Header taken from app note XAPP1081. Information on the commands +// can be found in the 7 Series FPGA configuration user guide - UG470 +static const uint8_t BANK_B_SELECT[] = { + 0x20, 0x00, 0x00, 0x00, // 0x20000000 NOP + 0x30, 0x02, 0x00, 0x01, // 0x30020001 WRITE to WBSTAR (Warm boot start address register) + 0x00, 0x23, 0x00, 0x00, // 0x00230000 0x230000 = Second bank start address + 0x30, 0x00, 0x80, 0x01, // 0x30008001 WRITE to CMD register + 0x00, 0x00, 0x00, 0x0F, // 0x0000000F 0x0F = IPROG command (starts warm boot) + 0x20, 0x00, 0x00, 0x00, // 0x20000000 NOP + 0x20, 0x00, 0x00, 0x00, // 0x20000000 NOP + 0x20, 0x00, 0x00, 0x00 // 0x20000000 NOP +}; + +static const uint8_t SYNC_WORD[] = { + 0xAA, 0x99, 0x55, 0x66 // 0xAA995566 Sync word +}; + +static bool _firmware_header_valid(BlockDevice &flash, bool &valid) +{ + uint8_t buf[64]; + size_t pos = 0; + size_t read_size; + + // Default to invalid + valid = false; + + // Check that first portion is erased + while (pos < FLASH_SECTOR_SIZE - sizeof(SYNC_WORD)) { + read_size = FLASH_SECTOR_SIZE - pos; + if (read_size > sizeof(buf)) { + read_size = sizeof(buf); + } + if (flash.read(buf, pos, read_size) != BD_ERROR_OK) { + return false; + } + pos += read_size; + for (size_t i = 0; i < read_size; i++) { + if (buf[i] != 0xFF) { + valid = false; + return true; + } + } + } + + // Skip the sync word + pos += sizeof(SYNC_WORD); + + // Check that BANK_B_SELECT is valid + read_size = sizeof(BANK_B_SELECT); + if (flash.read(buf, pos, read_size) != BD_ERROR_OK) { + return false; + } + pos += read_size; + if (memcmp(buf, BANK_B_SELECT, sizeof(BANK_B_SELECT)) != 0) { + valid = false; + return true; + } + + // Check if the rest is 0xFF + while (pos < FIRMWARE_HEADER_SIZE) { + read_size = FIRMWARE_HEADER_SIZE - pos; + if (read_size > sizeof(buf)) { + read_size = sizeof(buf); + } + if (flash.read(buf, pos, read_size) != BD_ERROR_OK) { + return false; + } + pos += read_size; + for (size_t i = 0; i < read_size; i++) { + if (buf[i] != 0xFF) { + valid = false; + return true; + } + } + } + + valid = true; + return true; +} + +static bool _firmware_get_active_bank(BlockDevice &flash, bool &second_bank_active) +{ + uint8_t buf[sizeof(SYNC_WORD)]; + size_t pos = 0; + size_t read_size; + + if (flash.read(buf, FLASH_SECTOR_SIZE - sizeof(SYNC_WORD), sizeof(SYNC_WORD)) != BD_ERROR_OK) { + return false; + } + + second_bank_active = memcmp(buf, SYNC_WORD, sizeof(SYNC_WORD)) == 0 ? true : false; + return true; +} + +static bool _firmware_set_active_bank(BlockDevice &flash, bool second_bank) +{ + bool valid = false; + if (!_firmware_header_valid(flash, valid)) { + return false; + } + if (!valid) { + if (flash.erase(0, FIRMWARE_HEADER_SIZE) != BD_ERROR_OK) { + return false; + } + if (flash.program(BANK_B_SELECT, FLASH_SECTOR_SIZE, sizeof(BANK_B_SELECT)) != BD_ERROR_OK) { + return false; + } + } + if (!flash.erase(0, FLASH_SECTOR_SIZE)) { + return false; + } + + + if (second_bank) { + // Write the sync word. Before the sync word is written the FPGA will boot from the first bank. + // After the sync word is written the FPGA will boot from the second bank. + if (flash.program(SYNC_WORD, FLASH_SECTOR_SIZE - sizeof(SYNC_WORD), sizeof(SYNC_WORD)) != BD_ERROR_OK) { + return false; + } + } + + return true; +} + +MbedTester::MbedTester(const PinList *form_factor, const PinList *exclude_pins) + : _form_factor(form_factor), _exclude_pins(exclude_pins), _control_auto(true), _control_valid(false), + _clk_index(PHYSICAL_NC), _mosi_index(PHYSICAL_NC), _miso_index(PHYSICAL_NC), _aux_index(PHYSICAL_NC), + _clk(NULL), _mosi(NULL), _miso(NULL), _aux(NULL) +{ + _reset(); + _init_io_exp_rst_flag = 0; +} + +MbedTester::~MbedTester() +{ + _free_control_pins(); +} + + +void MbedTester::set_control_pins_auto() +{ + _control_auto = true; +} + +void MbedTester::set_control_pins_manual(PinName clk, PinName mosi, PinName miso, PinName aux) +{ + int index; + index = _form_factor.index(clk); + if (index < 0) { + error("Invalid CLK index"); + } + PhysicalIndex clk_index = index; + + index = _form_factor.index(mosi); + if (index < 0) { + error("Invalid MOSI index"); + } + PhysicalIndex mosi_index = index; + + index = _form_factor.index(miso); + if (index < 0) { + error("Invalid MISO index"); + } + PhysicalIndex miso_index = index; + + index = _form_factor.index(aux); + if (index < 0) { + error("Invalid AUX index"); + } + PhysicalIndex aux_index = index; + + if (clk_index + 1 != mosi_index) { + error("MOSI pin index does not follow CLK as required"); + } + + if ((miso_index == clk_index) || (miso_index == mosi_index)) { + error("MISO conflicts with a control channel"); + } + if ((aux_index == clk_index) || (aux_index == mosi_index) || (aux_index == miso_index)) { + error("AUX conflicts with a control channel"); + } + + // All criteria have been met so set the pins + _control_auto = false; + _free_control_pins(); + _clk_index = clk_index; + _mosi_index = mosi_index; + _miso_index = miso_index; + _aux_index = aux_index; + _setup_control_pins(); + _control_valid = true; +} + +bool MbedTester::firmware_dump(FileHandle *dest, Callback progress) +{ + _update_control_pins(); + + if (!progress) { + progress = callback(dummy_progress); + } + + // Mapping intentionally different from control channel to prevent + // unintentional activation (clk and mosi flipped) + MbedTesterBlockDevice flash(*_clk, *_miso , *_mosi, *_aux, FLASH_SPI_FREQ_HZ); + sys_pin_mode_spi_serial_flash(_clk_index , _miso_index, _mosi_index, _aux_index); + + progress(0); + + if (flash.init() != BD_ERROR_OK) { + sys_pin_mode_disabled(); + return false; + } + + // Set the start of dump to the active bank + bool second_bank_active; + if (!_firmware_get_active_bank(flash, second_bank_active)) { + // Error determining active bank + sys_pin_mode_disabled(); + return false; + } + const uint32_t start = FIRMWARE_HEADER_SIZE + (second_bank_active ? FIRMWARE_REGION_SIZE : 0); + + // Get the firmware size + uint32_t offset = 0; + uint8_t buf[256]; + uint32_t prev_percent_done = 0; + if (flash.read(buf, start + offset, LENGTH_SIZE) != BD_ERROR_OK) { + sys_pin_mode_disabled(); + return false; + } + if (dest->write(buf, LENGTH_SIZE) != LENGTH_SIZE) { + sys_pin_mode_disabled(); + return false; + } + offset += LENGTH_SIZE; + uint32_t data_size = (buf[0] << (0 * 8)) | + (buf[1] << (1 * 8)) | + (buf[2] << (2 * 8)) | + (buf[3] << (3 * 8)); + if (data_size > FIRMWARE_REGION_SIZE - LENGTH_SIZE - CRC_SIZE) { + data_size = FIRMWARE_REGION_SIZE - LENGTH_SIZE - CRC_SIZE; + } + const uint32_t firmware_size = data_size + LENGTH_SIZE + CRC_SIZE; + + // Dump firmware + while (offset < firmware_size) { + uint32_t read_size = firmware_size - offset; + if (read_size > sizeof(buf)) { + read_size = sizeof(buf); + } + if (flash.read(buf, start + offset, read_size) != BD_ERROR_OK) { + sys_pin_mode_disabled(); + return false; + } + ssize_t write_size = dest->write(buf, read_size); + if (write_size != read_size) { + sys_pin_mode_disabled(); + return false; + } + offset += read_size; + + const uint8_t percent_done = (offset * 100) / firmware_size; + if (percent_done != prev_percent_done) { + progress(percent_done); + prev_percent_done = percent_done; + } + } + + progress(100); + + sys_pin_mode_disabled(); + return true; +} + +bool MbedTester::firmware_dump_all(FileHandle *dest, Callback progress) +{ + _update_control_pins(); + + if (!progress) { + progress = callback(dummy_progress); + } + + // Mapping intentionally different from control channel to prevent + // unintentional activation (clk and mosi flipped) + MbedTesterBlockDevice flash(*_clk, *_miso , *_mosi, *_aux, FLASH_SPI_FREQ_HZ); + sys_pin_mode_spi_serial_flash(_clk_index , _miso_index, _mosi_index, _aux_index); + + progress(0); + + if (flash.init() != BD_ERROR_OK) { + sys_pin_mode_disabled(); + return false; + } + + uint32_t pos = 0; + uint8_t buf[256]; + uint32_t prev_percent_done = 0; + const uint32_t total_size = flash.size(); + while (pos < total_size) { + uint32_t read_size = total_size - pos; + if (read_size > sizeof(buf)) { + read_size = sizeof(buf); + } + if (flash.read(buf, pos, read_size) != BD_ERROR_OK) { + sys_pin_mode_disabled(); + return false; + } + ssize_t write_size = dest->write(buf, read_size); + if (write_size != read_size) { + sys_pin_mode_disabled(); + return false; + } + pos += read_size; + + const uint8_t percent_done = (pos * 100) / total_size; + if (percent_done != prev_percent_done) { + progress(percent_done); + prev_percent_done = percent_done; + } + } + + progress(100); + + sys_pin_mode_disabled(); + return true; +} + +bool MbedTester::firmware_update(FileHandle *src, Callback progress) +{ + _update_control_pins(); + + if (!progress) { + progress = callback(dummy_progress); + } + + // Mapping intentionally different from control channel to prevent + // unintentional activation (clk and mosi flipped) + MbedTesterBlockDevice flash(*_clk, *_miso , *_mosi, *_aux, FLASH_SPI_FREQ_HZ); + sys_pin_mode_spi_serial_flash(_clk_index , _miso_index, _mosi_index, _aux_index); + + progress(0); + + if (flash.init() != BD_ERROR_OK) { + sys_pin_mode_disabled(); + return false; + } + + // Validate file size + const uint32_t file_size = src->size(); + if (file_size > FIRMWARE_REGION_SIZE) { + // Firmware image too big + sys_pin_mode_disabled(); + return false; + } + if (file_size < LENGTH_SIZE + CRC_SIZE) { + // Firmware image too small + sys_pin_mode_disabled(); + return false; + } + + // Set the start of programming to the inactive bank + bool second_bank_active; + if (!_firmware_get_active_bank(flash, second_bank_active)) { + // Error determining active bank + sys_pin_mode_disabled(); + return false; + } + const uint32_t start = FIRMWARE_HEADER_SIZE + (second_bank_active ? 0 : FIRMWARE_REGION_SIZE); + + // Setup CRC calculation + uint32_t crc; + MbedCRC ct; + if (ct.compute_partial_start(&crc) != 0) { + sys_pin_mode_disabled(); + return false; + } + + uint8_t buf[256]; + const bd_size_t erase_size = flash.get_erase_size(); + uint32_t offset = 0; + uint32_t prev_percent_done = 0; + uint32_t stored_crc = 0; + bool size_valid = false; + while (offset < file_size) { + + // Prepare data + uint32_t program_size = file_size - offset; + if (program_size > sizeof(buf)) { + program_size = sizeof(buf); + } + ssize_t read_size = src->read(buf, program_size); + if (read_size < 0) { + sys_pin_mode_disabled(); + return false; + } else if (read_size == 0) { + break; + } + program_size = read_size; + + // Record values and calculate checksum + uint32_t crc_offset = 0; + uint32_t crc_size = program_size; + if (offset == 0) { + // Overlap with the size field + + // Check that the data length is correct + const size_t data_size = (buf[0] << (0 * 8)) | + (buf[1] << (1 * 8)) | + (buf[2] << (2 * 8)) | + (buf[3] << (3 * 8)); + if (data_size != file_size - LENGTH_SIZE - CRC_SIZE) { + // Invalid data length + sys_pin_mode_disabled(); + return false; + } + size_valid = true; + + // Don't include the length in the checksum + crc_offset += LENGTH_SIZE; + crc_size -= LENGTH_SIZE; + } + if (offset + program_size > file_size - CRC_SIZE) { + // Overlap with the CRC field + for (uint32_t i = 0; i < CRC_SIZE; i++) { + uint32_t byte_offset = file_size - CRC_SIZE + i; + if ((byte_offset >= offset) && (byte_offset < offset + program_size)) { + uint32_t buf_pos = byte_offset - offset; + stored_crc |= buf[buf_pos] << (i * 8); + + // Don't include the stored CRC in the CRC + crc_size--; + } + } + } + if (ct.compute_partial(buf + crc_offset, crc_size, &crc) != 0) { + sys_pin_mode_disabled(); + return false; + } + + // Write data to file + const uint32_t addr = start + offset; + if (addr % erase_size == 0) { + if (flash.erase(addr, erase_size) != BD_ERROR_OK) { + sys_pin_mode_disabled(); + return false; + } + } + if (flash.program(buf, addr, read_size) != BD_ERROR_OK) { + sys_pin_mode_disabled(); + return false; + } + + offset += program_size; + + const uint8_t percent_done = (offset * 100) / file_size; + if (percent_done != prev_percent_done) { + progress(percent_done); + prev_percent_done = percent_done; + } + } + + // Check that everything was good and if so switch active bank + if (!size_valid) { + sys_pin_mode_disabled(); + return false; + } + if (ct.compute_partial_stop(&crc) != 0) { + sys_pin_mode_disabled(); + return false; + } + if (crc != stored_crc) { + sys_pin_mode_disabled(); + return false; + } + if (!_firmware_set_active_bank(flash, !second_bank_active)) { + sys_pin_mode_disabled(); + return false; + } + + progress(100); + + sys_pin_mode_disabled(); + return true; +} + +void MbedTester::pin_map_set(PinName physical, LogicalPin logical) +{ + int index = _form_factor.index(physical); + if (index < 0) { + error("Pin %i not in form factor", physical); + return; + } + if (logical >= LogicalPinTotal) { + error("Invalid logical pin %i", logical); + return; + } + pin_map_index(index, logical); +} + +void MbedTester::pin_map_reset() +{ + for (uint32_t i = 0; i < sizeof(_mapping) / sizeof(_mapping[0]); i++) { + _mapping[i] = PHYSICAL_NC; + } + + uint8_t pin_buf[PHYSICAL_PINS + LOGICAL_PINS]; + memset(pin_buf, 0xFF, sizeof(pin_buf)); + write(TESTER_REMAP, pin_buf, sizeof(pin_buf)); +} + +void MbedTester::peripherals_reset() +{ + uint8_t buf = TESTER_CONTROL_RESET_PERIPHERALS; + write(TESTER_CONTROL_RESET, &buf, sizeof(buf)); +} + +void MbedTester::reset() +{ + // Reset pullup settings + pin_pull_reset_all(); + + // Reset the FPGA + uint8_t buf = TESTER_CONTROL_RESET_ALL; + write(TESTER_CONTROL_RESET, &buf, sizeof(buf)); + + // Reset the pinmap + // NOTE - this is only needed for compatibility with + // older firmware which resets the mapping + // of all pins to 0x00 rather than 0xFF. + pin_map_reset(); + + // Reset internal state variables + _reset(); +} + +void MbedTester::reprogram() +{ + // Trigger reprogramming + uint8_t buf = TESTER_CONTROL_REPROGRAM; + write(TESTER_CONTROL_RESET, &buf, sizeof(buf)); + + // Reset internal state variables + _reset(); +} + +uint32_t MbedTester::version() +{ + uint32_t software_version; + + read(TESTER_CONTROL_VERSION, (uint8_t *)&software_version, sizeof(software_version)); + + return software_version; +} + +void MbedTester::select_peripheral(Peripheral peripheral) +{ + uint8_t data = peripheral; + write(TESTER_PERIPHERAL_SELECT, &data, sizeof(data)); +} + +void MbedTester::pin_pull_reset_all() +{ + _init_io_exp_rst_flag = 1; + sys_pin_write(I2CReset, 0, true); + wait_us(1); + sys_pin_write(I2CReset, 0, false); +} + +int MbedTester::pin_set_pull(PinName pin, PullMode mode) +{ + int index = _form_factor.index(pin); + if ((index < 0) || (index > 127)) { + error("Pin %i not in form factor", pin); + return -1; + } + + return pin_set_pull_index(index, mode); +} + +int MbedTester::pin_set_pull_index(int index, PullMode mode) +{ + // Reset IO expanders once after Mbed reset if user attempts + // to read/write them without explicitly reseting them + if (_init_io_exp_rst_flag == 0) { + pin_pull_reset_all(); + } + uint8_t chip_num;//can be 0-5 + uint16_t dev_addr;//can be 0x44 or 0x46 + uint8_t port_num;//can be 0-23 + uint8_t output_port_reg;//can be 4, 5, or 6 + uint8_t config_reg;//can be 12, 13, or 14 + uint8_t reg_bit;//can be 0-7 + uint8_t cmd0[2];//for writing configuration register + uint8_t cmd1[2];//for writing output port register + uint8_t i2c_index;//can be 0, 1, or 2 for TESTER_SYS_IO_MODE_I2C_IO_EXPANDER0/1/2 + + chip_num = index / 24; + if ((chip_num == 0) || (chip_num == 1)) { + i2c_index = 0; + } else if ((chip_num == 2) || (chip_num == 3)) { + i2c_index = 1; + } else if ((chip_num == 4) || (chip_num == 5)) { + i2c_index = 2; + } else { + error("Corrupt index %i, should be 0-127\r\n", index); + return -1; + } + dev_addr = (chip_num % 2) ? 0x44 : 0x46; + port_num = index % 24; + output_port_reg = 4 + (port_num / 8); + config_reg = 12 + (port_num / 8); + reg_bit = port_num % 8; + + uint8_t read_config_byte[1]; + uint8_t read_output_byte[1]; + if (io_expander_i2c_read(i2c_index, dev_addr, config_reg, read_config_byte, 1) != 0) { + return -1; + } + if (io_expander_i2c_read(i2c_index, dev_addr, output_port_reg, read_output_byte, 1) != 0) { + return -1; + } + cmd0[0] = config_reg; + if ((mode == PullDown) || (mode == PullUp)) { + cmd0[1] = read_config_byte[0] & ~(1 << reg_bit); + cmd1[0] = output_port_reg; + if (mode == PullDown) { + cmd1[1] = read_output_byte[0] & ~(1 << reg_bit); + } else if (mode == PullUp) { + cmd1[1] = read_output_byte[0] | (1 << reg_bit); + } + } else if (mode == PullNone) { + cmd0[1] = read_config_byte[0] | (1 << reg_bit); + } + + //write configuration register for all 3 modes + if (io_expander_i2c_write(i2c_index, dev_addr, cmd0, 2) != 0) { + return -1; + } + //only write output register for pulldown and pullup + if ((mode == PullDown) || (mode == PullUp)) { + if (io_expander_i2c_write(i2c_index, dev_addr, cmd1, 2) != 0) { + return -1; + } + } + return 0; +} + +uint8_t MbedTester::io_expander_read(PinName pin, IOExpanderReg reg_type) +{ + int index = _form_factor.index(pin); + + return io_expander_read_index(index, reg_type); +} + +uint8_t MbedTester::io_expander_read_index(int index, IOExpanderReg reg_type) +{ + // Reset IO expanders once after Mbed reset if user attempts + // to read/write them without explicitly reseting them + if (_init_io_exp_rst_flag == 0) { + pin_pull_reset_all(); + } + uint8_t read_byte[1] = {0}; + uint8_t chip_num;//can be 0-5 + uint16_t dev_addr;//can be 0x44 or 0x46 + uint8_t port_num;//can be 0-23 + uint8_t input_port_reg;//can be 0, 1, or 2 + uint8_t output_port_reg;//can be 4, 5, or 6 + uint8_t config_reg;//can be 12, 13, or 14 + uint8_t reg_bit;//can be 0-7 + uint8_t i2c_index; + + chip_num = index / 24; + if ((chip_num == 0) || (chip_num == 1)) { + i2c_index = 0; + } else if ((chip_num == 2) || (chip_num == 3)) { + i2c_index = 1; + } else if ((chip_num == 4) || (chip_num == 5)) { + i2c_index = 2; + } else { + i2c_index = 0xFF; + error("Invalid pin index, index should be in the range of 0-127"); + } + dev_addr = (chip_num % 2) ? 0x44 : 0x46; + port_num = index % 24; + input_port_reg = (port_num / 8); + output_port_reg = 4 + (port_num / 8); + config_reg = 12 + (port_num / 8); + reg_bit = port_num % 8; + uint8_t reg; + if (reg_type == RegInput) { + reg = input_port_reg; + } else if (reg_type == RegOutput) { + reg = output_port_reg; + } else if (reg_type == RegConfig) { + reg = config_reg; + } else { + reg = 0xFF; + error("Invalid register type, should be: INPUT, OUTPUT, or RegConfig"); + } + + int read_success = io_expander_i2c_read(i2c_index, dev_addr, reg, read_byte, 1); + // MBED_ASSERT(read_success == 0); + uint8_t bit = (read_byte[0] & (1 << reg_bit)) >> reg_bit; + return bit; +} + +int MbedTester::io_expander_i2c_read(uint8_t i2c_index, uint8_t dev_addr, uint8_t start_reg, uint8_t *data, int length) +{ + _update_control_pins(); + //sda_in = _miso_index + //sda_val = _aux_index + //scl_in = _mosi_index (PHYSICAL_NC) + //scl_val = _clk_index + DigitalInOut *sda_in = _miso; + DigitalInOut *sda_val = _aux; + DigitalInOut *scl_val = _clk; + sda_in->input(); + sda_val->output(); + *sda_val = 1; + scl_val->output(); + sys_pin_mode_i2c_io_expander(i2c_index, _miso_index, _aux_index, PHYSICAL_NC, _clk_index); + + //start condition + *scl_val = 1; + wait_ns(2500); + *sda_val = 0; + wait_ns(2500); + + // begin writing data, dev_addr first + uint8_t send_bit; + for (int j = 0; j < 2; j += 1) { + *scl_val = 0; + *sda_val = 0; + wait_ns(2500); + for (int i = 7; i > -1; i -= 1) { + if (j == 0) { + send_bit = (dev_addr & (1 << i)) >> i; + } else { + send_bit = (start_reg & (1 << i)) >> i; + } + *sda_val = send_bit; + wait_ns(500); + + *scl_val = 1; + wait_ns(2500); + *scl_val = 0; + wait_ns(1000); + *sda_val = 0; + wait_ns(1000); + } + // receive ACK from IO extender + *sda_val = 1;//make sda high z to receive ACK + //clk the ACK + *scl_val = 1; + //read sda to check for ACK or NACK + if (*sda_in) { + return -1;//NACK - write failed + } + wait_ns(2500); + *scl_val = 0; + wait_ns(2500); + } + + //start condition + *sda_val = 1; + *scl_val = 1; + wait_ns(2500); + *sda_val = 0; + wait_ns(2500); + + // begin reading data, write (dev_addr | 1) first + dev_addr |= 1; + for (int j = -1; j < length; j += 1) { + uint8_t read_byte = 0; + for (int i = 7; i > -1; i -= 1) { + if (j == -1) { + *scl_val = 0; + *sda_val = 0; + send_bit = (dev_addr & (1 << i)) >> i; + *sda_val = send_bit; + wait_ns(500); + + *scl_val = 1; + wait_ns(2500); + *scl_val = 0; + wait_ns(1000); + *sda_val = 0; + wait_ns(1000); + } else { + *scl_val = 1; + read_byte |= (*sda_in << i); + wait_ns(2500); + *scl_val = 0; + wait_ns(2500); + } + } + if (j > -1) { + data[j] = read_byte; + } + if (j == -1) { + // receive ACK from IO extender + *sda_val = 1;//make sda high z to receive ACK + //clk the ACK + *scl_val = 1; + //read sda to check for ACK or NACK + if (*sda_in) { + return -1;//NACK - write failed + } + wait_ns(2500); + *scl_val = 0; + wait_ns(2500); + } else { + if (j == (length-1)) {//NACK to signal end of read + *sda_val = 1; + wait_ns(1000); + *scl_val = 1; + wait_ns(2500); + *scl_val = 0; + wait_ns(1500); + } else {//ACK to signal read will continue + *sda_val = 0; + wait_ns(1000); + *scl_val = 1; + wait_ns(2500); + *scl_val = 0; + wait_ns(500); + *sda_val = 1; + wait_ns(1000); + } + } + } + + //stop condition + *sda_val = 0; + wait_ns(2500); + *scl_val = 1; + wait_ns(2500); + *sda_val = 1; + wait_ns(2500); + + sys_pin_mode_disabled(); + + return 0; +} + +int MbedTester::io_expander_i2c_write(uint8_t i2c_index, uint8_t dev_addr, uint8_t *data, int length) +{ + _update_control_pins(); + //sda_in = _miso_index + //sda_val = _aux_index + //scl_in = _mosi_index (PHYSICAL_NC) + //scl_val = _clk_index + DigitalInOut *sda_in = _miso; + DigitalInOut *sda_val = _aux; + DigitalInOut *scl_val = _clk; + sda_in->input(); + sda_val->output(); + *sda_val = 1; + scl_val->output(); + sys_pin_mode_i2c_io_expander(i2c_index, _miso_index, _aux_index, PHYSICAL_NC, _clk_index); + + //start condition + *scl_val = 1; + wait_ns(2500); + *sda_val = 0; + wait_ns(2500); + + // begin writing data, dev_addr first + uint8_t send_bit; + for (int j = -1; j < length; j += 1) { + *scl_val = 0; + *sda_val = 0; + for (int i = 7; i > -1; i -= 1) { + if (j == -1) { + send_bit = (dev_addr & (1 << i)) >> i; + } else { + send_bit = (data[j] & (1 << i)) >> i; + } + + *sda_val = send_bit; + wait_ns(500); + + *scl_val = 1; + wait_ns(2500); + *scl_val = 0; + wait_ns(1000); + *sda_val = 0; + wait_ns(1000); + } + // receive ACK from IO extender + *sda_val = 1;//make sda high z to receive ACK + //clk the ACK + *scl_val = 1; + //read sda to check for ACK or NACK + if (*sda_in) { + return -1;//NACK - write failed + } + wait_ns(2500); + *scl_val = 0; + wait_ns(2500); + } + + //stop condition + *sda_val = 0; + wait_ns(2500); + *scl_val = 1; + wait_ns(2500); + *sda_val = 1; + wait_ns(2500); + + sys_pin_mode_disabled(); + + return 0; +} + +int MbedTester::pin_set_pull_bb(PinName pin, PullMode mode) +{ + int index = _form_factor.index(pin); + if ((index < 0) || (index > 127)) { + error("Pin %i not in form factor", pin); + return -1; + } + uint8_t chip_num;//can be 0-5 + SystemPin sda;//can be I2CSda0, I2CSda1, or I2CSda2 + SystemPin scl;//can be I2CScl0, I2CScl1, or I2CScl2 + uint16_t dev_addr;//can be 0x44 or 0x46 + uint8_t port_num;//can be 0-23 + uint8_t output_port_reg;//can be 4, 5, or 6 + uint8_t config_reg;//can be 12, 13, or 14 + uint8_t reg_bit;//can be 0-7 + uint8_t cmd0[2];//for writing configuration register + uint8_t cmd1[2];//for writing output port register + + chip_num = index / 24; + if ((chip_num == 0) || (chip_num == 1)) { + sda = I2CSda0; + scl = I2CScl0; + } else if ((chip_num == 2) || (chip_num == 3)) { + sda = I2CSda1; + scl = I2CScl1; + } else if ((chip_num == 4) || (chip_num == 5)) { + sda = I2CSda2; + scl = I2CScl2; + } else { + error("Pin %i not in form factor", pin); + return -1; + } + dev_addr = (chip_num % 2) ? 0x44 : 0x46; + port_num = index % 24; + output_port_reg = 4 + (port_num / 8); + config_reg = 12 + (port_num / 8); + reg_bit = port_num % 8; + + uint8_t read_config_byte[1]; + uint8_t read_output_byte[1]; + if (io_expander_i2c_read_bb(sda, scl, dev_addr, config_reg, read_config_byte, 1) != 0) { + return -1; + } + if (io_expander_i2c_read_bb(sda, scl, dev_addr, output_port_reg, read_output_byte, 1) != 0) { + return -1; + } + cmd0[0] = config_reg; + if ((mode == PullDown) || (mode == PullUp)) { + cmd0[1] = read_config_byte[0] & ~(1 << reg_bit); + cmd1[0] = output_port_reg; + if (mode == PullDown) { + cmd1[1] = read_output_byte[0] & ~(1 << reg_bit); + } else if (mode == PullUp) { + cmd1[1] = read_output_byte[0] | (1 << reg_bit); + } + } else if (mode == PullNone) { + cmd0[1] = read_config_byte[0] | (1 << reg_bit); + } + + //write configuration register for all 3 modes + if (io_expander_i2c_write_bb(sda, scl, dev_addr, cmd0, 2) != 0) { + return -1; + } + //only write output register for pulldown and pullup + if ((mode == PullDown) || (mode == PullUp)) { + if (io_expander_i2c_write_bb(sda, scl, dev_addr, cmd1, 2) != 0) { + return -1; + } + } + return 0; +} + +uint8_t MbedTester::io_expander_read_bb(PinName pin, IOExpanderReg reg_type) +{ + int index = _form_factor.index(pin); + uint8_t read_byte[1] = {0}; + uint8_t chip_num;//can be 0-5 + SystemPin sda;//can be I2CSda0, I2CSda1, or I2CSda2 + SystemPin scl;//can be I2CScl0, I2CScl1, or I2CScl2 + uint16_t dev_addr;//can be 0x44 or 0x46 + uint8_t port_num;//can be 0-23 + uint8_t input_port_reg;//can be 0, 1, or 2 + uint8_t output_port_reg;//can be 4, 5, or 6 + uint8_t config_reg;//can be 12, 13, or 14 + uint8_t reg_bit;//can be 0-7 + + chip_num = index / 24; + if ((chip_num == 0) || (chip_num == 1)) { + sda = I2CSda0; + scl = I2CScl0; + } else if ((chip_num == 2) || (chip_num == 3)) { + sda = I2CSda1; + scl = I2CScl1; + } else if ((chip_num == 4) || (chip_num == 5)) { + sda = I2CSda2; + scl = I2CScl2; + } else { + sda = (SystemPin)-1; + scl = (SystemPin)-1; + error("Invalid pin index, index should be in the range of 0-127"); + } + + dev_addr = (chip_num % 2) ? 0x44 : 0x46; + port_num = index % 24; + input_port_reg = (port_num / 8); + output_port_reg = 4 + (port_num / 8); + config_reg = 12 + (port_num / 8); + reg_bit = port_num % 8; + uint8_t reg; + if (reg_type == RegInput) { + reg = input_port_reg; + } else if (reg_type == RegOutput) { + reg = output_port_reg; + } else if (reg_type == RegConfig) { + reg = config_reg; + } else { + reg = 0xFF; + error("Invalid register type, should be: INPUT, OUTPUT, or CONFIG"); + } + + int read_success = io_expander_i2c_read_bb(sda, scl, dev_addr, reg, read_byte, 1); + // MBED_ASSERT(read_success == 0); + uint8_t bit = (read_byte[0] & (1 << reg_bit)) >> reg_bit; + return bit; +} + +int MbedTester::io_expander_i2c_read_bb(SystemPin sda, SystemPin scl, uint8_t dev_addr, uint8_t start_reg, uint8_t *data, int length) +{ + //start condition + sys_pin_write(sda, 0, false); + sys_pin_write(scl, 0, false); + sys_pin_write(sda, 0, true); + + // begin writing data, dev_addr first + uint8_t send_bit; + for (int j = 0; j < 2; j += 1) { + sys_pin_write(scl, 0, true); + sys_pin_write(sda, 0, true); + for (int i = 7; i > -1; i -= 1) { + if (j == 0) { + send_bit = (dev_addr & (1 << i)) >> i; + } else { + send_bit = (start_reg & (1 << i)) >> i; + } + if (send_bit == 1) { + sys_pin_write(sda, 0, false); + } else if (send_bit == 0) { + sys_pin_write(sda, 0, true); + } + sys_pin_write(scl, 0, false); + sys_pin_write(scl, 0, true); + sys_pin_write(sda, 0, true); + } + // receive ACK from IO extender + sys_pin_write(sda, 0, false);//make sda high z to receive ACK + //clk the ACK + sys_pin_write(scl, 0, false); + //read sda to check for ACK or NACK + if (sys_pin_read(sda)) { + return -1;//NACK - write failed + } + sys_pin_write(scl, 0, true); + } + + //start condition + sys_pin_write(sda, 0, false); + sys_pin_write(scl, 0, false); + sys_pin_write(sda, 0, true); + + // begin reading data, write (dev_addr | 1) first + dev_addr |= 1; + for (int j = -1; j < length; j += 1) { + uint8_t read_byte = 0; + for (int i = 7; i > -1; i -= 1) { + if (j == -1) { + sys_pin_write(scl, 0, true); + sys_pin_write(sda, 0, true); + send_bit = (dev_addr & (1 << i)) >> i; + if (send_bit == 1) { + sys_pin_write(sda, 0, false); + } else if (send_bit == 0) { + sys_pin_write(sda, 0, true); + } + sys_pin_write(scl, 0, false); + sys_pin_write(scl, 0, true); + sys_pin_write(sda, 0, true); + } else { + sys_pin_write(scl, 0, false); + read_byte |= (sys_pin_read(sda) << i); + sys_pin_write(scl, 0, true); + } + } + if (j > -1) { + data[j] = read_byte; + } + if (j == -1) { + // receive ACK from IO extender + sys_pin_write(sda, 0, false);//make sda high z to receive ACK + //clk the ACK + sys_pin_write(scl, 0, false); + //read sda to check for ACK or NACK + if (sys_pin_read(sda)) { + return -1;//NACK - write failed + } + sys_pin_write(scl, 0, true); + } else { + if (j == (length-1)) {//NACK to signal end of read + sys_pin_write(sda, 0, false); + sys_pin_write(scl, 0, false); + sys_pin_write(scl, 0, true); + } else {//ACK to signal read will continue + sys_pin_write(sda, 0, true); + sys_pin_write(scl, 0, false); + sys_pin_write(scl, 0, true); + sys_pin_write(sda, 0, false); + } + } + } + + //stop condition + sys_pin_write(sda, 0, true); + sys_pin_write(scl, 0, false); + sys_pin_write(sda, 0, false); + return 0; +} + +int MbedTester::io_expander_i2c_write_bb(SystemPin sda, SystemPin scl, uint8_t dev_addr, uint8_t *data, int length) +{ + //start condition + sys_pin_write(sda, 0, false); + sys_pin_write(scl, 0, false); + sys_pin_write(sda, 0, true); + + // begin writing data, dev_addr first + uint8_t send_bit; + for (int j = -1; j < length; j += 1) { + sys_pin_write(scl, 0, true); + sys_pin_write(sda, 0, true); + for (int i = 7; i > -1; i -= 1) { + if (j == -1) { + send_bit = (dev_addr & (1 << i)) >> i; + } else { + send_bit = (data[j] & (1 << i)) >> i; + } + if (send_bit == 1) { + sys_pin_write(sda, 0, false); + } else if (send_bit == 0) { + sys_pin_write(sda, 0, true); + } + sys_pin_write(scl, 0, false); + sys_pin_write(scl, 0, true); + sys_pin_write(sda, 0, true); + } + // receive ACK from IO extender + sys_pin_write(sda, 0, false);//make sda high z to receive ACK + //clk the ACK + sys_pin_write(scl, 0, false); + //read sda to check for ACK or NACK + if (sys_pin_read(sda)) { + return -1;//NACK - write failed + } + sys_pin_write(scl, 0, true); + } + + //stop condition + sys_pin_write(sda, 0, true); + sys_pin_write(scl, 0, false); + sys_pin_write(sda, 0, false); + return 0; +} + +void MbedTester::set_analog_out(bool enable, float voltage) +{ + uint32_t cycles_high = (int)(100 * voltage); + uint32_t period = 100; + set_pwm_period_and_cycles_high(period, cycles_high); + set_pwm_enable(enable); +} + +int MbedTester::set_mux_addr(PinName pin) +{ + int index = _form_factor.index(pin); + if ((index < 0) || (index > 127)) { + error("Pin %i not in form factor", pin); + return -1; + } + + return set_mux_addr_index(index); +} + +int MbedTester::set_mux_addr_index(int index) +{ + int addr_pin = (int)AnalogMuxAddr0; + for (int i = 0; i < 8; i += 1) { + sys_pin_write((SystemPin)addr_pin, (((1 << i) & index) >> i), true); + addr_pin += 1; + } + + return 0; +} + +void MbedTester::set_mux_enable(bool val) +{ + if (val == true) { + sys_pin_write(AnalogMuxEnable, 1, true); + } + else if (val == false) { + sys_pin_write(AnalogMuxEnable, 0, true); + } + wait_us(10); +} + +void MbedTester::set_pwm_enable(bool val) +{ + uint8_t data; + if (val == true) { + data = 1; + } + else if (val == false) { + data = 0; + } + write(TESTER_SYS_IO_PWM_ENABLE, &data, sizeof(data)); +} + +bool MbedTester::get_pwm_enable() +{ + uint8_t val = 0; + read(TESTER_SYS_IO_PWM_ENABLE, &val, sizeof(val)); + if (val == 1) { + return true; + } + else if (val == 0) { + return false; + } else { + error("Corrupt pwm enable value"); + return false; + } +} + +void MbedTester::set_pwm_period_and_cycles_high(uint32_t period, uint32_t cycles_high) +{ + set_pwm_enable(false); + uint32_t p = period - 1;//period in cycles + uint32_t d = cycles_high;//number of cycles pwm out is high + write(TESTER_SYS_IO_PWM_PERIOD, (uint8_t*)&p, sizeof(p)); + write(TESTER_SYS_IO_PWM_CYCLES_HIGH, (uint8_t*)&d, sizeof(d)); + set_pwm_enable(true); +} + +uint32_t MbedTester::get_pwm_period() +{ + uint32_t period = 0; + read(TESTER_SYS_IO_PWM_PERIOD, (uint8_t*)&period, sizeof(period)); + return period + 1;//clk cycles +} + +uint8_t MbedTester::get_pwm_cycles_high() +{ + uint8_t cycles_high = 0; + read(TESTER_SYS_IO_PWM_CYCLES_HIGH, &cycles_high, sizeof(cycles_high)); + return cycles_high; +} + +uint16_t MbedTester::get_analogmuxin_measurement() +{ + wait_ms(1);//wait for value to stabalize + //take snapshot of conversion value to make safe for reading + set_snapshot(); + uint16_t an_mux_analogin_measurement = 0; + read(TESTER_SYS_AN_MUX_ANALOGIN_MEASUREMENT, (uint8_t*)&an_mux_analogin_measurement, sizeof(an_mux_analogin_measurement)); + return an_mux_analogin_measurement; +} + +uint16_t MbedTester::get_anin_measurement(int index) +{ + //check index is in bounds + if ((index < 0) || (index >= ANALOG_COUNT)) { + error("AnalogIn index is out of bounds"); + } + //take snapshot of conversion value to make safe for reading + set_snapshot(); + uint16_t anin_measurement = 0; + read((TESTER_SYS_ANIN0_MEASUREMENT + (index * 10)), (uint8_t*)&anin_measurement, sizeof(anin_measurement));//10 because sizeof measurement + sizeof measurements_sum = 10 + return anin_measurement; +} + +void MbedTester::get_anin_sum_samples_cycles(int index, uint64_t *sum, uint32_t *samples, uint64_t *cycles) +{ + //check index is in bounds + if ((index < 0) || (index >= ANALOG_COUNT)) { + error("AnalogIn index is out of bounds"); + } + //take snapshot of the sum/samples/cycles so that all 3 values are correct in relation to each other + set_snapshot(); + read((TESTER_SYS_ANIN0_MEASUREMENTS_SUM + (index * 10)), (uint8_t*)sum, sizeof(*sum));//10 because sizeof measurement + sizeof measurements_sum = 10 + read(TESTER_SYS_NUM_POWER_SAMPLES, (uint8_t*)samples, sizeof(*samples)); + read(TESTER_SYS_NUM_POWER_CYCLES, (uint8_t*)cycles, sizeof(*cycles)); +} + +void MbedTester::set_snapshot() +{ + uint8_t data = 1; + write(TESTER_SYS_ADC_SNAPSHOT, &data, sizeof(data)); + wait_us(1); +} + +void MbedTester::set_sample_adc(bool val) +{ + uint8_t data; + if (val == true) { + data = 1; + } + else if (val == false) { + data = 0; + } + write(TESTER_SYS_SAMPLE_ADC, &data, sizeof(data)); +} + +float MbedTester::get_analog_in() +{ + uint16_t data = get_analogmuxin_measurement(); + float data_f = (float)data / 4095.0f; + return data_f; +} + +float MbedTester::get_anin_voltage(int index) +{ + uint16_t data = get_anin_measurement(index); + float data_f = (float)data / 4095.0f; + return data_f; +} + +int MbedTester::gpio_read(LogicalPin gpio) +{ + if (gpio >= LogicalPinCount) { + error("Invalid pin for gpio_read"); + return 0; + } + uint8_t data = 0; + read(TESTER_GPIO + gpio, &data, sizeof(data)); + return data; +} + +void MbedTester::gpio_write(LogicalPin gpio, int value, bool drive) +{ + if (gpio >= LogicalPinCount) { + error("Invalid pin for gpio_write"); + return; + } + uint8_t data = 0; + data |= value ? (1 << 0) : 0; + data |= drive ? (1 << 1) : 0; + write(TESTER_GPIO + gpio, &data, sizeof(data)); +} + +void MbedTester::io_metrics_start() +{ + uint8_t data = TESTER_IO_METRICS_CTRL_RESET_BIT; + write(TESTER_IO_METRICS_CTRL, &data, sizeof(data)); + + data = TESTER_IO_METRICS_CTRL_ACTIVE_BIT; + write(TESTER_IO_METRICS_CTRL, &data, sizeof(data)); +} + +void MbedTester::io_metrics_stop() +{ + uint8_t data = 0; + write(TESTER_IO_METRICS_CTRL, &data, sizeof(data)); +} + +void MbedTester::io_metrics_continue() +{ + uint8_t data = TESTER_IO_METRICS_CTRL_ACTIVE_BIT; + write(TESTER_IO_METRICS_CTRL, &data, sizeof(data)); +} + +uint32_t MbedTester::io_metrics_min_pulse_low(LogicalPin pin) +{ + if (pin >= LogicalPinCount) { + error("Invalid pin for io_metrics"); + return 0; + } + + uint32_t data = 0; + read(TESTER_IO_METRICS_MIN_PULSE_LOW(pin), (uint8_t *)&data, sizeof(data)); + return data; +} + +uint32_t MbedTester::io_metrics_min_pulse_high(LogicalPin pin) +{ + if (pin >= LogicalPinCount) { + error("Invalid pin for io_metrics"); + return 0; + } + + uint32_t data = 0; + read(TESTER_IO_METRICS_MIN_PULSE_HIGH(pin), (uint8_t *)&data, sizeof(data)); + return data; +} + +uint32_t MbedTester::io_metrics_max_pulse_low(LogicalPin pin) +{ + if (pin >= LogicalPinCount) { + error("Invalid pin for io_metrics"); + return 0; + } + + uint32_t data = 0; + read(TESTER_IO_METRICS_MAX_PULSE_LOW(pin), (uint8_t *)&data, sizeof(data)); + return data; +} + +uint32_t MbedTester::io_metrics_max_pulse_high(LogicalPin pin) +{ + if (pin >= LogicalPinCount) { + error("Invalid pin for io_metrics"); + return 0; + } + + uint32_t data = 0; + read(TESTER_IO_METRICS_MAX_PULSE_HIGH(pin), (uint8_t *)&data, sizeof(data)); + return data; +} + +uint32_t MbedTester::io_metrics_rising_edges(LogicalPin pin) +{ + if (pin >= LogicalPinCount) { + error("Invalid pin for io_metrics"); + return 0; + } + + uint32_t data = 0; + read(TESTER_IO_METRICS_RISING_EDGES(pin), (uint8_t *)&data, sizeof(data)); + return data; +} + +uint32_t MbedTester::io_metrics_falling_edges(LogicalPin pin) +{ + if (pin >= LogicalPinCount) { + error("Invalid pin for io_metrics"); + return 0; + } + + uint32_t data = 0; + read(TESTER_IO_METRICS_FALLING_EDGES(pin), (uint8_t *)&data, sizeof(data)); + return data; +} + +bool MbedTester::sys_pin_read(SystemPin pin) +{ + + if (pin >= SystemPinCount) { + error("Invalid pin for gpio_read"); + return 0; + } + uint8_t data = 0; + read(TESTER_SYS_IO + pin, &data, sizeof(data)); + return data; +} + +void MbedTester::sys_pin_write(SystemPin pin, int value, bool drive) +{ + if (pin >= SystemPinCount) { + error("Invalid pin for gpio_write"); + return; + } + uint8_t data = 0; + data |= value ? (1 << 0) : 0; + data |= drive ? (1 << 1) : 0; + write(TESTER_SYS_IO + pin, &data, sizeof(data)); +} + +void MbedTester::sys_pin_mode_disabled() +{ + const uint32_t base = LogicalPinTotal; + + pin_map_index(PHYSICAL_NC, (LogicalPin)(base + 0)); + pin_map_index(PHYSICAL_NC, (LogicalPin)(base + 1)); + pin_map_index(PHYSICAL_NC, (LogicalPin)(base + 2)); + pin_map_index(PHYSICAL_NC, (LogicalPin)(base + 3)); + + uint8_t mode = TESTER_SYS_IO_MODE_DISABLED; + write(TESTER_SYS_IO_MODE, &mode, sizeof(mode)); +} + +void MbedTester::sys_pin_mode_spi_serial_flash(PhysicalIndex mosi, PhysicalIndex miso, PhysicalIndex clk, PhysicalIndex ssel) +{ + const uint32_t base = LogicalPinTotal; + + pin_map_index(mosi, (LogicalPin)(base + 0)); + pin_map_index(miso, (LogicalPin)(base + 1)); + pin_map_index(clk, (LogicalPin)(base + 2)); + pin_map_index(ssel, (LogicalPin)(base + 3)); + + uint8_t mode = TESTER_SYS_IO_MODE_SPI_SERIAL_FLASH; + write(TESTER_SYS_IO_MODE, &mode, sizeof(mode)); +} + +void MbedTester::sys_pin_mode_i2c_io_expander(int index, PhysicalIndex sda_in, PhysicalIndex sda_val, PhysicalIndex scl_in, PhysicalIndex scl_val) +{ + const uint32_t base = LogicalPinTotal; + + pin_map_index(sda_in, (LogicalPin)(base + 0)); + pin_map_index(sda_val, (LogicalPin)(base + 1)); + pin_map_index(scl_in, (LogicalPin)(base + 2)); + pin_map_index(scl_val, (LogicalPin)(base + 3)); + + uint8_t mode = 0; + if (index == 0) { + mode = TESTER_SYS_IO_MODE_I2C_IO_EXPANDER0; + } else if (index == 1) { + mode = TESTER_SYS_IO_MODE_I2C_IO_EXPANDER1; + } else if (index == 2) { + mode = TESTER_SYS_IO_MODE_I2C_IO_EXPANDER2; + } else { + error("Invalid index for sys_pin_mode_i2c_io_expander"); + } + + write(TESTER_SYS_IO_MODE, &mode, sizeof(mode)); +} + +void MbedTester::pin_map_index(PhysicalIndex physical_index, LogicalPin logical) +{ + uint8_t remap; + if ((physical_index >= PHYSICAL_PINS) && (physical_index != PHYSICAL_NC)) { + error("Invalid physical pin index %i", physical_index); + return; + } + if (logical >= sizeof(_mapping) / sizeof(_mapping[0])) { + error("Invalid logical pin %i", logical); + return; + } + + // Unmap the previous pin if it had been mapped + if (_mapping[logical] < PHYSICAL_PINS) { + remap = PHYSICAL_NC; + write(TESTER_REMAP + _mapping[logical], &remap, sizeof(remap)); + } + _mapping[logical] = physical_index; + + // Remap physical pin if it is not PHYSICAL_NC + if (physical_index < PHYSICAL_PINS) { + remap = logical; + write(TESTER_REMAP + physical_index, &remap, sizeof(remap)); + } + // Remap logical pin + remap = physical_index; + write(TESTER_REMAP + PHYSICAL_PINS + logical, &remap, sizeof(remap)); +} + +void MbedTester::write(uint32_t addr, const uint8_t *data, uint32_t size) +{ + _update_control_pins(); + + mbed_tester_command(_clk, _mosi, _miso, _miso_index, addr, true, (uint8_t *)data, size); +} + +void MbedTester::read(uint32_t addr, uint8_t *data, uint32_t size) +{ + _update_control_pins(); + + mbed_tester_command(_clk, _mosi, _miso, _miso_index, addr, false, data, size); +} + +bool MbedTester::self_test_all() +{ + return self_test_control_channels() && self_test_control_miso(); +} + +bool MbedTester::self_test_control_channels() +{ + for (uint32_t i = 0; i < _form_factor.count() / 2; i++) { + const int clk_index = i * 2 + 0; + const int mosi_index = i * 2 + 1; + const PinName clk = _form_factor.get(clk_index); + const PinName mosi = _form_factor.get(mosi_index); + + // Check if the control pair is allowed and skip if it is not + if (_exclude_pins.has_pin(clk) || _exclude_pins.has_pin(mosi)) { + mbed_tester_printf("Skipping pin indexes clk=%i, mosi=%i\r\n", i * 2 + 0, i * 2 + 1); + continue; + } + + // Find a pin to use as miso + int miso_index = 0; + PinName miso = NC; + DynamicPinList more_restricted(_exclude_pins); + more_restricted.add(clk); + more_restricted.add(mosi); + for (uint32_t j = 0; j < _form_factor.count(); j++) { + miso_index = j; + const PinName temp = _form_factor.get(miso_index); + if (!more_restricted.has_pin(temp)) { + miso = temp; + break; + } + } + if (miso == NC) { + set_control_pins_auto(); + return false; + } + + // Find a pin to use as aux + int aux_index = 0; + PinName aux = NC; + more_restricted.add(miso); + for (uint32_t j = 0; j < _form_factor.count(); j++) { + aux_index = j; + const PinName temp = _form_factor.get(aux_index); + if (!more_restricted.has_pin(temp)) { + aux = temp; + break; + } + } + if (aux == NC) { + set_control_pins_auto(); + return false; + } + + // Write and read back a value + mbed_tester_printf("Testing clk_index=%2i, mosi_index=%2i, miso_index=%2i, aux_index=%2i\r\n", clk_index, mosi_index, miso_index, aux_index); + set_control_pins_manual(clk, mosi, miso, aux); + if (!self_test_control_current()) { + mbed_tester_printf(" Fail\r\n"); + set_control_pins_auto(); + return false; + } + mbed_tester_printf(" Pass\r\n"); + } + + set_control_pins_auto(); + return true; +} + +bool MbedTester::self_test_control_miso() +{ + for (uint32_t i = 0; i < _form_factor.count(); i++) { + const int miso_index = i; + const PinName miso = _form_factor.get(miso_index); + + if (_exclude_pins.has_pin(miso)) { + mbed_tester_printf("Skipping miso index %i\r\n", i); + continue; + } + + // Find pins to use as clk and mosi + int clk_index = 0; + int mosi_index = 0; + PinName clk = NC; + PinName mosi = NC; + DynamicPinList more_restricted(_exclude_pins); + more_restricted.add(miso); + for (uint32_t j = 0; j < _form_factor.count() / 2; j++) { + clk_index = j * 2 + 0; + mosi_index = j * 2 + 1; + const PinName possible_clk = _form_factor.get(clk_index); + const PinName possible_mosi = _form_factor.get(mosi_index); + + // Check if the control pair is allowed and skip if it is not + if (!more_restricted.has_pin(possible_clk) && !more_restricted.has_pin(possible_mosi)) { + clk = possible_clk; + mosi = possible_mosi; + break; + } + } + + if ((clk == NC) && (mosi == NC)) { + set_control_pins_auto(); + return false; + } + + // Find aux pin + int aux_index = 0; + PinName aux = NC; + more_restricted.add(clk); + more_restricted.add(mosi); + for (uint32_t j = 0; j < _form_factor.count(); j++) { + aux_index = j; + const PinName possible_aux = _form_factor.get(aux_index); + + // Check if the control pair is allowed and skip if it is not + if (!more_restricted.has_pin(possible_aux)) { + aux = possible_aux; + break; + } + } + if (aux == NC) { + set_control_pins_auto(); + return false; + } + + + mbed_tester_printf("Testing clk_index=%2i, mosi_index=%2i, miso_index=%2i, aux_index=%2i\r\n", clk_index, mosi_index, miso_index, aux_index); + set_control_pins_manual(clk, mosi, miso, aux); + if (!self_test_control_current()) { + mbed_tester_printf(" Fail\r\n"); + set_control_pins_auto(); + return false; + } + mbed_tester_printf(" Pass\r\n"); + } + set_control_pins_auto(); + return true; +} + +bool MbedTester::self_test_control_current() +{ + uint8_t buf[4]; + + read(TESTER_CONTROL, buf, sizeof(buf)); + + return memcmp(buf, "mbed", sizeof(buf)) == 0; +} + +bool MbedTester::_find_control_indexes(PhysicalIndex &clk_out, PhysicalIndex &mosi_out, PhysicalIndex &miso_out, PhysicalIndex &aux_out) +{ + MbedTesterBitMap allowed; + const size_t max_pins = _form_factor.count(); + const size_t max_controls = max_pins / 2; + + for (size_t i = 0; i < max_pins; i++) { + PinName pin = _form_factor.get(i); + if ((pin == NC) || _exclude_pins.has_pin(pin)) { + // Skip these pins + continue; + } + + // Pin is allowed + allowed.set(i); + } + for (size_t i = 0; i < LogicalPinTotal; i++) { + PhysicalIndex index = _mapping[i]; + if (index < PHYSICAL_PINS) { + allowed.clear(index); + } + } + + for (size_t i = 0; i < max_controls; i++) { + uint8_t clk_index = i * 2 + 0; + uint8_t mosi_index = i * 2 + 1; + if (!allowed.get(clk_index) || !allowed.get(mosi_index)) { + continue; + } + + // Free CLK and MOSI pins found. Mark them as unavailable + allowed.clear(clk_index); + allowed.clear(mosi_index); + DigitalInOut clk(_form_factor.get(clk_index), PIN_OUTPUT, ::PullNone, 0); + DigitalInOut mosi(_form_factor.get(mosi_index), PIN_OUTPUT, ::PullNone, 0); + + for (uint8_t miso_index = 0; miso_index < max_pins; miso_index++) { + if (!allowed.get(miso_index)) { + continue; + } + + DigitalInOut miso(_form_factor.get(miso_index)); + if (!mbed_tester_test(&clk, &mosi, &miso, miso_index)) { + // Pin doesn't work + continue; + } + + // MISO found so find AUX starting from where miso left off + for (uint8_t aux_index = miso_index + 1; aux_index < max_pins; aux_index++) { + if (!allowed.get(aux_index)) { + continue; + } + + DigitalInOut aux(_form_factor.get(aux_index)); + if (!mbed_tester_test(&clk, &mosi, &aux, aux_index)) { + // Pin doesn't work + continue; + } + + // Found all 4 pins + clk_out = clk_index; + mosi_out = mosi_index; + miso_out = miso_index; + aux_out = aux_index; + clk.input(); + mosi.input(); + return true; + } + + // A valid control channel was found but the system + // does not have enough working pins. + clk.input(); + mosi.input(); + return false; + } + + // Set CLK and MOSI pins don't work so set them back to available + clk.input(); + mosi.input(); + allowed.set(clk_index); + allowed.set(mosi_index); + } + return false; +} + +void MbedTester::_setup_control_pins() +{ + _clk = new DigitalInOut(_form_factor.get(_clk_index), PIN_OUTPUT, ::PullNone, 0); + _mosi = new DigitalInOut(_form_factor.get(_mosi_index), PIN_OUTPUT, ::PullNone, 0); + _miso = new DigitalInOut(_form_factor.get(_miso_index)); + _aux = new DigitalInOut(_form_factor.get(_aux_index)); +} + +void MbedTester::_free_control_pins() +{ + if (_clk) { + _clk->input(); + delete _clk; + } + _clk = NULL; + _clk_index = PHYSICAL_NC; + if (_mosi) { + _mosi->input(); + delete _mosi; + } + _mosi = NULL; + _mosi_index = PHYSICAL_NC; + if (_miso) { + _miso->input(); + delete _miso; + } + _miso = NULL; + _miso_index = PHYSICAL_NC; + if (_aux) { + _aux->input(); + delete _aux; + } + _aux = NULL; + _aux_index = PHYSICAL_NC; + _control_valid = false; +} + +void MbedTester::_update_control_pins() +{ + if (_update_needed()) { + + if (!_control_auto) { + error("Invalid control channel configuration"); + } + + _free_control_pins(); + if (_find_control_indexes(_clk_index, _mosi_index, _miso_index, _aux_index)) { + mbed_tester_printf("Updating control pins to clk=%i, mosi=%i, miso=%i, aux=%i\r\n", _clk_index, _mosi_index, _miso_index, _aux_index); + _setup_control_pins(); + _control_valid = true; + return; + } else { + error("An MbedTester communication channel could not be created"); + } + } +} + +bool MbedTester::_update_needed() +{ + if (!_control_valid) { + return true; + } + // Note - mappings beyond the the first two logical banks are allowed to overlap + // with the control channels. These mappings are used for firmware updates and + // IO expander control. + for (size_t i = 0; i < LogicalPinTotal; i++) { + PhysicalIndex pin = _mapping[i]; + if ((pin == _clk_index) || (pin == _mosi_index) || (pin == _miso_index) || (pin == _aux_index)) { + return true; + } + } + return false; +} + +void MbedTester::_reset() +{ + for (uint32_t i = 0; i < sizeof(_mapping) / sizeof(_mapping[0]); i++) { + _mapping[i] = PHYSICAL_NC; + } + _free_control_pins(); + _control_auto = true; +} diff --git a/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/MbedTester.h b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/MbedTester.h new file mode 100644 index 0000000000..402884cb8e --- /dev/null +++ b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/MbedTester.h @@ -0,0 +1,878 @@ +/* + * 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. + */ + +#ifndef MBED_TESTER_H +#define MBED_TESTER_H + +#include "mbed.h" +#include "DynamicPinList.h" + +class MbedTester { +public: + + /* + * This type represents the index of a physical pin on the FPGA. + * This can be any value from 0 to 127. A form factor is used to + * map a PinName to a physical pin index. + */ + typedef uint8_t PhysicalIndex; + + /* + * There are 8 logical pins connected to the peripherals inside the FPGA. + * These are logical pins 0 through 7. All peripherals can read from these + * pins and the currently active peripheral can output on these pins. + * + * Each logical pin has a fixed function for a given peripheral which is + * why the same value is defined multiple times. For example logical pin + * 2 is SPI clock (SPISclk) when SPI is the active peripheral and + * UART clear to send (UARTCts) when UART is the active peripheral. + * + * A logic pin can be mapped to any physical pin (PinName type) by calling + * the function MbedTester::pin_map_set. + */ + enum LogicalPin { + + LogicalPinGPIO0 = 0, + LogicalPinGPIO1 = 1, + LogicalPinGPIO2 = 2, + LogicalPinGPIO3 = 3, + LogicalPinGPIO4 = 4, + LogicalPinGPIO5 = 5, + LogicalPinGPIO6 = 6, + LogicalPinGPIO7 = 7, + + LogicalPinSPIMosi = 0, + LogicalPinSPIMiso = 1, + LogicalPinSPISclk = 2, + LogicalPinSPISsel = 3, + + LogicalPinIOMetrics0 = 0, + LogicalPinIOMetrics1 = 1, + LogicalPinIOMetrics2 = 2, + LogicalPinIOMetrics3 = 3, + LogicalPinIOMetrics4 = 4, + LogicalPinIOMetrics5 = 5, + LogicalPinIOMetrics6 = 6, + LogicalPinIOMetrics7 = 7, + + LogicalPinUARTTx = 0, + LogicalPinUARTRx = 1, + LogicalPinUARTCts = 2, + LogicalPinUARTRts = 3, + + LogicalPinI2CSda = 0, + LogicalPinI2CScl = 1, + + LogicalPinCount = 8, + LogicalPinBanks = 2, + LogicalPinTotal = LogicalPinCount * LogicalPinBanks + }; + + /* + * These are the peripherals internal to the FPGA. A peripheral can be + * selected by calling MbedTester::select_peripheral. + */ + enum Peripheral { + PeripheralGPIO = 1, + PeripheralSPI = 2, + PeripheralUART = 4, + PeripheralI2C = 5, + PeripheralSPISlave = 6 + }; + + /** + * Construct a new MbedTester object + * + * The form factor pins passed into this class must match the + * physical MbedTester shield connected to this board or testing + * will not work correctly. The order of pins in this list must + * match the order of the shield. + * + * The exclude pins list should be used to exclude pins which you either + * don't want to be tested or are not suitable for use as a control + * channel. This list is allowed to contain pins that are not in the + * form factor. + * + * @param form_factor The pins that are available on this form factor + * @param exclude_pins The pins that must not be used + */ + MbedTester(const PinList *form_factor, const PinList *exclude_pins); + + /** + * Destroy and cleanup resources associated with this object + */ + ~MbedTester(); + + /** + * Enable automatic selection and update of control pins + * + * Calling this function configures MbedTester to automatically select + * and update the control pins. The control pins are moved if the + * function MbedTester::pin_map_set is called and maps a pin that is being used + * for control. + * + * @note Automatic selection and update of control pins is the default. + * Unless MbedTester::set_control_pins_manual has been called to manually + * set the control pins this function has no effect. + */ + void set_control_pins_auto(); + + /** + * Set the control pins to use for communication + * + * Manually set the control pins. Calling this function + * disables automatic control pin selection and updates. + * The function MbedTester::pin_map_set must not be used to map over + * control pins when in this mode. + * + * @param clk Clock pin to use as the control channel + * @param mosi Mosi pin to use as the control channel + * @param miso Miso pin to use as the control channel + * @param aux Auxillary pin to use as the control cannel + */ + void set_control_pins_manual(PinName clk, PinName mosi, PinName miso, PinName aux); + + /** + * Read FPGA CI Test Shield firmware + * + * Read the firmware on the FPGA CI Test Shield. An optional progress callback + * can be supplied to display progress while the firmware is being read. + * + * @param dest File to write the firmware to. This file must have been opened as writeable + * @param progress Optional progress callback called when the percent complete changes + * @return true if firmware was successfully read, false otherwise + */ + bool firmware_dump(FileHandle *dest, Callback progress = Callback()); + + /** + * Read FPGA CI Test Shield flash + * + * Read the entire flash contents of the FPGA CI Test Shield. An optional progress callback + * can be supplied to display progress while the firmware is being read. + * + * @param dest File to write the firmware to. This file must have been opened as writeable + * @param progress Optional progress callback called when the percent complete changes + * @return true if firmware was successfully read, false otherwise + */ + bool firmware_dump_all(FileHandle *dest, Callback progress = Callback()); + + /** + * Program new FPGA CI Test Shield firmware + * + * Program firmware from the file given. The binary bitstream must be in the correct format + * and contain a correct CRC or the update will fail. To correctly format a bitstream binary + * the post_process_bitstream.py script in the fpga-ci-test-shield repository should be used. + * + * Note - release binaries for the FPGA CI Test Shield have already been formatted and + * can be loaded directly with this function + * + * @param src File containing the new firmware to program + * @param progress Optional progress callback called when the percent complete changes + * @return true if firmware was successfully applied, false otherwise + */ + bool firmware_update(FileHandle *src, Callback progress = Callback()); + + /** + * Map a physical pin to the given logical pin + * + * This function will automatically move the control channel + * pins to avoid interfering with the mapped pin. + * + * @param physical Physical pin on the board + * @param logical Logical pin to map to + */ + void pin_map_set(PinName physical, LogicalPin logical); + + /** + * Reset all pin mappings + * + * After this call all pins will be unmapped + */ + void pin_map_reset(); + + /** + * Reset all peripherals + * + * This does not reset the pin mappings + */ + void peripherals_reset(); + + /** + * Reset everything + * + * This function resets the state of both the FPGA CI Test Shield + * and the MbedTester object itself. + * + * Reset effects on the FPGA CI Test Shield include: + * - All pins are tristated + * - All pin mappings are reset + * - All pullup/pulldown settings are reset + * - All peripherals are reset + * + * Reset effects on the MbedTester object include + * - Control channels tristated and freed + * - Control channel selection set to automatic + * - Most internal state reinitialized + */ + void reset(); + + /** + * Reprogram the FPGA + * + * This function causes the FPGA to reboot and reload RAM contents. + * This should be used after MbedTester::firmware_update to load the + * new image. + */ + void reprogram(); + + /** + * Get the running FPGA firmware version + * + * @return The version of firmware running on the FPGA. + * + */ + uint32_t version(); + + /** + * Select the currently active peripheral + * + * @param peripheral Active peripheral + */ + void select_peripheral(Peripheral peripheral); + + /* **GPIO peripheral functions** */ + + /** + * Read a gpio pin + * + * @param gpio Logical pin to read from + * @return 1 if the pin is high 0 if the pin is low + */ + int gpio_read(LogicalPin gpio); + + /** + * Set value and drive of a gpio pin + * + * @param gpio Logical pin to write to + * @param value 0 to set the pin low or non-zero to set it high + * @param driver 0 to set the pin to Hi-Z or non-zero to drive the pin + */ + void gpio_write(LogicalPin gpio, int value, bool drive); + + /* **IO Metrics functions** */ + + /** + * Start recording metrics on all logical pins + * + * This function resets all past metrics to 0. To + * preserve these call io_metrics_continue instead. + */ + void io_metrics_start(); + + /** + * Stop recording metrics on all logical pins + * + * This function should be called before any metrics + * are read to ensure the value does not change while + * they are being read. + */ + void io_metrics_stop(); + + /** + * Continue recording metrics on all logical pins + * + * Resume recording metrics. + */ + void io_metrics_continue(); + + /** + * Get the shortest low pulse recorded + * + * @param pin Pin to read the metrics for + * @return The shortest number of 100MHz clock cycles the pin was low + */ + uint32_t io_metrics_min_pulse_low(LogicalPin pin); + + /** + * Get the shortest high pulse recorded + * + * @param pin Pin to read the metrics for + * @return The shortest number of 100MHz clock cycles the pin was high + */ + uint32_t io_metrics_min_pulse_high(LogicalPin pin); + + /** + * Get the longest low pulse recorded + * + * @param pin Pin to read the metrics for + * @return The longest number of 100MHz clock cycles the pin was low + */ + uint32_t io_metrics_max_pulse_low(LogicalPin pin); + + /** + * Get the longest high pulse recorded + * + * @param pin Pin to read the metrics for + * @return The longest number of 100MHz clock cycles the pin was high + */ + uint32_t io_metrics_max_pulse_high(LogicalPin pin); + + /** + * Get the number of rising edges + * + * @param pin Pin to read the metrics for + * @return The number of rising edges + */ + uint32_t io_metrics_rising_edges(LogicalPin pin); + + /** + * Get the number of falling edges + * + * @param pin Pin to read the metrics for + * @return The number of falling edges + */ + uint32_t io_metrics_falling_edges(LogicalPin pin); + + /** + * Reset the IO expander modules + * + */ + void pin_pull_reset_all(); + + /** + * FPGA Pullup mode + */ + enum PullMode { + PullUp, + PullDown, + PullNone + }; + + /** + * Configure an Mbed pin for a pulldown resistor, pullup resistor, or tristate mode via PinName + * + * @param pin Mbed pin whose mode is being set + * @param mode (MbedTester::PullUp, MbedTester::PullDown, or MbedTester::PullNone) + * @return 0 on success, nonzero on failure + */ + int pin_set_pull(PinName pin, PullMode mode); + + /** + * Configure an Mbed pin for a pulldown resistor, pullup resistor, or tristate mode via pin index + * + * @param index Mbed pin index whose mode is being set + * @param mode (MbedTester::PullUp, MbedTester::PullDown, or MbedTester::PullNone) + * @return 0 on success, nonzero on failure + */ + int pin_set_pull_index(int index, PullMode mode); + + /* + * Register of the IO expander + */ + enum IOExpanderReg { + RegInput, + RegOutput, + RegConfig + }; + + /** + * Read a bit for a specific Mbed pin that is set in the input, output, or configuration registers inside of the IO expander via PinName + * + * @param pin Mbed pin whose register bit is being read + * @param reg_type Pin register to access, options are: MbedTester::RegInput, MbedTester::RegOutput, or MbedTester::RegConfig + * @return The value of the bit read + */ + uint8_t io_expander_read(PinName pin, IOExpanderReg reg_type); + + /** + * Read a bit for a specific Mbed pin that is set in the input, output, or configuration registers inside of the IO expander via pin index + * + * @param index Mbed pin index whose register bit is being read + * @param reg_type Pin register to access, options are: MbedTester::RegInput, MbedTester::RegOutput, or MbedTester::RegConfig + * @return The value of the bit read + */ + uint8_t io_expander_read_index(int index, IOExpanderReg reg_type); + + /** + * Configure an Mbed pin for a pulldown resistor, pullup resistor, or tristate mode + * (this version of the function uses io_expander_i2c_read_bb and io_expander_i2c_write_bb) + * + * @param pin Mbed pin whose mode is being set + * @param mode (MbedTester::PullUp, MbedTester::PullDown, or MbedTester::PullNone) + * @return 0 on success, nonzero on failure + */ + int pin_set_pull_bb(PinName pin, PullMode mode); + + /** + * Read a bit for a specific Mbed pin that is set in the input, output, or configuration registers inside of the IO expander + * (this version of the function uses io_expander_i2c_read_bb) + * + * @param pin Mbed pin whose register bit is being read + * @param reg_type Pin register to access, options are: MbedTester::RegInput, MbedTester::RegOutput, or MbedTester::RegConfig + * @return The value of the bit read + */ + uint8_t io_expander_read_bb(PinName pin, IOExpanderReg reg_type); + + /** + * Create an analog voltage via the FPGA sys pwm in order to test Mbed AnalogIn + * + * @param enable Enable the FPGA system PWM (false: of, true: on) + * @param voltage The analog voltage that will be created by the FPGA CI test shield (float: 0.0 to 1.0) + */ + void set_analog_out(bool enable, float voltage); + + /** + * Turn the FPGA ADC on and off (power management data will be collected while the ADC is on) + * + * @param val FPGA ADC enable bit (false: off, true: on) + */ + void set_sample_adc(bool val); + + /** + * Get the result of the analog to digital conversion computed on the FPGA in the form of a voltage reading. The FPGA ADC operates on 0V-1V, which means this function will only ever return a float ranging from 0.0-1.0. + * + * @return The conversion result in the form of a voltage measurement for AnalogMuxIn, Eg. a return value of 0.7 means the ADC on the FPGA read 0.7 volts on its analog input + */ + float get_analog_in(); + + /** + * Similar to 'get_analog_in' but returns a voltage reading from ANIN0-3 + * + * @param index ANIN pin to read (0-3) + * @return The conversion result in the form of a voltage measurement for ANIN0-3 + */ + float get_anin_voltage(int index); + + /* **Mid level system access** */ + + /* + * These are the pins on the FPGA that are not connected to the Mbed board. + * These can be controlled by using MbedTester::sys_pin_read and MbedTester::sys_pin_write. + * These should not be used directly when using the FPGA CI Test Shield. The higher layer + * APIs should be used instead. + */ + enum SystemPin { + Reset, + Reprogram, + + DigitalID0, + DigitalID1, + DigitalID2, + + Led0, + Led1, + Led2, + Led3, + + SPIIO0, + SPIIO1, + SPIIO2, + SPIIO3, + SPIClk, + SPICS, + + I2CReset, + I2CSda0, + I2CSda1, + I2CSda2, + I2CScl0, + I2CScl1, + I2CScl2, + + AnalogMuxEnable, + AnalogMuxPwmOut, + AnalogMuxIn, + AnalogMuxAddr0, + AnalogMuxAddr1, + AnalogMuxAddr2, + AnalogMuxAddr3, + AnalogMuxAddr4, + AnalogMuxAddr5, + AnalogMuxAddr6, + AnalogMuxAddr7, + + AnalogInP0, + AnalogInP1, + AnalogInP2, + AnalogInP3, + AnalogInN0, + AnalogInN1, + AnalogInN2, + AnalogInN3, + + SystemPinCount + }; + + /** + * Read from the given system pin + * + * @param pin The pin to read from + * @return true if 1 was read, false if 0 + */ + bool sys_pin_read(SystemPin pin); + + /** + * Write to the given system pin + * + * @param pin The pin to write to + * @param value The value to output on the pin when driven + * @param true to drive the output, false to set the output high-z + * @return true if 1 was read, false if 0 + */ + void sys_pin_write(SystemPin pin, int value, bool drive); + + /** + * I2C read on I2C system channels 0, 1, or 2 + * + * @param i2c_index The number corresponding to the system i2c bus being used (0, 1, or 2) + * @param dev_addr The I2C address of the device being read from + * @param start_reg The internal device address where the read will start + * @param data Data buffer for data to be read into + * @param length The number of bytes to read + * @return 0 on success (ACK), nonzero on failure (NACK) + */ + int io_expander_i2c_read(uint8_t i2c_index, uint8_t dev_addr, uint8_t start_reg, uint8_t *data, int length); + + /** + * I2C write on I2C system channels 0, 1, or 2 + * + * @param i2c_index The number corresponding to the system i2c bus being used (0, 1, or 2) + * @param dev_addr The I2C address of the device being written to + * @param data The data to be written + * @param length The number of bytes to be written + * @return 0 on success (ACK), nonzero on failure (NACK) + */ + int io_expander_i2c_write(uint8_t i2c_index, uint8_t dev_addr, uint8_t *data, int length); + + /** + * I2C read on I2C system channels 0, 1, or 2 + * (bit banged version of function, bit banged over control channel) + * + * @param sda System pin used for sda + * @param scl System pin used for scl + * @param dev_addr The I2C address of the device being read from + * @param start_reg The internal device address where the read will start + * @param data Data buffer for data to be read into + * @param length The number of bytes to read + * @return 0 on success (ACK), nonzero on failure (NACK) + */ + int io_expander_i2c_read_bb(SystemPin sda, SystemPin scl, uint8_t dev_addr, uint8_t start_reg, uint8_t *data, int length); + + /** + * I2C write on I2C system channels 0, 1, or 2 + * (bit banged version of function, bit banged over control channel) + * + * @param sda System pin used for sda + * @param scl System pin used for scl + * @param dev_addr The I2C address of the device being written to + * @param data The data to be written + * @param length The number of bytes to be written + * @return 0 on success (ACK), nonzero on failure (NACK) + */ + int io_expander_i2c_write_bb(SystemPin sda, SystemPin scl, uint8_t dev_addr, uint8_t *data, int length); + + /** + * Set the AnalogMuxAddr pins on the FPGA via PinName + * + * @param pin The Mbed pin that the analog signal will be routed to + * @return 0 on success, nonzero on failure + */ + int set_mux_addr(PinName pin); + + /** + * Set the AnalogMuxAddr pins on the FPGA via pin index + * + * @param index The Mbed pin index that the analog signal will be routed to + * @return 0 on success, nonzero on failure + */ + int set_mux_addr_index(int index); + + /* **AnalogIn peripheral functions** */ + + /** + * Turn on/off the analog muxes + * + * @param val false: off, true: on + */ + void set_mux_enable(bool val); + + /** + * Turn on/off pwm output on FPGA to test Mbed AnalogIn + * + * @param val false: off, true: on + */ + void set_pwm_enable(bool val); + + /** + * Check if FPGA pwm out is on or off + * + * @return FPGA enable bit (false: off, true: on) + */ + bool get_pwm_enable(); + + /** + * Set the pwm output period and number of cycles high (duty cycle) on the FPGA + * + * @param period In units of clk cycles + * @param cycles_high In units of clk cycles + */ + void set_pwm_period_and_cycles_high(uint32_t period, uint32_t cycles_high); + + /** + * Get the pwm output period of the FPGA + * + * @return FPGA pwm output period in units of clk cycles + */ + uint32_t get_pwm_period(); + + /** + * Get the number of cycles that are high (duty cycle) from FPGA pwm + * + * @return FPGA pwm output cycles high + */ + uint8_t get_pwm_cycles_high(); + + /** + * Get the 12-bit analog to digital conversion result from the FPGA + * + * @return 12-bit FPGA ADC result for AnalogMuxIn + */ + uint16_t get_analogmuxin_measurement(); + + /** + * Similar to 'get_analogmuxin_measurement' but returns the current XADC measurement for ANIN0-3 + * + * @param index ANIN pin to read (0-3) + * @return 12-bit FPGA ADC result for ANIN0-3 + */ + uint16_t get_anin_measurement(int index); + + /** + * Gets (by reference) the sum of all ANIN ADC results for any of the 4 ANIN pins specified by the index, + * number of ADC sample sequences that have completed since the XADC was turned on, and the number of FPGA clk cycles + * that have taken place since the ADC was turned on. + * + * @param index ANIN pin of which to get sum of results (0-3) + * @param sum The sum of all specified ANIN pin's ADC results + * @param samples The number of ADC sample sequences that have completed since the XADC was turned on + * @param cycles The number of FPGA clk cycles that have taken place since the ADC was turned on + */ + void get_anin_sum_samples_cycles(int index, uint64_t *sum, uint32_t *samples, uint64_t *cycles); + + /** + * Allows safe reading of FPGA ADC related values while the FPGA ADC is on + * If snapshot is set then the ADC values will be safely latched in the FPGA and safe to read. + * The RTL will set snapshot to 0 after 1 clk cycle. + */ + void set_snapshot(); + + /** + * Set the current system pin mode to disabled + * + * This releases any pin mappings that were set by the previous pin mode. + */ + void sys_pin_mode_disabled(); + + /** + * Set the current system pin mode to serial flash + * + * Remap physical pins to the serial flash the FPGA boots from. + * This is used for firmware updates. + * + * @param mosi The physical pin index to connect to serial flash mosi + * @param miso The physical pin index to connect to serial flash miso + * @param clk The physical pin index to connect to serial flash clk + * @param ssel The physical pin index to connect to serial flash cs + */ + void sys_pin_mode_spi_serial_flash(PhysicalIndex mosi, PhysicalIndex miso, PhysicalIndex clk, PhysicalIndex ssel); + + /** + * Set the current system pin mode to io expander I2C bus + * + * Remap physical pins to the io expander I2C bus. The IO expanders + * are used for setting pullups and pulldowns. + * + * @param index The index of the I2C bus to connect to + * @param sda_in Physical pin index for the FPGA to output the state of SDA on + * @param sda_val Physical pin index for the FPGA to read SDA from. When in this mode the Mbed board + * must always drive this pin. Driving a 0 causes the FPGA to pull the SDA on the I2C bus low. Setting + * a 1 causes the FPGA to let SDA on the I2C bus float (and get pulled to 1). + * @param scl_in Physical pin index for the FPGA to output the state of SCL on + * @param scl_val Physical pin index for the FPGA to read SCL from. When in this mode the Mbed board + * must always drive this pin. Driving a 0 causes the FPGA to pull the SCL on the I2C bus low. Setting + * a 1 causes the FPGA to let SDA on the SCL bus float (and get pulled to 1). + */ + void sys_pin_mode_i2c_io_expander(int index, PhysicalIndex sda_in, PhysicalIndex sda_val, PhysicalIndex scl_in, PhysicalIndex scl_val); + + /** + * Map a physical pin index to the given logical pin + * + * This function will automatically move the control channel + * pins to avoid interfering with the mapped pin. The physical + * pin index does not need to be part of the form factor. + * + * @param physical_index Index of the physical pin on the board + * @param logical Logical pin to map to + */ + void pin_map_index(PhysicalIndex physical_index, LogicalPin logical); + + + /* **Low level memory access** */ + + /** + * Write to tester memory + * + * @addr addr Address to write to + * @param data Data to write + * @param size Number of bytes to write + */ + void write(uint32_t addr, const uint8_t *data, uint32_t size); + + /** + * Read from tester memory + * + * @addr addr Address to read from + * @param data Buffer to fill with data + * @param size Number of bytes to read + */ + void read(uint32_t addr, uint8_t *data, uint32_t size); + + /* **Self tests** */ + + /** + * Run all self tests + * + * @return true if all self tests pass, false otherwise + */ + bool self_test_all(); + + /** + * Test that all allowed control channels can be used + * + * Check that all pairs of clk and mosi which aren't in + * the restricted list can be used. + * + * @note CLK and MOSI lines are paired, where CLK is always + * on an even index and MOSI is always on an oddd index: + * clk_index_N = N * 2 + * mosi_index_N = N * 2 + 1 + * + * @note This functions sets the control pin management mode + * to automatic when it completes. + * + * @return true if all control channel pairs (clk and mosi) of this + * configuration can be used, false otherwise + */ + bool self_test_control_channels(); + + /** + * Test that all allowed control miso lines can be used + * + * Check that every pin of this form factor aside from the + * pins in the restricted list can be used as miso. + * + * @note This functions sets the control pin management mode + * to automatic when it completes. + * + * @return true if all control channel miso lines of this + * configuration can be used, false otherwise + */ + bool self_test_control_miso(); + + /** + * Test that the current control channel works + * + * @return true if communication is successful, false otherwise + */ + bool self_test_control_current(); + +private: + + /* + * Find suitable control pins + * + * This function finds the appropriate set of pins and test to ensure that communication with + * the FPGA can be performed over them. Before calling this function MbedTester pins must be + * freed by calling MbedTester::_free_control_pins. + * + * @param clk_out Clock pin to find and set + * @param mosi_out Mosi pin to find and set + * @param miso_out Miso pin to find and set + * @param aux_out Aux pin to find and set + * @return true if all pins were found, false otherwise + */ + bool _find_control_indexes(PhysicalIndex &clk_out, PhysicalIndex &mosi_out, PhysicalIndex &miso_out, PhysicalIndex &aux_out); + + /* + * Allocate control pins + * + * The pin indexes must already have been found + */ + void _setup_control_pins(); + + /* + * Free control pins + * + * This function is safe to call even if the pins have been freed + */ + void _free_control_pins(); + + /* + * Update the control channel if needed + * + * Open a control channel using allowed pins: + * - Pin must be in the form factor + * - Pin must not be in the exclude list + * - Pin must not be mapped already + */ + void _update_control_pins(); + + /* + * Check if this control channel needs to be updated + * + * @return true if the control channel needs to be updated, false otherwise + */ + bool _update_needed(); + + /* + * Reset the state of the MbedTester class + * + * This does not effect the state of the FPGA in any way. + */ + void _reset(); + + PhysicalIndex _mapping[LogicalPinCount * (LogicalPinBanks + 1)]; + DynamicPinList _form_factor; + DynamicPinList _exclude_pins; + bool _control_auto; + bool _control_valid; + PhysicalIndex _clk_index; + PhysicalIndex _mosi_index; + PhysicalIndex _miso_index; + PhysicalIndex _aux_index; + DigitalInOut *_clk; + DigitalInOut *_mosi; + DigitalInOut *_miso; + DigitalInOut *_aux; + /* + * Used to reset IO expander chips only once the first time + * any IO expander is accessed as well as during an io_exp_test + */ + int _init_io_exp_rst_flag; +}; + +#endif diff --git a/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPIMasterTester.cpp b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPIMasterTester.cpp new file mode 100644 index 0000000000..fd4062d91d --- /dev/null +++ b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPIMasterTester.cpp @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#include "SPIMasterTester.h" +#include "fpga_config.h" + +uint16_t SPIMasterTester::get_transfer_count() +{ + return SPITester::get_transfer_count(TESTER_SPI_MASTER_TRANSFERS, TESTER_SPI_MASTER_TRANSFERS_SIZE); +} + +uint32_t SPIMasterTester::get_receive_checksum() +{ + return SPITester::get_receive_checksum(TESTER_SPI_MASTER_TO_SLAVE_CHECKSUM, TESTER_SPI_MASTER_TO_SLAVE_CHECKSUM_SIZE); +} + +void SPIMasterTester::set_mode(SPITester::SpiMode mode) +{ + SPITester::set_mode(mode, TESTER_SPI_MASTER_CTRL, TESTER_SPI_MASTER_CTRL_SIZE, TESTER_SPI_MASTER_CLK_MODE_OFFSET, TESTER_SPI_MASTER_CLK_MODE_SIZE); +} + +void SPIMasterTester::set_bit_order(SPITester::SpiBitOrder bit_order) +{ + SPITester::set_bit_order(bit_order, TESTER_SPI_MASTER_CTRL, TESTER_SPI_MASTER_CTRL_SIZE, TESTER_SPI_MASTER_BIT_ORDER_OFFSET, TESTER_SPI_MASTER_BIT_ORDER_SIZE); +} + +void SPIMasterTester::set_sym_size(uint32_t sym_size) +{ + SPITester::set_sym_size(sym_size, TESTER_SPI_MASTER_CTRL, TESTER_SPI_MASTER_CTRL_SIZE, TESTER_SPI_MASTER_SYM_SIZE_OFFSET, TESTER_SPI_MASTER_SYM_SIZE_SIZE); +} + +void SPIMasterTester::set_duplex_mode(SPITester::SpiDuplex duplex) +{ + SPITester::set_duplex_mode(duplex, TESTER_SPI_MASTER_CTRL, TESTER_SPI_MASTER_CTRL_SIZE, TESTER_SPI_MASTER_DUPLEX_OFFSET, TESTER_SPI_MASTER_DUPLEX_SIZE); +} + +void SPIMasterTester::set_hd_tx_rx_cnt(uint16_t tx_cnt, uint16_t rx_cnt) +{ + SPITester::set_hd_tx_rx_cnt(tx_cnt, rx_cnt, TESTER_SPI_MASTER_HD_RX_CNT, TESTER_SPI_MASTER_HD_RX_CNT_SIZE, TESTER_SPI_MASTER_HD_TX_CNT, TESTER_SPI_MASTER_HD_TX_CNT_SIZE); +} + +uint32_t SPIMasterTester::get_cs_to_first_clk_edge_ns() +{ + uint32_t delay_ns; + read(TESTER_SPI_MASTER_CS_TO_FIRST_SCLK_CNT, (uint8_t*)&delay_ns, TESTER_SPI_MASTER_CS_TO_FIRST_SCLK_CNT_SIZE); + + return (delay_ns * 10); +} + +uint32_t SPIMasterTester::get_last_clk_edge_to_cs_ns() +{ + uint32_t delay_ns; + read(TESTER_SPI_MASTER_LAST_SCLK_TO_CS_CNT, (uint8_t*)&delay_ns, TESTER_SPI_MASTER_LAST_SCLK_TO_CS_CNT_SIZE); + + return (delay_ns * 10); +} diff --git a/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPIMasterTester.h b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPIMasterTester.h new file mode 100644 index 0000000000..a1b73b687c --- /dev/null +++ b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPIMasterTester.h @@ -0,0 +1,109 @@ +/* + * 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. + */ + +#ifndef SPI_MASTER_TESTER_H +#define SPI_MASTER_TESTER_H + +#include "SPITester.h" + +class SPIMasterTester: public SPITester { +public: + + SPIMasterTester(const PinList *form_factor, const PinList *exclude_pins) + : SPITester(form_factor, exclude_pins) + { + + } + + /** + * Read the number of transfers which have occurred + * + * @return The number of SPI transfers that have completed since + * spi was reset. + */ + uint16_t get_transfer_count(); + + /** + * Read a checksum of data send to the tester + * + * @return The sum of all bytes sent to the tester since reset. + */ + uint32_t get_receive_checksum(); + + /** + * Set the clock mode of the spi_slave module. + * + * @param mode Spi clock mode + */ + void set_mode(SpiMode mode); + + /** + * Set bit order durring transmission of the spi_slave module. + * + * @param mode Spi clock mode + */ + void set_bit_order(SpiBitOrder bit_order); + + /** + * Set symbol size used durring transmission of the spi_slave module. + * + * @param mode Spi clock mode + */ + void set_sym_size(uint32_t sym_size); + + /** + * Set full-duplex/half-duplex transmission mode of the spi_slave module. + * + * @param duplex duplex mode used for the transmission + */ + void set_duplex_mode(SpiDuplex duplex); + + /** + * Set tx/rx symbol count. + * + * @tx_cnt TX symbol count + * @rx_cnt RX symbol count + * + * @note Required only in Half-Duplex mode. + */ + void set_hd_tx_rx_cnt(uint16_t tx_cnt, uint16_t rx_cnt); + + /** + * Get nano seconds between chip select assertion and the first spi clock edge. + * + * @return nano seconds between chip select assertion and the first spi clock edge. + * + * @note Number of nano seconds is calculated based of number of counted clk changes and + * clk frequency (100 MHz => 1 clk tick corresponds to 10 ns). + * Accuracy of the returned value is +/- 10 ns. + */ + uint32_t get_cs_to_first_clk_edge_ns(); + + /** + * Get nano seconds between last spi clock edge and chip select de-assertion. + * + * @return nano seconds between last spi clock edge and chip select de-assertion. + * + * @note Number of nano seconds is calculated based of number of counted clk changes and + * clk frequency (100 MHz => 1 clk tick corresponds to 10 ns). + * Accuracy of the returned value is +/- 10 ns. + */ + uint32_t get_last_clk_edge_to_cs_ns(); + +}; + +#endif diff --git a/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPISlaveTester.cpp b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPISlaveTester.cpp new file mode 100644 index 0000000000..ae579697cd --- /dev/null +++ b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPISlaveTester.cpp @@ -0,0 +1,84 @@ +/* + * 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. + */ + +#include "SPISlaveTester.h" +#include "fpga_config.h" + +uint16_t SPISlaveTester::get_transfer_count() +{ + return SPITester::get_transfer_count(TESTER_SPI_SLAVE_TRANSFERS, TESTER_SPI_SLAVE_TRANSFERS_SIZE); +} + +uint32_t SPISlaveTester::get_receive_checksum() +{ + return SPITester::get_receive_checksum(TESTER_SPI_SLAVE_TO_MASTER_CHECKSUM, TESTER_SPI_SLAVE_TO_MASTER_CHECKSUM_SIZE); +} + +void SPISlaveTester::set_mode(SPITester::SpiMode mode) +{ + SPITester::set_mode(mode, TESTER_SPI_SLAVE_CTRL, TESTER_SPI_SLAVE_CTRL_SIZE, TESTER_SPI_SLAVE_CLK_MODE_OFFSET, TESTER_SPI_SLAVE_CLK_MODE_SIZE); +} + +void SPISlaveTester::set_bit_order(SPITester::SpiBitOrder bit_order) +{ + SPITester::set_bit_order(bit_order, TESTER_SPI_SLAVE_CTRL, TESTER_SPI_SLAVE_CTRL_SIZE, TESTER_SPI_SLAVE_BIT_ORDER_OFFSET, TESTER_SPI_SLAVE_BIT_ORDER_SIZE); +} + +void SPISlaveTester::set_sym_size(uint32_t sym_size) +{ + SPITester::set_sym_size(sym_size, TESTER_SPI_SLAVE_CTRL, TESTER_SPI_SLAVE_CTRL_SIZE, TESTER_SPI_SLAVE_SYM_SIZE_OFFSET, TESTER_SPI_SLAVE_SYM_SIZE_SIZE); +} + +void SPISlaveTester::set_duplex_mode(SPITester::SpiDuplex duplex) +{ + SPITester::set_duplex_mode(duplex, TESTER_SPI_SLAVE_CTRL, TESTER_SPI_SLAVE_CTRL_SIZE, TESTER_SPI_SLAVE_DUPLEX_OFFSET, TESTER_SPI_SLAVE_DUPLEX_SIZE); +} + +void SPISlaveTester::set_hd_tx_rx_cnt(uint16_t tx_cnt, uint16_t rx_cnt) +{ + SPITester::set_hd_tx_rx_cnt(tx_cnt, rx_cnt, TESTER_SPI_SLAVE_HD_RX_CNT, TESTER_SPI_SLAVE_HD_RX_CNT_SIZE, TESTER_SPI_SLAVE_HD_TX_CNT, TESTER_SPI_SLAVE_HD_TX_CNT_SIZE); +} + +void SPISlaveTester::set_spi_master_freq(uint16_t clk_div) +{ + write(TESTER_SPI_SLAVE_CLK_DIV, (uint8_t*)&clk_div, TESTER_SPI_SLAVE_CLK_DIV_SIZE); +} + +void SPISlaveTester::set_num_of_symbols(uint16_t num_of_sym) +{ + write(TESTER_SPI_SLAVE_NUM_OF_SYMBOLS, (uint8_t*)&num_of_sym, TESTER_SPI_SLAVE_NUM_OF_SYMBOLS_SIZE); +} + +void SPISlaveTester::set_start_delay_us(uint8_t start_delay_us) +{ + write(TESTER_SPI_SLAVE_START_DELAY_US, (uint8_t*)&start_delay_us, TESTER_SPI_SLAVE_START_DELAY_US_SIZE); +} + +void SPISlaveTester::set_sym_delay_ns(uint16_t sym_delay_ns) +{ + const uint16_t sym_delay_ticks = (sym_delay_ns + 9) / 10; // round up + write(TESTER_SPI_SLAVE_SYM_DELAY_TICKS, (uint8_t*)&sym_delay_ticks, TESTER_SPI_SLAVE_SYM_DELAY_TICKS_SIZE); +} + +void SPISlaveTester::start_transfer() +{ + uint32_t spi_ctrl = 0; + read(TESTER_SPI_SLAVE_CTRL, (uint8_t*)&spi_ctrl, TESTER_SPI_SLAVE_CTRL_SIZE); + spi_ctrl &= ~(((1 << TESTER_SPI_SLAVE_START_REQUEST_SIZE) - 1) << TESTER_SPI_SLAVE_START_REQUEST_OFFSET); + spi_ctrl |= (1 << TESTER_SPI_SLAVE_START_REQUEST_OFFSET); + write(TESTER_SPI_SLAVE_CTRL, (uint8_t*)&spi_ctrl, TESTER_SPI_SLAVE_CTRL_SIZE); +} diff --git a/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPISlaveTester.h b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPISlaveTester.h new file mode 100644 index 0000000000..5aa65a14d1 --- /dev/null +++ b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPISlaveTester.h @@ -0,0 +1,121 @@ +/* + * 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. + */ + +#ifndef SPI_SLAVE_TESTER_H +#define SPI_SLAVE_TESTER_H + +#include "SPITester.h" + +class SPISlaveTester: public SPITester { +public: + + SPISlaveTester(const PinList *form_factor, const PinList *exclude_pins) + : SPITester(form_factor, exclude_pins) + { + + } + + /** + * Read the number of transfers which have occurred + * + * @return The number of SPI transfers that have completed since + * spi was reset. + */ + uint16_t get_transfer_count(); + + /** + * Read a checksum of data sent to the tester + * + * @return The sum of all bytes sent to the tester since reset. + */ + uint32_t get_receive_checksum(); + + /** + * Set the clock mode of the spi_slave module. + * + * @param mode Spi clock mode + */ + void set_mode(SpiMode mode); + + /** + * Set bit order during transmission of the spi_slave module. + * + * @param mode Spi clock mode + */ + void set_bit_order(SpiBitOrder bit_order); + + /** + * Set symbol size used during transmission of the spi_slave module. + * + * @param mode Spi clock mode + */ + void set_sym_size(uint32_t sym_size); + + /** + * Set full-duplex/half-duplex transmission mode of the spi_slave module. + * + * @param duplex duplex mode used for the transmission + */ + void set_duplex_mode(SpiDuplex duplex); + + /** + * Set tx/rx symbol count. + * + * @tx_cnt TX symbol count + * @rx_cnt RX symbol count + * + * @note Required only in Half-Duplex mode. + */ + void set_hd_tx_rx_cnt(uint16_t tx_cnt, uint16_t rx_cnt); + + /** + * Set divisor to generate spi clock from FPGA base clock (100 MHz). + * + * @clk_div clock divisor. + */ + void set_spi_master_freq(uint16_t clk_div); + + /** + * Set number of symbols to be transmitted by spi master. + * + * @num_of_sym Number of symbols to be transmitted by spi master. + */ + void set_num_of_symbols(uint16_t num_of_sym); + + /** + * Set delay in us between start request and start of transmission. + * + * @start_delay_us Delay in us between start request and start of transmission. + */ + void set_start_delay_us(uint8_t start_delay_us); + + /** + * Set delay in ns between transmission of successive symbols. + * + * @sym_delay_ns Delay in ns between transmission of successive symbols. + */ + void set_sym_delay_ns(uint16_t sym_delay_ns); + + /** + * Request transmission start from FPGA master. + * + * @note Transmission will be started after the specified delay. + */ + void start_transfer(); +}; + +#endif diff --git a/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPITester.cpp b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPITester.cpp new file mode 100644 index 0000000000..34f024c5f1 --- /dev/null +++ b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPITester.cpp @@ -0,0 +1,77 @@ +/* + * 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. + */ + +#include "SPITester.h" +#include "fpga_config.h" + +uint16_t SPITester::get_transfer_count(uint32_t addr_transfers, uint32_t size_transfers) +{ + uint16_t transfers = 0; + MBED_ASSERT(sizeof(transfers) == size_transfers); + read(addr_transfers, (uint8_t *)&transfers, sizeof(transfers)); + return transfers; +} + +uint32_t SPITester::get_receive_checksum(uint32_t addr_checksum, uint32_t size_checksum) +{ + uint32_t checksum = 0; + MBED_ASSERT(sizeof(checksum) == size_checksum); + read(addr_checksum, (uint8_t *)&checksum, sizeof(checksum)); + return checksum; +} + +void SPITester::set_mode(SPITester::SpiMode mode, uint32_t addr_spi_ctrl, uint32_t size_spi_ctrl, uint32_t offset_clk_mode, uint32_t size_clk_mode) +{ + uint32_t spi_ctrl = 0; + read(addr_spi_ctrl, (uint8_t*)&spi_ctrl, size_spi_ctrl); + spi_ctrl &= ~(((1 << size_clk_mode) - 1) << offset_clk_mode); + spi_ctrl |= (mode << offset_clk_mode); + write(addr_spi_ctrl, (uint8_t*)&spi_ctrl, size_spi_ctrl); +} + +void SPITester::set_bit_order(SPITester::SpiBitOrder bit_order, uint32_t addr_spi_ctrl, uint32_t size_spi_ctrl, uint32_t offset_bit_order, uint32_t size_bit_order) +{ + uint32_t spi_ctrl = 0; + read(addr_spi_ctrl, (uint8_t*)&spi_ctrl, size_spi_ctrl); + spi_ctrl &= ~(((1 << size_bit_order) - 1) << offset_bit_order); + spi_ctrl |= (bit_order << offset_bit_order); + write(addr_spi_ctrl, (uint8_t*)&spi_ctrl, size_spi_ctrl); +} + +void SPITester::set_sym_size(uint32_t sym_size, uint32_t addr_spi_ctrl, uint32_t size_spi_ctrl, uint32_t offset_sym_size, uint32_t size_sym_size) +{ + uint32_t spi_ctrl = 0; + read(addr_spi_ctrl, (uint8_t*)&spi_ctrl, size_spi_ctrl); + spi_ctrl &= ~(((1 << size_sym_size) - 1) << offset_sym_size) ; + spi_ctrl |= (sym_size << offset_sym_size); + write(addr_spi_ctrl, (uint8_t*)&spi_ctrl, size_spi_ctrl); +} + +void SPITester::set_duplex_mode(SPITester::SpiDuplex duplex, uint32_t addr_spi_ctrl, uint32_t size_spi_ctrl, uint32_t offset_duplex, uint32_t size_duplex) +{ + uint32_t spi_ctrl = 0; + read(addr_spi_ctrl, (uint8_t*)&spi_ctrl, size_spi_ctrl); + spi_ctrl &= ~(((1 << size_duplex) - 1) << offset_duplex); + spi_ctrl |= (duplex << offset_duplex); + write(addr_spi_ctrl, (uint8_t*)&spi_ctrl, size_spi_ctrl); +} + +void SPITester::set_hd_tx_rx_cnt(uint16_t tx_cnt, uint16_t rx_cnt, uint32_t addr_hd_rx_cnt, uint32_t size_hd_rx_cnt, uint32_t addr_hd_tx_cnt, uint32_t size_hd_tx_cnt) +{ + write(addr_hd_rx_cnt, (uint8_t*)&rx_cnt, size_hd_rx_cnt); + write(addr_hd_tx_cnt, (uint8_t*)&tx_cnt, size_hd_tx_cnt); +} diff --git a/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPITester.h b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPITester.h new file mode 100644 index 0000000000..cf6896e3a7 --- /dev/null +++ b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/SPITester.h @@ -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. + */ + +#ifndef SPI_TESTER_H +#define SPI_TESTER_H + +#include "MbedTester.h" + +class SPITester: public MbedTester { +public: + + enum SpiMode { + Mode0 = 0, + Mode1 = 1, + Mode2 = 2, + Mode3 = 3 + }; + + enum SpiBitOrder { + MSBFirst = 0, + LSBFirst = 1 + }; + + enum SpiDuplex { + FullDuplex = 0, + HalfDuplex = 1 + }; + + SPITester(const PinList *form_factor, const PinList *exclude_pins) + : MbedTester(form_factor, exclude_pins) + { + + } + +protected: + /* + * Read the number of transfers which have occurred + * + * return The number of SPI transfers that have completed since + * spi was reset. + */ + uint16_t get_transfer_count(uint32_t addr_transfers, uint32_t size_transfers); + + /* + * Read a checksum of data send to the tester + * + * param addr_checksum Address of the FPGA checksum reg. + * param size_checksum Size of the FPGA checksum reg. + * + * return The sum of all bytes sent to the tester since reset. + */ + uint32_t get_receive_checksum(uint32_t addr_checksum, uint32_t size_checksum); + + /* + * Set the clock mode of the spi_slave module. + * + * param mode Spi clock mode + * param addr_spi_ctrl Address of the FPGA spi control reg. + * param size_spi_ctrl Size of the FPGA FPGA spi control reg. + * param offset_clk_mode Clock mode offset. + * param size_clk_mode Clock mode size. + */ + void set_mode(SpiMode mode, uint32_t addr_spi_ctrl, uint32_t size_spi_ctrl, uint32_t offset_clk_mode, uint32_t size_clk_mode); + + /* + * Set bit order durring transmission of the spi_slave module. + * + * param bit_order Spi clock mode + * param addr_spi_ctrl Address of the FPGA spi control reg. + * param size_spi_ctrl Size of the FPGA FPGA spi control reg. + * param offset_bit_order Bit order offset. + * param size_bit_order Bit order size. + */ + void set_bit_order(SpiBitOrder bit_order, uint32_t addr_spi_ctrl, uint32_t size_spi_ctrl, uint32_t offset_bit_order, uint32_t size_bit_order); + + /* + * Set symbol size used durring transmission of the spi_slave module. + * + * param sym_size Spi symbol size + * param addr_spi_ctrl Address of the FPGA spi control reg. + * param size_spi_ctrl Size of the FPGA FPGA spi control reg. + * param offset_sym_size Symbol size offset. + * param size_sym_size Symbol size size. + */ + void set_sym_size(uint32_t sym_size, uint32_t addr_spi_ctrl, uint32_t size_spi_ctrl, uint32_t offset_sym_size, uint32_t size_sym_size); + + /* + * Set full-duplex/half-duplex transmission mode of the spi_slave module. + * + * param duplex Duplex mode used for the transmission + * param addr_spi_ctrl Address of the FPGA spi control reg. + * param size_spi_ctrl Size of the FPGA FPGA spi control reg. + * param offset_duplex Duplex mode offset. + * param size_duplex Duplex mode size. + */ + void set_duplex_mode(SpiDuplex duplex, uint32_t addr_spi_ctrl, uint32_t size_spi_ctrl, uint32_t offset_duplex, uint32_t size_duplex); + + /* + * Set tx/rx symbol count. + * + * tx_cnt TX symbol count + * rx_cnt RX symbol count + * param addr_hd_rx_cnt Address of the FPGA half duplex RX count reg. + * param size_hd_rx_cnt Size of the FPGA half duplex RX count reg. + * param addr_hd_tx_cnt Address of the FPGA half duplex TX count reg. + * param size_hd_tx_cnt Size of the FPGA half duplex TX count reg. + * + * note Required only in Half-Duplex mode. + */ + void set_hd_tx_rx_cnt(uint16_t tx_cnt, uint16_t rx_cnt, uint32_t addr_hd_rx_cnt, uint32_t size_hd_rx_cnt, uint32_t addr_hd_tx_cnt, uint32_t size_hd_tx_cnt); +}; + +#endif diff --git a/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/UARTTester.cpp b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/UARTTester.cpp new file mode 100644 index 0000000000..c5680c44a3 --- /dev/null +++ b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/UARTTester.cpp @@ -0,0 +1,131 @@ +/* + * 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. + */ + +#include "UARTTester.h" +#include "fpga_config.h" + +void UARTTester::set_baud(uint32_t baudrate) +{ + uint32_t divisor = TESTER_CLOCK_FREQUENCY_HZ / baudrate; + // Baud divisor is only 16 bits + MBED_ASSERT((divisor & 0xFFFF0000) == 0); + write(TESTER_UART_BAUD_DIVISOR, (uint8_t *)&divisor, TESTER_UART_BAUD_DIVISOR_SIZE); +} + +void UARTTester::set_bits(uint8_t data_bits) +{ + // Check for supported range + MBED_ASSERT((data_bits >= 1) && (data_bits <= 16)); + write(TESTER_UART_BIT_COUNT, &data_bits, sizeof(data_bits)); +} + +void UARTTester::set_stops(uint8_t stop_bits) +{ + // Check for supported range + MBED_ASSERT((stop_bits >= 1) && (stop_bits <= 16)); + write(TESTER_UART_STOP_COUNT, &stop_bits, sizeof(stop_bits)); +} + +void UARTTester::set_parity(bool enable, bool odd_n_even) +{ + uint8_t parity = (enable ? TESTER_UART_PARITY_ENABLE : 0) | + (odd_n_even ? TESTER_UART_PARITY_ODD_N_EVEN : 0); + write(TESTER_UART_PARITY, &parity, sizeof(parity)); +} + +void UARTTester::rx_start() +{ + uint8_t data = TESTER_UART_RX_CONTROL_ENABLE; + write(TESTER_UART_RX_CONTROL, &data, sizeof(data)); +} + +void UARTTester::rx_stop() +{ + uint8_t data = 0; + write(TESTER_UART_RX_CONTROL, &data, sizeof(data)); +} + +uint32_t UARTTester::rx_get_checksum() +{ + uint32_t checksum = 0; + read(TESTER_UART_RX_CHECKSUM, (uint8_t *)&checksum, sizeof(checksum)); + return checksum; +} + +uint32_t UARTTester::rx_get_count() +{ + uint32_t count = 0; + read(TESTER_UART_RX_COUNT, (uint8_t *)&count, sizeof(count)); + return count; +} + +uint16_t UARTTester::rx_get_data(int prev) +{ + MBED_ASSERT((prev >= 1) && (prev <= 4)); + uint16_t data = 0; + read(TESTER_UART_RX_PREV_1 - (prev - 1) * 2, (uint8_t *)&data, sizeof(data)); + return data; +} + +uint32_t UARTTester::rx_get_parity_errors() +{ + uint32_t errors = 0; + read(TESTER_UART_RX_PARITY_ERRORS, (uint8_t *)&errors, sizeof(errors)); + return errors; +} + +uint32_t UARTTester::rx_get_stop_errors() +{ + uint32_t errors = 0; + read(TESTER_UART_RX_STOP_ERRORS, (uint8_t *)&errors, sizeof(errors)); + return errors; +} + +uint32_t UARTTester::rx_get_framing_errors() +{ + uint32_t errors = 0; + read(TESTER_UART_RX_FRAMING_ERRORS, (uint8_t *)&errors, sizeof(errors)); + return errors; +} + +void UARTTester::tx_start(bool cts_enabled) +{ + uint32_t control = TESTER_UART_TX_CONTROL_ENABLE | (cts_enabled ? TESTER_UART_TX_CONTROL_ENABLE_CTS : 0); + write(TESTER_UART_TX_CONTROL, (uint8_t *)&control, sizeof(control)); +} + +void UARTTester::tx_stop() +{ + uint32_t control = 0; + write(TESTER_UART_TX_CONTROL, (uint8_t *)&control, sizeof(control)); +} + +void UARTTester::tx_set_count(uint32_t count) +{ + write(TESTER_UART_TX_COUNT, (uint8_t *)&count, sizeof(count)); +} + +void UARTTester::tx_set_next(uint16_t value) +{ + write(TESTER_UART_TX_NEXT, (uint8_t *)&value, sizeof(value)); +} + +void UARTTester::cts_deassert_delay(uint32_t delay_ns) +{ + uint32_t delay_clks = delay_ns / TESTER_CLOCK_PERIOD_NS; + write(TESTER_UART_CTS_DEACTIVATE_DELAY, (uint8_t *)&delay_clks, sizeof(delay_clks)); +} diff --git a/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/UARTTester.h b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/UARTTester.h new file mode 100644 index 0000000000..e2e998e420 --- /dev/null +++ b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/UARTTester.h @@ -0,0 +1,158 @@ +/* + * 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. + */ + +#ifndef UART_TESTER_H +#define UART_TESTER_H + +#include "MbedTester.h" + + +class UARTTester: public MbedTester { +public: + + UARTTester(const PinList *form_factor, const PinList *exclude_pins) + : MbedTester(form_factor, exclude_pins) + { + + } + + /** + * Set the baudrate for uart TX and RX + * + * @param baudrate Target baudrate in HZ + */ + void set_baud(uint32_t baudrate); + + /** + * Set the number of data bits + * + * Supported values for data bits is 1 to 16 + * + * @param data_bits The number of data bits in this transfer + */ + void set_bits(uint8_t data_bits); + + /** + * Set the number of stop bits + * + * Supported values for stop bits is 1 to 16 + * + * @param stop_bits The number of stop bits to end the transfer with + */ + void set_stops(uint8_t stop_bits); + + /** + * Enable or disable parity checking + * + * @param enable true to enable parity checking, false to disable it + * @param odd_n_even true of odd parity, false for even + */ + void set_parity(bool enable, bool odd_n_even); + + /** + * Enable UART reception + */ + void rx_start(); + + /** + * Disable UART reception + */ + void rx_stop(); + + /** + * Get the sum of all bytes received + * + * @return the sum of all bytes received + */ + uint32_t rx_get_checksum(); + + /** + * Get the number of bytes received + * + * @return the number of bytes received + */ + uint32_t rx_get_count(); + + /** + * Get the previous data(s) sent + * + * @param prev index of data to get 1 for the previous + * @return data + */ + uint16_t rx_get_data(int prev = 1); + + /** + * Get the number of parity errors that have occurred + * + * @return number of parity errors that have occurred + */ + uint32_t rx_get_parity_errors(); + + /** + * Get the number of stop errors that have occurred + * + * @return number of stop errors that have occurred + */ + uint32_t rx_get_stop_errors(); + + /** + * Get the number of framing errors that have occurred + * + * @return number of framing errors that have occurred + */ + uint32_t rx_get_framing_errors(); + + /** + * Start UART transmission + */ + void tx_start(bool cts_enabled=false); + + /** + * Stop UART transmission + */ + void tx_stop(); + + /** + * Set the number of bytes to send + * + * @param count Number of bytes to send when started + */ + void tx_set_count(uint32_t count); + + /** + * Set next sequence value to send + * + * When TX is started 'count' bytes will be sent. Each value will + * be one greater than the previous. + * + * @param value Next value to send + */ + void tx_set_next(uint16_t value); + + /** + * Set the delay seen when deasserting the CTS line + * + * When delay is set to 0 then transmission will be immediately + * stopped when CTS goes to 1. + * + * @param delay in nanoseconds + */ + void cts_deassert_delay(uint32_t delay_ns); + +}; + +#endif diff --git a/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/fpga_config.h b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/fpga_config.h new file mode 100644 index 0000000000..7ed7fe3145 --- /dev/null +++ b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/fpga_config.h @@ -0,0 +1,190 @@ +/* + * 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. + */ + +#define TESTER_CLOCK_FREQUENCY_HZ 100000000 +#define TESTER_CLOCK_PERIOD_NS 10 +#define TESTER_CONTROL 0x00000000 +#define TESTER_CONTROL_RESET 0x00000000 +#define TESTER_CONTROL_RESET_PERIPHERALS (1 << 0) +#define TESTER_CONTROL_RESET_ALL (1 << 1) +#define TESTER_CONTROL_REPROGRAM (1 << 2) +#define TESTER_CONTROL_VERSION 0x00000010 +#define TESTER_CONTROL_VERSION_SIZE 4 +#define TESTER_REMAP 0x00001000 +#define TESTER_SYS_IO 0x00002000 +#define TESTER_SYS_IO_MODE (0x000 + 0x00002C00) +#define TESTER_SYS_IO_MODE_DISABLED 0 +#define TESTER_SYS_IO_MODE_SPI_SERIAL_FLASH 1 +#define TESTER_SYS_IO_MODE_I2C_IO_EXPANDER0 2 +#define TESTER_SYS_IO_MODE_I2C_IO_EXPANDER1 3 +#define TESTER_SYS_IO_MODE_I2C_IO_EXPANDER2 4 +#define TESTER_SYS_IO_PWM_ENABLE (0x001 + 0x00002C00) +#define TESTER_SYS_IO_PWM_PERIOD (0x002 + 0x00002C00) +#define TESTER_SYS_IO_PWM_CYCLES_HIGH (0x006 + 0x00002C00) +#define TESTER_SYS_AN_MUX_ANALOGIN_MEASUREMENT (0x00A + 0x00002C00) +#define TESTER_SYS_NUM_POWER_SAMPLES (0x00C + 0x00002C00) +#define TESTER_SYS_NUM_POWER_CYCLES (0x010 + 0x00002C00) +#define TESTER_SYS_ADC_SNAPSHOT (0x018 + 0x00002C00) +#define TESTER_SYS_SAMPLE_ADC (0x019 + 0x00002C00) +#define TESTER_SYS_ANIN0_MEASUREMENT (0x030 + 0x00002C00) +#define TESTER_SYS_ANIN0_MEASUREMENTS_SUM (0x032 + 0x00002C00) +#define TESTER_SYS_ANIN1_MEASUREMENT (0x03A + 0x00002C00) +#define TESTER_SYS_ANIN1_MEASUREMENTS_SUM (0x03C + 0x00002C00) +#define TESTER_SYS_ANIN2_MEASUREMENT (0x044 + 0x00002C00) +#define TESTER_SYS_ANIN2_MEASUREMENTS_SUM (0x046 + 0x00002C00) +#define TESTER_SYS_ANIN3_MEASUREMENT (0x04E + 0x00002C00) +#define TESTER_SYS_ANIN3_MEASUREMENTS_SUM (0x050 + 0x00002C00) +#define TESTER_PERIPHERAL 0x00100000 +#define TESTER_PERIPHERAL_SELECT 0x00100000 +#define TESTER_GPIO 0x00101000 +#define TESTER_SPI_MASTER 0x00102000 +#define TESTER_SPI_MASTER_STARTS 0x00102008 +#define TESTER_SPI_MASTER_STOPS 0x00102009 +#define TESTER_SPI_MASTER_TRANSFERS 0x0010200A +#define TESTER_SPI_MASTER_TRANSFERS_SIZE 2 +#define TESTER_SPI_MASTER_TO_SLAVE_CHECKSUM 0x00102012 +#define TESTER_SPI_MASTER_TO_SLAVE_CHECKSUM_SIZE 4 +#define TESTER_SPI_MASTER_CTRL 0x00102016 +#define TESTER_SPI_MASTER_CTRL_SIZE 2 +#define TESTER_SPI_MASTER_HD_TX_CNT 0x00102018 +#define TESTER_SPI_MASTER_HD_TX_CNT_SIZE 2 +#define TESTER_SPI_MASTER_HD_RX_CNT 0x0010201A +#define TESTER_SPI_MASTER_HD_RX_CNT_SIZE 2 +#define TESTER_SPI_MASTER_CS_TO_FIRST_SCLK_CNT 0x0010201C +#define TESTER_SPI_MASTER_CS_TO_FIRST_SCLK_CNT_SIZE 4 +#define TESTER_SPI_MASTER_LAST_SCLK_TO_CS_CNT 0x00102020 +#define TESTER_SPI_MASTER_LAST_SCLK_TO_CS_CNT_SIZE 4 +#define TESTER_SPI_MASTER_CLK_MODE_OFFSET 0 +#define TESTER_SPI_MASTER_CLK_MODE_SIZE 2 +#define TESTER_SPI_MASTER_BIT_ORDER_OFFSET 2 +#define TESTER_SPI_MASTER_BIT_ORDER_SIZE 1 +#define TESTER_SPI_MASTER_DUPLEX_OFFSET 3 +#define TESTER_SPI_MASTER_DUPLEX_SIZE 1 +#define TESTER_SPI_MASTER_SYM_SIZE_OFFSET 4 +#define TESTER_SPI_MASTER_SYM_SIZE_SIZE 6 +#define TESTER_SPI_SLAVE 0x00106000 +#define TESTER_SPI_SLAVE_STARTS 0x00106008 +#define TESTER_SPI_SLAVE_STOPS 0x00106009 +#define TESTER_SPI_SLAVE_TRANSFERS 0x0010600A +#define TESTER_SPI_SLAVE_TRANSFERS_SIZE 2 +#define TESTER_SPI_SLAVE_TO_MASTER_CHECKSUM 0x00106015 +#define TESTER_SPI_SLAVE_TO_MASTER_CHECKSUM_SIZE 4 +#define TESTER_SPI_SLAVE_CTRL 0x00106019 +#define TESTER_SPI_SLAVE_CTRL_SIZE 2 +#define TESTER_SPI_SLAVE_HD_TX_CNT 0x0010601B +#define TESTER_SPI_SLAVE_HD_TX_CNT_SIZE 2 +#define TESTER_SPI_SLAVE_HD_RX_CNT 0x0010601D +#define TESTER_SPI_SLAVE_HD_RX_CNT_SIZE 2 +#define TESTER_SPI_SLAVE_CLK_DIV 0x0010601F +#define TESTER_SPI_SLAVE_CLK_DIV_SIZE 2 +#define TESTER_SPI_SLAVE_NUM_OF_SYMBOLS 0x00106021 +#define TESTER_SPI_SLAVE_NUM_OF_SYMBOLS_SIZE 2 +#define TESTER_SPI_SLAVE_START_DELAY_US 0x00106023 +#define TESTER_SPI_SLAVE_START_DELAY_US_SIZE 1 +#define TESTER_SPI_SLAVE_SYM_DELAY_TICKS 0x00106024 +#define TESTER_SPI_SLAVE_SYM_DELAY_TICKS_SIZE 2 +#define TESTER_SPI_SLAVE_CLK_MODE_OFFSET 0 +#define TESTER_SPI_SLAVE_CLK_MODE_SIZE 2 +#define TESTER_SPI_SLAVE_BIT_ORDER_OFFSET 2 +#define TESTER_SPI_SLAVE_BIT_ORDER_SIZE 1 +#define TESTER_SPI_SLAVE_DUPLEX_OFFSET 3 +#define TESTER_SPI_SLAVE_DUPLEX_SIZE 1 +#define TESTER_SPI_SLAVE_SYM_SIZE_OFFSET 4 +#define TESTER_SPI_SLAVE_SYM_SIZE_SIZE 6 +#define TESTER_SPI_SLAVE_START_REQUEST_OFFSET 10 +#define TESTER_SPI_SLAVE_START_REQUEST_SIZE 1 +#define TESTER_IO_METRICS 0x00103000 +#define TESTER_IO_METRICS_CTRL 0x00103000 +#define TESTER_IO_METRICS_CTRL_ACTIVE_BIT (1 << 0) +#define TESTER_IO_METRICS_CTRL_RESET_BIT (1 << 1) +#define TESTER_IO_METRICS_BASE(i) (0x00103040 + 0x40 * (i)) +#define TESTER_IO_METRICS_MIN_PULSE_LOW(i) (TESTER_IO_METRICS_BASE(i) + 0x00) +#define TESTER_IO_METRICS_MIN_PULSE_LOW_SIZE 4 +#define TESTER_IO_METRICS_MIN_PULSE_HIGH(i) (TESTER_IO_METRICS_BASE(i) + 0x04) +#define TESTER_IO_METRICS_MIN_PULSE_HIGH_SIZE 4 +#define TESTER_IO_METRICS_MAX_PULSE_LOW(i) (TESTER_IO_METRICS_BASE(i) + 0x08) +#define TESTER_IO_METRICS_MAX_PULSE_LOW_SIZE 4 +#define TESTER_IO_METRICS_MAX_PULSE_HIGH(i) (TESTER_IO_METRICS_BASE(i) + 0x0C) +#define TESTER_IO_METRICS_MAX_PULSE_HIGH_SIZE 4 +#define TESTER_IO_METRICS_RISING_EDGES(i) (TESTER_IO_METRICS_BASE(i) + 0x10) +#define TESTER_IO_METRICS_RISING_EDGES_SIZE 4 +#define TESTER_IO_METRICS_FALLING_EDGES(i) (TESTER_IO_METRICS_BASE(i) + 0x14) +#define TESTER_IO_METRICS_FALLING_EDGES_SIZE 4 +#define TESTER_UART_CONTROL (0x000 + 0x00104000) +#define TESTER_UART_CONTROL_SIZE 4 +#define TESTER_UART_BAUD_DIVISOR (0x004 + 0x00104000) +#define TESTER_UART_BAUD_DIVISOR_SIZE 2 +#define TESTER_UART_BIT_COUNT (0x010 + 0x00104000) +#define TESTER_UART_BIT_COUNT_SIZE 1 +#define TESTER_UART_STOP_COUNT (0x011 + 0x00104000) +#define TESTER_UART_STOP_COUNT_SIZE 1 +#define TESTER_UART_PARITY (0x012 + 0x00104000) +#define TESTER_UART_PARITY_SIZE 1 +#define TESTER_UART_PARITY_ENABLE (1 << 0) +#define TESTER_UART_PARITY_ODD_N_EVEN (1 << 1) +#define TESTER_UART_RX_CONTROL (0x100 + 0x00104000) +#define TESTER_UART_RX_CONTROL_SIZE 4 +#define TESTER_UART_RX_CONTROL_ENABLE (1 << 0) +#define TESTER_UART_RX_CONTROL_RESET (1 << 1) +#define TESTER_UART_RX_CHECKSUM (0x104 + 0x00104000) +#define TESTER_UART_RX_CHECKSUM_SIZE 4 +#define TESTER_UART_RX_COUNT (0x108 + 0x00104000) +#define TESTER_UART_RX_COUNT_SIZE 4 +#define TESTER_UART_RX_PARITY_ERRORS (0x10C + 0x00104000) +#define TESTER_UART_RX_PARITY_ERRORS_SIZE 4 +#define TESTER_UART_RX_STOP_ERRORS (0x110 + 0x00104000) +#define TESTER_UART_RX_STOP_ERRORS_SIZE 4 +#define TESTER_UART_RX_FRAMING_ERRORS (0x114 + 0x00104000) +#define TESTER_UART_RX_FRAMING_ERRORS_SIZE 4 +#define TESTER_UART_RX_PREV_4 (0x118 + 0x00104000) +#define TESTER_UART_RX_PREV_4_SIZE 2 +#define TESTER_UART_RX_PREV_3 (0x11A + 0x00104000) +#define TESTER_UART_RX_PREV_3_SIZE 2 +#define TESTER_UART_RX_PREV_2 (0x11C + 0x00104000) +#define TESTER_UART_RX_PREV_2_SIZE 2 +#define TESTER_UART_RX_PREV_1 (0x11E + 0x00104000) +#define TESTER_UART_RX_PREV_1_SIZE 2 +#define TESTER_UART_TX_CONTROL (0x200 + 0x00104000) +#define TESTER_UART_TX_CONTROL_SIZE 4 +#define TESTER_UART_TX_CONTROL_ENABLE (1 << 0) +#define TESTER_UART_TX_CONTROL_RESET (1 << 1) +#define TESTER_UART_TX_CONTROL_ENABLE_CTS (1 << 2) +#define TESTER_UART_TX_COUNT (0x204 + 0x00104000) +#define TESTER_UART_TX_COUNT_SIZE 4 +#define TESTER_UART_TX_NEXT (0x208 + 0x00104000) +#define TESTER_UART_TX_NEXT_SIZE 2 +#define TESTER_UART_CTS_DEACTIVATE_DELAY (0x210 + 0x00104000) +#define TESTER_UART_CTS_DEACTIVATE_DELAY_SIZE 4 +#define TESTER_I2C_STARTS (0x000 + 0x00105000) +#define TESTER_I2C_STOPS (0x001 + 0x00105000) +#define TESTER_I2C_ACKS (0x002 + 0x00105000) +#define TESTER_I2C_NACKS (0x004 + 0x00105000) +#define TESTER_I2C_TRANSFERS (0x006 + 0x00105000) +#define TESTER_I2C_TRANSFERS_SIZE 2 +#define TESTER_I2C_TO_SLAVE_CHECKSUM (0x008 + 0x00105000) +#define TESTER_I2C_TO_SLAVE_CHECKSUM_SIZE 4 +#define TESTER_I2C_STATE_NUM (0x00C + 0x00105000) +#define TESTER_I2C_NUMBER_DEV_ADDR_MATCHES (0x00D + 0x00105000) +#define TESTER_I2C_DEVICE_ADDRESS (0x00E + 0x00105000) +#define TESTER_I2C_SET_SDA (0x010 + 0x00105000) +#define TESTER_I2C_PREV_TO_SLAVE_4 (0x011 + 0x00105000) +#define TESTER_I2C_PREV_TO_SLAVE_3 (0x012 + 0x00105000) +#define TESTER_I2C_PREV_TO_SLAVE_2 (0x013 + 0x00105000) +#define TESTER_I2C_PREV_TO_SLAVE_1 (0x014 + 0x00105000) +#define TESTER_I2C_NEXT_FROM_SLAVE (0x015 + 0x00105000) +#define TESTER_I2C_NUM_WRITES (0x016 + 0x00105000) +#define TESTER_I2C_NUM_READS (0x018 + 0x00105000) diff --git a/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/test_utils.h b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/test_utils.h new file mode 100644 index 0000000000..cc1246bac8 --- /dev/null +++ b/components/testing/COMPONENT_FPGA_CI_TEST_SHIELD/test_utils.h @@ -0,0 +1,465 @@ +/* + * 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. + */ + +#ifndef TEST_UTILS_H +#define TEST_UTILS_H + +#include + +// test function prototypes +typedef void (*TF1)(PinName p0); +typedef void (*TF2)(PinName p0, PinName p1); +typedef void (*TF3)(PinName p0, PinName p1, PinName p2); +typedef void (*TF4)(PinName p0, PinName p1, PinName p2, PinName p3); +typedef void (*TF5)(PinName p0, PinName p1, PinName p2, PinName p3, PinName p4); + +template +struct FunctionCaller +{ + +}; + +template +struct FunctionCaller +{ + void operator()(PortType &port) + { + f(port.pins[0]); + } +}; + +template +struct FunctionCaller +{ + void operator()(PortType &port) + { + f(port.pins[0], port.pins[1]); + } +}; + +template +struct FunctionCaller +{ + void operator()(PortType &port) + { + f(port.pins[0], port.pins[1], port.pins[2]); + } +}; + +template +struct FunctionCaller +{ + void operator()(PortType &port) + { + f(port.pins[0], port.pins[1], port.pins[2], port.pins[3]); + } +}; + +template +struct FunctionCaller +{ + void operator()(PortType &port) + { + f(port.pins[0], port.pins[1], port.pins[2], port.pins[3], port.pins[4]); + } +}; + +template +bool peripheral_comparator(const PortType &port1, const PortType &port2) +{ + return port1.peripheral == port2.peripheral; +} + +template +bool peripheral_less(const PortType &port1, const PortType &port2) +{ + return port1.peripheral < port2.peripheral; +} + +template +static bool find_port_pins(PortType &port) +{ + return pinmap_find_peripheral_pins(FormFactorType::pins(), FormFactorType::restricted_pins(), + port.peripheral, PortType::PinMap::maps, port.ppins, PortType::pin_count); +} + +template +void find_ports(std::list &matched_ports, std::list ¬_matched_ports) +{ + // Loop through every pin type + for (uint32_t i = 0; i < PortType::pin_count; i++) { + const PinMap *map = PortType::PinMap::maps[i]; + const char *pin_type = PortType::PinMap::pin_type_names[i]; + + // Loop through each pin of a given type + for (; map->pin != NC; map++) { + PortType port; + // Set pin being tested + port.pins[i] = map->pin; + port.peripheral = map->peripheral; + // Only form factor pins can be tested + if (!pinmap_list_has_pin(FormFactorType::pins(), port.pins[i])) { + continue; + } + // Don't test restricted pins + if (pinmap_list_has_pin(FormFactorType::restricted_pins(), port.pins[i])) { + utest_printf("Skipping %s pin %s (%i)\r\n", pin_type, + FormFactorType::pin_to_string(port.pins[i]), port.pins[i]); + continue; + } + // skipp pin searching if single pin port type + if (PortType::pin_count > 1) { + find_port_pins(port); + } + if (port.empty()) { + not_matched_ports.push_back(port); + } else { + matched_ports.push_back(port); + } + } + } +} + + +template +void test_all_ports(std::list &matched_ports, std::list ¬_matched_ports) +{ + typedef typename std::list::iterator Iter; + utest_printf("***Testing %s on all form factor ports***\n", PortType::PinMap::name); + const PinList *ff_pins = FormFactorType::pins(); + FunctionCaller call; + + if (matched_ports.empty() && not_matched_ports.empty()) { + utest_printf("Could not find pins for %s testing \n", PortType::PinMap::name); + return; + } + + for (uint32_t i = 0; i < ff_pins->count; i++) { + for (Iter it = matched_ports.begin(); it != matched_ports.end(); ++it) { + PortType &port = *it; + for (uint32_t j = 0; j < PortType::pin_count; j++) { + if (ff_pins->pins[i] == port.pins[j]) { + utest_printf("%3s - %s pin tested on port: %s...", FormFactorType::pin_to_string(ff_pins->pins[i]), + PortType::PinMap::pin_type_names[j], port.str()); + if (port.status == PortType::StatusNotTested) { + call(port); + port.status = PortType::StatusPass; + } + utest_printf("%s\n", port.status == PortType::StatusPass ? "succeeded" : "failed"); + goto end_port_iteration; + } + } + } + for (Iter it = not_matched_ports.begin(); it != not_matched_ports.end(); ++it) { + PortType &port = *it; + for (uint32_t j = 0; j < PortType::pin_count; j++) { + if (ff_pins->pins[i] == port.pins[j]) { + utest_printf("%3s - Could not find pins to test %s pin %s (%d)\n", + FormFactorType::pin_to_string(ff_pins->pins[i]), + PortType::PinMap::pin_type_names[j], + FormFactorType::pin_to_string(ff_pins->pins[i]), + ff_pins->pins[i]); + goto end_port_iteration; + } + } + } +end_port_iteration: + ; + } +} + +template +void test_peripheral(PortType &port) +{ + if (port.empty()) { + utest_printf("%d - Could not find pins to test peripheral\n", port.peripheral); + } else { + utest_printf("%d - peripheral tested on port: %s...", port.peripheral, port.str()); + if (port.status == PortType::StatusNotTested) { + FunctionCaller call; + call(port); // run test + port.status = PortType::StatusPass; + } + utest_printf("%s\n", port.status == PortType::StatusPass ? "succeeded" : "failed"); + } +} + +template +void test_all_peripherals(std::list &matched_ports, std::list ¬_matched_ports) +{ + typedef typename std::list::iterator Iter; + utest_printf("***Testing all %s peripherals***\n", PortType::PinMap::name); + + if (matched_ports.empty() && not_matched_ports.empty()) { + utest_printf("Could not find pins for %s testing \n", PortType::PinMap::name); + return; + } + + matched_ports.sort(peripheral_less); + not_matched_ports.sort(peripheral_less); + + for (Iter m_it = matched_ports.begin(), nm_it = not_matched_ports.begin(); + m_it != matched_ports.end() || nm_it != not_matched_ports.end();) { + if (m_it != matched_ports.end() && nm_it != not_matched_ports.end()) { + if ((*m_it).peripheral < (*nm_it).peripheral) { + test_peripheral(*m_it); + ++m_it; + } else { + test_peripheral(*nm_it); + ++nm_it; + } + } else if (m_it != matched_ports.end()) { + test_peripheral(*m_it); + ++m_it; + } else if (nm_it != not_matched_ports.end()) { + test_peripheral(*nm_it); + ++nm_it; + } + } +} + +template +void all_ports() +{ + std::list matched_ports, not_matched_ports; + find_ports(matched_ports, not_matched_ports); + matched_ports.unique(); + not_matched_ports.unique(); + test_all_ports(matched_ports, not_matched_ports); +} + +template +void all_peripherals() +{ + std::list matched_ports, not_matched_ports; + find_ports(matched_ports, not_matched_ports); + + matched_ports.sort(peripheral_less); + not_matched_ports.sort(peripheral_less); + matched_ports.unique(peripheral_comparator); + not_matched_ports.unique(peripheral_comparator); + + test_all_peripherals(matched_ports, not_matched_ports); +} + +template +void one_peripheral() +{ + std::list matched_ports, not_matched_ports; + find_ports(matched_ports, not_matched_ports); + + utest_printf("***Testing one %s pin configuration***\n", PortType::PinMap::name); + if (matched_ports.empty()) { + utest_printf("Could not find pins for %s testing \n", PortType::PinMap::name); + } else { + test_peripheral(matched_ports.front()); + } +} + +template +class Port; + +template +bool operator== (const Port &port1, + const Port &port2); + +template +class Port { +public: + int peripheral; + PinName pins[N]; + PinName *ppins[N]; + + static const uint32_t pin_count = N; + typedef PinMapType PinMap; + typedef FunctionType TestFunctionType; + + enum Status { StatusPass, StatusFail, StatusNotTested }; + Status status; + + Port(): peripheral(NC), status(StatusNotTested) + { + init_pins(); + } + + Port(const Port &port) + { + init_pins(); + copy_from(port); + } + + void init_pins() + { + for (uint32_t i = 0; i < N; i++) { + pins[i] = NC; + ppins[i] = &pins[i]; + } + } + + void copy_from(const Port &port) + { + peripheral = port.peripheral; + status = port.status; + for (uint32_t i = 0; i < N; i++) { + pins[i] = port.pins[i]; + } + } + + bool empty() + { + if (peripheral == NC) { + return true; + } + for (uint32_t i = 0; i < N; i++) { + if (pins[i] == NC) { + return true; + } + } + return false; + } + + const char *str() + { + static char port_str[128]; + char pin_str[32]; + sprintf(port_str, "peripheral=(%d) ", peripheral); + for (uint32_t i = 0; i < N; i++) { + sprintf(pin_str, "%s=(%s) ", PinMap::pin_type_names[i], FormFactorType::pin_to_string(pins[i])); + strcat(port_str, pin_str); + } + return port_str; + } + + friend bool operator==<> (const Port &port1, const Port &port2); +}; + +template +const uint32_t Port::pin_count; + +template +bool operator== (const Port &port1, const Port &port2) +{ + if (port1.peripheral != port2.peripheral) { + return false; + } + for (uint32_t i = 0; i < N; i++) { + if (port1.pins[i] != port2.pins[i]) { + return false; + } + } + return true; +} + +class DefaultFormFactor { +public: + static const PinList *pins() + { + return pinmap_ff_default_pins(); + } + + static const PinList *restricted_pins() + { + return pinmap_restricted_pins(); + } + + static const char *pin_to_string(PinName pin) + { + return pinmap_ff_default_pin_to_string(pin); + } +}; + +#if DEVICE_SPI +struct SPIMaps { + static const PinMap *maps[]; + static const char *const pin_type_names[]; + static const char *const name; +}; +const PinMap *SPIMaps::maps[] = { spi_master_mosi_pinmap(), spi_master_miso_pinmap(), spi_master_clk_pinmap(), spi_master_cs_pinmap() }; +const char *const SPIMaps::pin_type_names[] = { "MOSI", "MISO", "SCLK", "SSEL" }; +const char *const SPIMaps::name = "SPI"; +typedef Port<4, SPIMaps, DefaultFormFactor, TF4> SPIPort; + +struct SPINoCSMaps { + static const PinMap *maps[]; + static const char *const pin_type_names[]; + static const char *const name; +}; +const PinMap *SPINoCSMaps::maps[] = { spi_master_mosi_pinmap(), spi_master_miso_pinmap(), spi_master_clk_pinmap()}; +const char *const SPINoCSMaps::pin_type_names[] = { "MOSI", "MISO", "SCLK" }; +const char *const SPINoCSMaps::name = "SPI"; +typedef Port<3, SPINoCSMaps, DefaultFormFactor, TF3> SPINoCSPort; + +struct SPISlaveMaps { + static const PinMap *maps[]; + static const char *const pin_type_names[]; + static const char *const name; +}; +const PinMap *SPISlaveMaps::maps[] = { spi_slave_mosi_pinmap(), spi_slave_miso_pinmap(), spi_slave_clk_pinmap(), spi_slave_cs_pinmap() }; +const char *const SPISlaveMaps::pin_type_names[] = { "MOSI", "MISO", "SCLK", "SSEL" }; +const char *const SPISlaveMaps::name = "SPISlave"; +typedef Port<4, SPISlaveMaps, DefaultFormFactor, TF4> SPISlavePort; +#endif + +#if DEVICE_I2C +struct I2CMaps { + static const PinMap *maps[]; + static const char *const pin_type_names[]; + static const char *const name; +}; +const PinMap *I2CMaps::maps[] = { i2c_master_sda_pinmap(), i2c_master_scl_pinmap() }; +const char *const I2CMaps::pin_type_names[] = { "SDA", "SCL" }; +const char *const I2CMaps::name = "I2C"; +typedef Port<2, I2CMaps, DefaultFormFactor, TF2> I2CPort; +#endif + +#if DEVICE_PWMOUT +struct PWMMaps { + static const PinMap *maps[]; + static const char *const pin_type_names[]; + static const char *const name; +}; +const PinMap *PWMMaps::maps[] = { pwmout_pinmap() }; +const char *const PWMMaps::pin_type_names[] = { "PWM_OUT" }; +const char *const PWMMaps::name = "PWM"; +typedef Port<1, PWMMaps, DefaultFormFactor, TF1> PWMPort; +#endif + +#if DEVICE_ANALOGIN +struct AnaloginMaps { + static const PinMap *maps[]; + static const char *const pin_type_names[]; + static const char *const name; +}; +const PinMap *AnaloginMaps::maps[] = { analogin_pinmap() }; +const char *const AnaloginMaps::pin_type_names[] = { "ADC_IN" }; +const char *const AnaloginMaps::name = "ADC"; +typedef Port<1, AnaloginMaps, DefaultFormFactor, TF1> AnaloginPort; +#endif + +#if DEVICE_ANALOGOUT +struct AnalogoutMaps { + static const PinMap *maps[]; + static const char *const pin_type_names[]; + static const char *const name; +}; +const PinMap *AnalogoutMaps::maps[] = { analogout_pinmap() }; +const char *const AnalogoutMaps::pin_type_names[] = { "DAC_OUT" }; +const char *const AnalogoutMaps::name = "DAC"; +typedef Port<1, AnalogoutMaps, DefaultFormFactor, TF1> AnalogoutPort; +#endif + +#endif