diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/.cproject b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/.cproject new file mode 100644 index 0000000..39ed230 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/.cproject @@ -0,0 +1,300 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/.gitignore b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/.gitignore new file mode 100644 index 0000000..4d0cf27 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/.gitignore @@ -0,0 +1,3 @@ +/.settings/ +/*miv-rv32-imc-debug*/ +/*miv-rv32-imc-release*/ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/.project b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/.project new file mode 100644 index 0000000..31a271b --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/.project @@ -0,0 +1,36 @@ + + + miv-rv32-coremmc-mmc_read_back + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + PLATFORM_LOC + $%7BPARENT-1-PROJECT_LOC%7D/platform + + + PLATFORM_LOC1 + $%7BPARENT-3-ECLIPSE_HOME%7D/BB5/g/new/mss-i2c-examples/myprojects/miv_rv32/platform + + + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/README.md b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/README.md new file mode 100644 index 0000000..e53ee75 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/README.md @@ -0,0 +1,74 @@ +# CoreMMC MMC Write and Read back example + +This project writes a block of data to the eMMC (embedded Multi-Media Card) +device, reads the block back and performs a read-back check to ensure that the +data is valid. + +The eMMC block size in this example is fixed to 512 Bytes, which is the standard +for eMMC devices which have a storage capacity of greater than 2GB. + +There are two different build configurations provided with this project which configure +this SoftConsole project for RISC-V IMC instruction extension. +The following configurations are provided with the example. + +- miv-rv32-imc-debug +- miv-rv32-imc-release + +## How to use this example + +- Connect via the board's USB-UART serial cable to a host PC running a terminal + emulator such as TeraTerm or PuTTY configured as follows: + - 115200 baud + - 8 data bits + - 1 stop bit + - no parity + - no flow control +- Run the example project using a debugger. + +The terminal emulator command line interface will display (through UART) the status +of the write and read operations to the eMMC device. + +## fpga_design_config (formerly known as hw_config.h) + +The SoftConsole project targeted for Mi-V processors now uses an improved directory +structure. +The `fpga_design_config.h` must be stored as shown below: + +`/boards//fpga_design_config/fpga_design_config.h` + +Currently, this file must be hand crafted when using the Mi-V Soft Processor. +In future, all the design and soft IP configurations will be automatically generated +from the Libero design description data. + +You can use the sample file provided with MIV_RV32 HAL as an example. +Rename it from `sample_fpga_design_config.h` to `fpga_design_config.h` and then customize +it per your hardware design such as `SYS_CLK_FREQ`, peripheral BASE addresses, interrupt +umbers, definition of `MSCC_STDIO_UART_BASE_ADDR` if you want a CoreUARTapb mapped +to STDIO, etc. + +## Target hardware + +This example can be targeted at the latest Mi-V Soft Processor MIV_RV32. +Choose the build configurations per the target processor in your design and the supported +ISA extensions that you want to use. + +All the design specific definitions such as peripheral base addresses, system clock +frequency etc. are included in fpga_design_config.h(hw_config.h). + +This project has been tested on the following board with the MIV_RV32 IMC Core: + +- Smart Fusion 2 Development Kit (Revision C) (M2S050T-FG896) + +The Smart Fusion 2 Development Kit has a SanDisk SDIN5C2-8G eMMC device on-board. + +The design for this sample program needs to contain a single instance of the CoreMMC +configured to an MMC Data Width and FIFO depth of your choice. +The driver supports all possible configurations of these parameters automatically. + +This example project can be used with another design using a different hardware +configuration. This can be achieved by overwriting the content of this example project's +"fpga_design_config.h(hw_config.h)" file with the correct data from your Libero design. + +## Silicon revision dependencies + +This example is tested on Smart Fusion 2 Development Kit (Revision C) (M2S050T-FG896) diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/miv-rv32-coremmc-mmc_read_back hw attach.launch b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/miv-rv32-coremmc-mmc_read_back hw attach.launch new file mode 100644 index 0000000..45bae8a --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/miv-rv32-coremmc-mmc_read_back hw attach.launch @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/miv-rv32-coremmc-mmc_read_back hw debug.launch b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/miv-rv32-coremmc-mmc_read_back hw debug.launch new file mode 100644 index 0000000..c0faadd --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/miv-rv32-coremmc-mmc_read_back hw debug.launch @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/applicaiton/main.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/applicaiton/main.c new file mode 100644 index 0000000..3fb8d3a --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/applicaiton/main.c @@ -0,0 +1,296 @@ +/******************************************************************************* + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * This sample program demonstrates the use of CoreMMC driver to interface + * with the eMMC Card on-board the SmartFusion 2 Development Board. + * + * Please refer to the file README.md for further details about this example. + * + */ + +/*-------------------------------------------------------------------------*/ +#include +#include +#include +#include "hal/hal.h" +#include "miv_rv32_hal/miv_rv32_hal.h" +#include "drivers/fpga_ip/CoreUARTapb/core_uart_apb.h" +#include "drivers/fpga_ip/CoreMMC/core_mmc.h" + +#define INTERRUPT_EVERY_10MS (100u) +#define SECTOR_SIZE_BYTES (512u) +#define SECTOR_SIZE_WORDS (SECTOR_SIZE_BYTES / sizeof(uint32_t)) +#define NUMBER_OF_SECTORS (1u) +#define SECTOR_1 (0x1u) +#define TIMEOUT_10_SECS (1000u) +#define INCREASE_BY_2 (8u) +#define SUCCESS (0u) +#define FAILURE (1u) + +/* Sector address within eMMC device */ +#define BLOCK_1 0x00000001u +#define NO_MULTI_BLKS 64 +#define MAX_NO_BLOCKS 15515584 /* 8meg device- can be read from the extended CSR register */ + +static uint32_t g_write_data_buffer[SECTOR_SIZE_WORDS] = {0u}; +static uint32_t g_read_data_buffer[SECTOR_SIZE_WORDS] = {0u}; +static uint32_t ten_ms_sys_tick = 0; + +static const uint8_t init_fail[] = "\r\neMMC device initialisation failure\r\n"; +static const uint8_t read_back_success[] = "\r\nSUCCESS: Write and read data match\r\n"; +static const uint8_t data_mismatch_fail[] = "\r\nFAILURE: Write and read data mismatch\r\n"; +static const uint8_t read_fail[] = "\r\nFAILURE: Read operation failed!\r\n"; +static const uint8_t write_fail[] = "\r\nFAILURE: Write operation failed!\r\n"; +static const uint8_t greeting_message[] = + "\033[2J******************************************************************************\r\n\n" + "***************************** Mi-V CoreMMC *****************************\r\n\n" + "************* Single and Multi-block Read and Write Example ************\r\n\n" + "******************************************************************************\r\n\r\n" + "This example project demonstrates writing and reading blocks of data to the MMC device.\r\n"; + +static mmc_instance_t g_emmc; +static mmc_params_t g_emmc_params; + +static UART_instance_t g_uart; + +void +MSYS_EI0_IRQHandler(void) +{ + MMC_isr(&g_emmc); +} + +void +SysTick_Handler(void) +{ + ten_ms_sys_tick++; +} + +static uint32_t +get_clock_ticks(void) +{ + return (ten_ms_sys_tick); +} + +static void +print(const uint8_t *const message) +{ + UART_polled_tx_string(&g_uart, message); +} + +static void +fill_buffer(uint32_t *const buffer, const uint32_t increment) +{ + uint32_t value = 0u; + + for (uint32_t index = 0u; index < SECTOR_SIZE_WORDS; index++) + { + *(buffer + index) = value; + value += increment; + } +} + +static uint8_t +verify_data(const uint32_t *const read_buffer, + const uint32_t *const write_buffer, + const uint32_t number_of_sectors) +{ + for (uint32_t index = 0; index < (SECTOR_SIZE_WORDS * number_of_sectors); index++) + { + if (*(read_buffer + index) != *(write_buffer + index)) + { + return FAILURE; + } + } + return SUCCESS; +} + +static mmc_transfer_status_t +single_block_write_readback(mmc_instance_t *const this_mmc, + const uint32_t *const write_buffer, + uint32_t *const read_buffer, + const uint32_t sector_address) +{ + volatile uint32_t *write_fifo = + (uint32_t *)MMC_get_fifo_write_address((mmc_instance_t *)this_mmc); + volatile uint32_t *read_fifo = + (uint32_t *)MMC_get_fifo_read_address((mmc_instance_t *)this_mmc); + volatile mmc_transfer_status_t mmc_command_status = MMC_TRANSFER_FAIL; + + print("\r\n\nCoreMMC single block write and read-back test:"); + + MMC_init_fifo(this_mmc); + + mmc_command_status = MMC_single_block_write_nb((mmc_instance_t *)this_mmc, + sector_address, + get_clock_ticks(), + TIMEOUT_10_SECS); + + for (uint32_t index = 0; index < SECTOR_SIZE_WORDS; index++) + { + *write_fifo = write_buffer[index]; + } + + while (MMC_CMD_PROCESSING == mmc_command_status) + { + mmc_command_status = MMC_status((mmc_instance_t *)this_mmc, get_clock_ticks()); + } + if (MMC_TRANSFER_SUCCESS == mmc_command_status) + { + MMC_init_fifo(this_mmc); + mmc_command_status = MMC_single_block_read_nb((mmc_instance_t *)this_mmc, + sector_address, + get_clock_ticks(), + TIMEOUT_10_SECS); + while (MMC_CMD_PROCESSING == mmc_command_status) + { + mmc_command_status = MMC_status((mmc_instance_t *)this_mmc, get_clock_ticks()); + } + if (MMC_TRANSFER_SUCCESS == mmc_command_status) + { + for (uint32_t index = 0; index < SECTOR_SIZE_WORDS; index++) + { + *(read_buffer + index) = *read_fifo; + } + if (verify_data(read_buffer, write_buffer, SECTOR_1) == SUCCESS) + { + print(read_back_success); + } + else + { + print(data_mismatch_fail); + } + } + else + { + print(read_fail); + } + } + else + { + print(write_fail); + } + return mmc_command_status; +} + +static mmc_transfer_status_t +multiblock_write_readback(mmc_instance_t *const this_mmc, + const uint32_t *const write_buffer, + uint32_t *const read_buffer, + const uint32_t sector_address, + const uint32_t number_of_sectors) +{ + volatile uint32_t *write_fifo = + (uint32_t *)MMC_get_fifo_write_address((mmc_instance_t *)this_mmc); + volatile uint32_t *read_fifo = + (uint32_t *)MMC_get_fifo_read_address((mmc_instance_t *)this_mmc); + mmc_transfer_status_t mmc_command_status = MMC_TRANSFER_FAIL; + + print("\r\n\nCoreMMC multi-block write and read-back test:"); + + MMC_init_fifo(this_mmc); + + mmc_command_status = MMC_multi_block_write((mmc_instance_t *)this_mmc, + number_of_sectors, + sector_address, + get_clock_ticks(), + TIMEOUT_10_SECS); + + for (uint32_t index = 0; index < (number_of_sectors * SECTOR_SIZE_WORDS); index++) + { + *write_fifo = *(write_buffer + index); + } + + while (MMC_CMD_PROCESSING == mmc_command_status) + { + mmc_command_status = MMC_status((mmc_instance_t *)this_mmc, get_clock_ticks()); + } + if (MMC_TRANSFER_SUCCESS == mmc_command_status) + { + MMC_init_fifo(this_mmc); + mmc_command_status = MMC_multi_block_read((mmc_instance_t *)this_mmc, + number_of_sectors, + sector_address, + get_clock_ticks(), + TIMEOUT_10_SECS); + + while (MMC_CMD_PROCESSING == mmc_command_status) + { + mmc_command_status = MMC_status((mmc_instance_t *)this_mmc, get_clock_ticks()); + } + + if (MMC_TRANSFER_SUCCESS == mmc_command_status) + { + for (uint32_t index = 0; index < (number_of_sectors * SECTOR_SIZE_WORDS); index++) + { + *(read_buffer + index) = *read_fifo; + } + if (verify_data(read_buffer, write_buffer, number_of_sectors) == SUCCESS) + { + print(read_back_success); + } + else + { + print(data_mismatch_fail); + return MMC_TRANSFER_FAIL; + } + } + else + { + print(read_fail); + } + } + else + { + print(write_fail); + } + return mmc_command_status; +} + +int +main(void) +{ + mmc_transfer_status_t mmc_state = MMC_TRANSFER_FAIL; + uint32_t sector = 1u; + + MRV_enable_interrupts(); + MRV_systick_config(SYS_CLK_FREQ / INTERRUPT_EVERY_10MS); + MRV_enable_local_irq(MRV32_TIMER_IRQn); + MRV_enable_local_irq(MRV32_MSYS_EIE0_IRQn); + + UART_init(&g_uart, COREUARTAPB0_BASE_ADDR, BAUD_VALUE_115200, (DATA_8_BITS | NO_PARITY)); + print(greeting_message); + + MMC_param_config(&g_emmc_params); + g_emmc_params.data_timeout = SYS_CLK_FREQ; /* Timeout set to 1 second (50000000 cycles) */ + mmc_state = MMC_init(&g_emmc, COREMMC_BASE_ADDR, &g_emmc_params); + + if (MMC_INIT_SUCCESS == mmc_state) + { + memset(g_read_data_buffer, 0x0u, sizeof(g_read_data_buffer)); + fill_buffer(g_write_data_buffer, INCREASE_BY_2); + + mmc_state = + single_block_write_readback(&g_emmc, g_write_data_buffer, g_read_data_buffer, SECTOR_1); + + memset(g_read_data_buffer, 0x0u, sizeof(g_read_data_buffer)); + fill_buffer(g_write_data_buffer, INCREASE_BY_2); + + mmc_state = multiblock_write_readback(&g_emmc, + g_write_data_buffer, + g_read_data_buffer, + sector * NO_MULTI_BLKS, + NUMBER_OF_SECTORS); + } + else + { + print(init_fail); + } + + while (1) + { + ; + } + return 0; +} diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/fpga_design/design_description/readme.md b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/fpga_design/design_description/readme.md new file mode 100644 index 0000000..bdfcb71 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/fpga_design/design_description/readme.md @@ -0,0 +1,3 @@ +# Design description + + The Libero generated design description will be stored here. diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/fpga_design_config/fpga_design_config.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/fpga_design_config/fpga_design_config.h new file mode 100644 index 0000000..8a955ab --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/fpga_design_config/fpga_design_config.h @@ -0,0 +1,155 @@ +/******************************************************************************* + * Copyright 2021 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file sample_fpga_design_config.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Sample design configuration settings + * + */ + /*========================================================================*//** + @mainpage + Example file detailing how the fpga_design_config.h should be constructed + for the SoftConsole project targeted for Mi-V processors. + + @section intro_sec Introduction + The SoftConsole project targeted for Mi-V processors now have an improved + folder structure. Detailed description of the folder structure is available + at https://github.com/Mi-V-Soft-RISC-V/miv-rv32-documentation. + + The fpga_design_config.h must be stored as shown below + /boards//fpga_design_config.h + + Currently this file must be hand crafted when using the Mi-V Soft Processor. + In future, all the design and soft IP configurations will be automatically + generated from the Libero design description data. + + You can use this sample file as an example. + Rename this file from sample_fpga_design_config.h to fpga_design_config.h + and then customize it per your hardware design. + + @section Project configuration Instructions + 1. Change SYS_CLK_FREQ define to frequency of Mi-V Soft processor clock + 2 Add all the soft IP core BASE addresses + 3. Add the peripheral Core Interrupts to Mi-V Soft processor IRQ number + mappings + 4. Define MSCC_STDIO_UART_BASE_ADDR if you want a CoreUARTapb mapped to + STDIO + + **NOTE** + In the legacy folder structures, the file hw_config.h as was used at the + root of the project folder. This file is now depricated. + +*//*=========================================================================*/ + +#ifndef FPGA_DESIGN_CONFIG_H_ +#define FPGA_DESIGN_CONFIG_H_ + +/***************************************************************************//** + * Soft-processor clock definition + * This is the only clock brought over from the Mi-V Libero design. + */ +#ifndef SYS_CLK_FREQ +#define SYS_CLK_FREQ 50000000UL +#endif + +/***************************************************************************//** + * Peripheral base addresses. + * Format of define is: + * __BASE_ADDR + * The field is optional if there is only one instance of the core + * in the design + */ +#define COREUARTAPB0_BASE_ADDR 0x70001000UL +#define COREGPIO_IN_BASE_ADDR 0x70002000UL +#define CORETIMER0_BASE_ADDR 0x70003000UL +#define CORETIMER1_BASE_ADDR 0x70004000UL +#define COREGPIO_OUT_BASE_ADDR 0x70005000UL +#define FLASH_CORE_SPI_BASE 0x70006000UL +#define CORE16550_BASE_ADDR 0x70007000UL + +/***************************************************************************//** + * Peripheral Interrupts are mapped to the corresponding Mi-V Soft processor + * interrupt in the Libero design. + * + * On the legacy RV32 cores, there can be up to 31 external interrupts (IRQ[30:0] + * pins). The legacy RV32 Soft processor external interrupts are defined in the + miv_rv32_plic.h + * + * These are of the form + * typedef enum +{ + NoInterrupt_IRQn = 0, + External_1_IRQn = 1, + External_2_IRQn = 2, + . + . + . + External_31_IRQn = 31 +} IRQn_Type; + + On the legacy RV32 processors, the PLIC identifies the interrupt and passes it + on to the processor core. The interrupt 0 is not used. The pin IRQ[0] should + map to External_1_IRQn likewise IRQ[30] should map to External_31_IRQn + +e.g + +#define TIMER0_IRQn External_30_IRQn +#define TIMER1_IRQn External_31_IRQn + + The MIV_RV32 soft processor has up to six optional system interrupts, MSYS_EI[n] + in addition to one EXT_IRQ. + The MIV_RV32 does not have an inbuilt PLIC and all the interrupts are directly + delivered to the processor core, hence unlike legacy RV32 cores, no interrupt + number mapping is necessary on MIV_RV32 core. + */ + +/**************************************************************************** + * Baud value to achieve a 115200 baud rate with system clock defined by + * SYS_CLK_FREQ. + * This value is calculated using the following equation: + * BAUD_VALUE = (CLOCK / (16 * BAUD_RATE)) - 1 + *****************************************************************************/ +#define BAUD_VALUE_115200 ((SYS_CLK_FREQ / (16 * 115200)) - 1) + +/****************************************************************************** + * Baud value to achieve a 57600 baud rate with system clock defined by + * SYS_CLK_FREQ. + * This value is calculated using the following equation: + * BAUD_VALUE = (CLOCK / (16 * BAUD_RATE)) - 1 + *****************************************************************************/ + #define BAUD_VALUE_57600 ((SYS_CLK_FREQ / (16 * 57600)) - 1) + +/***************************************************************************//** + * Define MSCC_STDIO_THRU_CORE_UART_APB in the project settings if you want the + * standard IOs to be redirected to a terminal via UART. + */ +#ifdef MSCC_STDIO_THRU_CORE_UART_APB +/* + * A base address mapping for the STDIO printf/scanf mapping to CortUARTapb + * must be provided if it is being used + * + * e.g. #define MSCC_STDIO_UART_BASE_ADDR COREUARTAPB1_BASE_ADDR + */ +#define MSCC_STDIO_UART_BASE_ADDR COREUARTAPB0_BASE_ADDR + +#ifndef MSCC_STDIO_UART_BASE_ADDR +#error MSCC_STDIO_UART_BASE_ADDR not defined- e.g. #define MSCC_STDIO_UART_BASE_ADDR COREUARTAPB1_BASE_ADDR +#endif + +#ifndef MSCC_STDIO_BAUD_VALUE +/* + * The MSCC_STDIO_BAUD_VALUE define should be set in your project's settings to + * specify the baud value used by the standard output CoreUARTapb instance for + * generating the UART's baud rate if you want a different baud rate from the + * default of 115200 baud + */ +#define MSCC_STDIO_BAUD_VALUE 115200 +#endif /*MSCC_STDIO_BAUD_VALUE*/ + +#endif /* end of MSCC_STDIO_THRU_CORE_UART_APB */ +/******************************************************************************* + * End of user edit section + */ +#endif /* FPGA_DESIGN_CONFIG_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/platform_config/driver_config/phy_sw_cfg.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/platform_config/driver_config/phy_sw_cfg.h new file mode 100644 index 0000000..6783c90 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/platform_config/driver_config/phy_sw_cfg.h @@ -0,0 +1,136 @@ +/***************************************************************************//** + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * @file core10gbasekr_phy_sw_cfg.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief PHY software configuration + * + */ + +#ifndef BOARDS_POLARFIRE_EVAL_KIT_PLATFORM_CONFIG_DRIVER_CONFIG_PHY_SW_CFG_H_ +#define BOARDS_POLARFIRE_EVAL_KIT_PLATFORM_CONFIG_DRIVER_CONFIG_PHY_SW_CFG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * Driver versioning macros. + */ +#define CORE_VENDOR "Microchip" +#define CORE_LIBRARY "Firmware" +#define CORE_NAME "Core10GBaseKR_PHY_Driver" +#define CORE_VERSION "x.x.x" + +/***************************************************************************//** + * Supported PHY models, used to control compile time inclusion of the + * associated PHY sub-drivers. + */ +#define CORE10GBASEKR_PHY +#define PF_XCVR_C10GB + +/***************************************************************************//** + * Define this macro to enable performance messages in the application. + */ +#undef C10GBKR_PERFORMANCE_MESSAGES + +/***************************************************************************//** + * User config options for overriding driver defaults of Core10GBaseKR_PHY + * + * These definitions can be overridden by defining the macro and assigning the + * desired value. + */ +#ifdef CORE10GBASEKR_PHY + +/* Main tap limits */ +#undef C10GBKR_LT_MAIN_TAP_MAX_LIMIT +#undef C10GBKR_LT_MAIN_TAP_MIN_LIMIT + +/* Post tap limits */ +#undef C10GBKR_LT_POST_TAP_MAX_LIMIT +#undef C10GBKR_LT_POST_TAP_MIN_LIMIT + +/* Pre tap limits */ +#undef C10GBKR_LT_PRE_TAP_MAX_LIMIT +#undef C10GBKR_LT_PRE_TAP_MIN_LIMIT + +/* Request to be sent to Link Partner + 0U => Preset + 1U => Initialize + */ +#undef C10GBKR_LT_INITIAL_REQUEST + +#undef C10GBKR_LT_INITIALIZE_MAIN_TAP +#undef C10GBKR_LT_INITIALIZE_POST_TAP +#undef C10GBKR_LT_INITIALIZE_PRE_TAP + +/***************************************************************************//** + Override XCVR configurations + */ +#undef PF_XCVR_C10GB_REG_VAL_SER_DRV_CTRL +#undef PF_XCVR_C10GB_REG_VAL_SER_DRV_DATA_CTRL +#undef PF_XCVR_C10GB_REG_VAL_SER_DRV_CTRL_SEL +#undef PF_XCVR_C10GB_REG_VAL_DES_DFE_CAL_CTRL_0 +#undef PF_XCVR_C10GB_REG_VAL_DES_DFE_CAL_CTRL_1 +#undef PF_XCVR_C10GB_REG_VAL_DES_DFE_CAL_CTRL_2 +#undef PF_XCVR_C10GB_REG_VAL_DES_DFE_CAL_CMD +#undef PF_XCVR_C10GB_REG_VAL_SER_RTL_CTRL +#undef PF_XCVR_C10GB_REG_VAL_DES_CDR_CTRL_2 +#undef PF_XCVR_C10GB_REG_VAL_DES_CDR_CTRL_3 +#undef PF_XCVR_C10GB_REG_VAL_DES_DFEEM_CTRL_1 +#undef PF_XCVR_C10GB_REG_VAL_DES_DFEEM_CTRL_2 +#undef PF_XCVR_C10GB_REG_VAL_DES_DFEEM_CTRL_3 +#undef PF_XCVR_C10GB_REG_VAL_DES_DFE_CTRL_2 +#undef PF_XCVR_C10GB_REG_VAL_DES_EM_CTRL_2 + +/***************************************************************************//** + Override Auto-Negotiation data rate configurations + */ +#undef PF_XCVR_C10GB_AN_CFG_DES_RXPLL_DIV +#undef PF_XCVR_C10GB_AN_CFG_PMA_DES_RSTPD_POWER_DOWN +#undef PF_XCVR_C10GB_AN_CFG_PMA_SERDES_RTL_CTRL +#undef PF_XCVR_C10GB_AN_CFG_PMA_DES_RTL_LOCK_CTRL +#undef PF_XCVR_C10GB_AN_CFG_PMA_DES_RSTPD_POWER_UP +#undef PF_XCVR_C10GB_AN_CFG_PCS_LRST_R0_RESET_ASSERT +#undef PF_XCVR_C10GB_AN_CFG_PCS_LRST_R0_RESET_DEASSERT + + +/***************************************************************************//** + Override Link Training data rate configurations + */ +#undef PF_XCVR_C10GB_LT_CFG_PMA_DES_RSTPD_POWER_DOWN +#undef PF_XCVR_C10GB_LT_CFG_PMA_DES_RSTPD_POWER_UP +#undef PF_XCVR_C10GB_LT_CFG_DES_RXPLL_DIV +#undef PF_XCVR_C10GB_LT_CFG_PMA_SERDES_RTL_CTRL +#undef PF_XCVR_C10GB_LT_CFG_PMA_DES_DFE_CAL_BYPASS +#undef PF_XCVR_C10GB_LT_CFG_PCS_LRST_R0_RESET_ASSERT +#undef PF_XCVR_C10GB_LT_CFG_PCS_LRST_R0_RESET_DEASSERT + +#endif /* CORE10GBASEKR_PHY */ + +#ifdef __cplusplus +} +#endif + + +#endif /* BOARDS_POLARFIRE_EVAL_KIT_PLATFORM_CONFIG_DRIVER_CONFIG_PHY_SW_CFG_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/platform_config/linker/miv-rv32-execute-in-place.ld b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/platform_config/linker/miv-rv32-execute-in-place.ld new file mode 100644 index 0000000..19df5fb --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/platform_config/linker/miv-rv32-execute-in-place.ld @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright 2019-2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * file name : miv-rv32-execute-in-place.ld + * Mi-V soft processor linker script for creating a SoftConsole downloadable + * image executing from a one address space whereas the data, sdata and stack + * sections are placed in another address space. This could be used in cases such + * as: + * 1) When using MIV_RV32, the reset vector points to the LSRAM at address + * 0x80000000 and the data, sdata, bss and stack sections are placed in the + * TCM region. + * + * 2) Executing from a Non Volatile memory. The actual memory will depend on + * the FPGA platform. For exameple, it could be the eNVM on SmartFusion2, + * Igloo2 or on-board non-volatile memory which supports code execution. + * + * NOTE: Modify the memory section addresses and the sizes according to your + * Libero design. + * + * To know more about the memory map of the MIV_RV32 based Libero design, open + * the MIV_RV32 IP configurator and look for "Reset Vector Address" and the + * "Memory Map" tab. + * + */ + +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + + +MEMORY +{ + rom (rx) : ORIGIN = 0x80000000, LENGTH = 16k + ram (rwx) : ORIGIN = 0x80004000, LENGTH = 16k +} + +STACK_SIZE = 1k; /* needs to be calculated for your application */ +HEAP_SIZE = 0; /* needs to be calculated for your application */ + +SECTIONS +{ + .entry : ALIGN(0x10) + { + KEEP (*(SORT_NONE(.entry))) + . = ALIGN(0x10); + } > rom + + .text : ALIGN(0x10) + { + KEEP (*(SORT_NONE(.text.entry))) + . = ALIGN(0x10); + *(.text .text.* .gnu.linkonce.t.*) + *(.plt) + . = ALIGN(0x10); + + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*crtend.o(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*crtend.o(.dtors)) + + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.gcc_except_table) + *(.eh_frame_hdr) + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + . = ALIGN(0x10); + + } >rom + + /* short/global data section */ + .sdata : ALIGN(0x10) + { + __sdata_load = LOADADDR(.sdata); + __sdata_start = .; + PROVIDE( __global_pointer$ = . + 0x800); + *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) + *(.srodata*) + *(.sdata .sdata.* .gnu.linkonce.s.*) + . = ALIGN(0x10); + __sdata_end = .; + } >ram AT>rom + + /* data section */ + .data : ALIGN(0x10) + { + __data_load = LOADADDR(.data); + __data_start = .; + *(.got.plt) *(.got) + *(.shdata) + *(.data .data.* .gnu.linkonce.d.*) + . = ALIGN(0x10); + __data_end = .; + } >ram AT>rom + + /* sbss section */ + .sbss : ALIGN(0x10) + { + __sbss_start = .; + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + . = ALIGN(0x10); + __sbss_end = .; + } > ram + + /* sbss section */ + .bss : ALIGN(0x10) + { + __bss_start = .; + *(.shbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(0x10); + __bss_end = .; + } > ram + + /* End of uninitialized data segment */ + _end = .; + + .heap : ALIGN(0x10) + { + __heap_start = .; + . += HEAP_SIZE; + __heap_end = .; + . = ALIGN(0x10); + _heap_end = __heap_end; + } > ram + + .stack : ALIGN(0x10) + { + __stack_bottom = .; + . += STACK_SIZE; + __stack_top = .; + } > ram +} + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/platform_config/linker/miv-rv32-ram.ld b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/platform_config/linker/miv-rv32-ram.ld new file mode 100644 index 0000000..1aa2a82 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/platform_config/linker/miv-rv32-ram.ld @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright 2019-2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * file name : miv-rv32-ram.ld + * Mi-V soft processor linker script for creating a SoftConsole downloadable + * debug image executing in SRAM. + * + * This linker script assumes that a RAM is connected at on Mi-V soft processor + * memory space pointed by the reset vector address. + * + * NOTE : Modify the memory section address and the size according to your + * Libero design. + * For example: + * 1) If you want to download and step debug at a different RAM memory address in + * your design (For example TCM base address) than the one provided in this file. + * 2) The MIV_RV32, when used with MIV_ESS IP, provides ways to copy the executable + * HEX file from external Non-Volatile memory into the TCM at reset. In this + * case your executable must be linked to the TCM address. + * + * To know more about the memory map of the MIV_RV32 based Libero design, open + * the MIV_RV32 IP configurator and look for "Reset Vector Address" and the + * "Memory Map" tab. + * + */ + +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +MEMORY +{ + ram (rwx) : ORIGIN = 0x80000000, LENGTH = 32k +} + +STACK_SIZE = 2k; /* needs to be calculated for your application */ +HEAP_SIZE = 1k; /* needs to be calculated for your application */ + +SECTIONS +{ + .entry : ALIGN(0x10) + { + KEEP (*(SORT_NONE(.entry))) + . = ALIGN(0x10); + } > ram + + .text : ALIGN(0x10) + { + *(.text .text.* .gnu.linkonce.t.*) + *(.plt) + . = ALIGN(0x10); + + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*crtend.o(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*crtend.o(.dtors)) + + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.gcc_except_table) + *(.eh_frame_hdr) + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + . = ALIGN(0x10); + + } > ram + + /* short/global data section */ + .sdata : ALIGN(0x10) + { + __sdata_load = LOADADDR(.sdata); + __sdata_start = .; + PROVIDE( __global_pointer$ = . + 0x800); + *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) + *(.srodata*) + *(.sdata .sdata.* .gnu.linkonce.s.*) + . = ALIGN(0x10); + __sdata_end = .; + } > ram + + /* data section */ + .data : ALIGN(0x10) + { + __data_load = LOADADDR(.data); + __data_start = .; + *(.got.plt) *(.got) + *(.shdata) + *(.data .data.* .gnu.linkonce.d.*) + . = ALIGN(0x10); + __data_end = .; + } > ram + + /* sbss section */ + .sbss : ALIGN(0x10) + { + __sbss_start = .; + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + . = ALIGN(0x10); + __sbss_end = .; + } > ram + + /* sbss section */ + .bss : ALIGN(0x10) + { + __bss_start = .; + *(.shbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(0x10); + __bss_end = .; + } > ram + + /* End of uninitialized data segment */ + _end = .; + + .heap : ALIGN(0x10) + { + __heap_start = .; + . += HEAP_SIZE; + __heap_end = .; + . = ALIGN(0x10); + _heap_end = __heap_end; + } > ram + + .stack : ALIGN(0x10) + { + __stack_bottom = .; + . += STACK_SIZE; + __stack_top = .; + } > ram +} + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/platform_config/miv_rv32_hal_config/readme.md b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/platform_config/miv_rv32_hal_config/readme.md new file mode 100644 index 0000000..6724ed6 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/legacy-design/platform_config/miv_rv32_hal_config/readme.md @@ -0,0 +1,3 @@ +# readme + +Software configurations that may be required for MIV_RV32 HAL in future will be stored here. \ No newline at end of file diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/fpga_design/design_description/readme.md b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/fpga_design/design_description/readme.md new file mode 100644 index 0000000..bdfcb71 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/fpga_design/design_description/readme.md @@ -0,0 +1,3 @@ +# Design description + + The Libero generated design description will be stored here. diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/fpga_design_config/fpga_design_config.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/fpga_design_config/fpga_design_config.h new file mode 100644 index 0000000..a380d96 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/fpga_design_config/fpga_design_config.h @@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file sample_fpga_design_config.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Sample design configuration settings + * + */ + /*========================================================================*//** + @mainpage + Example file detailing how the fpga_design_config.h should be constructed + for the SoftConsole project targeted for Mi-V processors. + + @section intro_sec Introduction + The SoftConsole project targeted for Mi-V processors now have an improved + folder structure. Detailed description of the folder structure is available + at https://github.com/Mi-V-Soft-RISC-V/miv-rv32-documentation. + + The fpga_design_config.h must be stored as shown below + /boards//fpga_design_config.h + + Currently this file must be hand crafted when using the Mi-V Soft Processor. + In future, all the design and soft IP configurations will be automatically + generated from the Libero design description data. + + You can use this sample file as an example. + Rename this file from sample_fpga_design_config.h to fpga_design_config.h + and then customize it per your hardware design. + + @section Project configuration Instructions + 1. Change SYS_CLK_FREQ define to frequency of Mi-V Soft processor clock + 2 Add all the soft IP core BASE addresses + 3. Add the peripheral Core Interrupts to Mi-V Soft processor IRQ number + mappings + 4. Define MSCC_STDIO_UART_BASE_ADDR if you want a CoreUARTapb mapped to + STDIO + + **NOTE** + In the legacy folder structures, the file hw_config.h as was used at the + root of the project folder. This file is now depricated. + +*//*=========================================================================*/ + +#ifndef FPGA_DESIGN_CONFIG_H_ +#define FPGA_DESIGN_CONFIG_H_ + +/***************************************************************************//** + * Soft-processor clock definition + * This is the only clock brought over from the Mi-V Libero design. + */ +#ifndef SYS_CLK_FREQ +#define SYS_CLK_FREQ 50000000UL +#endif + +/***************************************************************************//** + * Peripheral base addresses. + * Format of define is: + * __BASE_ADDR + * The field is optional if there is only one instance of the core + * in the design + * MIV_ESS is an extended peripheral subsystem IP core with peripherals + * connections as defined below. + * The system can be further extended by attaching APB peripherals to the + * empty APB slots. + */ +#define MIV_ESS_PLIC_BASE_ADDR 0x70000000UL +#define COREUARTAPB0_BASE_ADDR 0x71000000UL +#define MIV_MTIMER_BASE_ADDR 0x72000000UL +#define MIV_ESS_APBSLOT3_BASE_ADDR 0x73000000UL +#define MIV_ESS_APBSLOT4_BASE_ADDR 0x74000000UL +#define COREGPIO_OUT_BASE_ADDR 0x75000000UL +#define CORESPI_BASE_ADDR 0x76000000UL +#define MIV_ESS_uDMA_BASE_ADDR 0x78000000UL +#define MIV_ESS_WDOG_BASE_ADDR 0x79000000UL +#define MIV_ESS_I2C_BASE_ADDR 0x7A000000UL +#define MIV_ESS_APBSLOTB_BASE_ADDR 0x7B000000UL +#define MIV_ESS_APBSLOTC_BASE_ADDR 0x7C000000UL +#define MIV_ESS_APBSLOTD_BASE_ADDR 0x7D000000UL +#define MIV_ESS_APBSLOTE_BASE_ADDR 0x7E000000UL +#define MIV_ESS_APBSLOTF_BASE_ADDR 0x7F000000UL + +/***************************************************************************//** + * Peripheral Interrupts are mapped to the corresponding Mi-V Soft processor + * interrupt in the Libero design. + * + * On the legacy RV32 cores, there can be up to 31 external interrupts (IRQ[30:0] + * pins). The legacy RV32 Soft processor external interrupts are defined in the + miv_rv32_plic.h + * + * These are of the form + * typedef enum +{ + NoInterrupt_IRQn = 0, + External_1_IRQn = 1, + External_2_IRQn = 2, + . + . + . + External_31_IRQn = 31 +} IRQn_Type; + + On the legacy RV32 processors, the PLIC identifies the interrupt and passes it + on to the processor core. The interrupt 0 is not used. The pin IRQ[0] should + map to External_1_IRQn likewise IRQ[30] should map to External_31_IRQn + +e.g + +#define TIMER0_IRQn External_30_IRQn +#define TIMER1_IRQn External_31_IRQn + + The MIV_RV32 soft processor has up to six optional system interrupts, MSYS_EI[n] + in addition to one EXT_IRQ. + The MIV_RV32 does not have an inbuilt PLIC and all the interrupts are directly + delivered to the processor core, hence unlike legacy RV32 cores, no interrupt + number mapping is necessary on MIV_RV32 core. + */ + +/**************************************************************************** + * Baud value to achieve a 115200 baud rate with system clock defined by + * SYS_CLK_FREQ. + * This value is calculated using the following equation: + * BAUD_VALUE = (CLOCK / (16 * BAUD_RATE)) - 1 + *****************************************************************************/ +#define BAUD_VALUE_115200 ((SYS_CLK_FREQ / (16 * 115200)) - 1) + +/****************************************************************************** + * Baud value to achieve a 57600 baud rate with system clock defined by + * SYS_CLK_FREQ. + * This value is calculated using the following equation: + * BAUD_VALUE = (CLOCK / (16 * BAUD_RATE)) - 1 + *****************************************************************************/ + #define BAUD_VALUE_57600 ((SYS_CLK_FREQ / (16 * 57600)) - 1) + +/***************************************************************************//** + * Define MSCC_STDIO_THRU_CORE_UART_APB in the project settings if you want the + * standard IOs to be redirected to a terminal via UART. + */ +#ifdef MSCC_STDIO_THRU_CORE_UART_APB +/* + * A base address mapping for the STDIO printf/scanf mapping to CortUARTapb + * must be provided if it is being used + * + * e.g. #define MSCC_STDIO_UART_BASE_ADDR COREUARTAPB1_BASE_ADDR + */ +#define MSCC_STDIO_UART_BASE_ADDR COREUARTAPB0_BASE_ADDR + +#ifndef MSCC_STDIO_UART_BASE_ADDR +#error MSCC_STDIO_UART_BASE_ADDR not defined- e.g. #define MSCC_STDIO_UART_BASE_ADDR COREUARTAPB1_BASE_ADDR +#endif + +#ifndef MSCC_STDIO_BAUD_VALUE +/* + * The MSCC_STDIO_BAUD_VALUE define should be set in your project's settings to + * specify the baud value used by the standard output CoreUARTapb instance for + * generating the UART's baud rate if you want a different baud rate from the + * default of 115200 baud + */ +#define MSCC_STDIO_BAUD_VALUE 115200 +#endif /*MSCC_STDIO_BAUD_VALUE*/ + +#endif /* end of MSCC_STDIO_THRU_CORE_UART_APB */ +/******************************************************************************* + * End of user edit section + */ +#endif /* FPGA_DESIGN_CONFIG_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/platform_config/driver_config/phy_sw_cfg.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/platform_config/driver_config/phy_sw_cfg.h new file mode 100644 index 0000000..6783c90 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/platform_config/driver_config/phy_sw_cfg.h @@ -0,0 +1,136 @@ +/***************************************************************************//** + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * @file core10gbasekr_phy_sw_cfg.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief PHY software configuration + * + */ + +#ifndef BOARDS_POLARFIRE_EVAL_KIT_PLATFORM_CONFIG_DRIVER_CONFIG_PHY_SW_CFG_H_ +#define BOARDS_POLARFIRE_EVAL_KIT_PLATFORM_CONFIG_DRIVER_CONFIG_PHY_SW_CFG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * Driver versioning macros. + */ +#define CORE_VENDOR "Microchip" +#define CORE_LIBRARY "Firmware" +#define CORE_NAME "Core10GBaseKR_PHY_Driver" +#define CORE_VERSION "x.x.x" + +/***************************************************************************//** + * Supported PHY models, used to control compile time inclusion of the + * associated PHY sub-drivers. + */ +#define CORE10GBASEKR_PHY +#define PF_XCVR_C10GB + +/***************************************************************************//** + * Define this macro to enable performance messages in the application. + */ +#undef C10GBKR_PERFORMANCE_MESSAGES + +/***************************************************************************//** + * User config options for overriding driver defaults of Core10GBaseKR_PHY + * + * These definitions can be overridden by defining the macro and assigning the + * desired value. + */ +#ifdef CORE10GBASEKR_PHY + +/* Main tap limits */ +#undef C10GBKR_LT_MAIN_TAP_MAX_LIMIT +#undef C10GBKR_LT_MAIN_TAP_MIN_LIMIT + +/* Post tap limits */ +#undef C10GBKR_LT_POST_TAP_MAX_LIMIT +#undef C10GBKR_LT_POST_TAP_MIN_LIMIT + +/* Pre tap limits */ +#undef C10GBKR_LT_PRE_TAP_MAX_LIMIT +#undef C10GBKR_LT_PRE_TAP_MIN_LIMIT + +/* Request to be sent to Link Partner + 0U => Preset + 1U => Initialize + */ +#undef C10GBKR_LT_INITIAL_REQUEST + +#undef C10GBKR_LT_INITIALIZE_MAIN_TAP +#undef C10GBKR_LT_INITIALIZE_POST_TAP +#undef C10GBKR_LT_INITIALIZE_PRE_TAP + +/***************************************************************************//** + Override XCVR configurations + */ +#undef PF_XCVR_C10GB_REG_VAL_SER_DRV_CTRL +#undef PF_XCVR_C10GB_REG_VAL_SER_DRV_DATA_CTRL +#undef PF_XCVR_C10GB_REG_VAL_SER_DRV_CTRL_SEL +#undef PF_XCVR_C10GB_REG_VAL_DES_DFE_CAL_CTRL_0 +#undef PF_XCVR_C10GB_REG_VAL_DES_DFE_CAL_CTRL_1 +#undef PF_XCVR_C10GB_REG_VAL_DES_DFE_CAL_CTRL_2 +#undef PF_XCVR_C10GB_REG_VAL_DES_DFE_CAL_CMD +#undef PF_XCVR_C10GB_REG_VAL_SER_RTL_CTRL +#undef PF_XCVR_C10GB_REG_VAL_DES_CDR_CTRL_2 +#undef PF_XCVR_C10GB_REG_VAL_DES_CDR_CTRL_3 +#undef PF_XCVR_C10GB_REG_VAL_DES_DFEEM_CTRL_1 +#undef PF_XCVR_C10GB_REG_VAL_DES_DFEEM_CTRL_2 +#undef PF_XCVR_C10GB_REG_VAL_DES_DFEEM_CTRL_3 +#undef PF_XCVR_C10GB_REG_VAL_DES_DFE_CTRL_2 +#undef PF_XCVR_C10GB_REG_VAL_DES_EM_CTRL_2 + +/***************************************************************************//** + Override Auto-Negotiation data rate configurations + */ +#undef PF_XCVR_C10GB_AN_CFG_DES_RXPLL_DIV +#undef PF_XCVR_C10GB_AN_CFG_PMA_DES_RSTPD_POWER_DOWN +#undef PF_XCVR_C10GB_AN_CFG_PMA_SERDES_RTL_CTRL +#undef PF_XCVR_C10GB_AN_CFG_PMA_DES_RTL_LOCK_CTRL +#undef PF_XCVR_C10GB_AN_CFG_PMA_DES_RSTPD_POWER_UP +#undef PF_XCVR_C10GB_AN_CFG_PCS_LRST_R0_RESET_ASSERT +#undef PF_XCVR_C10GB_AN_CFG_PCS_LRST_R0_RESET_DEASSERT + + +/***************************************************************************//** + Override Link Training data rate configurations + */ +#undef PF_XCVR_C10GB_LT_CFG_PMA_DES_RSTPD_POWER_DOWN +#undef PF_XCVR_C10GB_LT_CFG_PMA_DES_RSTPD_POWER_UP +#undef PF_XCVR_C10GB_LT_CFG_DES_RXPLL_DIV +#undef PF_XCVR_C10GB_LT_CFG_PMA_SERDES_RTL_CTRL +#undef PF_XCVR_C10GB_LT_CFG_PMA_DES_DFE_CAL_BYPASS +#undef PF_XCVR_C10GB_LT_CFG_PCS_LRST_R0_RESET_ASSERT +#undef PF_XCVR_C10GB_LT_CFG_PCS_LRST_R0_RESET_DEASSERT + +#endif /* CORE10GBASEKR_PHY */ + +#ifdef __cplusplus +} +#endif + + +#endif /* BOARDS_POLARFIRE_EVAL_KIT_PLATFORM_CONFIG_DRIVER_CONFIG_PHY_SW_CFG_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/platform_config/linker/miv-rv32-execute-in-place.ld b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/platform_config/linker/miv-rv32-execute-in-place.ld new file mode 100644 index 0000000..19df5fb --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/platform_config/linker/miv-rv32-execute-in-place.ld @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright 2019-2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * file name : miv-rv32-execute-in-place.ld + * Mi-V soft processor linker script for creating a SoftConsole downloadable + * image executing from a one address space whereas the data, sdata and stack + * sections are placed in another address space. This could be used in cases such + * as: + * 1) When using MIV_RV32, the reset vector points to the LSRAM at address + * 0x80000000 and the data, sdata, bss and stack sections are placed in the + * TCM region. + * + * 2) Executing from a Non Volatile memory. The actual memory will depend on + * the FPGA platform. For exameple, it could be the eNVM on SmartFusion2, + * Igloo2 or on-board non-volatile memory which supports code execution. + * + * NOTE: Modify the memory section addresses and the sizes according to your + * Libero design. + * + * To know more about the memory map of the MIV_RV32 based Libero design, open + * the MIV_RV32 IP configurator and look for "Reset Vector Address" and the + * "Memory Map" tab. + * + */ + +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + + +MEMORY +{ + rom (rx) : ORIGIN = 0x80000000, LENGTH = 16k + ram (rwx) : ORIGIN = 0x80004000, LENGTH = 16k +} + +STACK_SIZE = 1k; /* needs to be calculated for your application */ +HEAP_SIZE = 0; /* needs to be calculated for your application */ + +SECTIONS +{ + .entry : ALIGN(0x10) + { + KEEP (*(SORT_NONE(.entry))) + . = ALIGN(0x10); + } > rom + + .text : ALIGN(0x10) + { + KEEP (*(SORT_NONE(.text.entry))) + . = ALIGN(0x10); + *(.text .text.* .gnu.linkonce.t.*) + *(.plt) + . = ALIGN(0x10); + + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*crtend.o(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*crtend.o(.dtors)) + + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.gcc_except_table) + *(.eh_frame_hdr) + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + . = ALIGN(0x10); + + } >rom + + /* short/global data section */ + .sdata : ALIGN(0x10) + { + __sdata_load = LOADADDR(.sdata); + __sdata_start = .; + PROVIDE( __global_pointer$ = . + 0x800); + *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) + *(.srodata*) + *(.sdata .sdata.* .gnu.linkonce.s.*) + . = ALIGN(0x10); + __sdata_end = .; + } >ram AT>rom + + /* data section */ + .data : ALIGN(0x10) + { + __data_load = LOADADDR(.data); + __data_start = .; + *(.got.plt) *(.got) + *(.shdata) + *(.data .data.* .gnu.linkonce.d.*) + . = ALIGN(0x10); + __data_end = .; + } >ram AT>rom + + /* sbss section */ + .sbss : ALIGN(0x10) + { + __sbss_start = .; + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + . = ALIGN(0x10); + __sbss_end = .; + } > ram + + /* sbss section */ + .bss : ALIGN(0x10) + { + __bss_start = .; + *(.shbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(0x10); + __bss_end = .; + } > ram + + /* End of uninitialized data segment */ + _end = .; + + .heap : ALIGN(0x10) + { + __heap_start = .; + . += HEAP_SIZE; + __heap_end = .; + . = ALIGN(0x10); + _heap_end = __heap_end; + } > ram + + .stack : ALIGN(0x10) + { + __stack_bottom = .; + . += STACK_SIZE; + __stack_top = .; + } > ram +} + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/platform_config/linker/miv-rv32-ram.ld b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/platform_config/linker/miv-rv32-ram.ld new file mode 100644 index 0000000..1aa2a82 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/platform_config/linker/miv-rv32-ram.ld @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright 2019-2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * file name : miv-rv32-ram.ld + * Mi-V soft processor linker script for creating a SoftConsole downloadable + * debug image executing in SRAM. + * + * This linker script assumes that a RAM is connected at on Mi-V soft processor + * memory space pointed by the reset vector address. + * + * NOTE : Modify the memory section address and the size according to your + * Libero design. + * For example: + * 1) If you want to download and step debug at a different RAM memory address in + * your design (For example TCM base address) than the one provided in this file. + * 2) The MIV_RV32, when used with MIV_ESS IP, provides ways to copy the executable + * HEX file from external Non-Volatile memory into the TCM at reset. In this + * case your executable must be linked to the TCM address. + * + * To know more about the memory map of the MIV_RV32 based Libero design, open + * the MIV_RV32 IP configurator and look for "Reset Vector Address" and the + * "Memory Map" tab. + * + */ + +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +MEMORY +{ + ram (rwx) : ORIGIN = 0x80000000, LENGTH = 32k +} + +STACK_SIZE = 2k; /* needs to be calculated for your application */ +HEAP_SIZE = 1k; /* needs to be calculated for your application */ + +SECTIONS +{ + .entry : ALIGN(0x10) + { + KEEP (*(SORT_NONE(.entry))) + . = ALIGN(0x10); + } > ram + + .text : ALIGN(0x10) + { + *(.text .text.* .gnu.linkonce.t.*) + *(.plt) + . = ALIGN(0x10); + + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*crtend.o(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*crtend.o(.dtors)) + + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.gcc_except_table) + *(.eh_frame_hdr) + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + . = ALIGN(0x10); + + } > ram + + /* short/global data section */ + .sdata : ALIGN(0x10) + { + __sdata_load = LOADADDR(.sdata); + __sdata_start = .; + PROVIDE( __global_pointer$ = . + 0x800); + *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) + *(.srodata*) + *(.sdata .sdata.* .gnu.linkonce.s.*) + . = ALIGN(0x10); + __sdata_end = .; + } > ram + + /* data section */ + .data : ALIGN(0x10) + { + __data_load = LOADADDR(.data); + __data_start = .; + *(.got.plt) *(.got) + *(.shdata) + *(.data .data.* .gnu.linkonce.d.*) + . = ALIGN(0x10); + __data_end = .; + } > ram + + /* sbss section */ + .sbss : ALIGN(0x10) + { + __sbss_start = .; + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + . = ALIGN(0x10); + __sbss_end = .; + } > ram + + /* sbss section */ + .bss : ALIGN(0x10) + { + __bss_start = .; + *(.shbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(0x10); + __bss_end = .; + } > ram + + /* End of uninitialized data segment */ + _end = .; + + .heap : ALIGN(0x10) + { + __heap_start = .; + . += HEAP_SIZE; + __heap_end = .; + . = ALIGN(0x10); + _heap_end = __heap_end; + } > ram + + .stack : ALIGN(0x10) + { + __stack_bottom = .; + . += STACK_SIZE; + __stack_top = .; + } > ram +} + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/platform_config/linker/miv-rv32-tcm.ld b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/platform_config/linker/miv-rv32-tcm.ld new file mode 100644 index 0000000..2612ec8 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/platform_config/linker/miv-rv32-tcm.ld @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright 2019-2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * file name : miv-rv32-tcm.ld + * Mi-V soft processor linker script for creating a SoftConsole downloadable + * debug image executing in SRAM. + * + * Derived from miv_rv32_hal/miv-rv32-ram.ld + * Links to the MIV_RV32 processor TCM default address = 0x400000000. size = 32K. + * + * To know more about the memory map of the MIV_RV32 based Libero design, open + * the MIV_RV32 IP configurator and look for "Reset Vector Address" and the + * "Memory Map" tab. + * + */ + +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +MEMORY +{ + ram (rwx) : ORIGIN = 0x40000000, LENGTH = 32k +} + +STACK_SIZE = 2k; /* needs to be calculated for your application */ +HEAP_SIZE = 0k; /* needs to be calculated for your application */ + +SECTIONS +{ + .entry : ALIGN(0x10) + { + KEEP (*(SORT_NONE(.entry))) + . = ALIGN(0x10); + } > ram + + .text : ALIGN(0x10) + { + *(.text .text.* .gnu.linkonce.t.*) + *(.plt) + . = ALIGN(0x10); + + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*crtend.o(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*crtend.o(.dtors)) + + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.gcc_except_table) + *(.eh_frame_hdr) + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + . = ALIGN(0x10); + + } > ram + + /* short/global data section */ + .sdata : ALIGN(0x10) + { + __sdata_load = LOADADDR(.sdata); + __sdata_start = .; + PROVIDE( __global_pointer$ = . + 0x800); + *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) + *(.srodata*) + *(.sdata .sdata.* .gnu.linkonce.s.*) + . = ALIGN(0x10); + __sdata_end = .; + } > ram + + /* data section */ + .data : ALIGN(0x10) + { + __data_load = LOADADDR(.data); + __data_start = .; + *(.got.plt) *(.got) + *(.shdata) + *(.data .data.* .gnu.linkonce.d.*) + . = ALIGN(0x10); + __data_end = .; + } > ram + + /* sbss section */ + .sbss : ALIGN(0x10) + { + __sbss_start = .; + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + . = ALIGN(0x10); + __sbss_end = .; + } > ram + + /* sbss section */ + .bss : ALIGN(0x10) + { + __bss_start = .; + *(.shbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(0x10); + __bss_end = .; + } > ram + + /* End of uninitialized data segment */ + _end = .; + + .heap : ALIGN(0x10) + { + __heap_start = .; + . += HEAP_SIZE; + __heap_end = .; + . = ALIGN(0x10); + _heap_end = __heap_end; + } > ram + + .stack : ALIGN(0x10) + { + __stack_bottom = .; + . += STACK_SIZE; + __stack_top = .; + } > ram +} + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/platform_config/miv_rv32_hal_config/readme.md b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/platform_config/miv_rv32_hal_config/readme.md new file mode 100644 index 0000000..6724ed6 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/polarfire-eval-kit/miv-rv32-design/platform_config/miv_rv32_hal_config/readme.md @@ -0,0 +1,3 @@ +# readme + +Software configurations that may be required for MIV_RV32 HAL in future will be stored here. \ No newline at end of file diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/fpga_design/design_description/readme.md b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/fpga_design/design_description/readme.md new file mode 100644 index 0000000..bdfcb71 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/fpga_design/design_description/readme.md @@ -0,0 +1,3 @@ +# Design description + + The Libero generated design description will be stored here. diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/fpga_design_config/fpga_design_config.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/fpga_design_config/fpga_design_config.h new file mode 100644 index 0000000..63ff818 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/fpga_design_config/fpga_design_config.h @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file sample_fpga_design_config.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Sample design configuration settings + * + */ + /*========================================================================*//** + @mainpage + Example file detailing how the fpga_design_config.h should be constructed + for the SoftConsole project targeted for Mi-V processors. + + @section intro_sec Introduction + The SoftConsole project targeted for Mi-V processors now have an improved + folder structure. Detailed description of the folder structure is available + at https://github.com/Mi-V-Soft-RISC-V/miv-rv32-documentation. + + The fpga_design_config.h must be stored as shown below + /boards//fpga_design_config.h + + Currently this file must be hand crafted when using the Mi-V Soft Processor. + In future, all the design and soft IP configurations will be automatically + generated from the Libero design description data. + + You can use this sample file as an example. + Rename this file from sample_fpga_design_config.h to fpga_design_config.h + and then customize it per your hardware design. + + @section Project configuration Instructions + 1. Change SYS_CLK_FREQ define to frequency of Mi-V Soft processor clock + 2 Add all the soft IP core BASE addresses + 3. Add the peripheral Core Interrupts to Mi-V Soft processor IRQ number + mappings + 4. Define MSCC_STDIO_UART_BASE_ADDR if you want a CoreUARTapb mapped to + STDIO + + **NOTE** + In the legacy folder structures, the file hw_config.h as was used at the + root of the project folder. This file is now depricated. + +*//*=========================================================================*/ + +#ifndef FPGA_DESIGN_CONFIG_H_ +#define FPGA_DESIGN_CONFIG_H_ + +/***************************************************************************//** + * Soft-processor clock definition + * This is the only clock brought over from the Mi-V Libero design. + */ +#ifndef SYS_CLK_FREQ +#define SYS_CLK_FREQ 50000000UL +#endif + +/***************************************************************************//** + * Peripheral base addresses. + * Format of define is: + * __BASE_ADDR + * The field is optional if there is only one instance of the core + * in the design + * MIV_ESS is an extended peripheral subsystem IP core with peripherals + * connections as defined below. + * The system can be further extended by attaching APB peripherals to the + * empty APB slots. + */ +#define MIV_ESS_PLIC_BASE_ADDR 0x70000000UL +#define COREUARTAPB0_BASE_ADDR 0x71000000UL +#define MIV_MTIMER_BASE_ADDR 0x72000000UL +#define MIV_ESS_APBSLOT3_BASE_ADDR 0x73000000UL +#define MIV_ESS_APBSLOT4_BASE_ADDR 0x74000000UL +#define COREGPIO_OUT_BASE_ADDR 0x75000000UL +#define CORESPI_BASE_ADDR 0x76000000UL +#define MIV_ESS_uDMA_BASE_ADDR 0x78000000UL +#define MIV_ESS_WDOG_BASE_ADDR 0x79000000UL +#define MIV_ESS_I2C_BASE_ADDR 0x7A000000UL +#define MIV_ESS_APBSLOTB_BASE_ADDR 0x7B000000UL +#define MIV_ESS_APBSLOTC_BASE_ADDR 0x7C000000UL +#define MIV_ESS_APBSLOTD_BASE_ADDR 0x7D000000UL +#define MIV_ESS_APBSLOTE_BASE_ADDR 0x7E000000UL +#define MIV_ESS_APBSLOTF_BASE_ADDR 0x7F000000UL + +#define COREMMC_BASE_ADDR 0x60000000UL + +/***************************************************************************//** + * Peripheral Interrupts are mapped to the corresponding Mi-V Soft processor + * interrupt in the Libero design. + * + * On the legacy RV32 cores, there can be up to 31 external interrupts (IRQ[30:0] + * pins). The legacy RV32 Soft processor external interrupts are defined in the + miv_rv32_plic.h + * + * These are of the form + * typedef enum +{ + NoInterrupt_IRQn = 0, + External_1_IRQn = 1, + External_2_IRQn = 2, + . + . + . + External_31_IRQn = 31 +} IRQn_Type; + + On the legacy RV32 processors, the PLIC identifies the interrupt and passes it + on to the processor core. The interrupt 0 is not used. The pin IRQ[0] should + map to External_1_IRQn likewise IRQ[30] should map to External_31_IRQn + +e.g + +#define TIMER0_IRQn External_30_IRQn +#define TIMER1_IRQn External_31_IRQn + + The MIV_RV32 soft processor has up to six optional system interrupts, MSYS_EI[n] + in addition to one EXT_IRQ. + The MIV_RV32 does not have an inbuilt PLIC and all the interrupts are directly + delivered to the processor core, hence unlike legacy RV32 cores, no interrupt + number mapping is necessary on MIV_RV32 core. + */ + +/**************************************************************************** + * Baud value to achieve a 115200 baud rate with system clock defined by + * SYS_CLK_FREQ. + * This value is calculated using the following equation: + * BAUD_VALUE = (CLOCK / (16 * BAUD_RATE)) - 1 + *****************************************************************************/ +#define BAUD_VALUE_115200 ((SYS_CLK_FREQ / (16 * 115200)) - 1) + +/****************************************************************************** + * Baud value to achieve a 57600 baud rate with system clock defined by + * SYS_CLK_FREQ. + * This value is calculated using the following equation: + * BAUD_VALUE = (CLOCK / (16 * BAUD_RATE)) - 1 + *****************************************************************************/ + #define BAUD_VALUE_57600 ((SYS_CLK_FREQ / (16 * 57600)) - 1) + +/***************************************************************************//** + * Define MSCC_STDIO_THRU_CORE_UART_APB in the project settings if you want the + * standard IOs to be redirected to a terminal via UART. + */ +#ifdef MSCC_STDIO_THRU_CORE_UART_APB +/* + * A base address mapping for the STDIO printf/scanf mapping to CortUARTapb + * must be provided if it is being used + * + * e.g. #define MSCC_STDIO_UART_BASE_ADDR COREUARTAPB1_BASE_ADDR + */ +#define MSCC_STDIO_UART_BASE_ADDR COREUARTAPB0_BASE_ADDR + +#ifndef MSCC_STDIO_UART_BASE_ADDR +#error MSCC_STDIO_UART_BASE_ADDR not defined- e.g. #define MSCC_STDIO_UART_BASE_ADDR COREUARTAPB1_BASE_ADDR +#endif + +#ifndef MSCC_STDIO_BAUD_VALUE +/* + * The MSCC_STDIO_BAUD_VALUE define should be set in your project's settings to + * specify the baud value used by the standard output CoreUARTapb instance for + * generating the UART's baud rate if you want a different baud rate from the + * default of 115200 baud + */ +#define MSCC_STDIO_BAUD_VALUE 115200 +#endif /*MSCC_STDIO_BAUD_VALUE*/ + +#endif /* end of MSCC_STDIO_THRU_CORE_UART_APB */ +/******************************************************************************* + * End of user edit section + */ +#endif /* FPGA_DESIGN_CONFIG_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/platform_config/driver_config/README.md b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/platform_config/driver_config/README.md new file mode 100644 index 0000000..ba4fa2f --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/platform_config/driver_config/README.md @@ -0,0 +1 @@ +# Driver Config \ No newline at end of file diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/platform_config/linker/README.md b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/platform_config/linker/README.md new file mode 100644 index 0000000..37f9127 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/platform_config/linker/README.md @@ -0,0 +1 @@ +# readme \ No newline at end of file diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/platform_config/linker/miv-rv32-ram.ld b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/platform_config/linker/miv-rv32-ram.ld new file mode 100644 index 0000000..801b432 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/platform_config/linker/miv-rv32-ram.ld @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright 2019-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * file name : miv-rv32-ram.ld + * Mi-V soft processor linker script for creating a SoftConsole downloadable + * debug image executing in SRAM. + * + * This linker script assumes that a RAM is connected at on the Mi-V soft + * processor memory space. The start address and size of the memory space must + * be correct as per the Libero design. + * + * Supports MIV_RV32 as well as the legacy RV32 cores with appropriate memory + * section addresses as per your design. + * + */ + +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +MEMORY +{ + ram (rwx) : ORIGIN = 0x80000000, LENGTH = 32k +} + +RAM_START_ADDRESS = 0x80000000; /* Must be the same value MEMORY region ram ORIGIN above. */ +RAM_SIZE = 32k; /* Must be the same value MEMORY region ram LENGTH above. */ +STACK_SIZE = 2k; /* needs to be calculated for your application */ +HEAP_SIZE = 0k; /* needs to be calculated for your application */ + +SECTIONS +{ + .entry : ALIGN(0x10) + { + KEEP (*(SORT_NONE(.entry))) + . = ALIGN(0x10); + } > ram + + .text : ALIGN(0x10) + { + *(.text .text.* .gnu.linkonce.t.*) + *(.plt) + . = ALIGN(0x10); + + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*crtend.o(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*crtend.o(.dtors)) + + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.gcc_except_table) + *(.eh_frame_hdr) + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + . = ALIGN(0x10); + + } > ram + + /* short/global data section */ + .sdata : ALIGN(0x10) + { + __sdata_load = LOADADDR(.sdata); + __sdata_start = .; + PROVIDE( __global_pointer$ = . + 0x800); + *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) + *(.srodata*) + *(.sdata .sdata.* .gnu.linkonce.s.*) + . = ALIGN(0x10); + __sdata_end = .; + } > ram + + /* data section */ + .data : ALIGN(0x10) + { + __data_load = LOADADDR(.data); + __data_start = .; + *(.got.plt) *(.got) + *(.shdata) + *(.data .data.* .gnu.linkonce.d.*) + . = ALIGN(0x10); + __data_end = .; + } > ram + + /* sbss section */ + .sbss : ALIGN(0x10) + { + __sbss_start = .; + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + . = ALIGN(0x10); + __sbss_end = .; + } > ram + + /* sbss section */ + .bss : ALIGN(0x10) + { + __bss_start = .; + *(.shbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(0x10); + __bss_end = .; + } > ram + + /* End of uninitialized data segment */ + _end = .; + + .heap : ALIGN(0x10) + { + __heap_start = .; + . += HEAP_SIZE; + __heap_end = .; + . = ALIGN(0x10); + _heap_end = __heap_end; + } > ram + + .stack : ALIGN(0x10) + { + __stack_bottom = .; + . += STACK_SIZE; + __stack_top = .; + } > ram + +} + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/platform_config/miv_rv32_hal_config/readme.md b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/platform_config/miv_rv32_hal_config/readme.md new file mode 100644 index 0000000..6724ed6 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/boards/smart-fusion2-dev-kit/platform_config/miv_rv32_hal_config/readme.md @@ -0,0 +1,3 @@ +# readme + +Software configurations that may be required for MIV_RV32 HAL in future will be stored here. \ No newline at end of file diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/LICENSE.md b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/LICENSE.md new file mode 100644 index 0000000..f2e6956 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/LICENSE.md @@ -0,0 +1,26 @@ +# The Mi-V soft processor Platform Software License + +The Mi-V soft processor Platform Software is released under the following +software license: + + Copyright 2019 Microchip Corporation. + + SPDX-License-Identifier: MIT + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/README.md b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/README.md new file mode 100644 index 0000000..f7f6030 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/README.md @@ -0,0 +1,27 @@ +# Mi-V soft processor platform source code + +## Repo organization + +``` + + | + |-- drivers + | |- fpga_ip + | | | CoreGPIO + | | | CoreSysServices_PF + | | | CoreUARTapb + | | + | |- off_chip + | | | . + | | | . + | | + |-- hal + | | + |-- miv_rv32_hal + + +``` + +The drivers published here are compatible with the improved SoftConsole project folder structure being used in the latest [example projects](https://github.com/Mi-V-Soft-RISC-V/miv-rv32-bare-metal-examples). +These drivers can also be used with the legacy folder structure (projects released via Firmware Catalog) by defining the **LEGACY_DIR_STRUCTURE** macro in the SoftConsole project settings. + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/README.md b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/README.md new file mode 100644 index 0000000..8561920 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/README.md @@ -0,0 +1 @@ +# Core10GBASEKR_PHY Source \ No newline at end of file diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy.c new file mode 100644 index 0000000..595b4a6 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy.c @@ -0,0 +1,677 @@ +/** + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core10gbasekr_phy.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief Core10GBaseKR_PHY Source + * + */ + +#include +#include "core10gbasekr_phy.h" +#include "core10gbasekr_phy_link_training.h" + +/*------------------------Private Definitions---------------------------------*/ +#define AN_RESET (1U) +#define AN_ENABLE (1U) +#define AN_RESTART (1U) + +#define TX_CTRL_DATA (0U) +#define TX_CTRL_AN (2U) +#define TX_CTRL_LT (3U) + +#define COEFF_UPDATE_PRESET (1U << 13U) +#define COEFF_UPDATE_INITIALIZE (1U << 12U) + +#define MR_ADV_3_BIT_OFFSET (32U) +#define MR_ADV_FEC_ABILITY (46U) +#define MR_ADV_FEC_REQUESTED (47U) + +/*------------------------Public Variables------------------------------------*/ +extern uint32_t prbs_cnt; + +/*------------------------Private Function-------------------------------------*/ +/** + * This is a private function which is called to reconfigure the core with + * default conditions, reset states, counters and algorithm attributes. + */ +static uint32_t +phy_10gbasekr_reset(phy10gkr_instance_t *this_phy) +{ + /* set data path to AN block */ + HAL_set_32bit_reg_field(this_phy->tx_ctrl_base_addr, C10GB_TX_CTRL_PMA_DATA, TX_CTRL_AN); + + /* reset phy tx and rx */ + HAL_set_32bit_reg_field(this_phy->tx_ctrl_base_addr, C10GB_TX_CTRL_TX_RESET, 0x1); + HAL_set_32bit_reg_field(this_phy->tx_ctrl_base_addr, C10GB_TX_CTRL_RX_RESET, 0x1); + + /* set data path to AN block */ + HAL_set_32bit_reg_field(this_phy->tx_ctrl_base_addr, C10GB_TX_CTRL_PMA_DATA, TX_CTRL_AN); + + /* transmit equalization tap limit configurations */ + /* main coefficients (P0) */ + HAL_set_32bit_reg_field(this_phy->lt_base_addr, + C10GB_LT_MAX_MAIN_TAP, + this_phy->main_max_tap_ceoff); + HAL_set_32bit_reg_field(this_phy->lt_base_addr, + C10GB_LT_MIN_MAIN_TAP, + this_phy->main_min_tap_ceoff); + + if ((C10GBKR_LT_MAIN_TAP_MAX_LIMIT != this_phy->main_max_tap_ceoff)) + { + this_phy->main_preset_tap_coeff = this_phy->main_max_tap_ceoff; + } + + /* post coefficients (P1) */ + HAL_set_32bit_reg_field(this_phy->lt_base_addr, + C10GB_LT_MAX_POST_TAP, + this_phy->post_max_tap_ceoff); + HAL_set_32bit_reg_field(this_phy->lt_base_addr, + C10GB_LT_MIN_POST_TAP, + this_phy->post_min_tap_ceoff); + + if ((C10GBKR_LT_POST_TAP_MAX_LIMIT != this_phy->post_max_tap_ceoff)) + { + this_phy->post_preset_tap_coeff = this_phy->post_max_tap_ceoff; + } + + /* pre coefficients (P-1) */ + HAL_set_32bit_reg_field(this_phy->lt_base_addr, + C10GB_LT_MAX_PRE_TAP, + this_phy->pre_max_tap_ceoff); + HAL_set_32bit_reg_field(this_phy->lt_base_addr, + C10GB_LT_MIN_PRE_TAP, + this_phy->pre_min_tap_ceoff); + + if ((C10GBKR_LT_PRE_TAP_MAX_LIMIT != this_phy->pre_max_tap_ceoff)) + { + this_phy->pre_preset_tap_coeff = this_phy->pre_max_tap_ceoff; + } + + /* set core preset conditions */ + HAL_set_32bit_reg_field(this_phy->lt_base_addr, + C10GB_LT_PRESET_MAIN_TAP, + this_phy->main_preset_tap_coeff); + HAL_set_32bit_reg_field(this_phy->lt_base_addr, + C10GB_LT_PRESET_POST_TAP, + this_phy->post_preset_tap_coeff); + HAL_set_32bit_reg_field(this_phy->lt_base_addr, + C10GB_LT_PRESET_PRE_TAP, + this_phy->pre_preset_tap_coeff); + + /* set core initialize conditions */ + HAL_set_32bit_reg_field(this_phy->lt_base_addr, + C10GB_LT_INIT_MAIN_TAP, + this_phy->main_initialize_tap_coeff); + HAL_set_32bit_reg_field(this_phy->lt_base_addr, + C10GB_LT_INIT_POST_TAP, + this_phy->post_initialize_tap_coeff); + HAL_set_32bit_reg_field(this_phy->lt_base_addr, + C10GB_LT_INIT_PRE_TAP, + this_phy->pre_initialize_tap_coeff); + + /* track number of fails since begining */ + uint32_t tmp_fail_count = this_phy->lt.fail_cnt; + uint32_t tmp_complete_count = this_phy->lt.complete_cnt; + this_phy->lt = (phy10gkr_lt_instance_t){0}; + this_phy->lt.fail_cnt = tmp_fail_count; + this_phy->lt.complete_cnt = tmp_complete_count; + + /* Set AN and LT api state machine to init state */ + this_phy->an.api_state = AN_API_SM_INIT; + this_phy->lt.api_state = LT_API_SM_INIT; + + /* Set status to original condition */ + this_phy->an.status = STATUS_AN_INCOMPLETE; + this_phy->lt.status = STATUS_LT_INCOMPLETE; + + /* clear link training tap data */ + HAL_set_32bit_reg_field(this_phy->lt_base_addr, + C10GB_LT_LOCAL_RCVR_LOCKED, + LOCAL_RXCVR_UNLOCKED); + this_phy->lt.local_rxcvr = LOCAL_RXCVR_UNLOCKED; + + this_phy->lt.main = (const phy10gkr_coeff_update_t){0}; + this_phy->lt.post = (const phy10gkr_coeff_update_t){0}; + this_phy->lt.pre = (const phy10gkr_coeff_update_t){0}; + + this_phy->lt.lp_cal_sweep_state = MAIN_TAP; + + this_phy->lt.tx_equ_cnt = 0; + this_phy->lt.rx_cal_cnt = 0; + + prbs_cnt = 0; + + return 0; +} + +/** + * The private function enables the auto-negotiation state machine, this enables + * the auto-negotiation registers and then checks the status of the auto-negotiation + * state machine to determine if auto-negotiation has complete. + */ +static void +phy_10gbasekr_an(phy10gkr_instance_t *this_phy) +{ + switch (this_phy->an.api_state) + { + case AN_API_SM_INIT: + /* Reset auto-negotiation. */ + HAL_set_32bit_reg_field(this_phy->an_base_addr, C10GB_AN_RESET, AN_RESET); + + /* FEC configuration */ + this_phy->fec_negotiated = C10GBKR_FEC_NOT_NEGOTIATED; + ; + + uint32_t mr_adv_3 = + HAL_get_32bit_reg(this_phy->an_base_addr, C10GB_AN_MR_ADV_CAPABILITY_3); + + if (this_phy->fec_configured) + { + mr_adv_3 |= 1U << (MR_ADV_FEC_ABILITY - MR_ADV_3_BIT_OFFSET); + + if (this_phy->fec_request) + { + mr_adv_3 |= 1U << (MR_ADV_FEC_REQUESTED - MR_ADV_3_BIT_OFFSET); + } + } + else + { + /* clearing FEC bits */ + mr_adv_3 &= ~(1U << (MR_ADV_FEC_ABILITY - MR_ADV_3_BIT_OFFSET) | + 1U << (MR_ADV_FEC_REQUESTED - MR_ADV_3_BIT_OFFSET)); + } + HAL_set_32bit_reg(this_phy->an_base_addr, C10GB_AN_MR_ADV_CAPABILITY_3, mr_adv_3); + + /* Enable auto-negotiation. */ + HAL_set_32bit_reg_field(this_phy->an_base_addr, C10GB_AN_ENABLE, AN_ENABLE); + + /* Restart auto-negotiation */ + HAL_set_32bit_reg_field(this_phy->an_base_addr, C10GB_AN_RESTART, AN_RESTART); + + /* Tx control: Auto-negotiation, PCS data transmitted to Serdes */ + HAL_set_32bit_reg_field(this_phy->tx_ctrl_base_addr, + C10GB_TX_CTRL_PMA_DATA, + TX_CTRL_AN); + + /* set link fail inhibit timer time to 500 ms*/ + HAL_set_32bit_reg(this_phy->an_base_addr, + C10GB_AN_LINK_FAIL_INHIBIT_TIMER, + C10GBKR_AN_LINK_FAIL_INHITBIT_TIMER); + + this_phy->an.adv_ability = + (uint64_t)HAL_get_32bit_reg(this_phy->an_base_addr, C10GB_AN_MR_ADV_CAPABILITY_1) | + ((uint64_t)HAL_get_32bit_reg(this_phy->an_base_addr, C10GB_AN_MR_ADV_CAPABILITY_2) + << 16) | + ((uint64_t)HAL_get_32bit_reg(this_phy->an_base_addr, C10GB_AN_MR_ADV_CAPABILITY_3) + << 32); + + this_phy->an.status = STATUS_AN_INCOMPLETE; + this_phy->an.api_state = AN_API_SM_STATUS_UPDATE; + break; + + case AN_API_SM_STATUS_UPDATE: + /* Get auto-negotiation state machine state */ + this_phy->an.state = HAL_get_32bit_reg_field(this_phy->an_base_addr, C10GB_AN_STATE); + if (ST_AN_GOOD_CHECK == this_phy->an.state) + { + this_phy->an.complete_cnt++; + + this_phy->an.status = STATUS_AN_COMPLETE; + + this_phy->an.lp_bp_adv_ability = + (uint64_t)HAL_get_32bit_reg(this_phy->an_base_addr, + C10GB_AN_MR_LP_BASE_PG_CAPABILITY_1) | + ((uint64_t)HAL_get_32bit_reg(this_phy->an_base_addr, + C10GB_AN_MR_LP_BASE_PG_CAPABILITY_2) + << 16) | + ((uint64_t)HAL_get_32bit_reg(this_phy->an_base_addr, + C10GB_AN_MR_LP_BASE_PG_CAPABILITY_3) + << 32); + + /* Set data path to LT block, enable LT and timers */ + HAL_set_32bit_reg_field(this_phy->lt_base_addr, + C10GB_LT_MAX_WAIT_TIMER, + MAX_WAIT_TIMER_500MS); + + HAL_set_32bit_reg_field(this_phy->tx_ctrl_base_addr, + C10GB_TX_CTRL_PMA_DATA, + TX_CTRL_LT); + + /* Have both the local device and link partner advertised FEC ability? */ + if ((this_phy->fec_configured) && + (this_phy->an.lp_bp_adv_ability & (uint64_t)1U << (uint64_t)MR_ADV_FEC_ABILITY)) + { + /* Has either device requested FEC? */ + if ((this_phy->an.adv_ability & (uint64_t)1U + << (uint64_t)MR_ADV_FEC_REQUESTED) || + (this_phy->an.lp_bp_adv_ability & (uint64_t)1U + << (uint64_t)MR_ADV_FEC_REQUESTED)) + { + this_phy->fec_negotiated = C10GBKR_FEC_NEGOTIATED; + } + } + + this_phy->lt.timer.start = PHY10GKR_get_current_time_ms(); + } + break; + + default: + HAL_ASSERT(0); + break; + } +} + +/** + * This private function enables the link training state machine, this enables the + * link training registers and then runs the link training algorithm. + * + * The connected transceiver must have a data rate of 10 Gbps and locked to a + * link partner with the same data rate for successful link training. + */ +void +phy_10gbasekr_lt(phy10gkr_instance_t *this_phy) +{ + uint32_t c10gbkr_status = 0; + + this_phy->lt.state = HAL_get_32bit_reg_field(this_phy->lt_base_addr, C10GB_LT_TRAINING_SM); + + switch (this_phy->lt.api_state) + { + case LT_API_SM_INIT: + if (C10GBKR_LT_PRESET == this_phy->rx_calibration_request) + { + HAL_set_32bit_reg(this_phy->lt_base_addr, + C10GB_LT_CTRL, + (C10GB_LT_RESTART_EN_MASK | C10GB_LT_PRESET_MASK)); + } + else if (C10GBKR_LT_INITALISE == this_phy->rx_calibration_request) + { + HAL_set_32bit_reg(this_phy->lt_base_addr, + C10GB_LT_CTRL, + (C10GB_LT_RESTART_EN_MASK | C10GB_LT_INIT_MASK)); + } + + this_phy->lt.api_state = LT_API_SM_STATUS_UPDATE; + break; + + case LT_API_SM_STATUS_UPDATE: + c10gbkr_status = HAL_get_32bit_reg(this_phy->lt_base_addr, C10GB_LT_STATUS); + + this_phy->lt.timer.end = PHY10GKR_get_current_time_ms() - this_phy->lt.timer.start; + + if ((c10gbkr_status & C10GB_LT_TRAINING_FAIL_MASK) || + this_phy->lt.timer.end > LT_SOFTWARE_WAIT_TIMER_MS) + { + this_phy->lt.fail_cnt++; + this_phy->lt.status = STATUS_LT_FAILURE; + + /* disable lt hardware in the case that the software timer + * trigger the failure + */ + HAL_set_32bit_reg_field(this_phy->lt_base_addr, C10GB_LT_RESTART_EN, 0x0); + break; + } + + if (c10gbkr_status & C10GB_LT_REQ_TX_EQUAL_MASK) + { + uint32_t tx_main_tap; + uint32_t tx_post_tap; + uint32_t tx_pre_tap; + + uint32_t rcvd_coeff; + + this_phy->lt.tx_equ_cnt++; + + rcvd_coeff = + HAL_get_32bit_reg_field(this_phy->lt_base_addr, C10GB_LT_NEW_RCVD_COEFF); + + if (rcvd_coeff & (COEFF_UPDATE_INITIALIZE | COEFF_UPDATE_PRESET)) + { + if (rcvd_coeff & COEFF_UPDATE_PRESET) + { + tx_main_tap = this_phy->main_preset_tap_coeff; + tx_post_tap = + this_phy->post_preset_tap_coeff - this_phy->post_max_tap_ceoff; + tx_pre_tap = this_phy->pre_preset_tap_coeff - this_phy->pre_max_tap_ceoff; + } + else + { + tx_main_tap = this_phy->main_initialize_tap_coeff; + tx_post_tap = + this_phy->post_initialize_tap_coeff - this_phy->post_max_tap_ceoff; + tx_pre_tap = + this_phy->pre_initialize_tap_coeff - this_phy->pre_max_tap_ceoff; + } + } + else + { + tx_main_tap = + HAL_get_32bit_reg_field(this_phy->lt_base_addr, C10GB_LT_TX_NEW_MAIN_TAP); + tx_post_tap = + HAL_get_32bit_reg_field(this_phy->lt_base_addr, C10GB_LT_TX_NEW_POST_TAP) - + this_phy->post_max_tap_ceoff; + tx_pre_tap = + HAL_get_32bit_reg_field(this_phy->lt_base_addr, C10GB_LT_TX_NEW_PRE_TAP) - + this_phy->pre_max_tap_ceoff; + } + + this_phy->xcvr_api.tx_equalization(this_phy->xcvr, + tx_main_tap, + tx_post_tap, + tx_pre_tap); + + /* signal to hardware to set tx status report field */ + HAL_set_32bit_reg( + this_phy->lt_base_addr, + C10GB_LT_TX_EQUAL, + C10GB_LT_TX_EQUAL_PRE_DONE_MASK | C10GB_LT_TX_EQUAL_POST_DONE_MASK | + C10GB_LT_TX_EQUAL_MAIN_DONE_MASK | C10GB_LT_TX_EQUAL_DONE_MASK); + } + + if ((c10gbkr_status & C10GB_LT_REQ_RX_CAL_MASK) && + LOCAL_RXCVR_UNLOCKED == this_phy->lt.local_rxcvr) + { + /* signal to hardware to set tx coefficient update field */ + HAL_set_32bit_reg_field(this_phy->lt_base_addr, C10GB_LT_RX_CAL_DONE, 1U); + + HAL_set_32bit_reg_field(this_phy->lt_base_addr, C10GB_LT_RX_CAL_DONE, 0U); + + this_phy->lt.rx_cal_cnt++; + if (C10GBKR_LT_PRESET == this_phy->rx_calibration_request) + { + link_partner_calibration_sm(this_phy); + } + else if (C10GBKR_LT_INITALISE == this_phy->rx_calibration_request) + { + /* check status report */ + + this_phy->lt.local_rxcvr = LOCAL_RXCVR_LOCKED; + + this_phy->lt.rcvr_cnt++; + + HAL_set_32bit_reg_field(this_phy->lt_base_addr, + C10GB_LT_LOCAL_RCVR_LOCKED, + LOCAL_RXCVR_LOCKED); + } + } + + if (c10gbkr_status & C10GB_LT_SIGNAL_DETECT_MASK) + { + this_phy->lt.complete_cnt++; + + /* Tx control: CL49 Data, PCS data */ + HAL_set_32bit_reg_field(this_phy->tx_ctrl_base_addr, + C10GB_TX_CTRL_PMA_DATA, + TX_CTRL_DATA); + this_phy->lt.sig_cnt++; + this_phy->lt.status = STATUS_LT_COMPLETE; + break; + } + break; + + default: + HAL_ASSERT(0); + break; + } + this_phy->lt.sm_cycle_cnt++; +} + +/*------------------------Public Function-------------------------------------*/ +/** + * Please see core10gbasekr_phy.h for description + */ +void +PHY10GKR_cfg_struct_def_init(phy10gkr_cfg_t *cfg) +{ + phy10gkr_xcvr_api_t xcvr_api = {NULL}; + + cfg->fec_request = C10GBKR_DISABLE_FEC_REQUEST; + cfg->rx_calibration_request = C10GBKR_LT_PRESET; + + cfg->xcvr_api = xcvr_api; + + cfg->main_max_tap_ceoff = C10GBKR_LT_MAIN_TAP_MAX_LIMIT; + cfg->main_min_tap_ceoff = C10GBKR_LT_MAIN_TAP_MIN_LIMIT; + cfg->post_max_tap_ceoff = C10GBKR_LT_POST_TAP_MAX_LIMIT; + cfg->post_min_tap_ceoff = C10GBKR_LT_POST_TAP_MIN_LIMIT; + cfg->pre_max_tap_ceoff = C10GBKR_LT_PRE_TAP_MAX_LIMIT; + cfg->pre_min_tap_ceoff = C10GBKR_LT_PRE_TAP_MIN_LIMIT; + + cfg->main_preset_tap_coeff = C10GBKR_LT_PRESET_MAIN_TAP; + cfg->post_preset_tap_coeff = C10GBKR_LT_PRESET_POST_TAP; + cfg->pre_preset_tap_coeff = C10GBKR_LT_PRESET_PRE_TAP; + + cfg->main_initialize_tap_coeff = C10GBKR_LT_INITIALIZE_MAIN_TAP; + cfg->post_initialize_tap_coeff = C10GBKR_LT_INITIALIZE_POST_TAP; + cfg->pre_initialize_tap_coeff = C10GBKR_LT_INITIALIZE_PRE_TAP; +} + +/** + * Please see core10gbasekr_phy.h for description + */ +uint32_t +PHY10GKR_init(phy10gkr_instance_t *this_phy, addr_t base_addr, phy10gkr_cfg_t *cfg, void *xcvr) +{ + /* Set Core10GBaseKR PHY Base addresses */ + this_phy->base_addr = base_addr; + this_phy->an_base_addr = this_phy->base_addr | C10GB_AN_BASE_OFFSET; + this_phy->lt_base_addr = this_phy->base_addr | C10GB_LT_BASE_OFFSET; + this_phy->tx_ctrl_base_addr = this_phy->base_addr | C10GB_TX_CTRL_BASE_OFFSET; + this_phy->rx_status_base_addr = this_phy->base_addr | C10GB_RX_STATUS_BASE_OFFSET; + + /* User config error handling */ + if ((NULL == cfg)) + { + HAL_ASSERT(0); + return PHY10GKR_ERR_USER_CONFIG; /* Release build error */ + } + + if (NULL == xcvr) + { + HAL_ASSERT(0); + return PHY10GKR_ERR_NO_XCVR; /* Release build error */ + } + + /* setting configurations */ + this_phy->xcvr = xcvr; + this_phy->xcvr_api = cfg->xcvr_api; + + if (this_phy->xcvr_api.init == NULL || this_phy->xcvr_api.auto_neg_data_rate == NULL || + this_phy->xcvr_api.link_training_data_rate == NULL || this_phy->xcvr_api.cdr_lock == NULL || + this_phy->xcvr_api.ctle_cal == NULL || this_phy->xcvr_api.ctle_cal_status == NULL || + this_phy->xcvr_api.dfe_cal == NULL || this_phy->xcvr_api.dfe_cal_status == NULL || + this_phy->xcvr_api.reset_pcs_rx == NULL || this_phy->xcvr_api.tx_equalization == NULL) + { + /* function pointer assignment error */ + HAL_ASSERT(0); + return PHY10GKR_ERR_XCVR_API_FUNCTION_POINTER; /* Release build error */ + } + + /* Check if FEC configured in the IP, advertise ability if it exists */ + if (HAL_get_32bit_reg_field(this_phy->tx_ctrl_base_addr, C10GB_FEC_USE_CFG) != 0) + { + this_phy->fec_configured = C10GBKR_FEC_ABILITY; + } + else + { + this_phy->fec_configured = C10GBKR_NO_FEC_ABILITY; + } + + this_phy->fec_request = cfg->fec_request; + this_phy->rx_calibration_request = cfg->rx_calibration_request; + + this_phy->main_max_tap_ceoff = cfg->main_max_tap_ceoff; + this_phy->main_min_tap_ceoff = cfg->main_min_tap_ceoff; + this_phy->post_max_tap_ceoff = cfg->post_max_tap_ceoff; + this_phy->post_min_tap_ceoff = cfg->post_min_tap_ceoff; + this_phy->pre_max_tap_ceoff = cfg->pre_max_tap_ceoff; + this_phy->pre_min_tap_ceoff = cfg->pre_min_tap_ceoff; + + this_phy->main_preset_tap_coeff = cfg->main_preset_tap_coeff; + this_phy->post_preset_tap_coeff = cfg->post_preset_tap_coeff; + this_phy->pre_preset_tap_coeff = cfg->pre_preset_tap_coeff; + + this_phy->main_initialize_tap_coeff = cfg->main_initialize_tap_coeff; + this_phy->post_initialize_tap_coeff = cfg->post_initialize_tap_coeff; + this_phy->pre_initialize_tap_coeff = cfg->pre_initialize_tap_coeff; + + phy_10gbasekr_reset(this_phy); + + this_phy->xcvr_api.init(this_phy->xcvr); + + return 0; +} + +/** + * Please see core10gbasekr_phy.h for description + */ +uint32_t +PHY10GKR_10gbasekr_sm(phy10gkr_instance_t *this_phy) +{ + if (this_phy == NULL) + { + HAL_ASSERT(0); + } + + switch (this_phy->c10gbkr_state) + { + case AN_SERDES_CONFIG: + this_phy->c10gbkr_status = AN_SERDES_CONFIGURATION; + phy_10gbasekr_reset(this_phy); + HAL_set_32bit_reg_field(this_phy->tx_ctrl_base_addr, + C10GB_TX_CTRL_XCVR_LOS, + LANE_LOS_LOCK_TO_REF); + this_phy->xcvr_api.auto_neg_data_rate(this_phy->xcvr); + this_phy->c10gbkr_state = AN_SM; + break; + + case AN_SM: + this_phy->c10gbkr_status = AN_IN_PROGRESS; + + phy_10gbasekr_an(this_phy); + if (STATUS_AN_COMPLETE == this_phy->an.status) + { + this_phy->c10gbkr_status = AN_COMPLETE; + + this_phy->c10gbkr_state = LT_SERDES_CONFIG; + } + break; + + case LT_SERDES_CONFIG: + this_phy->c10gbkr_status = LT_SERDES_CONFIGURATION; + HAL_set_32bit_reg_field(this_phy->tx_ctrl_base_addr, + C10GB_TX_CTRL_XCVR_LOS, + LANE_LOS_LOCK_TO_DATA); + this_phy->xcvr_api.link_training_data_rate(this_phy->xcvr); + + while (this_phy->xcvr_api.cdr_lock(this_phy->xcvr) != 0) + { + this_phy->lt.timer.end = PHY10GKR_get_current_time_ms() - this_phy->lt.timer.start; + + /* determine if hardware or software timed out */ + if (this_phy->lt.timer.end >= LT_SOFTWARE_WAIT_TIMER_MS || + HAL_get_32bit_reg_field(this_phy->lt_base_addr, C10GB_LT_TRAINING_FAIL) == 1) + { + this_phy->c10gbkr_status = LT_SERDES_CAL_FAILURE; + break; + } + } + + if (this_phy->c10gbkr_status != LT_SERDES_CAL_FAILURE) + { + this_phy->xcvr_api.ctle_cal(this_phy->xcvr); + + while (this_phy->xcvr_api.ctle_cal_status(this_phy->xcvr) != 0) + { + this_phy->lt.timer.end = + PHY10GKR_get_current_time_ms() - this_phy->lt.timer.start; + if (this_phy->lt.timer.end >= LT_SOFTWARE_WAIT_TIMER_MS || + HAL_get_32bit_reg_field(this_phy->lt_base_addr, C10GB_LT_TRAINING_FAIL) == + 1) + { + this_phy->c10gbkr_status = LT_SERDES_CAL_FAILURE; + break; + } + } + } + + if (this_phy->c10gbkr_status != LT_SERDES_CAL_FAILURE) + { + this_phy->c10gbkr_status = LT_SERDES_CAL_COMPLETE; + this_phy->c10gbkr_state = LT_SM; + } + else + { + this_phy->c10gbkr_state = AN_SERDES_CONFIG; + } + + break; + + case LT_SM: + this_phy->c10gbkr_status = LT_IN_PROGRESS; + phy_10gbasekr_lt(this_phy); + + if (STATUS_LT_FAILURE == this_phy->lt.status) + { + /* reset the state machine for next time its called */ + this_phy->c10gbkr_state = AN_SERDES_CONFIG; + + this_phy->c10gbkr_status = LT_FAILURE; + } + else if (STATUS_LT_COMPLETE == this_phy->lt.status) + { + this_phy->c10gbkr_state = LINK_ESTABLISHED_CHECK; + this_phy->c10gbkr_status = LINK_ESTABLISHED; + } + break; + + case LINK_ESTABLISHED_CHECK: + if (this_phy->xcvr_api.cdr_lock(this_phy->xcvr) != 0) + { + /* reset the state machine for next time its called */ + this_phy->c10gbkr_state = AN_SERDES_CONFIG; + + this_phy->c10gbkr_status = LINK_BROKEN; + } + else + { + this_phy->c10gbkr_status = LINK_ESTABLISHED; + } + break; + + default: + HAL_ASSERT(0); + break; + } + return this_phy->c10gbkr_status; +} + +uint8_t +PHY10GKR_get_ip_version(phy10gkr_instance_t *this_phy, + uint32_t *major, + uint32_t *minor, + uint32_t *sub) +{ + if (this_phy == NULL) + { + HAL_ASSERT(0); + return 1; /* release build error */ + } + + *major = HAL_get_32bit_reg_field(this_phy->tx_ctrl_base_addr, C10GB_IP_VERSION_MAJOR); + *minor = HAL_get_32bit_reg_field(this_phy->tx_ctrl_base_addr, C10GB_IP_VERSION_MINOR); + *sub = HAL_get_32bit_reg_field(this_phy->tx_ctrl_base_addr, C10GB_IP_VERSION_SUB); + return 0; +} + +uint8_t +PHY10GKR_get_driver_version(uint32_t *major, uint32_t *minor, uint32_t *patch) +{ + *major = C10GBKR_VERSION_MAJOR; + *minor = C10GBKR_VERSION_MINOR; + *patch = C10GBKR_VERSION_PATCH; + return 0; +} diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy.h new file mode 100644 index 0000000..8bde4bb --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy.h @@ -0,0 +1,515 @@ +/** + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core10gbasekr_phy.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Core10GBaseKR_PHY prototypes + * + */ + +/** + * @mainpage Core10GBaseKR_PHY Bare Metal Driver + * + * ============================================================================== + * Introduction + * ============================================================================== + * Core10GBaseKR_PHY is designed for the IEEE® 802.3-2012 specification and + * supports the Core10GBaseKR_PHY interface for Backplane operations. This + * configurable core provides the Physical (PHY) layer when used with a + * transceiver interface. This IP interfaces with the Ten Gigabit Media + * Independent Interface (XGMII) compliant Media Access Control (MAC) at the + * system side and the transceiver block at the line side. The physical layer is + * designed to work seamlessly with the PolarFire® and PolarFire SoC transceiver + * using the Physical Medium Attachment (PMA) mode. This user guide documents the + * features provided by the Core10GBaseKR_PHY embedded software driver. + * + * ============================================================================== + * Hardware Flow Dependencies + * ============================================================================== + * This driver covers the configuration details of features such as the clauses 72 + * (Link Training), 73 (Auto-negotiation), and 74 (Forward Error Correction) of + * IEEE802.3. + * + * See the [Core10GBaseKR_PHY User + * Guide](https://mi-v-ecosystem.github.io/redirects/miv-rv32-ip-user-guide-core10gbasekr_phy) for a + * detailed description of design requirements when interfacing the Core10GBaseKR_PHY to a + * transceiver. + * + * ============================================================================== + * Theory of Operation + * ============================================================================== + * The Core10GBaseKR_PHY driver functions are grouped into the following + * categories: + * - Configuration + * - Initialization + * - 10GBASE-KR + * - Clause74: Forward Error Correction (FEC) + * - Clause73: Auto-negotiation + * - Clause72: Link training + * + * -------------------------------- + * Configuration + * -------------------------------- + * The Core10GBaseKR_PHY driver requires an instance of a PHY configuration + * phy10gkr_cfg_t to be initialised. To load phy10gkr_cfg_t with default + * configurations, this configuration instance is passed by reference to + * PHY10GKR_cfg_struct_def_init(). This configuration structure may be overridden + * with alternative configurations before initialising the driver. + * + * The application must point to an instance of the Transceiver (XCVR) per instance + * of a phy10gkr_instance_t. The phy10gkr_instance_t structure members xcvr and + * xcvr_api must point to the XCVR that the Core10GBaseKR_PHY IP is interfacing with + * and the appropriate XCVR APIs for dynamic configuration. + * + * -------------------------------- + * Initialization + * -------------------------------- + * The Core10GBaseKR_PHY driver is initialized through a call to PHY10GKR_init(). + * The PHY10GKR_init() function must be called before calling any other + * Core10GBaseKR_PHY driver functions. + * + * -------------------------------- + * 10GBASE-KR + * -------------------------------- + * The full 10GBASE-KR flow is handled by calling PHY10GKR_10gbasekr_sm(). As + * this function is dependent on interacting with a transceiver, transceiver + * function pointers must be configured by the user to point to the transceiver + * APIs that dynamically configure the transceiver implemented in the hardware + * design. + * + * Each time PHY10GKR_10gbasekr_sm() is in the AN_SERDES_CONFIG state, the + * private function phy_10gbasekr_reset(), is called, which will load any user + * configurations to the Core registers and resets all algorithm and debug counters. + * + * This API uses two private function calls, phy_10gbasekr_an() and phy_10gbasekr_lt(), + * to enable and interact with clause 72 and 73 hardware blocks within the + * Core10GBaseKR_PHY IP. + * + * -------------------------------- + * Clause74: FEC + * -------------------------------- + * To enable FEC, FEC must be configured in the Core10GBaseKR_PHY IP. See the + * [Core10GBaseKR_PHY User + * Guide](https://mi-v-ecosystem.github.io/redirects/miv-rv32-ip-user-guide-core10gbasekr_phy) for a + * detailed description of how to enable FEC logic using the IP configurator. + * + * During auto-negotiation initialization, if the FEC block has been configured + * in the Core, the driver will set the FEC ability bit 46 in the advertisement + * ability DME page. + * + * The fec_request member within the phy10gkr_cfg_t structure may be set prior + * to driver initialisation to indicate that the driver should set the + * FEC request bit 47 in the advertisement ability clause 73 DME page. The driver + * implements error checking which identifies if fec_request is set when FEC is + * not configured within the Core. + * + * -------------------------------- + * Clause73: Auto-negotiation + * -------------------------------- + * The IEEE802.3 clause 73 auto-negotiation is enabled and executed by the private + * function, phy_10gbasekr_an(). PHY10GKR_10gbasekr() uses this private function + * enable auto-negotiation and determines when the hardware has reached the + * AN_GOOD_CHECK state. + * + * -------------------------------- + * Clause72: Link Training + * -------------------------------- + * The IEEE802.3 clause 72 link training is enabled and executed by the private + * function phy_10gbasekr_lt(). The Core10GBaseKR_PHY IP and the + * Core10GBaseKR_PHY embedded software driver together carry out the link + * training. The driver initiates the link training and takes appropriate actions + * depending on the events indicated by the 10GBaseKR status register bits during + * the link training process. + * + * Training Failure: The training failure bit is set by the IP when the + * Core10GBaseKR_PHY link training timer exceeds 500 ms. The driver also + * implements a soft timer as an additional protection layer. PHY10GKR_get_current_time_ms() + * must be overridden by instantiating this function in user code, so that the + * current time of a timer will be returned in milli-seconds. When this status + * is set by Core10GBaseKR_PHY, the embedded software must reduce the XCVR data rate + * by calling PHY10GKR_serdes_an_config() and restart the auto-negotiation state + * machine by calling phy_10gbasekr_an(). + * + * Rx Calibration: The IP sets this status bit to indicate that there is a + * received status report of Max/Min/Updated that the Rx calibration algorithm + * must handle. The maximum to minimum sweep algorithm described in the + * [Core10GBaseKR_PHY User + * Guide](https://mi-v-ecosystem.github.io/redirects/miv-rv32-ip-user-guide-core10gbasekr_phy) is + * implemented by the functions which are defined within core10gbasekr_phy_link_training.h. + * + * After the Rx calibration algorithm completes, the driver updates the + * transmit coefficient with new transmitter tap, which is sent to the + * link partner. + * + * Tx Equalization: This status bit indicates that the received coefficient + * update has been updated and that the firmware needs to update the transceiver + * transmitter taps. The driver hands off the new coefficient settings to the + * transceiver using the PHY10GKR_serdes_tx_equalization() weak function, + * which must be overridden. + * + * Signal Detect: When the driver identifies that this bit has been set by the + * Core10GBaseKR_PHY IP, it sets the link training complete flag. The IP updates + * this status bit when both the transmitted and received status reports have the + * receiver ready bit set. This indicates that both devices have completed their + * Rx calibration algorithm. + */ +#ifndef CORE10GBASEKR_PHY_H_ +#define CORE10GBASEKR_PHY_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "core10gbasekr_phy_types.h" +#include "core10gbasekr_phy_reg.h" + +/** + * Hardware and software timer timeouts. + */ +#define MAX_WAIT_TIMER_500MS (161000000U) +#define LT_SOFTWARE_WAIT_TIMER_MS (500U) + +/** + * CORE10GBASEKR_PHY VERSION TAGS + * ============================ + * The version tags define the major, minor and patch driver release tags + */ +#define C10GBKR_VERSION_MAJOR (3U) +#define C10GBKR_VERSION_MINOR (0U) +#define C10GBKR_VERSION_PATCH (107U) + +/** + * CORE10GBASEKR_PHY FEC ABILITY + * ============================ + * This defines that the auto-negotiation advertisement FEC ability bit should be set + * as the IP is configured with FEC logic. + */ +#define C10GBKR_FEC_ABILITY (1U) + +/** + * CORE10GBASEKR_PHY NO FEC ABILITY + * ============================ + * This defines that the auto-negotiation advertisement FEC ability bit should be + * cleared as the IP is not configured with FEC logic. + */ +#define C10GBKR_NO_FEC_ABILITY (0U) + +/** + * CORE10GBASEKR_PHY ENABLE FEC REQUEST + * ============================ + * This defines that the auto-negotiation advertisement FEC request bit should be set + * if the IP is configured with the FEC logic. + */ +#define C10GBKR_ENABLE_FEC_REQUEST (1U) + +/** + * CORE10GBASEKR_PHY DISABLE FEC REQUEST + * ============================ + * This defines that the auto-negotiation advertisement FEC request bit should be + * cleared if the IP is configured with the FEC logic. + */ +#define C10GBKR_DISABLE_FEC_REQUEST (0U) + +/** + * CORE10GBASEKR_PHY FEC NEGOTIATED + * ============================ + * This defines that FEC was negotiated during auto-negotiation + */ +#define C10GBKR_FEC_NEGOTIATED (1U) + +/** + * CORE10GBASEKR_PHY FEC NOT NEGOTIATED + * ============================ + * This defines that FEC was not negotiated during auto-negotiation + */ +#define C10GBKR_FEC_NOT_NEGOTIATED (0U) + +/** + * CORE10GBASEKR_PHY LT MAX/MIN LIMITS + * ============================ + * The max/min limit constants define the XCVR tap coefficient limits. These + * constants can be overridden based on the XCVR, which is integrated into a + * specific design. + * + * Note: Post and Pre tap maximum limits are absolute. + */ +#ifndef C10GBKR_LT_MAIN_TAP_MAX_LIMIT +#define C10GBKR_LT_MAIN_TAP_MAX_LIMIT (41U) +#endif + +#ifndef C10GBKR_LT_MAIN_TAP_MIN_LIMIT +#define C10GBKR_LT_MAIN_TAP_MIN_LIMIT (26U) +#endif + +#ifndef C10GBKR_LT_POST_TAP_MAX_LIMIT +#define C10GBKR_LT_POST_TAP_MAX_LIMIT (16U) +#endif + +#ifndef C10GBKR_LT_POST_TAP_MIN_LIMIT +#define C10GBKR_LT_POST_TAP_MIN_LIMIT (0U) +#endif + +#ifndef C10GBKR_LT_PRE_TAP_MAX_LIMIT +#define C10GBKR_LT_PRE_TAP_MAX_LIMIT (5U) +#endif + +#ifndef C10GBKR_LT_PRE_TAP_MIN_LIMIT +#define C10GBKR_LT_PRE_TAP_MIN_LIMIT (0U) +#endif + +/** + * CORE10GBASEKR_PHY PRESET + * ============================ + * The preset constants define the XCVR tap coefficient settings for a preset + * request. They can be overridden based on the XCVR, which is integrated into a + * specific design. + * + * Note: Post and Pre tap maximum limits are absolute. + */ +#ifndef C10GBKR_LT_PRESET_MAIN_TAP +#define C10GBKR_LT_PRESET_MAIN_TAP C10GBKR_LT_MAIN_TAP_MAX_LIMIT +#endif + +#ifndef C10GBKR_LT_PRESET_POST_TAP +#define C10GBKR_LT_PRESET_POST_TAP C10GBKR_LT_POST_TAP_MAX_LIMIT +#endif + +#ifndef C10GBKR_LT_PRESET_PRE_TAP +#define C10GBKR_LT_PRESET_PRE_TAP C10GBKR_LT_PRE_TAP_MAX_LIMIT +#endif + +/** + * CORE10GBASEKR_PHY INIT + * ============================ + * The initialize constants define the coefficient settings, which is set + * when an initialize request is received from the link partner. These constants + * should be updated if there is no desire to calibrate the links. + */ +#ifndef C10GBKR_LT_INITIALIZE_MAIN_TAP +#define C10GBKR_LT_INITIALIZE_MAIN_TAP C10GBKR_LT_MAIN_TAP_MIN_LIMIT +#endif + +#ifndef C10GBKR_LT_INITIALIZE_POST_TAP +#define C10GBKR_LT_INITIALIZE_POST_TAP C10GBKR_LT_POST_TAP_MIN_LIMIT +#endif + +#ifndef C10GBKR_LT_INITIALIZE_PRE_TAP +#define C10GBKR_LT_INITIALIZE_PRE_TAP C10GBKR_LT_PRE_TAP_MIN_LIMIT +#endif + +/** + * CORE10GBASEKR_PHY LP REQUEST + * ============================ + * This constant defines the request, which will be sent to the link partner and + * determines which algorithm will be implemented to calibrate the link partner. + */ +#ifndef C10GBKR_LT_INITIAL_REQUEST +#define C10GBKR_LT_INITIAL_REQUEST C10GBKR_LT_PRESET +#endif + +/** + * CORE10GBASEKR_PHY AN LINK FAIL INHIBIT TIMER + * ============================ + * This constant defines the auto-negotiation link fail inhibit timer timeout in + * milli-seconds. + */ +#ifndef C10GBKR_AN_LINK_FAIL_INHITBIT_TIMER +#define C10GBKR_AN_LINK_FAIL_INHITBIT_TIMER (500U) +#endif + +/*------------------------Public Function-------------------------------------*/ +/** + * PHY10GKR_cfg_struct_def_init() loads the PHY configuration struct + * as default. + * + * @param cfg + * The cfg parameter specifies the PHY configuration instance. + * + * @return + * This function does not return a value. + * + * @example + * @code + * #include "core10gbasekr_phy.h" + * int main(void) + * { + * PHY10GKR_cfg_struct_def_init(&phy_cfg); + * phy_cfg.fec_requested = 1; + * + * PHY10GKR_init(&xcvr, &g_phy, CORE10GBKR_0_PHY_BASE_ADDR); + * PHY10GKR_config(&g_phy, &phy_cfg); + * return (0u); + * } + * @endcode + */ +void PHY10GKR_cfg_struct_def_init(phy10gkr_cfg_t *cfg); + +/** + * PHY10GKR_init() initializes the Core10GBaseKR_PHY bare-metal driver. This + * function sets the base address of the Auto-negotiation, link-training, tx + * control, and rx status registers. + * + * @param this_phy + * The this_phy parameter specifies the PHY instance. + * + * @param base_addr + * The base_addr specifies the base address of the IP block. + * + * @param cfg + * The cfg parameter specifies the PHY configuration instance. + * + * @param xcvr + * This is a pointer to the instance of XCVR which is connected to this IP. + * + * @return + * This function returns success or failure, 0 implies success. On failure this + * function will return phy10gkr_error_t enumeration which indicates the error type. + * + * @example + * @code + * #include "core10gbasekr_phy.h" + * int main(void) + * { + * void * xcvr; + * xcvr.base_addr = 0xFFFFFFFF; + * xcvr.lane = 1; + * xcvr.serdes = 2; + * + * PHY10GKR_init(&g_phy, CORE10GBKR_0_PHY_BASE_ADDR, &phy_cfg ,&xcvr); + * return (0u); + * } + * @endcode + */ +uint32_t PHY10GKR_init(phy10gkr_instance_t *this_phy, + addr_t base_addr, + phy10gkr_cfg_t *cfg, + void *xcvr); + +/** + * PHY10GKR_10gbasekr_sm() executes the full 10GBASE-KR flow required to + * complete the auto-negotiation and link training. + * + * The 10GBASE-KR status enumeration allows the user to debug the auto-negotiation + * and link training algorithms. + * + * @param this_phy + * The this_phy parameter specifies the PHY instance. + * + * @return + * This function returns the phy10gkr_status_t enumeration which indicates the status + * of the API state machine. 0 implies that a link has been established. + * + * @example + * @code + * #include "core10gbasekr_phy.h" + * int main(void) + * { + * uint32_t status; + * PHY10GKR_cfg_struct_def_init(&phy_cfg); + * phy_cfg.fec_requested = 1; + * PHY10GKR_init(&g_phy, CORE10GBKR_0_PHY_BASE_ADDR, &phy_cfg, &xcvr); + * while(1) + * { + * status = PHY10GKR_10gbasekr_sm(&g_phy); + * if(LINK_ESTABLISHED == status) + * { + * break; + * } + * } + * return (0u); + * } + * @endcode + */ +uint32_t PHY10GKR_10gbasekr_sm(phy10gkr_instance_t *this_phy); + +/** + * PHY10GKR_get_current_time_ms() is a weak function that can be overridden + * by the user to get the current time in milli-seconds. + * + * @return + * This function returns the time in milli-seconds. + */ +uint32_t __attribute__((weak)) PHY10GKR_get_current_time_ms(void); + +/** + * PHY10GKR_get_ip_version() retrieves the IP version tags, the tags are passed by + * reference as parameters and updated by this function. This function must be + * called after initialization as there is a dependency on the instance of a + * Core10GBaseKR_PHY instance. + * + * @param this_phy + * The this_phy parameter specifies the PHY instance. + * + * @param major + * This parameter identifies the major version number. + * + * @param minor + * This parameter identifies the minor version number. + * + * @param sub + * This parameter identifies the sub version number. + * + * @return + * This function returns 0 on success. + * + * @example + * @code + * #include "core10gbasekr_phy.h" + * int main(void) + * { + * uint32_t major, + * uint32_t minor, + * uint32_t sub, + * + * PHY10GKR_cfg_struct_def_init(&phy_cfg); + * PHY10GKR_init(&g_phy, CORE10GBKR_0_PHY_BASE_ADDR, &phy_cfg, &xcvr); + * + * PHY10GKR_get_ip_version(&g_phy, &major, &minor, &sub); + * return (0u); + * } + * @endcode + */ +uint8_t PHY10GKR_get_ip_version(phy10gkr_instance_t *this_phy, + uint32_t *major, + uint32_t *minor, + uint32_t *sub); + +/** + * PHY10GKR_get_driver_version() retrieves the driver version tags, the tags are + * passed by reference as parameters and updated by this function. + * + * @param major + * This parameter identifies the major version number. + * + * @param minor + * This parameter identifies the minor version number. + * + * @param patch + * This parameter identifies the patch version number. + * + * @return + * This function returns 0 on success. + * + * @example + * @code + * #include "core10gbasekr_phy.h" + * int main(void) + * { + * uint32_t major, + * uint32_t minor, + * uint32_t patch, + + * PHY10GKR_get_driver_version(&major, &minor, &patch); + * return (0u); + * } + * @endcode + */ +uint8_t PHY10GKR_get_driver_version(uint32_t *major, uint32_t *minor, uint32_t *patch); + +#ifdef __cplusplus +} +#endif + +#endif /* CORE10GBASEKR_PHY_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy_link_training.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy_link_training.c new file mode 100644 index 0000000..f717407 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy_link_training.c @@ -0,0 +1,509 @@ +/** + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core10gbasekr_phy_link_training.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief Core10GBaseKR PHY link training prototypes + * + */ + +#include "core10gbasekr_phy.h" +#include "core10gbasekr_phy_link_training.h" + +/*------------------------Private Variables-----------------------------------*/ +uint32_t prbs_arr[64] = {0}; +uint32_t prbs_cnt = 0; + +/*------------------------Private Functions-----------------------------------*/ +/** + * This phy_10gbasekr_dfe_cal is private function which uses the XCVR APIs to set + * and check DFE calibration for a set timeout in milli-seconds + */ +static uint32_t +phy_10gbasekr_dfe_cal(phy10gkr_instance_t *this_phy, uint32_t timeout_ms) +{ + this_phy->xcvr_api.dfe_cal(this_phy->xcvr); + + uint32_t start_time = PHY10GKR_get_current_time_ms(); + uint32_t end_time = 0; + do + { + if (this_phy->xcvr_api.dfe_cal_status(this_phy->xcvr) == 0) + { + this_phy->xcvr_api.reset_pcs_rx(this_phy->xcvr); + return 0; + } + end_time = PHY10GKR_get_current_time_ms() - start_time; + } while (end_time < timeout_ms); + + this_phy->xcvr_api.reset_pcs_rx(this_phy->xcvr); + return 1; +} + +/** + * Please see core10gbasekr_phy_link_training.h for description + */ +void +link_partner_calibration_sm(phy10gkr_instance_t *this_phy) +{ + switch (this_phy->lt.lp_cal_sweep_state) + { + case MAIN_TAP: + link_partner_main_sweep(this_phy); + break; + + case POST_TAP: + link_partner_post_sweep(this_phy); + break; + + case PRE_TAP: + link_partner_pre_sweep(this_phy); + break; + + default: + HAL_ASSERT(0); + break; + } +} + +/** + * Please see core10gbasekr_phy_link_training.h for description + */ +void +link_partner_main_sweep(phy10gkr_instance_t *this_phy) +{ + phy10gkr_coeff_status_report_t tap_coeff = + HAL_get_32bit_reg_field(this_phy->lt_base_addr, C10GB_LT_RCVD_COEFF_MAIN_VALUE); + + if (this_phy->lt.main.lp_tap_cal_state == TAP_OPTIMISE_CAL) + { + if (this_phy->lt.main.optimal_cnt == (this_phy->lt.main.optimal_index)) + { + lt_tap_increment(this_phy, POST_TAP); + this_phy->lt.post.cnt++; + this_phy->lt.post.inc_cnt++; + this_phy->lt.lp_cal_sweep_state = POST_TAP; + prbs_cnt = 0; + } + else + { + lt_tap_increment(this_phy, MAIN_TAP); + this_phy->lt.main.optimal_cnt++; + } + } + else + { + switch (tap_coeff) + { + case LT_COEFF_STATUS_MAX: + prbs_arr[prbs_cnt] = + HAL_get_32bit_reg(this_phy->lt_base_addr, C10GB_LT_PRBS_ERR_WRD); + prbs_cnt++; + lt_tap_decrement(this_phy, MAIN_TAP); + this_phy->lt.main.cnt++; + this_phy->lt.main.dec_cnt++; + break; + + case LT_COEFF_STATUS_NOT_UPDATED: + lt_tap_hold(this_phy, MAIN_TAP); + this_phy->lt.main.cnt++; + break; + + case LT_COEFF_STATUS_UPDATED: + prbs_arr[prbs_cnt] = + HAL_get_32bit_reg(this_phy->lt_base_addr, C10GB_LT_PRBS_ERR_WRD); + prbs_cnt++; + lt_tap_decrement(this_phy, MAIN_TAP); + this_phy->lt.main.cnt++; + this_phy->lt.main.dec_cnt++; + break; + + case LT_COEFF_STATUS_MIN: + prbs_arr[prbs_cnt] = + HAL_get_32bit_reg(this_phy->lt_base_addr, C10GB_LT_PRBS_ERR_WRD); + prbs_cnt++; + + this_phy->lt.main.optimal_index = lt_get_optimal_tap_index(prbs_arr, prbs_cnt); + + if (this_phy->lt.main.optimal_index == 0) + { + lt_tap_increment(this_phy, POST_TAP); + this_phy->lt.post.cnt++; + this_phy->lt.post.inc_cnt++; + this_phy->lt.lp_cal_sweep_state = POST_TAP; + prbs_cnt = 0; + } + else + { + this_phy->lt.main.lp_tap_cal_state = TAP_OPTIMISE_CAL; + lt_tap_increment(this_phy, MAIN_TAP); + this_phy->lt.main.optimal_cnt++; + } + break; + + default: + HAL_ASSERT(0); + break; + } + } +} + +/** + * Please see core10gbasekr_phy_link_training.h for description + */ +void +link_partner_post_sweep(phy10gkr_instance_t *this_phy) +{ + phy10gkr_coeff_status_report_t tap_coeff = + HAL_get_32bit_reg_field(this_phy->lt_base_addr, C10GB_LT_RCVD_COEFF_POST_VALUE); + + if (this_phy->lt.post.lp_tap_cal_state == TAP_OPTIMISE_CAL) + { + if (this_phy->lt.post.optimal_cnt == (this_phy->lt.post.optimal_index)) + { + lt_tap_increment(this_phy, PRE_TAP); + this_phy->lt.pre.cnt++; + this_phy->lt.pre.inc_cnt++; + this_phy->lt.lp_cal_sweep_state = PRE_TAP; + prbs_cnt = 0; + } + else + { + lt_tap_increment(this_phy, POST_TAP); + this_phy->lt.post.optimal_cnt++; + } + } + else + { + switch (tap_coeff) + { + case LT_COEFF_STATUS_MAX: + prbs_arr[prbs_cnt] = + HAL_get_32bit_reg(this_phy->lt_base_addr, C10GB_LT_PRBS_ERR_WRD); + prbs_cnt++; + lt_tap_decrement(this_phy, POST_TAP); + this_phy->lt.post.cnt++; + this_phy->lt.post.dec_cnt++; + break; + + case LT_COEFF_STATUS_NOT_UPDATED: + lt_tap_hold(this_phy, POST_TAP); + this_phy->lt.post.cnt++; + break; + + case LT_COEFF_STATUS_UPDATED: + prbs_arr[prbs_cnt] = + HAL_get_32bit_reg(this_phy->lt_base_addr, C10GB_LT_PRBS_ERR_WRD); + prbs_cnt++; + lt_tap_decrement(this_phy, POST_TAP); + this_phy->lt.post.cnt++; + this_phy->lt.post.dec_cnt++; + break; + + case LT_COEFF_STATUS_MIN: + prbs_arr[prbs_cnt] = + HAL_get_32bit_reg(this_phy->lt_base_addr, C10GB_LT_PRBS_ERR_WRD); + prbs_cnt++; + + this_phy->lt.post.optimal_index = lt_get_optimal_tap_index(prbs_arr, prbs_cnt); + + if (this_phy->lt.post.optimal_index == 0) + { + lt_tap_increment(this_phy, PRE_TAP); + this_phy->lt.pre.cnt++; + this_phy->lt.pre.inc_cnt++; + this_phy->lt.lp_cal_sweep_state = PRE_TAP; + prbs_cnt = 0; + } + else + { + this_phy->lt.post.lp_tap_cal_state = TAP_OPTIMISE_CAL; + lt_tap_increment(this_phy, POST_TAP); + this_phy->lt.post.optimal_cnt++; + } + break; + + default: + HAL_ASSERT(0); + break; + } + } +} + +/** + * Please see core10gbasekr_phy_link_training.h for description + */ +void +link_partner_pre_sweep(phy10gkr_instance_t *this_phy) +{ + phy10gkr_coeff_status_report_t tap_coeff = + HAL_get_32bit_reg_field(this_phy->lt_base_addr, C10GB_LT_RCVD_COEFF_PRE_VALUE); + + if (this_phy->lt.pre.lp_tap_cal_state == TAP_OPTIMISE_CAL) + { + if (this_phy->lt.pre.optimal_cnt == (this_phy->lt.pre.optimal_index)) + { + while ((phy_10gbasekr_dfe_cal(this_phy, 10) != 0) && + (STATUS_LT_FAILURE != this_phy->lt.status)) + { + uint32_t c10gbkr_status = + HAL_get_32bit_reg(this_phy->lt_base_addr, C10GB_LT_STATUS); + + this_phy->lt.timer.end = PHY10GKR_get_current_time_ms() - this_phy->lt.timer.start; + + if ((c10gbkr_status & C10GB_LT_TRAINING_FAIL_MASK) || + this_phy->lt.timer.end > LT_SOFTWARE_WAIT_TIMER_MS) + { + this_phy->lt.status = STATUS_LT_FAILURE; + break; + } + } + + if (STATUS_LT_FAILURE != this_phy->lt.status) + { + /* All sweeps complete set to best ber index */ + this_phy->lt.local_rxcvr = LOCAL_RXCVR_LOCKED; + + this_phy->lt.rcvr_cnt++; + + HAL_set_32bit_reg_field(this_phy->lt_base_addr, + C10GB_LT_LOCAL_RCVR_LOCKED, + LOCAL_RXCVR_LOCKED); + prbs_cnt = 0; + } + } + else + { + lt_tap_increment(this_phy, PRE_TAP); + this_phy->lt.pre.optimal_cnt++; + } + } + else + { + switch (tap_coeff) + { + case LT_COEFF_STATUS_MAX: + prbs_arr[prbs_cnt] = + HAL_get_32bit_reg(this_phy->lt_base_addr, C10GB_LT_PRBS_ERR_WRD); + prbs_cnt++; + lt_tap_decrement(this_phy, PRE_TAP); + this_phy->lt.pre.cnt++; + this_phy->lt.pre.dec_cnt++; + break; + + case LT_COEFF_STATUS_NOT_UPDATED: + lt_tap_hold(this_phy, PRE_TAP); + this_phy->lt.pre.cnt++; + break; + + case LT_COEFF_STATUS_UPDATED: + prbs_arr[prbs_cnt] = + HAL_get_32bit_reg(this_phy->lt_base_addr, C10GB_LT_PRBS_ERR_WRD); + prbs_cnt++; + lt_tap_decrement(this_phy, PRE_TAP); + this_phy->lt.pre.cnt++; + this_phy->lt.pre.dec_cnt++; + break; + + case LT_COEFF_STATUS_MIN: + prbs_arr[prbs_cnt] = + HAL_get_32bit_reg(this_phy->lt_base_addr, C10GB_LT_PRBS_ERR_WRD); + prbs_cnt++; + + this_phy->lt.pre.optimal_index = lt_get_optimal_tap_index(prbs_arr, prbs_cnt); + + if (this_phy->lt.post.optimal_index == 0) + { + while ((phy_10gbasekr_dfe_cal(this_phy, 10) != 0) && + (STATUS_LT_FAILURE != this_phy->lt.status)) + { + uint32_t c10gbkr_status = + HAL_get_32bit_reg(this_phy->lt_base_addr, C10GB_LT_STATUS); + + this_phy->lt.timer.end = + PHY10GKR_get_current_time_ms() - this_phy->lt.timer.start; + + if ((c10gbkr_status & C10GB_LT_TRAINING_FAIL_MASK) || + (this_phy->lt.timer.end > LT_SOFTWARE_WAIT_TIMER_MS)) + { + this_phy->lt.status = STATUS_LT_FAILURE; + break; + } + } + + if (STATUS_LT_FAILURE != this_phy->lt.status) + { + /* All sweeps complete set to best ber index */ + this_phy->lt.local_rxcvr = LOCAL_RXCVR_LOCKED; + + this_phy->lt.rcvr_cnt++; + + HAL_set_32bit_reg_field(this_phy->lt_base_addr, + C10GB_LT_LOCAL_RCVR_LOCKED, + LOCAL_RXCVR_LOCKED); + prbs_cnt = 0; + } + } + else + { + this_phy->lt.pre.lp_tap_cal_state = TAP_OPTIMISE_CAL; + lt_tap_increment(this_phy, PRE_TAP); + this_phy->lt.pre.optimal_cnt++; + } + break; + + default: + HAL_ASSERT(0); + break; + } + } +} + +/** + * Please see core10gbasekr_phy_link_training.h for description + */ +void +lt_tap_decrement(phy10gkr_instance_t *this_phy, phy10gkr_tx_equalizer_tap_t tap_type) +{ + switch (tap_type) + { + case MAIN_TAP: + HAL_set_32bit_reg(this_phy->lt_base_addr, + C10GB_LT_TX_COEFF_CFG, + C10GB_LT_TX_DEC_MAIN_MASK); + break; + + case POST_TAP: + HAL_set_32bit_reg(this_phy->lt_base_addr, + C10GB_LT_TX_COEFF_CFG, + C10GB_LT_TX_DEC_POST_MASK); + break; + + case PRE_TAP: + HAL_set_32bit_reg(this_phy->lt_base_addr, + C10GB_LT_TX_COEFF_CFG, + C10GB_LT_TX_DEC_PRE_MASK); + break; + + default: + HAL_ASSERT(0); + break; + } +} + +/** + * Please see core10gbasekr_phy_link_training.h for description + */ +void +lt_tap_hold(phy10gkr_instance_t *this_phy, phy10gkr_tx_equalizer_tap_t tap_type) +{ + switch (tap_type) + { + case MAIN_TAP: + HAL_set_32bit_reg(this_phy->lt_base_addr, + C10GB_LT_TX_COEFF_CFG, + C10GB_LT_TX_HOLD_MAIN_MASK); + break; + + case POST_TAP: + HAL_set_32bit_reg(this_phy->lt_base_addr, + C10GB_LT_TX_COEFF_CFG, + C10GB_LT_TX_HOLD_POST_MASK); + break; + + case PRE_TAP: + HAL_set_32bit_reg(this_phy->lt_base_addr, + C10GB_LT_TX_COEFF_CFG, + C10GB_LT_TX_HOLD_PRE_MASK); + break; + + default: + HAL_ASSERT(0); + break; + } +} + +/** + * Please see core10gbasekr_phy_link_training.h for description + */ +void +lt_tap_increment(phy10gkr_instance_t *this_phy, phy10gkr_tx_equalizer_tap_t tap_type) +{ + switch (tap_type) + { + case MAIN_TAP: + HAL_set_32bit_reg(this_phy->lt_base_addr, + C10GB_LT_TX_COEFF_CFG, + C10GB_LT_TX_INC_MAIN_MASK); + break; + + case POST_TAP: + HAL_set_32bit_reg(this_phy->lt_base_addr, + C10GB_LT_TX_COEFF_CFG, + C10GB_LT_TX_INC_POST_MASK); + break; + + case PRE_TAP: + HAL_set_32bit_reg(this_phy->lt_base_addr, + C10GB_LT_TX_COEFF_CFG, + C10GB_LT_TX_INC_PRE_MASK); + break; + + default: + HAL_ASSERT(0); + break; + } +} + +/** + * Please see core10gbasekr_phy_link_training.h for description + */ +uint32_t +lt_get_optimal_tap_index(uint32_t *prbs_arr, uint32_t prbs_arr_size) +{ + uint32_t i = 0; + uint32_t min_error = prbs_arr[0]; + uint32_t min_index = 0; + uint32_t min_midpoint_distance = prbs_arr_size / 2; + + /* sweep to find min prbs error */ + for (i = 0; i < prbs_arr_size; i++) + { + if (prbs_arr[i] < min_error) + { + min_error = prbs_arr[i]; + min_index = i; + } + } + + /* sweep to find which index are equal the min, weighted by distance to mid + * point of array + */ + for (i = 0; i < prbs_arr_size; i++) + { + if (prbs_arr[i] == min_error) + { + int32_t midpoint_distance = (prbs_arr_size / 2) - i; + if (midpoint_distance < 0) + { + midpoint_distance *= -1; + } + + if (midpoint_distance < min_midpoint_distance) + { + min_midpoint_distance = midpoint_distance; + min_index = i; + } + + if (midpoint_distance == 0) + { + break; + } + } + } + + /* index reversed as the order was set while decrementing from max to min */ + return prbs_arr_size - min_index; +} diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy_link_training.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy_link_training.h new file mode 100644 index 0000000..94bdff6 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy_link_training.h @@ -0,0 +1,178 @@ +/** + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core10gbasekr_phy_link_training.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Core10GBaseKR PHY link training prototypes + * + */ + +#ifndef CORE10GBASEKR_PHY_LINK_TRAINING_H_ +#define CORE10GBASEKR_PHY_LINK_TRAINING_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/// @cond @private + +#include "core10gbasekr_phy.h" + +/** + * Link partner calibration state machine. + * + * This function determines which of the link partners transmitter taps are to be + * calibrated. + * + * @param this_phy + * The this_phy parameter specifies the 10GBaseKR PHY block. + * + * @return + * This function does not return a value. + */ +void link_partner_calibration_sm(phy10gkr_instance_t *this_phy); + +/** + * Link partner main (c(0)) cursor sweep. + * + * The algorithm increments the link partners transmit taps to maximum (as this + * algorithm is only used when preset is sent the link partner main tap should be + * maximum). When the main tap is maximum the algorithm will decrement the link + * partner's main transmit tap to minimum before decrementing the algorithm + * stores the PRBS for optimisation. + * + * When the link partners transmit main tap is minimum algorithm determines the + * optimal tap setting based on the PRBS errors stored during sweeping. The + * algorithm now increments the link partners transmit tap back to the optimal + * tap setting. + * + * @param this_phy + * The this_phy parameter specifies the 10GBaseKR PHY block. + * + * @return + * This function does not return a value. + */ +void link_partner_main_sweep(phy10gkr_instance_t *this_phy); + +/** + * Link partner post (c(+1)) cursor sweep. + * + * The algorithm increments the link partners transmit taps to maximum (as this + * algorithm is only used when preset is sent the link partner post tap should be + * maximum). When the post tap is maximum the algorithm will decrement the link + * partner's post transmit tap to minimum before decrementing the algorithm + * stores the PRBS for optimisation. + * + * When the link partners transmit post tap is minimum algorithm determines the + * optimal tap setting based on the PRBS errors stored during sweeping. The + * algorithm now increments the link partners transmit tap back to the optimal + * tap setting. + * + * @param this_phy + * The this_phy parameter specifies the 10GBaseKR PHY block. + * + * @return + * This function does not return a value. + */ +void link_partner_post_sweep(phy10gkr_instance_t *this_phy); + +/** + * Link partner pre (c(-1)) cursor sweep. + * + * The algorithm increments the link partners transmit taps to maximum (as this + * algorithm is only used when preset is sent the link partner pre tap should be + * maximum). When the pre tap is maximum the algorithm will decrement the link + * partner's pre transmit tap to minimum before decrementing the algorithm + * stores the PRBS for optimisation. + * + * When the link partners transmit pre tap is minimum algorithm determines the + * optimal tap setting based on the PRBS errors stored during sweeping. The + * algorithm now increments the link partners transmit tap back to the optimal + * tap setting. + * + * Once the link partner's pre tap has incremented back to the optimal setting + * the local receiver ready bit is set to indicate to the link partner that + * this device is finished calibrating the link partners transmitter taps. + * + * @param this_phy + * The this_phy parameter specifies the 10GBaseKR PHY block. + * + * @return + * This function does not return a value. + */ +void link_partner_pre_sweep(phy10gkr_instance_t *this_phy); + +/** + * This function sets the decrement bit of a specific transmit equalizer tap + * register which updates the transmitted coefficient update to the link partner. + * + * @param this_phy + * The this_phy parameter specifies the 10GBaseKR PHY block. + * + * @param tap_type + * This enumeration specifies the which transmitter tap will be decremented + * + * @return + * This function does not return a value. + */ +void lt_tap_decrement(phy10gkr_instance_t *this_phy, phy10gkr_tx_equalizer_tap_t tap_type); + +/** + * This function sets the hold bit of a specific transmit equalizer tap + * register which updates the transmitted coefficient update to the link partner. + * + * @param this_phy + * The this_phy parameter specifies the 10GBaseKR PHY block. + * + * @param tap_type + * This enumeration specifies the which transmitter tap will be held + * + * @return + * This function does not return a value. + */ +void lt_tap_hold(phy10gkr_instance_t *this_phy, phy10gkr_tx_equalizer_tap_t tap_type); + +/** + * This function sets the increment bit of a specific transmit equalizer tap + * register which updates the transmitted coefficient update to the link partner. + * + * @param this_phy + * The this_phy parameter specifies the 10GBaseKR PHY block. + * + * @param tap_type + * This enumeration specifies the which transmitter tap will be incremented + * + * @return + * This function does not return a value. + */ +void lt_tap_increment(phy10gkr_instance_t *this_phy, phy10gkr_tx_equalizer_tap_t tap_type); + +/** + * This function calculates the optimal tap setting index based on the PRBS error + * counts stored during maximum to minimum tap sweep. + * + * Sweeps the PRBS error array to find the minimum error, if there are multiple + * samples that have the same error a weighting is applied to select the tap + * setting closest to the center tap setting. + * + * @param prbs_arr + * This is a pointer to the first sample of the PRBS error sample array + * + * @param prbs_arr_size + * This identifies the number of samples. + * + * @return + * This function returns the number of increments required to increment a tap + * to its optimal tap setting. + */ +uint32_t lt_get_optimal_tap_index(uint32_t *prbs_arr, uint32_t prbs_arr_size); + +/// @endcond + +#ifdef __cplusplus +} +#endif + +#endif /* CORE10GBASEKR_PHY_LINK_TRAINING_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy_reg.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy_reg.h new file mode 100644 index 0000000..96f7ffb --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy_reg.h @@ -0,0 +1,1249 @@ +/** + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core10gbasekr_phy_reg.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Core10GBaseKR PHY memory map + * + */ + +#ifndef CORE10GBASEKR_PHY_REG_H_ +#define CORE10GBASEKR_PHY_REG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/// @cond @private + +#include "uint_32_bit_masks.h" + +#define __I const volatile +#define __O volatile +#define __IO volatile + +/******************************************************************************/ +/* Core10GBaseKR PHY Clause 73 (Auto-negotiation) */ +/******************************************************************************/ + +/** + * AN_CONTROL register details + */ +#define C10GB_AN_CONTROL_REG_OFFSET (0x0U << 2U) + +/** + * Auto-negotiation Restart + * + * Restart_negotiation. Self clears, and always reads as 0. + */ +#define C10GB_AN_RESTART_OFFSET C10GB_AN_CONTROL_REG_OFFSET +#define C10GB_AN_RESTART_MASK MASK_BIT_9 +#define C10GB_AN_RESTART_SHIFT (9U) + +/** + * Auto-negotiation Enable + * + * Auto-negotiation Enable. Writing “1†in this register enables + * auto-negotiation + */ +#define C10GB_AN_ENABLE_OFFSET C10GB_AN_CONTROL_REG_OFFSET +#define C10GB_AN_ENABLE_MASK MASK_BIT_12 +#define C10GB_AN_ENABLE_SHIFT (12U) + +/** + * Auto-negotiation Reset + * + * Main_reset. Self clears. When reads as 0 the reset is complete. + */ +#define C10GB_AN_RESET_OFFSET C10GB_AN_CONTROL_REG_OFFSET +#define C10GB_AN_RESET_MASK MASK_BIT_15 +#define C10GB_AN_RESET_SHIFT (15U) + +/** + * AN_STATUS register details + */ +#define C10GB_AN_STATUS_REG_OFFSET (0x1U << 2U) + +/** + * Auto-negotiation Able + * + * If “1†indicates that Link Partner is Auto-negotiation Able + */ +#define C10GB_AN_CAPABLE_OFFSET C10GB_AN_STATUS_REG_OFFSET +#define C10GB_AN_CAPABLE_MASK MASK_BIT_0 +#define C10GB_AN_CAPABLE_SHIFT (0U) + +/** + * Auto-negotiation Support + * + * If “1†indicates that Auto-negotiation is supported + */ +#define C10GB_AN_SUPPORT_OFFSET C10GB_AN_STATUS_REG_OFFSET +#define C10GB_AN_SUPPORT_MASK MASK_BIT_3 +#define C10GB_AN_SUPPORT_SHIFT (3U) + +/** + * Auto-negotiation Complete + * + * If “1†indicates that AN is complete + */ +#define C10GB_AN_COMPLETE_OFFSET C10GB_AN_STATUS_REG_OFFSET +#define C10GB_AN_COMPLETE_MASK MASK_BIT_5 +#define C10GB_AN_COMPLETE_SHIFT (5U) + +/** + * Auto-negotiation Page Received + * + * Page_RX. If “1†that Page is received from the link partner. Clear on read + */ +#define C10GB_AN_PAGE_RX_OFFSET C10GB_AN_STATUS_REG_OFFSET +#define C10GB_AN_PAGE_RX_MASK MASK_BIT_6 +#define C10GB_AN_PAGE_RX_SHIFT (6U) + +/** + * Auto-negotiation Parallel Fault Detected + * + * Parallel_Detect_fault. Clear on read + */ +#define C10GB_AN_PARALLEL_DETECT_OFFSET C10GB_AN_STATUS_REG_OFFSET +#define C10GB_AN_PARALLEL_DETECT_MASK MASK_BIT_9 +#define C10GB_AN_PARALLEL_DETECT_SHIFT (9U) + +/** + * Auto-negotiation State Variable + * + * This register returns the state variables of the Auto-Negotiation. + * ST_AUTO_NEG_ENABLE = 0x0; + * ST_TRANSMIT_DISABLE = 0x1; + * ST_CAPABILITY_DETECT = 0x2; + * ST_ACKNOWLEDGE_DETECT = 0x3; + * ST_COMPLETE_ACKNOWLE = 0x4; + * ST_AN_GOOD_CHECK = 0x5; + * ST_AN_GOOD = 0x6; + * ST_NEXT_PAGE_WAIT = 0x7; + * ST_NEXT_PAGE_WAIT_TX_IDLE = 0x8; + * ST_LINK_STATUS_CHECK = 0x9; + * ST_PARALLEL_DETECTION_FAULT = 0xA; + */ +#define C10GB_AN_STATE_OFFSET C10GB_AN_STATUS_REG_OFFSET +#define C10GB_AN_STATE_SHIFT (12U) +#define C10GB_AN_STATE_MASK BIT_MASK_4_BITS << C10GB_AN_STATE_SHIFT + +/** + * MR_ADV_CAPABILITY_1 Bits [16:1] + * + * This register gives the value of the bits 16:1 of the Advertisement Ability + * Register + * Bits [4:0] - Selector Field (S[4:0]) is a five-bit wide field, encoding 32 + * possible messages. Selector Field encoding definitions are shown in Annex + * 28A of the IEEE 802.3 specification + * Bits[9:5] -Echoed Nonce Field (E[4:0]) is a 5-bit wide field containing + * the nonce received from the link partner + * Bits[12:10] - Pause Encoding as defined in Annexure 28B of the IEEE 802.3 + * specification + * Bit 13 - Remote Fault (RF) of the base link codeword. The default value is + * logical zero + * Bit 14 -Acknowledge (Ack) is used by the Auto-Negotiation function to + * indicate that a device has successfully received its link partner’s link + * codeword. + * Bit 15 -Next Page (NP) bit. Support of Next Pages is mandatory. If the + * device does not have any Next Pages to send, the NP bit shall be set to + * logical zero + */ +#define C10GB_AN_MR_ADV_CAPABILITY_1_REG_OFFSET (0x10U << 2U) + +/** + * MR_ADV_CAPABILITY_2 Bits [32:17] + * + * This register gives the value of the bits 32:17 of the Advertisement Ability + * Register + * Bits[20:16] – Transmitted Nonce Field + * Bits[31:21] – Technology Ability Field bits[ 10:0] . Technology Ability + * Field is a 25-bit wide field containing information indicating supported + * technologies specific to the selector field value when used with the + * Auto-Negotiation for Backplane Ethernet. + */ +#define C10GB_AN_MR_ADV_CAPABILITY_2_REG_OFFSET (0x11U << 2U) + +/** + * MR_ADV_CAPABILITY_3 Bits [48:33] + * + * This register gives the value of the bits 48:33 of the Advertisement Ability + * Register + * Bits [45:32] – Technology Ability Field bits[ 25:11]. For Technology Ability + * Field encoding refer to Table 73-4 of the IEEE specification + * Bits [47:46] - FEC Capability (FEC Not supported in current implementation). + * Bits [47:46] - FEC Capability (FEC Not supported in current implementation). + * Bit 46 - FEC ability, if set to ‘1’ PHY has FEC Ability + * Bit 47 - FEC requested When the FEC requested bit is set to logical one, it + * indicates a request to enable FEC on the link. + */ +#define C10GB_AN_MR_ADV_CAPABILITY_3_REG_OFFSET (0x12U << 2U) + +/** + * Adevertisment Ability Registers Details + */ +#define C10GB_AN_ADV_CAPABILITY_SHIFT (0U) +#define C10GB_AN_ADV_CAPABILITY_MASK BIT_MASK_16_BITS << C10GB_AN_ADV_CAPABILITY_SHIFT + +/** + * MR_LP_BASE_PAGE_CAPABILITY_1 Bits [16:1] + * + * This register gives the value of the bits 16:1 of the Link Partner Base Page + * Ability Register. The bits definition are same as Advertisement ability + * register -1 + */ +#define C10GB_AN_MR_LP_BASE_PG_CAPABILITY_1_REG_OFFSET (0x13U << 2U) + +/** + * MR_LP_BASE_PAGE_CAPABILITY_2 Bits [32:17] + * + * This register gives the value of the bits 32:17 of the Link Partner Base Page + * Ability Register. The bits definition are same as Advertisement ability + * register -2 + */ +#define C10GB_AN_MR_LP_BASE_PG_CAPABILITY_2_REG_OFFSET (0x14U << 2U) + +/** + * MR_LP_BASE_PAGE_CAPABILITY_3 Bits [48:33] + * + * This register gives the value of the bits 48:33 of the Link Partner Base Page + * Ability Register. The bits definition are same as Advertisement ability + * register -3 + * + */ +#define C10GB_AN_MR_LP_BASE_PG_CAPABILITY_3_REG_OFFSET (0x15U << 2U) + +/** + * Link Partner Base Page Ability Registers Details + */ +#define C10GB_AN_LP_BASE_PAGE_CAPABILITY_SHIFT (0U) +#define C10GB_AN_LP_BASE_PAGE_CAPABILITY_MASK \ + BIT_MASK_16_BITS << C10GB_AN_LP_BASE_PAGE_CAPABILITY_SHIFT + +/** + * MR_XNP_TRANSMIT_1 Bits [16:1] + * + * This register gives the value of the bits 16:1 of the XNP(next page)Transmit + * Register. + * Bit[10:0] – Message Code Field (M[10:0]) is an eleven bit wide field, + * encoding 2048 possible messages. Message Code Field definitions are shown in + * Annex 28C of the IEEE 802.3 specification + * Bit 11 – Toggle (T) is used by the Arbitration function to ensure + * synchronization with the Link Partner during Next Page exchange. This bit + * shall always take the opposite value of the Toggle bit in the previously + * exchanged link codeword + * Bit 12 – Acknowledge 2 (Ack2) is used by the Next Page function to indicate + * that a device has the ability to comply with the message. Ack2 shall be set + * as follows: + * 0 - cannot comply with message + * 1 - will comply with message + * Bit 13 – Message Page bit + * 0 – Unformatted Page + * 1 – Message Page + * Bit 14 – Acknowledge. Acknowledge (Ack) is used by the Auto-Negotiation + * function to indicate that a device has successfully received its Link + * Partner’s link codeword + * Bit 15 - Next Page Bit + * 0 - last page + * 1 - additional Next Page(s) will follow + */ +#define C10GB_AN_MR_XNP_TRANSMIT_1_REG_OFFSET (0x16U << 2U) + +/** + * MR_XNP_TRANSMIT_2 Bits [32:17] + * + * This register gives the value of the bits 32:17 of the XNP Transmit Register. + * This value corresponds to the Bits[31:16] of the Unformatted Code Field of + * the Next Page Register. + */ +#define C10GB_AN_MR_XNP_TRANSMIT_2_REG_OFFSET (0x17U << 2U) + +/** + * MR_XNP_TRANSMIT_3 Bits [48:33] + * + * This register gives the value of the bits 48:33 of the XNP Transmit Register. + * This value corresponds to the Bits[47:32] of the Unformatted Code Field of + * the Next Page Register. + */ +#define C10GB_AN_MR_XNP_TRANSMIT_3_REG_OFFSET (0x18U << 2U) + +/** + * XNP Transmit Registers Details + */ +#define C10GB_AN_XNP_TRANSMIT_SHIFT (0U) +#define C10GB_AN_XNP_TRANSMIT_MASK BIT_MASK_16_BITS << C10GB_AN_XNP_TRANSMIT_SHIFT + +/** + * LP_XNP_CAPABILITY_1 Bits [16:1] + * + * This register gives the value of the bits 16:1 of the Link Partner next page + * Ability register. The bits definition are same as XNP Transmit register -1 + */ +#define C10GB_AN_LP_XNP_CAPABILITY_1_REG_OFFSET (0x19U << 2U) + +/** + * LP_XNP_CAPABILITY_2 Bits [32:17] + * + * This register gives the value of the bits 32:17 of the Link Partner next page + * Ability register. The bits definition are same as XNP Transmit register -2 + */ +#define C10GB_AN_LP_XNP_CAPABILITY_2_REG_OFFSET (0x1aU << 2U) + +/** + * LP_XNP_CAPABILITY_3 Bits [48:33] + * + * This register gives the value of the bits 48:33 of the Link Partner next page + * Ability register. The bits definition are same as XNP Transmit register -3 + */ +#define C10GB_AN_LP_XNP_CAPABILITY_3_REG_OFFSET (0x1bU << 2U) + +/** + * Link Partner Next Page Ability Registers Details + */ +#define C10GB_AN_LP_XNP_CAPABILITY_SHIFT (0U) +#define C10GB_AN_LP_XNP_CAPABILITY_MASK BIT_MASK_16_BITS << C10GB_AN_LP_XNP_CAPABILITY_SHIFT + +/** + * AN Link Fail Inhibit Timer + * + * An_link_fail_inhibit_timer is 500ms timer which starts incrementing in + * AN GOOD CHECK state (This state is indication of AN completion) and if it + * elapses in during this state then the Auto-negotiation process starts again + */ +#define C10GB_AN_LINK_FAIL_INHIBIT_TIMER_REG_OFFSET (0x0dU << 2U) + +/******************************************************************************/ +/* Core10GBaseKR PHY Clause 72 (Link Training) */ +/******************************************************************************/ + +/** + * Link Training Control Register + */ +#define C10GB_LT_CTRL_REG_OFFSET (0x0U << 2U) + +/** + * Link Training Initialize + * + * If “1†initialize coefficient is transmitted to the link partner + */ +#define C10GB_LT_INIT_OFFSET C10GB_LT_CTRL_REG_OFFSET +#define C10GB_LT_INIT_SHIFT (3U) +#define C10GB_LT_INIT_MASK MASK_BIT_3 + +/** + * Link Training Preset + * + * If “1†preset coefficient is transmitted to the link partner + */ +#define C10GB_LT_PRESET_OFFSET C10GB_LT_CTRL_REG_OFFSET +#define C10GB_LT_PRESET_SHIFT (2U) +#define C10GB_LT_PRESET_MASK MASK_BIT_2 + +/** + * Link Restart Training + * + * A write of “1†to this field brings the link training IP in reset mode. + * To bring the Link training IP out of reset, write a value of 0x0 into this + * register + */ +#define C10GB_LT_RESTART_TRAINING_OFFSET C10GB_LT_CTRL_REG_OFFSET +#define C10GB_LT_RESTART_TRAINING_SHIFT (1U) +#define C10GB_LT_RESTART_TRAINING_MASK MASK_BIT_1 + +/** + * Link Training Enable + * + * If “1†enables the link training. This bit should be enabled from the + * software after the AN_GOOD_CHK interrupt is received by the Firmware + */ +#define C10GB_LT_RESTART_EN_OFFSET C10GB_LT_CTRL_REG_OFFSET +#define C10GB_LT_RESTART_EN_SHIFT (0U) +#define C10GB_LT_RESTART_EN_MASK MASK_BIT_0 + +/** + * Max Wait Timer Configuration + * + * The value in the MAX wait timer should be configured to 500ms, the training + * state diagram enters into the training failure state once the timer reaches + * 500ms. + */ +#define C10GB_LT_MAX_WAIT_TIMER_REG_OFFSET (0x1U << 2U) + +#define C10GB_LT_MAX_WAIT_TIMER_OFFSET C10GB_LT_MAX_WAIT_TIMER_REG_OFFSET +#define C10GB_LT_MAX_WAIT_TIMER_SHIFT (0U) +#define C10GB_LT_MAX_WAIT_TIMER_MASK BIT_MASK_32_BITS << C10GB_LT_MAX_WAIT_TIMER_SHIFT + +/** + * Frame WAIT Timer Configuration Register + * + * The value in the frame WAIT Timer register should be between 100 to 300 + * frames. The default value is configured to 100 frames. + */ +#define C10GB_LT_FRM_WAIT_TIMER_REG_OFFSET (0x2U << 2U) + +#define C10GB_LT_FRM_WAIT_TIMER_OFFSET C10GB_LT_FRM_WAIT_TIMER_REG_OFFSET +#define C10GB_LT_FRM_WAIT_TIMER_SHIFT (0U) +#define C10GB_LT_FRM_WAIT_TIMER_MASK BIT_MASK_32_BITS << C10GB_LT_FRM_WAIT_TIMER_SHIFT + +/** + * Preset MAIN-TAP Configuration Register + * + * The value in this register gives the main TAP value, when the preset + * Coefficient is received from the Link Partner. The Link Training Algorithm + * starts from this TAP value. + */ +#define C10GB_LT_PRESET_MAIN_TAP_REG_OFFSET (0x3U << 2U) + +#define C10GB_LT_PRESET_MAIN_TAP_OFFSET C10GB_LT_PRESET_MAIN_TAP_REG_OFFSET +#define C10GB_LT_PRESET_MAIN_TAP_SHIFT (0U) +#define C10GB_LT_PRESET_MAIN_TAP_MASK BIT_MASK_16_BITS << C10GB_LT_PRESET_MAIN_TAP_SHIFT + +/** + * Preset Post-TAP Configuration Register + * + * The value in this register gives the post-TAP value when the preset + * Coefficient is received from the Link Partner. The Link Training Algorithm + * starts from this TAP value. + */ +#define C10GB_LT_PRESET_POST_TAP_REG_OFFSET (0x4U << 2U) + +#define C10GB_LT_PRESET_POST_TAP_OFFSET C10GB_LT_PRESET_POST_TAP_REG_OFFSET +#define C10GB_LT_PRESET_POST_TAP_SHIFT (0U) +#define C10GB_LT_PRESET_POST_TAP_MASK BIT_MASK_16_BITS << C10GB_LT_PRESET_POST_TAP_SHIFT + +/** + * Preset Pre-TAP Configuration Register + * + * The value in this register gives the pre-TAP when the preset Coefficient is + * received from the Link Partner. The Link Training Algorithm starts from this + * TAP value. + */ +#define C10GB_LT_PRESET_PRE_TAP_REG_OFFSET (0x5U << 2U) + +#define C10GB_LT_PRESET_PRE_TAP_OFFSET C10GB_LT_PRESET_PRE_TAP_REG_OFFSET +#define C10GB_LT_PRESET_PRE_TAP_SHIFT (0U) +#define C10GB_LT_PRESET_PRE_TAP_MASK BIT_MASK_16_BITS << C10GB_LT_PRESET_PRE_TAP_SHIFT + +/** + * Initialize MAIN-TAP Configuration Register + * + * The value in this register gives the main TAP value, when the Initialize + * Coefficient is received from the Link Partner. + */ +#define C10GB_LT_INIT_MAIN_TAP_REG_OFFSET (0x6U << 2U) + +#define C10GB_LT_INIT_MAIN_TAP_OFFSET C10GB_LT_INIT_MAIN_TAP_REG_OFFSET +#define C10GB_LT_INIT_MAIN_TAP_SHIFT (0U) +#define C10GB_LT_INIT_MAIN_TAP_MASK BIT_MASK_16_BITS << C10GB_LT_INIT_MAIN_TAP_SHIFT + +/** + * Initialize Post-TAP Configuration Register + * + * The value in this register gives the post-TAP value when the Initialize + * Coefficient is received from the Link Partner. + */ +#define C10GB_LT_INIT_POST_TAP_REG_OFFSET (0x7U << 2U) + +#define C10GB_LT_INIT_POST_TAP_OFFSET C10GB_LT_INIT_POST_TAP_REG_OFFSET +#define C10GB_LT_INIT_POST_TAP_SHIFT (0U) +#define C10GB_LT_INIT_POST_TAP_MASK BIT_MASK_16_BITS << C10GB_LT_INIT_POST_TAP_SHIFT + +/** + * Initialize Pre-TAP Configuration Register + * + * The value in this register gives the pre-TAP when the Initialize Coefficient + * is received from the Link Partner. + */ +#define C10GB_LT_INIT_PRE_TAP_REG_OFFSET (0x8U << 2U) + +#define C10GB_LT_INIT_PRE_TAP_OFFSET C10GB_LT_INIT_PRE_TAP_REG_OFFSET +#define C10GB_LT_INIT_PRE_TAP_SHIFT (0U) +#define C10GB_LT_INIT_PRE_TAP_MASK BIT_MASK_16_BITS << C10GB_LT_INIT_PRE_TAP_SHIFT + +/** + * Maximum Coefficient Limit of Main Tap Configuration Register + * + * The value in this register gives the Maximum value of the main TAP setting + * which will be tested for the TX equalization for the optimum main tap + * settings. + */ +#define C10GB_LT_MAX_MAIN_TAP_REG_OFFSET (0x9U << 2U) + +#define C10GB_LT_MAX_MAIN_TAP_OFFSET C10GB_LT_MAX_MAIN_TAP_REG_OFFSET +#define C10GB_LT_MAX_MAIN_TAP_SHIFT (0U) +#define C10GB_LT_MAX_MAIN_TAP_MASK BIT_MASK_16_BITS << C10GB_LT_MAX_MAIN_TAP_SHIFT + +/** + * Minimum Coefficient Limit of Main Tap Configuration Register + * + * The value in this register gives the minimum value of the main TAP setting + * which will be tested for the TX equalization for the optimum main tap + * settings. + */ +#define C10GB_LT_MIN_MAIN_TAP_REG_OFFSET (0xAU << 2U) + +#define C10GB_LT_MIN_MAIN_TAP_OFFSET C10GB_LT_MIN_MAIN_TAP_REG_OFFSET +#define C10GB_LT_MIN_MAIN_TAP_SHIFT (0U) +#define C10GB_LT_MIN_MAIN_TAP_MASK BIT_MASK_16_BITS << C10GB_LT_MIN_MAIN_TAP_SHIFT + +/** + * Maximum Coefficient Limit of Post Tap Configuration Register + * + * The value in this register gives the Maximum value of the post TAP setting + * which will be tested for the TX equalization for the optimum post tap + * settings. + */ +#define C10GB_LT_MAX_POST_TAP_REG_OFFSET (0xBU << 2U) + +#define C10GB_LT_MAX_POST_TAP_OFFSET C10GB_LT_MAX_POST_TAP_REG_OFFSET +#define C10GB_LT_MAX_POST_TAP_SHIFT (0U) +#define C10GB_LT_MAX_POST_TAP_MASK BIT_MASK_16_BITS << C10GB_LT_MAX_POST_TAP_SHIFT + +/** + * Minimum Coefficient Limit of Post Tap Configuration Register + * + * The value in this register gives the minimum value of the post TAP setting + * which will be tested for the TX equalization for the optimum post tap + * settings. + */ +#define C10GB_LT_MIN_POST_TAP_REG_OFFSET (0xCU << 2U) + +#define C10GB_LT_MIN_POST_TAP_OFFSET C10GB_LT_MIN_POST_TAP_REG_OFFSET +#define C10GB_LT_MIN_POST_TAP_SHIFT (0U) +#define C10GB_LT_MIN_POST_TAP_MASK BIT_MASK_16_BITS << C10GB_LT_MIN_POST_TAP_SHIFT + +/** + * Maximum Coefficient Limit of Pre Tap Configuration Register + * + * The value in this register gives the Maximum value of the pre TAP setting + * which will be tested for the TX equalization for the optimum pre tap + * settings. + */ +#define C10GB_LT_MAX_PRE_TAP_REG_OFFSET (0xDU << 2U) + +#define C10GB_LT_MAX_PRE_TAP_OFFSET C10GB_LT_MAX_PRE_TAP_REG_OFFSET +#define C10GB_LT_MAX_PRE_TAP_SHIFT (0U) +#define C10GB_LT_MAX_PRE_TAP_MASK BIT_MASK_16_BITS << C10GB_LT_MAX_PRE_TAP_SHIFT + +/** + * Minimum Coefficient Limit of Pre Tap Configuration Register + * + * The value in this register gives the minimum value of the pre TAP setting + * which will be tested for the TX equalization for the optimum pre tap + * settings. + */ +#define C10GB_LT_MIN_PRE_TAP_REG_OFFSET (0xEU << 2U) + +#define C10GB_LT_MIN_PRE_TAP_OFFSET C10GB_LT_MIN_PRE_TAP_REG_OFFSET +#define C10GB_LT_MIN_PRE_TAP_SHIFT (0U) +#define C10GB_LT_MIN_PRE_TAP_MASK BIT_MASK_16_BITS << C10GB_LT_MIN_PRE_TAP_SHIFT + +/** + * TX Equalization Register + */ +#define C10GB_LT_TX_EQUAL_REG_OFFSET (0xFU << 2U) + +/** + * TX Equalization Pre Tap Done + * + * A write to “1†in this field indicates that that tx_equalization is done for + * the PRE TAP + */ +#define C10GB_LT_TX_EQUAL_PRE_DONE_OFFSET C10GB_LT_TX_EQUAL_REG_OFFSET +#define C10GB_LT_TX_EQUAL_PRE_DONE_SHIFT (3U) +#define C10GB_LT_TX_EQUAL_PRE_DONE_MASK MASK_BIT_3 + +/** + * TX Equalization Post Tap Done + * + * A write to “1†in this field indicates that that tx_equalization is done for + * the post TAP + */ +#define C10GB_LT_TX_EQUAL_POST_DONE_OFFSET C10GB_LT_TX_EQUAL_REG_OFFSET +#define C10GB_LT_TX_EQUAL_POST_DONE_SHIFT (2U) +#define C10GB_LT_TX_EQUAL_POST_DONE_MASK MASK_BIT_2 + +/** + * TX Equalization Main Tap Done + * + * A write to “1†in this field indicates that that tx_equalization is done for + * the main TAP + */ +#define C10GB_LT_TX_EQUAL_MAIN_DONE_OFFSET C10GB_LT_TX_EQUAL_REG_OFFSET +#define C10GB_LT_TX_EQUAL_MAIN_DONE_SHIFT (1U) +#define C10GB_LT_TX_EQUAL_MAIN_DONE_MASK MASK_BIT_1 + +/** + * TX Equalization Done + * + * A write to “1†in this field indicates that that tx_equalization is done for + * all the TAPs (pre, post and main) + */ +#define C10GB_LT_TX_EQUAL_DONE_OFFSET C10GB_LT_TX_EQUAL_REG_OFFSET +#define C10GB_LT_TX_EQUAL_DONE_SHIFT (0U) +#define C10GB_LT_TX_EQUAL_DONE_MASK MASK_BIT_0 + +/** + * Local Receiver Lock Register + */ +#define C10GB_LT_LOCAL_RCVR_LOCK_REG_OFFSET (0x10U << 2U) + +/** + * Local Receiver Locked + * + * Write “1†to this register from the software once the local receiver is + * ready + */ +#define C10GB_LT_LOCAL_RCVR_LOCKED_OFFSET C10GB_LT_LOCAL_RCVR_LOCK_REG_OFFSET +#define C10GB_LT_LOCAL_RCVR_LOCKED_SHIFT (0U) +#define C10GB_LT_LOCAL_RCVR_LOCKED_MASK MASK_BIT_0 + +/** + * TX New Main Tap Register + */ +#define C10GB_LT_TX_NEW_MAIN_TAP_REG_OFFSET (0x11U << 2U) + +/** + * TX New Main Tap + * + * A value in this register gives the value of the new main tap value, which + * is used to update the TX equalization SerDes registers. + */ +#define C10GB_LT_TX_NEW_MAIN_TAP_OFFSET C10GB_LT_TX_NEW_MAIN_TAP_REG_OFFSET +#define C10GB_LT_TX_NEW_MAIN_TAP_SHIFT (0U) +#define C10GB_LT_TX_NEW_MAIN_TAP_MASK BIT_MASK_8_BITS << C10GB_LT_TX_NEW_MAIN_TAP_SHIFT + +/** + * TX New Post Tap Register + */ +#define C10GB_LT_TX_NEW_POST_TAP_REG_OFFSET (0x12U << 2U) + +/** + * TX New Post Tap + * + * A value in this register gives the value of the new Post tap value, which + * is used to update the TX equalization SerDes registers. + */ +#define C10GB_LT_TX_NEW_POST_TAP_OFFSET C10GB_LT_TX_NEW_POST_TAP_REG_OFFSET +#define C10GB_LT_TX_NEW_POST_TAP_SHIFT (0U) +#define C10GB_LT_TX_NEW_POST_TAP_MASK BIT_MASK_8_BITS << C10GB_LT_TX_NEW_POST_TAP_SHIFT + +/** + * TX New Pre Tap Register + */ +#define C10GB_LT_TX_NEW_PRE_TAP_REG_OFFSET (0x13U << 2U) + +/** + * TX New Pre Tap + * + * A value in this register gives the value of the new Pre tap value, which is + * used to update the TX equalization SerDes registers. + */ +#define C10GB_LT_TX_NEW_PRE_TAP_OFFSET C10GB_LT_TX_NEW_PRE_TAP_REG_OFFSET +#define C10GB_LT_TX_NEW_PRE_TAP_SHIFT (0U) +#define C10GB_LT_TX_NEW_PRE_TAP_MASK BIT_MASK_8_BITS << C10GB_LT_TX_NEW_PRE_TAP_SHIFT + +/** + * Training State Machine Status + */ +#define C10GB_LT_TRAINING_SM_STATUS_REG_OFFSET (0x14U << 2U) + +/** + * Remote Receiver Ready + * + * If “1†it indicates that remote receiver is ready + */ +#define C10GB_LT_REMOTE_TRAINED_OFFSET C10GB_LT_TRAINING_SM_STATUS_REG_OFFSET +#define C10GB_LT_REMOTE_TRAINED_SHIFT (4U) +#define C10GB_LT_REMOTE_TRAINED_MASK MASK_BIT_4 + +/** + * Link Up + * + * If “1†it indicates that Link is ready + */ +#define C10GB_LT_LINK_UP_OFFSET C10GB_LT_TRAINING_SM_STATUS_REG_OFFSET +#define C10GB_LT_LINK_UP_SHIFT (3U) +#define C10GB_LT_LINK_UP_MASK MASK_BIT_3 + +/** + * Training State Machine + * + * The value in this register gives the current values of the training state + * machine. + * IDLE = 3'b000 + * INITIALIZE = 3'b001 + * SEND_TRAINING = 3'b011 + * TRAIN_LOCAL = 3'b010 + * TRAIN_REMOTE = 3'b110 + * LINK_READY = 3'b111 + * SEND_DATA = 3'b101 + * RAINING_FAILURE = 3'b100 + + */ +#define C10GB_LT_TRAINING_SM_OFFSET C10GB_LT_TRAINING_SM_STATUS_REG_OFFSET +#define C10GB_LT_TRAINING_SM_SHIFT (0U) +#define C10GB_LT_TRAINING_SM_MASK BIT_MASK_2_BITS << C10GB_LT_TRAINING_SM_SHIFT + +/** + * TX Updated Status Register + */ +#define C10GB_LT_TX_UPDATED_STATUS_REG_OFFSET (0x15U << 2U) + +/** + * Tx Updated Status Pre Tap + * + * A value in this register gives the value of the current status transmitted + * to the link partner for the pre Tap + * 1 1 = maximum + * 1 0 = minimum + * 0 1 = updated + * 0 0 = not_updated + */ +#define C10GB_LT_TX_UPDATED_STAT_PRE_OFFSET C10GB_LT_TX_UPDATED_STATUS_REG_OFFSET +#define C10GB_LT_TX_UPDATED_STAT_PRE_SHIFT (16U) +#define C10GB_LT_TX_UPDATED_STAT_PRE_MASK BIT_MASK_2_BITS << C10GB_LT_TX_UPDATED_STAT_PRE_SHIFT + +/** + * Tx Updated Status Post Tap + * + * A value in this register gives the value of the current status transmitted + * to the link partner for the post Tap + * 1 1 = maximum + * 1 0 = minimum + * 0 1 = updated + * 0 0 = not_updated + */ +#define C10GB_LT_TX_UPDATED_STAT_POST_OFFSET C10GB_LT_TX_UPDATED_STATUS_REG_OFFSET +#define C10GB_LT_TX_UPDATED_STAT_POST_SHIFT (14U) +#define C10GB_LT_TX_UPDATED_STAT_POST_MASK BIT_MASK_2_BITS << C10GB_LT_TX_UPDATED_STAT_POST_SHIFT + +/** + * Tx Updated Status Main Tap + * + * A value in this register gives the value of the current status transmitted + * to the link partner for the main Tap + * 1 1 = maximum + * 1 0 = minimum + * 0 1 = updated + * 0 0 = not_updated + */ +#define C10GB_LT_TX_UPDATED_STAT_MAIN_OFFSET C10GB_LT_TX_UPDATED_STATUS_REG_OFFSET +#define C10GB_LT_TX_UPDATED_STAT_MAIN_SHIFT (12U) +#define C10GB_LT_TX_UPDATED_STAT_MAIN_MASK BIT_MASK_2_BITS << C10GB_LT_TX_UPDATED_STAT_MAIN_SHIFT + +/** + * Link Training State Pre Tap + * + * A value in this register gives the value of the coefficient update state + * machine for the pre Tap + * IDLE = 3'b000 + * NOT_UPDATED = 3'b001 + * UPDATE_COEFF = 3'b011 + * MAXIMUM = 3'b010 + * UPDATED = 3'b110 + * MINIMUM = 3'b100 + */ +#define C10GB_LT_STATE_PRE_OFFSET C10GB_LT_TX_UPDATED_STATUS_REG_OFFSET +#define C10GB_LT_STATE_PRE_SHIFT (8U) +#define C10GB_LT_STATE_PRE_MASK BIT_MASK_3_BITS << C10GB_LT_STATE_PRE_SHIFT + +/** + * Link Training State Post Tap + * + * A value in this register gives the value of the coefficient update state + * machine for the post Tap + * IDLE = 3'b000 + * NOT_UPDATED = 3'b001 + * UPDATE_COEFF = 3'b011 + * MAXIMUM = 3'b010 + * UPDATED = 3'b110 + * MINIMUM = 3'b100 + */ +#define C10GB_LT_STATE_POST_OFFSET C10GB_LT_TX_UPDATED_STATUS_REG_OFFSET +#define C10GB_LT_STATE_POST_SHIFT (4U) +#define C10GB_LT_STATE_POST_MASK BIT_MASK_3_BITS << C10GB_LT_STATE_POST_SHIFT + +/** + * Link Training State Main Tap + * + * A value in this register gives the value of the coefficient update state + * machine for the main Tap + * IDLE = 3'b000 + * NOT_UPDATED = 3'b001 + * UPDATE_COEFF = 3'b011 + * MAXIMUM = 3'b010 + * UPDATED = 3'b110 + * MINIMUM = 3'b100 + */ +#define C10GB_LT_STATE_MAIN_OFFSET C10GB_LT_TX_UPDATED_STATUS_REG_OFFSET +#define C10GB_LT_STATE_MAIN_SHIFT (0U) +#define C10GB_LT_STATE_MAIN_MASK BIT_MASK_3_BITS << C10GB_LT_STATE_MAIN_SHIFT + +/** + * Received Coefficient Status Register + */ +#define C10GB_LT_RCVD_COEFF_STATUS_REG_OFFSET (0x16U << 2U) + +/** + * New Coefficient Received + * + * A value in this register gives the value of the new coefficient received + * from the link partner + */ +#define C10GB_LT_NEW_RCVD_COEFF_OFFSET C10GB_LT_RCVD_COEFF_STATUS_REG_OFFSET +#define C10GB_LT_NEW_RCVD_COEFF_SHIFT (16U) +#define C10GB_LT_NEW_RCVD_COEFF_MASK BIT_MASK_16_BITS << C10GB_LT_NEW_RCVD_COEFF_SHIFT + +/** + * Preset + * + * 1 = Pre-set coefficients + * 0 = Normal operation + */ +#define C10GB_LT_RCVD_COEFF_PRESET_OFFSET C10GB_LT_RCVD_COEFF_STATUS_REG_OFFSET +#define C10GB_LT_RCVD_COEFF_PRESET_SHIFT (29U) +#define C10GB_LT_RCVD_COEFF_PRESET_MASK MASK_BIT_29 + +/** + * Initialize + * + * 1 = Initialize coefficients + * 0 = Normal operation + */ +#define C10GB_LT_RCVD_COEFF_INIT_OFFSET C10GB_LT_RCVD_COEFF_STATUS_REG_OFFSET +#define C10GB_LT_RCVD_COEFF_INIT_SHIFT (28U) +#define C10GB_LT_RCVD_COEFF_INIT_MASK MASK_BIT_28 + +/** + * Post tap coefficient update + * + * 1 1 = Reserved + * 0 1 = Increment + * 1 0 = Decrement + * 0 0 = Hold + */ +#define C10GB_LT_RCVD_COEFF_POST_UPDATE_OFFSET C10GB_LT_RCVD_COEFF_STATUS_REG_OFFSET +#define C10GB_LT_RCVD_COEFF_POST_UPDATE_SHIFT (20U) +#define C10GB_LT_RCVD_COEFF_POST_UPDATE_MASK \ + BIT_MASK_2_BITS << C10GB_LT_RCVD_COEFF_POST_UPDATE_SHIFT + +/** + * Main tap coefficient update + * + * 1 1 = Reserved + * 0 1 = Increment + * 1 0 = Decrement + * 0 0 = Hold + */ +#define C10GB_LT_RCVD_COEFF_MAIN_UPDATE_OFFSET C10GB_LT_RCVD_COEFF_STATUS_REG_OFFSET +#define C10GB_LT_RCVD_COEFF_MAIN_UPDATE_SHIFT (18U) +#define C10GB_LT_RCVD_COEFF_MAIN_UPDATE_MASK \ + BIT_MASK_2_BITS << C10GB_LT_RCVD_COEFF_MAIN_UPDATE_SHIFT + +/** + * Pre tap coefficient update + * + * 1 1 = Reserved + * 0 1 = Increment + * 1 0 = Decrement + * 0 0 = Hold + */ +#define C10GB_LT_RCVD_COEFF_PRE_UPDATE_OFFSET C10GB_LT_RCVD_COEFF_STATUS_REG_OFFSET +#define C10GB_LT_RCVD_COEFF_PRE_UPDATE_SHIFT (16U) +#define C10GB_LT_RCVD_COEFF_PRE_UPDATE_MASK BIT_MASK_2_BITS << C10GB_LT_RCVD_COEFF_PRE_UPDATE_SHIFT + +/** + * New Status Received + * + * A value in this register gives the value of the new status received from the + * link partner + */ +#define C10GB_LT_NEW_RCVD_STATUS_OFFSET C10GB_LT_RCVD_COEFF_STATUS_REG_OFFSET +#define C10GB_LT_NEW_RCVD_STATUS_SHIFT (0U) +#define C10GB_LT_NEW_RCVD_STATUS_MASK BIT_MASK_16_BITS << C10GB_LT_NEW_RCVD_STATUS_SHIFT + +/** + * Receiver Ready + * + * 1 = The remote receiver has determined that training is completed and is + * prepared to receive data. + * 0 = The remote receiver is requesting that training continue. + */ +#define C10GB_LT_RCVD_COEFF_RCVR_READY_OFFSET C10GB_LT_RCVD_COEFF_STATUS_REG_OFFSET +#define C10GB_LT_RCVD_COEFF_RCVR_READY_SHIFT (15U) +#define C10GB_LT_RCVD_COEFF_RCVR_READY_MASK MASK_BIT_15 + +/** + * Post tap coefficient value + * + * 1 1 = Maximum + * 1 0 = Minimum + * 0 1 = updated + * 0 0 = Not Updated + */ +#define C10GB_LT_RCVD_COEFF_POST_VALUE_OFFSET C10GB_LT_RCVD_COEFF_STATUS_REG_OFFSET +#define C10GB_LT_RCVD_COEFF_POST_VALUE_SHIFT (4U) +#define C10GB_LT_RCVD_COEFF_POST_VALUE_MASK BIT_MASK_2_BITS << C10GB_LT_RCVD_COEFF_POST_VALUE_SHIFT + +/** + * Main tap coefficient value + * + * 1 1 = Maximum + * 1 0 = Minimum + * 0 1 = updated + * 0 0 = Not Updated + */ +#define C10GB_LT_RCVD_COEFF_MAIN_VALUE_OFFSET C10GB_LT_RCVD_COEFF_STATUS_REG_OFFSET +#define C10GB_LT_RCVD_COEFF_MAIN_VALUE_SHIFT (2U) +#define C10GB_LT_RCVD_COEFF_MAIN_VALUE_MASK BIT_MASK_2_BITS << C10GB_LT_RCVD_COEFF_MAIN_VALUE_SHIFT + +/** + * Pre tap coefficient value + * + * 1 1 = Maximum + * 1 0 = Minimum + * 0 1 = updated + * 0 0 = Not Updated + */ +#define C10GB_LT_RCVD_COEFF_PRE_VALUE_OFFSET C10GB_LT_RCVD_COEFF_STATUS_REG_OFFSET +#define C10GB_LT_RCVD_COEFF_PRE_VALUE_SHIFT (0U) +#define C10GB_LT_RCVD_COEFF_PRE_VALUE_MASK BIT_MASK_2_BITS << C10GB_LT_RCVD_COEFF_PRE_VALUE_SHIFT + +/** + * TX Coefficient Configuration Register + */ +#define C10GB_LT_TX_COEFF_CFG_REG_OFFSET (0x18U << 2U) + +/** + * Hold Pre Cursor Send + * + * If “1†then hold coefficient is sent to the link partner for pre cursor + */ +#define C10GB_LT_TX_HOLD_PRE_OFFSET C10GB_LT_TX_COEFF_CFG_REG_OFFSET +#define C10GB_LT_TX_HOLD_PRE_SHIFT (10U) +#define C10GB_LT_TX_HOLD_PRE_MASK MASK_BIT_10 + +/** + * Decrement Pre Cursor Send + * + * If “1†then the decrement coefficient is sent to the link partner for pre + * cursor + */ +#define C10GB_LT_TX_DEC_PRE_OFFSET C10GB_LT_TX_COEFF_CFG_REG_OFFSET +#define C10GB_LT_TX_DEC_PRE_SHIFT (9U) +#define C10GB_LT_TX_DEC_PRE_MASK MASK_BIT_9 + +/** + * Increment Pre Cursor Send + * + * If “1†then the increment coefficient is sent to the link partner for pre + * cursor + */ +#define C10GB_LT_TX_INC_PRE_OFFSET C10GB_LT_TX_COEFF_CFG_REG_OFFSET +#define C10GB_LT_TX_INC_PRE_SHIFT (8U) +#define C10GB_LT_TX_INC_PRE_MASK MASK_BIT_8 + +/** + * Hold Post Cursor Send + * + * If “1†then the hold coefficient is sent to the link partner for post + * cursor + */ +#define C10GB_LT_TX_HOLD_POST_OFFSET C10GB_LT_TX_COEFF_CFG_REG_OFFSET +#define C10GB_LT_TX_HOLD_POST_SHIFT (6U) +#define C10GB_LT_TX_HOLD_POST_MASK MASK_BIT_6 + +/** + * Decrement Post Cursor Send + * + * If “1†then the decrement coefficient is sent to the link partner for post + * cursor + */ +#define C10GB_LT_TX_DEC_POST_OFFSET C10GB_LT_TX_COEFF_CFG_REG_OFFSET +#define C10GB_LT_TX_DEC_POST_SHIFT (5U) +#define C10GB_LT_TX_DEC_POST_MASK MASK_BIT_5 + +/** + * Increment Post Cursor Send + * + * If “1†then the increment coefficient is sent to the link partner for post + * cursor + */ +#define C10GB_LT_TX_INC_POST_OFFSET C10GB_LT_TX_COEFF_CFG_REG_OFFSET +#define C10GB_LT_TX_INC_POST_SHIFT (4U) +#define C10GB_LT_TX_INC_POST_MASK MASK_BIT_4 + +/** + * Hold Main Cursor Send + * + * If “1†then the hold coefficient is sent to the link partner for mian + * cursor + */ +#define C10GB_LT_TX_HOLD_MAIN_OFFSET C10GB_LT_TX_COEFF_CFG_REG_OFFSET +#define C10GB_LT_TX_HOLD_MAIN_SHIFT (2U) +#define C10GB_LT_TX_HOLD_MAIN_MASK MASK_BIT_2 + +/** + * Decrement Main Cursor Send + * + * If “1†then the decrement coefficient is sent to the link partner for main + * cursor + */ +#define C10GB_LT_TX_DEC_MAIN_OFFSET C10GB_LT_TX_COEFF_CFG_REG_OFFSET +#define C10GB_LT_TX_DEC_MAIN_SHIFT (1U) +#define C10GB_LT_TX_DEC_MAIN_MASK MASK_BIT_1 + +/** + * Increment Main Cursor Send + * + * If “1†then the increment coefficient is sent to the link partner for main + * cursor + */ +#define C10GB_LT_TX_INC_MAIN_OFFSET C10GB_LT_TX_COEFF_CFG_REG_OFFSET +#define C10GB_LT_TX_INC_MAIN_SHIFT (0U) +#define C10GB_LT_TX_INC_MAIN_MASK MASK_BIT_0 + +/** + * PRBS error word count register + */ +#define C10GB_LT_PRBS_ERR_WRD_REG_OFFSET (0x1FU << 2U) + +/* + * PRBS error word count + */ +#define C10GB_LT_PRBS_ERR_WRD_CNT_OFFSET C10GB_LT_PRBS_ERR_WRD_REG_OFFSET +#define C10GB_LT_PRBS_ERR_WRD_CNT_SHIFT (0U) +#define C10GB_LT_PRBS_ERR_WRD_CNT_MASK BIT_MASK_32_BITS << C10GB_LT_PRBS_ERR_WRD_CNT_SHIFT + +/** + * 10GBASE-KR Status + */ +#define C10GB_LT_STATUS_REG_OFFSET (0x26U << 2U) + +/** + * RX calibration done + * + * 1’ Indicates remote receiver calibration is done. The Firmware should set + * this bit in response to the RX calibration request. + * This Bit will clear the Bit 2 of this Register. + * + */ +#define C10GB_LT_RX_CAL_DONE_OFFSET C10GB_LT_STATUS_REG_OFFSET +#define C10GB_LT_RX_CAL_DONE_SHIFT (6U) +#define C10GB_LT_RX_CAL_DONE_MASK MASK_BIT_6 + +/** + * Request TX equalization + * + * ‘1’ indicates local receiver is responded for remote receiver rx calibration + * request. + * + */ +#define C10GB_LT_REQ_TX_EQUAL_OFFSET C10GB_LT_STATUS_REG_OFFSET +#define C10GB_LT_REQ_TX_EQUAL_SHIFT (5U) +#define C10GB_LT_REQ_TX_EQUAL_MASK MASK_BIT_5 + +/** + * Signal detect + * + * ‘1’ indicated both local and remote receiver ready. + * + */ +#define C10GB_LT_SIGNAL_DETECT_OFFSET C10GB_LT_STATUS_REG_OFFSET +#define C10GB_LT_SIGNAL_DETECT_SHIFT (4U) +#define C10GB_LT_SIGNAL_DETECT_MASK MASK_BIT_4 + +/** + * Training Fail + * + * ‘1’ indicates 500ms of time is expired during link training + * + */ +#define C10GB_LT_TRAINING_FAIL_OFFSET C10GB_LT_STATUS_REG_OFFSET +#define C10GB_LT_TRAINING_FAIL_SHIFT (3U) +#define C10GB_LT_TRAINING_FAIL_MASK MASK_BIT_3 + +/** + * Request RX calibration + * + * ‘1’ Indicates remote receiver is requested for its calibration + * + */ +#define C10GB_LT_REQ_RX_CAL_OFFSET C10GB_LT_STATUS_REG_OFFSET +#define C10GB_LT_REQ_RX_CAL_SHIFT (2U) +#define C10GB_LT_REQ_RX_CAL_MASK MASK_BIT_2 + +/** + * Link training frame lock + * + * ‘1’ indicates link training frame is detected + * + */ +#define C10GB_LT_FRAME_LOCK_OFFSET C10GB_LT_STATUS_REG_OFFSET +#define C10GB_LT_FRAME_LOCK_SHIFT (1U) +#define C10GB_LT_FRAME_LOCK_MASK MASK_BIT_1 + +/** + * Auto-negotiation good link check + * + * ‘1’ indicates Auto-negotiation is completed + * + */ +#define C10GB_AN_GOOD_CHECK_OFFSET C10GB_LT_STATUS_REG_OFFSET +#define C10GB_AN_GOOD_CHECK_SHIFT (0U) +#define C10GB_AN_GOOD_CHECK_MASK MASK_BIT_0 + +/******************************************************************************/ +/* Core10GBaseKR PHY Transmit Control */ +/******************************************************************************/ + +/** + * Transmit Control Register + */ +#define C10GB_TX_CTRL_REG_OFFSET (0x0U << 2U) + +/** + * PMA Data Select + * + * PMA TX Data Select. Used to select the TX Raw data from the TX Clause Blocks + * to Serdes Interface. + * 2’b00 – PCS Sublayer clause 49 data will be transmitted to the serdes + * interface + * 2’b10 – Auto-negotiation block, PCS sublayer Clause 73 data will be + * transmitted to the serdes interface + * 2’b11 – Link Training block, PCS sublayer Clause 72 data will be transmitted + * to the serdes interface + * 2’b01 – Reserved + + */ +#define C10GB_TX_CTRL_PMA_DATA_OFFSET C10GB_TX_CTRL_REG_OFFSET +#define C10GB_TX_CTRL_PMA_DATA_SHIFT (0U) +#define C10GB_TX_CTRL_PMA_DATA_MASK BIT_MASK_2_BITS << C10GB_TX_CTRL_PMA_DATA_SHIFT + +/** + * XCVR LOS + * + * Loss of sync signal to the XCVR + * 1 - LOS signal is enabled, XCVR will lock to reference + * 0 - LOS signal is disabled, XCVR will lock to data + */ +#define C10GB_TX_CTRL_XCVR_LOS_OFFSET C10GB_TX_CTRL_REG_OFFSET +#define C10GB_TX_CTRL_XCVR_LOS_SHIFT (4U) +#define C10GB_TX_CTRL_XCVR_LOS_MASK MASK_BIT_4 + +/** + * PCS Tx Reset + * + * Soft reset bit for PCS reset + * 1 - TX logic is reset + * 0 - TX logic is not reset + * Self clearing + */ +#define C10GB_TX_CTRL_TX_RESET_OFFSET C10GB_TX_CTRL_REG_OFFSET +#define C10GB_TX_CTRL_TX_RESET_SHIFT (5U) +#define C10GB_TX_CTRL_TX_RESET_MASK MASK_BIT_5 + +/** + * PCS Rx Reset + * + * Soft reset bit for PCS reset + * 1 - RX logic is reset + * 0 - RX logic is not reset + * Self clearing + */ +#define C10GB_TX_CTRL_RX_RESET_OFFSET C10GB_TX_CTRL_REG_OFFSET +#define C10GB_TX_CTRL_RX_RESET_SHIFT (6U) +#define C10GB_TX_CTRL_RX_RESET_MASK MASK_BIT_6 + +/** + * IP version register + */ +#define C10GB_IP_VERSION_REG_OFFSET (0x1U << 2U) + +#define C10GB_IP_VERSION_OFFSET C10GB_IP_VERSION_REG_OFFSET +#define C10GB_IP_VERSION_SHIFT (0U) +#define C10GB_IP_VERSION_MASK BIT_MASK_32_BITS << C10GB_IP_VERSION_SHIFT + +/** + * Major Version + * + * This field provides the major version of the IP. + */ +#define C10GB_IP_VERSION_MAJOR_OFFSET C10GB_IP_VERSION_REG_OFFSET +#define C10GB_IP_VERSION_MAJOR_SHIFT (16U) +#define C10GB_IP_VERSION_MAJOR_MASK BIT_MASK_16_BITS << C10GB_IP_VERSION_MAJOR_SHIFT + +/** + * Minor Version + * + * This field provides the minor version of the IP. + */ +#define C10GB_IP_VERSION_MINOR_OFFSET C10GB_IP_VERSION_REG_OFFSET +#define C10GB_IP_VERSION_MINOR_SHIFT (8U) +#define C10GB_IP_VERSION_MINOR_MASK BIT_MASK_8_BITS << C10GB_IP_VERSION_MINOR_SHIFT + +/** + * Sub Version + * + * This field provides the sub version of the IP. + */ +#define C10GB_IP_VERSION_SUB_OFFSET C10GB_IP_VERSION_REG_OFFSET +#define C10GB_IP_VERSION_SUB_SHIFT (0U) +#define C10GB_IP_VERSION_SUB_MASK BIT_MASK_8_BITS << C10GB_IP_VERSION_SUB_SHIFT + +/** + * FEC user configuration register + */ +#define C10GB_FEC_USE_CFG_REG_OFFSET (0x2U << 2U) + +/** + * FEC user configuration + * + * User has configured FEC Logic as part of the IP + * 0 – FEC Not Configured + * 1 - FEC is Configured + * This register will be read by the software to enable the FEC during the AN + */ +#define C10GB_FEC_USE_CFG_OFFSET C10GB_FEC_USE_CFG_REG_OFFSET +#define C10GB_FEC_USE_CFG_SHIFT (0U) +#define C10GB_FEC_USE_CFG_MASK MASK_BIT_0 + +/******************************************************************************/ +/* Core10GBaseKR PHY Receive Status */ +/******************************************************************************/ + +/** + * Receive Status Register + */ +#define C10GB_RX_STATUS_REG_OFFSET (0x0U << 2U) + +/** + * PCS49 Status + * + * Receive Status signal + * 1 – Receiver has attained Block Lock + * 0 – Receiver has not attained the Block Lock + */ +#define C10GB_RX_STATUS_PCS49_OFFSET C10GB_RX_STATUS_REG_OFFSET +#define C10GB_RX_STATUS_PCS49_SHIFT (0U) +#define C10GB_RX_STATUS_PCS49_MASK BIT_MASK_2_BITS << C10GB_RX_STATUS_PCS49_SHIFT + +/******************************************************************************/ +/* Core10GBaseKR_PHY Memory Map */ +/******************************************************************************/ +#define C10GB_AN_BASE_OFFSET (0x0U << 8U) +#define C10GB_LT_BASE_OFFSET (0x4U << 8U) +#define C10GB_TX_CTRL_BASE_OFFSET (0x8U << 8U) +#define C10GB_RX_STATUS_BASE_OFFSET (0x9U << 8U) + +/// @endcond + +#ifdef __cplusplus +} +#endif + +#endif /* CORE10GBASEKR_PHY_REG_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy_types.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy_types.h new file mode 100644 index 0000000..217e903 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/core10gbasekr_phy_types.h @@ -0,0 +1,419 @@ +/** + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core10gbasekr_phy_types.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Core10GBaseKR_PHY Types + * + */ + +#ifndef CORE10GBASEKR_PHY_TYPES_H_ +#define CORE10GBASEKR_PHY_TYPES_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "hal/hal.h" + +/** + * The phy10gkr_error_t enumeration acts as an error lookup. + * + * - PHY10GKR_ERR_USER_CONFIG implies that the config structure is Null. + * - PHY10GKR_ERR_NO_XCVR implies that there is no pointer to an instance of a XCVR. + * - PHY10GKR_ERR_XCVR_API_FUCNTION_POINTER implies that at least one XCVR API + * function pointer is null. + */ +typedef enum __phy_error +{ + PHY10GKR_ERR_USER_CONFIG = 1, + PHY10GKR_ERR_NO_XCVR = 2, + PHY10GKR_ERR_XCVR_API_FUNCTION_POINTER = 3, +} phy10gkr_error_t; + +/** + * The phy10gkr_lane_los_t enumeration identifies the state of the lane loss + * detection. + */ +typedef enum __lane_los_state +{ + LANE_LOS_LOCK_TO_DATA = 0, + LANE_LOS_LOCK_TO_REF = 1 +} phy10gkr_lane_los_t; + +/** + * The phy10gkr_timer_t structure describes the start and end time instance of + * the measurement with the PHY driver. + */ +typedef struct __phy_timer +{ + uint32_t start; + uint32_t end; +} phy10gkr_timer_t; + +/** + * The phy10gkr_an_state_t enumeration is used to identify the state of the auto- + * negotiation arbitration state machine, which is implemented by the + * Core10GBaseKR_PHY. + */ +typedef enum __an_state +{ + ST_AUTO_NEG_ENABLE = 0x0, + ST_TRANSMIT_DISABLE = 0x1, + ST_ABILITY_DETECT = 0x2, + ST_ACKNOWLEDGE_DETECT = 0x3, + ST_COMPLETE_ACKNOWLEDEGE = 0x4, + ST_AN_GOOD_CHECK = 0x5, + ST_AN_GOOD = 0x6, + ST_NEXT_PAGE_WAIT = 0x7, + ST_NEXT_PAGE_WAIT_TX_IDLE = 0x8, + ST_LINK_STATUS_CHECK = 0x9, + ST_PARALLEL_DETECTION_FAULT = 0xA +} phy10gkr_an_state_t; + +/** + * The phy10gkr_an_status_t enumeration specifies the status of auto-negotiation. + */ +typedef enum __an_status +{ + STATUS_AN_INCOMPLETE, + STATUS_AN_COMPLETE +} phy10gkr_an_status_t; + +/** + * The phy10gkr_an_api_state_t enumeration identifies the state of the + * auto-negotiation state machine API. + */ +typedef enum __api_status +{ + AN_API_SM_INIT, + AN_API_SM_STATUS_UPDATE +} phy10gkr_an_api_state_t; + +/** + * The phy10gkr_an_instance_t struct describes an instance of the + * auto-negotiation parameters. + */ +typedef struct __an_instance +{ + phy10gkr_an_state_t state; + phy10gkr_an_api_state_t api_state; + + uint32_t complete_cnt; + phy10gkr_an_status_t status; + + uint64_t adv_ability; + uint64_t lp_bp_adv_ability; +} phy10gkr_an_instance_t; + +/** + * The phy10gkr_lt_state_t enumeration identifies the link training state machine + * state implemented by the Core10GBaseKr_PHY IP block. + */ +typedef enum __lt_state +{ + LT_STATE_IDLE = 0, + LT_STATE_INITIALIZE = 1, + LT_STATE_SEND_TRAINING = 3, + LT_STATE_TRAIN_LOCAL = 2, + LT_STATE_TRAIN_REMOTE = 6, + LT_STATE_LINK_READY = 7, + LT_STATE_SEND_DATA = 5, + LT_STATE_FAILURE = 4 +} phy10gkr_lt_state_t; + +/** + * The phy10gkr_lt_link_status_t enumeration identifies the status and state of + * the link with the link partner. + */ +typedef enum __lt_link_status +{ + STATUS_LT_INCOMPLETE, + STATUS_LT_COMPLETE, + STATUS_LT_LINK_MAINTAINED, + STATUS_LT_FAILURE +} phy10gkr_lt_link_status_t; + +/** + * The phy10gkr_lt_api_state_t enumeration identifies the link training API state + * machine state. + */ +typedef enum __lt_api_status +{ + LT_API_SM_INIT, + LT_API_SM_STATUS_UPDATE +} phy10gkr_lt_api_state_t; + +/** + * The phy10gkr_coeff_update_status_t enumeration identifies the status of the + * coefficient sweep algorithm. + */ +typedef enum coeff_update_status +{ + SWEEP_NOT_STARTED, + SWEEP_START, + SWEEP_INCOMPLETE, + SWEEP_COMPLETE, +} phy10gkr_coeff_update_status_t; + +/** + * The phy10gkr_calirbation_request_t enumeration identifies the initial conditions of a device. For + * example, the local device will calibrate using a preset request. + */ +typedef enum __calirbation_request +{ + C10GBKR_LT_PRESET = 0U, + C10GBKR_LT_INITALISE = 1U +} phy10gkr_calirbation_request_t; + +/** + * The phy10gkr_coeff_status_report_t enumeration identifies the link training + * status report update. + */ +typedef enum coeff_status_report +{ + LT_COEFF_STATUS_NOT_UPDATED = 0U, + LT_COEFF_STATUS_UPDATED = 1U, + LT_COEFF_STATUS_MIN = 2U, + LT_COEFF_STATUS_MAX = 3U +} phy10gkr_coeff_status_report_t; + +/** + * The phy10gkr_tx_equalizer_tap_t enumeration specifies the three different transmitter taps. + */ +typedef enum tx_equalizer_tap +{ + PRE_TAP, + MAIN_TAP, + POST_TAP +} phy10gkr_tx_equalizer_tap_t; + +/** + * The phy10gkr_tap_cal_state_t enumeration specifies the state of the link partner calibration + * algorithm. + */ +typedef enum tap_cal_state +{ + TAP_MAX_CAL, + TAP_MIN_CAL, + TAP_OPTIMISE_CAL +} phy10gkr_tap_cal_state_t; + +/** + * The phy10gkr_local_rxcvr_lock_t enumeration specifies the condition of the + * link partner calibration algorithm. During training, when the link partner has + * been calibrated, the local receiver ready lock is locked and the status report + * is updated to notify the link partner. + */ +typedef enum local_rxcvr_lock +{ + LOCAL_RXCVR_UNLOCKED = 0, + LOCAL_RXCVR_LOCKED = 1 +} phy10gkr_local_rxcvr_lock_t; + +/** + * The phy10gkr_coeff_update_t struct describes an instance of the link training + * coefficient update. This structure supports calibrating the link partners + * transmitter taps. + */ +typedef struct coeff_update +{ + uint32_t cnt; + uint32_t inc_cnt; + uint32_t dec_cnt; + + phy10gkr_tap_cal_state_t lp_tap_cal_state; + + uint32_t optimal_index; + uint32_t optimal_cnt; +} phy10gkr_coeff_update_t; + +/** + * The phy10gkr_lt_instance_t struct describes an instance of the link training + * parameters. + */ +typedef struct __lt_instance +{ + phy10gkr_lt_state_t state; + phy10gkr_lt_api_state_t api_state; + phy10gkr_lt_link_status_t status; + + phy10gkr_timer_t timer; + + uint32_t fail_cnt; + uint32_t complete_cnt; + uint32_t tx_equ_cnt; + uint32_t rx_cal_cnt; + uint32_t sig_cnt; + uint32_t rcvr_cnt; + uint32_t sm_cycle_cnt; + + phy10gkr_tx_equalizer_tap_t lp_cal_sweep_state; + phy10gkr_coeff_update_t main; + phy10gkr_coeff_update_t post; + phy10gkr_coeff_update_t pre; + + phy10gkr_local_rxcvr_lock_t local_rxcvr; +} phy10gkr_lt_instance_t; + +/** + * The phy10gkr_state_t enumeration identifies the state of the 10GBASE-KR state + * machine. + */ +typedef enum __c10gbkr_state +{ + AN_SERDES_CONFIG, + AN_SM, + LT_SERDES_CONFIG, + LT_SM, + LINK_ESTABLISHED_CHECK +} phy10gkr_state_t; + +/** + * The phy10gkr_status_t enumeration identifies the status of the 10GBASE-KR + * state machine. + * + * This enumeration can identify failures encountered by the 10GBASE-KR + * algorithm. + */ +typedef enum __c10gbkr_status +{ + AN_SERDES_CONFIGURATION = 1, + AN_IN_PROGRESS = 2, + AN_COMPLETE = 3, + LT_SERDES_CONFIGURATION = 4, + LT_SERDES_CAL_FAILURE = 5, + LT_SERDES_CAL_COMPLETE = 6, + LT_IN_PROGRESS = 7, + LT_FAILURE = 8, + LINK_BROKEN = 9, + LINK_ESTABLISHED = 0 +} phy10gkr_status_t; + +/** + * The phy10gkr_xcvr_api_t structure identifies the required XCVR APIs that this + * driver requires to complete IEEE802.3 Clause 72 and 73. All function pointers + * require a return value of type uint8_t. Each function that the pointers point to + * require void pointer to an XCVR instance, with the exception of tx_equalization + * which requires three additional parameters, absolute tap coefficients. + * + * - init: Initialise the XCVR which is implemented in the hardware design. + * - auto_neg_data_rate: Configure the XCVR for auto-negotiation. + * - link_training_data_rate: Configure the XCVR for link training. + * - cdr_lock: Check if CDR is locked and return success or failure, where 0 is + * success. + * - ctle_cal: Start CTLE calibration. + * - ctle_cal_status: Check if CTLE is complete and return success or failure, where 0 is + * success. + * - dfe_cal: Start DFE calibration. + * - dfe_cal_status: Check if DFE is complete and return success or failure, where 0 is + * success. + * - reset_pcs_rx: Reset the XCVR PCS RX path. + * - tx_equalization: Set the XCVR coefficients which are passed as parameters, + * where tx_main_tap implies C(0), tx_post_tap implies C(+1) and tx_pre_tap implies + * C(-1). These coefficients are absolute but the XCVR may require the signed + * value. + */ +typedef struct __phy10gkr_xcvr_api +{ + uint8_t (*init)(void *xcvr); + uint8_t (*auto_neg_data_rate)(void *xcvr); + uint8_t (*link_training_data_rate)(void *xcvr); + uint8_t (*cdr_lock)(void *xcvr); + uint8_t (*ctle_cal)(void *xcvr); + uint8_t (*ctle_cal_status)(void *xcvr); + uint8_t (*dfe_cal)(void *xcvr); + uint8_t (*dfe_cal_status)(void *xcvr); + uint8_t (*reset_pcs_rx)(void *xcvr); + uint8_t (*tx_equalization)(void *xcvr, + uint32_t tx_main_tap, + uint32_t tx_post_tap, + uint32_t tx_pre_tap); +} phy10gkr_xcvr_api_t; + +/** + * The phy10gkr_cfg_t struct describes an instance of the Core10GBaseKR_PHY + * configuration parameters. + */ +typedef struct __phy10gkr_cfg +{ + /* XCVR APIs */ + phy10gkr_xcvr_api_t xcvr_api; + + /* PHY Configurations */ + uint32_t fec_request; + + /* Link Training */ + uint32_t rx_calibration_request; + + uint32_t main_preset_tap_coeff; + uint32_t post_preset_tap_coeff; + uint32_t pre_preset_tap_coeff; + + uint32_t main_initialize_tap_coeff; + uint32_t post_initialize_tap_coeff; + uint32_t pre_initialize_tap_coeff; + + uint32_t main_max_tap_ceoff; + uint32_t main_min_tap_ceoff; + uint32_t post_max_tap_ceoff; + uint32_t post_min_tap_ceoff; + uint32_t pre_max_tap_ceoff; + uint32_t pre_min_tap_ceoff; +} phy10gkr_cfg_t; + +/** + * The phy10gkr_instance_t struct describes an instance of the Core10GBaseKR_PHY + * parameters. + */ +typedef struct __phy10gkr_instance +{ + addr_t base_addr; + addr_t an_base_addr; + addr_t lt_base_addr; + addr_t tx_ctrl_base_addr; + addr_t rx_status_base_addr; + + phy10gkr_an_instance_t an; + phy10gkr_lt_instance_t lt; + + phy10gkr_state_t c10gbkr_state; + phy10gkr_status_t c10gbkr_status; + + uint32_t serdes_id; + uint32_t serdes_lane_id; + + uint32_t fec_configured; /* FEC configured in RTL */ + uint32_t fec_negotiated; /* FEC negotiated with link partner */ + + /* Configurations */ + void *xcvr; + phy10gkr_xcvr_api_t xcvr_api; + + uint32_t fec_request; /* Request FEC */ + phy10gkr_calirbation_request_t rx_calibration_request; + + uint32_t main_preset_tap_coeff; + uint32_t post_preset_tap_coeff; + uint32_t pre_preset_tap_coeff; + + uint32_t main_initialize_tap_coeff; + uint32_t post_initialize_tap_coeff; + uint32_t pre_initialize_tap_coeff; + + uint32_t main_max_tap_ceoff; + uint32_t main_min_tap_ceoff; + uint32_t post_max_tap_ceoff; + uint32_t post_min_tap_ceoff; + uint32_t pre_max_tap_ceoff; + uint32_t pre_min_tap_ceoff; +} phy10gkr_instance_t; + +#ifdef __cplusplus +} +#endif + +#endif /* CORE10GBASEKR_PHY_TYPES_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/uint_32_bit_masks.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/uint_32_bit_masks.h new file mode 100644 index 0000000..70c5f23 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/Core10GBaseKR_PHY/uint_32_bit_masks.h @@ -0,0 +1,94 @@ +/** + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file uint_32_bit_masks.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief unsigned 32 bit masks + * + */ + +#ifndef UNIT_32_BIT_MASKS_H_ +#define UNIT_32_BIT_MASKS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Single Bit Mask + */ +#define MASK_BIT_31 ((uint32_t)(0x80000000UL)) +#define MASK_BIT_30 ((uint32_t)(0x40000000UL)) +#define MASK_BIT_29 ((uint32_t)(0x20000000UL)) +#define MASK_BIT_28 ((uint32_t)(0x10000000UL)) +#define MASK_BIT_27 ((uint32_t)(0x08000000UL)) +#define MASK_BIT_26 ((uint32_t)(0x04000000UL)) +#define MASK_BIT_25 ((uint32_t)(0x02000000UL)) +#define MASK_BIT_24 ((uint32_t)(0x01000000UL)) +#define MASK_BIT_23 ((uint32_t)(0x00800000UL)) +#define MASK_BIT_22 ((uint32_t)(0x00400000UL)) +#define MASK_BIT_21 ((uint32_t)(0x00200000UL)) +#define MASK_BIT_20 ((uint32_t)(0x00100000UL)) +#define MASK_BIT_19 ((uint32_t)(0x00080000UL)) +#define MASK_BIT_18 ((uint32_t)(0x00040000UL)) +#define MASK_BIT_17 ((uint32_t)(0x00020000UL)) +#define MASK_BIT_16 ((uint32_t)(0x00010000UL)) +#define MASK_BIT_15 ((uint32_t)(0x00008000UL)) +#define MASK_BIT_14 ((uint32_t)(0x00004000UL)) +#define MASK_BIT_13 ((uint32_t)(0x00002000UL)) +#define MASK_BIT_12 ((uint32_t)(0x00001000UL)) +#define MASK_BIT_11 ((uint32_t)(0x00000800UL)) +#define MASK_BIT_10 ((uint32_t)(0x00000400UL)) +#define MASK_BIT_9 ((uint32_t)(0x00000200UL)) +#define MASK_BIT_8 ((uint32_t)(0x00000100UL)) +#define MASK_BIT_7 ((uint32_t)(0x00000080UL)) +#define MASK_BIT_6 ((uint32_t)(0x00000040UL)) +#define MASK_BIT_5 ((uint32_t)(0x00000020UL)) +#define MASK_BIT_4 ((uint32_t)(0x00000010UL)) +#define MASK_BIT_3 ((uint32_t)(0x00000008UL)) +#define MASK_BIT_2 ((uint32_t)(0x00000004UL)) +#define MASK_BIT_1 ((uint32_t)(0x00000002UL)) +#define MASK_BIT_0 ((uint32_t)(0x00000001UL)) + +/** + * Multi Bit Mask + */ +#define BIT_MASK_32_BITS ((uint32_t)(0xFFFFFFFFUL)) +#define BIT_MASK_31_BITS ((uint32_t)(0x7FFFFFFFUL)) +#define BIT_MASK_30_BITS ((uint32_t)(0x3FFFFFFFUL)) +#define BIT_MASK_29_BITS ((uint32_t)(0x1FFFFFFFUL)) +#define BIT_MASK_28_BITS ((uint32_t)(0x0FFFFFFFUL)) +#define BIT_MASK_27_BITS ((uint32_t)(0x07FFFFFFUL)) +#define BIT_MASK_26_BITS ((uint32_t)(0x03FFFFFFUL)) +#define BIT_MASK_25_BITS ((uint32_t)(0x01FFFFFFUL)) +#define BIT_MASK_24_BITS ((uint32_t)(0x00FFFFFFUL)) +#define BIT_MASK_23_BITS ((uint32_t)(0x007FFFFFUL)) +#define BIT_MASK_22_BITS ((uint32_t)(0x003FFFFFUL)) +#define BIT_MASK_21_BITS ((uint32_t)(0x001FFFFFUL)) +#define BIT_MASK_20_BITS ((uint32_t)(0x000FFFFFUL)) +#define BIT_MASK_19_BITS ((uint32_t)(0x0007FFFFUL)) +#define BIT_MASK_18_BITS ((uint32_t)(0x0003FFFFUL)) +#define BIT_MASK_17_BITS ((uint32_t)(0x0001FFFFUL)) +#define BIT_MASK_16_BITS ((uint32_t)(0x0000FFFFUL)) +#define BIT_MASK_15_BITS ((uint32_t)(0x00007FFFUL)) +#define BIT_MASK_14_BITS ((uint32_t)(0x00003FFFUL)) +#define BIT_MASK_13_BITS ((uint32_t)(0x00001FFFUL)) +#define BIT_MASK_12_BITS ((uint32_t)(0x00000FFFUL)) +#define BIT_MASK_11_BITS ((uint32_t)(0x000007FFUL)) +#define BIT_MASK_10_BITS ((uint32_t)(0x000003FFUL)) +#define BIT_MASK_9_BITS ((uint32_t)(0x000001FFUL)) +#define BIT_MASK_8_BITS ((uint32_t)(0x000000FFUL)) +#define BIT_MASK_7_BITS ((uint32_t)(0x0000007FUL)) +#define BIT_MASK_6_BITS ((uint32_t)(0x0000003FUL)) +#define BIT_MASK_5_BITS ((uint32_t)(0x0000001FUL)) +#define BIT_MASK_4_BITS ((uint32_t)(0x0000000FUL)) +#define BIT_MASK_3_BITS ((uint32_t)(0x00000007UL)) +#define BIT_MASK_2_BITS ((uint32_t)(0x00000003UL)) + +#ifdef __cplusplus +} +#endif + +#endif /* UNIT_32_BIT_MASKS_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreGPIO/core_gpio.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreGPIO/core_gpio.c new file mode 100644 index 0000000..f8ed780 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreGPIO/core_gpio.c @@ -0,0 +1,533 @@ +/******************************************************************************* + * (c) Copyright 2008-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core_gpio.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreGPIO bare metal driver implementation. + * + */ +#include "coregpio_regs.h" +#include "core_gpio.h" + +/*-------------------------------------------------------------------------*//** + * + */ +#define GPIO_INT_ENABLE_MASK (uint32_t)0x00000008UL +#define OUTPUT_BUFFER_ENABLE_MASK 0x00000004UL + + +#define NB_OF_GPIO 32 + +#define CLEAR_ALL_IRQ32 (uint32_t)0xFFFFFFFF +#define CLEAR_ALL_IRQ16 (uint16_t)0xFFFF +#define CLEAR_ALL_IRQ8 (uint8_t)0xFF + +/*-------------------------------------------------------------------------*//** + * GPIO_init() + * See "core_gpio.h" for details of how to use this function. + */ +void GPIO_init +( + gpio_instance_t * this_gpio, + addr_t base_addr, + gpio_apb_width_t bus_width +) +{ + uint8_t i = 0; + addr_t cfg_reg_addr = base_addr; + + this_gpio->base_addr = base_addr; + this_gpio->apb_bus_width = bus_width; + + /* Clear configuration. */ + for( i = 0, cfg_reg_addr = base_addr; i < NB_OF_GPIO; ++i ) + { + HW_set_8bit_reg( cfg_reg_addr, 0 ); + cfg_reg_addr += 4; + } + /* Clear any pending interrupts */ + switch( this_gpio->apb_bus_width ) + { + case GPIO_APB_32_BITS_BUS: + HAL_set_32bit_reg( this_gpio->base_addr, IRQ, CLEAR_ALL_IRQ32 ); + break; + + case GPIO_APB_16_BITS_BUS: + HAL_set_16bit_reg( this_gpio->base_addr, IRQ0, (uint16_t)CLEAR_ALL_IRQ16 ); + HAL_set_16bit_reg( this_gpio->base_addr, IRQ1, (uint16_t)CLEAR_ALL_IRQ16 ); + break; + + case GPIO_APB_8_BITS_BUS: + HAL_set_8bit_reg( this_gpio->base_addr, IRQ0, (uint8_t)CLEAR_ALL_IRQ8 ); + HAL_set_8bit_reg( this_gpio->base_addr, IRQ1, (uint8_t)CLEAR_ALL_IRQ8 ); + HAL_set_8bit_reg( this_gpio->base_addr, IRQ2, (uint8_t)CLEAR_ALL_IRQ8 ); + HAL_set_8bit_reg( this_gpio->base_addr, IRQ3, (uint8_t)CLEAR_ALL_IRQ8 ); + break; + + default: + HAL_ASSERT(0); + break; + } +} + +/*-------------------------------------------------------------------------*//** + * GPIO_config + * See "core_gpio.h" for details of how to use this function. + */ +void GPIO_config +( + gpio_instance_t * this_gpio, + gpio_id_t port_id, + uint32_t config +) +{ + HAL_ASSERT( port_id < NB_OF_GPIO ); + + if ( port_id < NB_OF_GPIO ) + { + uint32_t cfg_reg_addr = this_gpio->base_addr; + cfg_reg_addr += (port_id * 4); + HW_set_32bit_reg( cfg_reg_addr, config ); + + /* + * Verify that the configuration was correctly written. Failure to read + * back the expected value may indicate that the GPIO port was configured + * as part of the hardware flow and cannot be modified through software. + * It may also indicate that the base address passed as parameter to + * GPIO_init() was incorrect. + */ + HAL_ASSERT( HW_get_32bit_reg( cfg_reg_addr ) == config ); + } +} + +/*-------------------------------------------------------------------------*//** + * GPIO_set_outputs + * See "core_gpio.h" for details of how to use this function. + */ +void GPIO_set_outputs +( + gpio_instance_t * this_gpio, + uint32_t value +) +{ + switch( this_gpio->apb_bus_width ) + { + case GPIO_APB_32_BITS_BUS: + HAL_set_32bit_reg( this_gpio->base_addr, GPIO_OUT, value ); + break; + + case GPIO_APB_16_BITS_BUS: + HAL_set_16bit_reg( this_gpio->base_addr, GPIO_OUT0, (uint16_t)value ); + HAL_set_16bit_reg( this_gpio->base_addr, GPIO_OUT1, (uint16_t)(value >> 16) ); + break; + + case GPIO_APB_8_BITS_BUS: + HAL_set_8bit_reg( this_gpio->base_addr, GPIO_OUT0, (uint8_t)value ); + HAL_set_8bit_reg( this_gpio->base_addr, GPIO_OUT1, (uint8_t)(value >> 8) ); + HAL_set_8bit_reg( this_gpio->base_addr, GPIO_OUT2, (uint8_t)(value >> 16) ); + HAL_set_8bit_reg( this_gpio->base_addr, GPIO_OUT3, (uint8_t)(value >> 24) ); + break; + + default: + HAL_ASSERT(0); + break; + } + + /* + * Verify that the output register was correctly written. Failure to read back + * the expected value may indicate that some of the GPIOs may not exist due to + * the number of GPIOs selected in the CoreGPIO hardware flow configuration. + * It may also indicate that the base address or APB bus width passed as + * parameter to the GPIO_init() function do not match the hardware design. + */ + HAL_ASSERT( GPIO_get_outputs( this_gpio ) == value ); +} + +/*-------------------------------------------------------------------------*//** + * GPIO_get_inputs + * See "core_gpio.h" for details of how to use this function. + */ +uint32_t GPIO_get_inputs +( + gpio_instance_t * this_gpio +) +{ + uint32_t gpio_in = 0; + + switch( this_gpio->apb_bus_width ) + { + case GPIO_APB_32_BITS_BUS: + gpio_in = HAL_get_32bit_reg( this_gpio->base_addr, GPIO_IN ); + break; + + case GPIO_APB_16_BITS_BUS: + gpio_in |= HAL_get_16bit_reg( this_gpio->base_addr, GPIO_IN0 ); + gpio_in |= (HAL_get_16bit_reg( this_gpio->base_addr, GPIO_IN1 ) << 16); + break; + + case GPIO_APB_8_BITS_BUS: + gpio_in |= HAL_get_8bit_reg( this_gpio->base_addr, GPIO_IN0 ); + gpio_in |= (HAL_get_8bit_reg( this_gpio->base_addr, GPIO_IN1 ) << 8); + gpio_in |= (HAL_get_8bit_reg( this_gpio->base_addr, GPIO_IN2 ) << 16); + gpio_in |= (HAL_get_8bit_reg( this_gpio->base_addr, GPIO_IN3 ) << 24); + break; + + default: + HAL_ASSERT(0); + break; + } + + return gpio_in; +} + +/*-------------------------------------------------------------------------*//** + * GPIO_get_outputs + * See "core_gpio.h" for details of how to use this function. + */ +uint32_t GPIO_get_outputs +( + gpio_instance_t * this_gpio +) +{ + uint32_t gpio_out = 0; + + switch( this_gpio->apb_bus_width ) + { + case GPIO_APB_32_BITS_BUS: + gpio_out = HAL_get_32bit_reg( this_gpio->base_addr, GPIO_OUT ); + break; + + case GPIO_APB_16_BITS_BUS: + gpio_out |= HAL_get_16bit_reg( this_gpio->base_addr, GPIO_OUT0 ); + gpio_out |= (HAL_get_16bit_reg( this_gpio->base_addr, GPIO_OUT1 ) << 16); + break; + + case GPIO_APB_8_BITS_BUS: + gpio_out |= HAL_get_16bit_reg( this_gpio->base_addr, GPIO_OUT0 ); + gpio_out |= (HAL_get_16bit_reg( this_gpio->base_addr, GPIO_OUT1 ) << 8); + gpio_out |= (HAL_get_16bit_reg( this_gpio->base_addr, GPIO_OUT2 ) << 16); + gpio_out |= (HAL_get_16bit_reg( this_gpio->base_addr, GPIO_OUT3 ) << 24); + break; + + default: + HAL_ASSERT(0); + break; + } + + return gpio_out; +} + +/*-------------------------------------------------------------------------*//** + * GPIO_set_output + * See "core_gpio.h" for details of how to use this function. + */ +void GPIO_set_output +( + gpio_instance_t * this_gpio, + gpio_id_t port_id, + uint8_t value +) +{ + HAL_ASSERT( port_id < NB_OF_GPIO ); + + + switch( this_gpio->apb_bus_width ) + { + case GPIO_APB_32_BITS_BUS: + { + uint32_t outputs_state; + + outputs_state = HAL_get_32bit_reg( this_gpio->base_addr, GPIO_OUT ); + if ( 0 == value ) + { + outputs_state &= ~(1 << port_id); + } + else + { + outputs_state |= 1 << port_id; + } + HAL_set_32bit_reg( this_gpio->base_addr, GPIO_OUT, outputs_state ); + + /* + * Verify that the output register was correctly written. Failure to read back + * the expected value may indicate that some of the GPIOs may not exist due to + * the number of GPIOs selected in the CoreGPIO hardware flow configuration. + * It may also indicate that the base address or APB bus width passed as + * parameter to the GPIO_init() function do not match the hardware design. + */ + HAL_ASSERT( HAL_get_32bit_reg( this_gpio->base_addr, GPIO_OUT ) == outputs_state ); + } + break; + + case GPIO_APB_16_BITS_BUS: + { + uint16_t outputs_state; + uint32_t gpio_out_reg_addr = this_gpio->base_addr + GPIO_OUT_REG_OFFSET + ((port_id >> 4) * 4); + + outputs_state = HW_get_16bit_reg( gpio_out_reg_addr ); + if ( 0 == value ) + { + outputs_state &= ~(1 << (port_id & 0x0F)); + } + else + { + outputs_state |= 1 << (port_id & 0x0F); + } + HW_set_16bit_reg( gpio_out_reg_addr, outputs_state ); + + /* + * Verify that the output register was correctly written. Failure to read back + * the expected value may indicate that some of the GPIOs may not exist due to + * the number of GPIOs selected in the CoreGPIO hardware flow configuration. + * It may also indicate that the base address or APB bus width passed as + * parameter to the GPIO_init() function do not match the hardware design. + */ + HAL_ASSERT( HW_get_16bit_reg( gpio_out_reg_addr ) == outputs_state ); + } + break; + + case GPIO_APB_8_BITS_BUS: + { + uint8_t outputs_state; + uint32_t gpio_out_reg_addr = this_gpio->base_addr + GPIO_OUT_REG_OFFSET + ((port_id >> 3) * 4); + + outputs_state = HW_get_8bit_reg( gpio_out_reg_addr ); + if ( 0 == value ) + { + outputs_state &= ~(1 << (port_id & 0x07)); + } + else + { + outputs_state |= 1 << (port_id & 0x07); + } + HW_set_8bit_reg( gpio_out_reg_addr, outputs_state ); + + /* + * Verify that the output register was correctly written. Failure to read back + * the expected value may indicate that some of the GPIOs may not exist due to + * the number of GPIOs selected in the CoreGPIO hardware flow configuration. + * It may also indicate that the base address or APB bus width passed as + * parameter to the GPIO_init() function do not match the hardware design. + */ + HAL_ASSERT( HW_get_8bit_reg( gpio_out_reg_addr ) == outputs_state ); + } + break; + + default: + HAL_ASSERT(0); + break; + } +} + +/*-------------------------------------------------------------------------*//** + * GPIO_drive_inout + * See "core_gpio.h" for details of how to use this function. + */ +void GPIO_drive_inout +( + gpio_instance_t * this_gpio, + gpio_id_t port_id, + gpio_inout_state_t inout_state +) +{ + uint32_t config; + uint32_t cfg_reg_addr = this_gpio->base_addr; + + HAL_ASSERT( port_id < NB_OF_GPIO ); + + switch( inout_state ) + { + case GPIO_DRIVE_HIGH: + /* Set output high */ + GPIO_set_output( this_gpio, port_id, 1 ); + + /* Enable output buffer */ + cfg_reg_addr = this_gpio->base_addr + (port_id * 4); + config = HW_get_8bit_reg( cfg_reg_addr ); + config |= OUTPUT_BUFFER_ENABLE_MASK; + HW_set_8bit_reg( cfg_reg_addr, config ); + break; + + case GPIO_DRIVE_LOW: + /* Set output low */ + GPIO_set_output( this_gpio, port_id, 0 ); + + /* Enable output buffer */ + cfg_reg_addr = this_gpio->base_addr + (port_id * 4); + config = HW_get_8bit_reg( cfg_reg_addr ); + config |= OUTPUT_BUFFER_ENABLE_MASK; + HW_set_8bit_reg( cfg_reg_addr, config ); + break; + + case GPIO_HIGH_Z: + /* Disable output buffer */ + cfg_reg_addr = this_gpio->base_addr + (port_id * 4); + config = HW_get_8bit_reg( cfg_reg_addr ); + config &= ~OUTPUT_BUFFER_ENABLE_MASK; + HW_set_8bit_reg( cfg_reg_addr, config ); + break; + + default: + HAL_ASSERT(0); + break; + } +} + +/*-------------------------------------------------------------------------*//** + * GPIO_enable_irq + * See "core_gpio.h" for details of how to use this function. + */ +void GPIO_enable_irq +( + gpio_instance_t * this_gpio, + gpio_id_t port_id +) +{ + uint32_t cfg_value; + uint32_t cfg_reg_addr = this_gpio->base_addr; + + HAL_ASSERT( port_id < NB_OF_GPIO ); + + if ( port_id < NB_OF_GPIO ) + { + cfg_reg_addr += (port_id * 4); + cfg_value = HW_get_8bit_reg( cfg_reg_addr ); + cfg_value |= GPIO_INT_ENABLE_MASK; + HW_set_8bit_reg( cfg_reg_addr, cfg_value ); + } +} + +/*-------------------------------------------------------------------------*//** + * GPIO_disable_irq + * See "core_gpio.h" for details of how to use this function. + */ +void GPIO_disable_irq +( + gpio_instance_t * this_gpio, + gpio_id_t port_id +) +{ + uint32_t cfg_value; + uint32_t cfg_reg_addr = this_gpio->base_addr; + + HAL_ASSERT( port_id < NB_OF_GPIO ); + + if ( port_id < NB_OF_GPIO ) + { + cfg_reg_addr += (port_id * 4); + cfg_value = HW_get_8bit_reg( cfg_reg_addr ); + cfg_value &= ~GPIO_INT_ENABLE_MASK; + HW_set_8bit_reg( cfg_reg_addr, cfg_value ); + } +} + +/*-------------------------------------------------------------------------*//** + * GPIO_clear_irq + * See "core_gpio.h" for details of how to use this function. + */ +void GPIO_clear_irq +( + gpio_instance_t * this_gpio, + gpio_id_t port_id +) +{ + uint32_t irq_clr_value = ((uint32_t)1) << ((uint32_t)port_id); + + switch( this_gpio->apb_bus_width ) + { + case GPIO_APB_32_BITS_BUS: + HAL_set_32bit_reg( this_gpio->base_addr, IRQ, irq_clr_value ); + break; + + case GPIO_APB_16_BITS_BUS: + HAL_set_16bit_reg( this_gpio->base_addr, IRQ0, irq_clr_value ); + HAL_set_16bit_reg( this_gpio->base_addr, IRQ1, irq_clr_value >> 16 ); + break; + + case GPIO_APB_8_BITS_BUS: + HAL_set_8bit_reg( this_gpio->base_addr, IRQ0, irq_clr_value ); + HAL_set_8bit_reg( this_gpio->base_addr, IRQ1, irq_clr_value >> 8 ); + HAL_set_8bit_reg( this_gpio->base_addr, IRQ2, irq_clr_value >> 16 ); + HAL_set_8bit_reg( this_gpio->base_addr, IRQ3, irq_clr_value >> 24 ); + break; + + default: + HAL_ASSERT(0); + break; + } +} + +/*-------------------------------------------------------------------------*//** + * GPIO_get_irq_sources + * See "core_gpio.h" for details of how to use this function. + */ +uint32_t GPIO_get_irq_sources +( + gpio_instance_t * this_gpio +) +{ + uint32_t intr_src = 0; + + switch( this_gpio->apb_bus_width ) + { + case GPIO_APB_32_BITS_BUS: + intr_src = HAL_get_32bit_reg( this_gpio->base_addr, IRQ ); + break; + + case GPIO_APB_16_BITS_BUS: + intr_src |= HAL_get_16bit_reg( this_gpio->base_addr, IRQ0 ); + intr_src |= (HAL_get_16bit_reg( this_gpio->base_addr, IRQ1 ) << 16); + break; + + case GPIO_APB_8_BITS_BUS: + intr_src |= HAL_get_16bit_reg( this_gpio->base_addr, IRQ0 ); + intr_src |= (HAL_get_16bit_reg( this_gpio->base_addr, IRQ1 ) << 8); + intr_src |= (HAL_get_16bit_reg( this_gpio->base_addr, IRQ2 ) << 16); + intr_src |= (HAL_get_16bit_reg( this_gpio->base_addr, IRQ3 ) << 24); + break; + + default: + HAL_ASSERT(0); + break; + } + + return intr_src; +} + +/*-------------------------------------------------------------------------*//** + * GPIO_clear_all_irq_sources + * See "core_gpio.h" for details of how to use this function. + */ +void GPIO_clear_all_irq_sources +( + gpio_instance_t * this_gpio, + uint32_t bitmask +) +{ + uint32_t irq_clr_value = bitmask; + + switch( this_gpio->apb_bus_width ) + { + case GPIO_APB_32_BITS_BUS: + HAL_set_32bit_reg( this_gpio->base_addr, IRQ, irq_clr_value ); + break; + + case GPIO_APB_16_BITS_BUS: + HAL_set_16bit_reg( this_gpio->base_addr, IRQ0, irq_clr_value ); + HAL_set_16bit_reg( this_gpio->base_addr, IRQ1, irq_clr_value >> 16 ); + break; + + case GPIO_APB_8_BITS_BUS: + HAL_set_8bit_reg( this_gpio->base_addr, IRQ0, irq_clr_value ); + HAL_set_8bit_reg( this_gpio->base_addr, IRQ1, irq_clr_value >> 8 ); + HAL_set_8bit_reg( this_gpio->base_addr, IRQ2, irq_clr_value >> 16 ); + HAL_set_8bit_reg( this_gpio->base_addr, IRQ3, irq_clr_value >> 24 ); + break; + + default: + HAL_ASSERT(0); + break; + } +} diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreGPIO/core_gpio.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreGPIO/core_gpio.h new file mode 100644 index 0000000..f1d629b --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreGPIO/core_gpio.h @@ -0,0 +1,722 @@ +/******************************************************************************* + * (c) Copyright 2008-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * @file core_gpio.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreGPIO bare metal driver public API. + * + */ + +/*=========================================================================*//** + @mainpage CoreGPIO Bare Metal Driver. + + @section intro_sec Introduction + The CoreGPIO hardware IP includes up to 32 general-purpose input/output GPIOs. + This driver provides a set of functions for controlling the GPIOs as part of a + bare-metal system where no operating system is available. These drivers + can be adapted for use as part of an operating system, but the implementation + of the adaptation layer between this driver and the operating system's driver + model is outside the scope of this driver. + + @section driver_configuration Driver Configuration + The individual IOs of CoreGPIO can be configured either in the hardware flow + or as part of the software application through calls to the GPIO_config() + function. GPIOs configured as part of the hardware are fixed and cannot be + modified using a call to the GPI_config() function. + + @section theory_op Theory of Operation + The CoreGPIO driver uses the Actel Hardware Abstraction Layer (HAL) to access + hardware registers. You must ensure that the Actel HAL is included as part of + your software project. The Actel HAL is available through the Actel Firmware + Catalog. + + The CoreGPIO driver functions are logically grouped into the following groups: + - Initialization + - Configuration + - Reading and writing GPIO state + - Interrupt control + + The CoreGPIO driver is initialized through a call to the GPIO_init() function. + The GPIO_init() function must be called before any other GPIO driver functions + can be called. + + Each GPIO port is individually configured through a call to the + GPIO_config() function. Configuration includes deciding if a GPIO port is + going to be used as input, output, or both. GPIO ports configured as inputs + are further configured to generate interrupts based on the state of input. + Interrupts is either level- or edge-sensitive. + Note that a CoreGPIO hardware instance is generated as part of the hardware + flow with a fixed configuration for some or all of its IOs. + Attempting to modify the configuration of such a hardware-configured IO using + the GPIO_config() function has no effect. + + The state of the GPIO ports can be read and written using the following + functions: + - GPIO_get_inputs() + - GPIO_get_outputs() + - GPIO_set_outputs() + - GPIO_drive_inout() + + Interrupts generated by GPIO ports configured as inputs are controlled using + the following functions: + - GPIO_enable_irq() + - GPIO_disable_irq() + - GPIO_clear_irq() + - GPIO_get_irq_sources() + - GPIO_clear_all_irq_sources() + + *//*=========================================================================*/ +#ifndef CORE_GPIO_H_ +#define CORE_GPIO_H_ + +#ifndef LEGACY_DIR_STRUCTURE +#include "hal/hal.h" + +#else +#include "hal.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/*-------------------------------------------------------------------------*//** + The gpio_id_t enumeration is used to identify GPIOs as part of the + parameter to functions: + - GPIO_config(), + - GPIO_drive_inout(), + - GPIO_enable_int(), + - GPIO_disable_int(), + - GPIO_clear_int() + */ +typedef enum __gpio_id_t +{ + GPIO_0 = 0, + GPIO_1 = 1, + GPIO_2 = 2, + GPIO_3 = 3, + GPIO_4 = 4, + GPIO_5 = 5, + GPIO_6 = 6, + GPIO_7 = 7, + GPIO_8 = 8, + GPIO_9 = 9, + GPIO_10 = 10, + GPIO_11 = 11, + GPIO_12 = 12, + GPIO_13 = 13, + GPIO_14 = 14, + GPIO_15 = 15, + GPIO_16 = 16, + GPIO_17 = 17, + GPIO_18 = 18, + GPIO_19 = 19, + GPIO_20 = 20, + GPIO_21 = 21, + GPIO_22 = 22, + GPIO_23 = 23, + GPIO_24 = 24, + GPIO_25 = 25, + GPIO_26 = 26, + GPIO_27 = 27, + GPIO_28 = 28, + GPIO_29 = 29, + GPIO_30 = 30, + GPIO_31 = 31 +} gpio_id_t; + +/*-------------------------------------------------------------------------*//** + Possible width of the APB bus + */ +typedef enum __gpio_apb_width_t +{ + GPIO_APB_8_BITS_BUS = 0, + GPIO_APB_16_BITS_BUS = 1, + GPIO_APB_32_BITS_BUS = 2, + GPIO_APB_UNKNOWN_BUS_WIDTH = 3 +} gpio_apb_width_t; + +/*-------------------------------------------------------------------------*//** + Structure instance holding all data regarding the CoreGPIO + */ +typedef struct __gpio_instance_t +{ + addr_t base_addr; + gpio_apb_width_t apb_bus_width; +} gpio_instance_t; + +/*-------------------------------------------------------------------------*//** + GPIO ports definitions used to identify GPIOs as part of the parameter to + function GPIO_set_outputs(). + These definitions are also be used to identity GPIO through logical operations + on the return value of function GPIO_get_inputs(). + # GPIO_0_MASK + # GPIO_1_MASK + # GPIO_2_MASK + # GPIO_3_MASK + # GPIO_4_MASK + # GPIO_5_MASK + # GPIO_6_MASK + # GPIO_7_MASK + # GPIO_8_MASK + # GPIO_9_MASK + # GPIO_10_MASK + # GPIO_11_MASK + # GPIO_12_MASK + # GPIO_13_MASK + # GPIO_14_MASK + # GPIO_15_MASK + # GPIO_16_MASK + # GPIO_17_MASK + # GPIO_18_MASK + # GPIO_19_MASK + # GPIO_20_MASK + # GPIO_21_MASK + # GPIO_22_MASK + # GPIO_23_MASK + # GPIO_24_MASK + # GPIO_25_MASK + # GPIO_26_MASK + # GPIO_27_MASK + # GPIO_28_MASK + # GPIO_29_MASK + # GPIO_30_MASK + # GPIO_31_MASK + */ +#define GPIO_0_MASK 0x00000001UL +#define GPIO_1_MASK 0x00000002UL +#define GPIO_2_MASK 0x00000004UL +#define GPIO_3_MASK 0x00000008UL +#define GPIO_4_MASK 0x00000010UL +#define GPIO_5_MASK 0x00000020UL +#define GPIO_6_MASK 0x00000040UL +#define GPIO_7_MASK 0x00000080UL +#define GPIO_8_MASK 0x00000100UL +#define GPIO_9_MASK 0x00000200UL +#define GPIO_10_MASK 0x00000400UL +#define GPIO_11_MASK 0x00000800UL +#define GPIO_12_MASK 0x00001000UL +#define GPIO_13_MASK 0x00002000UL +#define GPIO_14_MASK 0x00004000UL +#define GPIO_15_MASK 0x00008000UL +#define GPIO_16_MASK 0x00010000UL +#define GPIO_17_MASK 0x00020000UL +#define GPIO_18_MASK 0x00040000UL +#define GPIO_19_MASK 0x00080000UL +#define GPIO_20_MASK 0x00100000UL +#define GPIO_21_MASK 0x00200000UL +#define GPIO_22_MASK 0x00400000UL +#define GPIO_23_MASK 0x00800000UL +#define GPIO_24_MASK 0x01000000UL +#define GPIO_25_MASK 0x02000000UL +#define GPIO_26_MASK 0x04000000UL +#define GPIO_27_MASK 0x08000000UL +#define GPIO_28_MASK 0x10000000UL +#define GPIO_29_MASK 0x20000000UL +#define GPIO_30_MASK 0x40000000UL +#define GPIO_31_MASK 0x80000000UL + +/*-------------------------------------------------------------------------*//** + * GPIO modes + * # GPIO_INPUT_MODE + * # GPIO_OUTPUT_MODE + * # GPIO_INOUT_MODE + */ +#define GPIO_INPUT_MODE 0x0000000002UL +#define GPIO_OUTPUT_MODE 0x0000000005UL +#define GPIO_INOUT_MODE 0x0000000003UL + +/*-------------------------------------------------------------------------*//** + * Possible GPIO inputs interrupt configurations. + * # GPIO_IRQ_LEVEL_HIGH + * # GPIO_IRQ_LEVEL_LOW + * # GPIO_IRQ_EDGE_POSITIVE + * # GPIO_IRQ_EDGE_NEGATIVE + * # GPIO_IRQ_EDGE_BOTH + */ +#define GPIO_IRQ_LEVEL_HIGH 0x0000000000UL +#define GPIO_IRQ_LEVEL_LOW 0x0000000020UL +#define GPIO_IRQ_EDGE_POSITIVE 0x0000000040UL +#define GPIO_IRQ_EDGE_NEGATIVE 0x0000000060UL +#define GPIO_IRQ_EDGE_BOTH 0x0000000080UL + +/*-------------------------------------------------------------------------*//** + * Possible states for GPIO configured as INOUT + */ +typedef enum gpio_inout_state +{ + GPIO_DRIVE_LOW = 0, + GPIO_DRIVE_HIGH, + GPIO_HIGH_Z +} gpio_inout_state_t; + +/*-------------------------------------------------------------------------*//** + The GPIO_init() function initializes a CoreGPIO hardware instance and the data + structure associated with the CoreGPIO hardware instance. + Note that a CoreGPIO hardware instance is generated with a fixed configuration + for some or all of its IOs as part of the hardware flow. + Attempting to modify the configuration of such a hardware-configured IO using + the GPIO_config() function has no effect. + + @param this_gpio + Pointer to the gpio_instance_t data structure instance holding all data + regarding the CoreGPIO hardware instance being initialized. A pointer to the + same data structure is used in subsequent calls to the CoreGPIO driver + functions in order to identify the CoreGPIO instance that must perform the + operation implemented by the called driver function. + + @param base_addr + The base_addr parameter is the base address in the memory map of the + processor for the registers of the GPIO instance being initialized. + + @param bus_width + The bus_width parameter informs the driver of the APB bus width selected + during the hardware flow configuration of the CoreGPIO hardware instance. It + indicates to the driver whether the CoreGPIO hardware registers are visible + as 8, 16, or 32-bits registers. Allowed values are: + - GPIO_APB_8_BITS_BUS + - GPIO_APB_16_BITS_BUS + - GPIO_APB_32_BITS_BUS + + @return + none. + + @example + @code + #define COREGPIO_BASE_ADDR 0xC2000000 + + gpio_instance_t g_gpio; + + void system_init( void ) + { + GPIO_init( &g_gpio, COREGPIO_BASE_ADDR, GPIO_APB_32_BITS_BUS ); + } + @endcode + */ +void GPIO_init +( + gpio_instance_t * this_gpio, + addr_t base_addr, + gpio_apb_width_t bus_width +); + +/*-------------------------------------------------------------------------*//** + The GPIO_config() function is used to configure an individual GPIO port. + + @param this_gpio + The this_gpio parameter is a pointer to the gpio_instance_t structure + holding all the data regarding the CoreGPIO instance controlled through this + function call. + + @param port_id + The port_id parameter identifies the GPIO port to be configured. + An enumeration item of the form GPIO_n, where n is the number of the GPIO + port, is used to identify the GPIO port. For example, GPIO_0 identifies the + first GPIO port and GPIO_31 the last one. + + @param config + The config parameter specifies the configuration to be applied to the GPIO + port identified by the first parameter. It is a logical OR of GPIO mode and + interrupt mode. The interrupt mode is only relevant if the GPIO is + configured as an input. + - Possible modes are: + - GPIO_INPUT_MODE, + - GPIO_OUTPUT_MODE, + - GPIO_INOUT_MODE. + - Possible interrupt modes are: + - GPIO_IRQ_LEVEL_HIGH, + - GPIO_IRQ_LEVEL_LOW, + - GPIO_IRQ_EDGE_POSITIVE, + - GPIO_IRQ_EDGE_NEGATIVE, + - GPIO_IRQ_EDGE_BOTH + + @return + none. + + @example + For example, the following call configures GPIO 4 as an input that generates + interrupts on a low-to-high transition of the input: + @code + GPIO_config( &g_gpio, GPIO_4, GPIO_INPUT_MODE | GPIO_IRQ_EDGE_POSITIVE ); + @endcode + */ +void GPIO_config +( + gpio_instance_t * this_gpio, + gpio_id_t port_id, + uint32_t config +); + +/*-------------------------------------------------------------------------*//** + The GPIO_set_outputs() function is used to set the state of the GPIO ports + configured as outputs. + + @param this_gpio + The this_gpio parameter is a pointer to the gpio_instance_t structure + holding all the data regarding the CoreGPIO instance controlled through this + function call. + + @param value + The value parameter specifies the state of the GPIO ports configured as + outputs. It is a bit mask of the form (GPIO_n_MASK | GPIO_m_MASK), where n + and m are numbers identifying GPIOs. + For example, (GPIO_0_MASK | GPIO_1_MASK | GPIO_2_MASK ) specifies that the + first, second, and third GPIO must be set high and all other outputs set + low. + + @return + none. + + @example + Set GPIO 0 and 8 outputs high and all other GPIO outputs low. + @code + GPIO_set_outputs( &g_gpio, GPIO_0_MASK | GPIO_8_MASK ); + @endcode + + @example + Set GPIO 2 and 4 outputs low without affecting the other GPIO outputs. + @code + uint32_t gpio_outputs; + gpio_outputs = GPIO_get_outputs( &g_gpio ); + gpio_outputs &= ~( GPIO_2_MASK | GPIO_4_MASK ); + GPIO_set_outputs( &g_gpio, gpio_outputs ); + @endcode + + @see GPIO_get_outputs() + */ +void GPIO_set_outputs +( + gpio_instance_t * this_gpio, + uint32_t value +); + +/*-------------------------------------------------------------------------*//** + The GPIO_set_output() function is used to set the state of a single GPIO port + configured as an output. + + @param this_gpio + The this_gpio parameter is a pointer to the gpio_instance_t structure + holding all the data regarding the CoreGPIO instance controlled through this + function call. + + @param port_id + The port_id parameter specifies the GPIO port that has its output set by a + call to this function. + + @param value + The value parameter specifies the desired state for the GPIO output. A value + of 0 sets the output low, and a value of 1 sets the port high. + + @return + none. + */ +void GPIO_set_output +( + gpio_instance_t * this_gpio, + gpio_id_t port_id, + uint8_t value +); + +/*-------------------------------------------------------------------------*//** + The GPIO_get_inputs() function is used to read the state of all GPIOs + configured as inputs. + + @param this_gpio + The this_gpio parameter is a pointer to the gpio_instance_t structure + holding all the data regarding the CoreGPIO instance controlled through this + function call. + + @return + This function returns a 32-bit unsigned integer, where each bit represents + the state of an input. The least significant bit represents the state of + GPIO 0, and the most significant bit represents the state of GPIO 31. + */ +uint32_t GPIO_get_inputs +( + gpio_instance_t * this_gpio +); + +/*-------------------------------------------------------------------------*//** + The GPIO_get_outputs() function is used to read the current state of all + GPIO outputs. + + @param this_gpio + The this_gpio parameter is a pointer to the gpio_instance_t structure + holding all the data regarding the CoreGPIO instance controlled through this + function call. + + @return + This function returns a 32-bit unsigned integer where each bit represents + the state of an output. The least significant bit represents the state + of GPIO 0, and the most significant bit represents the state of GPIO 31. + */ +uint32_t GPIO_get_outputs +( + gpio_instance_t * this_gpio +); + +/*-------------------------------------------------------------------------*//** + The GPIO_drive_inout() function is used to set the output state of a + GPIO configured as INOUT. An INOUT GPIO is in one of three states: + - high + - low + - high impedance + + An INOUT output is typically be used where several devices drive the state of + a signal. The high and low states are equivalent to the high and low states of + a GPIO configured as an output. The high impedance state is used to prevent + the GPIO from driving the state of the output and therefore allow reading the + state of the GPIO as an input. + Note that the GPIO port you wish to use as INOUT through this function + must be configurable through software. Therefore, the GPIO ports used as INOUT + must not have a fixed configuration selected as part of the hardware flow. + + @param this_gpio + The this_gpio parameter is a pointer to the gpio_instance_t structure + holding all the data regarding the CoreGPIO instance controlled through this + function call. + + @param port_id + The port_id parameter identifies the GPIO for which this function will + change the output state. + An enumeration item of the form GPIO_n, where n is the number of the GPIO + port, is used to identify the GPIO port. For example, GPIO_0 identifies the + first GPIO port and GPIO_31 the last one. + + @param inout_state + The inout_state parameter specifies the state of the I/O identified by the + first parameter. Possible states are: + - GPIO_DRIVE_HIGH, + - GPIO_DRIVE_LOW, + - GPIO_HIGH_Z (high impedance) + + @return + none. + + @example + The call to GPIO_drive_inout() below will set the GPIO 7 output to + high impedance state. + @code + GPIO_drive_inout( &g_gpio, GPIO_7, GPIO_HIGH_Z ); + @endcode + */ +void GPIO_drive_inout +( + gpio_instance_t * this_gpio, + gpio_id_t port_id, + gpio_inout_state_t inout_state +); + +/*-------------------------------------------------------------------------*//** + The GPIO_enable_irq() function is used to enable an interrupt to be + generated based on the state of the input identified as a parameter. + + @param this_gpio + The this_gpio parameter is a pointer to the gpio_instance_t structure + holding all the data regarding the CoreGPIO instance controlled through this + function call. + + @param port_id + The port_id parameter identifies the GPIO input that the call to + GPIO_enable_irq() enables to generate interrupts. + An enumeration item of the form GPIO_n, where n is the number of the GPIO + port, is used to identify the GPIO port. For example, GPIO_0 identifies the + first GPIO port and GPIO_31 the last one. + + @return + none. + + @example + The call to GPIO_enable_irq() below allows GPIO 8 to generate interrupts. + + @code + GPIO_enable_irq( &g_gpio, GPIO_8 ); + @endcode + */ +void GPIO_enable_irq +( + gpio_instance_t * this_gpio, + gpio_id_t port_id +); + +/*-------------------------------------------------------------------------*//** + The GPIO_disable_irq() function is used to disable interrupts from being + generated based on the state of the input specified as a parameter. + + @param this_gpio + The this_gpio parameter is a pointer to the gpio_instance_t structure + holding all the data regarding the CoreGPIO instance controlled through this + function call. + + @param port_id + The port_id parameter identifies the GPIO input that the call to + GPIO_disable_irq() disables from generating interrupts. + An enumeration item of the form GPIO_n, where n is the number of the GPIO + port, is used to identify the GPIO port. For example, GPIO_0 identifies the + first GPIO port and GPIO_31 the last one. + + @return + none. + + @example + The call to GPIO_disable_irq() below prevents GPIO 8 from generating + interrupts. + @code + GPIO_disable_irq( &g_gpio, GPIO_8 ); + @endcode + */ +void GPIO_disable_irq +( + gpio_instance_t * this_gpio, + gpio_id_t port_id +); + +/*-------------------------------------------------------------------------*//** + The GPIO_clear_irq() function is used to clear the interrupt generated by + the GPIO specified as a parameter. The GPIO_clear_irq() function must be + called as part of a GPIO interrupt service routine (ISR) in order to prevent + the same interrupt event from re-triggering a call to the GPIO ISR. + Note that interrupts may also need to be cleared in the processor's + interrupt controller. + + @param this_gpio + The this_gpio parameter is a pointer to the gpio_instance_t structure + holding all the data regarding the CoreGPIO instance controlled through this + function call. + + @param port_id + The port_id parameter identifies the GPIO input for which to clear the + interrupt. + An enumeration item of the form GPIO_n, where n is the number of the GPIO + port, is used to identify the GPIO port. For example, GPIO_0 identifies the + first GPIO port and GPIO_31 the last one. + + @return + none. + + @example + The example below demonstrates the use of the GPIO_clear_irq() function as + part of the GPIO-9 interrupt service routine. + @code + void GPIO9_IRQHandler( void ) + { + do_interrupt_processing(); + + GPIO_clear_irq( &g_gpio, GPIO_9 ); + + NVIC_ClearPendingIRQ( GPIO9_IRQn ); + } + @endcode + */ +void GPIO_clear_irq +( + gpio_instance_t * this_gpio, + gpio_id_t port_id +); + +/*-------------------------------------------------------------------------*//** + The GPIO_get_irq_sources() function is used to identify the source of the + interrupt. i.e. to That is the GPIO input line, whose state change triggered + the interrupt. The GPIO_get_irq_sources() function must be called as part of + a GPIO interrupt service routine (ISR) in order to determine the interrupt + source. + + @param this_gpio + The this_gpio parameter is a pointer to the gpio_instance_t structure + holding all the data regarding the CoreGPIO instance controlled through this + function call. + + @return + This function returns a 32-bit unsigned integer, where each bit represents + the pin number of a GPIO. + + @example + The example below demonstrates the use of the GPIO_get_irq_sources() + function as part of the GPIO-9 interrupt service routine. + + @code + void GPIO9_IRQHandler( void ) + { + do_interrupt_processing(); + + GPIO_clear_all_irq_sources(g_p_mygpio, GPIO_get_irq_sources(g_p_mygpio)); + + NVIC_ClearPendingIRQ( GPIO9_IRQn ); + } + @endcode + */ +uint32_t GPIO_get_irq_sources +( + gpio_instance_t * this_gpio +); + +/*-------------------------------------------------------------------------*//** + The GPIO_clear_all_irq_sources() function is used to clear all the active + interrupts generated by the GPIO specified as a parameter. The + GPIO_clear_all_irq_sources() function must be called as part of a GPIO + interrupt service routine (ISR) in order to prevent the same interrupt event + from re-triggering a call to the GPIO ISR. + Note that interrupts may also need to be cleared in the processor's + interrupt controller. + + @param this_gpio + The this_gpio parameter is a pointer to the gpio_instance_t structure + holding all the data regarding the CoreGPIO instance controlled through this + function call. + + @param bitmask + This bitmask parameter is a 32-bit unsigned integer where each bit + represents the GPIO pin used to clear the interrupt bit register of the + corresponding GPIO bit. The least significant bit represents the status of + GPIO 0, and the most significant bit represents the status of GPIO 31. + + @return + none. + + @example + The example below demonstrates the use of the GPIO_clear_all_irq_sources() + function as part of the GPIO-9 interrupt service routine. + @code + void GPIO9_IRQHandler( void ) + { + do_interrupt_processing(); + + do_interrupt_processing(); + + GPIO_clear_all_irq_sources(g_p_mygpio, GPIO_get_irq_sources(g_p_mygpio)); + + NVIC_ClearPendingIRQ( GPIO9_IRQn ); + } + @endcode + */ +void GPIO_clear_all_irq_sources +( + gpio_instance_t * this_gpio, + uint32_t bitmask +); + +#ifdef __cplusplus +} +#endif + +#endif /* CORE_GPIO_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreGPIO/coregpio_regs.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreGPIO/coregpio_regs.h new file mode 100644 index 0000000..411667b --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreGPIO/coregpio_regs.h @@ -0,0 +1,45 @@ +/******************************************************************************* + * (c) Copyright 2008-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file coregpio_regs.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreGPIO register definitions + * + */ + +#ifndef __CORE_GPIO_REGISTERS_H +#define __CORE_GPIO_REGISTERS_H 1 + +/*------------------------------------------------------------------------------ + * + */ +#define IRQ_REG_OFFSET 0x80 + +#define IRQ0_REG_OFFSET 0x80 +#define IRQ1_REG_OFFSET 0x84 +#define IRQ2_REG_OFFSET 0x88 +#define IRQ3_REG_OFFSET 0x8C + +/*------------------------------------------------------------------------------ + * + */ +#define GPIO_IN_REG_OFFSET 0x90 + +#define GPIO_IN0_REG_OFFSET 0x90 +#define GPIO_IN1_REG_OFFSET 0x94 +#define GPIO_IN2_REG_OFFSET 0x98 +#define GPIO_IN3_REG_OFFSET 0x9C + +/*------------------------------------------------------------------------------ + * + */ +#define GPIO_OUT_REG_OFFSET 0xA0 + +#define GPIO_OUT0_REG_OFFSET 0xA0 +#define GPIO_OUT1_REG_OFFSET 0xA4 +#define GPIO_OUT2_REG_OFFSET 0xA8 +#define GPIO_OUT3_REG_OFFSET 0xAC + +#endif /* __CORE_GPIO_REGISTERS_H */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreI2C/core_i2c.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreI2C/core_i2c.c new file mode 100644 index 0000000..c6d2c7f --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreI2C/core_i2c.c @@ -0,0 +1,1495 @@ +/******************************************************************************* + * Copyright 2009-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * CoreI2C software driver implementation. + * + */ + +#include "core_smbus_regs.h" +#include "core_i2c.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*------------------------------------------------------------------------------ + * I2C transaction direction. + */ +#define WRITE_DIR 0u +#define READ_DIR 1u + +/* -- TRANSACTIONS TYPES -- */ +#define NO_TRANSACTION 0u +#define MASTER_WRITE_TRANSACTION 1u +#define MASTER_READ_TRANSACTION 2u +#define MASTER_RANDOM_READ_TRANSACTION 3u +#define WRITE_SLAVE_TRANSACTION 4u +#define READ_SLAVE_TRANSACTION 5u + +/* -- SMBUS H/W STATES -- */ +/* -- MASTER STATES -- */ +#define ST_BUS_ERROR 0x00u /* Bus error during MST or selected slave modes */ +#define ST_I2C_IDLE 0xF8u /* No activity and no interrupt either... */ +#define ST_START 0x08u /* start condition sent */ +#define ST_RESTART 0x10u /* repeated start */ +#define ST_SLAW_ACK 0x18u /* SLA+W sent, ack received */ +#define ST_SLAW_NACK 0x20u /* SLA+W sent, nack received */ +#define ST_TX_DATA_ACK 0x28u /* Data sent, ACK'ed */ +#define ST_TX_DATA_NACK 0x30u /* Data sent, NACK'ed */ +#define ST_LOST_ARB 0x38u /* Master lost arbitration */ +#define ST_SLAR_ACK 0x40u /* SLA+R sent, ACK'ed */ +#define ST_SLAR_NACK 0x48u /* SLA+R sent, NACK'ed */ +#define ST_RX_DATA_ACK 0x50u /* Data received, ACK sent */ +#define ST_RX_DATA_NACK 0x58u /* Data received, NACK sent */ +#define ST_RESET_ACTIVATED 0xD0u /* Master reset is activated */ +#define ST_STOP_TRANSMIT 0xE0u /* Stop has been transmitted */ + +/* -- SLAVE STATES -- */ +#define ST_SLAVE_SLAW 0x60u /* SLA+W received */ +#define ST_SLAVE_SLAR_ACK 0xA8u /* SLA+R received, ACK returned */ +#define ST_SLV_LA 0x68u /* Slave lost arbitration */ +#define ST_GCA 0x70u /* GCA received */ +#define ST_GCA_LA 0x78u /* GCA lost arbitration */ +#define ST_RDATA 0x80u /* Data received */ +#define ST_SLA_NACK 0x88u /* Slave addressed, NACK returned */ +#define ST_GCA_ACK 0x90u /* Previously addresses with GCA, data ACKed */ +#define ST_GCA_NACK 0x98u /* GCA addressed, NACK returned */ +#define ST_RSTOP 0xA0u /* Stop received */ +#define ST_SLARW_LA 0xB0u /* Arbitration lost */ +#define ST_RACK 0xB8u /* Byte sent, ACK received */ +#define ST_SLAVE_RNACK 0xC0u /* Byte sent, NACK received */ +#define ST_FINAL 0xC8u /* Final byte sent, ACK received */ +#define ST_SLV_RST 0xD8u /* Slave reset state */ + + +/* I2C Channel base offset */ +#define CHANNEL_BASE_SHIFT 5u +#define CHANNEL_MASK 0x1E0u + +/* + * Maximum address offset length in slave write-read transactions. + * A maximum of two bytes will be interpreted as address offset within the slave + * tx buffer. + */ +#define MAX_OFFSET_LENGTH 2u + +/*------------------------------------------------------------------------------ + * I2C interrupts control functions implemented "i2c_interrupt.c". + * the implementation of these functions depend on the underlying hardware + * design and how the CoreI2C interrupt line is connected to the system's + * interrupt controller. + */ +void I2C_enable_irq( i2c_instance_t * this_i2c ); +void I2C_disable_irq( i2c_instance_t * this_i2c ); +static void enable_slave_if_required(i2c_instance_t * this_i2c); + +/*------------------------------------------------------------------------------ + * I2C_init() + * See "core_i2c.h" for details of how to use this function. + */ +void I2C_init +( + i2c_instance_t * this_i2c, + addr_t base_address, + uint8_t ser_address, + i2c_clock_divider_t ser_clock_speed +) +{ + psr_t saved_psr; + uint_fast16_t clock_speed = (uint_fast16_t)ser_clock_speed; + + /* + * We need to disable ints while doing this as there is no guarantee we + * have not been called already and the ISR is active. + */ + saved_psr = HAL_disable_interrupts(); + + /* + * Initialize all items of the this_i2c data structure to zero. This + * initializes all state variables to their init value. It relies on + * the fact that NO_TRANSACTION, I2C_SUCCESS and I2C_RELEASE_BUS all + * have an actual value of zero. + */ + memset(this_i2c, 0, sizeof(i2c_instance_t)); + + /* + * Set base address of I2C hardware used by this instance. + */ + this_i2c->base_address = base_address; + + /* + * Update Serial address of the device + */ + this_i2c->ser_address = ((uint_fast8_t)ser_address << 1u); + + /* + * Configure hardware. + */ + HAL_set_8bit_reg_field(this_i2c->base_address, ENS1, 0x00); /* Reset I2C hardware. */ + HAL_set_8bit_reg_field(this_i2c->base_address, ENS1, 0x01); /* set enable bit */ + HAL_set_8bit_reg_field(this_i2c->base_address, CR2, ( (clock_speed >> 2) & 0x01) ); + HAL_set_8bit_reg_field(this_i2c->base_address, CR1, ( (clock_speed >> 1) & 0x01) ); + HAL_set_8bit_reg_field(this_i2c->base_address, CR0, ( clock_speed & 0x01) ); + + HAL_set_8bit_reg(this_i2c->base_address, ADDRESS, this_i2c->ser_address); + HAL_set_8bit_reg(this_i2c->base_address, ADDRESS1, this_i2c->ser_address); + + /* + * Finally safe to enable interrupts. + */ + HAL_restore_interrupts( saved_psr ); +} +/*------------------------------------------------------------------------------ + * I2C_channel_init() + * See "core_i2c.h" for details of how to use this function. + */ +void I2C_channel_init +( + i2c_instance_t * this_i2c_channel, + i2c_instance_t * this_i2c, + i2c_channel_number_t channel_number, + i2c_clock_divider_t ser_clock_speed +) +{ + psr_t saved_psr; + uint_fast16_t clock_speed = (uint_fast16_t)ser_clock_speed; + + HAL_ASSERT(channel_number < I2C_MAX_CHANNELS); + HAL_ASSERT(I2C_CHANNEL_0 != channel_number); + + /* + * Cannot allow channel 0 in this function as we will trash the hardware + * base address and slave address. + */ + if ((channel_number < I2C_MAX_CHANNELS) && + (I2C_CHANNEL_0 != channel_number)) + { + /* + * We need to disable ints while doing this as the hardware should already + * be active at this stage. + */ + saved_psr = HAL_disable_interrupts(); + + /* + * Initialize channel data. + */ + memset(this_i2c_channel, 0, sizeof(i2c_instance_t)); + + this_i2c_channel->base_address = + ((this_i2c->base_address) & ~((addr_t)CHANNEL_MASK)) + | (((addr_t)channel_number) << CHANNEL_BASE_SHIFT); + + this_i2c_channel->ser_address = this_i2c->ser_address; + + HAL_set_8bit_reg_field(this_i2c_channel->base_address, ENS1, 0x00); /* Reset I2C channel hardware. */ + HAL_set_8bit_reg_field(this_i2c_channel->base_address, ENS1, 0x01); /* set enable bit */ + HAL_set_8bit_reg_field(this_i2c_channel->base_address, CR2, ( (clock_speed >> 2) & 0x01) ); + HAL_set_8bit_reg_field(this_i2c_channel->base_address, CR1, ( (clock_speed >> 1) & 0x01) ); + HAL_set_8bit_reg_field(this_i2c_channel->base_address, CR0, ( clock_speed & 0x01) ); + /* + * Finally safe to enable interrupts. + */ + HAL_restore_interrupts( saved_psr ); + } +} + +/*------------------------------------------------------------------------------ + * I2C_write() + * See "core_i2c.h" for details of how to use this function. + */ +void I2C_write +( + i2c_instance_t * this_i2c, + uint8_t serial_addr, + const uint8_t * write_buffer, + uint16_t write_size, + uint8_t options +) +{ + psr_t saved_psr; + volatile uint8_t stat_ctrl; + + saved_psr = HAL_disable_interrupts(); + + /* Update the transaction only when there is no transaction going on I2C */ + if( this_i2c->transaction == NO_TRANSACTION) + { + this_i2c->transaction = MASTER_WRITE_TRANSACTION; + } + + /* Update the Pending transaction information so that transaction can restarted */ + this_i2c->pending_transaction = MASTER_WRITE_TRANSACTION ; + + /* Update target address */ + this_i2c->target_addr = (uint_fast8_t)serial_addr << 1u; + this_i2c->dir = WRITE_DIR; + this_i2c->master_tx_buffer = write_buffer; + this_i2c->master_tx_size = write_size; + this_i2c->master_tx_idx = 0u; + + /* Set I2C status in progress */ + this_i2c->master_status = I2C_IN_PROGRESS; + this_i2c->options = options; + + if(I2C_IN_PROGRESS == this_i2c->slave_status) + { + this_i2c->is_transaction_pending = 1u; + } + else + { + HAL_set_8bit_reg_field(this_i2c->base_address, STA, 0x01u); + } + + /* + * Clear interrupts if required (depends on repeated starts). + * Since the Bus is on hold, only then prior status needs to + * be cleared. + */ + if ( I2C_HOLD_BUS == this_i2c->bus_status ) + { + HAL_set_8bit_reg_field(this_i2c->base_address, SI, 0x00u); + } + + stat_ctrl = HAL_get_8bit_reg( this_i2c->base_address, STATUS); + stat_ctrl = stat_ctrl; /* Avoids lint warning. */ + + /* Enable the interrupt. ( Re-enable) */ + I2C_enable_irq( this_i2c ); + + HAL_restore_interrupts( saved_psr ); +} + +/*------------------------------------------------------------------------------ + * I2C_read() + * See "core_i2c.h" for details of how to use this function. + */ +void I2C_read +( + i2c_instance_t * this_i2c, + uint8_t serial_addr, + uint8_t * read_buffer, + uint16_t read_size, + uint8_t options +) +{ + psr_t saved_psr; + volatile uint8_t stat_ctrl; + + saved_psr = HAL_disable_interrupts(); + + /* Update the transaction only when there is no transaction going on I2C */ + if( this_i2c->transaction == NO_TRANSACTION) + { + this_i2c->transaction = MASTER_READ_TRANSACTION; + } + + /* Update the Pending transaction information so that transaction can restarted */ + this_i2c->pending_transaction = MASTER_READ_TRANSACTION ; + + /* Update target address */ + this_i2c->target_addr = (uint_fast8_t)serial_addr << 1u; + + this_i2c->dir = READ_DIR; + + this_i2c->master_rx_buffer = read_buffer; + this_i2c->master_rx_size = read_size; + this_i2c->master_rx_idx = 0u; + + /* Set I2C status in progress */ + this_i2c->master_status = I2C_IN_PROGRESS; + + this_i2c->options = options; + + if(I2C_IN_PROGRESS == this_i2c->slave_status) + { + this_i2c->is_transaction_pending = 1u; + } + else + { + HAL_set_8bit_reg_field(this_i2c->base_address, STA, 0x01u); + } + + /* + * Clear interrupts if required (depends on repeated starts). + * Since the Bus is on hold, only then prior status needs to + * be cleared. + */ + if ( I2C_HOLD_BUS == this_i2c->bus_status ) + { + HAL_set_8bit_reg_field(this_i2c->base_address, SI, 0x00u); + } + + stat_ctrl = HAL_get_8bit_reg( this_i2c->base_address, STATUS); + stat_ctrl = stat_ctrl; /* Avoids lint warning. */ + + /* Enable the interrupt. ( Re-enable) */ + I2C_enable_irq( this_i2c ); + HAL_restore_interrupts( saved_psr ); +} + +/*------------------------------------------------------------------------------ + * I2C_write_read() + * See "core_i2c.h" for details of how to use this function. + */ +void I2C_write_read +( + i2c_instance_t * this_i2c, + uint8_t serial_addr, + const uint8_t * addr_offset, + uint16_t offset_size, + uint8_t * read_buffer, + uint16_t read_size, + uint8_t options +) +{ + HAL_ASSERT(offset_size > 0u); + HAL_ASSERT(addr_offset != (uint8_t *)0); + HAL_ASSERT(read_size > 0u); + HAL_ASSERT(read_buffer != (uint8_t *)0); + + this_i2c->master_status = I2C_FAILED; + + if((read_size > 0u) && (offset_size > 0u)) + { + psr_t saved_psr; + volatile uint8_t stat_ctrl; + + saved_psr = HAL_disable_interrupts(); + + /* Update the transaction only when there is no transaction going on I2C */ + if( this_i2c->transaction == NO_TRANSACTION) + { + this_i2c->transaction = MASTER_RANDOM_READ_TRANSACTION; + } + + /* Update the Pending transaction information so that transaction can restarted */ + this_i2c->pending_transaction = MASTER_RANDOM_READ_TRANSACTION ; + + /* Update target address */ + this_i2c->target_addr = (uint_fast8_t)serial_addr << 1u; + + this_i2c->dir = WRITE_DIR; + + this_i2c->master_tx_buffer = addr_offset; + this_i2c->master_tx_size = offset_size; + this_i2c->master_tx_idx = 0u; + + this_i2c->master_rx_buffer = read_buffer; + this_i2c->master_rx_size = read_size; + this_i2c->master_rx_idx = 0u; + + /* Set I2C status in progress */ + this_i2c->master_status = I2C_IN_PROGRESS; + this_i2c->options = options; + + if(I2C_IN_PROGRESS == this_i2c->slave_status) + { + this_i2c->is_transaction_pending = 1u; + } + else + { + HAL_set_8bit_reg_field(this_i2c->base_address, STA, 0x01u); + } + + /* + * Clear interrupts if required (depends on repeated starts). + * Since the Bus is on hold, only then prior status needs to + * be cleared. + */ + if ( I2C_HOLD_BUS == this_i2c->bus_status ) + { + HAL_set_8bit_reg_field(this_i2c->base_address, SI, 0x00u); + } + + stat_ctrl = HAL_get_8bit_reg( this_i2c->base_address, STATUS); + stat_ctrl = stat_ctrl; /* Avoids lint warning. */ + + /* Enable the interrupt. ( Re-enable) */ + I2C_enable_irq( this_i2c ); + + HAL_restore_interrupts( saved_psr ); + } +} + +/*------------------------------------------------------------------------------ + * I2C_get_status() + * See "core_i2c.h" for details of how to use this function. + */ +i2c_status_t I2C_get_status +( + i2c_instance_t * this_i2c +) +{ + i2c_status_t i2c_status ; + + i2c_status = this_i2c->master_status ; + + return i2c_status; +} + +/*------------------------------------------------------------------------------ + * I2C_wait_complete() + * See "core_i2c.h" for details of how to use this function. + */ +i2c_status_t I2C_wait_complete +( + i2c_instance_t * this_i2c, + uint32_t timeout_ms +) +{ + i2c_status_t i2c_status; + psr_t saved_psr; + /* + * Because we have no idea of what CPU we are supposed to be running on + * we need to guard this write to the timeout value to avoid ISR/user code + * interaction issues. Checking the status below should be fine as only a + * single byte should change in that. + */ + saved_psr = HAL_disable_interrupts(); + this_i2c->master_timeout_ms = timeout_ms; + HAL_restore_interrupts( saved_psr ); + + /* Run the loop until state returns I2C_FAILED or I2C_SUCESS*/ + do { + i2c_status = this_i2c->master_status; + } while(I2C_IN_PROGRESS == i2c_status); + return i2c_status; +} + +/*------------------------------------------------------------------------------ + * I2C_system_tick() + * See "core_i2c.h" for details of how to use this function. + */ +void I2C_system_tick +( + i2c_instance_t * this_i2c, + uint32_t ms_since_last_tick +) +{ + if(this_i2c->master_timeout_ms != I2C_NO_TIMEOUT) + { + if(this_i2c->master_timeout_ms > ms_since_last_tick) + { + this_i2c->master_timeout_ms -= ms_since_last_tick; + } + else + { + psr_t saved_psr; + /* + * We need to disable interrupts here to ensure we can update the + * shared data without the I2C ISR interrupting us. + */ + saved_psr = HAL_disable_interrupts(); + + /* + * Mark current transaction as having timed out. + */ + this_i2c->master_status = I2C_TIMED_OUT; + this_i2c->transaction = NO_TRANSACTION; + this_i2c->is_transaction_pending = 0; + + HAL_restore_interrupts( saved_psr ); + + /* + * Make sure we do not incorrectly signal a timeout for subsequent + * transactions. + */ + this_i2c->master_timeout_ms = I2C_NO_TIMEOUT; + } + } +} + +/*------------------------------------------------------------------------------ + * I2C_set_slave_tx_buffer() + * See "core_i2c.h" for details of how to use this function. + */ +void I2C_set_slave_tx_buffer +( + i2c_instance_t * this_i2c, + const uint8_t * tx_buffer, + uint16_t tx_size +) +{ + psr_t saved_psr; + + /* + * We need to disable interrupts here to ensure we can update the + * shared data without the I2C ISR interrupting us. + */ + saved_psr = HAL_disable_interrupts(); + + this_i2c->slave_tx_buffer = tx_buffer; + this_i2c->slave_tx_size = tx_size; + this_i2c->slave_tx_idx = 0u; + + HAL_restore_interrupts( saved_psr ); +} + +/*------------------------------------------------------------------------------ + * I2C_set_slave_rx_buffer() + * See "core_i2c.h" for details of how to use this function. + */ +void I2C_set_slave_rx_buffer +( + i2c_instance_t * this_i2c, + uint8_t * rx_buffer, + uint16_t rx_size +) +{ + psr_t saved_psr; + + /* + * We need to disable interrupts here to ensure we can update the + * shared data without the I2C ISR interrupting us. + */ + saved_psr = HAL_disable_interrupts(); + + this_i2c->slave_rx_buffer = rx_buffer; + this_i2c->slave_rx_size = rx_size; + this_i2c->slave_rx_idx = 0u; + + HAL_restore_interrupts( saved_psr ); +} + +/*------------------------------------------------------------------------------ + * I2C_set_slave_mem_offset_length() + * See "core_i2c.h" for details of how to use this function. + */ +void I2C_set_slave_mem_offset_length +( + i2c_instance_t * this_i2c, + uint8_t offset_length +) +{ + HAL_ASSERT(offset_length <= MAX_OFFSET_LENGTH); + + /* + * Single byte update, should be interrupt safe + */ + if(offset_length > MAX_OFFSET_LENGTH) + { + this_i2c->slave_mem_offset_length = MAX_OFFSET_LENGTH; + } + else + { + this_i2c->slave_mem_offset_length = offset_length; + } +} + +/*------------------------------------------------------------------------------ + * I2C_register_write_handler() + * See "core_i2c.h" for details of how to use this function. + */ +void I2C_register_write_handler +( + i2c_instance_t * this_i2c, + i2c_slave_wr_handler_t handler +) +{ + psr_t saved_psr; + + /* + * We need to disable interrupts here to ensure we can update the + * shared data without the I2C ISR interrupting us. + */ + saved_psr = HAL_disable_interrupts(); + + this_i2c->slave_write_handler = handler; + + HAL_restore_interrupts( saved_psr ); +} + +/*------------------------------------------------------------------------------ + * I2C_enable_slave() + * See "core_i2c.h" for details of how to use this function. + */ +void I2C_enable_slave +( + i2c_instance_t * this_i2c +) +{ + psr_t saved_psr; + + /* + * We need to disable interrupts here to ensure we can update the + * hardware register and slave mode flag without the I2C ISR interrupting + * us. + */ + saved_psr = HAL_disable_interrupts(); + + /* Set the Assert Acknowledge bit. */ + HAL_set_8bit_reg_field(this_i2c->base_address, AA, 0x01u); + + /* Enable slave mode */ + this_i2c->is_slave_enabled = 1u; + + HAL_restore_interrupts( saved_psr ); + + /* Enable I2C IRQ*/ + I2C_enable_irq( this_i2c ); +} + +/*------------------------------------------------------------------------------ + * I2C_disable_slave() + * See "core_i2c.h" for details of how to use this function. + */ +void I2C_disable_slave +( + i2c_instance_t * this_i2c +) +{ + psr_t saved_psr; + + /* + * We need to disable interrupts here to ensure we can update the + * hardware register without the I2C ISR interrupting us. + */ + saved_psr = HAL_disable_interrupts(); + + /* Reset the assert acknowledge bit. */ + HAL_set_8bit_reg_field(this_i2c->base_address, AA, 0x00u); + + /* Disable slave mode with IRQ blocked to make whole change atomic */ + this_i2c->is_slave_enabled = 0u; + + HAL_restore_interrupts( saved_psr ); +} + +/*------------------------------------------------------------------------------ + * + */ +static void enable_slave_if_required +( + i2c_instance_t * this_i2c +) +{ + /* + * This function is only called from within the ISR and so does not need + * guarding on the register access. + */ + if( 0 != this_i2c->is_slave_enabled ) + { + HAL_set_8bit_reg_field( this_i2c->base_address, AA, 0x01u ); + } +} +/*------------------------------------------------------------------------------ + * I2C_set_slave_second_addr() + * See "i2c.h" for details of how to use this function. + */ +void I2C_set_slave_second_addr +( + i2c_instance_t * this_i2c, + uint8_t second_slave_addr +) +{ + uint8_t second_slave_address; + + /* + This function does not support CoreI2C hardware configured with a fixed + second slave address. The current implementation of the ADDR1[0] register + bit makes it difficult for the driver to support both programmable and + fixed second slave address, so we choose to support programmable only. + With the programmable configuration, ADDR1[0] and ADDR0[0] both control + enable/disable of GCA recognition, as an effective OR of the 2 bit fields. + Therefore we set ADDR1[0] to 0 here, so that only ADDR0[0] controls GCA. + */ + second_slave_address = (uint8_t)((second_slave_addr << 1u) & (~SLAVE1_EN_MASK)); + + /* + * Single byte register write, should be interrupt safe + */ + HAL_set_8bit_reg(this_i2c->base_address, ADDRESS1, second_slave_address); +} + +/*------------------------------------------------------------------------------ + * I2C_disable_slave_second_addr() + * See "i2c.h" for details of how to use this function. + */ +void I2C_disable_slave_second_addr +( + i2c_instance_t * this_i2c +) +{ + /* + We are disabling the second slave address by setting the value of the 2nd + slave address to the primary slave address. The reason for using this method + of disabling 2nd slave address is that ADDRESS1[0] has different meaning + depending on hardware configuration. Its use would likely interfere with + the intended GCA setting. + */ + /* + * Single byte register write, should be interrupt safe + */ + HAL_set_8bit_reg(this_i2c->base_address, ADDRESS1, this_i2c->ser_address); +} + +/*------------------------------------------------------------------------------ + * i2C_set_gca() + * See "i2c.h" for details of how to use this function. + */ + +void I2C_set_gca +( + i2c_instance_t * this_i2c +) +{ + /* + * This read modify write access should be interrupt safe as the address + * register is not written to in the ISR. + */ + /* accept GC addressing. */ + HAL_set_8bit_reg_field(this_i2c->base_address, GC, 0x01u); +} + +/*------------------------------------------------------------------------------ + * I2C_clear_gca() + * See "i2c.h" for details of how to use this function. + */ +void I2C_clear_gca +( + i2c_instance_t * this_i2c +) +{ + /* + * This read modify write access should be interrupt safe as the address + * register is not written to in the ISR. + */ + /* Clear GC addressing. */ + HAL_set_8bit_reg_field(this_i2c->base_address, GC, 0x00u); +} + +/*------------------------------------------------------------------------------ + * I2C_isr() + * See "core_i2c.h" for details of how to use this function. + */ +void I2C_isr +( + i2c_instance_t * this_i2c +) +{ + volatile uint8_t status; + uint8_t data; + uint8_t hold_bus; + uint8_t clear_irq = 1u; + + status = HAL_get_8bit_reg( this_i2c->base_address, STATUS); + + switch( status ) + { + /************** MASTER TRANSMITTER / RECEIVER *******************/ + + case ST_START: /* start has been xmt'd */ + case ST_RESTART: /* repeated start has been xmt'd */ + HAL_set_8bit_reg_field( this_i2c->base_address, STA, 0x00u); + HAL_set_8bit_reg( this_i2c->base_address, DATA, this_i2c->target_addr); /* write call address */ + HAL_set_8bit_reg_field( this_i2c->base_address, DIR, this_i2c->dir); /* set direction bit */ + if(this_i2c->dir == WRITE_DIR) + { + this_i2c->master_tx_idx = 0u; + } + else + { + this_i2c->master_rx_idx = 0u; + } + + /* + * Clear the pending transaction. This condition will be true if the slave + * has acquired the bus to carry out pending master transaction which + * it had received during its slave transmission or reception mode. + */ + if(this_i2c->is_transaction_pending) + { + this_i2c->is_transaction_pending = 0u; + } + + /* + * Make sure to update proper transaction after master START + * or RESTART + */ + if(this_i2c->transaction != this_i2c->pending_transaction) + { + this_i2c->transaction = this_i2c->pending_transaction; + } + break; + + case ST_LOST_ARB: + /* Set start bit. Let's keep trying! Don't give up! */ + HAL_set_8bit_reg_field(this_i2c->base_address, STA, 0x01u); + break; + + case ST_STOP_TRANSMIT: + /* Stop has been transmitted. Do nothing */ + break; + + /******************* MASTER TRANSMITTER *************************/ + case ST_SLAW_NACK: + /* SLA+W has been transmitted; not ACK has been received - let's stop. */ + HAL_set_8bit_reg_field(this_i2c->base_address, STO, 0x01u); + this_i2c->master_status = I2C_FAILED; + this_i2c->transaction = NO_TRANSACTION; + enable_slave_if_required(this_i2c); + break; + + case ST_SLAW_ACK: + case ST_TX_DATA_ACK: + /* data byte has been xmt'd with ACK, time to send stop bit or repeated start. */ + if (this_i2c->master_tx_idx < this_i2c->master_tx_size) + { + HAL_set_8bit_reg(this_i2c->base_address, DATA, (uint_fast8_t)this_i2c->master_tx_buffer[this_i2c->master_tx_idx++]); + } + else if ( this_i2c->transaction == MASTER_RANDOM_READ_TRANSACTION ) + { + /* We are finished sending the address offset part of a random read transaction. + * It is is time to send a restart in order to change direction. */ + this_i2c->dir = READ_DIR; + HAL_set_8bit_reg_field(this_i2c->base_address, STA, 0x01u); + } + else /* done sending. let's stop */ + { + /* + * Set the transaction back to NO_TRANSACTION to allow user to do further + * transaction + */ + this_i2c->transaction = NO_TRANSACTION; + hold_bus = this_i2c->options & I2C_HOLD_BUS; + + /* Store the information of current I2C bus status in the bus_status*/ + this_i2c->bus_status = hold_bus; + if ( hold_bus == 0u ) + { + HAL_set_8bit_reg_field(this_i2c->base_address, STO, 0x01u); /*xmt stop condition */ + enable_slave_if_required(this_i2c); + } + else + { + I2C_disable_irq( this_i2c ); + clear_irq = 0u; + } + this_i2c->master_status = I2C_SUCCESS; + } + break; + + case ST_TX_DATA_NACK: + /* data byte SENT, ACK to be received + * In fact, this means we've received a NACK (This may not be + * obvious, but if we've rec'd an ACK then we would be in state + * 0x28!) hence, let's send a stop bit + */ + HAL_set_8bit_reg_field(this_i2c->base_address, STO, 0x01u);/* xmt stop condition */ + this_i2c->master_status = I2C_FAILED; + + /* + * Set the transaction back to NO_TRANSACTION to allow user to do further + * transaction + */ + this_i2c->transaction = NO_TRANSACTION; + enable_slave_if_required(this_i2c); + break; + + /********************* MASTER (or slave?) RECEIVER *************************/ + + /* STATUS codes 08H, 10H, 38H are all covered in MTX mode */ + case ST_SLAR_ACK: /* SLA+R tx'ed. */ + /* Let's make sure we ACK the first data byte received (set AA bit in CTRL) unless + * the next byte is the last byte of the read transaction. + */ + if(this_i2c->master_rx_size > 1u) + { + HAL_set_8bit_reg_field(this_i2c->base_address, AA, 0x01u); + } + else if(1u == this_i2c->master_rx_size) + { + HAL_set_8bit_reg_field(this_i2c->base_address, AA, 0x00u); + } + else /* this_i2c->master_rx_size == 0u */ + { + HAL_set_8bit_reg_field(this_i2c->base_address, AA, 0x01u); + HAL_set_8bit_reg_field(this_i2c->base_address, STO, 0x01u); + this_i2c->master_status = I2C_SUCCESS; + this_i2c->transaction = NO_TRANSACTION; + } + break; + + case ST_SLAR_NACK: /* SLA+R tx'ed; let's release the bus (send a stop condition) */ + HAL_set_8bit_reg_field(this_i2c->base_address, STO, 0x01u); + this_i2c->master_status = I2C_FAILED; + + /* + * Set the transaction back to NO_TRANSACTION to allow user to do further + * transaction + */ + this_i2c->transaction = NO_TRANSACTION; + enable_slave_if_required(this_i2c); + break; + + case ST_RX_DATA_ACK: /* Data byte received, ACK returned */ + /* First, get the data */ + this_i2c->master_rx_buffer[this_i2c->master_rx_idx++] = HAL_get_8bit_reg(this_i2c->base_address, DATA); + if( this_i2c->master_rx_idx >= (this_i2c->master_rx_size - 1u)) + { + /* If we're at the second last byte, let's set AA to 0 so + * we return a NACK at the last byte. */ + HAL_set_8bit_reg_field(this_i2c->base_address, AA, 0x00u); + } + break; + + case ST_RX_DATA_NACK: /* Data byte received, NACK returned */ + /* Get the data, then send a stop condition */ + this_i2c->master_rx_buffer[this_i2c->master_rx_idx] = HAL_get_8bit_reg(this_i2c->base_address, DATA); + + hold_bus = this_i2c->options & I2C_HOLD_BUS; + + /* Store the information of current I2C bus status in the bus_status*/ + this_i2c->bus_status = hold_bus; + if ( hold_bus == 0u ) + { + HAL_set_8bit_reg_field(this_i2c->base_address, STO, 0x01u); /*xmt stop condition */ + + /* Bus is released, now we can start listening to bus, if it is slave */ + enable_slave_if_required(this_i2c); + } + else + { + I2C_disable_irq( this_i2c ); + clear_irq = 0u; + } + /* + * Set the transaction back to NO_TRANSACTION to allow user to do further + * transaction + */ + this_i2c->transaction = NO_TRANSACTION; + this_i2c->master_status = I2C_SUCCESS; + break; + + /******************** SLAVE RECEIVER **************************/ + case ST_GCA_NACK: /* NACK after, GCA addressing */ + case ST_SLA_NACK: /* Re-enable AA (assert ack) bit for future transmissions */ + HAL_set_8bit_reg_field(this_i2c->base_address, AA, 0x01u); + + this_i2c->transaction = NO_TRANSACTION; + this_i2c->slave_status = I2C_SUCCESS; + + /* Check if transaction was pending. If yes, set the START bit */ + if(this_i2c->is_transaction_pending) + { + HAL_set_8bit_reg_field(this_i2c->base_address, STA, 0x01u); + } + break; + + case ST_GCA_LA: /* Arbitr. lost (GCA rec'd) */ + case ST_SLV_LA: /* Arbitr. lost (SLA rec'd) */ + /* + * We lost arbitration and either the GCE or our address was the + * one received so pend the master operation we were starting. + */ + this_i2c->is_transaction_pending = 1u; + /* Fall through to normal ST processing as we are now in slave mode */ + + case ST_GCA: /* General call address received, ACK returned */ + case ST_SLAVE_SLAW: /* SLA+W received, ACK returned */ + this_i2c->transaction = WRITE_SLAVE_TRANSACTION; + this_i2c->slave_rx_idx = 0u; + this_i2c->random_read_addr = 0u; + /* + * If Start Bit is set clear it, but store that information since it is because of + * pending transaction + */ + if(HAL_get_8bit_reg_field(this_i2c->base_address, STA)) + { + HAL_set_8bit_reg_field(this_i2c->base_address, STA, 0x00u); + this_i2c->is_transaction_pending = 1u; + } + this_i2c->slave_status = I2C_IN_PROGRESS; +#ifdef INCLUDE_SLA_IN_RX_PAYLOAD + /* Fall through to put address as first byte in payload buffer */ +#else + /* Only break from this case if the slave address must NOT be included at the + * beginning of the received write data. */ + break; +#endif + case ST_GCA_ACK: /* DATA received; ACK sent after GCA */ + case ST_RDATA: /* DATA received; must clear DATA register */ + if((this_i2c->slave_rx_buffer != (uint8_t *)0) + && (this_i2c->slave_rx_idx < this_i2c->slave_rx_size)) + { + data = HAL_get_8bit_reg(this_i2c->base_address, DATA); + this_i2c->slave_rx_buffer[this_i2c->slave_rx_idx++] = data; + +#ifdef INCLUDE_SLA_IN_RX_PAYLOAD + if((ST_RDATA == status) || (ST_GCA_ACK == status)) + { + /* Ignore the slave address byte in the random read address + computation in the case where INCLUDE_SLA_IN_RX_PAYLOAD + is defined. */ +#endif + this_i2c->random_read_addr = (this_i2c->random_read_addr << 8) + data; +#ifdef INCLUDE_SLA_IN_RX_PAYLOAD + } +#endif + } + + if(this_i2c->slave_rx_idx >= this_i2c->slave_rx_size) + { + /* Rx buffer is full. NACK next received byte. */ + HAL_set_8bit_reg_field(this_i2c->base_address, AA, 0x00u); + } + break; + + case ST_RSTOP: + /* STOP or repeated START occurred. */ + /* We cannot be sure if the transaction has actually completed as + * this hardware state reports that either a STOP or repeated START + * condition has occurred. We assume that this is a repeated START + * if the transaction was a write from the master to this point.*/ + if ( this_i2c->transaction == WRITE_SLAVE_TRANSACTION ) + { + if ( this_i2c->slave_rx_idx == this_i2c->slave_mem_offset_length ) + { + this_i2c->slave_tx_idx = this_i2c->random_read_addr; + } + /* Call the slave's write transaction handler if it exists. */ + if ( this_i2c->slave_write_handler != 0u ) + { + i2c_slave_handler_ret_t h_ret; + h_ret = this_i2c->slave_write_handler( this_i2c, this_i2c->slave_rx_buffer, (uint16_t)this_i2c->slave_rx_idx ); + if ( I2C_REENABLE_SLAVE_RX == h_ret ) + { + /* There is a small risk that the write handler could + * call I2C_disable_slave() but return + * I2C_REENABLE_SLAVE_RX in error so we only enable + * ACKs if still in slave mode. */ + enable_slave_if_required(this_i2c); + } + else + { + HAL_set_8bit_reg_field( this_i2c->base_address, AA, 0x0u ); + /* Clear slave mode flag as well otherwise in mixed + * master/slave applications, the AA bit will get set by + * subsequent master operations. */ + this_i2c->is_slave_enabled = 0u; + } + } + else + { + /* Re-enable address acknowledge in case we were ready to nack the next received byte. */ + HAL_set_8bit_reg_field( this_i2c->base_address, AA, 0x01u ); + } + } + else /* A stop or repeated start outside a write/read operation */ + { + /* + * Reset slave_tx_idx so that a subsequent read will result in the slave's + * transmit buffer being sent from the first byte. + */ + this_i2c->slave_tx_idx = 0u; + /* + * See if we need to re-enable acknowledgement as some error conditions, such + * as a master prematurely ending a transfer, can see us get here with AA set + * to 0 which will disable slave operation if we are not careful. + */ + enable_slave_if_required(this_i2c); + } + + /* Mark any previous master write transaction as complete. */ + this_i2c->slave_status = I2C_SUCCESS; + + /* Check if transaction was pending. If yes, set the START bit */ + if(this_i2c->is_transaction_pending) + { + HAL_set_8bit_reg_field(this_i2c->base_address, STA, 0x01u); + } + + /* + * Set the transaction back to NO_TRANSACTION to allow user to do further + * transaction + */ + this_i2c->transaction = NO_TRANSACTION; + + break; + + case ST_SLV_RST: /* SMBUS ONLY: timeout state. must clear interrupt */ + /* + * Set the transaction back to NO_TRANSACTION to allow user to do further + * transaction. + */ + this_i2c->transaction = NO_TRANSACTION; + /* + * Reset slave_tx_idx so that a subsequent read will result in the slave's + * transmit buffer being sent from the first byte. + */ + this_i2c->slave_tx_idx = 0u; + /* + * Clear status to I2C_FAILED only if there was an operation in progress. + */ + if(I2C_IN_PROGRESS == this_i2c->slave_status) + { + this_i2c->slave_status = I2C_FAILED; + } + + enable_slave_if_required(this_i2c); /* Make sure AA is set correctly */ + + break; + + /****************** SLAVE TRANSMITTER **************************/ + case ST_SLAVE_SLAR_ACK: /* SLA+R received, ACK returned */ + case ST_SLARW_LA: /* Arbitration lost, and: */ + case ST_RACK: /* Data tx'ed, ACK received */ + if ( status == ST_SLAVE_SLAR_ACK ) + { + this_i2c->transaction = READ_SLAVE_TRANSACTION; + this_i2c->random_read_addr = 0u; + this_i2c->slave_status = I2C_IN_PROGRESS; + /* If Start Bit is set clear it, but store that information since it is because of + * pending transaction + */ + if(HAL_get_8bit_reg_field(this_i2c->base_address, STA)) + { + HAL_set_8bit_reg_field(this_i2c->base_address, STA, 0x00u); + this_i2c->is_transaction_pending = 1u; + } + } + if (this_i2c->slave_tx_idx >= this_i2c->slave_tx_size) + { + /* Ensure 0xFF is returned to the master when the slave specifies + * an empty transmit buffer. */ + HAL_set_8bit_reg(this_i2c->base_address, DATA, 0xFFu); + } + else + { + /* Load the data the data byte to be sent to the master. */ + HAL_set_8bit_reg(this_i2c->base_address, DATA, (uint_fast8_t)this_i2c->slave_tx_buffer[this_i2c->slave_tx_idx++]); + } + /* Determine if this is the last data byte to send to the master. */ + if (this_i2c->slave_tx_idx >= this_i2c->slave_tx_size) /* last byte? */ + { + HAL_set_8bit_reg_field(this_i2c->base_address, AA, 0x00u); + /* Next read transaction will result in slave's transmit buffer + * being sent from the first byte. */ + this_i2c->slave_tx_idx = 0u; + } + break; + + case ST_SLAVE_RNACK: /* Data byte has been transmitted; not-ACK has been received. */ + case ST_FINAL: /* Last Data byte tx'ed, ACK received */ + /* We assume that the transaction will be stopped by the master. + * Reset slave_tx_idx so that a subsequent read will result in the slave's + * transmit buffer being sent from the first byte. */ + this_i2c->slave_tx_idx = 0u; + HAL_set_8bit_reg_field(this_i2c->base_address, AA, 0x01u); + + /* Mark previous state as complete */ + this_i2c->slave_status = I2C_SUCCESS; + /* Check if transaction was pending. If yes, set the START bit */ + if(this_i2c->is_transaction_pending) + { + HAL_set_8bit_reg_field(this_i2c->base_address, STA, 0x01u); + } + /* + * Set the transaction back to NO_TRANSACTION to allow user to do further + * transaction + */ + this_i2c->transaction = NO_TRANSACTION; + + break; + + /* Master Reset has been activated Wait 35 ms for interrupt to be set, + * clear interrupt and proceed to 0xF8 state. */ + case ST_RESET_ACTIVATED: + case ST_BUS_ERROR: /* Bus error during MST or selected slave modes */ + default: + /* Some undefined state has encountered. Clear Start bit to make + * sure, next good transaction happen */ + HAL_set_8bit_reg_field(this_i2c->base_address, STA, 0x00u); + /* + * Set the transaction back to NO_TRANSACTION to allow user to do further + * transaction. + */ + this_i2c->transaction = NO_TRANSACTION; + /* + * Reset slave_tx_idx so that a subsequent read will result in the slave's + * transmit buffer being sent from the first byte. + */ + this_i2c->slave_tx_idx = 0u; + /* + * Clear statuses to I2C_FAILED only if there was an operation in progress. + */ + if(I2C_IN_PROGRESS == this_i2c->master_status) + { + this_i2c->master_status = I2C_FAILED; + } + + if(I2C_IN_PROGRESS == this_i2c->slave_status) + { + this_i2c->slave_status = I2C_FAILED; + } + + break; + } + + if ( clear_irq ) + { + /* clear interrupt. */ + HAL_set_8bit_reg_field(this_i2c->base_address, SI, 0x00u); + } + + /* Read the status register to ensure the last I2C registers write took place + * in a system built around a bus making use of posted writes. */ + status = HAL_get_8bit_reg( this_i2c->base_address, STATUS); +} + +/*------------------------------------------------------------------------------ + * I2C_smbus_init() + * See "i2c.h" for details of how to use this function. + */ + +/* + * SMBSUS_NO = 1 + * SMBALERT_NO = 1 + * SMBus enable = 1 + */ +#define INIT_AND_ENABLE_SMBUS 0x54u +void I2C_smbus_init +( + i2c_instance_t * this_i2c +) +{ + /* + * Single byte register write, should be interrupt safe + */ + /* Enable SMBUS */ + HAL_set_8bit_reg(this_i2c->base_address, SMBUS, INIT_AND_ENABLE_SMBUS); +} + +/*------------------------------------------------------------------------------ + * I2C_enable_smbus_irq() + * See "i2c.h" for details of how to use this function. + */ +void I2C_enable_smbus_irq +( + i2c_instance_t * this_i2c, + uint8_t irq_type +) +{ + psr_t saved_psr; + + /* + * We need to disable interrupts here to ensure we can update the + * hardware register without the SMBUS IRQs interrupting us. + */ + saved_psr = HAL_disable_interrupts(); + + if ( irq_type & I2C_SMBALERT_IRQ) + { + HAL_set_8bit_reg_field(this_i2c->base_address, SMBALERT_IE, 0x01u); + } + if ( irq_type & I2C_SMBSUS_IRQ) + { + HAL_set_8bit_reg_field(this_i2c->base_address, SMBSUS_IE, 0x01u); + } + + HAL_restore_interrupts( saved_psr ); +} + +/*------------------------------------------------------------------------------ + * I2C_disable_smbus_irq() + * See "i2c.h" for details of how to use this function. + */ +void I2C_disable_smbus_irq +( + i2c_instance_t * this_i2c, + uint8_t irq_type +) +{ + psr_t saved_psr; + + /* + * We need to disable interrupts here to ensure we can update the + * hardware register without the SMBUS IRQs interrupting us. + */ + saved_psr = HAL_disable_interrupts(); + + if ( irq_type & I2C_SMBALERT_IRQ) + { + HAL_set_8bit_reg_field(this_i2c->base_address, SMBALERT_IE, 0x00u); + } + if (irq_type & I2C_SMBSUS_IRQ ) + { + HAL_set_8bit_reg_field(this_i2c->base_address, SMBSUS_IE, 0x00u); + } + + HAL_restore_interrupts( saved_psr ); +} + +/*------------------------------------------------------------------------------ + * I2C_suspend_smbus_slave() + * See "i2c.h" for details of how to use this function. + */ +void I2C_suspend_smbus_slave +( + i2c_instance_t * this_i2c +) +{ + psr_t saved_psr; + + /* + * We need to disable interrupts here to ensure we can update the + * hardware register without the SMBUS IRQs interrupting us. + */ + saved_psr = HAL_disable_interrupts(); + + HAL_set_8bit_reg_field(this_i2c->base_address, SMBSUS_NO_CONTROL, 0x00u); + + HAL_restore_interrupts( saved_psr ); +} + +/*------------------------------------------------------------------------------ + * I2C_resume_smbus_slave() + * See "i2c.h" for details of how to use this function. + */ +void I2C_resume_smbus_slave +( + i2c_instance_t * this_i2c +) +{ + psr_t saved_psr; + + /* + * We need to disable interrupts here to ensure we can update the + * hardware register without the SMBUS IRQs interrupting us. + */ + saved_psr = HAL_disable_interrupts(); + + HAL_set_8bit_reg_field(this_i2c->base_address, SMBSUS_NO_CONTROL, 0x01u); + + HAL_restore_interrupts( saved_psr ); +} + +/*------------------------------------------------------------------------------ + * I2C_reset_smbus() + * See "i2c.h" for details of how to use this function. + */ +void I2C_reset_smbus +( + i2c_instance_t * this_i2c +) +{ + psr_t saved_psr; + + /* + * We need to disable interrupts here to ensure we can update the + * hardware register without the SMBUS IRQs interrupting us. + */ + saved_psr = HAL_disable_interrupts(); + HAL_set_8bit_reg_field(this_i2c->base_address, SMBUS_MST_RESET, 0x01u); + + HAL_restore_interrupts( saved_psr ); +} + +/*------------------------------------------------------------------------------ + * I2C_set_smbus_alert() + * See "i2c.h" for details of how to use this function. + */ +void I2C_set_smbus_alert +( + i2c_instance_t * this_i2c +) +{ + psr_t saved_psr; + + /* + * We need to disable interrupts here to ensure we can update the + * hardware register without the SMBUS IRQs interrupting us. + */ + saved_psr = HAL_disable_interrupts(); + HAL_set_8bit_reg_field(this_i2c->base_address, SMBALERT_NO_CONTROL, 0x00u); + + HAL_restore_interrupts( saved_psr ); +} + +/*------------------------------------------------------------------------------ + * I2C_clear_smbus_alert() + * See "i2c.h" for details of how to use this function. + */ +void I2C_clear_smbus_alert +( + i2c_instance_t * this_i2c +) +{ + psr_t saved_psr; + + /* + * We need to disable interrupts here to ensure we can update the + * hardware register without the SMBUS IRQs interrupting us. + */ + saved_psr = HAL_disable_interrupts(); + + HAL_set_8bit_reg_field(this_i2c->base_address, SMBALERT_NO_CONTROL, 0x01u); + + HAL_restore_interrupts( saved_psr ); +} + +/*------------------------------------------------------------------------------ + * I2C_get_irq_status() + * See "i2c.h" for details of how to use this function. + */ +uint8_t I2C_get_irq_status +( + i2c_instance_t * this_i2c +) +{ + uint8_t status ; + uint8_t irq_type = I2C_NO_IRQ ; + + status = HAL_get_8bit_reg(this_i2c->base_address, SMBUS); + + if( status & (uint8_t)SMBALERT_NI_STATUS_MASK ) + { + irq_type |= I2C_SMBALERT_IRQ ; + } + + if( status & (uint8_t)SMBSUS_NI_STATUS_MASK ) + { + irq_type |= I2C_SMBSUS_IRQ ; + } + + status = HAL_get_8bit_reg(this_i2c->base_address, CONTROL); + + if( status & (uint8_t)SI_MASK ) + { + irq_type |= I2C_INTR_IRQ ; + } + return(irq_type); +} + +/*------------------------------------------------------------------------------ + * I2C_set_slave_addr2() + * See "i2c.h" for details of how to use this function. + */ +void I2C_set_user_data +( + i2c_instance_t * this_i2c, + void * p_user_data +) +{ + this_i2c->p_user_data = p_user_data ; +} + +/*------------------------------------------------------------------------------ + * I2C_get_user_data() + * See "i2c.h" for details of how to use this function. + */ +void * I2C_get_user_data +( + i2c_instance_t * this_i2c +) +{ + return( this_i2c->p_user_data); +} + +#ifdef __cplusplus +} +#endif + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreI2C/core_i2c.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreI2C/core_i2c.h new file mode 100644 index 0000000..13f090d --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreI2C/core_i2c.h @@ -0,0 +1,2306 @@ +/***************************************************************************//** + * Copyright 2009-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * CoreI2C software driver Application Programming Interface. + * This file contains defines and function declarations allowing to interface + * with the CoreI2C software driver. + * + */ +/*=========================================================================*//** + @mainpage CoreI2C Bare Metal Driver. + The CoreI2C bare metal software driver supports I2C master and slave + operations. + + ============================================================================== + Introduction + ============================================================================== + The CoreI2C driver provides a set of functions for controlling the Microchip + CoreI2C hardware IP. The driver supports up to 16 separate I2C channels per + CoreI2C instance, with common slave address settings shared between channels + on a device. + + Optional features of the CoreI2C allow it to operate with I2C based protocols + such as System Management Bus (SMBus), Power Management Bus (PMBus), and + Intelligent Platform Management Interface (IPMI). This driver provides support + for these features when enabled in the CoreI2C IP. + + The major features provided by CoreI2C driver: + - Provides support to configuring the I2C channels of each CoreI2C peripheral device. + - I2C master operations. + - I2C slave operations. + - SMBus related operations. + + This driver is used as part of a bare metal system where no operating + system is available. The driver gets adapted as a part of an operating system, + but the implementation of the adaptation layer between the driver and the operating + system's driver model is outside the scope of this driver. + + ============================================================================== + Hardware Flow Dependencies + ============================================================================== + Your application software should configure the CoreI2C driver through + calls to the I2C_init() function for each CoreI2C instance in the + hardware design. The configuration parameters include the CoreI2C hardware + instance base address and other runtime parameters, such as the I2C serial + clock frequency and the I2C device address. + + Once channel 0 of a CoreI2C peripheral has been initialized by I2C_init(), + any additional channels present should be configured by calling + I2C_channel_init() for each of the remaining channels. + + Apart from the CoreI2C hardware instance base address, no CoreI2C hardware configuration + parameters are used by the driver. Hence, no additional configuration files are required + to use the driver. + + -------------------------------- + Interrupt Control + -------------------------------- + The CoreI2C driver has to enable and disable the generation of interrupts by + CoreI2C at various times when it is operating. This enabling and disabling of + interrupts must be done through the system’s interrupt controller. For that + reason, the method of controlling the CoreI2C interrupt is system specific + and it is necessary to customize the I2C_enable_irq() and I2C_disable_irq() + functions. These functions are available in the i2c_interrupt.c file. + The default implementation calls HAL_ASSERT(0) to indicate to the application + developer that a suitable implementations for these functions must be provided. + + The implementation of the I2C_enable_irq() function should permit interrupts + generated by a CoreI2C instance to interrupt the processor. The implementation + of the I2C_disable_irq() function should prevent interrupts generated by a + CoreI2C instance from interrupting the processor. See the provided example + projects for a working implementation of these functions. + + The I2C_register_write_handler() function registers a write handler + function with the CoreI2C driver that calls on completion of an I2C write + transaction by the CoreI2C slave. It is your responsibility to create and + register the implementation of this handler function that processes or + trigger the processing of the received data. + + The SMBSUS and SMBALERT interrupts are related to the SMBus interface and are + enabled and disabled through I2C_enable_smbus_irq() and + I2C_disable_smbus_irq() respectively. It is your responsibility to create + interrupt handler functions in your application to get the desired response + for the SMBus interrupts. + + Note: You must include the path to any application header files that are + included in the i2c_interrupt.c file, as an include path in your + project's compiler settings. The details of how to do this will depend + on your development software. + + -------------------------------- + SMBus Logic Options + -------------------------------- + SMBus related APIs does not have any effect if the "Generate SMBus Logic" + is not enabled in the CoreI2C hardware configuration. Following are API's + that does not give the desired results if SMBus Logic is disabled. + + - I2C_smbus_init() + - I2C_reset_smbus() + - I2C_enable_smbus_irq() + - I2C_disable_smbus_irq() + - I2C_suspend_smbus_slave() + - I2C_resume_smbus_slave() + - I2C_set_smsbus_alert() + - I2C_clear_smsbus_alert() + - I2C_get_irq_status() + + -------------------------------- + Fixed Baud Rate Values + -------------------------------- + The serial clock frequency parameter passed to the I2C_init() and + I2C_channel_init() functions may not have any effect if fixed values were + selected for Baud rate in the hardware configuration of CoreI2C. When fixed + values are selected for these baud rates, the driver cannot overwrite + the fixed values. + + ----------------------------------- + Fixed Slave Address Options Values + ----------------------------------- + The primary slave address parameter passed to the I2C_init() function and + secondary address value passed to the I2C_set_slave_second_addr() function, + may not have the desired effect if fixed values were selected for the slave 0 + address and slave 1 address respectively. Proper operation of this version of + the driver requires the slave addresses to be programmable. + + ============================================================================== + Theory of Operation + ============================================================================== + The CoreI2C software driver is designed to allow the control of multiple + instances of CoreI2C with one or more I2C channels. Each channel in an + instance of CoreI2C in the hardware design is associated with a single + instance of the i2c_instance_t structure in the software. You must allocate + memory for one unique i2c_instance_t structure instance for each channel of + each CoreI2C hardware instance. The contents of these data structures are + initialised by calling I2C_init() and if necessary I2C_channel_init(). + A pointer to the structure is passed to the subsequent driver functions in order + to identify the CoreI2C hardware instance and channel to perform the + requested operation. + + Note: Do not attempt to directly manipulate the contents of i2c_instance_t + structures. These structures are only intended to be modified by the driver + functions. + + The CoreI2C driver functions are grouped into the following categories: + - Initialization and configuration functions + - Interrupt control + - I2C slave addressing functions + - I2C master operations functions to handle write, read, and write-read + transactions + - I2C slave operations functions to handle write, read, and write-read + transactions + - Mixed master-slave operations + - SMBus interface configuration and control + + -------------------------------- + Initialization and Configuration + -------------------------------- + The CoreI2C device is first initialized by calling the I2C_init() + function. Since each CoreI2C peripheral supports up to 16 channels, an + additional function, I2C_channel_init(), is required to initialize the + remaining channels with their own data structures. + + I2C_init() function initializes channel 0 of a CoreI2C and the i2c_instance_t + for channel 0 acts as the basis for further channel initialization as the + hardware base address and I2C serial address are same across all the channels. + Ensure to call I2C_init() function before calling any other I2C driver function + calls. The I2C_init() call for each CoreI2C takes the I2C serial address assigned + to the I2C and the serial clock divider to generate its I2C clock as configuration + parameters. + + I2C_channel_init() function takes as input parameters a pointer to the CoreI2C + i2c_instance_t which has been initialized by calling the I2C_init() and a pointer + to a separate i2c_instance_t which represents this new channel. Another input + parameter which is required by this function is serial clock divider which generates + its I2C clock. + + -------------------------------- + Interrupt Control + -------------------------------- + The CoreI2C driver is interrupt driven and it uses each channels INT + interrupt to drive the state machine which is at the heart of the driver. + The application is responsible for providing the link between the interrupt + generating hardware and the CoreI2C interrupt handler and must ensure that + the I2C_isr() function is called with the correct i2c_instance_t structure + pointer for the CoreI2C channel initiating the interrupt. + + The driver enables and disables the generation of INT interrupts by CoreI2C + at various times when it is operating through the user supplied + I2C_enable_irq() and I2C_disable_irq() functions. + + The I2C_register_write_handler() function is used to register a write + handler function with the CoreI2C driver which is called on completion + of an I2C write transaction by the CoreI2C slave. It is the user + applications responsibility to create and register the implementation of + this handler function that processes or triggers the processing of the + received data. + + The other two interrupt sources in the CoreI2C are related to SMBus + operation and are enabled and disabled through I2C_enable_smbus_irq() and + I2C_disable_smbus_irq() respectively. Due to the application specific + nature of the response to SMBus interrupts, you must design interrupt + handler functions in the application to get the desired behaviour for + SMBus related interrupts. + + If enabled, the SMBA_INT signal from the CoreI2C is asserted if an + SMBALERT condition is signalled on the SMBALERT_NI input for the channel. + + If enabled, the SMBS_INT signal from the CoreI2C is asserted if an + SMBSUSPEND condition is signalled on the SMBSUS_NI input for the channel. + + ## I2C Slave Addressing Functions + A CoreI2C peripheral responds to the following three slave addresses: + - Slave address 0 - This is the primary slave address that accesses + a CoreI2C channel when it acts as a slave in + I2C transactions. You must configure the primary slave + address using I2C_init(). + + - Slave address 1 - This is the secondary slave address which might be + required in certain application specific scenarios. + The secondary slave address is configured by + I2C_set_slave_second_addr() and is disabled by + I2C_disable_slave_second_addr(). + + - General call address - A CoreI2C slave can be configured to respond to + a broadcast command by a master transmitting the + general call address of 0x00. Use the I2C_set_gca() + function to enable the slave to respond to the general + call address. If the CoreI2C slave is not required to + respond to the general call address, disable this + address by calling I2C_clear_gca(). + + Note: All channels on a CoreI2C instance share the same slave address logic. + This means that they cannot have separate slave addresses and rely on + the separate physical I2C bus connections to distinguish them. + + -------------------------------- + Transaction Types + -------------------------------- + The I2C driver is designed to handle three types of I2C transaction: + - Write transactions + - Read transactions + - Write-read transactions + + ## Write Transaction + The master I2C device initiates a write transaction by sending a START bit + as soon as the bus becomes free. The START bit is followed by the 7-bit + serial address of the target slave device followed by the read/write bit + indicating the direction of the transaction. The slave acknowledges the + receipt of it's address with an acknowledge bit. The master sends data one + byte at a time to the slave, which must acknowledge the receipt of each byte + for the next byte to be sent. The master sends a STOP bit to complete the + transaction. The slave can abort the transaction by replying with a + non-acknowledge bit instead of an acknowledge bit. + + The application programmer can choose not to send a STOP bit at the end of + the transaction causing the next transaction to begin with a repeated + START bit. + + ## Read Transaction + The master I2C device initiates a read transaction by sending a START bit + as soon as the bus becomes free. The START bit is followed by the 7-bit + serial address of the target slave device followed by the read/write bit + indicating the direction of the transaction. The slave acknowledges the + receipt of it's slave address with an acknowledge bit. The slave sends + data one byte at a time to the master, which must acknowledge the receipt of + each byte for the next byte to be sent. The master sends a non-acknowledge + bit following the last byte it wishes to read followed by a STOP bit. + + The application programmer can choose not to send a STOP bit at the end of + the transaction causing the next transaction to begin with a repeated + START bit. + + ## Write-Read Transaction + The write-read transaction is a combination of a write transaction + immediately followed by a read transaction. There is no STOP bit in between + the write and read phases of a write-read transaction. A repeated START + bit is sent between the write and read phases. + + Whilst the write handler is being executed, the slave holds the clock line + low to stretch the clock until the response is ready. + + The write-read transaction is typically used to send a command or offset + in the write transaction specifying the logical data to be transferred + during the read phase. + + The application programmer can choose not to send a STOP bit at the end of + the transaction causing the next transaction to begin with a repeated + START bit. + + ## Master Operations + The application can use the I2C_write(), I2C_read(), and I2C_write_read() + functions to initiate an I2C bus transaction. The application can then wait + for the transaction to complete using the I2C_wait_complete() function + or poll the status of the I2C transaction using the I2C_get_status() + function until it returns a value different from I2C_IN_PROGRESS. The + I2C_system_tick() function is used to set a time base for the + I2C_wait_complete() function's time out delay. + + ## Slave Operations + To configure the I2C driver to operate as an I2C slave requires the use + of the following functions: + - I2C_set_slave_tx_buffer() + - I2C_set_slave_rx_buffer() + - I2C_set_slave_mem_offset_length() + - I2C_register_write_handler() + - I2C_enable_slave() + + Use of all functions is not required if the slave I2C does not need to support + all types of I2C read transactions. The subsequent sections list the functions + that must be used to support each transaction type. + + ## Responding to Read Transactions + The following functions are used to configure the CoreI2C driver to + respond to I2C read transactions: + • I2C_set_slave_tx_buffer() + • I2C_enable_slave() + + The I2C_set_slave_tx_buffer() function specifies the data buffer that + is transmitted when the I2C slave is the target of an I2C read + transaction. It is then up to the application to manage the content of + that buffer to control the data that will be transmitted to the I2C + master as a result of the read transaction. + + The I2C_enable_slave() function enables the I2C hardware instance + to respond to the I2C transactions. It must be called after the I2C driver + has been configured to respond to the required transaction types. + + ## Responding to Write Transactions + The following functions are used to configure the I2C driver to respond + to I2C write transactions: + • I2C_set_slave_rx_buffer() + • I2C_register_write_handler() + • I2C_enable_slave() + + The I2C_set_slave_rx_buffer() function specifies the data buffer that + stored the data received by the I2C slave when it targets an I2C write + transaction. + + The I2C_register_write_handler() function specifies the handler function + that must be called on completion of the I2C write transaction. It is this + handler function that processes or triggers the processing of the received + data. + + The I2C_enable_slave() function enables the I2C hardware instance + to respond to I2C transactions. It must be called after the I2C driver + has been configured to respond to the required transaction types. + + ## Responding to Write-Read Transactions + The following functions are used to configure the CoreI2C driver to + respond to write-read transactions: + - I2C_set_slave_mem_offset_length() + - I2C_set_slave_tx_buffer() + - I2C_set_slave_rx_buffer() + - I2C_register_write_handler() + - I2C_enable_slave() + + The I2C_set_slave_mem_offset_length() function specifies the number of + bytes expected by the I2C slave during the write phase of the write-read + transaction. + + The I2C_set_slave_tx_buffer() function specifies the data that is + transmitted to the I2C master during the read phase of the write-read + transaction. The value received by the I2C slave during the write phase of + the transaction will be used as an index into the transmit buffer + specified by this function. It decides which part of the transmit buffer + will be transmitted to the I2C master as part of the read phase of the + write-read transaction. + + The I2C_set_slave_rx_buffer() function specifies the data buffer that + stores the data received by the I2C slave during the write phase of + the write-read transaction. This buffer must be large enough to accommodate + the number of bytes specified through the I2C_set_slave_mem_offset_length() + function. + + The I2C_register_write_handler() function can optionally be used to + specify a handler function that is called on completion of the write phase + of the I2C write-read transaction. If a handler function is registered, it + is responsible for processing the received data in the slave receive + buffer and populating the slave transmit buffer with the data that will be + transmitted to the I2C master as part of the read phase of the write-read + transaction. + + The I2C_enable_slave() function enables the CoreI2C hardware instance to + respond to the I2C transactions. It must be called after configuring the + CoreI2C driver to respond to the required transaction types. + + ## Mixed Master-Slave Operations + The CoreI2C device supports mixed master and slave operations. If the + CoreI2C slave has a transaction in progress and your application attempts to + begin a master mode transaction, the CoreI2C driver queues the master mode + transaction until the bus is released and the CoreI2C can switch to master + mode and acquire the bus. The CoreI2C master then starts the previously + queued master transaction. + + ## SMBus Control + The CoreI2C driver enables the CoreI2C peripheral’s SMBus functionality + using the I2C_smbus_init() function. + + The I2C_suspend_smbus_slave() function is used with a master mode CoreI2C + to force slave devices on the SMBus to enter their Power-Down/Suspend mode. + The I2C_resume_smbus_slave() function is used to end the suspend operation + on the SMBus. + + The I2C_reset_smbus() function is used with a master mode CoreI2C to force + all devices on the SMBus to reset their SMBUs interface. + + The I2C_set_smsbus_alert() function is used by a slave mode CoreI2C to + force communication with the SMBus master. Once communications with the + master is initiated, the I2C_clear_smsbus_alert() function clears the alert + condition. + + The I2C_enable_smbus_irq() and I2C_disable_smbus_irq() functions are used to + enable and disable the SMBSUS and SMBALERT SMBus interrupts. + + *//*=========================================================================*/ + +#ifndef CORE_I2C_H_ +#define CORE_I2C_H_ + +#ifndef LEGACY_DIR_STRUCTURE +#include "hal/hal.h" + +#else +#include "hal.h" +#include "hal_assert.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/*-------------------------------------------------------------------------*//** + I2C_RELEASE_BUS + ======================================= + The I2C_RELEASE_BUS constant is used to specify the options parameter to + functions I2C_read(), I2C_write() and I2C_write_read() to indicate + that a STOP bit must be generated at the end of the I2C transaction to release + the bus. + */ +#define I2C_RELEASE_BUS 0x00u + +/*-------------------------------------------------------------------------*//** + I2C_HOLD_BUS + ======================================= + The I2C_HOLD_BUS constant specify the options parameter to functions I2C_read(), + I2C_write(), and I2C_write_read() to indicate that a STOP bit must not be + generated at the end of the I2C transaction in order to retain the bus ownership. + This causes the next transaction to begin with a repeated START bit and no STOP + bit between the transactions. + */ +#define I2C_HOLD_BUS 0x01u + +/*-------------------------------------------------------------------------*//** + Interrupt Identifier Number + ======================================= + The following constants specify the interrupt identifier number which is + solely used by the driver API. This has nothing to do with hardware interrupt + line. I2C_INTR_IRQ is the primary interrupt signal which drives the state machine + of the CoreI2C driver. The I2C_SMBALERT_IRQ and I2C_SMBSUS_IRQ are used by + SMBus interrupt enable and disable functions. These IRQ numbers are also used + by I2C_get_irq_status(). + + | Constant | Description | + |--------------------|--------------------------------------------------------| + | I2C_NO_IRQ | No interrupt | + | I2C_SMBALERT_IRQ | Used by SMBus interrupt enable functions | + | I2C_SMBSUS_IRQ | Used by SMBus interrupt disable functions | + | I2C_INTR_IRQ | Primary interrupt signal which drives the state machine| + + */ +#define I2C_NO_IRQ 0x00u +#define I2C_SMBALERT_IRQ 0x01u +#define I2C_SMBSUS_IRQ 0x02u +#define I2C_INTR_IRQ 0x04u + +/*-------------------------------------------------------------------------*//** + I2C_NO_TIMEOUT + ======================================= + The I2C_wait_complete() function uses I2C_NO_TIMEOUT constant as a parameter + to indicate that the wait for completion of the transaction should not time out. + */ +#define I2C_NO_TIMEOUT 0u + +/***************************************************************************//** + The i2c_channel_number_t type is used to specify the channel number of a + CoreI2C instance. + */ +typedef enum i2c_channel_number { + I2C_CHANNEL_0 = 0u, + I2C_CHANNEL_1, + I2C_CHANNEL_2, + I2C_CHANNEL_3, + I2C_CHANNEL_4, + I2C_CHANNEL_5, + I2C_CHANNEL_6, + I2C_CHANNEL_7, + I2C_CHANNEL_8, + I2C_CHANNEL_9, + I2C_CHANNEL_10, + I2C_CHANNEL_11, + I2C_CHANNEL_12, + I2C_CHANNEL_13, + I2C_CHANNEL_14, + I2C_CHANNEL_15, + I2C_MAX_CHANNELS = 16u +} i2c_channel_number_t; + +/***************************************************************************//** + The i2c_clock_divider_t type specifies the divider to be applied + to the I2C PCLK or BCLK signal in order to generate the I2C clock. + The I2C_BCLK_DIV_8 value selects a clock frequency based on division of BCLK, + all other values select a clock frequency based on division of PCLK. + */ +typedef enum i2c_clock_divider { + I2C_PCLK_DIV_256 = 0u, + I2C_PCLK_DIV_224, + I2C_PCLK_DIV_192, + I2C_PCLK_DIV_160, + I2C_PCLK_DIV_960, + I2C_PCLK_DIV_120, + I2C_PCLK_DIV_60, + I2C_BCLK_DIV_8 +} i2c_clock_divider_t; + +/***************************************************************************//** + The i2c_status_t type is used to report the status of I2C transactions. + */ +typedef enum i2c_status +{ + I2C_SUCCESS = 0u, + I2C_IN_PROGRESS, + I2C_FAILED, + I2C_TIMED_OUT +} i2c_status_t; + +/***************************************************************************//** + The i2c_slave_handler_ret_t type is used by slave write handler functions + to indicate whether or not the received data buffer should be released. + */ +typedef enum i2c_slave_handler_ret { + I2C_REENABLE_SLAVE_RX = 0u, + I2C_PAUSE_SLAVE_RX = 1u +} i2c_slave_handler_ret_t; + +/***************************************************************************//** + This structure identifies various CoreI2C hardware instances in the system + and the I2C channels within them. The application software should declare + one instance of this structure for each channel of each instance of CoreI2C + in your system. I2C_init() and I2C_channel_init() functions initialize this + structure depending on whether it is channel 0 or one of the additional + channels, respectively. A pointer to an initialized instance of the + structure should be passed as the first parameter to the CoreI2C driver + functions, to identify which CoreI2C hardware instance and channel should + perform the requested operation. + + The contents of this data structure should not be modified or used outside of + the CoreI2C driver. Software using the CoreI2C driver should only need to + create one single instance of this data structure for each channel of each + CoreI2C hardware instance in the system then pass a pointer to these data + structures with each call to the CoreI2C driver in order to identify which + CoreI2C hardware instance to use. + */ +typedef struct i2c_instance i2c_instance_t ; +/***************************************************************************//* + Slave write handler functions prototype +/***************************************************************************//** + This defines the function prototype that must be followed by I2C slave write + handler functions. These functions are registered with the CoreI2C driver + through the I2C_register_write_handler() function. + + Declaring and Implementing Slave Write Handler Functions: + + Slave write handler functions should follow the following prototype: + @code + i2c_slave_handler_ret_t write_handler + ( + i2c_instance_t *instance, uint8_t * data, uint16_t size + ); + @endcode + + The instance parameter is a pointer to the i2c_instance_t for which this + slave write handler has been declared. + + The data parameter is a pointer to a buffer (received data buffer) holding + the data written to the I2C slave. + + Define the INCLUDE_SLA_IN_RX_PAYLOAD macro for the driver to insert the + actual address used to access the slave as the first byte in the buffer. + This allows the applications to tailor their response based on the actual + address used to access the slave (primary address, secondary address, or GCA). + + The size parameter is the number of bytes held in the received data buffer. + Handler functions must return one of the following values: + - I2C_REENABLE_SLAVE_RX + - I2C_PAUSE_SLAVE_RX + + If the handler function returns I2C_REENABLE_SLAVE_RX, the driver releases + the received data buffer and allows further I2C write transactions to the + I2C slave. + + If the handler function returns I2C_PAUSE_SLAVE_RX, the I2C slave responds + to subsequent write requests with a non-acknowledge bit (NACK), until the + received data buffer content gets processed by some other part of the + software application. + + Call the I2C_enable_slave() after returning the I2C_PAUSE_SLAVE_RX to release + the received data buffer in order to store the data received by the subsequent + I2C write transactions. + */ +typedef i2c_slave_handler_ret_t (*i2c_slave_wr_handler_t)(i2c_instance_t *instance, uint8_t *, uint16_t ); + +/***************************************************************************//** + This structure is used to identify the various CoreI2C hardware instances in + your system and the I2C channels within them. Your application software should + declare one instance of this structure for each channel of each instance of + CoreI2C in your system. The functions I2C_init() and I2C_channel_init() + initialize this structure depending on whether it is channel 0 or one of the + additional channels respectively. A pointer to an initialized instance of the + structure should be passed as the first parameter to the CoreI2C driver + functions, to identify which CoreI2C hardware instance and channel should + perform the requested operation. + + The contents of this data structure should not be modified or used outside of + the CoreI2C driver. Software using the CoreI2C driver should only need to + create one single instance of this data structure for each channel of each + CoreI2C hardware instance in the system then pass a pointer to these data + structures with each call to the CoreI2C driver in order to identify the + CoreI2C hardware instance it wishes to use. + */ +struct i2c_instance +{ + addr_t base_address; + uint_fast8_t ser_address; + + /* Transmit related info:*/ + uint_fast8_t target_addr; + + /* Current transaction type (WRITE, READ, RANDOM_READ)*/ + uint8_t transaction; + + uint_fast16_t random_read_addr; + + uint8_t options; + + /* Master TX INFO: */ + const uint8_t * master_tx_buffer; + uint_fast16_t master_tx_size; + uint_fast16_t master_tx_idx; + uint_fast8_t dir; + + /* Master RX INFO: */ + uint8_t * master_rx_buffer; + uint_fast16_t master_rx_size; + uint_fast16_t master_rx_idx; + + /* Master Status */ + volatile i2c_status_t master_status; + uint32_t master_timeout_ms; + + /* Slave TX INFO */ + const uint8_t * slave_tx_buffer; + uint_fast16_t slave_tx_size; + uint_fast16_t slave_tx_idx; + + /* Slave RX INFO */ + uint8_t * slave_rx_buffer; + uint_fast16_t slave_rx_size; + uint_fast16_t slave_rx_idx; + /* Slave Status */ + volatile i2c_status_t slave_status; + + /* Slave data: */ + uint_fast8_t slave_mem_offset_length; + i2c_slave_wr_handler_t slave_write_handler; + uint8_t is_slave_enabled; + + /* user specific data */ + void *p_user_data ; + + /* I2C bus status */ + uint8_t bus_status; + + /* Is transaction pending flag */ + uint8_t is_transaction_pending; + + /* I2C Pending transaction */ + uint8_t pending_transaction; +}; + +/*------------------------Public Function-------------------------------------*/ + +/***************************************************************************//** + The I2C_init() function configures channel 0 of a CoreI2C instance. It sets + the base hardware address which is used to locate the CoreI2C instance + in memory and also used internally by I2C_channel_init() to calculate the + register addresses for any additional channels. The slave serial address set + is shared by all channels on a CoreI2C instance. + + If only one channel is configured in a CoreI2C, the address of the + i2c_instance_t used in I2C_Init() will also be used in subsequent calls to the + CoreI2C driver functions. If more than one channel is configured in the + CoreI2C, I2C_channel_init() will be called after I2C_init(), which initializes + the i2c_instance_t data structure for a specific channel. + + @param this_i2c + Pointer to the i2c_instance_t data structure that holds all the data + related to channel 0 of the CoreI2C instance is initialized. A pointer + to this structure is used in all subsequent calls to the CoreI2C driver + functions which operates on channel 0 of this CoreI2C instance. + + @param base_address + Base address in the processor's memory map of the registers of the CoreI2C + instance being initialized. + + @param ser_address + This parameter sets the primary I2C serial address (SLAVE0 address) for the + CoreI2C to initialize. It is the principal I2C bus address to which the + CoreI2C instance will respond. CoreI2C can operate in master mode or slave + mode and the serial address is significant only in the case of I2C slave + mode. In master mode, CoreI2C does not require a serial address and the + value of this parameter is not important. If you do not intend to use the + CoreI2C device in slave mode, then provide any dummy slave address value + to this parameter. However, in systems where the CoreI2C is expected to + switch from master mode to slave mode, it is advisable to initialize the + CoreI2C device with a valid serial slave address. Call the I2C_init() + function whenever it is required to change the primary slave address as + there is no separate function to set the primary slave address of the + I2C device. The serial address initialized through this function is + basically the primary slave address or slave address0. + I2C_set_slave_second_addr() is used to set the secondary slave address + or slave address 1. + Note : ser_address parameter does not have any affect if fixed slave + address is enabled in CoreI2C hardware design. CoreI2C will + be always addressed with the hardware configured fixed slave + address. + Note : ser_address parameter will not have any affect if the CoreI2C + instance is only used in master mode. + + @param ser_clock_speed + This parameter sets the I2C serial clock frequency. It selects the divider + that generates the serial clock from the APB PCLK or from the BCLK. + It can be one of the following: + - I2C_PCLK_DIV_256 + - I2C_PCLK_DIV_224 + - I2C_PCLK_DIV_192 + - I2C_PCLK_DIV_160 + - I2C_PCLK_DIV_960 + - I2C_PCLK_DIV_120 + - I2C_PCLK_DIV_60 + - I2C_BCLK_DIV_8 + Note: serial_clock_speed value does not have any affect if the fixed baud + rate is enabled in CoreI2C hardware instance configuration dialogue + window. The fixed baud rate divider value overrides the value passed + as parameter in this function. + Note: serial_clock_speed value is not critical for devices that only operate + as slaves and can be set to any of the above values. + + @return None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define COREI2C_SER_ADDR 0x10u + + i2c_instance_t g_i2c_inst; + + void system_init( void ) + { + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_SER_ADDR, + I2C_PCLK_DIV_256 ); + } + @endcode + */ +void I2C_init +( + i2c_instance_t * this_i2c, + addr_t base_address, + uint8_t ser_address, + i2c_clock_divider_t ser_clock_speed +); + +/***************************************************************************//** + The I2C_channel_init() function initializes and configures hardware and data + structures of one of the additional channels of a CoreI2C instance. + I2C_init() must be called before calling this function to set the CoreI2C + instance hardware base address and I2C serial address. I2C_channel_init() also + initializes I2C serial clock divider to set the serial clock baud rate. + The pointer to data structure i2c_instance_t used for a particular channel + is used as an input parameter to subsequent CoreI2C driver functions + which operate on this channel. + + @param this_i2c_channel + Pointer to the i2c_instance_t data structure that holds all data related to + the CoreI2C channel gets initialized. A pointer to the same data structure + is used in subsequent calls to the CoreI2C driver functions in order to + identify the CoreI2C channel instance that should perform the operation + implemented by the called driver function. + + @param this_i2c + This is a pointer to an i2c_instance_t structure, previously initialized by + I2C_init(). It holds information regarding the hardware base address and + I2C serial address for the CoreI2C containing the channel to be + initialized. This information is required by I2C_channel_init() to + initialize the i2c_instance_t structure pointed by this_i2c_channel as + all channels in a CoreI2C instance share the same base address and serial + address. It is very important that the i2c_instance_t structure pointed + by this_i2c must be previously initialized by calling I2C_init(). + + @param channel_number + This parameter of type i2c_channel_number_t identifies the channel to be + initialized. + + @param ser_clock_speed + This parameter sets the I2C serial clock frequency. It selects the divider + that is used to generate the serial clock from the APB PCLK or from + the BCLK. It can be one of the following: + - I2C_PCLK_DIV_256 + - I2C_PCLK_DIV_224 + - I2C_PCLK_DIV_192 + - I2C_PCLK_DIV_160 + - I2C_PCLK_DIV_960 + - I2C_PCLK_DIV_120 + - I2C_PCLK_DIV_60 + - I2C_BCLK_DIV_8 + Note: serial_clock_speed value does not have any affect if the fixed baud + rate is enabled in CoreI2C hardware instance configuration dialogue + window. The fixed baud rate divider value will supersede the value + passed as parameter in this function. + Note: ser_clock_speed value is not critical for devices that only operate + as slaves and can be set to any of the above values. + @return None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define COREI2C_SER_ADDR 0x10u + #define DATA_LENGTH 16u + + i2c_instance_t g_i2c_inst; + i2c_instance_t g_i2c_channel_1_inst; + + uint8_t tx_buffer[DATA_LENGTH]; + uint8_t write_length = DATA_LENGTH; + + void system_init( void ) + { + uint8_t target_slave_addr = 0x12; + + // Initialize base CoreI2C instance + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_SER_ADDR, + I2C_PCLK_DIV_256 ); + + // Initialize CoreI2C channel 1 with different clock speed + I2C_channel_init( &g_i2c_channel_1_inst, &g_i2c_inst, I2C_CHANNEL_1, + I2C_PCLK_DIV_224 ); + + // Write data to Channel 1 of CoreI2C instance. + I2C_write( &g_i2c_channel_1_inst, target_slave_addr, tx_buffer, + write_length, I2C_RELEASE_BUS ); + } + @endcode + +*/ +void I2C_channel_init +( + i2c_instance_t * this_i2c_channel, + i2c_instance_t * this_i2c, + i2c_channel_number_t channel_number, + i2c_clock_divider_t ser_clock_speed +); + +/***************************************************************************//** + The I2C_isr function is the CoreI2C interrupt service routine. User must + call this function from their application level CoreI2C interrupt handler + function. This function runs the I2C state machine based on previous and + current status. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @return None. + + @example + @code + + #define COREI2C_BASE_ADDR 0xC0000000u + #define COREINTERRUPT_BASE_ADDR 0xCC000000u + #define COREI2C_SER_ADDR 0x10u + #define I2C_IRQ_NB 2u + + i2c_instance_t g_i2c_inst; + + void core_i2c_isr( void ) + { + I2C_isr( &g_i2c_inst ); + } + + void main( void ) + { + CIC_init( COREINTERRUPT_BASE_ADDR ); + NVIC_init(); + CIC_set_irq_handler( I2C_IRQ_NB, core_i2c_isr ); + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_SER_ADDR, + I2C_PCLK_DIV_256 ); + NVIC_enable_interrupt( NVIC_IRQ_0 ); + } + @endcode + */ +void I2C_isr +( + i2c_instance_t * this_i2c +); + +/***************************************************************************//* + #Master Specific Functions + + The following functions are only used within an I2C master's implementation. +/***************************************************************************//** + This function initiates an I2C master write transaction. This function returns + immediately after initiating the transaction. The content of the write buffer + passed as parameter should not be modified until the write transaction + completes. It also means that the memory allocated for the write buffer should + not be freed or should not go out of scope before the write completes. You can + check for the write transaction completion using the I2C_status() function. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @param serial_addr + This parameter specifies the serial address of the target I2C device. + + @param write_buffer + This parameter is a pointer to a buffer holding the data to be written to + the target I2C device. Do not to release the memory used by this buffer + before the write transaction completes. For example, it is not appropriate + to return from a function allocating this buffer as an auto array variable + before the write transaction completes as this would result in the buffer's + memory being de-allocated from the stack when the function returns. This + memory could then be subsequently reused and modified causing unexpected + data to be written to the target I2C device. + + @param write_size + Number of bytes held in the write_buffer to be written to the target I2C + device. + + @param options + The options parameter is used to indicate if the I2C bus should be released + on completion of the write transaction. Using the I2C_RELEASE_BUS + constant for the options parameter causes a STOP bit to be generated at the + end of the write transaction causing the bus to be released for other I2C + devices to use. Using the I2C_HOLD_BUS constant as options parameter + prevents a STOP bit from being generated at the end of the write + transaction, preventing other I2C devices from initiating a bus transaction. + + @return None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define COREI2C_DUMMY_ADDR 0x10u + #define DATA_LENGTH 16u + + i2c_instance_t g_i2c_inst; + + uint8_t tx_buffer[DATA_LENGTH]; + uint8_t write_length = DATA_LENGTH; + + void main( void ) + { + uint8_t target_slave_addr = 0x12; + i2c_status_t status; + + // Initialize base CoreI2C instance + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_DUMMY_ADDR, + I2C_PCLK_DIV_256 ); + + // Write data to Channel 0 of CoreI2C instance. + I2C_write( &g_i2c_inst, target_slave_addr, tx_buffer, write_length, + I2C_RELEASE_BUS ); + + // Wait for completion and record the outcome + status = I2C_wait_complete( &g_i2c_inst, I2C_NO_TIMEOUT ); + } + @endcode + */ +void I2C_write +( + i2c_instance_t * this_i2c, + uint8_t serial_addr, + const uint8_t * write_buffer, + uint16_t write_size, + uint8_t options +); + +/***************************************************************************//** + This function initiates an I2C master read transaction. This function returns + immediately after initiating the transaction. + The contents of the read buffer passed as parameter should not be modified + until the read transaction completes. It also means that the memory allocated + for the read buffer should not be freed or should not go out of scope before + the read completes. You can check for the read transaction completion using + the I2C_status() function. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @param serial_addr + This parameter specifies the serial address of the target I2C device. + + @param read_buffer + This is a pointer to a buffer where the data received from the target device + gets stored. Do not to release the memory used by this buffer before the read + transaction completes. For example, it is not appropriate to return from a + function allocating this buffer as an auto array variable before the read + transaction completes as this would result in the buffer's memory being + de-allocated from the stack when the function returns. This memory could + then be subsequently reallocated resulting in the read transaction + corrupting the newly allocated memory. + + @param read_size + This parameter specifies the number of bytes to read from the target device. + This size must not exceed the size of the read_buffer buffer. + + @param options + The options parameter is used to indicate if the I2C bus should be released + on completion of the read transaction. Using the I2C_RELEASE_BUS + constant for the options parameter causes a STOP bit to be generated at the + end of the read transaction causing the bus to be released for other I2C + devices to use. Using the I2C_HOLD_BUS constant as options parameter + prevents a STOP bit from being generated at the end of the read transaction, + preventing other I2C devices from initiating a bus transaction. + + @return None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define COREI2C_DUMMY_ADDR 0x10u + #define DATA_LENGTH 16u + + i2c_instance_t g_i2c_inst; + + uint8_t rx_buffer[DATA_LENGTH]; + uint8_t read_length = DATA_LENGTH; + + void main( void ) + { + uint8_t target_slave_addr = 0x12; + i2c_status_t status; + + // Initialize base CoreI2C instance + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_DUMMY_ADDR, + I2C_PCLK_DIV_256 ); + + // Read data from target slave Channel 0 of CoreI2C instance. + I2C_read( &g_i2c_inst, target_slave_addr, rx_buffer, read_length, + I2C_RELEASE_BUS ); + + status = I2C_wait_complete( &g_i2c_inst, I2C_NO_TIMEOUT ); + } + @endcode + */ +void I2C_read +( + i2c_instance_t * this_i2c, + uint8_t serial_addr, + uint8_t * read_buffer, + uint16_t read_size, + uint8_t options +); + +/***************************************************************************//** + This function initiates an I2C write-read transaction where data is first + written to the target device before issuing a restart condition and changing + the direction of the I2C transaction in order to read from the target device. + + The same warnings about buffer allocation in I2C_write() and I2C_read() + applies to this function. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @param serial_addr + This parameter specifies the serial address of the target I2C device. + + @param addr_offset + This parameter is a pointer to the buffer containing the data that is sent + to the slave during the write phase of the write-read transaction. This + data is typically used to specify an address offset specifying to the I2C + slave device what data it must return during the read phase of the + write-read transaction. + + @param offset_size + This parameter specifies the number of offset bytes to be written during the + write phase of the write-read transaction. This is typically the size of the + buffer pointed by the addr_offset parameter. + + @param read_buffer + This parameter is a pointer to the buffer where the data read from the I2C + slave will be stored. + + @param read_size + This parameter specifies the number of bytes to read from the target I2C + slave device. This size must not exceed the size of the buffer pointed by + the read_buffer parameter. + + @param options + The options parameter is used to indicate if the I2C bus should be released + on completion of the write-read transaction. Using the I2C_RELEASE_BUS + constant for the options parameter causes a STOP bit to be generated at the + end of the write-read transaction causing the bus to be released for other + I2C devices to use. Using the I2C_HOLD_BUS constant as options parameter + prevents a STOP bit from being generated at the end of the write-read + transaction, preventing other I2C devices from initiating a bus transaction. + + @return None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define COREI2C_DUMMY_ADDR 0x10u + #define TX_LENGTH 16u + #define RX_LENGTH 8u + + i2c_instance_t g_i2c_inst; + uint8_t rx_buffer[RX_LENGTH]; + uint8_t read_length = RX_LENGTH; + uint8_t tx_buffer[TX_LENGTH]; + uint8_t write_length = TX_LENGTH; + + void main( void ) + { + uint8_t target_slave_addr = 0x12; + i2c_status_t status; + // Initialize base CoreI2C instance + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_DUMMY_ADDR, + I2C_PCLK_DIV_256 ); + + I2C_write_read( &g_i2c_inst, target_slave_addr, tx_buffer, write_length, + rx_buffer, read_length, I2C_RELEASE_BUS ); + + status = I2C_wait_complete( &g_i2c_inst, I2C_NO_TIMEOUT ); + } + @endcode + */ +void I2C_write_read +( + i2c_instance_t * this_i2c, + uint8_t serial_addr, + const uint8_t * addr_offset, + uint16_t offset_size, + uint8_t * read_buffer, + uint16_t read_size, + uint8_t options +); + +/***************************************************************************//** + This function indicates the current state of a CoreI2C channel. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @return + The return value indicates the current state of a CoreI2C channel or the + outcome of the previous transaction if no transaction is in progress. + Following are the return values are: + - I2C_SUCCESS + The last I2C transaction has completed successfully. + - I2C_IN_PROGRESS + There is an I2C transaction in progress. + - I2C_FAILED + The last I2C transaction failed. + - I2C_TIMED_OUT + The request has failed to complete in the allotted time. + + @example + @code + i2c_instance_t g_i2c_inst; + + while( I2C_IN_PROGRESS == I2C_get_status( &g_i2c_inst ) ) + { + // Do something useful while waiting for I2C operation to complete + our_i2c_busy_task(); + } + + if( I2C_SUCCESS != I2C_get_status( &g_i2c_inst ) ) + { + // Something went wrong... + our_i2c_error_recovery( &g_i2c_inst ); + } + @endcode + */ +i2c_status_t I2C_get_status +( + i2c_instance_t * this_i2c +); + +/***************************************************************************//** + This function waits for the current I2C transaction to complete. The return + value indicates whether the last I2C transaction was successful or not. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + @param timeout_ms + The timeout_ms parameter specifies the delay within which the current I2C + transaction should complete. The time out delay is given in + milliseconds. I2C_wait_complete() will return I2C_TIMED_OUT if the current + transaction has not completed after the time out delay has expired. This + parameter can be set to I2C_NO_TIMEOUT to indicate that I2C_wait_complete() + must not time out. + + @return + The return value indicates the outcome of the last I2C transaction. It can + be one of the following: + - I2C_SUCCESS + The last I2C transaction has completed successfully. + - I2C_FAILED + The last I2C transaction failed. + - I2C_TIMED_OUT + The last transaction failed to complete within the time out delay given + as second parameter. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define COREI2C_DUMMY_ADDR 0x10u + #define DATA_LENGTH 16u + + i2c_instance_t g_i2c_inst; + + uint8_t rx_buffer[DATA_LENGTH]; + uint8_t read_length = DATA_LENGTH; + + void main( void ) + { + uint8_t target_slave_addr = 0x12; + i2c_status_t status; + + // Initialize base CoreI2C instance + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_DUMMY_ADDR, + I2C_PCLK_DIV_256 ); + + // Read data from Channel 0 of CoreI2C instance. + I2C_read( &g_i2c_inst, target_slave_addr, rx_buffer, read_length, + I2C_RELEASE_BUS ); + + // Wait for completion and record the outcome + status = I2C_wait_complete( &g_i2c_inst, I2C_NO_TIMEOUT ); + } + @endcode + */ +i2c_status_t I2C_wait_complete +( + i2c_instance_t * this_i2c, + uint32_t timeout_ms +); + +/***************************************************************************//** + This function is used to control the expiration of the time out delay + specified as a parameter to the I2C_wait_complete() function. It must be + called from the interrupt service routine of a periodic interrupt source such + as the SysTick timer interrupt. It takes the period of the interrupt + source as its ms_since_last_tick parameter and uses it as the time base for + the I2C_wait_complete() function's time out delay. + + Note: This function does not need to be called if the I2C_wait_complete() + function is called with a timeout_ms value of I2C_NO_TIMEOUT. + + Note: If this function is not called then the I2C_wait_complete() function + will behave as if its timeout_ms was specified as I2C_NO_TIMEOUT and it + will not time out. + + Note: If this function is being called from an interrupt handler (for example, + SysTick) it is important that the calling interrupt have a lower priority + than the CoreI2C interrupt(s) to ensure any updates to the shared data are + protected. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + @param ms_since_last_tick + The ms_since_last_tick parameter specifies the number of milliseconds that + elapsed since the last call to I2C_system_tick(). This parameter would + typically be a constant specifying the interrupt rate of a timer used to + generate system ticks. + + @return None. + + @example + The following example shows how the I2C_system_tick() function. I2C_system_tick() + is called for each I2C channel from the SysTick timer interrupt service routine. + The following example shows how the SysTick is configured to generate an interrupt + in every 10 milliseconds. + @code + #define SYSTICK_INTERVAL_MS 10 + + void SysTick_Handler(void) + { + I2C_system_tick(&g_core_i2c0, SYSTICK_INTERVAL_MS); + I2C_system_tick(&g_core_i2c2, SYSTICK_INTERVAL_MS); + } + @endcode + */ +void I2C_system_tick +( + i2c_instance_t * this_i2c, + uint32_t ms_since_last_tick +); + +/***************************************************************************//* + #Slave Specific Functions + + The following functions are only used within the implementation of an I2C slave device. + +/***************************************************************************//** + This function specifies the memory buffer holding the data that will be sent + to the I2C master when this CoreI2C channel is the target of an I2C read or + write-read transaction. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @param tx_buffer + This parameter is a pointer to the memory buffer holding the data to be + returned to the I2C master when this CoreI2C channel is the target of an + I2C read or write-read transaction. + + @param tx_size + Size of the transmit buffer pointed by the tx_buffer parameter. + + @return None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define SLAVE_SER_ADDR 0x10u + #define SLAVE_TX_BUFFER_SIZE 10u + + i2c_instance_t g_i2c_inst; + + uint8_t g_slave_tx_buffer[SLAVE_TX_BUFFER_SIZE] = { 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10 }; + + void main( void ) + { + // Initialize the CoreI2C driver with its base address, I2C serial + // address and serial clock divider. + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, + I2C_PCLK_DIV_256 ); + + // Specify the transmit buffer containing the data that will be + // returned to the master during read and write-read transactions. + I2C_set_slave_tx_buffer( &g_i2c_inst, g_slave_tx_buffer, + sizeof(g_slave_tx_buffer) ); + } + @endcode + */ +void I2C_set_slave_tx_buffer +( + i2c_instance_t * this_i2c, + const uint8_t * tx_buffer, + uint16_t tx_size +); + +/***************************************************************************//** + This function specifies the memory buffer that is used by the CoreI2C channel + to receive data when it is a slave. This buffer is the memory where data gets + stored when the CoreI2C channel is the target of an I2C master write + transaction (that is, when it is the slave). + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @param rx_buffer + This parameter is a pointer to the memory buffer allocated by the caller + software to be used as a slave receive buffer. + + @param rx_size + Size of the slave receive buffer. This is the amount of memory allocated + to the buffer pointed by rx_buffer. + Note: Indirectly, this buffer size specifies the maximum I2C write + transaction length this CoreI2C channel targets. This is because + this CoreI2C channel responds to further received bytes with + a non-acknowledge bit (NACK) as soon as its receive buffer is + full. This causes the write transaction to fail. + + @return None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define SLAVE_SER_ADDR 0x10u + #define SLAVE_RX_BUFFER_SIZE 10u + + i2c_instance_t g_i2c_inst; + + uint8_t g_slave_rx_buffer[SLAVE_RX_BUFFER_SIZE]; + + void main( void ) + { + // Initialize the CoreI2C driver with its base address, I2C serial + // address and serial clock divider. + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, + I2C_PCLK_DIV_256 ); + + // Specify the buffer used to store the data written by the I2C master. + I2C_set_slave_rx_buffer( &g_i2c_inst, g_slave_rx_buffer, + sizeof(g_slave_rx_buffer) ); + } + @endcode + */ +void I2C_set_slave_rx_buffer +( + i2c_instance_t * this_i2c, + uint8_t * rx_buffer, + uint16_t rx_size +); + +/***************************************************************************//** + This function is used as part of the configuration of a CoreI2C channel to + operate as a slave supporting write-read transactions. It specifies the + number of bytes expected as part of the write phase of a write-read + transaction. The bytes received during the write phase of a write-read + transaction will be interpreted as an offset into the slave's transmit buffer. + This allows random access into the I2C slave transmit buffer from a remote + I2C master. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @param offset_length + The offset_length parameter configures the number of bytes to be interpreted + by the CoreI2C slave as a memory offset value during the write phase of + write-read transactions. The maximum value for the offset_length parameter + is two. The value of offset_length has the following effect on the + interpretation of the received data. + • If offset_length is 0, the offset into the transmit buffer is fixed at 0. + • If offset_length is 1, a single byte of received data is interpreted as an + unsigned 8-bit offset value in the range 0 to 255. + • If offset_length is 2, 2 bytes of received data are interpreted as an + unsigned 16-bit offset value in the range 0 to 65535. The first byte + received in this case provides the high order bits of the offset and + the second byte provides the low order bits. + If the number of bytes received does not match the non 0 value of + offset_length, the transmit buffer offset is set to 0. + + @return None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define SLAVE_SER_ADDR 0x10u + #define SLAVE_TX_BUFFER_SIZE 10u + + i2c_instance_t g_i2c_inst; + + uint8_t g_slave_tx_buffer[SLAVE_TX_BUFFER_SIZE] = { 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10 }; + + void main( void ) + { + // Initialize the CoreI2C driver with its base address, I2C serial + // address and serial clock divider. + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, + I2C_PCLK_DIV_256 ); + I2C_set_slave_tx_buffer( &g_i2c_inst, g_slave_tx_buffer, + sizeof(g_slave_tx_buffer) ); + I2C_set_slave_mem_offset_length( &g_i2c_inst, 1 ); + } + @endcode + */ +void I2C_set_slave_mem_offset_length +( + i2c_instance_t * this_i2c, + uint8_t offset_length +); + +/***************************************************************************//** + Register the function that is called to process the data written to this + CoreI2C channel when it is the slave in an I2C write transaction. + + Note: If a write handler is registered, it is called on completion of the + write phase of a write-read transaction and responsible for processing + the received data in the slave receive buffer and populating the slave + transmit buffer with the data that is transmitted to the I2C master + as part of the read phase of the write-read transaction. If a write + handler is not registered, the write data of a write-read transaction is + interpreted as an offset into the slave’s transmit buffer and handled by + the driver. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @param handler + Pointer to the function that processes the I2C write request. + + @return None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define SLAVE_SER_ADDR 0x10u + #define SLAVE_TX_BUFFER_SIZE 10u + + i2c_instance_t g_i2c_inst; + + uint8_t g_slave_tx_buffer[SLAVE_TX_BUFFER_SIZE] = { 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10 }; + + // local function prototype + void slave_write_handler + ( + i2c_instance_t * this_i2c, + uint8_t * p_rx_data, + uint16_t rx_size + ); + + void main( void ) + { + // Initialize the CoreI2C driver with its base address, I2C serial + // address and serial clock divider. + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, + I2C_PCLK_DIV_256 ); + I2C_set_slave_tx_buffer( &g_i2c_inst, g_slave_tx_buffer, + sizeof(g_slave_tx_buffer) ); + I2C_set_slave_mem_offset_length( &g_i2c_inst, 1 ); + I2C_register_write_handler( &g_i2c_inst, slave_write_handler ); + } + @endcode +*/ +void I2C_register_write_handler +( + i2c_instance_t * this_i2c, + i2c_slave_wr_handler_t handler +); + +/***************************************************************************//** + This function enables slave mode operation for a CoreI2C channel. It enables + the CoreI2C slave to receive data when it is the target of an I2C read, write, + or write-read transaction. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @return None. + + @example + @code + // Enable I2C slave + I2C_enable_slave( &g_i2c_inst ); + @endcode + */ +void I2C_enable_slave +( + i2c_instance_t * this_i2c +); + +/***************************************************************************//** + This function disables the slave mode operation for a CoreI2C channel. It stops + the CoreI2C slave that acknowledges the I2C read, write, or write-read + transactions targeted at it. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @return None. + + @example + @code + // Disable I2C slave + I2C_disable_slave( &g_i2c_inst ); + @endcode + */ +void I2C_disable_slave +( + i2c_instance_t * this_i2c +); + +/***************************************************************************//** + The I2C_set_slave_second_addr() function sets the secondary slave address for + a CoreI2C slave device. This is an additional slave address required in certain + applications, for example, to enable fail-safe operation in a system. As the + CoreI2C device supports 7-bit addressing, the highest value assigned to second + slave address is 127 (0x7F). + + Note: This function does not support CoreI2C hardware configured with a fixed + second slave address. The current implementation of the ADDR1[0] register + bit makes it difficult for the driver to support both programmable and + fixed second slave address, so we choose to support programmable only. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @param second_slave_addr + The second_slave_addr parameter is the secondary slave address of the I2C + device. + + @return None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define SLAVE_SER_ADDR 0x10u + #define SECOND_SLAVE_ADDR 0x20u + + i2c_instance_t g_i2c_inst; + void main( void ) + { + // Initialize the CoreI2C driver with its base address, primary I2C + // serial address and serial clock divider. + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, + I2C_PCLK_DIV_256 ); + I2C_set_slave_second_addr( &g_i2c_inst, SECOND_SLAVE_ADDR ); + } + @endcode + */ +void I2C_set_slave_second_addr +( + i2c_instance_t * this_i2c, + uint8_t second_slave_addr +); + +/***************************************************************************//** + The I2C_disable_slave_second_addr() function disables the secondary slave + address of the CoreI2C slave device. + + Note: This version of the driver only supports CoreI2C hardware configured + with a programmable second slave address. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @return None. + + @example + @code + i2c_instance_t g_i2c_inst; + I2C_disable_slave_second_addr( &g_i2c_inst); + @endcode + */ +void I2C_disable_slave_second_addr +( + i2c_instance_t * this_i2c +); + +/***************************************************************************//** + The I2C_set_gca() function is used to set the general call acknowledgement bit + of a CoreI2C slave device. This allows all channels of the CoreI2C slave + device to respond to a general call or broadcast message from an I2C master. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @return None. + + @example + @code + i2c_instance_t g_i2c_inst; + + // Enable recognition of the General Call Address + I2C_set_gca( &g_i2c_inst ); + @endcode + */ +void I2C_set_gca +( + i2c_instance_t * this_i2c +); + +/***************************************************************************//** + The I2C_clear_gca() function is used to clear the general call acknowledgement + bit of a CoreI2C slave device. This will stop all channels of the I2C slave + device responding to any general call or broadcast message from the master. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @return None. + + @example + @code + i2c_instance_t g_i2c_inst; + + // Disable recognition of the General Call Address + I2C_clear_gca( &g_i2c_inst ); + @endcode + */ + +void I2C_clear_gca +( + i2c_instance_t * this_i2c +); + +/***************************************************************************//* + #I2C SMBUS Specific APIs + +/***************************************************************************//** + The I2C_smbus_init() function enables SMBus timeouts and status logic for a + CoreI2C channel. + + Note: This and any of the other SMBus related functionality will only have an + effect if the CoreI2C was instantiated with the Generate SMBus Logic + option checked. + + Note: If the CoreI2C was instantiated with the Generate IPMI Logic option + checked this function then enables the IPMI 3mS SCL low timeout but + none of the other SMBus functions will have any effect. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @return None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define SLAVE_SER_ADDR 0x10u + + i2c_instance_t g_i2c_inst; + + void system_init( void ) + { + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, + I2C_PCLK_DIV_256 ); + + // Initialize SMBus feature + I2C_smbus_init( &g_i2c_inst); + } + @endcode + */ +void I2C_smbus_init +( + i2c_instance_t * this_i2c +); + +/***************************************************************************//** + The I2C_enable_smbus_irq() function is used to enable the CoreI2C channel’s + SMBSUS and SMBALERT SMBus interrupts. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @param irq_type + The irq_type specify the SMBUS interrupt(s) which will be enabled. + The two possible interrupts are: + - I2C_SMBALERT_IRQ + - I2C_SMBSUS_IRQ + To enable both interrupts in one call, use I2C_SMBALERT_IRQ | I2C_SMBSUS_IRQ. + + @return + None + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define SLAVE_SER_ADDR 0x10u + + i2c_instance_t g_i2c_inst; + + void main( void ) + { + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, + I2C_PCLK_DIV_256 ); + + // Initialize SMBus feature + I2C_smbus_init( &g_i2c_inst ); + + // Enable both I2C_SMBALERT_IRQ & I2C_SMBSUS_IRQ interrupts + I2C_enable_smbus_irq( &g_i2c_inst, + (uint8_t)(I2C_SMBALERT_IRQ | I2C_SMBSUS_IRQ) ); + } + @endcode + */ +void I2C_enable_smbus_irq +( + i2c_instance_t * this_i2c, + uint8_t irq_type +); + +/***************************************************************************//** + The I2C_disable_smbus_irq() function disable the CoreI2C channel’s SMBSUS and + SMBALERT SMBus interrupts. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @param irq_type + The irq_type specifies the SMBUS interrupt(s) which are disabled. + The two possible interrupts are: + • I2C_SMBALERT_IRQ + • I2C_SMBSUS_IRQ + To disable both ints in one call, use I2C_SMBALERT_IRQ | I2C_SMBSUS_IRQ. + + @return + None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define SLAVE_SER_ADDR 0x10u + + i2c_instance_t g_i2c_inst; + + void main( void ) + { + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, + I2C_PCLK_DIV_256 ); + + // Initialize SMBus feature + I2C_smbus_init( &g_i2c_inst ); + + // Enable both SMBALERT & SMBSUS interrupts + I2C_enable_smbus_irq( &g_i2c_inst, + (uint8_t)(I2C_SMBALERT_IRQ | I2C_SMBSUS_IRQ)); + + ... + + // Disable the SMBALERT interrupt + I2C_disable_smbus_irq( &g_i2c_inst, I2C_SMBALERT_IRQ ); + } + @endcode + */ +void I2C_disable_smbus_irq +( + i2c_instance_t * this_i2c, + uint8_t irq_type +); + +/***************************************************************************//** + The I2C_suspend_smbus_slave() function forces any SMBUS slave devices + connected to a CoreI2C channel into Power-Down or Suspend mode by asserting + the channel's SMBSUS signal. The CoreI2C channel is the SMBus master in this + case. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @return + None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define SLAVE_SER_ADDR 0x10u + + i2c_instance_t g_i2c_inst; + + void main( void ) + { + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, + I2C_PCLK_DIV_256 ); + + // Initialize SMBus feature + I2C_smbus_init( &g_i2c_inst ); + + // suspend SMBus slaves + I2C_suspend_smbus_slave( &g_i2c_inst ); + + ... + + // Re-enable SMBus slaves + I2C_resume_smbus_slave( &g_i2c_inst ); + } + @endcode + */ +void I2C_suspend_smbus_slave +( + i2c_instance_t * this_i2c +); + +/***************************************************************************//** + The I2C_resume_smbus_slave() function de-asserts the CoreI2C channel's SMBSUS + signal to take any connected slave devices out of the Suspend mode. The CoreI2C + channel is the SMBus master in this case. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @return + None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define SLAVE_SER_ADDR 0x10u + + i2c_instance_t g_i2c_inst; + + void main( void ) + { + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, + I2C_PCLK_DIV_256 ); + + // Initialize SMBus feature + I2C_smbus_init( &g_i2c_inst ); + + // suspend SMBus slaves + I2C_suspend_smbus_slave( &g_i2c_inst ); + + ... + + // Re-enable SMBus slaves + I2C_resume_smbus_slave( &g_i2c_inst ); + } + @endcode + */ +void I2C_resume_smbus_slave +( + i2c_instance_t * this_i2c +); + +/***************************************************************************//** + The I2C_reset_smbus() function resets the CoreI2C channel's SMBus connection + by forcing SCLK low for 35 mS. The reset that automatically cleares after 35 ms + gets elapsed. The CoreI2C channel is the SMBus master in this case. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @return + None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define SLAVE_SER_ADDR 0x10u + + i2c_instance_t g_i2c_inst; + + void main( void ) + { + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, + I2C_PCLK_DIV_256 ); + + // Initialize SMBus feature + I2C_smbus_init( &g_i2c_inst ); + + // Make sure the SMBus channel is in a known state by resetting it + I2C_reset_smbus( &g_i2c_inst ); + } + @endcode + */ +void I2C_reset_smbus +( + i2c_instance_t * this_i2c +); + +/***************************************************************************//** + The I2C_set_smbus_alert() function is used to force master communication with + an I2C slave device by asserting the CoreI2C channel's SMBALERT signal. The + CoreI2C channel is the SMBus slave in this case. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @return + None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define SLAVE_SER_ADDR 0x10u + + i2c_instance_t g_i2c_inst; + + void main( void ) + { + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, + I2C_PCLK_DIV_256 ); + + // Initialize SMBus feature + I2C_smbus_init( &g_i2c_inst ); + + // Get the SMBus masters attention + I2C_set_smbus_alert( &g_i2c_inst ); + + ... + + // Once we are happy, drop the alert + I2C_clear_smbus_alert( &g_i2c_inst ); + } + @endcode + */ +void I2C_set_smbus_alert +( + i2c_instance_t * this_i2c +); + +/***************************************************************************//** + The I2C_clear_smbus_alert() function is used to de-assert the CoreI2C channel's + SMBALERT signal once a slave device gets a response from the master. The + CoreI2C channel is the SMBus slave in this case. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @return + None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define SLAVE_SER_ADDR 0x10u + + i2c_instance_t g_i2c_inst; + + void main( void ) + { + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, + I2C_PCLK_DIV_256 ); + + // Initialize SMBus feature + I2C_smbus_init( &g_i2c_inst ); + + // Get the SMBus masters attention + I2C_set_smbus_alert( &g_i2c_inst ); + + ... + + // Once we are happy, drop the alert + I2C_clear_smbus_alert( &g_i2c_inst ); + } + @endcode + */ +void I2C_clear_smbus_alert +( + i2c_instance_t * this_i2c +); + +/***************************************************************************//** + The I2C_get_irq_status function returns information about which interrupts are + currently pending in a CoreI2C channel. + The interrupts supported by CoreI2C are: + • SMBUSALERT + • SMBSUS + • INTR + + The macros I2C_NO_IRQ, I2C_SMBALERT_IRQ, I2C_SMBSUS_IRQ, and I2C_INTR_IRQ are + provided to use with this function. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @return + This function returns the status of the CoreI2C channel's interrupts as a + single byte bitmap where a bit is set to indicate a pending interrupt. + The following are the bit positions associated with each interrupt type: + Bit 0 - SMBUS_ALERT_IRQ + Bit 1 - SMBSUS_IRQ + Bit 2 - INTR_IRQ + It returns 0, if there are no pending interrupts. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define SLAVE_SER_ADDR 0x10u + + i2c_instance_t g_i2c_inst; + + void main( void ) + { + uint8_t irq_to_enable = I2C_SMBALERT_IRQ | I2C_SMBSUS_IRQ; + uint8_t pending_irq = 0u; + + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, + I2C_PCLK_DIV_256 ); + + // Initialize SMBus feature + I2C_smbus_init( &g_i2c_inst ); + + // Enable both I2C_SMBALERT_IRQ & I2C_SMBSUS_IRQ irq + I2C_enable_smbus_irq( &g_i2c_inst, irq_to_enable ); + + // Get I2C IRQ type + pending_irq = I2C_get_irq_status( &g_i2c_inst ); + + // Let's assume, in system, INTR and SMBALERT IRQ is pending. + // So pending_irq will return status of both the IRQs + + if( pending_irq & I2C_SMBALERT_IRQ ) + { + // if true, it means SMBALERT_IRQ is there in pending IRQ list + } + if( pending_irq & I2C_INTR_IRQ ) + { + // if true, it means I2C_INTR_IRQ is there in pending IRQ list + } + } + @endcode + */ +uint8_t I2C_get_irq_status +( + i2c_instance_t * this_i2c +); + +/***************************************************************************//** + The I2C_set_user_data() function allows the association of a block of application + specific data with a CoreI2C channel. The composition of the data block is an + application matter and the driver simply provides the means for the application + to set and retrieve the pointer. For example, this is used to provide additional + channel specific information to the slave write handler. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @param p_user_data + The p_user_data parameter is a pointer to the user specific data block for + this channel. It is defined as void * as the driver does not know the actual + type of data being pointed to and simply stores the pointer for later + retrieval by the application. + + @return + None. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define SLAVE_SER_ADDR 0x10u + + i2c_instance_t g_i2c_inst; + app_data_t channel_xdata; + + void main( void ) + { + app_data_t *p_xdata; + + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, + I2C_PCLK_DIV_256 ); + + // Store location of user data in instance structure + I2C_set_user_data( &g_i2c_inst, (void *)&channel_xdata ); + + ... + + // Retrieve location of user data and do some work on it + p_xdata = (app_data_t *)I2C_get_user_data( &g_i2c_inst ); + if( NULL != p_xdata ) + { + p_xdata->foo = 123; + } + } + @endcode + */ +void I2C_set_user_data +( + i2c_instance_t * this_i2c, + void * p_user_data +); + +/***************************************************************************//** + The I2C_get_user_data() function is used to allows the retrieval of the address + of a block of application specific data associated with a CoreI2C channel. + The composition of the data block is an application matter and the driver + simply provides the means for the application to set and retrieve the pointer. + For example, this is used to provide additional channel specific information + to the slave write handler. + + @param this_i2c + The this_i2c parameter is a pointer to the i2c_instance_t data structure + holding all data related to a specific CoreI2C channel. For example, if only + one channel is initialized, this data structure holds the information of + channel 0 of the instantiated CoreI2C hardware. + + @return + This function returns a pointer to the user specific data block for this + channel. It is defined as void * as the driver does not know the actual type + of data being pointed. If no user data has been registered for this channel + a NULL pointer is returned. + + @example + @code + #define COREI2C_BASE_ADDR 0xC0000000u + #define SLAVE_SER_ADDR 0x10u + + i2c_instance_t g_i2c_inst; + app_data_t channel_xdata; + + void main( void ) + { + app_data_t *p_xdata; + + I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, + I2C_PCLK_DIV_256 ); + + // Store location of user data in instance structure + I2C_set_user_data( &g_i2c_inst, (void *)&channel_xdata ); + + ... + + // Retrieve location of user data and do some work on it + p_xdata = (app_data_t *)I2C_get_user_data( &g_i2c_inst ); + if( NULL != p_xdata ) + { + p_xdata->foo = 123; + } + } + @endcode + */ +void * I2C_get_user_data +( + i2c_instance_t * this_i2c +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreI2C/core_smbus_regs.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreI2C/core_smbus_regs.h new file mode 100644 index 0000000..42ba1df --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreI2C/core_smbus_regs.h @@ -0,0 +1,190 @@ +/******************************************************************************* + * Copyright 2009-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + */ + +#ifndef __CORE_SMBUS_REGISTERS +#define __CORE_SMBUS_REGISTERS 1 + +/*------------------------------------------------------------------------------ + * CONTROL register details + */ +#define CONTROL_REG_OFFSET 0x00u + +/* + * CR0 bits. + */ +#define CR0_OFFSET 0x00u +#define CR0_MASK 0x01u +#define CR0_SHIFT 0u + +/* + * CR1 bits. + */ +#define CR1_OFFSET 0x00u +#define CR1_MASK 0x02u +#define CR1_SHIFT 1u + +/* + * AA bits. + */ +#define AA_OFFSET 0x00u +#define AA_MASK 0x04u +#define AA_SHIFT 2u + +/* + * SI bits. + */ +#define SI_OFFSET 0x00u +#define SI_MASK 0x08u +#define SI_SHIFT 3u + +/* + * STO bits. + */ +#define STO_OFFSET 0x00u +#define STO_MASK 0x10u +#define STO_SHIFT 4u + +/* + * STA bits. + */ +#define STA_OFFSET 0x00u +#define STA_MASK 0x20u +#define STA_SHIFT 5u + +/* + * ENS1 bits. + */ +#define ENS1_OFFSET 0x00u +#define ENS1_MASK 0x40u +#define ENS1_SHIFT 6u + +/* + * CR2 bits. + */ +#define CR2_OFFSET 0x00u +#define CR2_MASK 0x80u +#define CR2_SHIFT 7u + +/*------------------------------------------------------------------------------ + * STATUS register details + */ +#define STATUS_REG_OFFSET 0x04u + +/*------------------------------------------------------------------------------ + * DATA register details + */ +#define DATA_REG_OFFSET 0x08u + +/* + * TARGET_ADDR bits. + */ +#define TARGET_ADDR_OFFSET 0x08u +#define TARGET_ADDR_MASK 0xFEu +#define TARGET_ADDR_SHIFT 1u + +/* + * DIR bit. + */ +#define DIR_OFFSET 0x08u +#define DIR_MASK 0x01u +#define DIR_SHIFT 0u + + +/*------------------------------------------------------------------------------ + * ADDRESS register details + */ +#define ADDRESS_REG_OFFSET 0x0Cu + +/* + * GC bits. + */ +#define GC_OFFSET 0x0Cu +#define GC_MASK 0x01u +#define GC_SHIFT 0u + +/* + * ADR bits. + */ +#define OWN_SLAVE_ADDR_OFFSET 0x0Cu +#define OWN_SLAVE_ADDR_MASK 0xFEu +#define OWN_SLAVE_ADDR_SHIFT 1u + +/*------------------------------------------------------------------------------ + * SMBUS register details + */ +#define SMBUS_REG_OFFSET 0x10u + +/* + * SMBALERT_IE bits. + */ +#define SMBALERT_IE_OFFSET 0x10u +#define SMBALERT_IE_MASK 0x01u +#define SMBALERT_IE_SHIFT 0u + +/* + * SMBSUS_IE bits. + */ +#define SMBSUS_IE_OFFSET 0x10u +#define SMBSUS_IE_MASK 0x02u +#define SMBSUS_IE_SHIFT 1u + +/* + * SMB_IPMI_EN bits. + */ +#define SMB_IPMI_EN_OFFSET 0x10u +#define SMB_IPMI_EN_MASK 0x04u +#define SMB_IPMI_EN_SHIFT 2u + +/* + * SMBALERT_NI_STATUS bits. + */ +#define SMBALERT_NI_STATUS_OFFSET 0x10u +#define SMBALERT_NI_STATUS_MASK 0x08u +#define SMBALERT_NI_STATUS_SHIFT 3u + +/* + * SMBALERT_NO_CONTROL bits. + */ +#define SMBALERT_NO_CONTROL_OFFSET 0x10u +#define SMBALERT_NO_CONTROL_MASK 0x10u +#define SMBALERT_NO_CONTROL_SHIFT 4u + +/* + * SMBSUS_NI_STATUS bits. + */ +#define SMBSUS_NI_STATUS_OFFSET 0x10u +#define SMBSUS_NI_STATUS_MASK 0x20u +#define SMBSUS_NI_STATUS_SHIFT 5u + +/* + * SMBSUS_NO_CONTROL bits. + */ +#define SMBSUS_NO_CONTROL_OFFSET 0x10u +#define SMBSUS_NO_CONTROL_MASK 0x40u +#define SMBSUS_NO_CONTROL_SHIFT 6u + +/* + * SMBUS_MST_RESET bits. + */ +#define SMBUS_MST_RESET_OFFSET 0x10u +#define SMBUS_MST_RESET_MASK 0x80u +#define SMBUS_MST_RESET_SHIFT 7u + +/*------------------------------------------------------------------------------ + * SLAVE ADDRESS 1 register details + */ + +#define ADDRESS1_REG_OFFSET 0x1Cu + +/* + * SLAVE1_EN bit of Slave Address 1 . + */ +#define SLAVE1_EN_OFFSET 0x1Cu +#define SLAVE1_EN_MASK 0x01u +#define SLAVE1_EN_SHIFT 0u + +#endif /* __CORE_SMBUS_REGISTERS */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreI2C/i2c_interrupt.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreI2C/i2c_interrupt.c new file mode 100644 index 0000000..f4f1ff5 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreI2C/i2c_interrupt.c @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright 2009-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * CoreI2C driver interrupt control. + * + */ +#include "core_i2c.h" + +/*------------------------------------------------------------------------------ + * This function must be modified to enable interrupts generated from the + * CoreI2C instance identified as parameter. + */ +void I2C_enable_irq( i2c_instance_t * this_i2c ) +{ + HAL_ASSERT(0) +} + +/*------------------------------------------------------------------------------ + * This function must be modified to disable interrupts generated from the + * CoreI2C instance identified as parameter. + */ +void I2C_disable_irq( i2c_instance_t * this_i2c ) +{ + HAL_ASSERT(0) +} diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc.c new file mode 100644 index 0000000..b5942d8 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc.c @@ -0,0 +1,1229 @@ +/** + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core_mmc.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreMMC Source + * + */ + +#include "core_mmc.h" +#include "core_mmc_cmd_defs.h" +#include "core_mmc_if.h" +#include "core_mmc_regs.h" + +#ifndef LEGACY_DIR_STRUCTURE +#include "hal/hal.h" +#include "hal/hal_assert.h" + +#else +#include "hal.h" +#include "hal_assert.h" +#endif + +/*--------------------------Private functions---------------------------------*/ + +static cif_response_t set_mmc_clk_div(const mmc_instance_t *this_mmc); +static cif_response_t set_mmc_dwidth(const mmc_instance_t *this_mmc); +static mmc_transfer_status_t get_hardware_config(mmc_instance_t *this_mmc); +static void get_card_sector_size(mmc_instance_t *this_mmc); +static mmc_transfer_status_t oper_cond_config(const mmc_instance_t *this_mmc); +static mmc_transfer_status_t data_transfer_config(mmc_instance_t *this_mmc); +static mmc_transfer_status_t dev_addr_config(const mmc_instance_t *this_mmc); + +/** + Macros for commands + */ +#define NO_ERROR 0u +#define SET_BIT 1u +#define DONE 1u +#define BLK_SIZE 512u +#define SECTOR_SIZE_512 512u +#define BUFF_EMPTY 0u +#define RCA_VALUE 0x0001FFFFu +#define STUFF_BITS 0x0000FFFFu +#define OP_COND 0x40000080u +#define ISR_READ_ERR 0xF2u +#define BISR_READ_ERR 0x3Cu +#define BUSY_BIT_MASK 0x80u +#define SECT_SIZE_LWR_CSD_MASK 0x03u +#define SECT_SIZE_UPR_CSD_MASK 0x0Cu +#define FIFO_EMPTY 1u +#define NO_BLOCK_TRAN_ERR 0x24u +#define SECTOR_ACCESS_MODE_MASK 0x60u +#define SECTOR_MODE 0x02u +#define NULL_PARAMS 0xFEu +#define HCLK_DIV_2 0u +#define HCLK_DIV_128 63u +#define SECTOR_SHIFT 5u +#define RESET_ARG 0x00000000u +#define MMC_DW_CSD 0x03B70000u +#define MMC_HS_MODE 0x03B90100u + +/*--------------------------CONFIG APIs---------------------------------------*/ + +/** + * MMC_param_config() + * See ".h" for details of how to use this function. + */ +void MMC_param_config(mmc_params_t *this_mmc_params) { + this_mmc_params->clk_rate_div = HCLK_DIV_2; + this_mmc_params->init_clk_rate_div = HCLK_DIV_128; + this_mmc_params->valid_param_config = NULL_PARAMS; +} + +/** + * MMC_init() + * See ".h" for details of how to use this function. + */ +mmc_transfer_status_t MMC_init(mmc_instance_t *this_mmc, addr_t base_address, + const mmc_params_t *this_mmc_params) { + mmc_transfer_status_t ret_status; + cif_response_t response_status; + uint8_t response_reg; + uint32_t csd_reg[512 / 4]; + uint8_t *pCsd_reg; + volatile uint32_t delay; + + HAL_ASSERT(NULL_PARAMS == this_mmc_params->valid_param_config); + + this_mmc->clk_rate_div = this_mmc_params->clk_rate_div; + this_mmc->init_clk_rate_div = this_mmc_params->init_clk_rate_div; + this_mmc->base_addr = (addr_t)base_address; + this_mmc->data_timeout = this_mmc_params->data_timeout; + + /* Configure data timeout */ + HAL_set_32bit_reg(this_mmc->base_addr, DATATO, this_mmc->data_timeout); + HAL_set_32bit_reg(this_mmc->base_addr, DATATO, 0x7fffffff); + + /* Configure the pre-scaler for initialization clock */ + HAL_set_8bit_reg(this_mmc->base_addr, CLKR, this_mmc->init_clk_rate_div); + + delay = 0x111BAU; + while (delay) { + delay--; + } + ret_status = + get_hardware_config(this_mmc); /* If issue with HW config, stop now */ + + if (MMC_TRANSFER_SUCCESS == ret_status) { + /* + * Reset the device + * 4- Send CMD0 to reset the bus, keep CS line high during this step. + * Wait for 1ms, then wait for 74 more clock cycles between + * setting clock to 400KHz and sending CMD0 + */ + response_status = + cif_send_cmd(this_mmc->base_addr, RESET_ARG, MMC_CMD_0_GO_IDLE_STATE); + delay = 0xFFFFU; + while (delay) { + delay--; + } + + if (TRANSFER_IF_SUCCESS == response_status) { + /* Specify the operating conditions to the device */ + ret_status = oper_cond_config(this_mmc); + if (MMC_TRANSFER_SUCCESS == ret_status) { + delay = 0xFFFFU; + while (delay--) { + ; + } + response_reg = HAL_get_8bit_reg(this_mmc->base_addr, RR1); + this_mmc->access_mode = + ((SECTOR_ACCESS_MODE_MASK & response_reg) >> SECTOR_SHIFT); + HAL_ASSERT(SECTOR_MODE == this_mmc->access_mode); + /* Assign an RCA to the device */ + ret_status = dev_addr_config(this_mmc); + if (MMC_TRANSFER_SUCCESS == ret_status) { + /* Configure the data transfer */ + ret_status = data_transfer_config(this_mmc); + /* Fill in some items from the extended SCSD register */ + if (MMC_INIT_SUCCESS == ret_status) { + ret_status = + MMC_single_block_read(this_mmc, READ_SEND_EXT_CSD, csd_reg); + if (MMC_TRANSFER_SUCCESS == ret_status) { + pCsd_reg = (uint8_t *)csd_reg; + this_mmc->hw_sec_count = + csd_reg[212 / + 4]; /* Offsets defined in JESD84-A441 extended CSD */ + this_mmc->hw_ext_csd_rev = (uint8_t)(csd_reg[192 / 4] & 0xFFU); + this_mmc->hw_card_type = (uint8_t)(csd_reg[196 / 4] & 0xFFU); + this_mmc->hw_hs_timing = pCsd_reg[185]; + if (!this_mmc->hw_hs_timing) { + ret_status = MMC_CLK_DIV_ERR; + } else { + ret_status = MMC_INIT_SUCCESS; + } + } + } + } else { + ret_status = MMC_RCA_ERROR; + } + } else { + ret_status = MMC_OP_COND_ERR; + } + } else { + ret_status = MMC_RESET_ERR; + } + } /* end of if(MMC_TRANSFER_SUCCESS == response_status) */ + + /* Clear interrupts in the BISR & ISR*/ + HAL_set_32bit_reg(this_mmc->base_addr, ICR, CLEAR_ALL_32); + + return (ret_status); +} +/*--------------------------UTILITY APIs--------------------------------------*/ + +/** + * Returns write address of FIFO + * Used in instead of + * HAL_set_32bit_reg(this_mmc->base_addr, WDR, value); + * as faster. + * See ".h" for details of how to use this function. + */ +addr_t MMC_get_fifo_write_address(mmc_instance_t *this_mmc) { + return ((addr_t)(this_mmc->base_addr + FIELD_OFFSET(WDR_REG))); +} + +/** + * Returns address of read FIFO register (RDR) + * Used in instead of + * HAL_get_32bit_reg(this_mmc->base_addr, RDR); + * as faster. + * See ".h" for details of how to use this function. + */ +addr_t MMC_get_fifo_read_address(mmc_instance_t *this_mmc) { + return ((addr_t)(this_mmc->base_addr + FIELD_OFFSET(RDR_REG))); +} + +/** + * Returns status of current operation + */ +mmc_transfer_status_t MMC_status(mmc_instance_t *this_mmc, + uint32_t current_timeout_ticks) { + /* Check if timeout passed */ + if ((current_timeout_ticks - this_mmc->status.start_systick) > + (this_mmc->status.timeout_value_ticks)) { + /* Disable interrupts */ + HAL_set_32bit_reg(this_mmc->base_addr, IMR, CLEAR_ALL_32); + /* And clear them- perhaps just clear on start sequence */ + HAL_set_32bit_reg(this_mmc->base_addr, ICR, CLEAR_ALL_32); + /* Let application know we have timed out */ + this_mmc->status.int_state = MMC_ISR_ERROR; + this_mmc->status.err_state = MMC_APP_TIMEOUT_FAIL; + } + + return (this_mmc->status.err_state); +} + +/** + * Initializes FIFO on CoreMMC + */ +void MMC_init_fifo(const mmc_instance_t *this_mmc) { + HAL_set_8bit_reg_field(this_mmc->base_addr, CTRL_RFIFO, SET_BIT); +} + +/*--------------------------WRITE APIs----------------------------------------*/ + +/** + * MMC_single_block_write() + * See ".h" for details of how to use this function. + */ +mmc_transfer_status_t MMC_single_block_write(const mmc_instance_t *this_mmc, + const uint32_t *src_addr, + uint32_t dst_addr) { + cif_response_t response_status; + mmc_transfer_status_t ret_status; + uint16_t word_cnt = BUFF_EMPTY; + uint8_t blk_tran_err; + uint8_t blk_write; + + /* + * Ensure that the write FIFO is empty and load the block of data into the + * FIFO, one byte at a time through the Write Data Register. + */ + MMC_init_fifo(this_mmc); + + do { + response_status = + cif_send_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS); + } while (DEVICE_BUSY == response_status); + + /* + * Configure the eMMC device to receive a single block, set the block + * Write Start Bit in hardware to transmit a block of data from the FIFO + * to the MMC device and wait until the BDONE signal is set. + */ + response_status = cif_send_cmd(this_mmc->base_addr, dst_addr, + MMC_CMD_24_WRITE_SINGLE_BLOCK); + HAL_set_8bit_reg_field(this_mmc->base_addr, BCSR_BWSTRT, SET_BIT); + + while ((BLK_SIZE / 4) > word_cnt) { + HAL_set_32bit_reg(this_mmc->base_addr, WDR, src_addr[word_cnt]); + ++word_cnt; + } + + /* Check is block write complete */ + do { + blk_write = HAL_get_8bit_reg_field(this_mmc->base_addr, BISR_SBWDONE); + } while (DONE != blk_write); + blk_tran_err = + (NO_BLOCK_TRAN_ERR & HAL_get_8bit_reg(this_mmc->base_addr, BISR)); + if (NO_ERROR == blk_tran_err) { + /* + * Ensure no error fields in Card Status register are set and that + * the device is idle before this function returns. + */ + do { + response_status = + cif_send_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS); + } while (DEVICE_BUSY == response_status); + + if (TRANSFER_IF_SUCCESS == response_status) { + ret_status = MMC_TRANSFER_SUCCESS; + } else { + ret_status = MMC_TRANSFER_FAIL; + } + } else { + ret_status = MMC_CRC_ERR; + } + + /* Clear interrupts in the BISR & ISR*/ + HAL_set_32bit_reg(this_mmc->base_addr, ICR, CLEAR_ALL_32); + + return (ret_status); +} + +/** + * MMC_single_block_write_nb() + * See ".h" for details of how to use this function. + * Before entering this function, MMC_init_fifo must be called, and mechanism + * set-up to fill FIFO. + */ +mmc_transfer_status_t MMC_single_block_write_nb(mmc_instance_t *this_mmc, + uint32_t dst_addr, + uint32_t current_timeout_ticks, + uint32_t timeout_ticks) { + mmc_transfer_status_t ret_status; + + /* Parse input parameters */ + HAL_ASSERT(0x0U != this_mmc); + HAL_ASSERT(0x0U != (uint8_t)timeout_ticks); + + /* Initialize interrupt state machine */ + this_mmc->status.int_state = + MMC_ISR_SW_PARSE_CMD13; /* Set the current CoreMMC interrupt state machine + state */ + this_mmc->status.no_of_blks = + 1U; /* Number of blocks to either send or received in current command */ + this_mmc->status.blk_addr = dst_addr; /* Block to start read/write from */ + this_mmc->status.start_systick = + current_timeout_ticks; /* Set at start- to enable timeout */ + this_mmc->status.timeout_value_ticks = timeout_ticks; /* In systicks */ + this_mmc->status.err_state = + MMC_CMD_PROCESSING; /* Error report from interrupt */ + + /* Enable required interrupts in CoreMMC */ + HAL_set_8bit_reg(this_mmc->base_addr, IMR, IMR_MASK_MULTI_WRITE); + + /* Send Command to the MMC device */ + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + + /* Interrupt handler has now taken over */ + /* use mmc_status() to check progress */ + ret_status = MMC_CMD_PROCESSING; + + return (ret_status); +} + +/** + * MMC_multi_block_write() + * See ".h" for details of how to use this function. + * Before entering this function, MMC_init_fifo must be called, and mechanism + * set-up to fill FIFO. + */ +mmc_transfer_status_t MMC_multi_block_write(mmc_instance_t *this_mmc, + uint16_t no_of_blks, + uint32_t dst_addr, + uint32_t current_timeout_ticks, + uint32_t timeout_ticks) { + mmc_transfer_status_t ret_status; + + /* Parse input parameters */ + HAL_ASSERT(0U != this_mmc); + HAL_ASSERT(0U != no_of_blks); + HAL_ASSERT(0u != timeout_ticks); + + /* Initialize interrupt state machine */ + this_mmc->status.int_state = + MMC_ISR_MW_PARSE_CMD13; /* Set the current CoreMMC interrupt state machine + state */ + this_mmc->status.no_of_blks = no_of_blks; /* Number of blocks to either send + or received in current command */ + this_mmc->status.blk_addr = dst_addr; /* Block to start read/write from */ + this_mmc->status.start_systick = + current_timeout_ticks; /* Set at start- to enable timeout */ + this_mmc->status.timeout_value_ticks = timeout_ticks; /* In systicks */ + this_mmc->status.err_state = + MMC_CMD_PROCESSING; /* Error report from interrupt */ + + /* Enable required interrupts in CoreMMC */ + HAL_set_8bit_reg(this_mmc->base_addr, IMR, IMR_MASK_MULTI_WRITE); + + /* Send Command to the MMC device */ + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + + /* Interrupt handler has now taken over */ + /* use mmc_status() to check progress */ + ret_status = MMC_CMD_PROCESSING; + + return (ret_status); +} + +/*---------------------------READ APIs----------------------------------------*/ + +/** + * MMC_single_block_read() + * See ".h" for details of how to use this function. + */ +mmc_transfer_status_t MMC_single_block_read(const mmc_instance_t *this_mmc, + uint32_t src_addr, + uint32_t *dst_addr) { + uint32_t isr_errors; + uint8_t blk_read; + uint16_t word_cnt = (BLK_SIZE / 4); + uint32_t idx_cnt = 0u; + cif_response_t response_status; + mmc_transfer_status_t ret_status; + + /* + * Ensure that the Read FIFO is empty. + */ + MMC_init_fifo(this_mmc); + + do { + response_status = + cif_send_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS); + } while (DEVICE_BUSY == response_status); + + /* + * Configure the hardware FIFO to receive a single block, instruct the eMMC + * device to send a block of data and wait until BRDONE is set. + */ + HAL_set_8bit_reg_field(this_mmc->base_addr, BCSR_BRSTRT, SET_BIT); + if (READ_SEND_EXT_CSD == src_addr) { + response_status = + cif_send_cmd(this_mmc->base_addr, 0U, MMC_CMD_8_SEND_EXT_CSD); + } else { + response_status = cif_send_cmd(this_mmc->base_addr, src_addr, + MMC_CMD_17_READ_SINGLE_BLOCK); + } + /* Check is block read complete */ + do { + blk_read = HAL_get_8bit_reg_field(this_mmc->base_addr, BISR_SBRDONE); + } while ((DONE != blk_read)); + + /* Read in the contents of the FIFO */ + do { + dst_addr[idx_cnt] = HAL_get_32bit_reg(this_mmc->base_addr, RDR); + ++idx_cnt; + --word_cnt; + } while (word_cnt > (BUFF_EMPTY)); + + isr_errors = HAL_get_32bit_reg(this_mmc->base_addr, ISR); + + if (!(ISR_BISR_MBSIR_ERR & isr_errors)) /* Abort if any errors*/ + { + uint8_t read_err_isr; + + read_err_isr = ISR_READ_ERR & HAL_get_8bit_reg(this_mmc->base_addr, ISR); + if (NO_ERROR == read_err_isr) { + /* + * Ensure no error fields in Card Status register are set and that + * the device is idle before this function returns. + */ + do { + response_status = cif_send_cmd(this_mmc->base_addr, RCA_VALUE, + MMC_CMD_13_SEND_STATUS); + } while (DEVICE_BUSY == response_status); + + if (TRANSFER_IF_SUCCESS == response_status) { + ret_status = MMC_TRANSFER_SUCCESS; + } else { + ret_status = MMC_TRANSFER_FAIL; + } + } else { + ret_status = MMC_READ_CTRL_ERR; + } + } else { + ret_status = MMC_CRC_ERR; + } + + /* Clear interrupts in the BISR & ISR*/ + HAL_set_32bit_reg(this_mmc->base_addr, ICR, CLEAR_ALL_32); + + return (ret_status); +} + +/** + * MMC_single_block_read_nb() + * See ".h" for details of how to use this function. + */ +mmc_transfer_status_t MMC_single_block_read_nb(mmc_instance_t *this_mmc, + uint32_t src_addr, + uint32_t current_timeout_ticks, + uint32_t timeout_ticks) { + mmc_transfer_status_t ret_status; + + /* Parse input parameters */ + HAL_ASSERT(0U != this_mmc); + HAL_ASSERT(0U != timeout_ticks); + + if (READ_SEND_EXT_CSD == src_addr) { + /* Set the current CoreMMC interrupt state machine state */ + this_mmc->status.int_state = MMC_ISR_EXT_CSD_PARSE_CMD13; + } else { + /* Set the current CoreMMC interrupt state machine state */ + this_mmc->status.int_state = MMC_ISR_SR_PARSE_CMD13; + } + + /* Initialize interrupt state machine */ + this_mmc->status.no_of_blks = + 0x1U; /* Number of blocks to either send or received in current command */ + this_mmc->status.blk_addr = src_addr; /* Block to start read/write from */ + this_mmc->status.start_systick = + current_timeout_ticks; /* Set at start- to enable timeout */ + this_mmc->status.timeout_value_ticks = timeout_ticks; /* In systicks */ + this_mmc->status.err_state = + MMC_CMD_PROCESSING; /* Error report from interrupt */ + + /* Enable required interrupts in CoreMMC- for sending commands / error + * checking */ + HAL_set_8bit_reg(this_mmc->base_addr, IMR, IMR_MASK_MULTI_READ); + + /* Set number of blocks to send in the coreMMC */ + HAL_set_16bit_reg(this_mmc->base_addr, MBCOUNT, this_mmc->status.no_of_blks); + + /* Send Command to the MMC device */ + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + + /* Interrupt handler has now taken over */ + /* use mmc_status() to check progress */ + ret_status = MMC_CMD_PROCESSING; + + return (ret_status); +} + +/** + * MMC_multi_block_read() + * See ".h" for details of how to use this function. + */ +mmc_transfer_status_t MMC_multi_block_read(mmc_instance_t *this_mmc, + uint16_t no_of_blks, + uint32_t src_addr, + uint32_t current_timeout_ticks, + uint32_t timeout_ticks) { + mmc_transfer_status_t ret_status; + + /* Parse input parameters */ + HAL_ASSERT(0U != this_mmc); + HAL_ASSERT(0U != no_of_blks); + HAL_ASSERT(0U != timeout_ticks); + + /* Set the current CoreMMC interrupt state machine state */ + this_mmc->status.int_state = MMC_ISR_MR_PARSE_CMD13; + + /* Initialize interrupt state machine */ + this_mmc->status.no_of_blks = no_of_blks; /* Number of blocks to either send + or received in current command */ + this_mmc->status.blk_addr = src_addr; /* Block to start read/write from */ + this_mmc->status.start_systick = + current_timeout_ticks; /* Set at start- to enable timeout */ + this_mmc->status.timeout_value_ticks = timeout_ticks; /* In systicks */ + this_mmc->status.err_state = + MMC_CMD_PROCESSING; /* Error report from interrupt */ + + /* Enable required interrupts in CoreMMC- for sending commands / error + * checking */ + HAL_set_8bit_reg(this_mmc->base_addr, IMR, IMR_MASK_MULTI_READ); + + /* Set number of blocks to send in the coreMMC */ + HAL_set_16bit_reg(this_mmc->base_addr, MBCOUNT, this_mmc->status.no_of_blks); + + /* Send Command to the MMC device */ + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + + /* Interrupt handler has now taken over */ + /* use mmc_status() to check progress */ + ret_status = MMC_CMD_PROCESSING; + + return (ret_status); +} + +/*--------------------------Private Functions---------------------------------*/ + +/** + * Set MMC data width function + * + * The set_mmc_dwidth() function configures the number of data lines used in the + * interface between the eMMC device and this instance of the CoreMMC hardware. + * This function sets the number of data lines used in accordance to number of + * data lines connected, specified in the version register, and then informs the + * eMMC device of the number of lines configured, before waiting on the device + * to return to idle, indicating that the MMC data width was modified + * successfully. This function can only be called when the device is in Transfer + * State. + */ +static cif_response_t set_mmc_dwidth(const mmc_instance_t *this_mmc) { + cif_response_t response_status; + + HAL_set_8bit_reg(this_mmc->base_addr, DCTRL, this_mmc->hw_mmc_dwidth); + + /* Set MMC data bus width */ + response_status = cif_send_cmd( + this_mmc->base_addr, + MMC_DW_CSD | ((uint32_t)this_mmc->hw_mmc_dwidth << 8u), MMC_CMD_6_SWITCH); + /* + * Writing to the EXT CSD register takes significant time, so function must + * not return until the 'READY FOR DATA' bit is set in the Card Status + * Register. + */ + while (DEVICE_BUSY == response_status) { + response_status = + cif_send_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS); + } + + return (response_status); +} + +/** + * Get hardware configuration function + * + * The get_hardware_config() function reads in the contents of the version + * register in hardware, parsers it and stores the resultant configuration data + * in the relative fields of the mmc_instance_t structure corresponding to this + * instance of the core. + */ +static mmc_transfer_status_t get_hardware_config(mmc_instance_t *this_mmc) { + mmc_transfer_status_t ret_status = MMC_TRANSFER_SUCCESS; + + this_mmc->hw_fifo_depth = + HAL_get_8bit_reg_field(this_mmc->base_addr, VR_MMCFD); + this_mmc->hw_mmc_dwidth = + HAL_get_8bit_reg_field(this_mmc->base_addr, VR_MMCDW); + this_mmc->hw_maj_revision = + HAL_get_8bit_reg_field(this_mmc->base_addr, VR_REV); + + /* If revision is equal to 3, indicates Major revision now stored in + * VR_MAJ_REG and minor in VR_MIN_REG */ + if (this_mmc->hw_maj_revision == 0x03U) { + this_mmc->hw_maj_revision = HAL_get_8bit_reg(this_mmc->base_addr, VR_MAJ); + this_mmc->hw_min_revision = HAL_get_8bit_reg(this_mmc->base_addr, VR_MIN); + } else { + this_mmc->hw_min_revision = 0U; + ret_status = MMC_UNSUPPORTED_HW_REVISION; + } + + return ret_status; +} + +/** + * MMC Read Device Sector Size Function + * + * The get_card_sector_size() function is used to determine the sector size + * supported by the eMMC device. This function reads in the sector size from the + * CSD Register, performs base conversion to convert the value to decimal base + * and stores the result in the mmc_instance_t structure corresponding to this + * instance of the Core. This function can only be called when the device is in + * Standby State. + */ +static void get_card_sector_size(mmc_instance_t *this_mmc) { + uint32_t csd_max_sector_lwr; + uint32_t csd_max_sector_upr; + uint32_t max_sector_len; + cif_response_t ret_val; + + ret_val = cif_send_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_9_SEND_CSD); + + csd_max_sector_lwr = + SECT_SIZE_LWR_CSD_MASK & + ((uint32_t)HAL_get_8bit_reg(this_mmc->base_addr, RR13) >> 6u); + + csd_max_sector_upr = + SECT_SIZE_UPR_CSD_MASK & + ((uint32_t)HAL_get_8bit_reg(this_mmc->base_addr, RR12) << 2u); + + max_sector_len = csd_max_sector_lwr | csd_max_sector_upr; + this_mmc->sector_size = (uint16_t)((uint32_t)1u << max_sector_len); +} + +/** + * Set MMC clock divider rate function + * + * The set_mmc_clk_div() function sets the clock divider rate which governs the + * data rate of the MMC interface with respect to HCLK. The function sets the + * clock divider rate in the hardware register, configures the device for + * high speed operation and waits for the device to become idle before + * returning.This function can only be called when the eMMC device is in + * Transfer State. + */ +static cif_response_t set_mmc_clk_div(const mmc_instance_t *this_mmc) { + cif_response_t response_status; + + do { + response_status = + cif_send_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS); + } while (DEVICE_BUSY == response_status); + + /* From JEDEC Standard No. 84-A441: + * After the host verifies that the card complies with version 4.0, or + * higher, of this standard, it has to enable the high speed mode timing in + * the card, before changing the clock frequency to a frequency higher than + * 20MHz. + * After power-on, or software reset, the interface timing of the card is + * set as specified in Table 114 on page 177. For the host to change to a + * higher clock frequency, it has to enable the high speed interface timing. + * The host uses the SWITCH command to write 0x01 to the HS_TIMING byte, in + * the Modes segment of the EXT_CSD register. + * The valid values for this register are defined in "HS_TIMING [185]" on + * page 141. If the host tries to write an invalid value, the HS_TIMING byte + * is not changed, the high speed interface timing is not enabled, and the + * SWITCH_ERROR bit is set. + */ + + /* Set high speed mode bit in EXT CSD Register of device */ + if (TRANSFER_IF_SUCCESS == response_status) { + response_status = + cif_send_cmd(this_mmc->base_addr, MMC_HS_MODE, MMC_CMD_6_SWITCH); + } + + if ((TRANSFER_IF_SUCCESS == response_status) || + (DEVICE_BUSY == response_status)) { + do { + response_status = + cif_send_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS); + } while (DEVICE_BUSY == response_status); + + /* Up the CoreMMC clock frequency */ + HAL_set_8bit_reg(this_mmc->base_addr, CLKR, this_mmc->clk_rate_div); + } + + return (response_status); +} + +/** + * Operating conditions configuration function + * + * The oper_cond_config() function specifies the operating conditions of the MMC + * host to the eMMC slave device. These conditions are re-specified until the + * power-up status bit is set in the OCR Register, provided that a transmission + * error does not occur during the process. + * From JEDEC Standard No. 84-A441: + * 5- Send CMD1, with the intended voltage range in the argument + * (either 0x00FF8000 or 0x00000080) + * 6- Receive R3 + * 7- If the OCR busy bit is "0", repeat steps 5 and 6 + */ +static mmc_transfer_status_t oper_cond_config(const mmc_instance_t *this_mmc) { + mmc_transfer_status_t ret_status = MMC_TRANSFER_FAIL; + cif_response_t response_status; + uint8_t response_reg; + uint8_t power_up_status; + + do { + response_status = + cif_send_cmd(this_mmc->base_addr, OP_COND, MMC_CMD_1_SEND_OP_COND); + + response_reg = HAL_get_8bit_reg(this_mmc->base_addr, RR1); + + power_up_status = BUSY_BIT_MASK & response_reg; + + } while ((BUSY_BIT_MASK != power_up_status) && + (TRANSFER_IF_FAIL != response_status)); + if (TRANSFER_IF_FAIL != response_status) { + ret_status = MMC_TRANSFER_SUCCESS; + } + + return (ret_status); +} + +/*----------------------------------------------------------------------------- + * Data transfer configuration function + * + * The data_transfer_config() function configures the MMC data width + * and data rate used during MMC transfers and verifies that the device + * supports 512 byte sector size. + */ +/* + * JEDEC Standard No. 84-A441: + * Switching to high-speed mode + * The following steps are supported by cards implementing version 4.0 or + * higher. Do these steps after the bus is initialized according to section + * Annex A.8.1 on page 203. + * 22-Send CMD7 with the cards RCA to place the card in transmit state + * 23-Send CMD8, SEND_EXT_CSD. From the EXT_CSD the host can learn the power + * class of the card, and choose to work with a wider data bus (See steps 26-37) + * 24-Send CMD6, writing 0x1 to the HS_TIMING byte of the EXT_CSD. The argument + * 0x03B9_0100 will do it. + * 24.1-The card might enter BUSY right after R1, if so, wait until the BUSY + * signal is de-asserted + * 24.2-After the card comes out of BUSY it is configured for high speed + * timing + * 25-Change the clock frequency to the chosen frequency (any frequency + * between 0 and 26/52MHz). + */ +static mmc_transfer_status_t data_transfer_config(mmc_instance_t *this_mmc) { + mmc_transfer_status_t ret_status = MMC_DWIDTH_ERR; + volatile cif_response_t response_status; + + get_card_sector_size(this_mmc); + /* Verify that the device supports 512 byte block size */ + HAL_ASSERT(SECTOR_SIZE_512 == this_mmc->sector_size); + + response_status = cif_send_cmd(this_mmc->base_addr, RCA_VALUE, + MMC_CMD_7_SELECT_DESELECT_CARD); + if (TRANSFER_IF_SUCCESS == response_status) { + response_status = set_mmc_dwidth(this_mmc); + if (TRANSFER_IF_SUCCESS == response_status) { + response_status = set_mmc_clk_div(this_mmc); + if (TRANSFER_IF_SUCCESS == response_status) { + ret_status = MMC_INIT_SUCCESS; + } else { + ret_status = MMC_CLK_DIV_ERR; + } + } + } + + return (ret_status); +} + +/** + * Device address configuration function + * + * The dev_addr_config() function assigns a relative card address to a single + * MMC device connected to the CoreMMC hardware. This function reads in the card + * identification data from the CID Register and sets the relative card address + * to 0x0001. + */ +static mmc_transfer_status_t dev_addr_config(const mmc_instance_t *this_mmc) { + mmc_transfer_status_t ret_status; + cif_response_t response_status; + + response_status = + cif_send_cmd(this_mmc->base_addr, STUFF_BITS, MMC_CMD_2_ALL_SEND_CID); + if (TRANSFER_IF_SUCCESS == response_status) { + response_status = cif_send_cmd(this_mmc->base_addr, RCA_VALUE, + MMC_CMD_3_SET_RELATIVE_ADDR); + if (TRANSFER_IF_SUCCESS == response_status) { + ret_status = MMC_TRANSFER_SUCCESS; + } else { + ret_status = MMC_RCA_ERROR; + } + } else { + ret_status = MMC_CID_RESP_ERR; + } + + return (ret_status); +} + +/** + * ISR related to CoreMMC. See core_mmc.h for details. + */ +void MMC_isr(mmc_instance_t *this_mmc) { + uint8_t read_err_sr; + uint32_t isr_flags, MMC_Response; + + /* Check for errors in CoreMMC */ + isr_flags = HAL_get_32bit_reg(this_mmc->base_addr, ISR); + HAL_set_32bit_reg(this_mmc->base_addr, ICR, CLEAR_ALL_32); + + if (ISR_BISR_MBSIR_ERR & isr_flags) { + /* FIFO timeout error */ + if (((MBISR_MWBFIFOTOERR_MASK << 16) | (BISR_SWBFIFOTOERR_MASK << 8)) & + isr_flags) { + this_mmc->status.temp_err_state = MMC_TIMOUT_FAIL; + } + /* Error symptomatic of eMMC card in wrong state due to write error- + * no response to non-interrupt(not CM12/13) command being sent */ + else if (((ISR_SSBI_MASK | ISR_SRRI_MASK | ISR_SCSI_MASK) & isr_flags) == + (ISR_SCSI_MASK | ISR_SSBI_MASK)) { + this_mmc->status.temp_err_state = MMC_NO_RESPONSE_FAIL; + } else { + this_mmc->status.temp_err_state = MMC_OTHER_FAIL; + } + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + this_mmc->status.int_state = MMC_ISR_STOP_TRANSMISSION; + /* Most important feedback is user error- over or under filling the + * FIFO */ + if (ISR_SUER_MASK & isr_flags) { + this_mmc->status.temp_err_state = MMC_USER_ERROR_FAIL; + } else { + ; + } + } else { + switch (this_mmc->status.int_state) { + /* Multi write */ + case MMC_ISR_MW_PARSE_CMD13: + /* Parse CMD13 response */ + MMC_Response = HAL_get_32bit_reg(this_mmc->base_addr, RR1); + if ((READY_FOR_DATA & MMC_Response) == 0U) /* Not ready */ + { + /* Re-send as busy */ + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + break; + } else if (CURRENT_STATE_TRAN != + (CURRENT_STATE12_9_MASK & MMC_Response)) /* Wrong state */ + { + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + break; + } else if (R1_ERR_RESP_MASK & MMC_Response) /* Error */ + { + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + break; + } else { + ; /* No error */ + } + send_mmc_cmd(this_mmc->base_addr, (uint32_t)(this_mmc->status.no_of_blks), + MMC_CMD_23_SET_BLOCK_COUNT, CHECK_IF_CMD_SENT_NO); + this_mmc->status.int_state = MMC_ISR_MW_PARSE_CMD23; + break; + case MMC_ISR_MW_PARSE_CMD23: + /* Parse CMD23 response */ + MMC_Response = HAL_get_32bit_reg(this_mmc->base_addr, RR1); + if (R1_ERR_RESP_MASK & + HAL_get_32bit_reg(this_mmc->base_addr, RR1)) /* Error */ + { + this_mmc->status.int_state = MMC_ISR_ERROR; + this_mmc->status.err_state = MMC_MBW_FAIL; + break; + } else if ((READY_FOR_DATA & MMC_Response) == 0U) /* Not ready */ + { + /* Re-send as busy */ + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + break; + } else { + ; /* No error */ + } + /* Set number of blocks to send in the coreMMC */ + HAL_set_16bit_reg(this_mmc->base_addr, MBCOUNT, + this_mmc->status.no_of_blks); + /* Send command 25- kick-off block count */ + send_mmc_cmd(this_mmc->base_addr, this_mmc->status.blk_addr, + MMC_CMD_25_WRITE_MULTI_BLOCK, CHECK_IF_CMD_SENT_NO); + this_mmc->status.int_state = MMC_ISR_MW_PARSE_CMD25; + break; + case MMC_ISR_MW_PARSE_CMD25: + /* Parse CMD25 response */ + MMC_Response = HAL_get_32bit_reg(this_mmc->base_addr, RR1); + if (R1_ERR_RESP_MASK & MMC_Response) /* error */ + { + this_mmc->status.int_state = MMC_ISR_ERROR; + this_mmc->status.err_state = MMC_MBW_FAIL; + break; + } + /* No need to check to see if busy as CoreMMC will handle this situation + * when sending */ + /* tell CoreMMC to kick-off write */ + HAL_set_8bit_reg(this_mmc->base_addr, MBIMR, + MBIMR_MASK_MULTI_WRITE); /* Set required interrupts */ + HAL_set_8bit_reg_field(this_mmc->base_addr, MBCSR_MBWSTRT, + SET_BIT); /* Tell MMC to kick-off write */ + this_mmc->status.int_state = MMC_ISR_MW_END; + break; + case MMC_ISR_MW_END: + /* Check CoreMMC multi-write bit finished*/ + if ((MBISR_MBWDONE_MASK << 16) & isr_flags) { + /* Success- report it */ + this_mmc->status.int_state = MMC_ISR_IDLE; + this_mmc->status.err_state = MMC_TRANSFER_SUCCESS; + } else /* Anything else is a fail */ + { + this_mmc->status.int_state = MMC_ISR_ERROR; + this_mmc->status.err_state = MMC_MBW_FAIL; + } + /* Clear masks */ + HAL_set_32bit_reg(this_mmc->base_addr, IMR, 0x00000000); + break; + + /* Single write */ + case MMC_ISR_SW_PARSE_CMD13: + MMC_Response = HAL_get_32bit_reg(this_mmc->base_addr, RR1); + if ((READY_FOR_DATA & MMC_Response) == 0U) /* Not ready */ + { + /* Re-send as busy */ + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + break; + } else if (CURRENT_STATE_TRAN != + (CURRENT_STATE12_9_MASK & MMC_Response)) /* Wrong state */ + { + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + break; + } else if (R1_ERR_RESP_MASK & MMC_Response) /* Error */ + { /* Error clear by ready, hopefully gone next time through */ + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + break; + } else { + ; /* No error */ + } + /* + * Configure the eMMC device to receive a single block, set the block + * Write Start Bit in hardware to transmit a block of data from the FIFO + * to the MMC device and wait until the BDONE signal is set. + */ + send_mmc_cmd(this_mmc->base_addr, this_mmc->status.blk_addr, + MMC_CMD_24_WRITE_SINGLE_BLOCK, CHECK_IF_CMD_SENT_NO); + this_mmc->status.int_state = MMC_ISR_SW_PARSE_CMD24; + break; + case MMC_ISR_SW_PARSE_CMD24: + /* Parse CMD24 response */ + MMC_Response = HAL_get_32bit_reg(this_mmc->base_addr, RR1); + if (R1_ERR_RESP_MASK & MMC_Response) /* error */ + { + this_mmc->status.int_state = MMC_ISR_ERROR; + this_mmc->status.err_state = MMC_SBW_FAIL; + break; + } + /* No need to check to see if busy as CoreMMC will handle this situation + * when sending */ + /* tell CoreMMC to kick-off write */ + HAL_set_8bit_reg(this_mmc->base_addr, BIMR, + BIMR_MASK_SINGLE_WRITE); /* Set required interrupts */ + HAL_set_8bit_reg_field(this_mmc->base_addr, BCSR_BWSTRT, + SET_BIT); /* Tell MMC to kick-off write */ + this_mmc->status.int_state = MMC_ISR_SW_END; + break; + case MMC_ISR_SW_END: + /* Check CoreMMC single-write bit finished*/ + if ((BIMR_SBWDONE_MASK << 8) & isr_flags) { + /* success- report it */ + this_mmc->status.int_state = MMC_ISR_IDLE; + this_mmc->status.err_state = MMC_TRANSFER_SUCCESS; + } else /* Anything else is a fail */ + { + this_mmc->status.int_state = MMC_ISR_ERROR; + this_mmc->status.err_state = MMC_SBW_FAIL; + } + /* Clear masks */ + HAL_set_32bit_reg(this_mmc->base_addr, IMR, 0x00000000); + break; + + /* Multi-read */ + case MMC_ISR_MR_PARSE_CMD13: + /* Parse CMD23 response */ + MMC_Response = HAL_get_32bit_reg(this_mmc->base_addr, RR1); + if ((READY_FOR_DATA & MMC_Response) == 0U) /* Not ready */ + { + /* Re-send as busy */ + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + break; + } else if (CURRENT_STATE_TRAN != + (CURRENT_STATE12_9_MASK & MMC_Response)) /* wrong state */ + { + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + break; + } else if (R1_ERR_RESP_MASK & MMC_Response) /* error */ + { + this_mmc->status.int_state = MMC_ISR_ERROR; + this_mmc->status.err_state = MMC_MBR_FAIL; + break; + } else { + ; /* No error */ + } + send_mmc_cmd(this_mmc->base_addr, (uint32_t)(this_mmc->status.no_of_blks), + MMC_CMD_23_SET_BLOCK_COUNT, CHECK_IF_CMD_SENT_NO); + this_mmc->status.int_state = MMC_ISR_MR_PARSE_CMD23; + break; + case MMC_ISR_MR_PARSE_CMD23: + /* Parse CMD23 response */ + MMC_Response = HAL_get_32bit_reg(this_mmc->base_addr, RR1); + if (R1_ERR_RESP_MASK & MMC_Response) /* error */ + { + this_mmc->status.int_state = MMC_ISR_ERROR; + this_mmc->status.err_state = MMC_MBR_FAIL; + break; + } else if ((READY_FOR_DATA & MMC_Response) == 0U) /* not ready */ + { + /* Re-set eMMC as busy */ + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + break; + } else { + ; /* Do nothing*/ + } + /* Verify read FIF0 empty before start */ + read_err_sr = HAL_get_8bit_reg(this_mmc->base_addr, SR); + if (!(SR_SRFE_MASK & read_err_sr)) { + this_mmc->status.int_state = MMC_ISR_ERROR; + this_mmc->status.err_state = MMC_MBR_FAIL; + break; + } + /* Set-up CoreMMC to expect read */ + HAL_set_8bit_reg(this_mmc->base_addr, MBIMR, + MBIMR_MASK_MULTI_READ); /* set required interrupts */ + HAL_set_8bit_reg_field(this_mmc->base_addr, MBCSR_MBRSTRT, + SET_BIT); /* tell MMC to expect read */ + /* Send command 18- kickoff block read */ + send_mmc_cmd(this_mmc->base_addr, this_mmc->status.blk_addr, + MMC_CMD_18_READ_MULTIPLE_BLOCK, CHECK_IF_CMD_SENT_NO); + this_mmc->status.int_state = MMC_ISR_MR_PARSE_CMD18; + break; + case MMC_ISR_MR_PARSE_CMD18: + /* Parse CMD18 response */ + MMC_Response = HAL_get_32bit_reg(this_mmc->base_addr, RR1); + if (R1_ERR_RESP_MASK & MMC_Response) /* error */ + { + this_mmc->status.int_state = MMC_ISR_ERROR; + this_mmc->status.err_state = MMC_MBR_FAIL; + break; + } + /* No need to check to see if busy as CoreMMC will handle this situation + * when sending or receiving multi blocks no need to do anything else, + * read will be signaled via DMA, or next state if manually transferring. + */ + this_mmc->status.int_state = MMC_ISR_MR_END; + break; + case MMC_ISR_MR_END: + /* Check CoreMMC multi-read bit finished*/ + if ((MBIMR_MBRDONE_MASK << 16) & isr_flags) { + /* Success- report it */ + this_mmc->status.int_state = MMC_ISR_IDLE; + this_mmc->status.err_state = MMC_TRANSFER_SUCCESS; + } else /* Anything else is a fail */ + { + this_mmc->status.int_state = MMC_ISR_ERROR; + this_mmc->status.err_state = MMC_MBR_FAIL; + } + /* Clear masks */ + HAL_set_32bit_reg(this_mmc->base_addr, IMR, 0x00000000); + break; + + /* Single-read */ + case MMC_ISR_SR_PARSE_CMD13: + case MMC_ISR_EXT_CSD_PARSE_CMD13: + MMC_Response = HAL_get_32bit_reg(this_mmc->base_addr, RR1); + if ((READY_FOR_DATA & MMC_Response) == 0U) /* Not ready */ + { + /* Re-send as busy */ + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + break; + } else if (CURRENT_STATE_TRAN != + (CURRENT_STATE12_9_MASK & MMC_Response)) /* Wrong state */ + { + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + break; + } else if (R1_ERR_RESP_MASK & MMC_Response) /* Error */ + { + this_mmc->status.int_state = MMC_ISR_ERROR; + this_mmc->status.err_state = MMC_SBR_FAIL; + break; + } else { + ; /* No error */ + } + /* Set-up CoreMMC to expect read */ + HAL_set_8bit_reg(this_mmc->base_addr, BIMR, + BIMR_MASK_SINGLE_READ); /* Set required interrupts */ + HAL_set_8bit_reg_field(this_mmc->base_addr, BCSR_BRSTRT, + SET_BIT); /* Tell MMC to expect read */ + /* Send command 17- kickoff single read */ + if (MMC_ISR_SR_PARSE_CMD13 == this_mmc->status.int_state) { + send_mmc_cmd(this_mmc->base_addr, this_mmc->status.blk_addr, + MMC_CMD_17_READ_SINGLE_BLOCK, CHECK_IF_CMD_SENT_NO); + } else { + send_mmc_cmd(this_mmc->base_addr, 0, MMC_CMD_8_SEND_EXT_CSD, + CHECK_IF_CMD_SENT_NO); + } + this_mmc->status.int_state = MMC_ISR_SR_PARSE_CMD17; + break; + case MMC_ISR_SR_PARSE_CMD17: + /* Parse CMD18 response */ + MMC_Response = HAL_get_32bit_reg(this_mmc->base_addr, RR1); + if (R1_ERR_RESP_MASK & MMC_Response) /* error */ + { + this_mmc->status.int_state = MMC_ISR_ERROR; + this_mmc->status.err_state = MMC_SBR_FAIL; + break; + } + /* No need to check to see if busy as CoreMMC will handle this situation + * when sending or receiving multi blocks no need to do anything else, + * read will be signaled via DMA, or next state if manually transferring. + */ + this_mmc->status.int_state = MMC_ISR_SR_END; + break; + case MMC_ISR_SR_END: + if ((BIMR_SBRDONE_MASK << 8) & isr_flags) { + /* Success- report it */ + this_mmc->status.int_state = MMC_ISR_IDLE; + this_mmc->status.err_state = MMC_TRANSFER_SUCCESS; + } else /* Anything else is a fail */ + { + this_mmc->status.int_state = MMC_ISR_ERROR; + this_mmc->status.err_state = MMC_SBR_FAIL; + } + /* Clear masks */ + HAL_set_32bit_reg(this_mmc->base_addr, IMR, 0x00000000); + break; + + case MMC_ISR_ERROR: /* An easy way for driver to know if something went + wrong */ + /* Clear masks */ + HAL_set_32bit_reg(this_mmc->base_addr, IMR, 0x00000000U); + this_mmc->status.int_state = MMC_ISR_ERROR; + break; + case MMC_ISR_IDLE: /* Should never get an interrupt here, if we do, go to + error state */ + /* Clear masks */ + HAL_set_32bit_reg(this_mmc->base_addr, IMR, 0x00000000U); + this_mmc->status.int_state = MMC_ISR_ERROR; + break; + /* Common states */ + default: + case MMC_ISR_STOP_TRANSMISSION: + MMC_Response = HAL_get_32bit_reg(this_mmc->base_addr, RR1); + if ((READY_FOR_DATA & MMC_Response) == 0U) /* Not ready */ + { + /* Re-send as busy */ + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + break; + } + + else if (R1_ERR_RESP_MASK & MMC_Response) /* Error */ + { + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO); + break; + } else if (CURRENT_STATE_TRAN != + (CURRENT_STATE12_9_MASK & MMC_Response)) /* Wrong state */ + { + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, + MMC_CMD_12_STOP_TRANSMISSION, CHECK_IF_CMD_SENT_NO); + break; + } else { + ; /* No error */ + } + + this_mmc->status.int_state = MMC_ISR_ERROR; + this_mmc->status.err_state = this_mmc->status.temp_err_state; + HAL_set_32bit_reg(this_mmc->base_addr, IMR, 0x00000000); + break; + } + } +} diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc.h new file mode 100644 index 0000000..331ca5e --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc.h @@ -0,0 +1,1255 @@ +/** + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core_mmc.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreMMC prototypes + * + */ + +/** + @mainpage CoreMMC Bare Metal Driver + + @section intro Introduction + Core Multi-Media Card (CoreMMC) is an IP component that facilitates + connection to an external embedded Multi-Media Card (MMC) with configurable + MMC bus width and data rates. This driver only supports eMMC devices with a + capacity more than 2 GB, bus widths of 1-bit, 4-bits, and 8-bits and at MMC + clock rates of up to 52 MHz. + + This driver provides a set of functions for configuring and controlling + CoreMMC as part of a bare metal system, where no operating system is + available. This driver can be adapted as part of an operating system, but the + implementation of the adaptation layer between the driver and the operating + system's driver model is outside the scope of this user guide. + + This driver is not compatible with the CoreMMC versions below v3.0. + + @subsection features Features + + + + The CoreMMC driver provides support for the following features: + + - Initialization of the CoreMMC hardware + - Configuration of the MMC data rate + - Automatically drives the bus width connected (1-bit, 4-bit, 8-bit + connections) + - Single 512-byte block write + - Single 512-byte block read + - Multiple 512-byte block writes + - Multiple 512-byte block reads + + The CoreMMC driver is provided as C source code. + + Supported Hardware IP: The CoreMMC bare metal driver is only compatible + with CoreMMC v3.0 and higher. + + @section driver_configuration Driver Configuration + + For each CoreMMC instance in the hardware design, your application software + must configure the CoreMMC driver by calling `MMC_param_config()` and ` + MMC_init()`. These functions configure a default set of parameters for this + instance, that includes the CoreMMC hardware instance base address and the + MMC clock rate divider. + + The CoreMMC instance is configured at the time of instantiation in the + hardware design for the FIFO depth and the MMC data width parameters. + + This driver supports the control of a single eMMC device connected to the MMC + data bus for each CoreMMC hardware instance. + + Note 1: When using the driver, the clock divider settings must be adjusted + based on the clock speed of the associated AHB HCLK in the Libero design. + + Note 2: When using the non-blocking single read and write API functions, or + the multiple block read/write API functions, the interrupt signal from + CoreMMC must be connected to the processor in the Libero design. + + Note 3: When using the multiple block read/write API functions, the size of + the multi-block read or write (number of blocks block size) used with these + functions must not be greater than the FIFO size configured for CoreMMC in + the Libero design. + + @section theory_op Theory of Operation + + The CoreMMC driver functions allows 512-byte blocks of data to be written to + and read from a single eMMC device connected to each instance of the CoreMMC + hardware. + The blocks can be read/written individually or by using the multi-block + functions. The single block functions are available in two variants. One set + is blocking, the other is non-blocking. The multi-block functions are + non-blocking. + + Note: The eMMC device connected to the CoreMMC hardware instance must support + a 512-byte sector size. This is the default block size for the MMC device + less than 2 GB. + + @section init Initialization + The initialization of the CoreMMC driver involves the following main steps: + - Initialize the `mmc_params_t` data structure. + - Specify the custom `clk_rate_div` and `init_clk_rate_div` configuration. + - Instantiate the HAL interrupt handler associated with the interrupt line + connected to the CoreMMC instance in the Libero hardware design. + - Call `MSS_isr()` from within that handler. + - Call `MMC_init()`. + + During the first step, `MMC_param_config()` is called, which loads + the `mmc_params_t` data structure with the default values. + This configures the `clk_rate_div` parameter such that the MMC clock rate is + 1/2 of HCLK. This also configures the `init_clk_rate_div` parameter such that + the MMC clock rate is 1/128 of HCLK on startup. + + If the default clock rate is sufficient for your clock customization + application, you can skip this step in the initialization process. + + Note: On start-up, the clock rate of the eMMC must be set to a maximum of 400 + KHz. + The `init_clk_rate_div` parameter is adjusted to achieve this using the + following formula. + + MMC Clock Rate = HCLK / (2 (clk_rate_div + 1)) + + The maximum permitted clock rate of the eMMC, which is set by clk_rate_div, + is 52 MHz. + + The `clk_rate_div` parameter in the `mmc_params_t` data structure must be + modified based on your system's clock rate by calling `MMC_param_config()` + and `MMC_init()`. + If the application program modifies the value of `clk_rate_div` after calling + `MMC_init()`, the modified value has no effect. + + The final step involves calling `MMC_init()`. + `MMC_init()` takes: a pointer to a global CoreMMC instance data structure + of the `mmc_instance_t` type; the base address of the CoreMMC instance as + defined by the hardware design; and a pointer to a global parameter data + structure of the `mmc_params_t` type. + + The driver uses the CoreMMC global instance data structure to store the state + information for each CoreMMC instance. Except for MMC_param_config(), a + pointer to this data structure is also used as the first parameter to any of + the driver functions to identify which CoreMMC instance is being used by the + called function. The application programmer is responsible for creating and + maintaining these global CoreMMC instances and parameter data structures. + + Note: `MMC_init()` must be called before any other driver functions is + called. + + @section mmc_sb_tf_ctrl MMC Single Block Transfer Control + + @subsection wr_tf Write Transfer + + To write a single block of data to the eMMC device in non-blocking mode, call + `MC_single_block_write_nb()`. + The user must specify the following information: + + - The CoreMMC hardware instance connected to the required eMMC device. + - The base address of the 512-byte buffer holding the data to transfer. + - The destination sector address, where the data is stored within the eMMC + device. + - The current system ticks. + - The number of system ticks before a timeout occurs. + + To check the status of the write transfer, use `MMC_status()`. + Alternatively, use `MMC_single_block_write()` to perform a single block write + operation in blocking mode. This function waits until an error occurs, or the + write operation completes. + + @subsection rd_tf Read Transfer + + To read a single block of data within the eMMC device in non-blocking mode, + call `MMC_single_block_read_nb()`. The user must specify the following + information: + + - The CoreMMC hardware instance to which the required eMMC device is + connected. + - The sector address within the eMMC device where the requested data is + located. + - The base address of the 512-byte buffer where the data read from the eMMC + device is stored. + + To check the status of the read transfer, use `MMC_status()`. + Alternatively, use `MMC_single_block_read()` to perform a single block read + operation in blocking mode. This function waits until an error occurs, or the + read operation completes. + + @section mmc_mb_tf_ctrl MMC Multiple Block Transfer Control + + @subsection multi_blk_wr_tf Multiple Block Write Transfer + + To write multiple blocks of data to the eMMC device, call + `MMC_multi_block_write()`. The user must specify the following information: + + - The CoreMMC hardware instance to which the required eMMC device is + connected. + - The destination sector address on the eMMC device + - The number of sectors to write. + - The current system ticks. + - The number of system ticks before a timeout occurs. + + To check the status of the read operation, call `MMC_status()`. + The source data is assumed to be present in or in the process of being + transferred (using the DMA) to the CoreMMC FIFO. + The maximum size that can be transferred per function call is limited by the + maximum size of the CoreMMC FIFO buffer that is instantiated. + Currently the maximum possible size is 32 KB. + + @subsection multi_blk_rd_tf Multiple Block Read Transfer + + To read multiple blocks of data stored within the eMMC device, call + `MMC_multi_block_read()`. The user must specify the following information: + + - The CoreMMC hardware instance to which the required eMMC device is + connected. + - The sector address within the eMMC device where the requested data is + located. + - The number of blocks to read. + - The current system ticks. + - The number of system ticks before a timeout occurs. + + To check the status of the read operation, call `MMC_status()`. + If the read operation is successful, the received data is stored in the + Read FIFO. + The size of the read is limited to the maximum size of the CoreMMC FIFO + buffer, that is instantiated. Currently, the maximum possible size is 32 KB. + */ + +#ifndef __CORE_MMC_H +#define __CORE_MMC_H + +#ifndef LEGACY_DIR_STRUCTURE +#include "hal/cpu_types.h" + +#else +#include "cpu_types.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + The `mmc_transfer_status_t` enumeration indicates the return status of the + MMC data transfer. + A variable of this type is returned by the `MMC_init()`, `MMC_status()`, + `MMC_single_block_write()`, `MMC_single_block_read()`, + `MMC_single_block_write_nb()`, `MMC_single_block_read_nb()`, + `MMC_multi_block_write()`, and `MMC_multi_block_read()` functions. + + - `MMC_TRANSFER_FAIL`: A failure during block data transfer. This response + indicates + that an error is detected within the eMMC device's card status register. + + - `MMC_TRANSFER_SUCCESS`: Successful MMC block data transfer. This response + indicates that the data transfer has been completed successfully and that the + driver is ready to proceed with subsequent transfers. + + - `MMC_INIT_SUCCESS`: Initialization of the driver, the CoreMMC hardware, and + the eMMC device is completed successfully. + + Note: No other driver functions can be called until `MMC_init()` has returned + this response. + + - `MMC_DWIDTH_ERR`: MMC data width configuration error. + This response indicates that the MMC card is unreachable and may be in a + lockdown state, having previously received an invalid command. + + - `MMC_RCA_ERROR`: An error occurred while assigning a relative card address + to the eMMC device. + This response indicates that multiple devices are connected to the same + CoreMMC hardware instance, which is not supported by the driver. + + - `MMC_CID_RESP_ERR`: An error occurred while reading the card identification + register. + This response indicates that the MMC device entered Lockdown mode due to + incompatibility issues. + + - `MMC_OP_COND_ERR`: An error occurred while specifying the interface + operating conditions for the eMMC device. + + Note: The device is now in Lockdown mode and may only be recovered by + performing a hard reset of the system. + + - `MMC_RESET_ERR`: An error occurred during the reset cycle of the + initialization. + This response indicates that the device is incorrectly wired to the CoreMMC + hardware instance. + + - `MMC_CRC_ERR`: A CRC error is detected on the MMC interface during a block + write or block read data transfer. + + - `MMC_FIFO_N_EMPTY`: This response indicates the presence of data in the + hardware FIFO prior to calling the `MMC_single_block_read()` function. + + - `MMC_FIFO_N_EMPTY`: This response indicates the presence of data in the + hardware FIFO prior to calling the `MMC_single_block_read()` function. + + - `MMC_READ_CTRL_ERR`: A control error occurred during a data read transfer. + This response indicates that a FIFO overflow occurred or that there is a + start/stop bit error on the MMC command interface. + + - `MMC_CLK_DIV_ERR`: An error occurred while configuring the device to + operate in high-speed mode. + This response indicates that the device does not support the MMC clock rate + configured or that it does not have a high-speed mode configuration setting in + the EXT CSD register. + */ + +typedef enum mmc_transfer_status_ { + MMC_TRANSFER_FAIL = 0u, + MMC_TRANSFER_SUCCESS = 1u, + MMC_INIT_SUCCESS = 2u, + MMC_DWIDTH_ERR = 3u, + MMC_RCA_ERROR = 4u, + MMC_CID_RESP_ERR = 5u, + MMC_OP_COND_ERR = 6u, + MMC_RESET_ERR = 7u, + MMC_CRC_ERR = 8u, + MMC_FIFO_N_EMPTY = 9u, + MMC_READ_CTRL_ERR = 10u, + MMC_CLK_DIV_ERR = 11u, /**< Busy, wait a while*/ + MMC_CMD_PROCESSING = 12u, /**< Multiblock-write fail*/ + MMC_MBW_FAIL = 13u, /**< Multiblock-read fail*/ + MMC_MBR_FAIL = 15u, /**< Single block-write fail*/ + MMC_SBW_FAIL = 16u, /**< Single block-read fail*/ + MMC_SBR_FAIL = + 17u, /**< Application timeout- Passed timeout set by application*/ + MMC_APP_TIMEOUT_FAIL = 18u, /**< Interrupt status error*/ + MMC_INT_STATUS_FAIL = + 19u, /**< Application has over or under filled the FIFO*/ + MMC_USER_ERROR_FAIL = 20u, /**< Interrupt flags showed error*/ + MMC_OTHER_FAIL = 21u, /**< CoreMMC timed out when sending or receiving*/ + MMC_TIMOUT_FAIL = + 22u, /**< Hardware revision of CoreMMC not supported by this driver*/ + MMC_UNSUPPORTED_HW_REVISION = 23u, /**< No response to command from eMMC*/ + MMC_NO_RESPONSE_FAIL = 24u +} mmc_transfer_status_t; + +/** + States used in the CoreMMC interrupt state machine. + */ +typedef enum mmc_state_ { + MMC_ISR_MW_PARSE_CMD13, + MMC_ISR_MW_PARSE_CMD23, + MMC_ISR_MW_PARSE_CMD25, + MMC_ISR_MW_END, + MMC_ISR_SW_PARSE_CMD13, + MMC_ISR_SW_PARSE_CMD24, + MMC_ISR_SW_END, + MMC_ISR_MR_PARSE_CMD13, + MMC_ISR_MR_PARSE_CMD23, + MMC_ISR_MR_PARSE_CMD18, + MMC_ISR_MR_END, + MMC_ISR_SR_PARSE_CMD13, + MMC_ISR_EXT_CSD_PARSE_CMD13, + MMC_ISR_SR_PARSE_CMD17, + MMC_ISR_SR_END, + MMC_ISR_STOP_TRANSMISSION, + MMC_ISR_ERROR, + MMC_ISR_IDLE +} mmc_state; + +/** + ## READ_SEND_EXT_CSD + + A number outside of the normal range is used to instruct the read block + routine to read the extended CSD register. + */ +#define READ_SEND_EXT_CSD 0xFFFFFFFFU + +/** + The `mmc_status` structure contains status information relating to current + Firmware driver operations and the particular instance of CoreMMC. + + - `int_state`: contains the current state of the CoreMMC interrupt state + machine. + - `no_of_blks`: number of blocks to either send or receive in the current + command. + - `blk_addr`: block to start read/write from. + - `start_systick`: set at start to enable timeout. + - `timeout_value_ticks`: in systicks. + - `err_state`: error report from interrupt. + - `temp_err_state`: temporary store for error reports from interrupt. + */ + +typedef struct mmc_status_ { + mmc_state int_state; + uint16_t no_of_blks; + uint32_t blk_addr; + uint32_t start_systick; + uint32_t timeout_value_ticks; + mmc_transfer_status_t err_state; + mmc_transfer_status_t temp_err_state; +} mmc_status; + +/** + The `mmc_instance_t` structure stores information on each instance of CoreMMC + hardware in your system. + Your application software must declare one instance of this structure for + each instance of CoreMMC in your system. + + `MMC_init()` initializes this structure. + A pointer to an initialized instance of this structure must be passed as the + first parameter to the CoreMMC driver functions. + This identifies which CoreMMC hardware instance will perform the requested + operation. + + - `base_addr`: `base_addr` represents the base address in the processor's + memory map of the registers for this CoreMMC instance. + + - `hw_sec_count`: `hw_sec_count` is the sector count read from the eMMC. + It is the maximum sector count supported by the particular eMMC device. + The device has a size of `hw_sec_count` * 512. + + - `sector_size`: `sector_size` indicates the size of the memory sectors + (in Bytes) within the eMMC device. + All operations must be carried out on entire sectors. + + Note 1: This driver only supports 512-byte sector size. + + Note 2: `sector_size` is maintained within the driver and must + not be modified by the user application. + + - `access_mode`: `access_mode` indicates the memory addressing system + (Byte or Sector Addressing) used within the eMMC device. + This driver only supports sector access. + + Note: `access_mode` is maintained within the driver and must + not be modified by the user application. + + - `hw_fifo_depth`: `hw_fifo_depth` indicates the size of the FIFO + instantiated within this instance of the CoreMMC hardware. + + Note: `hw_fifo_depth` is maintained within the driver and must + not be modified by the user application. + + - `hw_mmc_dwidth`: `hw_mmc_dwidth` denotes the width of the MMC interface + connected between the eMMC device and this instance of the CoreMMC hardware. + + Note 1: This driver automatically configures the device and hardware to drive + the width of the bus connected, for example a 1-bit, 4-,bit or 8-bit + interface. + + Note 2: `hw_mmc_dwidth` is maintained within the driver and must + not be modified by the user application. + + - `clk_rate_div`: `clk_rate_div` internally sets the clock rate of the MMC + interface. + + Note: `clk_rate_div` is maintained within the driver and must + not be modified by the user application. + + - `init_clk_rate_div`: `init_clk_rate_div` internally sets the initialization + clock rate of the MMC interface. + + Note: `init_clk_rate_div` is maintained within the driver and + must not be modified by the user application. + + - `hw_hs_timing`: `hw_hs_timing` contains contents read from the extended CSD + register's `HS_TIMING` byte. It has a default value of 0x00 and is set to + 0x01 when the eMMC device operates in high-speed mode. + */ + +typedef struct mmc_instance { + addr_t base_addr; + uint32_t hw_sec_count; + uint8_t hw_ext_csd_rev; /**< This contains the extended CSD rev read during + eMMC initialisation*/ + uint8_t hw_card_type; /**< This contains the cared type read from the extended + CSD register*/ + uint16_t sector_size; + uint8_t access_mode; + uint8_t hw_fifo_depth; + uint8_t hw_maj_revision; /**< This is the major version of CoreMMC. Only + present in Cores >= 3.00*/ + uint8_t hw_min_revision; /**< This is the minor version of CoreMMC. Only + present in Cores >= 3.00 >= 3.0*/ + uint8_t hw_mmc_dwidth; + uint8_t clk_rate_div; + uint8_t init_clk_rate_div; + mmc_status status; /**< Firmware_status*/ + uint32_t data_timeout; /**< Sets the length in cycles before timeout occurs*/ + uint8_t hw_hs_timing; +} mmc_instance_t; + +/** + The `mmc_params_t` structure passes configuration parameters to `MMC_init()`. + Your application software must declare one instance of this structure for + each instance of CoreMMC in your system. `MMC_param_config()` initializes + this structure. To configure the driver for your specific application, + provide custom configuration parameters by passing a pointer to an + initialized instance of this structure to `MMC_init()`. + + - `clk_rate_div`: `clk_rate_div` assigns a default MMC clock + rate of HCLK/2. + If this MMC clock rate is not suitable for the custom application, then + `clk_rate_div` can be modified to be to any value in the range of 0-255, + giving an MMC clock rate range of HCLK/2 to HCLK/512. The relationship + between the MMC clock rate, HCLK, and `clk_rate_div` is as follows: MMC Clock + = HCLK/(2(`clk_rate_div` + 1)) + + - `init_clk_rate_div`: The maximum clock frequency on startup is 400 KHz. + `init_clk_rate_div` of this structure assigns a default MMC clock + initialization rate of HCLK/128. If this MMC clock rate is not suitable for + the custom application, then `init_clk_rate_div` can be modified to be to any + value in the range of 0-255, giving an MMC initialization clock rate range of + HCLK/2 to HCLK/512. The relationship between the MMC clock rate, HCLK, and + `init_clk_rate_div` is as follows: MMC Clock = HCLK/(2(`init_clk_rate_div` + + 1)) + + - `valid_param_config`: `valid_param_config` is maintained within the driver + and must not be modified by the user application. + */ + +typedef struct mmc_params { + uint8_t clk_rate_div; + uint8_t init_clk_rate_div; + uint8_t valid_param_config; + uint32_t data_timeout; /**< Sets the length in cycles before timeout occurs */ +} mmc_params_t; + +/*------------------------Public Functions------------------------------------*/ + +/** + `MMC_param_config()` assigns default values to the members of the + `mmc_params_t` structure. This function must be called prior to calling + `MMC_init()`. By default, this function sets the clock divider rate to 1/2 + times that of HCLK. + + @param this_mmc_params + The `this_mmc_params` parameter is a pointer to the `mmc_params_t` structure, + which holds the parameters required to initialize this instance of CoreMMC. + This parameter must point to the instance of this structure, defined within + the application code. + + @return + This function does not return any value. + */ +void MMC_param_config(mmc_params_t *this_mmc_params); + +/** + `MMC_init()` initializes: + + - The data structures of a CoreMMC instance, referenced by the `this_mmc` + parameter. + - The CoreMMC hardware. + - The eMMC device connected to the MMC interface of the CoreMMC hardware + instance. + + This function must be called for each CoreMMC instance with a unique this_mmc + parameter and `base_address` parameter combination. + `MMC_init()` must be called prior to calling any MMC data transfer functions, + and must be preceded by calling the `MMC_param_config()` function. + + @param this_mmc + The `this_mmc` parameter is a pointer to the `mmc_instance_t` data structure + holding all data related to the specific CoreMMC hardware instance. + + @param base_address + The `base_address` parameter specifies the base address in the processor's + memory map for the registers of the CoreMMC instance being initialized. + + @param this_mmc_params + The `this_mmc_params` parameter is a pointer to the `mmc_params_t` structure, + holding the parameters required for the initial configuration of the CoreMMC + instance. + + @return + This function returns the initialization status of the MMC device of + the `mmc_transfer_status_t` type. + + @example + This example shows how to initialize the device and configure the MMC data + rate to 1/10 of HCLK. + + @code + #define MMC0_BASE_ADDR 0x30000000u + + mmc_instance_t g_mmc0; + mmc_params_t g_mmc0_param; + mmc_transfer_status_t resp_reg; + + MMC_param_config(&g_mmc0_param); + + // Set the MMC clock rate to 1/10 HCLK. + g_mmc0_param.clk_rate_div = 4u; + resp_reg = MMC_init(&g_mmc0, MMC0_BASE_ADDR, &g_mmc0_param); + if(MMC_INIT_SUCCESS == resp_reg) + { + ... + } + @endcode + */ + +mmc_transfer_status_t MMC_init(mmc_instance_t *this_mmc, addr_t base_address, + const mmc_params_t *this_mmc_params); + +/** + `MMC_get_fifo_write_address()` returns the address of the Write + FIFO data register of the CoreMMC. + This is required when setting up DMA to write to the FIFO. + It can also be used to write to the FIFO using a pointer in preference to the + HAL register access macros, which are slower. + + @param this_mmc + The `this_mmc` parameter is a pointer to the `mmc_instance_t` data structure + holding all data related to a specific CoreMMC hardware instance. + + @return + This function returns the address of the CoreMMC Write FIFO. + + @example + This example shows how to use `MMC_get_fifo_write_address()`. + + @code + uint8_t *pFifoData; + ... + pFifoData = (uint8_t *)MMC_get_fifo_write_address(&g_emmc_1); + MMC_init_fifo( &g_emmc_1); + iCount= no_blocks * size_of_block; + while(iCount--) + { + pFifoData = storage[iCount]; + } + @endcode + */ +addr_t MMC_get_fifo_write_address(mmc_instance_t *this_mmc); + +/** + `MMC_get_fifo_read_address()` returns the address of Read FIFO data register + for the CoreMMC. + This is required when setting up DMA to read from the FIFO. + It can also be used to read from FIFO using a pointer in preference to the + HAL register access macros, which are slower. + + @param this_mmc + The `this_mmc` parameter is a pointer to the `mmc_instance_t` data structure + holding all data related to a specific CoreMMC hardware instance. + + @return + This function returns the address of the CoreMMC Read FIFO. + + @example + This example shows how to use `MMC_get_fifo_read_address()`. + + @code + uint8_t *pFifoData; + ... + pFifoData = (uint8_t *)MMC_get_fifo_read_address(&g_emmc_1); + iCount= no_blocks *size_of_block; + while(iCount--) + { + storage[iCount] = *pFifoData; ..fills memory from FIFO + } + @endcode + */ +addr_t MMC_get_fifo_read_address(mmc_instance_t *this_mmc); + +/** + `MMC_status()` returns the current state of the read/write process initiated + by any of the non-blocking functions. The non-blocking functions, which can + be used to start a read/write transfer are `MMC_multi_block_write()`, + `MMC_single_block_write_nb()`, `MMC_single_block_read_nb()`, + `MMC_multi_block_write()`, and `MMC_multi_block_read()`. + + @param this_mmc + The `this_mmc` parameter is a pointer to the `mmc_instance_t` data structure + holding all data related to a specific CoreMMC hardware instance. + + @param current_timeout_ticks + The `current_timeout_ticks` parameter passes the current tick in milliseconds + to the function to allow it determine if operation timed out. + + @return + This function returns the transfer status of the `mmc_transfer_status_t` + type. + + @example + This example shows how to use `MMC_status()` with `MMC_multi_block_write()` + command. + + @code + ... + ret_var = MMC_multi_block_write( &g_emmc_1, no_blocks, sect_addr, timeout); + while(MMC_CMD_PROCESSING == MMC_status(&g_emmc_1)) + { + ; // wait for MMC_multi_block_write() to finish + // Could do other stuff here + } + if(MMC_TRANSFER_SUCCESS != MMC_status(&g_emmc_1, )) + { + // handle the error + } + else + { + // write success, continue on + } + @endcode + */ +mmc_transfer_status_t MMC_status(mmc_instance_t *this_mmc, + uint32_t current_timeout_ticks); + +/** + `MMC_init_fifo()` clears the content of both the Read and Write FIFOs, and + returns them to their reset state. `MMC_init_fifo()` must be called before + transferring data into the Read FIFO or Write FIFO, when using the + non-blocking read and write functions. The non-blocking functions are + `MMC_single_block_write_nb()`, `MMC_single_block_read_nb()`, + `MMC_multi_block_write()`, and `MMC_multi_block_read()`. + + @param this_mmc + The `this_mmc` parameter is a pointer to the `mmc_instance_t` data structure + holding all data related to a specific CoreMMC hardware instance. + + @return + This function does not return a value. + + @example + This example shows how to use `MMC_init_fifo()` when filling FIFO. + + @code + uint8_t *pFifoData; + ... + pFifoData = (uint8_t *)MMC_get_fifo_write_address(&g_emmc_1); + MMC_init_fifo( &g_emmc_1); // makes sure FIFO is empty before starting + iCount= no_blocks *size_of_block; + while(iCount--) + { + *pFifoData = storage[iCount]; + } + @endcode + */ + +void MMC_init_fifo(const mmc_instance_t *this_mmc); + +/** + `MMC_single_block_write()` transmits a single block of data from the MMC host + to the eMMC device. This function writes the block of data into the hardware + FIFO, transfers the data from the hardware FIFO to the eMMC device, and waits + for write operation to complete. The size of the block of data transferred by + this function is set to 512 bytes, which is the standard sector size for all + eMMC devices with a capacity of greater than 2 GB. + + Note 1: This driver does not support devices with a capacity of less than 2 + GB. + + Note 2: This function is a blocking function, it does not return until the + write operation is successful or an error occurs. + + @param this_mmc + The `this_mmc` parameter is a pointer to the `mmc_instance_t` data structure + holding all data related to a specific CoreMMC hardware instance. + + @param src_addr + The `src_addr` parameter is a pointer to a buffer containing the data that is + stored in the eMMC device. + The buffer must be a minimum size of 512 bytes. + + Note: If the buffer is not declared with adequate size, then the eMMC sector + may be loaded with erroneous data. + + @param dst_addr + The `dst_addr` parameter specifies the sector address in the MMC device where + the block must be stored. + + Note: For eMMC devices of greater than 2 GB in size this address refers to a + 512-byte sector. + + @return + This function returns a value of the `mmc_transfer_status_t` type which + specifies the transfer status of the operation. + + @example + This example shows how to initialize the device and perform a single block + transfer. + + @code + #define MMC0_BASE_ADDR 0x30000000u + #define BLOCK_1 0x00000001u + + mmc_instance_t g_mmc0; + mmc_params_t g_mmc0_param; + + mmc_transfer_status_t resp_reg; + uint8_t data_buffer[512] = {0u}; + + MMC_param_config(&g_mmc0_param); + NVIC_EnableIRQ(FabricIrq0_IRQn); + + Set the MMC clock rate to 1/10 HCLK. + g_mmc0_param.clk_rate_div = 4u; + resp_reg = MMC_init(&g_mmc0, MMC0_BASE_ADDR, &g_mmc0_param); + if(MMC_INIT_SUCCESS == resp_reg) + { + resp_reg= MMC_single_block_write(&g_mmc0, + data_buffer, + BLOCK_1); + if(MMC_TRANSFER_SUCCESS == resp_reg) + { + ... + } + } + @endcode + */ +mmc_transfer_status_t MMC_single_block_write(const mmc_instance_t *this_mmc, + const uint32_t *src_addr, + uint32_t dst_addr); + +/** + `MMC_single_block_write_nb()` transfers a single block + of data from the MMC host to the eMMC device. This function requires the + program to fill the CoreMMC Write FIFO with the correct amount of data in + advance. This can be achieved by doing a memory copy before calling this + function, or setting up a DMA transfer before calling this function. + `MMC_single_block_write_nb()` initiates the transfer of the data + from the Write FIFO to the eMMC device. + The size of the block of data transferred by this function is set to 512 + bytes, which is the standard sector size for all eMMC devices with a + capacity of greater than 2 GB. + To check the status of a read operation, call `MMC_status()`. + + Note 1: This driver does not support devices with a capacity of less than 2 + GB. + + Note 2: `MMC_init_fifo()` must be used called to clear the Write FIFO before + writing any data to the FIFO and before calling this function. + + Note 3: This function is a non-blocking function, it returns immediately + after initiating the write transfer. + + @param this_mmc + The `this_mmc` parameter is a pointer to the `mmc_instance_t` data structure + holding all data related to a specific CoreMMC hardware instance. + + @param dst_addr + The `dst_addr` parameter specifies the sector address in the MMC device where + the block must be stored. + + Note: For eMMC devices of greater than 2 GB in size, this address refers to a + 512-byte sector. + + @param current_timeout_ticks + The `current_timeout_ticks` parameter specifies the current tick in + milliseconds. + + @param timeout_ticks + This `timeout_ticks` parameter specifies the timeout abort value, in + milliseconds. + + @return + This function returns a value of the `mmc_transfer_status_t` type which + specifies the transfer status of the operation. + + @example + This example shows how to initialize the device and perform a single block + transfer. + + + @code + MMC_init_fifo(&g_emmc_1) + fill the FIFO - from g_buffer_a to FIFO + iCount= 512/4; + while(iCount--) + { + *pFifoData = p_tx_buff[iCount]; + } + ret_var = MMC_single_block_write_nb( &g_emmc_1, sect_addr, + current_timeout_ticks, timeout); + while(MMC_CMD_PROCESSING == MMC_status(&g_emmc_1,GetTickCount())) + { + ; + } + if(MMC_TRANSFER_SUCCESS != MMC_status(&g_emmc_1,GetTickCount())) + { + ..handle the error + } + else + { + ..success- continue + } + @endcode + */ + +mmc_transfer_status_t MMC_single_block_write_nb(mmc_instance_t *this_mmc, + uint32_t dst_addr, + uint32_t current_timeout_ticks, + uint32_t timeout_ticks); + +/** + `MMC_multi_block_write()` transfers multiple blocks of data from the MMC host + to the eMMC device. This function requires the program to fill the CoreMMC + Write FIFO with the correct amount of data in advance. This can be achieved + by doing a memory copy before calling this function, or setting up a DMA + transfer before calling this function. The size of the individual block of + data transferred by this function is set to 512 bytes, which is the standard + sector size for all eMMC devices with a capacity of greater than 2 GB. To + check the status of a read operation, call `MMC_status()`. + + Note 1: This driver does not support devices with a capacity of less than 2 + GB. + + Note 2: `MMC_init_fifo()` must be called to clear the Write FIFO before + writing any data to the FIFO and before calling this function. + + Note 3: This function is a non-blocking function and returns immediately + after initiating the transfer. + + @param this_mmc + The `this_mmc` parameter is a pointer to the `mmc_instance_t` data structure + holding all data related to a specific CoreMMC hardware instance. + + @param no_of_blks + The `no_of_blks` parameter specifies the number of contiguous blocks to + transfer. + + @param dst_addr + The `dst_addr` parameter specifies the sector address in the MMC device where + the block must be stored. + + Note: For eMMC devices of greater than 2 GB in size, this address refers to a + 512-byte sector. + + @param current_timeout_ticks + The `current_timeout_ticks` parameter specifies the current tick in + milliseconds. + + @param timeout_ticks + The `timeout_ticks` parameter specifies the timeout abort value in + milliseconds. + + @return + This function returns a value of the `mmc_transfer_status_t` type which + specifies the transfer status of the operation. + + @example + This example shows how to initialize the device and perform a multiple block + transfer. + + @code + ... + pFifoData = (uint32_t *)MMC_get_fifo_write_address(&g_emmc_1); + MMC_init_fifo( &g_emmc_1); -> make sure FIFO empty + + iCount= no_blocks * 512; + while(iCount--) -> fill the FIFO - from g_buffer_a to FIFO + { + pFifoData = g_write_data_buff[iCount]; note:in this case filling in + reverse + } + ret_var = MMC_multi_block_write( &g_emmc_1, no_blocks, sect_addr, + GetTickCount(), TEN_SEC_TIMEOUT); + while(MMC_CMD_PROCESSING == ret_var) + { + ret_var = MMC_status(&g_emmc_1,GetTickCount()); + } + + if(MMC_TRANSFER_SUCCESS != MMC_status(&g_emmc_1,GetTickCount())) + { + ... handle the error + } + else + { + ... success + } + @endcode + */ +mmc_transfer_status_t MMC_multi_block_write(mmc_instance_t *this_mmc, + uint16_t no_of_blks, + uint32_t dst_addr, + uint32_t current_timeout_ticks, + uint32_t timeout_ticks); + +/** + `MMC_single_block_read` reads a single block of data from the eMMC slave to + the FIFO on the CoreMMC. The function configures the CoreMMC Read FIFO + hardware to receive a block of data, instructs the eMMC device to transmit + the data stored at the sector pointed to by the `src_addr` parameter, and + waits to complete the read operation. The size of the block of data + transferred by this function is set to 512 bytes, which is the standard + sector size for all eMMC devices with a capacity of greater than 2 GB. + + Note 1: This driver does not support devices with a capacity of less than 2 + GB. + + Note 2: This function is a blocking function, it does not return until the + read operation is successful or an error occurs. + + @param this_mmc + The `this_mmc` parameter is a pointer to the `mmc_instance_t` data structure + holding all data related to a specific CoreMMC hardware instance. + + @param src_addr + The `src_addr` parameter specifies the sector address in the MMC device where + the 512-byte block of data is read from. + + @param dst_addr + The `dst_addr` parameter is a pointer to a buffer where the data read from + the eMMC device is stored. The buffer must be a minimum size of 512 bytes. + + Note: If the buffer is not declared with adequate size, the response data + read-back from the eMMC device overflows from the receive buffer and causes + its storage to become unpredictable + + @return + This function returns a value of the `mmc_transfer_status_t` type which + specifies the transfer status of the operation. + + @example + This example shows how to initialize the device, perform a single block + transfer, and read back the data from the sector written to within the eMMC + device. + + @code + #define MMC0_BASE_ADDR 0x30000000u + #define BLOCK_1 0x00000001u + + mmc_instance_t g_mmc0; + mmc_params_t g_mmc0_param; + + mmc_transfer_status_t resp_reg; + uint8_t tx_data_buffer[512] = {0u}; + uint8_t rx_data_buffer[512] = {0u}; + + MMC_param_config(&g_mmc0_param); + + Set the MMC clock rate to 1/10 HCLK. + g_mmc0_param.clk_rate_div = 4u; + + resp_reg = MMC_init(&g_mmc0, MMC0_BASE_ADDR, &g_mmc0_param); + if(MMC_INIT_SUCCESS == resp_reg) + { + resp_reg= MMC_single_block_write(&g_mmc0, + tx_data_buffer, + BLOCK_1); + if(MMC_TRANSFER_SUCCESS == resp_reg) + { + resp_reg = MMC_single_block_read(&g_mmc0, + BLOCK_1, + rx_data_buffer); + if(MMC_TRANSFER_SUCCESS == resp_reg) + { + ... + } + } + } + + @endcode + */ +mmc_transfer_status_t MMC_single_block_read(const mmc_instance_t *this_mmc, + uint32_t src_addr, + uint32_t *dst_addr); + +/** + `MMC_single_block_read_nb()` reads a single block of data from the eMMC + device to the MMC host. The function configures the CoreMMC Read FIFO to + receive a block of data and instructs the eMMC device to transmit the data + stored at the sector pointed to by the `src_addr` parameter. The size of the + block of data transferred by this function is set to 512 bytes, which is the + standard sector size for all eMMC devices with a capacity of greater than 2 + GB. To check the status of a read operation, call `MMC_status()`. If read + operation is successful, the received data is stored in the Read FIFO. You + must call `MMC_get_fifo_read_address()` to get the address of the Read FIFO + data register and copy the data from the Read FIFO register to a buffer after + the read operation has completed. + + Note 1: This driver does not support devices with a capacity of less than 2 + GB. + + Note 2: `MMC_init_fifo()` must be used to clear the FIFO before calling this + function. + + Note 3: This function is a non-blocking function and returns immediately + after initiating the read transfer. + + @param this_mmc + The `this_mmc` parameter is a pointer to the `mmc_instance_t` data structure + holding all data related to a specific CoreMMC hardware instance. + + @param src_addr + The `src_addr` parameter specifies the sector address in the eMMC device + where the 512-byte block of data is read from. + + Note: For eMMC devices of greater than 2 GB in size, this address refers to a + 512-byte sector. + + @param current_timeout_ticks + The `current_timeout_ticks` parameter specifies the current tick in + milliseconds. + + @param timeout_ticks + This `timeout_ticks` parameter specifies the timeout abort value in + milliseconds. + + @return + This function returns a value of the `mmc_transfer_status_t` type which + specifies the transfer status of the operation. + + @example + This example shows how to initialize the device, perform a single block + transfer, and read back the data from the sector written to within the eMMC + device. + + @code + MMC_init_fifo( &g_emmc_1); + ret_var = MMC_single_block_read_nb( &g_emmc_1, sect_addr , + GetTickCount(), TEN_SEC_TIMEOUT); + if(MMC_CMD_PROCESSING == ret_var) + { + while(MMC_CMD_PROCESSING == MMC_status(&g_emmc_1,GetTickCount())) + { + ; + } + if(MMC_TRANSFER_SUCCESS == MMC_status(&g_emmc_1,GetTickCount())) + { + empty the FIFO - + pFifoData = (uint8_t *)MMC_get_fifo_read_address(&g_emmc_1); + while( size_of_transfer--) + { + g_write_data_buff[temp_compare] = *pFifoData; + } + } + else + { + + .. handle the error + } + } end of processing- command finished + @endcode + */ +mmc_transfer_status_t MMC_single_block_read_nb(mmc_instance_t *this_mmc, + uint32_t src_addr, + uint32_t current_timeout_ticks, + uint32_t timeout_ticks); + +/** + `MMC_multi_block_read()` reads multiple contiguous blocks of data from the + eMMC + device to the MMC host. + The function configures the CoreMMC Read FIFO to receive multiple blocks of + data, + and instructs the eMMC device to transmit the data pointed to by the src_addr + parameter. + The size of the individual blocks of data transferred by this function is + set to 512 Bytes, which is the standard sector size for all eMMC devices + with a capacity of greater than 2 GB. + To check the status of a read operation, call `MMC_status()`. + If the read operation is successful, the received data is stored in the Read + FIFO. + You must call `MMC_get_fifo_read_address()` to get the address of the Read + FIFO + data register and copy the data from the Read FIFO to a buffer after the read + operation has completed. + + Note 1: This driver does not support devices with a capacity of less than 2 + GB. + + Note 2: `MMC_init_fifo()` must be called to clear FIFO before calling this + function. + + Note 3: This function is a non-blocking function, it returns immediately + after + initiating the read transfer. + + @param this_mmc + The `this_mmc` parameter is a pointer to the `mmc_instance_t` data structure + holding all data related to a specific CoreMMC hardware instance. + + @param no_of_blks + The `no_of_blks` parameter specifies the number of contiguous blocks to + transfer. + + Note: An error is returned if `src_addr` + `no_of_blocks` > end address of + eMMC + device. + + @param src_addr + The `src_addr` parameter specifies the sector address in the MMC device where + the 512-byte block of data is read from. + + Note: For eMMC devices of greater than 2 GB in size, this address refers to a + 512-byte sector. + + @param current_timeout_ticks + The `current_timeout_ticks` parameter specifies the current tick in + milliseconds + + @param timeout_ticks + This `timeout_ticks` parameter specifies the timeout abort value, entered in + system ticks. + + @return + This function returns a value of the `mmc_transfer_status_t` type which + specifies + the transfer status of the operation. + + @example + This example shows how to initialize the device and perform a multiple block + read operation. + + @code + MMC_init_fifo( &g_emmc_1); + ret_var = MMC_multi_block_read( &g_emmc_1, no_blocks, sect_addr, + GetTickCount(), TEN_SEC_TIMEOUT); + if(MMC_CMD_PROCESSING == ret_var) + { + while(MMC_CMD_PROCESSING == MMC_status(&g_emmc_1,GetTickCount())) + { + ; + } + if(MMC_TRANSFER_SUCCESS == MMC_status(&g_emmc_1,GetTickCount())) + { + empty the FIFO - + pFifoData = (uint8_t *)MMC_get_fifo_read_address(&g_emmc_1); + while( index < size_of_transfer) + { + p_rx_buff[index] = *pFifoData; + } + } + else + { + handle the error + } + } end of processing- command finished + @endcode + */ +mmc_transfer_status_t MMC_multi_block_read(mmc_instance_t *this_mmc, + uint16_t no_of_blks, + uint32_t src_addr, + uint32_t current_timeout_ticks, + uint32_t timeout_ticks); + +/** + `MMC_isr()` controls the eMMC data transfer operations within the + driver for the non-blocking read and write functions. + It must be called from the HAL interrupt handler associated with the + interrupt line connected to the CoreMMC instance in the Libero hardware + design. This interrupt handler must be instantiated in the application + program. + + @param this_mmc + The `this_mmc` parameter is a pointer to the `mmc_instance_t` data structure + holding all data related to a specific CoreMMC hardware instance. + + @return + This function does not return a value. + + @example + This example shows `MMC_isr()` function being called from HAL Interrupt + handler, which is in user application. + + @code + void FabricIrq0_IRQHandler(void) // Interrupt from Libero design. + { + MMC_isr(&g_emmc_1); + } + @endcode + */ +void MMC_isr(mmc_instance_t *this_mmc); + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_MMC_H */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc_cmd_defs.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc_cmd_defs.h new file mode 100644 index 0000000..cd5fff8 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc_cmd_defs.h @@ -0,0 +1,102 @@ +/** + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core_mmc_cmd_defs.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreMMC interface level header file + * + * This MMC Interface header file provides a subset of definitions from the eMCC + * protocol JESD84-A441 + * + */ + +#ifndef __CORE_MMC_CMD_DEFD_H +#define __CORE_MMC_CMD_DEFD_H + +#ifndef LEGACY_DIR_STRUCTURE +#include "hal/cpu_types.h" + +#else +#include "cpu_types.h" +#endif + +#ifdef __cplusplus +extern "C" +#endif + +/** + * MMC commands + */ +#define MMC_CMD_15_GOTO_INACTIVE_STATE 15u /** No Rsp */ +#define MMC_CMD_4_SET_DSR 4u /** No Rsp */ +#define MMC_CMD_0_GO_IDLE_STATE 0u /** No Rsp */ +#define MMC_CMD_6_SWITCH 6u /** R1b Rsp */ +#define MMC_CMD_7_SELECT_DESELECT_CARD 7u /** R1/R1b Rsp */ + +#define MMC_CMD_3_SET_RELATIVE_ADDR 3u /** R1 Rsp */ +#define MMC_CMD_17_READ_SINGLE_BLOCK 17u /** R1 Rsp */ +#define MMC_CMD_18_READ_MULTIPLE_BLOCK 18u /** R1 Rsp */ +#define MMC_CMD_24_WRITE_SINGLE_BLOCK 24u /** R1 Rsp */ +#define MMC_CMD_23_SET_BLOCK_COUNT 23u /** R1 Rsp */ +#define MMC_CMD_25_WRITE_MULTI_BLOCK 25u /** R1 Rsp */ +#define MMC_CMD_13_SEND_STATUS 13u /** R1 Rsp */ +#define MMC_CMD_12_STOP_TRANSMISSION 12u /** R1/R1b Rsp */ +#define MMC_CMD_8_SEND_EXT_CSD 8u /** R1 Rsp */ + +#define MMC_CMD_2_ALL_SEND_CID 2u /** R2 Rsp */ +#define MMC_CMD_9_SEND_CSD 9u /** R2 Rsp */ +#define MMC_CMD_10_SEND_CID 10u /** R2 Rsp */ +#define MMC_CMD_1_SEND_OP_COND 1u /** R3 Rsp */ +#define MMC_CMD_39_FAST_IO 39u /** R4 Rsp */ +#define MMC_CMD_40_GO_IRQ_STATE 40u /** R5 Rsp */ + + typedef enum R1_CARD_STATUS_RESPONSE_ { + ADDRESS_OUT_OF_RANGE = 0x00000080u, /**< RR1 */ + ADDRESS_MISALIGN = 0x00000040u, + BLOCK_LEN_ERROR = 0x00000020u, + ERASE_SEQ_ERROR = 0x00000010u, + ERASE_PARAM = 0x00000008u, + WP_VIOLATION = 0x00000004u, + CARD_IS_LOCKED = 0x00000002u, + LOCK_UNLOCK_FAILED = 0x00000001u, + + COM_CRC_ERROR = 0x00008000u, /**< RR2 */ + ILLEGAL_COMMAND = 0x00004000u, + CARD_ECC_FAILED = 0x00002000u, + CC_ERROR = 0x00001000u, + GEN_ERROR = 0x00000800u, + UNDERRUN = 0x00000400u, + OVERRUN = 0x00000200u, + CSD_OVERWRITE = 0x00000100u, + + WP_ERASE_SKIP = 0x00800000u, /**< RR3 */ + RESERVED_14 = 0x00400000u, + ERASE_RESET = 0x00200000u, + CURRENT_STATE12_9_MASK = ((0x0Fu << 1) << 16), + CURRENT_STATE_RCV = + ((0x06u << 1) << 16), /**< in this state when rx data from eMMC */ + CURRENT_STATE_DATA = + ((0x05u << 1) << 16), /**< in this state when sending data to eMMC */ + CURRENT_STATE_TRAN = ((0x04u << 1) << 16), /**< ready to send/receive */ + READY_FOR_DATA = 0x00010000u, + + SWITCH_ERROR = 0x80000000u, /**< RR4 */ + URGENT_BKOPS = 0x40000000u, + APP_CMD = 0x20000000u, + RESERVED_0_4 = 0x00000000u, + } R1_CARD_STATUS_RESPONSE; + +#define R1_ERR_RESP_MASK \ + (SWITCH_ERROR | WP_ERASE_SKIP | ERASE_RESET | COM_CRC_ERROR | \ + ILLEGAL_COMMAND | CARD_ECC_FAILED | CC_ERROR | GEN_ERROR | UNDERRUN | \ + OVERRUN | CSD_OVERWRITE | ADDRESS_OUT_OF_RANGE | ADDRESS_MISALIGN | \ + BLOCK_LEN_ERROR | ERASE_SEQ_ERROR | ERASE_PARAM | WP_VIOLATION | \ + LOCK_UNLOCK_FAILED) + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_MMC_CMD_DEFD_H */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc_if.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc_if.c new file mode 100644 index 0000000..566b848 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc_if.c @@ -0,0 +1,136 @@ +/** + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core_mmc_if.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreMMC interface driver file + * + * This CoreMMC Interface driver provides functions for transferring + * configuration and programming commands to the eMMC device. Functions + * contained within the CoreMMC Interface Driver are accessed through the + * core_mmc_if.h header file. + * + */ + +#include "core_mmc_if.h" +#include "core_mmc_cmd_defs.h" +#include "core_mmc_regs.h" + +#ifndef LEGACY_DIR_STRUCTURE +#include "hal/hal.h" + +#else +#include "hal.h" +#endif + +/** + * Local Function Prototypes + */ +static cif_response_t response_1_parser(addr_t addr); + +/** + * cif_send_cmd() + * See ".h" for details of how to use this function. + */ +cif_response_t cif_send_cmd(addr_t addr, uint32_t cmd_arg, uint8_t cmd_type) { + cif_response_t ret_status = TRANSFER_IF_FAIL; + + /* Make sure flags cleared */ + HAL_set_32bit_reg(addr, ICR, CLEAR_ALL_32); + + /* Transfer the Command to the MMC device */ + send_mmc_cmd(addr, cmd_arg, cmd_type, CHECK_IF_CMD_SENT_POLL); + + /* No responses for CMD 0,4,15 */ + if ((MMC_CMD_0_GO_IDLE_STATE != cmd_type) && + (MMC_CMD_4_SET_DSR != cmd_type) && + (MMC_CMD_15_GOTO_INACTIVE_STATE != cmd_type)) { + /* + * Poll the ISR for activity. If change detected due to a response being + * received, determine the response type and parse it accordingly. + * Otherwise, alert the calling function that an error occurred. + */ + volatile uint32_t trans_status_isr; + + do { + trans_status_isr = HAL_get_32bit_reg(addr, ISR); + } while ((!(ISR_RESP_REG_MASK & trans_status_isr)) && + (!(ISR_BISR_MBSIR_ERR & trans_status_isr))); + + if (RESP_RX == HAL_get_8bit_reg_field(addr, ISR_SRRI)) { + /* If the response is an R1/B response */ + if ((MMC_CMD_1_SEND_OP_COND != cmd_type) && + (MMC_CMD_2_ALL_SEND_CID != cmd_type) && + (MMC_CMD_9_SEND_CSD != cmd_type) && + (MMC_CMD_10_SEND_CID != cmd_type) && + (MMC_CMD_39_FAST_IO != cmd_type) && + (MMC_CMD_40_GO_IRQ_STATE != cmd_type)) { + ret_status = response_1_parser(addr); + } else { + ret_status = TRANSFER_IF_SUCCESS; + } + } + } else { + ret_status = TRANSFER_IF_SUCCESS; + } + + /* Clear flags for the next time */ + HAL_set_32bit_reg(addr, ICR, CLEAR_ALL_32); + + return (ret_status); +} + +/** + * send_mmc_cmd() + * See ".h" for details of how to use this function. + */ +void send_mmc_cmd(addr_t addr, uint32_t cmd_arg, uint8_t cmd_type, + cmd_response_check_options cmd_option) { + HAL_set_8bit_reg(addr, CR0, cmd_type); + HAL_set_8bit_reg(addr, CR1, (uint_fast8_t)(cmd_arg >> 24u)); + HAL_set_8bit_reg(addr, CR2, (uint_fast8_t)(cmd_arg >> 16u)); + HAL_set_8bit_reg(addr, CR3, (uint_fast8_t)(cmd_arg >> 8u)); + HAL_set_8bit_reg(addr, CR4, (uint_fast8_t)cmd_arg); /* when CR4 is written to, + transfer is initiated + by coreMMC */ + + switch (cmd_option) { + case CHECK_IF_CMD_SENT_POLL: /* only need to wait around if expecting no + response */ + while (CMD_SENT != HAL_get_8bit_reg_field(addr, ISR_SCSI)) { + ; + } + break; + case CHECK_IF_CMD_SENT_INT: + break; + case CHECK_IF_CMD_SENT_NO: /* use when expecting a response */ + /* No check- will be checked when response received */ + break; + default: + /* No check */ + break; + } +} + +/** + * The response_1_parser() returns the contents of the Card Status Register. + * This function checks that none of the error fields are set within the CSR + * and the status of the READY_FOR_DATA flag (Bit 8). + */ +static cif_response_t response_1_parser(addr_t addr) { + cif_response_t ret_status = TRANSFER_IF_FAIL; + uint32_t response; + + response = HAL_get_32bit_reg(addr, RR1); + if (!(R1_ERR_RESP_MASK & response)) { + if (READY_FOR_DATA & response) { + ret_status = TRANSFER_IF_SUCCESS; + } else { + ret_status = DEVICE_BUSY; + } + } + + return (ret_status); +} diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc_if.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc_if.h new file mode 100644 index 0000000..59292a7 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc_if.h @@ -0,0 +1,139 @@ +/** + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core_mmc_if.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreMMC interface header file + * + * This CoreMMC Interface header file provides access to functions which + * configure and program the eMMC Slave device to allow data transfers to be + * performed with the eMMc Master. + * + */ + +#ifndef __CORE_MMC_IF_H +#define __CORE_MMC_IF_H + +#ifndef LEGACY_DIR_STRUCTURE +#include "hal/cpu_types.h" + +#else +#include "cpu_types.h" +#endif + +#ifdef __cplusplus +extern "C" +#endif + +#define NO_ERROR 0u +#define NO_STATUS 0u +#define CLEAR_BIT 1u +#define CMD_SENT 1u +#define RESP_RX 1u +#define READY 1u +#define ISR_RESP_REG_MASK 0x78u /** No user error, RSP received */ + +/** + The `cif_response` type specifies the status of the MMC command transfer + to the eMMC device. A value of this type is returned by `cif_send_cmd()`. + */ + +typedef enum { + TRANSFER_IF_FAIL = 0u, + TRANSFER_IF_SUCCESS = 1u, + DEVICE_BUSY = 2u +} cif_response_t; + +typedef enum { + CHECK_IF_CMD_SENT_POLL = 0u, + CHECK_IF_CMD_SENT_INT = 1u, + CHECK_IF_CMD_SENT_NO = 2u +} cmd_response_check_options; + +/** + `cif_send_cmd()` handles the interface level command and response + data for communicating with the eMMC device. This function issues + configuration and control commands to the device, waits on the status + register to update, indicating there was a response received (where expected) + and parses the response to determine the outcome of the transfer. + + @param addr + The `addr` parameter specifies the base address of the instance of CoreMMC in + hardware which the command and argument are to be transmitted to. + + @param cmd_arg + The `cmd_arg` parameter specifies the MMC argument to be passed to the eMMC + device. + + @param cmd_type + The `cmd_type` parameter specifies the MMC Command type to be passed to the + eMMC device. + + @return + This function returns a value of `cif_response_t` type representing the + outcome of the transfer. + If this return value indicates that the eMMC device is busy, subsequent + actions must be taken to ensure that a command is not issued until the device + returns to idle. + + @example + @code + #define MMC_DW_CSD 0x03B70300u + #define MMC_CMD_SWITCH 6u + cif_response_t response_status + + response_status = cif_send_cmd(BASE_ADDR, MMC_DW_CSD, MMC_CMD_SWITCH) + + while(DEVICE_BUSY == response_status) + { + response_status = cif_send_cmd(BASE_ADDR, + RCA_VALUE, + MMC_CMD_SEND_STATUS) + } + @endcode + */ +cif_response_t cif_send_cmd(addr_t addr, uint32_t cmd_arg, uint8_t cmd_type); + +/** + `send_mmc_cmd()` transfers the MMC bus command and argument to the MMC device + and waits until the core indicates that the command has been transferred + successfully. + + @param addrr + The addr parameter specifies the base address of the instance of CoreMMC in + hardware which the command and argument are to be transmitted to. + + @param cmd_arg + The `cmd_arg` parameter specifies the MMC argument to be passed to the eMMC + device. + + @param cmd_type + The `cmd_type` parameter specifies the MMC Command type to be passed to the + eMMC device. + + @param cmd_option + The `cmd_option` parameter specifies if the function checks if CoreMMC has + sent the command or not before returning. There is no need to check if you + are expecting a response, just check for the response. + + @return + This function does not return any value. + + @example + Send Command to the MMC device. + + @code + send_mmc_cmd(this_mmc->base_addr, RCA_VALUE, MMC_CMD_13_SEND_STATUS, + CHECK_IF_CMD_SENT_NO) + @endcode + */ +void send_mmc_cmd(addr_t addr, uint32_t cmd_arg, uint8_t cmd_type, + cmd_response_check_options cmd_option); + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_MMC_IF_H */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc_regs.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc_regs.h new file mode 100644 index 0000000..24e9638 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreMMC/core_mmc_regs.h @@ -0,0 +1,880 @@ +/** + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core_mmc_regs.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreMMC memory map + * + */ + +#ifndef COREMMC_REGS_H_ +#define COREMMC_REGS_H_ + +/**----------------------------------------------------------------------------- + CoreMMC Registers + */ +#define CR0_REG_OFFSET 0x04u +#define CR1_REG_OFFSET 0x08u +#define CR2_REG_OFFSET 0x09u +#define CR3_REG_OFFSET 0x0Au +#define CR4_REG_OFFSET 0x0Bu +#define RR0_REG_OFFSET 0x10u + +/** Response registers */ +#define RR1_REG_OFFSET 0x14u +#define RR2_REG_OFFSET 0x15u +#define RR3_REG_OFFSET 0x16u +#define RR4_REG_OFFSET 0x17u +#define RR5_REG_OFFSET 0x18u +#define RR6_REG_OFFSET 0x19u +#define RR7_REG_OFFSET 0x1Au +#define RR8_REG_OFFSET 0x1Bu +#define RR9_REG_OFFSET 0x1Cu +#define RR10_REG_OFFSET 0x1Du +#define RR11_REG_OFFSET 0x1Eu +#define RR12_REG_OFFSET 0x1Fu +#define RR13_REG_OFFSET 0x20u +#define RR14_REG_OFFSET 0x21u +#define RR15_REG_OFFSET 0x22u +#define WDR_REG_OFFSET 0x24u +#define RDR_REG_OFFSET 0x28u +#define IMR_REG_OFFSET 0x2Cu +#define BIMR_REG_OFFSET 0x2Du + +#define RSPTO_REG_OFFSET 0x3Cu +#define DATATO_REG_OFFSET 0x40u +#define BLOCK_LEN_0_REG_OFFSET 0x44u +#define BLOCK_LEN_1_REG_OFFSET 0x45u +#define BLOCK_LEN_2_REG_OFFSET 0x46u +#define BLOCK_LEN_3_REG_OFFSET 0x47u +#define DCTRL_REG_OFFSET 0x48u +#define CLKR_REG_OFFSET 0x4Cu + +#define MBCOUNT_REG_OFFSET 0x50u + +#define CLEAR_ALL 0xFFu +#define CLEAR_ALL_32 0xFFFFFFFFu + +/** + STATUS register details + */ +#define SR_REG_OFFSET 0x00u + +/** + ECRD bits. CRC Error Detected + */ +#define SR_ECRD_OFFSET 0x00u +#define SR_ECRD_MASK 0x01u +#define SR_ECRD_SHIFT 0u + +/** + EBUCD bits. Buffer Under run Detected + */ +#define SR_EBUCD_OFFSET 0x00u +#define SR_EBUCD_MASK 0x02u +#define SR_EBUCD_SHIFT 1u + +/** + EBOD bits. Buffer Overrun Detected + */ +#define SR_EBOD_OFFSET 0x00u +#define SR_EBOD_MASK 0x04u +#define SR_EBOD_SHIFT 2u + +/** + SRFE bits. Read FIFO Empty + */ +#define SR_SRFE_OFFSET 0x00u +#define SR_SRFE_MASK 0x08u +#define SR_SRFE_SHIFT 3u + +/** + SWFE bits. Write FIFO Empty + */ +#define SR_SWFE_OFFSET 0x00u +#define SR_SWFE_MASK 0x10u +#define SR_SWFE_SHIFT 4u + +/** + SRFF bits. Read FIFO full + */ +#define SR_SRFF_OFFSET 0x00u +#define SR_SRFF_MASK 0x20u +#define SR_SRFF_SHIFT 5u + +/** + SWFF bits. Write FIFO full + */ +#define SR_SWFF_OFFSET 0x00u +#define SR_SWFF_MASK 0x40u +#define SR_SWFF_SHIFT 6u + +/** + RDRE bits. Response Data ready + */ +#define SR_RDRE_OFFSET 0x00u +#define SR_RDRE_MASK 0x80u +#define SR_RDRE_SHIFT 7u + +/** + Version Register details + */ +#define VR_REG_OFFSET 0x01u + +/** + REV bits. Hardware Revision Number + */ +#define VR_REV_OFFSET 0x01u +#define VR_REV_MASK 0x03u +#define VR_REV_SHIFT 0u + +/** + MMCDW bits. MMC Data Width + */ +#define VR_MMCDW_OFFSET 0x01u +#define VR_MMCDW_MASK 0x0Cu +#define VR_MMCDW_SHIFT 2u + +/** + MMCFD bits. MMC FIFO Depth + */ +#define VR_MMCFD_OFFSET 0x01u +#define VR_MMCFD_MASK 0x30u +#define VR_MMCFD_SHIFT 4u + +/** + CLKDIV bits. Clock Divider + */ +#define VR_CLKDIV_OFFSET 0x01u +#define VR_CLKDIV_MASK 0xC0u +#define VR_CLKDIV_SHIFT 6u + +/**----------------------------------------------------------------------------- + Major Version Register details + Only present if Hardware Revision Number in VR_REG_OFFSET register == 0x03 + Uses all 8 bits + */ +#define VR_MAJ_REG_OFFSET 0x02u + +/**----------------------------------------------------------------------------- + Minor Version Register details + Only present if Hardware Revision Number in VR_REG_OFFSET register == 0x03 + Uses all 8 bits + */ +#define VR_MIN_REG_OFFSET 0x03u + +/** + SBUI bits. Under-run. + */ +#define IMR_MBUI_OFFSET 0x2Cu +#define IMR_MBUI_MASK 0x01u +#define IMR_MBUI_SHIFT 0u + +/** + SBOI bits. Over-run. + */ +#define IMR_MBOI_OFFSET 0x2Cu +#define IMR_MBOI_MASK 0x02u +#define IMR_MBOI_SHIFT 1u + +/** + SCSI bits. Command Sent. + */ +#define IMR_MCSI_OFFSET 0x2Cu +#define IMR_MCSI_MASK 0x04u +#define IMR_MCSI_SHIFT 2u + +/** + SRRI bits. Response Received. + */ +#define IMR_MRRI_OFFSET 0x2Cu +#define IMR_MRRI_MASK 0x08u +#define IMR_MRRI_SHIFT 3u + +/** + STXI bits. Transmit bit error. + */ +#define IMR_MTXI_OFFSET 0x2Cu +#define IMR_MTXI_MASK 0x10u +#define IMR_MTXI_SHIFT 4u + +/** + STBI bits. Stop Bit Error. + */ +#define IMR_MTBI_OFFSET 0x2Cu +#define IMR_MTBI_MASK 0x20u +#define IMR_MTBI_SHIFT 5u + +/** + SSBI bits. Start Bit Error => No response from eMMC to command after x + clock cycles + */ +#define IMR_MSBI_OFFSET 0x2Cu +#define IMR_MSBI_MASK 0x40u +#define IMR_MSBI_SHIFT 6u + +/** + MUER bits. User Error. FIFO has not been filled. + */ +#define IMR_MUER_OFFSET 0x2Cu +#define IMR_MUER_MASK 0x80u +#define IMR_MUER_SHIFT 7u + +#define IMR_MASK_MULTI_READ \ + (0xFF & \ + ~(IMR_MCSI_MASK /*| IMR_MUER_MASK*/)) /* Command send, user error masked */ +#define IMR_MASK_MULTI_WRITE \ + (0xFF & \ + ~(IMR_MCSI_MASK /*| IMR_MUER_MASK*/)) /* Command send, user error masked */ + +/**----------------------------------------------------------------------------- + Block Interrupt Mask Register + */ +#define BIMR_REG_OFFSET 0x2Du + +/** + SBWDONE bits. Block Write Done. + */ +#define BIMR_SBWDONE_OFFSET 0x2Du +#define BIMR_SBWDONE_MASK 0x01u +#define BIMR_SBWDONE_SHIFT 0u + +/** + SBRDONE bits. Block Read Done. + */ +#define BIMR_SBRDONE_OFFSET 0x2Du +#define BIMR_SBRDONE_MASK 0x02u +#define BIMR_SBRDONE_SHIFT 1u + +/** + SBCRCERR bits. CRC Error. + */ +#define BIMR_SBCRCERR_OFFSET 0x2Du +#define BIMR_SBCRCERR_MASK 0x04u +#define BIMR_SBCRCERR_SHIFT 2u + +/** + SDATTO bits. Block Read Data Time-out. + */ +#define BIMR_SDATTO_OFFSET 0x2Du +#define BIMR_SDATTO_MASK 0x08u +#define BIMR_SDATTO_SHIFT 3u + +/** + SBRSTERR bits. Block Read Stop Bit Error. + */ +#define BIMR_SBRSTERR_OFFSET 0x2Du +#define BIMR_SBRSTERR_MASK 0x10u +#define BIMR_SBRSTERR_SHIFT 4u + +/** + SCRCSTERR bits. Block CRC Response Error. + */ +#define BIMR_SCRCSTERR_OFFSET 0x2Du +#define BIMR_SCRCSTERR_MASK 0x20u +#define BIMR_SCRCSTERR_SHIFT 5u + +/** + SWBUSYTOERR bits. Card not ready-- timeout Data0 down for too long + */ +#define BIMR_SWBUSYTOERR_OFFSET 0x2Du +#define BIMR_SWBUSYTOERR_MASK 0x40u +#define BIMR_SWBUSYTOERR_SHIFT 6u + +/** + SWBFIFOTOERR bits. Timeout on data being present in FIFO to write to eMMC + */ +#define BIMR_SWBFIFOTOERR_OFFSET 0x2Du +#define BIMR_SWBFIFOTOERR_MASK 0x80u +#define BIMR_SWBFIFOTOERR_SHIFT 7u + +#define BIMR_MASK_MULTI_READ \ + (0xFF & ~0x03) /* all except block read and block write done */ +#define BIMR_MASK_MULTI_WRITE \ + (0xFF & ~0x03) /* all except block read and block write done */ +#define BIMR_MASK_SINGLE_READ (0xFF & ~BIMR_SBWDONE_MASK) +#define BIMR_MASK_SINGLE_WRITE (0xFF & ~BIMR_SBRDONE_MASK) + +/**----------------------------------------------------------------------------- + Multi-Block Interrupt Mask Register + */ +#define MBIMR_REG_OFFSET 0x2Eu + +/** + MBWDONE bits. Multi Block Write Done. + */ +#define MBIMR_MBWDONE_OFFSET 0x2Eu +#define MBIMR_MBWDONE_MASK 0x01u +#define MBIMR_MBWDONE_SHIFT 0u + +/** + MBRDONE bits. Multi Block Read Done. + */ +#define MBIMR_MBRDONE_OFFSET 0x2Eu +#define MBIMR_MBRDONE_MASK 0x02u +#define MBIMR_MBRDONE_SHIFT 1u + +/** + MBCRCERR bits. CRC Error. + */ +#define MBIMR_MBCRCERR_OFFSET 0x2Eu +#define MBIMR_MBCRCERR_MASK 0x04u +#define MBIMR_MBCRCERR_SHIFT 2u + +/** + MDATTO bits. MultiBlock Read Data Time-out. + */ +#define MBIMR_MDATTO_OFFSET 0x2Eu +#define MBIMR_MDATTO_MASK 0x08u +#define MBIMR_MDATTO_SHIFT 3u + +/** + MBRSTERR bits. Block Read Stop Bit Error. + */ +#define MBIMR_MBRSTERR_OFFSET 0x2Eu +#define MBIMR_MBRSTERR_MASK 0x10u +#define MBIMR_MBRSTERR_SHIFT 4u + +/** + MCRCSTERR bits. Block CRC Response Error. + */ +#define MBIMR_MCRCSTERR_OFFSET 0x2Eu +#define MBIMR_MCRCSTERR_MASK 0x20u +#define MBIMR_MCRCSTERR_SHIFT 5u + +/** + MWBUSYTOERR bits. Card not ready-- timeout Data0 down for too long + */ +#define MBIMR_MWBUSYTOERR_OFFSET 0x2Eu +#define MBIMR_MWBUSYTOERR_MASK 0x40u +#define MBIMR_MWBUSYTOERR_SHIFT 6u + +/** + MWBFIFOTOERR bits. Timeout on data being present in FIFO to write to eMMC + */ +#define MBIMR_MWBFIFOTOERR_OFFSET 0x2Eu +#define MBIMR_MWBFIFOTOERR_MASK 0x80u +#define MBIMR_MWBFIFOTOERR_SHIFT 7u + +#define MBIMR_MASK_MULTI_READ \ + (0xFF & ~MBISR_MBWDONE_MASK) /* all except multi-block write done */ +#define MBIMR_MASK_MULTI_WRITE \ + (0xFF & ~MBIMR_MBRDONE_MASK) /* all except multi-block read done */ + +/**----------------------------------------------------------------------------- + Interrupt Status Register details + */ +#define ISR_REG_OFFSET 0x30u + +/** + SBUI bits. Under-run. + */ +#define ISR_SBUI_OFFSET 0x30u +#define ISR_SBUI_MASK 0x01u +#define ISR_SBUI_SHIFT 0u + +/** + SBUI bits. Over-run. + */ +#define ISR_SBOI_OFFSET 0x30u +#define ISR_SBOI_MASK 0x02u +#define ISR_SBOI_SHIFT 1u + +/** + SCSI bits. Command Sent. + */ +#define ISR_SCSI_OFFSET 0x30u +#define ISR_SCSI_MASK 0x04u +#define ISR_SCSI_SHIFT 2u + +/** + SRRI bits. Response Received. + */ +#define ISR_SRRI_OFFSET 0x30u +#define ISR_SRRI_MASK 0x08u +#define ISR_SRRI_SHIFT 3u + +/** + STXI bits. Transmit bit error. + */ +#define ISR_STXI_OFFSET 0x30u +#define ISR_STXI_MASK 0x10u +#define ISR_STXI_SHIFT 4u + +/** + STBI bits. Stop Bit Error. + */ +#define ISR_STBI_OFFSET 0x30u +#define ISR_STBI_MASK 0x20u +#define ISR_STBI_SHIFT 5u + +/** + SSBI bits. Start bit Bit Error => no data received from eMMC + */ +#define ISR_SSBI_OFFSET 0x30u +#define ISR_SSBI_MASK 0x40u +#define ISR_SSBI_SHIFT 6u + +/** + SUER bits. User Error. FIFI not filled/emptied as expected + */ +#define ISR_SUER_OFFSET 0x30u +#define ISR_SUER_MASK 0x80u +#define ISR_SUER_SHIFT 7u + +#define ISR_ERRORS \ + (ISR_SUER_MASK | ISR_SSBI_MASK | ISR_STBI_MASK | ISR_STXI_MASK | \ + ISR_SBOI_MASK | ISR_SBUI_MASK) + +/**----------------------------------------------------------------------------- + Block Interrupt Status Register Details + */ +#define BISR_REG_OFFSET 0x31u + +/** + SBWDONE bits. Block Write Done. + */ +#define BISR_SBWDONE_OFFSET 0x31u +#define BISR_SBWDONE_MASK 0x01u +#define BISR_SBWDONE_SHIFT 0u + +/** + SBRDONE bits. Block Read Done. + */ +#define BISR_SBRDONE_OFFSET 0x31u +#define BISR_SBRDONE_MASK 0x02u +#define BISR_SBRDONE_SHIFT 1u + +/** + SBCRCERR bits. CRC Error. + */ +#define BISR_SBCRCERR_OFFSET 0x31u +#define BISR_SBCRCERR_MASK 0x04u +#define BISR_SBCRCERR_SHIFT 2u + +/** + SBRTOUTERR bits. Block Read start Time-out. + */ +#define BISR_SBRTOUTERR_OFFSET 0x31u +#define BISR_SBRTOUTERR_MASK 0x08u +#define BISR_SBRTOUTERR_SHIFT 3u + +/** + SBRSTERR bits. Block Read Stop Bit Error. + */ +#define BISR_SBRSTERR_OFFSET 0x31u +#define BISR_SBRSTERR_MASK 0x10u +#define BISR_SBRSTERR_SHIFT 4u + +/** + SCRCSTERR bits. Block write timeout on start bit of CRC response frame or no + stop bit received on same frame. + */ +#define BISR_SCRCSTERR_OFFSET 0x31u +#define BISR_SCRCSTERR_MASK 0x20u +#define BISR_SCRCSTERR_SHIFT 5u + +/** + SWBUSYTOERR bits. Card not ready-- timeout Data0 down for too long + */ +#define BISR_SWBUSYTOERR_OFFSET 0x31u +#define BISR_SWBUSYTOERR_MASK 0x40u +#define BISR_SWBUSYTOERR_SHIFT 6u + +/** + SWBFIFOTOERR bits. Timeout on data being present in FIFO to write to eMMC + */ +#define BISR_SWBFIFOTOERR_OFFSET 0x31u +#define BISR_SWBFIFOTOERR_MASK 0x80u +#define BISR_SWBFIFOTOERR_SHIFT 7u + +#define BISR_ERRORS \ + (BISR_SWBFIFOTOERR_MASK | BISR_SWBUSYTOERR_MASK | BISR_SCRCSTERR_MASK | \ + BISR_SBRSTERR_MASK | BISR_SBRTOUTERR_MASK | BISR_SBCRCERR_MASK) + +/**----------------------------------------------------------------------------- + Multi Block Interrupt Status Register Details + */ +#define MBISR_REG_OFFSET 0x32u + +/** + MBWDONE bits. Block Write Done. + */ +#define MBISR_MBWDONE_OFFSET 0x32u +#define MBISR_MBWDONE_MASK 0x01u +#define MBISR_MBWDONE_SHIFT 0u + +/** + MBRDONE bits. Block Read Done. + */ +#define MBISR_MBRDONE_OFFSET 0x32u +#define MBISR_MBRDONE_MASK 0x02u +#define MBISR_MBRDONE_SHIFT 1u + +/** + MBCRCERR bits. CRC Error. + */ +#define MBISR_MBCRCERR_OFFSET 0x32u +#define MBISR_MBCRCERR_MASK 0x04u +#define MBISR_MBCRCERR_SHIFT 2u + +/** + MBRTOUTERR bits. Block Read Data Time-out + */ +#define MBISR_MBRTOUTERR_OFFSET 0x32u +#define MBISR_MBRTOUTERR_MASK 0x08u +#define MBISR_MBRTOUTERR_SHIFT 3u + +/** + MBRSTERR bits. Block Read Stop Bit Error. + */ +#define MBISR_MBRSTERR_OFFSET 0x32u +#define MBISR_MBRSTERR_MASK 0x10u +#define MBISR_MBRSTERR_SHIFT 4u + +/** + MCRCSTERR bits + Block CRC Response Error occurred with stop bit error + */ +#define MBISR_MCRCSTERR_OFFSET 0x32u +#define MBISR_MCRCSTERR_MASK 0x20u +#define MBISR_MCRCSTERR_SHIFT 5u + +/** + MWBUSYTOERR bits + Card not ready -- timeout Data0 down for too long eMMC slave doesn't hold + DAT0 low indefinitely in single & multiple block transfers start bit to be + loaded onto the bus by the eMMC slave from either the SBRSTRT or the MBRSTRT + bit being set in the SBCSR or the MBSCR. + */ +#define MBISR_MWBUSYTOERR_OFFSET 0x32u +#define MBISR_MWBUSYTOERR_MASK 0x40u +#define MBISR_MWBUSYTOERR_SHIFT 6u + +/** + MWBFIFOTOERR bits + Timeout on data being present in FIFO to write to eMMC. + Timeout indicating that CoreMMC waits for a block length amount of data to be + present in the write FIFO from the setting of the SBWSTRT or MBWSTRT bits in + the SBCSR or the MBCSR for the first block in a single or multiple block + write transfer. + Or from the stop bit of the CRC status frame to there being a block length + amount of data in the write FIFO for subsequent blocks in a multiple block + write transfer + */ +#define MBISR_MWBFIFOTOERR_OFFSET 0x32u +#define MBISR_MWBFIFOTOERR_MASK 0x80u +#define MBISR_MWBFIFOTOERR_SHIFT 7u + +#define MBISR_ERRORS \ + (MBISR_MWBFIFOTOERR_MASK | MBISR_MWBUSYTOERR_MASK | MBISR_MCRCSTERR_MASK | \ + MBISR_MBRSTERR_MASK | MBISR_MBRTOUTERR_MASK | MBISR_MBCRCERR_MASK) +#define ISR_BISR_MBSIR_ERR \ + ((MBISR_ERRORS << 16) | (BISR_ERRORS << 8) | ISR_ERRORS) + +/**----------------------------------------------------------------------------- + Interrupt Clear Register details + */ +#define ICR_REG_OFFSET 0x34u + +/** + CBUI bits. Clear Under-run. + */ +#define ICR_CBUI_OFFSET 0x34u +#define ICR_CBUI_MASK 0x01u +#define ICR_CBUI_SHIFT 0u + +/** + CBUI bits. Clear Over-run. + */ +#define ICR_CBOI_OFFSET 0x34u +#define ICR_CBOI_MASK 0x02u +#define ICR_CBOI_SHIFT 1u + +/** + CCSI bits. Clear Command Sent. + */ +#define ICR_CCSI_OFFSET 0x34u +#define ICR_CCSI_MASK 0x04u +#define ICR_CCSI_SHIFT 2u + +/** + CRRI bits. Clear Response Received. + */ +#define ICR_CRRI_OFFSET 0x34u +#define ICR_CRRI_MASK 0x08u +#define ICR_CRRI_SHIFT 3u + +/** + CTXI bits. Clear Transmit bit error. + */ +#define ICR_CTXI_OFFSET 0x34u +#define ICR_CTXI_MASK 0x10u +#define ICR_CTXI_SHIFT 4u + +/** + CTBI bits. Clear Stop Bit Error. + */ +#define ICR_CTBI_OFFSET 0x34u +#define ICR_CTBI_MASK 0x20u +#define ICR_CTBI_SHIFT 5u + +/** + CSBI bits. Clear Stop Bit Error. + */ +#define ICR_CSBI_OFFSET 0x34u +#define ICR_CSBI_MASK 0x40u +#define ICR_CSBI_SHIFT 6u + +/** + CUER bits. Clear User Error. + */ +#define ICR_CUER_OFFSET 0x34u +#define ICR_CUER_MASK 0x80u +#define ICR_CUER_SHIFT 7u + +/**----------------------------------------------------------------------------- + Block Interrupt Clear Register Details + */ +#define BICR_REG_OFFSET 0x35u + +/** + CBWDONE bits. Clear Block Write Done. + */ +#define BICR_CBWDONE_OFFSET 0x35u +#define BICR_CBWDONE_MASK 0x01u +#define BICR_CBWDONE_SHIFT 0u + +/** + CBRDONE bits. Clear Block Read Done. + */ +#define BICR_CBRDONE_OFFSET 0x35u +#define BICR_CBRDONE_MASK 0x02u +#define BICR_CBRDONE_SHIFT 1u + +/** + CBCRCERR bits. Clear CRC Error. + */ +#define BICR_CBCRCERR_OFFSET 0x35u +#define BICR_CBCRCERR_MASK 0x04u +#define BICR_CBCRCERR_SHIFT 2u + +/** + CDATTO bits. Clear Block Read Data Time-out. + */ +#define BICR_CDATTO_OFFSET 0x35u +#define BICR_CDATTO_MASK 0x08u +#define BICR_CDATTO_SHIFT 3u + +/** + CBRSTERR bits. Clear Block Read Stop Bit Error. + */ +#define BICR_CBRSTERR_OFFSET 0x35u +#define BICR_CBRSTERR_MASK 0x10u +#define BICR_CBRSTERR_SHIFT 4u + +/** + CCRCSTERR bits. Clear Block CRC Response Error. + */ +#define BICR_CCRCSTERR_OFFSET 0x35u +#define BICR_CCRCSTERR_MASK 0x20u +#define BICR_CCRCSTERR_SHIFT 5u + +/**----------------------------------------------------------------------------- + Multi Block Interrupt Clear Register Details + */ +#define MBICR_REG_OFFSET 0x36u + +/** + CBWDONE bits. Clear Block Write Done. + */ +#define MBICR_CBWDONE_OFFSET 0x36u +#define MBICR_CBWDONE_MASK 0x01u +#define MBICR_CBWDONE_SHIFT 0u + +/** + CBRDONE bits. Clear Block Read Done. + */ +#define MBICR_CBRDONE_OFFSET 0x36u +#define MBICR_CBRDONE_MASK 0x02u +#define MBICR_CBRDONE_SHIFT 1u + +/** + CBCRCERR bits. Clear CRC Error. + */ +#define MBICR_CBCRCERR_OFFSET 0x36u +#define MBICR_CBCRCERR_MASK 0x04u +#define MBICR_CBCRCERR_SHIFT 2u + +/** + CDATTO bits. Clear Block Read Data Time-out. + */ +#define MBICR_CDATTO_OFFSET 0x36u +#define MBICR_CDATTO_MASK 0x08u +#define MBICR_CDATTO_SHIFT 3u + +/** + CBRSTERR bits. Clear Block Read Stop Bit Error. + */ +#define MBICR_CBRSTERR_OFFSET 0x36u +#define MBICR_CBRSTERR_MASK 0x10u +#define MBICR_CBRSTERR_SHIFT 4u + +/** + CCRCSTERR bits. Clear Block CRC Response Error. + */ +#define MBICR_CCRCSTERR_OFFSET 0x36u +#define MBICR_CCRCSTERR_MASK 0x20u +#define MBICR_CCRCSTERR_SHIFT 5u + +/** + MWBUSYTOERR bits. Card not ready-- timeout Data0 down for too long + */ +#define MBICR_MWBUSYTOERR_OFFSET 0x36u +#define MBICR_MWBUSYTOERR_MASK 0x40u +#define MBICR_MWBUSYTOERR_SHIFT 6u + +/** + MWBFIFOTOERR bits. Timeout on data being present in FIFO to write to eMMC + */ +#define MBICR_MWBFIFOTOERR_OFFSET 0x36u +#define MBICR_MWBFIFOTOERR_MASK 0x80u +#define MBICR_MWBFIFOTOERR_SHIFT 7u + +/**----------------------------------------------------------------------------- + Control Register Details + */ +#define CTRL_REG_OFFSET 0x38u + +/** + SWRST bits. Software Reset. + */ +#define CTRL_SWRST_OFFSET 0x38u +#define CTRL_SWRST_MASK 0x01u +#define CTRL_SWRST_SHIFT 0u + +/** + SLRST bits. Slave Reset. + */ +#define CTRL_SLRST_OFFSET 0x38u +#define CTRL_SLRST_MASK 0x02u +#define CTRL_SLRST_SHIFT 1u + +/** + CLKOE bits. Clock Output Enable. + */ +#define CTRL_CLKOE_OFFSET 0x38u +#define CTRL_CLKOE_MASK 0x04u +#define CTRL_CLKOE_SHIFT 2u + +/** + MIDLE bits. CoreMMC Idle. + */ +#define CTRL_MIDLE_OFFSET 0x38u +#define CTRL_MIDLE_MASK 0x08u +#define CTRL_MIDLE_SHIFT 3u + +/** + MFCL. Force Command Line Low. + */ +#define CTRL_MFCL_OFFSET 0x38u +#define CTRL_MFCL_MASK 0x10u +#define CTRL_MFCL_SHIFT 4u + +/** + RFIFO: Reset FIFO + */ +#define CTRL_RFIFO_OFFSET 0x38u +#define CTRL_RFIFO_MASK 0x20u +#define CTRL_RFIFO_SHIFT 5u + +/** + BUSY. MMC Device Busy. + */ +#define CTRL_MBUSY_OFFSET 0x38u +#define CTRL_MBUSY_MASK 0x80u +#define CTRL_MBUSY_SHIFT 7u + +/**----------------------------------------------------------------------------- + BLOCK CONTROl and STATUS register details + */ +#define BCSR_REG_OFFSET 0x39u + +/** + BWSTRT bits. Block Write Start. + */ +#define BCSR_BWSTRT_OFFSET 0x39u +#define BCSR_BWSTRT_MASK 0x01u +#define BCSR_BWSTRT_SHIFT 0u + +/** + BRSTRT bits. Block Read Start. + */ +#define BCSR_BRSTRT_OFFSET 0x39u +#define BCSR_BRSTRT_MASK 0x02u +#define BCSR_BRSTRT_SHIFT 1u + +/** + BDONE bits. Block Done. + */ +#define BCSR_BDONE_OFFSET 0x39u +#define BCSR_BDONE_MASK 0x04u +#define BCSR_BDONE_SHIFT 2u + +/** + BCRCERR bits. Block CRC Error. + */ +#define BCSR_BCRCERR_OFFSET 0x39u +#define BCSR_BCRCERR_MASK 0x08u +#define BCSR_BCRCERR_SHIFT 3u + +/** + BWST bits. Block Write Status. + */ +#define BCSR_BWST_OFFSET 0x39u +#define BCSR_BWST_MASK 0xF0u +#define BCSR_BWST_SHIFT 4u + +/**----------------------------------------------------------------------------- + MULTI BLOCK CONTROl and STATUS register details + */ +#define MBCSR_REG_OFFSET 0x3Au + +/** + MBWSTRT bits. Block Write Start. + */ +#define MBCSR_MBWSTRT_OFFSET 0x3Au +#define MBCSR_MBWSTRT_MASK 0x01u +#define MBCSR_MBWSTRT_SHIFT 0u + +/** + MBRSTRT bits. Block Read Start. + */ +#define MBCSR_MBRSTRT_OFFSET 0x3Au +#define MBCSR_MBRSTRT_MASK 0x02u +#define MBCSR_MBRSTRT_SHIFT 1u + +/** + MBDONE bits. Block Done. + */ +#define MBCSR_MBDONE_OFFSET 0x3Au +#define MBCSR_MBDONE_MASK 0x04u +#define MBCSR_MBDONE_SHIFT 2u + +/** + MBCRCERR bits. Block CRC Error. + */ +#define MBCSR_MBCRCERR_OFFSET 0x3Au +#define MBCSR_MBCRCERR_MASK 0x08u +#define MBCSR_MBCRCERR_SHIFT 3u + +/** + MBWST bits. Block Write Status. + */ +#define MBCSR_MBWST_OFFSET 0x3Au +#define MBCSR_MBWST_MASK 0xF0u +#define MBCSR_MBWST_SHIFT 4u + +#endif diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CorePWM/core_pwm.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CorePWM/core_pwm.c new file mode 100644 index 0000000..d025e25 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CorePWM/core_pwm.c @@ -0,0 +1,1136 @@ +/******************************************************************************* + * Copyright 2008 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core_pwm.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief CorePWM driver implementation. + * + */ +#include "core_pwm.h" +#include "corepwm_regs.h" + +#ifdef __cplusplus +extern "C" { +#endif +/*-------------------------------------------------------------------------*//** + * Default definitions for TACH Interface + */ +#define COREPWM_TACHMODE_DEFAULT 0x0000u +#define COREPWM_TACHIRQMASK_DEFAULT 0x0000u +#define COREPWM_TACHSTATUS_DEFAULT 0xFFFFu + +/*-------------------------------------------------------------------------*//** + * Lookup table containing the bit mask used to enable/disable PWMs by writing + * the PWM_ENABLE_1 and PWM_ENABLE_2 registers. + */ +static const uint8_t g_pwm_id_mask_lut[] = +{ + 0x00u, + 0x01u, /* PWM_1 */ + 0x02u, /* PWM_2 */ + 0x04u, /* PWM_3 */ + 0x08u, /* PWM_4 */ + 0x10u, /* PWM_5 */ + 0x20u, /* PWM_6 */ + 0x40u, /* PWM_7 */ + 0x80u, /* PWM_8 */ + 0x01u, /* PWM_9 */ + 0x02u, /* PWM_10 */ + 0x04u, /* PWM_11 */ + 0x08u, /* PWM_12 */ + 0x10u, /* PWM_13 */ + 0x20u, /* PWM_14 */ + 0x40u, /* PWM_15 */ + 0x80u, /* PWM_16 */ +}; + +/*-------------------------------------------------------------------------*//** + * Lookup table containing the bit mask used to find out the TachInput number + * for TachInterface + */ +static const uint16_t g_pwm_tach_id_mask_lut[] = +{ + 0x0000u, + 0x0001u, /* PWM_TACH_1 */ + 0x0002u, /* PWM_TACH_2 */ + 0x0004u, /* PWM_TACH_3 */ + 0x0008u, /* PWM_TACH_4 */ + 0x0010u, /* PWM_TACH_5 */ + 0x0020u, /* PWM_TACH_6 */ + 0x0040u, /* PWM_TACH_7 */ + 0x0080u, /* PWM_TACH_8 */ + 0x0100u, /* PWM_TACH_9 */ + 0x0200u, /* PWM_TACH_10 */ + 0x0400u, /* PWM_TACH_11 */ + 0x0800u, /* PWM_TACH_12 */ + 0x1000u, /* PWM_TACH_13 */ + 0x2000u, /* PWM_TACH_14 */ + 0x4000u, /* PWM_TACH_15 */ + 0x8000u, /* PWM_TACH_16 */ +}; + +/*-------------------------------------------------------------------------*//** + * Lookup table containing the offset of the registers used to control the + * positive edge of a PWM. The LUT is indexed on the PWM number from 1 to 16. + */ +static const addr_t g_pwm_posedge_offset_lut[] = +{ + 0x0000u, + PWM1_POSEDGE_REG_OFFSET, /* PWM_1 */ + PWM2_POSEDGE_REG_OFFSET, /* PWM_2 */ + PWM3_POSEDGE_REG_OFFSET, /* PWM_3 */ + PWM4_POSEDGE_REG_OFFSET, /* PWM_4 */ + PWM5_POSEDGE_REG_OFFSET, /* PWM_5 */ + PWM6_POSEDGE_REG_OFFSET, /* PWM_6 */ + PWM7_POSEDGE_REG_OFFSET, /* PWM_7 */ + PWM8_POSEDGE_REG_OFFSET, /* PWM_8 */ + PWM9_POSEDGE_REG_OFFSET, /* PWM_9 */ + PWM10_POSEDGE_REG_OFFSET, /* PWM_10 */ + PWM11_POSEDGE_REG_OFFSET, /* PWM_11 */ + PWM12_POSEDGE_REG_OFFSET, /* PWM_12 */ + PWM13_POSEDGE_REG_OFFSET, /* PWM_13 */ + PWM14_POSEDGE_REG_OFFSET, /* PWM_14 */ + PWM15_POSEDGE_REG_OFFSET, /* PWM_15 */ + PWM16_POSEDGE_REG_OFFSET, /* PWM_16 */ +}; + +/*-------------------------------------------------------------------------*//** + * Lookup table containing the offset of the registers used to control the + * negative edge of a PWM. The LUT is indexed on the PWM number from 1 to 16. + */ +static const addr_t g_pwm_negedge_offset_lut[] = +{ + 0x0000u, + PWM1_NEGEDGE_DACLEV_REG_OFFSET, /* PWM_1 */ + PWM2_NEGEDGE_DACLEV_REG_OFFSET, /* PWM_2 */ + PWM3_NEGEDGE_DACLEV_REG_OFFSET, /* PWM_3 */ + PWM4_NEGEDGE_DACLEV_REG_OFFSET, /* PWM_4 */ + PWM5_NEGEDGE_DACLEV_REG_OFFSET, /* PWM_5 */ + PWM6_NEGEDGE_DACLEV_REG_OFFSET, /* PWM_6 */ + PWM7_NEGEDGE_DACLEV_REG_OFFSET, /* PWM_7 */ + PWM8_NEGEDGE_DACLEV_REG_OFFSET, /* PWM_8 */ + PWM9_NEGEDGE_DACLEV_REG_OFFSET, /* PWM_9 */ + PWM10_NEGEDGE_DACLEV_REG_OFFSET, /* PWM_10 */ + PWM11_NEGEDGE_DACLEV_REG_OFFSET, /* PWM_11 */ + PWM12_NEGEDGE_DACLEV_REG_OFFSET, /* PWM_12 */ + PWM13_NEGEDGE_DACLEV_REG_OFFSET, /* PWM_13 */ + PWM14_NEGEDGE_DACLEV_REG_OFFSET, /* PWM_14 */ + PWM15_NEGEDGE_DACLEV_REG_OFFSET, /* PWM_15 */ + PWM16_NEGEDGE_DACLEV_REG_OFFSET, /* PWM_16 */ +}; + +/*-------------------------------------------------------------------------*//** + * Lookup table containing the offset of the registers used to stores the + * number of timer ticks between two successive positive (or negative) edges + * from the TACHIN[i] The LUT is indexed on the Tachometer Input number from + * 1 to 16. + */ +static const addr_t g_tachpulsedur_offset_lut[] = +{ + 0x0000u, + TACHPULSEDUR_0_REG_OFFSET, /* PWM_TACH_1 */ + TACHPULSEDUR_1_REG_OFFSET, /* PWM_TACH_2 */ + TACHPULSEDUR_2_REG_OFFSET, /* PWM_TACH_3 */ + TACHPULSEDUR_3_REG_OFFSET, /* PWM_TACH_4 */ + TACHPULSEDUR_4_REG_OFFSET, /* PWM_TACH_5 */ + TACHPULSEDUR_5_REG_OFFSET, /* PWM_TACH_6 */ + TACHPULSEDUR_6_REG_OFFSET, /* PWM_TACH_7 */ + TACHPULSEDUR_7_REG_OFFSET, /* PWM_TACH_8 */ + TACHPULSEDUR_8_REG_OFFSET, /* PWM_TACH_9 */ + TACHPULSEDUR_9_REG_OFFSET, /* PWM_TACH_10 */ + TACHPULSEDUR_10_REG_OFFSET, /* PWM_TACH_11 */ + TACHPULSEDUR_11_REG_OFFSET, /* PWM_TACH_12 */ + TACHPULSEDUR_12_REG_OFFSET, /* PWM_TACH_13 */ + TACHPULSEDUR_13_REG_OFFSET, /* PWM_TACH_14 */ + TACHPULSEDUR_14_REG_OFFSET, /* PWM_TACH_15 */ + TACHPULSEDUR_15_REG_OFFSET, /* PWM_TACH_16 */ +}; + +/*-------------------------------------------------------------------------*//** + * PWM_init() + * See "core_pwm.h" for details of how to use this function. + */ +void PWM_init +( + pwm_instance_t * pwm_inst, + addr_t base_addr, + uint32_t prescale, + uint32_t period +) +{ + pwm_inst->address = base_addr; + + HAL_set_8bit_reg( pwm_inst->address, PWM_ENABLE_1, 0u ); + HAL_set_8bit_reg( pwm_inst->address, PWM_ENABLE_2, 0u ); + + HAL_set_32bit_reg( pwm_inst->address, PRESCALE, (uint_fast32_t)prescale ); + + /* + * The minimum allowed period parameter value is 1. + * This simplifies the duty cycle and edge value calculations for the driver. + */ + HAL_ASSERT( period >= 1 ) + + HAL_set_32bit_reg( pwm_inst->address, PERIOD, (uint_fast32_t)period ); + + /* Set positive edge to 0 for all PWMs. */ + HAL_set_32bit_reg( pwm_inst->address, PWM1_POSEDGE, 0u ); + HAL_set_32bit_reg( pwm_inst->address, PWM2_POSEDGE, 0u ); + HAL_set_32bit_reg( pwm_inst->address, PWM3_POSEDGE, 0u ); + HAL_set_32bit_reg( pwm_inst->address, PWM4_POSEDGE, 0u ); + HAL_set_32bit_reg( pwm_inst->address, PWM5_POSEDGE, 0u ); + HAL_set_32bit_reg( pwm_inst->address, PWM6_POSEDGE, 0u ); + HAL_set_32bit_reg( pwm_inst->address, PWM7_POSEDGE, 0u ); + HAL_set_32bit_reg( pwm_inst->address, PWM8_POSEDGE, 0u ); + HAL_set_32bit_reg( pwm_inst->address, PWM9_POSEDGE, 0u ); + HAL_set_32bit_reg( pwm_inst->address, PWM10_POSEDGE, 0u ); + HAL_set_32bit_reg( pwm_inst->address, PWM11_POSEDGE, 0u ); + HAL_set_32bit_reg( pwm_inst->address, PWM12_POSEDGE, 0u ); + HAL_set_32bit_reg( pwm_inst->address, PWM13_POSEDGE, 0u ); + HAL_set_32bit_reg( pwm_inst->address, PWM14_POSEDGE, 0u ); + HAL_set_32bit_reg( pwm_inst->address, PWM15_POSEDGE, 0u ); + HAL_set_32bit_reg( pwm_inst->address, PWM16_POSEDGE, 0u ); +} + +/*-------------------------------------------------------------------------*//** + * PWM_enable() + * See "core_pwm.h" for details of how to use this function. + */ +void PWM_enable +( + pwm_instance_t * pwm_inst, + pwm_id_t pwm_id +) +{ + uint8_t pwm_enables; + uint8_t pwm_id_mask; + + /* Assertion will ensure correct PWM output has been selected. */ + HAL_ASSERT( pwm_id >= PWM_1 ) + HAL_ASSERT( pwm_id <= PWM_16 ) + + if ( (pwm_id >= PWM_1) && (pwm_id <= PWM_16 )) + { + pwm_id_mask = g_pwm_id_mask_lut[pwm_id]; + + if ( pwm_id < PWM_9 ) + { + pwm_enables = HAL_get_8bit_reg( pwm_inst->address, PWM_ENABLE_1 ); + pwm_enables |= pwm_id_mask; + HAL_set_8bit_reg + ( + pwm_inst->address, + PWM_ENABLE_1, + (uint_fast8_t)pwm_enables + ); + } + else + { + pwm_enables = HAL_get_8bit_reg( pwm_inst->address, PWM_ENABLE_2 ); + pwm_enables |= pwm_id_mask; + HAL_set_8bit_reg + ( + pwm_inst->address, + PWM_ENABLE_2, + (uint_fast8_t)pwm_enables + ); + } + } +} + +/*-------------------------------------------------------------------------*//** + * PWM_disable() + * See "core_pwm.h" for details of how to use this function. + */ +void PWM_disable +( + pwm_instance_t * pwm_inst, + pwm_id_t pwm_id +) +{ + uint8_t pwm_enables; + uint8_t pwm_id_mask; + + /* Assertion will ensure correct PWM output has been selected. */ + HAL_ASSERT( pwm_id >= PWM_1 ) + HAL_ASSERT( pwm_id <= PWM_16 ) + + if ( (pwm_id >= PWM_1) && (pwm_id <= PWM_16 )) + { + pwm_id_mask = g_pwm_id_mask_lut[pwm_id]; + + if ( pwm_id < PWM_9 ) + { + pwm_enables = HAL_get_8bit_reg( pwm_inst->address, PWM_ENABLE_1 ); + pwm_enables &= (uint8_t)~pwm_id_mask; + HAL_set_8bit_reg + ( + pwm_inst->address, + PWM_ENABLE_1, + (uint_fast8_t)pwm_enables + ); + } + else + { + pwm_enables = HAL_get_8bit_reg( pwm_inst->address, PWM_ENABLE_2 ); + pwm_enables &= (uint8_t)~pwm_id_mask; + HAL_set_8bit_reg + ( + pwm_inst->address, + PWM_ENABLE_2, + (uint_fast8_t)pwm_enables + ); + } + } +} + +/*-------------------------------------------------------------------------*//** + * PWM_enable_synch_update() + * See "core_pwm.h" for details of how to use this function. + */ +void PWM_enable_synch_update +( + pwm_instance_t * pwm_inst +) +{ + HAL_set_16bit_reg( pwm_inst->address, SYNC_UPDATE, 1u ); + + /* + * Assertion will ensure PWM feature has been enabled for CorePWM + * hardware instance. + */ + HAL_ASSERT( HAL_get_16bit_reg( pwm_inst->address, SYNC_UPDATE ) == 1u) +} + +/*-------------------------------------------------------------------------*//** + * PWM_disable_synch_update() + * See "core_pwm.h" for details of how to use this function. + */ +void PWM_disable_synch_update +( + pwm_instance_t * pwm_inst +) +{ + HAL_set_16bit_reg( pwm_inst->address, SYNC_UPDATE, 0u ); + + /* + * Assertion will ensure PWM feature has been enabled for CorePWM + * hardware instance. + */ + HAL_ASSERT( HAL_get_16bit_reg( pwm_inst->address, SYNC_UPDATE ) == 0u) +} + +/*-------------------------------------------------------------------------*//** + * PWM_set_duty_cycle() + * See "core_pwm.h" for details of how to use this function. + */ +void PWM_set_duty_cycle +( + pwm_instance_t * pwm_inst, + pwm_id_t pwm_id, + uint32_t duty_cycle +) +{ + + /* Assertion will ensure correct PWM output has been selected. */ + HAL_ASSERT( pwm_id >= PWM_1 ) + HAL_ASSERT( pwm_id <= PWM_16 ) + + if ( (pwm_id >= PWM_1) && (pwm_id <= PWM_16 )) + { + if ( duty_cycle == 0u ) + { + PWM_disable( pwm_inst, pwm_id ); + } + else + { + HW_set_32bit_reg + ( + pwm_inst->address + g_pwm_posedge_offset_lut[pwm_id], + 0u + ); + + HW_set_32bit_reg + ( + pwm_inst->address + g_pwm_negedge_offset_lut[pwm_id], + (uint_fast32_t)duty_cycle + ); + + /* + * Assertion will ensure PWM feature has been enabled for CorePWM + * hardware instance. + */ +#ifndef NDEBUG + { + uint8_t edge_value ; + addr_t neg_addr; + + neg_addr = g_pwm_negedge_offset_lut[pwm_id] ; + edge_value = HW_get_8bit_reg(pwm_inst->address + neg_addr ); + HAL_ASSERT( edge_value == (uint8_t)duty_cycle ) + } +#endif + PWM_enable( pwm_inst, pwm_id ); + } + } +} + +/*-------------------------------------------------------------------------*//** + * PWM_set_edges() + * See "core_pwm.h" for details of how to use this function. + */ +void PWM_set_edges +( + pwm_instance_t * pwm_inst, + pwm_id_t pwm_id, + uint32_t pos_edge, + uint32_t neg_edge +) +{ + /* Assertion will ensure correct PWM output has been selected. */ + HAL_ASSERT( pwm_id >= PWM_1 ) + HAL_ASSERT( pwm_id <= PWM_16 ) + + /* + * Assertion will ensure pos_edge & neg_edge is less than or equal to + * period value. + */ +#ifndef NDEBUG + { + uint32_t period ; + period = HAL_get_32bit_reg( pwm_inst->address, PERIOD ); + HAL_ASSERT( pos_edge <= period ); + HAL_ASSERT( neg_edge <= period ); + } +#endif + + if ( (pwm_id >= PWM_1) && (pwm_id <= PWM_16 )) + { + HW_set_32bit_reg + ( + pwm_inst->address + g_pwm_posedge_offset_lut[pwm_id], + (uint_fast32_t)pos_edge + ); + + /* + * Assertion will ensure PWM feature has been enabled for CorePWM + * hardware instance. + */ +#ifndef NDEBUG + { + uint8_t edge_value ; + addr_t pos_addr; + + pos_addr = g_pwm_posedge_offset_lut[pwm_id]; + edge_value = HW_get_8bit_reg(pwm_inst->address + pos_addr ); + HAL_ASSERT( edge_value == (uint8_t)pos_edge ) + } +#endif + + HW_set_32bit_reg + ( + pwm_inst->address + g_pwm_negedge_offset_lut[pwm_id], + (uint_fast32_t)neg_edge + ); + + /* + * Assertion will ensure PWM feature has been enabled for CorePWM + * hardware instance. + */ +#ifndef NDEBUG + { + uint8_t edge_value ; + addr_t neg_addr; + + neg_addr = g_pwm_negedge_offset_lut[pwm_id]; + edge_value = HW_get_8bit_reg(pwm_inst->address + neg_addr ); + HAL_ASSERT( edge_value == (uint8_t)neg_edge ) + } +#endif + } +} + +/*-------------------------------------------------------------------------*//** + * PWM_get_duty_cycle() + * See "core_pwm.h" for details of how to use this function. + */ +uint32_t PWM_get_duty_cycle +( + pwm_instance_t * pwm_inst, + pwm_id_t pwm_id +) +{ + uint32_t pos_edge ; + uint32_t neg_edge ; + uint32_t duty_cycle = 0u; + uint32_t period ; + uint8_t pwm_enables; + uint8_t pwm_id_mask; + + /* Assertion will ensure correct PWM output has been selected. */ + HAL_ASSERT( pwm_id >= PWM_1 ) + HAL_ASSERT( pwm_id <= PWM_16 ) + + if ((pwm_id >= PWM_1) && (pwm_id <= PWM_16 )) + { + pwm_id_mask = g_pwm_id_mask_lut[pwm_id]; + + /* Find out if the PWM output is enabled or disabled */ + if (pwm_id < PWM_9) + { + pwm_enables = HAL_get_8bit_reg( pwm_inst->address, PWM_ENABLE_1 ); + pwm_enables &= pwm_id_mask; + } + else + { + pwm_enables = HAL_get_8bit_reg( pwm_inst->address, PWM_ENABLE_2 ); + pwm_enables &= pwm_id_mask; + } + + /* + * If the PWM output is disabled, the duty cycle is implicitly 0, + * so allow the initial duty_cycle = 0u assignment fall through to + * the return() statement. + * NOTE: The PWM_set_duty_cycle() and PWM_generate_aligned_wave() + * functions disable the PWM output when a duty cycle = 0 is + * requested. + * + * Otherwise the PWM output is enabled, so read the positive and + * negative edge and period registers and calculate the duty cycle. + */ + if (pwm_enables) + { + pos_edge = HW_get_32bit_reg + ( + pwm_inst->address + g_pwm_posedge_offset_lut[pwm_id] + ); + + neg_edge = HW_get_32bit_reg + ( + pwm_inst->address + g_pwm_negedge_offset_lut[pwm_id] + ); + + period = HAL_get_32bit_reg( pwm_inst->address, PERIOD ); + + /* + * Calculate the duty cycle from the edge and period values. + * + * If pos_edge = neg_edge, this is the setting for PWM output + * toggle mode (50% duty cycle). For a description of toggle mode, + * see CorePWM Handbook, Fig1-3, PWM4. + * NOTE: The PWM_set_edges() function can set pos_edge = neg_edge + * and this is also the reset state for the edge registers. The + * PWM_set_duty_cycle() and PWM_generate_aligned_wave() functions + * cannot set pos_edge = neg_edge, instead they simply disable + * the PWM output when duty_cycle = 0 is requested. + */ + if (pos_edge <= neg_edge) + { + duty_cycle = neg_edge - pos_edge ; + } + else + { + duty_cycle = (period - (pos_edge - neg_edge))+1u ; + } + } + } + + return(duty_cycle); +} + +/*-------------------------------------------------------------------------*//** + PWM_generate_aligned_wave() + Algorithm for each Aligned wave type follows as below: + Parameters used in algorithm - + pos_edge: Specifies the value of the period counter at which the + PWM output identified by pwm_id will rise from low to high. + neg_edge: Specifies the value of the period counter at which the + PWM output identified by pwm_id will fall from high to low. + + Left Aligned Waveform - pos_edge 0 + neg_edge Duty_cycle + + Right Aligned Waveform - pos_edge PWM_Period+1- Duty_cycle + neg_edge 0 + + Central Aligned Waveform - + There are two cases : + Case 1: When Period + Duty_cycle = mathematical odd number + pos_edge (PWM_Period +1 - duty_cycle)/2 + neg_edge (PWM_Period +1 + duty_cycle)/2 + + Case 2: When Period + Duty_cycle = mathematical even number + In this scenario, there cannot be exact central waveform, + however, if pwm period is larger then the difference will + not be visible. + There can be two possible type of central waveform. + a) Left Centered Waveform + b) Right Centered Waveform + Basically, these will be central waveforms only but + slightly shifted towards left or right side. Formulae + for the same are below: + + a) Left Centered Aligned Waveform: + pos_edge (PWM_PERIOD -Duty_cycle)/2+1 + neg_edge (PWM_PERIOD +Duty_cycle)/2+1 + b) Right Centered Aligned Waveform: + pos_edge (PWM_PERIOD -Duty_cycle)/2 + neg_edge (PWM_PERIOD +Duty_cycle)/2 + + See "core_pwm.h" for more details of how to use this function. + */ +void PWM_generate_aligned_wave +( + pwm_instance_t * pwm_inst, + pwm_id_t pwm_id, + uint32_t duty_cycle, + pwm_wave_align_t alignment_type +) +{ + uint32_t period; + uint32_t pos_edge = 0u; + uint32_t neg_edge = 0u; + + /* Assertion will ensure correct PWM output has been selected. */ + HAL_ASSERT( pwm_id >= PWM_1 ) + HAL_ASSERT( pwm_id <= PWM_16 ) + + if( (pwm_id >= PWM_1) && (pwm_id <= PWM_16) ) + { + period = HAL_get_32bit_reg( pwm_inst->address, PERIOD ); + + /* + * Assertion will ensure duty cycle is less than or equal to + * period value. + */ + HAL_ASSERT( duty_cycle <= period ); + + if( 0u == duty_cycle) + { + /* + * For a duty cycle of 0, we cannot set pos_edge = neg_edge because + * this is the setting for PWM output toggle mode (50% duty cycle). + * See CorePWM Handbook, Fig1-3, PWM4, for a description of toggle + * mode. + * Instead, for a duty cycle of 0, disable the PWM output. + */ + PWM_disable( pwm_inst, pwm_id ); + } + else + { + switch(alignment_type) + { + case PWM_LEFT_ALIGN : + pos_edge = 0u; + neg_edge = duty_cycle; + break; + + case PWM_CENTER_ALIGN : + if( (uint32_t)0x1 & (period ^ duty_cycle)) + { + /* + * If the sum of period + duty_cycle is + * an odd number, then the duty cycle can + * be exactly centered in the period. The + * test for an "odd sum" may be expressed as, + * (lsb of period) XOR (lsb of duty_cycle) = 1 + */ + pos_edge = ((period - duty_cycle) +1u) >> 1 ; + neg_edge = pos_edge + duty_cycle ; + } + /* + * Else the sum of period + duty_cycle is an + * even number + */ + else + { +#ifdef ALTERNATE_CENTRAL_ALIGNED_WAVE + + /* case 1 -- right Shift */ + pos_edge = ((period - duty_cycle)>>1) +1u ; + + if ( period != duty_cycle ) + { + neg_edge = pos_edge + duty_cycle ; + } + else + { + /* + * When period = duty_cycle, we’ve reached the + * right shift limit for the neg_edge + */ + neg_edge = 0u; + } +#else + /* case 2 -- left Shift */ + pos_edge = (period - duty_cycle) >> 1 ; + neg_edge = pos_edge + duty_cycle ; +#endif + } + break; + + case PWM_RIGHT_ALIGN : + pos_edge = (period-duty_cycle) + 1u; + neg_edge = 0u ; + break; + + default : + break ; + } + + HW_set_32bit_reg + ( + pwm_inst->address + g_pwm_posedge_offset_lut[pwm_id], + (uint_fast32_t)pos_edge + ); + + HW_set_32bit_reg + ( + pwm_inst->address + g_pwm_negedge_offset_lut[pwm_id], + (uint_fast32_t)neg_edge + ); + + /* + * Assertion will ensure PWM feature has been enabled for CorePWM + * hardware instance. + */ +#ifndef NDEBUG + { + uint8_t edge_value ; + addr_t edge_addr; + + edge_addr = g_pwm_posedge_offset_lut[pwm_id]; + edge_value = HW_get_8bit_reg(pwm_inst->address + edge_addr ); + HAL_ASSERT( edge_value == (uint8_t)pos_edge ) + + edge_addr = g_pwm_negedge_offset_lut[pwm_id]; + edge_value = HW_get_8bit_reg(pwm_inst->address + edge_addr ); + HAL_ASSERT( edge_value == (uint8_t)neg_edge ) + } +#endif + PWM_enable( pwm_inst, pwm_id ); + } + } +} + +/*-------------------------------------------------------------------------*//** + * PWM_enable_stretch_pulse() + * See "core_pwm.h" for details of how to use this function. + */ +void PWM_enable_stretch_pulse +( + pwm_instance_t * pwm_inst, + pwm_id_t pwm_id +) +{ + uint16_t stretch_value ; + uint16_t pwm_id_mask ; + + /* Assertion will ensure correct PWM output has been selected. */ + HAL_ASSERT( pwm_id >= PWM_1 ) + HAL_ASSERT( pwm_id <= PWM_16 ) + + if ( (pwm_id >= PWM_1) && (pwm_id <= PWM_16 )) + { + pwm_id_mask = g_pwm_tach_id_mask_lut[pwm_id]; + + stretch_value = HAL_get_16bit_reg( pwm_inst->address,PWM_STRETCH ); + stretch_value |= pwm_id_mask; + + HAL_set_16bit_reg + ( + pwm_inst->address, + PWM_STRETCH, + (uint_fast16_t)stretch_value + ); + + /* + * Assertion will ensure Tachometer feature has been enabled + * for CorePWM hardware instance. + */ +#ifndef NDEBUG + { + uint16_t pwm_stretch ; + pwm_stretch = HAL_get_16bit_reg( pwm_inst->address, PWM_STRETCH ); + HAL_ASSERT( pwm_stretch == stretch_value ) + } +#endif + } +} + +/*-------------------------------------------------------------------------*//** + * PWM_disable_stretch_pulse() + * See "core_pwm.h" for details of how to use this function. + */ +void PWM_disable_stretch_pulse +( + pwm_instance_t * pwm_inst, + pwm_id_t pwm_id +) +{ + uint16_t stretch_value ; + uint16_t pwm_id_mask ; + + /* Assertion will ensure correct PWM output has been selected. */ + HAL_ASSERT( pwm_id >= PWM_1 ) + HAL_ASSERT( pwm_id <= PWM_16 ) + + if ( (pwm_id >= PWM_1) && (pwm_id <= PWM_16 )) + { + pwm_id_mask = g_pwm_tach_id_mask_lut[pwm_id]; + + stretch_value = HAL_get_16bit_reg( pwm_inst->address,PWM_STRETCH ); + stretch_value &= (uint16_t)~pwm_id_mask; + + HAL_set_16bit_reg + ( + pwm_inst->address, + PWM_STRETCH, + (uint_fast16_t)stretch_value + ); + + /* + * Assertion will ensure Tachometer feature has been enabled + * for CorePWM hardware instance. + */ +#ifndef NDEBUG + { + uint16_t pwm_stretch ; + pwm_stretch = HAL_get_16bit_reg( pwm_inst->address, PWM_STRETCH ); + HAL_ASSERT( pwm_stretch == stretch_value ) + } +#endif + } +} + +/*-------------------------------------------------------------------------*//** + * PWM_tach_init() + * See "core_pwm.h" for details of how to use this function. + */ +void PWM_tach_init +( + pwm_instance_t * pwm_inst, + pwm_tach_prescale_t tach_prescale +) +{ + HAL_set_16bit_reg + ( + pwm_inst->address, + TACHPRESCALE, + (uint_fast16_t)tach_prescale + ); + + /* + * Assertion will ensure Tachometer feature has been enabled + * for CorePWM hardware instance. + */ +#ifndef NDEBUG +{ + uint16_t prescale ; + prescale = HAL_get_16bit_reg(pwm_inst->address,TACHPRESCALE); + HAL_ASSERT( (pwm_tach_prescale_t)prescale == tach_prescale ) +} +#endif + + /* + * Tachometer mode and IRQ mask registers are updated with default value. + * So no need to check assertion. + */ + HAL_set_16bit_reg(pwm_inst->address,TACHMODE,COREPWM_TACHMODE_DEFAULT ); + HAL_set_16bit_reg(pwm_inst->address,TACHIRQMASK,COREPWM_TACHIRQMASK_DEFAULT); + + /* Clear any pending interrupts for all the tachometer inputs.*/ + HAL_set_16bit_reg(pwm_inst->address,TACHSTATUS,COREPWM_TACHSTATUS_DEFAULT ); + +} + +/*-------------------------------------------------------------------------*//** + * PWM_tach_set_mode() + * See "core_pwm.h" for details of how to use this function. + */ +void PWM_tach_set_mode +( + pwm_instance_t * pwm_inst, + pwm_tach_id_t pwm_tach_id, + uint16_t pwm_tachmode +) +{ + uint16_t pwm_tach_config; + uint16_t pwm_tach_id_mask; + + /* Assertion will ensure correct tachometer input has been selected. */ + HAL_ASSERT( pwm_tach_id >= PWM_TACH_1 ) + HAL_ASSERT( pwm_tach_id <= PWM_TACH_16 ) + + if ( (pwm_tach_id >= PWM_TACH_1) && (pwm_tach_id <= PWM_TACH_16 )) + { + pwm_tach_id_mask = g_pwm_tach_id_mask_lut[pwm_tach_id]; + + pwm_tach_config = HAL_get_16bit_reg( pwm_inst->address, TACHMODE ); + if (pwm_tachmode) + { + pwm_tach_config |= pwm_tach_id_mask; + } + else + { + pwm_tach_config &= (uint16_t)~pwm_tach_id_mask; + } + + HAL_set_16bit_reg(pwm_inst->address,TACHMODE,(uint_fast16_t)pwm_tach_config); + + /* + * Assertion will ensure Tachometer feature has been enabled + * for CorePWM hardware instance. + */ +#ifndef NDEBUG + { + uint16_t tach_mode; + tach_mode = HAL_get_16bit_reg( pwm_inst->address, TACHMODE); + HAL_ASSERT( tach_mode == pwm_tach_config ) + } +#endif + } +} + +/*-------------------------------------------------------------------------*//** + * PWM_tach_read_value() + * See "core_pwm.h" for details of how to use this function. + */ + +uint16_t PWM_tach_read_value +( + pwm_instance_t * pwm_inst, + pwm_tach_id_t pwm_tach_id +) +{ + uint16_t tach_value = 0u; + + /* Assertion will ensure correct tachometer input has been selected. */ + HAL_ASSERT( pwm_tach_id >= PWM_TACH_1 ) + HAL_ASSERT( pwm_tach_id <= PWM_TACH_16 ) + + if ((pwm_tach_id >= PWM_TACH_1) && (pwm_tach_id <= PWM_TACH_16 )) + { + tach_value = HW_get_16bit_reg + ( + pwm_inst->address + g_tachpulsedur_offset_lut[pwm_tach_id] + ); + } + return( tach_value ); +} + +/*-------------------------------------------------------------------------*//** + * PWM_tach_clear_status() + * See "core_pwm.h" for details of how to use this function. + */ + +void PWM_tach_clear_status +( + pwm_instance_t * pwm_inst, + pwm_tach_id_t pwm_tach_id +) +{ + uint16_t pwm_tach_id_mask; + + /* Assertion will ensure correct tachometer input has been selected. */ + HAL_ASSERT( pwm_tach_id >= PWM_TACH_1 ) + HAL_ASSERT( pwm_tach_id <= PWM_TACH_16 ) + + if ( (pwm_tach_id >= PWM_TACH_1) && (pwm_tach_id <= PWM_TACH_16 )) + { + pwm_tach_id_mask = g_pwm_tach_id_mask_lut[pwm_tach_id]; + + /* 0 does not have any effect. So, write 1 to the right one */ + HAL_set_16bit_reg + ( + pwm_inst->address , + TACHSTATUS, + (uint_fast16_t) pwm_tach_id_mask + ); + } +} + +/***************************************************************************//** + * PWM_tach_read_status () + * See "core_pwm.h" for details of how to use this function. + */ + +uint16_t PWM_tach_read_status +( + pwm_instance_t * pwm_inst, + pwm_tach_id_t pwm_tach_id +) +{ + uint16_t pwm_tach_id_mask ; + uint16_t pwm_tach_status = 0u; + + /* Assertion will ensure correct tachometer input has been selected. */ + HAL_ASSERT( pwm_tach_id >= PWM_TACH_1 ) + HAL_ASSERT( pwm_tach_id <= PWM_TACH_16 ) + + if((pwm_tach_id >= PWM_TACH_1) && (pwm_tach_id <= PWM_TACH_16 )) + { + pwm_tach_id_mask = g_pwm_tach_id_mask_lut[pwm_tach_id]; + pwm_tach_status = HAL_get_16bit_reg( pwm_inst->address , TACHSTATUS); + pwm_tach_status = ( pwm_tach_status & pwm_tach_id_mask); + } + return ( pwm_tach_status ); +} + +/*-------------------------------------------------------------------------*//** + * PWM_tach_get_irq_source() + * See "core_pwm.h" for details of how to use this function. + */ +pwm_tach_id_t PWM_tach_get_irq_source +( + pwm_instance_t * pwm_inst +) +{ + uint16_t status; + uint16_t irq_mask; + pwm_tach_id_t tach_id; + uint16_t n; + + irq_mask = HAL_get_16bit_reg( pwm_inst->address , TACHIRQMASK ); + status = HAL_get_16bit_reg( pwm_inst->address , TACHSTATUS ); + status = status & irq_mask; + + if(0u == status) + { + tach_id = PWM_TACH_INVALID; + } + else + { + n = 1u; + if((status & 0x00FFu) == 0u) {n = n + 8u; status = status >> 8;} + if((status & 0x000Fu) == 0u) {n = n + 4u; status = status >> 4;} + if((status & 0x0003u) == 0u) {n = n + 2u; status = status >> 2;} + if((status & 0x0001u) == 0u) {n = n + 1u;} + tach_id = (pwm_tach_id_t)n ; + } + + return tach_id; +} + +/*-------------------------------------------------------------------------*//** + * PWM_tach_enable_irq() + * See "core_pwm.h" for details of how to use this function. + */ +void PWM_tach_enable_irq +( + pwm_instance_t * pwm_inst, + pwm_tach_id_t pwm_tach_id +) +{ + uint16_t pwm_tach_irq; + uint16_t pwm_tach_id_mask; + + /* Assertion will ensure correct tachometer input has been selected. */ + HAL_ASSERT( pwm_tach_id >= PWM_TACH_1 ) + HAL_ASSERT( pwm_tach_id <= PWM_TACH_16 ) + + if ( (pwm_tach_id >= PWM_TACH_1) && (pwm_tach_id <= PWM_TACH_16 )) + { + pwm_tach_id_mask = g_pwm_tach_id_mask_lut[pwm_tach_id]; + pwm_tach_irq = HAL_get_16bit_reg( pwm_inst->address, TACHIRQMASK ); + pwm_tach_irq |= pwm_tach_id_mask; + HAL_set_16bit_reg + ( + pwm_inst->address , + TACHIRQMASK, + (uint_fast16_t)pwm_tach_irq + ); + + /* + * Assertion will ensure Tachometer feature has been enabled + * for CorePWM hardware instance. + */ +#ifndef NDEBUG + { + uint16_t tach_irq ; + tach_irq = HAL_get_16bit_reg( pwm_inst->address, TACHIRQMASK) ; + HAL_ASSERT( tach_irq == pwm_tach_irq ) + } +#endif + } +} + +/*-------------------------------------------------------------------------*//** + * PWM_tach_disable_irq() + * See "core_pwm.h" for details of how to use this function. + */ +void PWM_tach_disable_irq +( + pwm_instance_t * pwm_inst, + pwm_tach_id_t pwm_tach_id +) +{ + uint16_t pwm_tach_irq; + uint16_t pwm_tach_id_mask; + + /* Assertion will ensure correct tachometer input has been selected. */ + HAL_ASSERT( pwm_tach_id >= PWM_TACH_1 ) + HAL_ASSERT( pwm_tach_id <= PWM_TACH_16 ) + + if ( (pwm_tach_id >= PWM_TACH_1) && (pwm_tach_id <= PWM_TACH_16 )) + { + pwm_tach_id_mask = g_pwm_tach_id_mask_lut[pwm_tach_id]; + pwm_tach_irq = HAL_get_16bit_reg( pwm_inst->address, TACHIRQMASK ); + pwm_tach_irq &= (uint16_t)~pwm_tach_id_mask; + HAL_set_16bit_reg + ( + pwm_inst->address , + TACHIRQMASK, + (uint_fast16_t)pwm_tach_irq + ); + + /* + * Assertion will ensure Tachometer feature has been enabled + * for CorePWM hardware instance. + */ +#ifndef NDEBUG + { + uint16_t tach_irq ; + tach_irq = HAL_get_16bit_reg( pwm_inst->address, TACHIRQMASK) ; + HAL_ASSERT( tach_irq == pwm_tach_irq ) + } +#endif + } +} + +/*-------------------------------------------------------------------------*//** + * PWM_tach_clear_irq() + * See "core_pwm.h" for details of how to use this function. + */ + +void PWM_tach_clear_irq +( + pwm_instance_t * pwm_inst, + pwm_tach_id_t pwm_tach_id +) +{ + uint16_t pwm_tach_id_mask; + + /* Assertion will ensure correct tachometer input has been selected. */ + HAL_ASSERT( pwm_tach_id >= PWM_TACH_1 ) + HAL_ASSERT( pwm_tach_id <= PWM_TACH_16 ) + + if ( (pwm_tach_id >= PWM_TACH_1) && (pwm_tach_id <= PWM_TACH_16 )) + { + pwm_tach_id_mask = g_pwm_tach_id_mask_lut[pwm_tach_id]; + + /* 0 does not have any effect. So, write 1 to the right one. */ + HAL_set_16bit_reg + ( + pwm_inst->address , + TACHSTATUS, + (uint_fast16_t)pwm_tach_id_mask + ); + } +} + +#ifdef __cplusplus +} +#endif diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CorePWM/core_pwm.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CorePWM/core_pwm.h new file mode 100644 index 0000000..593b7bc --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CorePWM/core_pwm.h @@ -0,0 +1,1171 @@ +/******************************************************************************* + * Copyright 2008 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core_pwm.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief CorePWM driver Application Programming Interface(API). This file + * contains defines and function declarations allowing to interface with the + * CorePWM software driver. + * + */ +/*=========================================================================*//** + @mainpage CorePWM Bare Metal Driver. + + ============================================================================== + Introduction + ============================================================================== + The CorePWM hardware IP includes up to 16 Pulse Width Modulated (PWM) outputs + and up to 16 tachometer inputs. The CorePWM bare metal software driver is + designed for use in systems with no operating system. + + The CorePWM driver provides: + - Functions to control the duty cycle of each independent PWM output. The + duty cycle control functions are identical for both the General Purpose + PWM and Low Ripple DAC modes of CorePWM. + - A function to control the positive and negative edges of each independent + PWM output. This function can only be used for CorePWM outputs configured + as General Purpose PWM. This function is useful for controlling the phase + between different PWM outputs. + - A function to generate left, centre, or right aligned PWM output waveforms. + - Functions to enable and disable synchronous update of the PWM output + waveforms. + - Functions to enable and disable pulse stretching of the PWM output waveforms. + - Functions to configure and control the tachometer and measure the period of + tachometer input signals. + - Functions to control tachometer interrupts. + + ============================================================================== + Driver Configuration + ============================================================================== + The CorePWM driver is configured through calling the PWM_init() function for + each CorePWM instance in the hardware design. The configuration parameters + include the CorePWM hardware instance base address and other runtime + parameters, such as the PWM clock prescale and period. + + No CorePWM hardware configuration parameters are used by the driver, apart + from the CorePWM hardware instance base address. Hence, no additional + configuration files are required to use the driver. + + -------------------------------- + Configured PWM Register Widths + -------------------------------- + Each CorePWM instance in the hardware design is configured for the width of + the APB data bus, by the APB_DWIDTH configuration parameter. This parameter + also configures the width of CorePWM's PRESCALE, PERIOD, PWMx_POSEDGE, + PWMx_NEGEDGE, and DACx_LEVELOUT registers. The CorePWM driver's PWM_init(), + PWM_set_duty_cycle(), PWM_set_edges(), and PWM_generate_aligned_wave() + functions write values to these registers through one or more of the parameters + prescale, period, duty_cycle, pos_edge, or neg_edge - all of type uint32_t. + It is your responsibility to ensure that the values passed by these parameters + are within the range 0 to (2^APB_DWIDTH - 1), where APB_DWIDTH is 8, 16, or 32 + dependent upon your CorePWM hardware configuration, The return value from the + PWM_get_duty_cycle() function is also within the range 0 to (2^APB_DWIDTH - 1). + + Note: Failure to keep the prescale, period, duty_cycle, pos_edge, and neg_edge + parameter values within the range 0 to (2^APB_DWIDTH - 1) results in + unintended CorePWM register settings. + + --------------------------------------------- + Fixed PWM Period or Prescale Register Values + --------------------------------------------- + The prescale and period parameter values passed to the PWM_init() function may + not have any effect if fixed values were selected for the PRESCALE and PERIOD + registers in the hardware configuration of CorePWM. When fixed values are + selected for these registers, the driver cannot overwrite the fixed values. + + ---------------------------------------------------- + Fixed PWM Positive or Negative Edge Register Values + ---------------------------------------------------- + The pos_edge and neg_edge parameter values passed to the PWM_set_edges() + function, and the duty_cycle parameter value passed to the PWM_set_duty_cycle() + and PWM_generate_aligned_wave() functions, may not have the desired effect if + fixed values were selected for the PWMx_POSEDGE (positive edge) or + PWMx_NEGEDGE (negative edge) registers in the hardware configuration of + CorePWM. When fixed values are selected for these registers, the driver cannot + overwrite the fixed values. + + -------------------------------------------- + Synchronized Update of PWM Output Waveforms + -------------------------------------------- + The configuration of the CorePWM instance in the hardware design must enable + the shadow update register for each PWM channel that requires synchronous + output waveform updates. The PWM_enable_synch_update() and + PWM_disable_synch_update() functions only affect PWM channels that have + their shadow update registers enabled in hardware. + + ============================================================================== + Theory of Operation + ============================================================================== + The CorePWM software driver is designed to control the multiple instances of + CorePWM. Each instance of CorePWM in the hardware design is associated with a + single instance of the pwm_instance_t structure in the software. + + You need to allocate memory for one unique pwm_instance_t structure instance + for each CorePWM hardware instance.The contents of these data structures are + initialized by calling the PWM_init() function. A pointer to the structure + is passed to subsequent driver functions in order to identify the CorePWM + hardware instance you wish to perform the requested operation on. + + Note: Do not attempt to directly manipulate the content of pwm_instance_t + structures. This structure is only intended to be modified by the driver + function. + + -------------------------------- + Initialization + -------------------------------- + The PWM granularity is configured through the PWM_init() function prescale + parameter. The PWM granularity specifies the resolution of the PWM output + waveforms for the targeted CorePWM instance. It is also sometimes called the + PWM period count time base. It is defined by the following equation: + + ` PWM_GRANULARITY = SYS_CLOCK_PERIOD * ( prescale + 1 ) ` + + Where SYS_CLOCK_PERIOD is the period of the system clock used to clock the + CorePWM hardware instance and prescale is the value of the prescale parameter + passed to the PWM_init() function. + + The PWM period is configured through the PWM_init() function period parameter. + It specifies the period of the PWM output waveforms for the targeted CorePWM + instance. The PWM period is defined by the following equation: + + ` PWM_PERIOD = PWM_GRANULARITY * ( period + 1 ) ` + + Note: The prescale and period values passed to the PWM_init() function may not + have any effect if fixed values were selected for the prescale and + period in the hardware configuration of CorePWM. + + Note: The prescale and period parameters are not relevant to any PWM outputs + that were configured for Low Ripple DAC mode in the hardware configuration + of CorePWM. In this mode, their only role is to set the interval between + synchronized register updates when the shadow register is enabled for + the PWM output. + + -------------------------------- + PWM Output Control + -------------------------------- + The waveform for each PWM output is specified by calling PWM_set_duty_cycle() + or PWM_set_edges() functions. Each PWM output is configured as either a + General Purpose PWM output or a Low Ripple DAC output in the hardware + configuration of CorePWM. In General Purpose PWM mode, either of the + PWM_set_duty_cycle() or PWM_set_edges() functions may be used to specify the + PWM output waveform. In Low Ripple DAC mode, only the PWM_set_duty_cycle() + function may be used to specify the PWM output waveform. The duty cycle of the + PWM is read by calling the PWM_get_duty_cyle() function. + + The waveform alignment of General Purpose PWM outputs is set by calling + the PWM_generate_aligned_wave() function. + + The PWM_enable_synch_update() function is used to enable the synchronous + update of a selected group of PWM channels. The PWM_disable_synch_update() is + used to terminate the synchronous update cycle after at least one PWM period + has elapsed. In synchronous mode, the channel output waveforms are updated at + the beginning of the PWM period, which is useful for motor control and also + keeps a constant dead band space between channel waveforms. The configuration + of the CorePWM instance in the hardware design must enable the shadow update + register for each PWM channel that requires synchronous output waveform updates. + When the shadow register is enabled, the PWM_set_duty_cycle(), PWM_set_edges(), + and PWM_generate_aligned_wave() functions set the new output waveform edge + values in the channel's shadow register instead of directly in the edge + registers. Then, call the PWM_enable_synch_update() function to update the + PWM channel's output waveform at the beginning of the next PWM period. + Finally, calling the PWM_disable_synch_update() function completes the update + cycle. + + Note: The PWM_enable_synch_update() and PWM_disable_synch_update() functions + have no affect on any PWM channels that do not have their shadow update + registers configured in hardware. These channels are always updated + immediately, asynchronous to the PWM period. + + Note: The pos_edge and neg_edge values passed to the PWM_set_edges() function, + and the duty_cycle value passed to the PWM_set_duty_cycle() and + PWM_generate_aligned_wave() functions, may not have the desired effect + if fixed values were selected for the positive or negative edge + registers in the hardware configuration of CorePWM. + + A typical sequence of function calls for PWM outputs is: + + - Call the PWM_init() function to initialize the CorePWM instance and pass it + the values for the prescale and period. + - Call either PWM_set_duty_cycle() or PWM_set_edges(), or + PWM_generate_aligned_wave() for each PWM output to specify the output + waveform. + - Call PWM_enable() to enable a PWM output. + - Call PWM_disable() to disable a PWM output. + - The function PWM_set_duty_cycle() and PWM_generate_aligned_wave() also + enables the PWM output if it is not already enabled. + + -------------------------------- + Tachometer Control + -------------------------------- + CorePWM also provides a tachometer interface. During the hardware design, the + CorePWM configuration mode should be set to either 1 - (PWM and TACH mode) or + 2- (TACH only mode) which enables the tachometer interface. Each CorePWM + hardware instance supports up to 16 tachometer inputs. + + The CorePWM tachometer is initialized through calling the PWM_tach_init() + function. Call the PWM_tach_init() function before any other CorePWM driver + tachometer functions to set up the tachometer prescale value, initialize the + measurement mode, and disable interrupts. + + The PWM_tach_set_mode() function is used to configure the tachometer input + channels for continuous or one time measurement of the input signal period. + The PWM_tach_clear_status() and PWM_tach_read_status() functions are used to + clear and read the measurement status bits for the tachometer input channels. + When the status bit indicates that a new input signal period measurement is + available, the PWM_tach_read_value() function is used to read the measured + signal period value. + + A typical sequence of function calls for measurement of a tachometer input + signal is: + + - Call the PWM_init() function to initialize the CorePWM instance and pass + it the values for the prescale and period. + - Call the PWM_tach_init() function to initialize a tachometer input and + pass it a tachometer prescale value. + - Call the PWM_tach_set_mode() function to set the tachometer measurement + mode. + - Call the PWM_tach_enable_irq() function, if interrupts are required. + - Call the PWM_enable_stretch_pulse() function to enable pulse stretching of + the PWM output. + - Call the PWM_tach_clear_status() to trigger new measurement of the + tachometer input signal period. + - Call the PWM_tach_read_status()function, if interrupts are not used, to + verify that input period value has been updated in the TACHPULSEDUR register. + - Call the PWM_tach_read_value()function to read the measured period of + tachometer input signal. + - Call PWM_disable_stretch_pulse() function to disable pulse stretching of + the PWM output and resume a previously programmed PWM output waveform + pattern. + + -------------------------------- + Interrupt Control + -------------------------------- + Interrupts generated by CorePWM tachometer status changes are controlled using + the following functions: + - PWM_tach_enable_irq() + - PWM_tach_disable_irq() + - PWM_tach_clear_irq() + - PWM_tach_get_irq_source() + + The PWM_tach_get_irq_source() function is used to identify which CorePWM + tachometer input channel has generated an interrupt, among all the tachometer + input channels that have interrupts enabled. + + *//*=========================================================================*/ + +#ifndef CORE_PWM_H_ +#define CORE_PWM_H_ + +#ifndef LEGACY_DIR_STRUCTURE +#include "hal/hal.h" + +#else +#include "hal.h" +#include "hal_assert.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/*-------------------------------------------------------------------------*//** + Tachometer Measurement Mode. + ============================================= + CorePWM allows the tachometer input measurement mode to be configured. The + following constants are used as an argument to the PWM_tach_set_mode() + function to specify the tachometer input measurement mode. + + | Constant | Description | + |--------------------|--------------------------------------------------------| + | TACH_CONTINUOUS | Continuous measurement mode | + | TACH_ONE_SHOT | One time measurement mode | + + */ +#define TACH_CONTINUOUS 0 +#define TACH_ONE_SHOT 1 + +/***************************************************************************//** + PWM identifiers. The identifiers defined in this enumeration are used to + identify individual PWM outputs. They are used as argument to most CorePWM + driver functions to identify the PWM controlled by a function call. + */ +typedef enum pwm_id_t { + PWM_1 = 1, + PWM_2, + PWM_3, + PWM_4, + PWM_5, + PWM_6, + PWM_7, + PWM_8, + PWM_9, + PWM_10, + PWM_11, + PWM_12, + PWM_13, + PWM_14, + PWM_15, + PWM_16 +} pwm_id_t; + +/***************************************************************************//** + The pwm_tach_id_t type is used to identify individual CorePWM tachometer + inputs. The inputs are used as an argument to most CorePWM driver functions + to identify the PWM tachometer input controlled by a function call. + */ +typedef enum pwm_tach_id_t { + PWM_TACH_INVALID = 0, + PWM_TACH_1, + PWM_TACH_2, + PWM_TACH_3, + PWM_TACH_4, + PWM_TACH_5, + PWM_TACH_6, + PWM_TACH_7, + PWM_TACH_8, + PWM_TACH_9, + PWM_TACH_10, + PWM_TACH_11, + PWM_TACH_12, + PWM_TACH_13, + PWM_TACH_14, + PWM_TACH_15, + PWM_TACH_16 +} pwm_tach_id_t; + +/***************************************************************************//** + The pwm_wave_align_t type is used to align the duty cycle of the PWM output + waveform within the period of the PWM output waveform. A value of this type + is used as an argument to the PWM_generate_aligned_wave() function. + + Note: The duty cycle corresponds to the number of period ticks for which + the PWM output remains high. + */ +typedef enum pwm_wave_align_t { + PWM_LEFT_ALIGN, + PWM_CENTER_ALIGN, + PWM_RIGHT_ALIGN +} pwm_wave_align_t; + +/***************************************************************************//** + The pwm_tach_prescale_t type is used to select the PCLK prescale divisor for + the CorePWM tachometer. A value of this type is used as an argument to the + PWM_tach_init() function. + */ +typedef enum pwm_tach_prescale { + TACH_PRESCALE_PCLK_DIV_1 = 0x0000, + TACH_PRESCALE_PCLK_DIV_2 = 0x0001, + TACH_PRESCALE_PCLK_DIV_4 = 0x0002, + TACH_PRESCALE_PCLK_DIV_8 = 0x0003, + TACH_PRESCALE_PCLK_DIV_16 = 0x0004, + TACH_PRESCALE_PCLK_DIV_32 = 0x0005, + TACH_PRESCALE_PCLK_DIV_64 = 0x0006, + TACH_PRESCALE_PCLK_DIV_128 = 0x0007, + TACH_PRESCALE_PCLK_DIV_256 = 0x0008, + TACH_PRESCALE_PCLK_DIV_512 = 0x0009, + TACH_PRESCALE_PCLK_DIV_1024 = 0x000A, + TACH_PRESCALE_PCLK_DIV_2048 = 0x000B +} pwm_tach_prescale_t; + +/***************************************************************************//** + This structure is used to identify the various CorePWM hardware instances in + your system. Your application software should declare one instance of this + structure for each instance of CorePWM in your system. The function PWM_init() + initializes this structure. A pointer to an initialized instance of the + structure should be passed as the first parameter to the CorePWM driver + functions, to identify which CorePWM hardware instance should perform the + requested operation. + */ +typedef struct pwm_instance +{ + addr_t address; +} pwm_instance_t; + +/*------------------------Public Function-------------------------------------*/ + +/***************************************************************************//** + The PWM_init() function initializes a CorePWM hardware instance and the + data structure associated with the CorePWM hardware instance. It disables + all PWM outputs and sets the prescale and period value for the PWM. This + function should be called before any other CorePWM driver functions. + + @param pwm_inst + Pointer to a PWM_instance_t structure holding all relevant data associated + with the target CorePWM hardware instance. This pointer is used to + identify the target CorePWM hardware instance in subsequent calls to the + CorePWM functions. + + @param base_addr + The base_address parameter is the base address in the processor's memory map + for the registers of the CorePWM hardware instance being initialized. + + @param prescale + The prescale parameter is used to specify the PWM period count time base. + It specifies the PWM granularity. The value of this parameter should be + between 0 and (2^APB_DWIDTH - 1), where APB_DWIDTH is the value selected for + the APB_DWIDTH in the instantiation of the CorePWM DirectCore hardware + instance. + ` PWM_GRANULARITY = system_clock_period * (prescale + 1) ` + @param period + The period parameter specifies the period of the PWM cycles. The value of + this parameter should be between 1 and (2^APB_DWIDTH - 1), where APB_DWIDTH + is the value selected for the APB_DWIDTH in the instantiation of the + CorePWM DirectCore hardware instance. + ` PWM_PERIOD = PWM_GRANULARITY * (period + 1) ` + @return None + + @example + @code + #define COREPWM_BASE_ADDR 0xC0000000 + #define PWM_PRESCALE 0 + #define PWM_PERIOD 10 + + pwm_instance_t the_pwm; + + void system_init( void ) + { + PWM_init( &the_pwm, COREPWM_BASE_ADDR, PWM_PRESCALE, PWM_PERIOD ) ; + } + @endcode + */ +void PWM_init +( + pwm_instance_t * pwm_inst, + addr_t base_addr, + uint32_t prescale, + uint32_t period +); +/*-------------------------------------------------------------------------*//** + The PWM_enable() function enables the specified PWM output. + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @param pwm_id + The pwm_id parameter identifies the target PWM output. + + @return None + + @example + The following call enables PWM 1. + @code + PWM_enable(&the_pwm, PWM_1); + @endcode + */ +void PWM_enable +( + pwm_instance_t * pwm_inst, + pwm_id_t pwm_id +); + +/*-------------------------------------------------------------------------*//** + The PWM_disable() function disables the specified PWM output. + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @param pwm_id + The pwm_id parameter identifies the target PWM output. + + @return None + + @example + The following call disables PWM 1. + @code + PWM_disable(&the_pwm, PWM_1); + @endcode + */ +void PWM_disable +( + pwm_instance_t * pwm_inst, + pwm_id_t pwm_id +); + +/*-------------------------------------------------------------------------*//** + The PWM_enable_synch_update() function enables synchronous update of PWM + outputs. In synchronous mode, a selected group of PWM outputs are updated + at the beginning of the PWM period, which is useful for motor control and + also keeps a constant dead band space between output waveforms. + Configuration updates for all of the selected PWM outputs are synchronized + to the beginning of the PWM period, allowing precise updates, and maintaining + phase alignments between outputs. + + Note: The configuration of the CorePWM instance in the hardware design must + enable the shadow update register for each PWM channel that requires + synchronous output waveform updates. This function has no affect on any + PWM channel that does not have its shadow update register configured in + hardware. + + Note: The PWM_set_duty_cycle(), PWM_set_edges(), or PWM_generate_aligned_wave() + functions must be called to set the new output waveform edge values in + the channel's shadow register before calling the PWM_enable_synch_update() + function to enable the update. + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @return None + + @example + Enable synchronous update of the duty cycle for the PWM 1 and PWM 2 outputs. + @code + uint32_t duty_cycle = 2; + PWM_set_duty_cycle( &the_pwm, PWM_1, duty_cycle ); + PWM_set_duty_cycle( &the_pwm, PWM_2, duty_cycle ); + PWM_enable_synch_update( &the_pwm ); + wait_more_than_one_period(); + PWM_disable_synch_update( &the_pwm ); + @endcode + */ +void PWM_enable_synch_update +( + pwm_instance_t * pwm_inst +); + +/*-------------------------------------------------------------------------*//** + The PWM_disable_synch_update() function disables synchronous update of PWM + outputs. The PWM_disable_synch_update() is used to terminate a synchronous + update cycle after at least one PWM period has elapsed. + + Note: The configuration of the CorePWM instance in the hardware design must + enable the shadow update register for each PWM channel that requires + synchronous output waveform updates. This function has no affect on + any PWM channel that does not have its shadow update register + configured in hardware. + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @return None + + @example + calling the PWM_disable_synch_update() below disables the synchronous update + of PWM outputs. + @code + PWM_enable_synch_update( &the_pwm ); + wait_more_than_one_period(); + PWM_disable_synch_update(&the_pwm); + @endcode + */ +void PWM_disable_synch_update +( + pwm_instance_t * pwm_inst +); + +/*-------------------------------------------------------------------------*//** + The PWM_set_duty_cycle() function is used for setting the duty cycle of a + PWM output. + + Note: It also enables the PWM output if it is not already enabled. + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @param pwm_id + The pwm_id parameter identifies the target PWM output. + + @param duty_cycle + The duty_cycle parameter specifies the PWM output duty cycle. + In General Purpose PWM mode: + This parameter corresponds to the number of period ticks for which + the PWM output remains high. The value of this parameter should be + between 0 and the value of the period selected for the calling the PWM_init(). + In Low Ripple DAC mode: + This parameter corresponds to the average density duty cycle. The value + of this parameter should be between 0 and (2^APB_DWIDTH - 1), where + APB_DWIDTH is the value selected for the APB_DWIDTH in the instantiation + of the CorePWM DirectCore hardware instance. This sets the average + density of the PWM output high pulses to between 0% and 100% in proportion + to the duty_cycle value as a percentage of the (2^APB_DWIDTH - 1) value. + Note: The only role that prescale and period play in Low Ripple DAC mode is + to set the point when synchronized register updates will take place, + if the shadow register is enabled for a PWM output. + + @return None + + @example + The following example sets the duty cycle of PWM 1 to the value 2. + @code + uint32_t duty_cycle = 2; + PWM_set_duty_cycle( &the_pwm, PWM_1, duty_cycle ); + + @endcode + */ +void PWM_set_duty_cycle +( + pwm_instance_t * pwm_inst, + pwm_id_t pwm_id, + uint32_t duty_cycle +); + +/*-------------------------------------------------------------------------*//** + The PWM_set_edges() function configures the positive and negative edge of the + specified PWM output. The PWM output waveform is controlled by specifying the + value of period counter at which the output will rise and fall. + + Note: The PWM_set_edges() function does not enable the PWM output. You must + call the PWM_enable() function to enable the PWM output, either before + or after calling the PWM_set_edges() function. + + Note: If you specify the same value for both the positive edge and the + negative edge, this will set the PWM output waveform to toggle mode + (50% duty cycle). The PWM output waveform will toggle high or low in + each succeeding PWM period when the period counter reaches the edge + value set by this function. + + Note: The PWM_get_duty_cycle() function will return the value 0 when it is + called after using the PWM_set_edges() function to set the PWM output + waveform to toggle mode (50% duty cycle). A return value of 0 from the + PWM_get_duty_cycle() function normally means a 0% duty cycle, Therefore, + be alert to this exception if you use the PWM_set_edges() + function to manipulate the PWM output waveform edges. + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify + the target CorePWM hardware instance. + + @param pwm_id + The pwm_id parameter identifies the target PWM output. + + @param pos_edge + The pos_edge parameter specifies the value of the period counter at which + the PWM output identified by pwm_id will rise from low to high. The value + of this parameter should be between 0 and the value of the period selected + for calling PWM_init(). + + @param neg_edge + The neg_edge parameter specifies the value of the period counter at which + the PWM output identified by pwm_id will fall from high to low. The value + of this parameter should be between 0 and the value of the period selected + for calling PWM_init(). + + @return None + + @example + The following example sets the positive and negative edges of PWM 1 to 0 + and 2 respectively. + @code + uint32_t pos_edge = 0; + uint32_t neg_edge = 2; + PWM_set_edges( &the_pwm, PWM_1, pos_edge, neg_edge ); + + @endcode + */ +void PWM_set_edges +( + pwm_instance_t * pwm_inst, + pwm_id_t pwm_id, + uint32_t pos_edge, + uint32_t neg_edge +); + +/*-------------------------------------------------------------------------*//** + The PWM_get_duty_cycle() function returns the current duty cycle of the + specified PWM output. The duty cycle corresponds to the number of period + ticks during which the output remains high and should be between 0 and the + value of the period selected to call PWM_init(). + + Note: Duty Cycle (in %) is calculated as follows: + ` Duty Cycle (%) = ( (returned duty cycle value) / (period +1) )*100` + + Note: The PWM_get_duty_cycle() function is intended to return the duty cycle + previously set by calling the PWM_set_duty_cycle() or + PWM_generate_aligned_wave() functions. + + Note: A returned duty cycle value of 0 normally means a 0% duty cycle. However, + the PWM_get_duty_cycle() function also returns the value 0 when it is + called after using the PWM_set_edges() function to set the PWM output + waveform to toggle mode. In this case, a return value of 0 does not mean + a 0% duty cycle; rather, it means that the PWM output waveform is in + toggle mode. See the description of the PWM_set_edges() function + for a description of toggle mode. You must be alert to this exception if + you use the PWM_set_edges() function to manipulate the PWM output + waveform edges. + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @param pwm_id + The pwm_id parameter identifies the target PWM output. + + @return + This function returns the duty cycle of the PWM output as a 32-bit unsigned + integer. The duty cycle is the number of period ticks during which the + output is high. + Note: A returned duty cycle value of 0 normally means the PWM output is + constantly low, However, it may also indicate that the PWM output + waveform is set to toggle mode (50% duty cycle). See the note + in the function description. + + @example + Read and assigns current duty cycle value to a variable. + @code + uint32_t duty_cycle ; + duty_cycle = PWM_get_duty_cycle( &the_pwm, PWM_1 ); + + @endcode + */ +uint32_t PWM_get_duty_cycle +( + pwm_instance_t * pwm_inst, + pwm_id_t pwm_id +); + +/*-------------------------------------------------------------------------*//** + The PWM_generate_aligned_wave() function aligns the duty_cycle of the PWM + output waveform within the period of the PWM output waveform. + The following three PWM output waveform alignment options are supported: + - Left Aligned Waveform + - Center Aligned Waveform + - Right Aligned Waveform + + The PWM_generate_aligned_wave() function sets the appropriate positive edge + and negative edge values to achieve the specified alignment for the PWM output + waveform. + + Note: It also enables the PWM output if it is not already enabled. + + The following diagram shows the possible waveform alignments: + Assume PWM Prescale = 0, so that PWM Period granularity = clock period, + PWM Period = 4, and Duty_cycle = 1. + +![Timing Diagram](https://www.plantuml.com/plantuml/svg/TP5D3i8W64JtdE9BJs3_zfYwPD4G4zTeAILIcb3KU7jRcvHPwEx1pBoGmEUeGdoCZivsjDxGoIeJrCYkrglmfgnnq-sUaPgftU-4xYCTdJLUTtHHJzrFOVnsHDff7tNutMhsbhHc-AEIZF43QydED2mjnlFjuAONumLm2LpXe4x1gXAe4g0Ie1AW4gWIg6hsLWvSRBmjIWWgu3hnQf9Wac2Iy2QPh1K4TMB6jU-MJ_m0 "Timing Diagram") + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @param pwm_id + The pwm_id parameter identifies the target PWM output. + + @param duty_cycle + The duty_cycle parameter specifies the PWM output duty cycle. The duty + cycle corresponds to the number of period ticks for which the PWM output + remains high. The value of this parameter should be between 0 and the value + of the period selected to call PWM_init(). + + @param alignment_type + The alignment_type parameter specifies required alignment for the PWM + output waveform. Allowed values of type pwm_wave_align_t are: + PWM_LEFT_ALIGN + PWM_CENTER_ALIGN + PWM_RIGHT_ALIGN + + @return None + + @example + The following example sets up PWM channels 1, 2, and 3 to generate left, + center, and right aligned output waveforms respectively. + @code + PWM_generate_aligned_wave(&the_pwm, PWM_1, duty_cycle, LEFT_ALIGN); + PWM_generate_aligned_wave(&the_pwm, PWM_2, duty_cycle, CENTER_ALIGN); + PWM_generate_aligned_wave(&the_pwm, PWM_3, duty_cycle, RIGHT_ALIGN); + @endcode + */ +void PWM_generate_aligned_wave +( + pwm_instance_t * pwm_inst, + pwm_id_t pwm_id, + uint32_t duty_cycle, + pwm_wave_align_t alignment_type +); + +/*-------------------------------------------------------------------------*//** + The PWM_enable_stretch_pulse() function enables pulse stretching for the + specified PWM output. To accurately measure the speed of 3-wire fans, + it is necessary to turn on the fan periodically for long enough to get a + complete measurement of the tachometer signal from the fan. This stretching + of the PWM output pulse for a long duration is referred as PWM pulse + stretching. When pulse stretching is enabled, the PWM output is set to high + or low, dependent upon the configuration of the CorePWM instance in the + hardware design. The PWM_disable_stretch_pulse() function must be used to + disable PWM pulse stretching once the fan speed measurement is completed. + + Note: The configuration of the CorePWM instance in the hardware design must + select the PWM output level - high or low - that is set when PWM pulse + stretching is enabled. For example, to measure 3-wire fan speed, the + CorePWM hardware configuration should select a high stretch level for + the PWM output. + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @param pwm_id + The pwm_id parameter identifies the target PWM output. + + @return None + + @example + The following function call enables the stretching of PWM output 1 pulse for + tachometer input measurement. + @code + PWM_enable_stretch_pulse(&the_pwm, PWM_1); + @endcode + */ +void PWM_enable_stretch_pulse +( + pwm_instance_t * pwm_inst, + pwm_id_t pwm_id +); + +/*-------------------------------------------------------------------------*//** + The PWM_disable_stretch_pulse() function disables pulse stretching for the + specified PWM output. This function must be called once your application has + completed the measurement of the tachometer input. When pulse stretching is + disabled, the PWM output resumes generation of its previously programmed + output waveform pattern. + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @param pwm_id + The pwm_id parameter identifies the target PWM output. + + @return None + + @example + The following function call disables the stretching of the PWM 1 output + pulse after completing the tachometer input measurement. + @code + PWM_disable_stretch_pulse(&the_pwm, PWM_1); + @endcode + */ +void PWM_disable_stretch_pulse +( + pwm_instance_t * pwm_inst, + pwm_id_t pwm_id +); + +/*-------------------------------------------------------------------------*//** + The PWM_tach_init() function initializes the configuration of all of the + CorePWM tachometer inputs. It sets the tachometer's TACHPRESCALE register to + the value specified by the tach_prescale parameter. It sets the tachometer + measurement mode to continuous measurement for all tachometer inputs. It + disables the interrupts for all tachometer inputs and clears any pending + interrupts. + + Note: This function should be called before any other CorePWM driver + tachometer functions. + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @param tach_prescale + The tach_prescale value is used to select the PCLK prescale divisor for the + CorePWM tachometer. This determines the period counter time base for + tachometer measurements. Following are the values allowed for type + pwm_tach_prescale_t: + - TACH_PRESCALE_PCLK_DIV_1 + - TACH_PRESCALE_PCLK_DIV_2 + - TACH_PRESCALE_PCLK_DIV_4 + - TACH_PRESCALE_PCLK_DIV_8 + - TACH_PRESCALE_PCLK_DIV_16 + - TACH_PRESCALE_PCLK_DIV_32 + - TACH_PRESCALE_PCLK_DIV_64 + - TACH_PRESCALE_PCLK_DIV_128 + - TACH_PRESCALE_PCLK_DIV_256 + - TACH_PRESCALE_PCLK_DIV_512 + - TACH_PRESCALE_PCLK_DIV_1024 + - TACH_PRESCALE_PCLK_DIV_2048 + + @return None + + @example + The following call to PWM_tach_init() initializes the CorePWM tachometer and + sets the PCLK prescale divisor to 8 for tachometer measurements. + @code + pwm_tach_prescale_t tach_prescale = TACH_PRESCALE_PCLK_DIV_8; + PWM_tach_init(&the_pwm, tach_prescale); + @endcode + + */ +void PWM_tach_init +( + pwm_instance_t *pwm_inst, + pwm_tach_prescale_t tach_prescale +); + +/*-------------------------------------------------------------------------*//** + The PWM_tach_set_mode() function sets the measurement mode for the + specified tachometer input. This function selects how frequently the + tachometer input should be measured. There are two options for the measurement + mode: + - Continuous measurement of tachometer input + - One time measurement of tachometer input + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @param pwm_tach_id + The pwm_tach_id parameter identifies the target tachometer input. + + @param pwm_tachmode + The pwm_tachmode parameter is used to specify the tachometer input + measurement mode. Following values are allowed for pwm_tachmode: + - TACH_CONTINUOUS + - TACH_ONE_SHOT + + @return None + + @example + The following example sets up tachometer input 1 for one time measurement. + @code + PWM_tach_set_mode(&the_pwm, PWM_TACH_1,TACH_ONE_SHOT); + @endcode + */ +void PWM_tach_set_mode +( + pwm_instance_t * pwm_inst, + pwm_tach_id_t pwm_tach_id, + uint16_t pwm_tachmode +); + +/*-------------------------------------------------------------------------*//** + The PWM_tach_read_value() function reads the measured period of the + tachometer input signal from the TACHPULSEDUR register corresponding to + the specified tachometer input. The value returned is the number of timer + ticks between two successive positive (or negative) edges of the tachometer + input signal. + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @param pwm_tach_id + The pwm_tach_id parameter identifies the target tachometer input. + + @return + It returns the number of timer ticks between two successive positive + (or negative) edges of the tachometer input signal as a 16-bit unsigned + integer. + + @example + Read and assign the value of the current period measurement for tachometer + input 1 to a variable. + @code + uint16_t tach_input_value ; + tach_input_value = PWM_tach_read_value(&the_pwm, PWM_TACH_1); + @endcode + */ +uint16_t PWM_tach_read_value +( + pwm_instance_t * pwm_inst, + pwm_tach_id_t pwm_tach_id +); + +/*-------------------------------------------------------------------------*//** + The PWM_tach_clear_status() function clears the tachometer status bit for the + specified tachometer input. This allows the tachometer to take a new + measurement of the input signal period and store it in the TACHPULSEDUR + register corresponding to the specified tachometer input. This function is + typically called when the user want to take a new reading of the tachometer + input signal period. + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @param pwm_tach_id + The pwm_tach_id parameter identifies the target tachometer input. + + @return None + + @example + The following call clears the status bit for tachometer input 1 to begin + a new input period measurement. + @code + PWM_tach_clear_status(&the_pwm, PWM_TACH_1); + @endcode + */ +void PWM_tach_clear_status +( + pwm_instance_t * pwm_inst, + pwm_tach_id_t pwm_tach_id +); + +/*-------------------------------------------------------------------------*//** + The PWM_tach_read_status() function returns the current status for the + specified tachometer input. This function returns 1 if the tachometer input + period measurement has been updated at least once since the status bit was + cleared,otherwise, it returns 0. + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @param pwm_tach_id + The pwm_tach_id parameter identifies the target tachometer input. + + @return + It returns 1 if the tachometer input period measurement has been updated + at least once since the status bit was cleared, otherwise, it returns 0. + + @example + Read and assign the current status of tachometer input 1 to a variable. + @code + uint16_t tach_input_status ; + tach_input_status = PWM_tach_read_status(&the_pwm, PWM_TACH_1); + @endcode + */ +uint16_t PWM_tach_read_status +( + pwm_instance_t * pwm_inst, + pwm_tach_id_t pwm_tach_id +); + +/*-------------------------------------------------------------------------*//** + The PWM_tach_get_irq_source() function identifies which tachometer input + channel has generated an interrupt, among all the tachometer input channels + with interrupts enabled. + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @return + It returns the pwm_tach_id_t identifier of the tachometer input generating + the interrupt. It returns 0, if no tachometer input is generating an interrupt. + + @example + The following example returns the tachometer 1 identifier, PWM_TACH_1, + when PWM_tach_get_irq_source() is called, after the tachometer input 1 + period measurement has been completed by CorePWM. + @code + pwm_tach_id_t tach_input_no ; + PWM_tach_enable_irq( &the_pwm, PWM_TACH_1); + PWM_tach_clear_status( &the_pwm, PWM_TACH_1); + tach_input_no = PWM_tach_get_irq_source( &the_pwm ); + @endcode + */ +pwm_tach_id_t PWM_tach_get_irq_source +( + pwm_instance_t * pwm_inst +); +/*-------------------------------------------------------------------------*//** + The PWM_tach_enable_irq() function enables interrupt generation for the + specified tachometer input channel. + + Note: CorePWM asserts its interrupt output signal, when a tachometer input + channel's period measurement has been updated at least once since the + channel's interrupt was last cleared, if the channel is enabled for + interrupt generation. + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @param pwm_tach_id + The pwm_tach_id parameter identifies the target tachometer input. + + @return None + + @example + Calling the PWM_tach_enable_irq() function allows tachometer input 1 to + generate an interrupt, when its input signal period measurement value is + updated. + @code + PWM_tach_enable_irq(&the_pwm, PWM_TACH_1); + @endcode + */ +void PWM_tach_enable_irq +( + pwm_instance_t * pwm_inst, + pwm_tach_id_t pwm_tach_id +); + +/*-------------------------------------------------------------------------*//** + The PWM_tach_disable_irq() function disables interrupt generation for the + specified tachometer input channel. + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @param pwm_tach_id + The pwm_tach_id parameter identifies the target tachometer input. + + @return None + + @example + Calling the PWM_tach_disable_irq() function prevents tachometer input 1 from + generating an interrupt when its input signal period measurement value is + updated. + @code + PWM_tach_disable_irq(&the_pwm, PWM_TACH_1); + @endcode + */ +void PWM_tach_disable_irq +( + pwm_instance_t * pwm_inst, + pwm_tach_id_t pwm_tach_id +); + +/*-------------------------------------------------------------------------*//** + The PWM_tach_clear_irq() function clears a pending interrupt generated by the + specified tachometer input channel. + + Note: The PWM_tach_clear_irq() function must be called as part of a PWM + tachometer Interrupt Service Routine (ISR) in order to prevent the + same interrupt event retriggering a call to the PWM tachometer ISR. + + Note: Interrupts may also need to be cleared in the processor's interrupt + controller. + + @param pwm_inst + Pointer to a pwm_inst structure holding all relevant data associated with + the target CorePWM hardware instance. This pointer is used to identify the + target CorePWM hardware instance. + + @param pwm_tach_id + The pwm_tach_id parameter identifies the target tachometer input. + + @return None + + @example + Calling the PWM_tach_clear_irq() function clears a pending interrupt from + tachometer input 1. + @code + PWM_tach_clear_irq(&the_pwm, PWM_TACH_1); + @endcode + */ +void PWM_tach_clear_irq +( + pwm_instance_t * pwm_inst, + pwm_tach_id_t pwm_tach_id +); + +#ifdef __cplusplus +} +#endif + +#endif /*CORE_PWM_H_*/ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CorePWM/corepwm_regs.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CorePWM/corepwm_regs.h new file mode 100644 index 0000000..c447c25 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CorePWM/corepwm_regs.h @@ -0,0 +1,496 @@ +/******************************************************************************* + * Copyright 2008 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core_pwm_regs.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief IP core registers definitions. This file contains the definitions + * required for accessing the IP core through the hardware abstraction layer(HAL). + * + */ +#ifndef COREPWM_REGISTERS_H_ +#define COREPWM_REGISTERS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************* + * PRESCALE register: + *------------------------------------------------------------------------------ + * The system clock cycle is multiplied by the PRESCALE value, resulting in the + * minimum PERIOD count timebase + */ +#define PRESCALE_REG_OFFSET 0x00u + +/******************************************************************************* + * PERIOD register: + *------------------------------------------------------------------------------ + * The PRESCALE value is multiplied by the PERIOD value, yielding the PWM + * waveform cycle. Example: system clock = 40 ns, PRESCALE register = 255, + * PERIOD register = 127. The PWM waveforms will repeat every 40 ns x 256 x + * 128 = 1.31 ms. The resolution of the PWM waveforms will be 1.31ms / 128 = + * 10.23 micros. R/W 0h08 + */ +#define PERIOD_REG_OFFSET 0x04u + +/******************************************************************************* + * PWM_ENABLE_1 register: + *------------------------------------------------------------------------------ + * '1' enables each PWM output. R/W 0h00 + */ +#define PWM_ENABLE_1_REG_OFFSET 0x08u + +/******************************************************************************* + * PWM_ENABLE_2 register: + *------------------------------------------------------------------------------ + * '1' enables each PWM output. R/W 0h00 + */ +#define PWM_ENABLE_2_REG_OFFSET 0x0Cu + + +/******************************************************************************* + * PWM1_POSEDGE register: + *------------------------------------------------------------------------------ + * Sets positive edge of PWM1 output with respect to the PERIOD resolution + */ +#define PWM1_POSEDGE_REG_OFFSET 0x10u + +/******************************************************************************* + * PWM1_NEGEDGE_DACLEV register: + *------------------------------------------------------------------------------ + * Sets negative edge of PWM1 output with respect to the PERIOD resolution + */ +#define PWM1_NEGEDGE_DACLEV_REG_OFFSET 0x14u + +/******************************************************************************* + * PWM2_POSEDGE register: + *------------------------------------------------------------------------------ + * Sets positive edge of PWM2 output with respect to the PERIOD resolution + */ +#define PWM2_POSEDGE_REG_OFFSET 0x18u + +/******************************************************************************* + * PWM2_NEGEDGE_DACLEV register: + *------------------------------------------------------------------------------ + * Sets negative edge of PWM2 output with respect to the PERIOD resolution + */ +#define PWM2_NEGEDGE_DACLEV_REG_OFFSET 0x1Cu + +/******************************************************************************* + * PWM3_POSEDGE register: + *------------------------------------------------------------------------------ + * Sets positive edge of PWM3 output with respect to the PERIOD resolution + */ +#define PWM3_POSEDGE_REG_OFFSET 0x20u + +/******************************************************************************* + * PWM3_NEGEDGE_DACLEV register: + *------------------------------------------------------------------------------ + * Sets negative edge of PWM3 output with respect to the PERIOD resolution + */ +#define PWM3_NEGEDGE_DACLEV_REG_OFFSET 0x24u + +/******************************************************************************* + * PWM4_POSEDGE register: + *------------------------------------------------------------------------------ + * Sets positive edge of PWM4 output with respect to the PERIOD resolution + */ +#define PWM4_POSEDGE_REG_OFFSET 0x28u + +/******************************************************************************* + * PWM4_NEGEDGE_DACLEV register: + *------------------------------------------------------------------------------ + * Sets negative edge of PWM4 output with respect to the PERIOD resolution + */ +#define PWM4_NEGEDGE_DACLEV_REG_OFFSET 0x2Cu + +/******************************************************************************* + * PWM5_POSEDGE register: + *------------------------------------------------------------------------------ + * Sets positive edge of PWM5 output with respect to the PERIOD resolution + */ +#define PWM5_POSEDGE_REG_OFFSET 0x30u + +/******************************************************************************* + * PWM5_NEGEDGE_DACLEV register: + *------------------------------------------------------------------------------ + * Sets negative edge of PWM5 output with respect to the PERIOD resolution + */ +#define PWM5_NEGEDGE_DACLEV_REG_OFFSET 0x34u + +/******************************************************************************* + * PWM6_POSEDGE register: + *------------------------------------------------------------------------------ + * Sets positive edge of PWM6 output with respect to the PERIOD resolution + */ +#define PWM6_POSEDGE_REG_OFFSET 0x38u + +/******************************************************************************* + * PWM6_NEGEDGE_DACLEV register: + *------------------------------------------------------------------------------ + * Sets negative edge of PWM6 output with respect to the PERIOD resolution + */ +#define PWM6_NEGEDGE_DACLEV_REG_OFFSET 0x3Cu + +/******************************************************************************* + * PWM7_POSEDGE register: + *------------------------------------------------------------------------------ + * Sets positive edge of PWM7 output with respect to the PERIOD resolution + */ +#define PWM7_POSEDGE_REG_OFFSET 0x40u + +/******************************************************************************* + * PWM7_NEGEDGE_DACLEV register: + *------------------------------------------------------------------------------ + * Sets negative edge of PWM7 output with respect to the PERIOD resolution + */ +#define PWM7_NEGEDGE_DACLEV_REG_OFFSET 0x44u + +/******************************************************************************* + * PWM8_POSEDGE register: + *------------------------------------------------------------------------------ + * Sets positive edge of PWM8 output with respect to the PERIOD resolution + */ +#define PWM8_POSEDGE_REG_OFFSET 0x48u + +/******************************************************************************* + * PWM8_NEGEDGE_DACLEV register: + *------------------------------------------------------------------------------ + * Sets negative edge of PWM8 output with respect to the PERIOD resolution + */ +#define PWM8_NEGEDGE_DACLEV_REG_OFFSET 0x4Cu + +/******************************************************************************* + * PWM9_POSEDGE register: + *------------------------------------------------------------------------------ + * Sets positive edge of PWM9 output with respect to the PERIOD resolution + */ +#define PWM9_POSEDGE_REG_OFFSET 0x50u + +/******************************************************************************* + * PWM9_NEGEDGE_DACLEV register: + *------------------------------------------------------------------------------ + * Sets negative edge of PWM9 output with respect to the PERIOD resolution + */ +#define PWM9_NEGEDGE_DACLEV_REG_OFFSET 0x54u + +/******************************************************************************* + * PWM10_POSEDGE register: + *------------------------------------------------------------------------------ + * Sets positive edge of PWM10 output with respect to the PERIOD resolution + */ +#define PWM10_POSEDGE_REG_OFFSET 0x58u + +/******************************************************************************* + * PWM10_NEGEDGE_DACLEV register: + *------------------------------------------------------------------------------ + * Sets negative edge of PWM10 output with respect to the PERIOD resolution + */ +#define PWM10_NEGEDGE_DACLEV_REG_OFFSET 0x5Cu + +/******************************************************************************* + * PWM11_POSEDGE register: + *------------------------------------------------------------------------------ + * Sets positive edge of PWM11 output with respect to the PERIOD resolution + */ +#define PWM11_POSEDGE_REG_OFFSET 0x60u + +/******************************************************************************* + * PWM11_NEGEDGE_DACLEV register: + *------------------------------------------------------------------------------ + * Sets negative edge of PWM11 output with respect to the PERIOD resolution + */ +#define PWM11_NEGEDGE_DACLEV_REG_OFFSET 0x64u + +/******************************************************************************* + * PWM12_POSEDGE register: + *------------------------------------------------------------------------------ + * Sets positive edge of PWM12 output with respect to the PERIOD resolution + */ +#define PWM12_POSEDGE_REG_OFFSET 0x68u + +/******************************************************************************* + * PWM12_NEGEDGE_DACLEV register: + *------------------------------------------------------------------------------ + * Sets negative edge of PWM12 output with respect to the PERIOD resolution + */ +#define PWM12_NEGEDGE_DACLEV_REG_OFFSET 0x6Cu + +/******************************************************************************* + * PWM13_POSEDGE register: + *------------------------------------------------------------------------------ + * Sets positive edge of PWM13 output with respect to the PERIOD resolution + */ +#define PWM13_POSEDGE_REG_OFFSET 0x70u + +/******************************************************************************* + * PWM13_NEGEDGE_DACLEV register: + *------------------------------------------------------------------------------ + * Sets negative edge of PWM13 output with respect to the PERIOD resolution + */ +#define PWM13_NEGEDGE_DACLEV_REG_OFFSET 0x74u + +/******************************************************************************* + * PWM14_POSEDGE register: + *------------------------------------------------------------------------------ + * Sets positive edge of PWM14 output with respect to the PERIOD resolution + */ +#define PWM14_POSEDGE_REG_OFFSET 0x78u + +/******************************************************************************* + * PWM14_NEGEDGE_DACLEV register: + *------------------------------------------------------------------------------ + * Sets negative edge of PWM14 output with respect to the PERIOD resolution + */ +#define PWM14_NEGEDGE_DACLEV_REG_OFFSET 0x7Cu + +/******************************************************************************* + * PWM15_POSEDGE register: + *------------------------------------------------------------------------------ + * Sets positive edge of PWM15 output with respect to the PERIOD resolution + */ +#define PWM15_POSEDGE_REG_OFFSET 0x80u + +/******************************************************************************* + * PWM15_NEGEDGE_DACLEV register: + *------------------------------------------------------------------------------ + * Sets negative edge of PWM15 output with respect to the PERIOD resolution + */ +#define PWM15_NEGEDGE_DACLEV_REG_OFFSET 0x84u + +/******************************************************************************* + * PWM16_POSEDGE register: + *------------------------------------------------------------------------------ + * Sets positive edge of PWM16 output with respect to the PERIOD resolution + */ +#define PWM16_POSEDGE_REG_OFFSET 0x88u + +/******************************************************************************* + * PWM16_NEGEDGE_DACLEV register: + *------------------------------------------------------------------------------ + * Sets negative edge of PWM16 output with respect to the PERIOD resolution + */ +#define PWM16_NEGEDGE_DACLEV_REG_OFFSET 0x8Cu + +/******************************************************************************* + * SYNC_UPDATE register: + *------------------------------------------------------------------------------ + * When this bit is set to "1" and SHADOW_REG_EN is selected, all POSEDGE and + * NEGEDGE registers are updated synchronously. Synchronous updates to the PWM + * waveform occur only when SHADOW_REG_EN is asserted and SYNC_UPDATE is set to + * "1".When this bit is set to "0", all the POSEDGE and NEGEDGE registers are + * updated asynchronously. + */ +#define SYNC_UPDATE_REG_OFFSET 0xE4u + +/******************************************************************************* + * PWM_STRETCH register: + *------------------------------------------------------------------------------ + * When 0, the state of PWMx is determined by PWMx_POSEDGE NEGEDGE register + * settings. + * When 1, PWMx is set to PWM_STRETCH_VALUEx. + */ +#define PWM_STRETCH_REG_OFFSET 0x90u + +/******************************************************************************* + * TACHPRESCALE register: + *------------------------------------------------------------------------------ + * Clock prescale setting. Determines effective clock rate for the counter + * based on PCLK: + * 0000 = divide by 1 (default) + * 0001 = divide by 2 + * 0010 = divide by 4 + * 0011 = divide by 8 + * 0100 = divide by 16 + * 0101 = divide by 32 + * 0110 = divide by 64 + * 0111 = divide by 128 + * 1000 = divide by 256 + * 1001 = divide by 512 + * 1010 = divide by 1,024 + * 1011 = divide by 2,048 + * Others = divide by 2,048 + */ +#define TACHPRESCALE_REG_OFFSET 0x94u + +/******************************************************************************* + * TACHSTATUS register: + *------------------------------------------------------------------------------ + * TACH status register which contains one bit per TACH input, indicating + * whether the respective TACHPULSEDUR register has been updated at + * least once since the bit was cleared. The bits in this register gets cleared + * by writing "1", "0" does not have any effect. + */ +#define TACHSTATUS_REG_OFFSET 0x98u + +/******************************************************************************* + * TACHIRQMASK register: + *------------------------------------------------------------------------------ + * TACH interrupt mask register with one bit per tachometer signal, + * indicating whether CorePWM needs to assert an interrupt if the respective + * bit in TACHSTATUS register is asserted. + */ +#define TACHIRQMASK_REG_OFFSET 0x9Cu + +/******************************************************************************* + * TACHMODE register: + *------------------------------------------------------------------------------ + * TACH Mode. Sets the measurement mode used for each TACH input. + * When 0: TACH input is continuously measured and stored in the respective + * TACHPULSEDUR register . + * When 1: A one-time measurement is performed only if the respective bit in + * TACHSTATUS register is cleared + */ +#define TACHMODE_REG_OFFSET 0xA0u + +/******************************************************************************* + * TACHPULSEDUR_0 register: + *------------------------------------------------------------------------------ + * Stores the number of timer ticks between two successive positive (or + * negative) edges from the TACHIN[0].If the number of timer ticks exceeds + * the maximum register value, the value of 0 shall be stored instead. + */ +#define TACHPULSEDUR_0_REG_OFFSET 0xA4u + +/******************************************************************************* + * TACHPULSEDUR_1 register: + *------------------------------------------------------------------------------ + * Stores the number of timer ticks between two successive positive (or + * negative) edges from the TACHIN[1].If the number of timer ticks exceeds + * the maximum register value, the value of 0 shall be stored instead. + */ +#define TACHPULSEDUR_1_REG_OFFSET 0xA8u + +/******************************************************************************* + * TACHPULSEDUR_2 register: + *------------------------------------------------------------------------------ + * Stores the number of timer ticks between two successive positive (or + * negative) edges from the TACHIN[2].If the number of timer ticks exceeds + * the maximum register value, the value of 0 shall be stored instead. + */ +#define TACHPULSEDUR_2_REG_OFFSET 0xACu + +/******************************************************************************* + * TACHPULSEDUR_3 register: + *------------------------------------------------------------------------------ + * Stores the number of timer ticks between two successive positive (or + * negative) edges from the TACHIN[3].If the number of timer ticks exceeds + * the maximum register value, the value of 0 shall be stored instead. + */ +#define TACHPULSEDUR_3_REG_OFFSET 0xB0u + +/******************************************************************************* + * TACHPULSEDUR_4 register: + *------------------------------------------------------------------------------ + * Stores the number of timer ticks between two successive positive (or + * negative) edges from the TACHIN[4].If the number of timer ticks exceeds + * the maximum register value, the value of 0 shall be stored instead. + */ +#define TACHPULSEDUR_4_REG_OFFSET 0xB4u + +/******************************************************************************* + * TACHPULSEDUR_5 register: + *------------------------------------------------------------------------------ + * Stores the number of timer ticks between two successive positive (or + * negative) edges from the TACHIN[5].If the number of timer ticks exceeds + * the maximum register value, the value of 0 shall be stored instead. + */ +#define TACHPULSEDUR_5_REG_OFFSET 0xB8u + +/******************************************************************************* + * TACHPULSEDUR_6 register: + *------------------------------------------------------------------------------ + * Stores the number of timer ticks between two successive positive (or + * negative) edges from the TACHIN[6].If the number of timer ticks exceeds + * the maximum register value, the value of 0 shall be stored instead. + */ +#define TACHPULSEDUR_6_REG_OFFSET 0xBCu + +/******************************************************************************* + * TACHPULSEDUR_7 register: + *------------------------------------------------------------------------------ + * Stores the number of timer ticks between two successive positive (or + * negative) edges from the TACHIN[7].If the number of timer ticks exceeds + * the maximum register value, the value of 0 shall be stored instead. + */ +#define TACHPULSEDUR_7_REG_OFFSET 0xC0u + +/******************************************************************************* + * TACHPULSEDUR_8 register: + *------------------------------------------------------------------------------ + * Stores the number of timer ticks between two successive positive (or + * negative) edges from the TACHIN[8].If the number of timer ticks exceeds + * the maximum register value, the value of 0 shall be stored instead. + */ +#define TACHPULSEDUR_8_REG_OFFSET 0xC4u + +/******************************************************************************* + * TACHPULSEDUR_9 register: + *------------------------------------------------------------------------------ + * Stores the number of timer ticks between two successive positive (or + * negative) edges from the TACHIN[9].If the number of timer ticks exceeds + * the maximum register value, the value of 0 shall be stored instead. + */ +#define TACHPULSEDUR_9_REG_OFFSET 0xC8u + +/******************************************************************************* + * TACHPULSEDUR_10 register: + *------------------------------------------------------------------------------ + * Stores the number of timer ticks between two successive positive (or + * negative) edges from the TACHIN[10].If the number of timer ticks exceeds + * the maximum register value, the value of 0 shall be stored instead. + */ +#define TACHPULSEDUR_10_REG_OFFSET 0xCCu + +/******************************************************************************* + * TACHPULSEDUR_11 register: + *------------------------------------------------------------------------------ + * Stores the number of timer ticks between two successive positive (or + * negative) edges from the TACHIN[11].If the number of timer ticks exceeds + * the maximum register value, the value of 0 shall be stored instead. + */ +#define TACHPULSEDUR_11_REG_OFFSET 0xD0u + +/******************************************************************************* + * TACHPULSEDUR_12 register: + *------------------------------------------------------------------------------ + * Stores the number of timer ticks between two successive positive (or + * negative) edges from the TACHIN[12].If the number of timer ticks exceeds + * the maximum register value, the value of 0 shall be stored instead. + */ +#define TACHPULSEDUR_12_REG_OFFSET 0xD4u + +/******************************************************************************* + * TACHPULSEDUR_13 register: + *------------------------------------------------------------------------------ + * Stores the number of timer ticks between two successive positive (or + * negative) edges from the TACHIN[13].If the number of timer ticks exceeds + * the maximum register value, the value of 0 shall be stored instead. + */ +#define TACHPULSEDUR_13_REG_OFFSET 0xD8u + +/******************************************************************************* + * TACHPULSEDUR_14 register: + *------------------------------------------------------------------------------ + * Stores the number of timer ticks between two successive positive (or + * negative) edges from the TACHIN[14].If the number of timer ticks exceeds + * the maximum register value, the value of 0 shall be stored instead. + */ +#define TACHPULSEDUR_14_REG_OFFSET 0xDCu + +/******************************************************************************* + * TACHPULSEDUR_15 register: + *------------------------------------------------------------------------------ + * Stores the number of timer ticks between two successive positive (or + * negative) edges from the TACHIN[15].If the number of timer ticks exceeds + * the maximum register value, the value of 0 shall be stored instead. + */ +#define TACHPULSEDUR_15_REG_OFFSET 0xE0u + +#ifdef __cplusplus +} +#endif + +#endif /* COREPWM_REGISTERS_H_*/ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSPI/core_spi.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSPI/core_spi.c new file mode 100644 index 0000000..bf3b312 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSPI/core_spi.c @@ -0,0 +1,1345 @@ +/***************************************************************************//** + * Copyright 2013-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * CoreSPI bare metal driver implementation for CoreSPI. + * + * This Core SPI driver provides functions for implementing SPI master or + * SPI slave operations with the CoreSPI version 4.2.xxx It is not compatible + * with CoreSPI version 3.0.xxx. + * + * @file core_spi.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreSPI software configuration + * + */ + +#include "core_spi.h" +#include "corespi_regs.h" +#include + +/******************************************************************************* + * Null parameters with appropriate type definitions + */ +#define NULL_ADDR ( ( addr_t ) 0u ) +#define NULL_INSTANCE ( ( spi_instance_t * ) 0u ) +#define NULL_BUFF ( ( uint8_t * ) 0u ) +#define NULL_FRAME_HANDLER ( ( spi_frame_rx_handler_t ) 0u ) +#define NULL_BLOCK_HANDLER ( ( spi_block_rx_handler_t ) 0u ) +#define NULL_SLAVE_TX_UPDATE_HANDLER ( ( spi_slave_frame_tx_handler_t ) 0u ) +#define NULL_SLAVE_CMD_HANDLER NULL_BLOCK_HANDLER + +#define SPI_ALL_INTS (0xFFu) /* For clearing all active interrupts */ + +/******************************************************************************* + * Possible states for different register bit fields + */ + +#define DISABLE 0u +#define ENABLE 1u + + +/******************************************************************************* + * Function return values + */ +enum { + FAILURE = 0u, + SUCCESS = 1u +}; + +/******************************************************************************* + * Local function declarations + */ +static void fill_slave_tx_fifo( spi_instance_t * this_spi ); +static void read_slave_rx_fifo( spi_instance_t * this_spi ); +static void recover_from_rx_overflow( const spi_instance_t * this_spi ); + +/******************************************************************************* + * SPI_init() + * See "core_spi.h" for details of how to use this function. + */ +void SPI_init +( + spi_instance_t * this_spi, + addr_t base_addr, + uint16_t fifo_depth +) +{ + HAL_ASSERT( NULL_INSTANCE != this_spi ); + HAL_ASSERT( NULL_ADDR != base_addr ); + HAL_ASSERT( SPI_MAX_FIFO_DEPTH >= fifo_depth ); + HAL_ASSERT( SPI_MIN_FIFO_DEPTH <= fifo_depth ); + + if( ( NULL_INSTANCE != this_spi ) && ( base_addr != NULL_ADDR ) ) + { + /* + * Initialize all transmit / receive buffers and handlers + * + * Relies on the fact that byte filling with 0x00 will equate + * to 0 for any non byte sized items too. + */ + + /* First fill struct with 0s */ + memset( this_spi, 0, sizeof(spi_instance_t) ); + + /* Configure CoreSPI instance attributes */ + this_spi->base_addr = (addr_t)base_addr; + + /* Store FIFO depth or fall back to minimum if out of range */ + if( ( SPI_MAX_FIFO_DEPTH >= fifo_depth ) && ( SPI_MIN_FIFO_DEPTH <= fifo_depth ) ) + { + this_spi->fifo_depth = fifo_depth; + } + else + { + this_spi->fifo_depth = SPI_MIN_FIFO_DEPTH; + } + /* Make sure the CoreSPI is disabled while we configure it */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, DISABLE ); + + /* Ensure all slaves are deselected */ + HAL_set_8bit_reg( this_spi->base_addr, SSEL, 0u ); + + /* Flush the receive and transmit FIFOs*/ + HAL_set_8bit_reg( this_spi->base_addr, CMD, CMD_TXFIFORST_MASK | CMD_RXFIFORST_MASK ); + + /* Clear all interrupts */ + HAL_set_8bit_reg( this_spi->base_addr, INTCLR, SPI_ALL_INTS ); + + /* Ensure RXAVAIL, TXRFM, SSEND and CMDINT are disabled */ + HAL_set_8bit_reg( this_spi->base_addr, CTRL2, 0u ); + /* + * Enable the CoreSPI in the reset default of master mode + * with TXUNDERRUN, RXOVFLOW and TXDONE interrupts disabled. + * The driver does not currently use interrupts in master mode. + */ + HAL_set_8bit_reg( this_spi->base_addr, CTRL1, ENABLE | CTRL1_MASTER_MASK ); + } +} + +/***************************************************************************//** + * SPI_configure_slave_mode() + * See "core_spi.h" for details of how to use this function. + */ +void SPI_configure_slave_mode +( + spi_instance_t * this_spi +) +{ + HAL_ASSERT( NULL_INSTANCE != this_spi ); + + if( NULL_INSTANCE != this_spi ) + { + /* Don't yet know what slave transfer mode will be used */ + this_spi->slave_xfer_mode = SPI_SLAVE_XFER_NONE; + + /* Make sure the CoreSPI is disabled while we configure it */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, DISABLE ); + + /* Flush the receive and transmit FIFOs*/ + HAL_set_8bit_reg( this_spi->base_addr, CMD, CMD_TXFIFORST_MASK | CMD_RXFIFORST_MASK ); + + /* Clear all interrupts */ + HAL_set_8bit_reg( this_spi->base_addr, INTCLR, SPI_ALL_INTS ); + + /* Ensure RXAVAIL, TXRFM, SSEND and CMDINT are disabled */ + HAL_set_8bit_reg( this_spi->base_addr, CTRL2, 0u ); + /* + * Enable the CoreSPI in slave mode with TXUNDERRUN, RXOVFLOW and TXDONE + * interrupts disabled. The appropriate interrupts will be enabled later + * on when the transfer mode is configured. + */ + HAL_set_8bit_reg( this_spi->base_addr, CTRL1, ENABLE ); + } +} + +/***************************************************************************//** + * SPI_configure_master_mode() + * See "core_spi.h" for details of how to use this function. + */ +void SPI_configure_master_mode +( + spi_instance_t * this_spi +) +{ + HAL_ASSERT( NULL_INSTANCE != this_spi ); + + if( NULL_INSTANCE != this_spi ) + { + /* Disable the CoreSPI for a little while, while we configure the CoreSPI */ + HAL_set_8bit_reg_field(this_spi->base_addr, CTRL1_ENABLE, DISABLE); + + /* Reset slave transfer mode to unknown in case it has been set previously */ + this_spi->slave_xfer_mode = SPI_SLAVE_XFER_NONE; + + /* Flush the receive and transmit FIFOs*/ + HAL_set_8bit_reg( this_spi->base_addr, CMD, CMD_TXFIFORST_MASK | CMD_RXFIFORST_MASK ); + + /* Clear all interrupts */ + HAL_set_8bit_reg( this_spi->base_addr, INTCLR, SPI_ALL_INTS ); + + /* Ensure RXAVAIL, TXRFM, SSEND and CMDINT are disabled */ + HAL_set_8bit_reg( this_spi->base_addr, CTRL2, 0u ); + + /* Enable the CoreSPI in master mode with TXUNDERRUN, RXOVFLOW and TXDONE interrupts disabled */ + HAL_set_8bit_reg( this_spi->base_addr, CTRL1, ENABLE | CTRL1_MASTER_MASK ); + } +} + +/***************************************************************************//** + * SPI_set_slave_select() + * See "core_spi.h" for details of how to use this function. + */ +void SPI_set_slave_select +( + spi_instance_t * this_spi, + spi_slave_t slave +) +{ + spi_slave_t temp = (spi_slave_t)(0x00u) ; + + HAL_ASSERT( NULL_INSTANCE != this_spi ); + HAL_ASSERT( SPI_MAX_NB_OF_SLAVES > slave ); + + if( ( NULL_INSTANCE != this_spi ) && ( SPI_MAX_NB_OF_SLAVES > slave ) ) + { + /* This function is only intended to be used with an SPI master */ + if( DISABLE != HAL_get_8bit_reg_field(this_spi->base_addr, CTRL1_MASTER ) ) + { + /* Recover from receiver overflow because of previous slave */ + if( ENABLE == HAL_get_8bit_reg_field(this_spi->base_addr, STATUS_RXOVFLOW ) ) + { + recover_from_rx_overflow( this_spi ); + } + /* Set the correct slave select bit */ + temp = (spi_slave_t)( HAL_get_8bit_reg( this_spi->base_addr, SSEL ) | ((uint32_t)1u << (uint32_t)slave) ); + HAL_set_8bit_reg( this_spi->base_addr, SSEL, (uint_fast8_t)temp ); + } + } +} + +/***************************************************************************//** + * SPI_clear_slave_select() + * See "core_spi.h" for details of how to use this function. + */ +void SPI_clear_slave_select +( + spi_instance_t * this_spi, + spi_slave_t slave +) +{ + spi_slave_t temp = (spi_slave_t) (0x00u) ; + + HAL_ASSERT( NULL_INSTANCE != this_spi ); + HAL_ASSERT( SPI_MAX_NB_OF_SLAVES > slave ); + + if( ( NULL_INSTANCE != this_spi ) && ( SPI_MAX_NB_OF_SLAVES > slave ) ) + { + /* This function is only intended to be used with an SPI master. */ + if( DISABLE != HAL_get_8bit_reg_field(this_spi->base_addr, CTRL1_MASTER ) ) + { + /* Recover from receiver overflow because of previous slave */ + if( ENABLE == HAL_get_8bit_reg_field(this_spi->base_addr, STATUS_RXOVFLOW) ) + { + recover_from_rx_overflow( this_spi ); + } + /* Clear the correct slave select bit */ + temp = (spi_slave_t)( HAL_get_8bit_reg( this_spi->base_addr, SSEL ) & ~((uint32_t)1u << (uint32_t)slave) ); + HAL_set_8bit_reg( this_spi->base_addr, SSEL, (uint_fast8_t)temp ) ; + } + } +} + +/***************************************************************************//** + * SPI_transfer_frame() + * See "core_spi.h" for details of how to use this function. + */ +uint32_t SPI_transfer_frame +( + spi_instance_t * this_spi, + uint32_t tx_bits +) +{ + volatile uint32_t rx_data = 0u; /* Ensure consistent return value if in slave mode */ + + HAL_ASSERT( NULL_INSTANCE != this_spi ); + + if( NULL_INSTANCE != this_spi ) + { + /* This function is only intended to be used with an SPI master. */ + if( DISABLE != HAL_get_8bit_reg_field(this_spi->base_addr, CTRL1_MASTER ) ) + { + /* Flush the receive and transmit FIFOs by resetting both */ + HAL_set_8bit_reg(this_spi->base_addr, CMD, CMD_TXFIFORST_MASK | CMD_RXFIFORST_MASK); + + /* Send frame. */ + HAL_set_32bit_reg( this_spi->base_addr, TXLAST, tx_bits ); + + /* Wait for frame Tx to complete. */ + while ( ENABLE != HAL_get_8bit_reg_field(this_spi->base_addr, STATUS_DONE ) ) + { + ; + } + + /* Read received frame. */ + rx_data = HAL_get_32bit_reg( this_spi->base_addr, RXDATA ); + } + } + + /* Finally, return the frame we received from the slave or 0 */ + return( rx_data ); +} + + +/***************************************************************************//** + * SPI_transfer_block() + * See "core_spi.h" for details of how to use this function. + */ +void SPI_transfer_block +( + spi_instance_t * this_spi, + const uint8_t * cmd_buffer, + uint16_t cmd_byte_size, + uint8_t * rx_buffer, + uint16_t rx_byte_size +) +{ + uint32_t transfer_size = 0U; /* Total number of bytes to transfer. */ + uint16_t transfer_idx = 0U; /* Number of bytes transferred so far */ + uint16_t tx_idx = 0u; /* Number of valid data bytes sent */ + uint16_t rx_idx = 0u; /* Number of valid response bytes received */ + uint16_t transit = 0U; /* Number of bytes "in flight" to avoid FIFO errors */ + + HAL_ASSERT( NULL_INSTANCE != this_spi ); + + if( NULL_INSTANCE != this_spi ) + { + /* This function is only intended to be used with an SPI master. */ + if( ( DISABLE != HAL_get_8bit_reg_field(this_spi->base_addr, CTRL1_MASTER ) ) && + /* Check for empty transfer as well */ + ( 0u != ( (uint32_t)cmd_byte_size + (uint32_t)rx_byte_size ) ) ) + { + /* + * tansfer_size is one less than the real amount as we have to write + * the last frame separately to trigger the slave deselect in case + * the SPS option is in place. + */ + transfer_size = ( (uint32_t)cmd_byte_size + (uint32_t)rx_byte_size ) - 1u; + /* Flush the receive and transmit FIFOs */ + HAL_set_8bit_reg(this_spi->base_addr, CMD, (uint32_t)(CMD_TXFIFORST_MASK | CMD_RXFIFORST_MASK )); + + /* Recover from receiver overflow because of previous slave */ + if( ENABLE == HAL_get_8bit_reg_field(this_spi->base_addr, STATUS_RXOVFLOW) ) + { + recover_from_rx_overflow( this_spi ); + } + + /* Disable the Core SPI for a little bit, while we load the TX FIFO */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, DISABLE ); + + while( ( tx_idx < transfer_size ) && ( tx_idx < this_spi->fifo_depth ) ) + { + if( tx_idx < cmd_byte_size ) + { + /* Push out valid data */ + HAL_set_32bit_reg( this_spi->base_addr, TXDATA, (uint32_t)cmd_buffer[tx_idx] ); + } + else + { + /* Push out 0s to get data back from slave */ + HAL_set_32bit_reg( this_spi->base_addr, TXDATA, 0U ); + } + ++transit; + ++tx_idx; + } + + /* If room left to put last frame in before the off, then do it */ + if( ( tx_idx == transfer_size ) && ( tx_idx < this_spi->fifo_depth ) ) + { + if( tx_idx < cmd_byte_size ) + { + /* Push out valid data, not expecting any reply this time */ + HAL_set_32bit_reg( this_spi->base_addr, TXLAST, (uint32_t)cmd_buffer[tx_idx] ); + } + else + { + /* Push out last 0 to get data back from slave */ + HAL_set_32bit_reg( this_spi->base_addr, TXLAST, 0U ); + } + + ++transit; + ++tx_idx; + } + + /* FIFO is all loaded up so enable Core SPI to start transfer */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, ENABLE ); + + /* Perform the remainder of the transfer by sending a byte every time a byte + * has been received. This should ensure that no Rx overflow can happen in + * case of an interrupt occurring during this function. + * + * We break the transfer down into stages to minimise the processing in + * each loop as the SPI interface is very demanding at higher clock rates. + * This works well with FIFOs but might be less efficient if there is only + * a single frame buffer. + * + * First stage transfers remaining command bytes (if any). + * At this stage anything in the RX FIFO can be discarded as it is + * not part of a valid response. + */ + while( tx_idx < cmd_byte_size ) + { + if( transit < this_spi->fifo_depth ) + { + /* Send another byte. */ + if( tx_idx == transfer_size ) /* Last frame is special... */ + { + HAL_set_32bit_reg( this_spi->base_addr, TXLAST, (uint32_t)cmd_buffer[tx_idx] ); + } + else + { + HAL_set_32bit_reg( this_spi->base_addr, TXDATA, (uint32_t)cmd_buffer[tx_idx] ); + } + ++tx_idx; + ++transit; + } + if( !HAL_get_8bit_reg_field( this_spi->base_addr, STATUS_RXEMPTY ) ) + { + /* Read and discard. */ + HAL_get_32bit_reg( this_spi->base_addr, RXDATA ); + ++transfer_idx; + --transit; + } + } + /* + * Now, we are writing dummy bytes to push through the response from + * the slave but we still have to keep discarding any read data that + * corresponds with one of our command bytes. + */ + while( transfer_idx < cmd_byte_size ) + { + if( transit < this_spi->fifo_depth ) + { + if( tx_idx < transfer_size ) + { + HAL_set_32bit_reg( this_spi->base_addr, TXDATA, 0U ); + ++tx_idx; + ++transit; + } + } + if( !HAL_get_8bit_reg_field(this_spi->base_addr, STATUS_RXEMPTY ) ) + { + /* Read and discard. */ + HAL_get_32bit_reg( this_spi->base_addr, RXDATA ); + ++transfer_idx; + --transit; + } + } + /* + * Now we are now only sending dummy data to push through the + * valid response data which we store in the response buffer. + */ + while( tx_idx < transfer_size ) + { + if( transit < this_spi->fifo_depth ) + { + HAL_set_32bit_reg( this_spi->base_addr, TXDATA, 0U ); + ++tx_idx; + ++transit; + } + if( !HAL_get_8bit_reg_field(this_spi->base_addr, STATUS_RXEMPTY ) ) + { + /* Process received byte. */ + rx_buffer[rx_idx] = (uint8_t)HAL_get_32bit_reg( this_spi->base_addr, RXDATA ); + ++rx_idx; + ++transfer_idx; + --transit; + } + } + /* If we still need to send the last frame */ + while( tx_idx == transfer_size ) + { + if( transit < this_spi->fifo_depth ) + { + HAL_set_32bit_reg( this_spi->base_addr, TXLAST, 0U ); + ++tx_idx; + ++transit; + } + if( !HAL_get_8bit_reg_field( this_spi->base_addr, STATUS_RXEMPTY ) ) + { + /* Process received byte. */ + rx_buffer[rx_idx] = (uint8_t)HAL_get_32bit_reg( this_spi->base_addr, RXDATA ); + ++rx_idx; + ++transfer_idx; + --transit; + } + } + /* + * Finally, we are now finished sending data and are only reading + * valid response data which we store in the response buffer. + */ + while( transfer_idx <= transfer_size ) + { + if( !HAL_get_8bit_reg_field(this_spi->base_addr, STATUS_RXEMPTY ) ) + { + /* Process received byte. */ + rx_buffer[rx_idx] = (uint8_t)HAL_get_32bit_reg( this_spi->base_addr, RXDATA ); + ++rx_idx; + ++transfer_idx; + } + } + } + } +} + +/***************************************************************************//** + * SPI_transfer_block_store_all_resp() + * See "core_spi.h" for details of how to use this function. + */ +void SPI_transfer_block_store_all_resp +( + spi_instance_t * this_spi, + const uint8_t * cmd_buffer, + uint16_t cmd_byte_size, + uint8_t * rx_data_buffer, + uint16_t rx_byte_size, + uint8_t * cmd_response_buffer +) +{ + uint32_t transfer_size = 0U; /* Total number of bytes to transfer. */ + uint16_t transfer_idx = 0U; /* Number of bytes transferred so far */ + uint16_t tx_idx = 0u; /* Number of valid data bytes sent */ + uint16_t rx_idx = 0u; /* Number of valid response bytes received */ + uint16_t transit = 0U; /* Number of bytes "in flight" to avoid FIFO errors */ + + HAL_ASSERT( NULL_INSTANCE != this_spi ); + + if( NULL_INSTANCE != this_spi ) + { + /* This function is only intended to be used with an SPI master. */ + if( ( DISABLE != HAL_get_8bit_reg_field(this_spi->base_addr, CTRL1_MASTER ) ) && + /* Check for empty transfer as well */ + ( 0u != ( (uint32_t)cmd_byte_size + (uint32_t)rx_byte_size ) ) ) + { + /* + * tansfer_size is one less than the real amount as we have to write + * the last frame separately to trigger the slave deselect in case + * the SPS option is in place. + */ + transfer_size = ( (uint32_t)cmd_byte_size + (uint32_t)rx_byte_size ) - 1u; + /* Flush the receive and transmit FIFOs */ + HAL_set_8bit_reg(this_spi->base_addr, CMD, (uint32_t)(CMD_TXFIFORST_MASK | CMD_RXFIFORST_MASK )); + + /* Recover from receiver overflow because of previous slave */ + if( ENABLE == HAL_get_8bit_reg_field(this_spi->base_addr, STATUS_RXOVFLOW) ) + { + recover_from_rx_overflow( this_spi ); + } + + /* Disable the Core SPI for a little bit, while we load the TX FIFO */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, DISABLE ); + + while( ( tx_idx < transfer_size ) && ( tx_idx < this_spi->fifo_depth ) ) + { + if( tx_idx < cmd_byte_size ) + { + /* Push out valid data */ + HAL_set_32bit_reg( this_spi->base_addr, TXDATA, (uint32_t)cmd_buffer[tx_idx] ); + } + else + { + /* Push out 0s to get data back from slave */ + HAL_set_32bit_reg( this_spi->base_addr, TXDATA, 0U ); + } + ++transit; + ++tx_idx; + } + + /* If room left to put last frame in before the off, then do it */ + if( ( tx_idx == transfer_size ) && ( tx_idx < this_spi->fifo_depth ) ) + { + if( tx_idx < cmd_byte_size ) + { + /* Push out valid data, not expecting any reply this time */ + HAL_set_32bit_reg( this_spi->base_addr, TXLAST, (uint32_t)cmd_buffer[tx_idx] ); + } + else + { + /* Push out last 0 to get data back from slave */ + HAL_set_32bit_reg( this_spi->base_addr, TXLAST, 0U ); + } + + ++transit; + ++tx_idx; + } + + /* FIFO is all loaded up so enable Core SPI to start transfer */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, ENABLE ); + + /* Perform the remainder of the transfer by sending a byte every time a byte + * has been received. This should ensure that no Rx overflow can happen in + * case of an interrupt occurring during this function. + * + * We break the transfer down into stages to minimise the processing in + * each loop as the SPI interface is very demanding at higher clock rates. + * This works well with FIFOs but might be less efficient if there is only + * a single frame buffer. + * + * First stage transfers remaining command bytes (if any). + * At this stage anything in the RX FIFO can be discarded as it is + * not part of a valid response. + */ + while( tx_idx < cmd_byte_size ) + { + if( transit < this_spi->fifo_depth ) + { + /* Send another byte. */ + if( tx_idx == transfer_size ) /* Last frame is special... */ + { + HAL_set_32bit_reg( this_spi->base_addr, TXLAST, (uint32_t)cmd_buffer[tx_idx] ); + } + else + { + HAL_set_32bit_reg( this_spi->base_addr, TXDATA, (uint32_t)cmd_buffer[tx_idx] ); + } + ++tx_idx; + ++transit; + } + if( !HAL_get_8bit_reg_field( this_spi->base_addr, STATUS_RXEMPTY ) ) + { + /* Process received command byte. */ + cmd_response_buffer[transfer_idx] = HAL_get_32bit_reg( this_spi->base_addr, RXDATA ); + ++transfer_idx; + --transit; + } + } + /* + * Now, we are writing dummy bytes to push through the response from + * the slave, which we store in the command response buffer. + */ + while( transfer_idx < cmd_byte_size ) + { + if( transit < this_spi->fifo_depth ) + { + if( tx_idx < transfer_size ) + { + HAL_set_32bit_reg( this_spi->base_addr, TXDATA, 0U ); + ++tx_idx; + ++transit; + } + } + if( !HAL_get_8bit_reg_field(this_spi->base_addr, STATUS_RXEMPTY ) ) + { + /* Process received command byte. */ + cmd_response_buffer[transfer_idx] = HAL_get_32bit_reg( this_spi->base_addr, RXDATA ); + ++transfer_idx; + --transit; + } + } + /* + * Now we are now only sending dummy data to push through the + * valid response data which we store in the data response buffer. + */ + while( tx_idx < transfer_size ) + { + if( transit < this_spi->fifo_depth ) + { + HAL_set_32bit_reg( this_spi->base_addr, TXDATA, 0U ); + ++tx_idx; + ++transit; + } + if( !HAL_get_8bit_reg_field(this_spi->base_addr, STATUS_RXEMPTY ) ) + { + /* Process received data byte. */ + rx_data_buffer[rx_idx] = (uint8_t)HAL_get_32bit_reg( this_spi->base_addr, RXDATA ); + ++rx_idx; + ++transfer_idx; + --transit; + } + } + /* If we still need to send the last frame */ + while( tx_idx == transfer_size ) + { + if( transit < this_spi->fifo_depth ) + { + HAL_set_32bit_reg( this_spi->base_addr, TXLAST, 0U ); + ++tx_idx; + ++transit; + } + if( !HAL_get_8bit_reg_field( this_spi->base_addr, STATUS_RXEMPTY ) ) + { + /* Process received data byte. */ + rx_data_buffer[rx_idx] = (uint8_t)HAL_get_32bit_reg( this_spi->base_addr, RXDATA ); + ++rx_idx; + ++transfer_idx; + --transit; + } + } + /* + * Finally, we are now finished sending data and are only reading + * valid response data which we store in the data response buffer. + */ + while( transfer_idx <= transfer_size ) + { + if( !HAL_get_8bit_reg_field(this_spi->base_addr, STATUS_RXEMPTY ) ) + { + /* Process received data byte. */ + rx_data_buffer[rx_idx] = (uint8_t)HAL_get_32bit_reg( this_spi->base_addr, RXDATA ); + ++rx_idx; + ++transfer_idx; + } + } + } + } +} +/***************************************************************************//** + * SPI_set_frame_rx_handler() + * See "core_spi.h" for details of how to use this function. + */ +void SPI_set_frame_rx_handler +( + spi_instance_t * this_spi, + spi_frame_rx_handler_t rx_handler +) +{ + HAL_ASSERT( NULL_INSTANCE != this_spi ); + + if(NULL_INSTANCE != this_spi) + { + /* This function is only intended to be used with an SPI slave. */ + if(DISABLE == HAL_get_8bit_reg_field(this_spi->base_addr, CTRL1_MASTER)) + { + /* Disable the Core SPI while we configure */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, DISABLE ); + + /* Clear all interrupts */ + HAL_set_8bit_reg( this_spi->base_addr, INTCLR, SPI_ALL_INTS ); + + /* Disable SSEND and CMD interrupts as we are not doing block transfers */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL2_INTSSEND, DISABLE ); + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL2_INTCMD, DISABLE ); + + /* Disable block Rx handler as they are mutually exclusive. */ + this_spi->block_rx_handler = 0U; + + /* Keep a copy of the pointer to the Rx handler function. */ + this_spi->frame_rx_handler = rx_handler; + + if( SPI_SLAVE_XFER_FRAME != this_spi->slave_xfer_mode ) + { + /* + * Either just coming from init or were previously in block mode + * so no tx frame handler is set at this point in time... + * + * Don't allow TXDONE interrupts. + */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_INTTXDONE, DISABLE ); + } + + /* Flush the receive and transmit FIFOs*/ + HAL_set_8bit_reg(this_spi->base_addr, CMD, CMD_TXFIFORST_MASK | CMD_RXFIFORST_MASK); + + /* Enable Rx and FIFO error interrupts */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_INTRXOVFLOW, ENABLE ); + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_INTTXURUN, ENABLE ); + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL2_INTRXDATA, ENABLE ); + + /* Make sure correct mode is selected */ + this_spi->slave_xfer_mode = SPI_SLAVE_XFER_FRAME; + + /* Finally re-enable the CoreSPI */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, ENABLE ); + } + } +} + +/***************************************************************************//** + * SPI_set_slave_tx_frame() + * See "core_spi.h" for details of how to use this function. + */ +void SPI_set_slave_tx_frame +( + spi_instance_t * this_spi, + uint32_t frame_value, + spi_slave_frame_tx_handler_t slave_tx_frame_handler +) +{ + HAL_ASSERT( NULL_INSTANCE != this_spi ); + + if( NULL_INSTANCE != this_spi ) + { + /* This function is only intended to be used with an SPI slave. */ + if( DISABLE == HAL_get_8bit_reg_field(this_spi->base_addr, CTRL1_MASTER ) ) + { + /* Disable the Core SPI while we configure */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, DISABLE ); + + /* Clear all interrupts */ + HAL_set_8bit_reg( this_spi->base_addr, INTCLR, SPI_ALL_INTS ); + + /* Disable SSEND and CMD interrupts as we are not doing block transfers */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL2_INTSSEND, DISABLE ); + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL2_INTCMD, DISABLE ); + + if( SPI_SLAVE_XFER_FRAME != this_spi->slave_xfer_mode ) + { + /* + * Either just coming from init or were previously in block mode + * so no rx frame handler is set at this point in time... + * + * Don't allow RXDATA interrupts. + */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL2_INTRXDATA, DISABLE ); + } + + /* Disable slave block tx buffer as it is mutually exclusive with frame + * level handling. */ + this_spi->slave_tx_buffer = NULL_BUFF; + this_spi->slave_tx_size = 0U; + this_spi->slave_tx_idx = 0U; + + /* Flush the receive and transmit FIFOs*/ + HAL_set_8bit_reg(this_spi->base_addr, CMD, CMD_TXFIFORST_MASK | CMD_RXFIFORST_MASK); + + /* Assign the slave frame update handler - NULL_SLAVE_TX_UPDATE_HANDLER for none */ + this_spi->slave_tx_frame_handler = slave_tx_frame_handler; + + /* Keep a copy of the slave Tx frame value. */ + this_spi->slave_tx_frame = frame_value; + + /* Load one frame into Tx data register. */ + HAL_set_32bit_reg( this_spi->base_addr, TXLAST, this_spi->slave_tx_frame ); + + /* Enable Tx Done interrupt in order to reload the slave Tx frame after each + * time it has been sent. */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_INTTXDONE, ENABLE ); + + /* Make sure correct mode is selected */ + this_spi->slave_xfer_mode = SPI_SLAVE_XFER_FRAME; + + /* Ready to go so enable CoreSPI */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, ENABLE ); + } + } +} + +/***************************************************************************//** + * SPI_set_slave_block_buffers() + * See "core_spi.h" for details of how to use this function. + */ +void SPI_set_slave_block_buffers +( + spi_instance_t * this_spi, + const uint8_t * tx_buffer, + uint32_t tx_buff_size, + uint8_t * rx_buffer, + uint32_t rx_buff_size, + spi_block_rx_handler_t block_rx_handler +) +{ + HAL_ASSERT( NULL_INSTANCE != this_spi ); + + if( NULL_INSTANCE != this_spi ) + { + /* This function is only intended to be used with an SPI slave. */ + if( DISABLE == HAL_get_8bit_reg_field(this_spi->base_addr, CTRL1_MASTER ) ) + { + /* Disable the Core SPI while we configure */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, DISABLE ); + + /* Make sure correct mode is selected */ + this_spi->slave_xfer_mode = SPI_SLAVE_XFER_BLOCK; + /* + * No command handler should be setup at this stage so fake this + * to ensure 0 padding works. + */ + this_spi->cmd_done = 1u; + + /* Disable frame handlers as they are mutually exclusive with block Rx handler. */ + this_spi->frame_rx_handler = NULL_FRAME_HANDLER; + this_spi->slave_tx_frame_handler = NULL_SLAVE_TX_UPDATE_HANDLER; + + /* Keep a copy of the pointer to the block Rx handler function. */ + this_spi->block_rx_handler = block_rx_handler; + + /* Assign slave receive buffer */ + this_spi->slave_rx_buffer = rx_buffer; + this_spi->slave_rx_size = rx_buff_size; + this_spi->slave_rx_idx = 0U; + + /* Assign slave transmit buffer*/ + this_spi->slave_tx_buffer = tx_buffer; + this_spi->slave_tx_size = tx_buff_size; + this_spi->slave_tx_idx = 0U; + + /* Flush the receive and transmit FIFOs */ + HAL_set_8bit_reg( this_spi->base_addr, CMD, CMD_TXFIFORST_MASK | CMD_RXFIFORST_MASK ); + + /* Clear all interrupts */ + HAL_set_8bit_reg( this_spi->base_addr, INTCLR, SPI_ALL_INTS ); + + /* Preload the transmit FIFO. */ + while( !(HAL_get_8bit_reg_field(this_spi->base_addr, STATUS_TXFULL)) && + ( this_spi->slave_tx_idx < this_spi->slave_tx_size ) ) + { + HAL_set_32bit_reg( this_spi->base_addr, TXDATA, (uint32_t)this_spi->slave_tx_buffer[this_spi->slave_tx_idx] ); + ++this_spi->slave_tx_idx; + } + /* + * Disable TXDATA interrupt as we will look after transmission in rx handling + * because we know that once we have read a frame it is safe to send another one. + */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL2_INTTXDATA, DISABLE ); + + /* Enable Rx, FIFO error and SSEND interrupts */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_INTRXOVFLOW, ENABLE ); + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_INTTXURUN, ENABLE ); + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL2_INTRXDATA, ENABLE ); + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL2_INTSSEND, ENABLE ); + + /* Disable command handler until it is set explicitly */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL2_INTCMD, DISABLE ); + + /* Now enable the CoreSPI */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, ENABLE ); + } + } +} + +/***************************************************************************//** + * SPI_set_cmd_handler() + * See "core_spi.h" for details of how to use this function. + */ +void SPI_set_cmd_handler +( + spi_instance_t * this_spi, + spi_block_rx_handler_t cmd_handler, + uint32_t cmd_size +) +{ + uint32_t ctrl2 = 0u; + + HAL_ASSERT( NULL_INSTANCE != this_spi ); + HAL_ASSERT( NULL_SLAVE_CMD_HANDLER != cmd_handler ); + HAL_ASSERT( 0u < cmd_size ); + + if( ( NULL_INSTANCE != this_spi ) && ( 0u < cmd_size ) && + ( NULL_SLAVE_CMD_HANDLER != cmd_handler ) ) + { + /* Disable the Core SPI while we configure */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, DISABLE ); + /* + * Note we don't flush the FIFOs as this has been done already when + * block mode was configured. + * + * Clear this flag so zero padding is disabled until command response + * has been taken care of. + */ + this_spi->cmd_done = 0u; + + /* Assign user handler for Command received interrupt */ + this_spi->cmd_handler = cmd_handler; + + /* Configure the command size and Enable Command received interrupt */ + ctrl2 = HAL_get_8bit_reg( this_spi->base_addr, CTRL2 ); + + /* First clear the count field then insert count and int enables */ + ctrl2 &= ~(uint32_t)CTRL2_CMDSIZE_MASK; + ctrl2 |= (uint32_t)((cmd_size & CTRL2_CMDSIZE_MASK) | CTRL2_INTCMD_MASK | CTRL2_INTRXDATA_MASK); + HAL_set_8bit_reg( this_spi->base_addr, CTRL2, ctrl2 ); + + /* Now enable the CoreSPI */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, ENABLE ); + } +} + +/***************************************************************************//** + * SPI_set_cmd_response() + * See "core_spi.h" for details of how to use this function. + */ +void SPI_set_cmd_response +( + spi_instance_t * this_spi, + const uint8_t * resp_tx_buffer, + uint32_t resp_buff_size +) +{ + HAL_ASSERT( NULL_INSTANCE != this_spi ); + HAL_ASSERT( NULL_BUFF != resp_tx_buffer ); + HAL_ASSERT( 0u < resp_buff_size ); + + if( ( NULL_INSTANCE != this_spi ) && ( 0u < resp_buff_size ) && + ( NULL_BUFF != resp_tx_buffer ) ) + { + this_spi->resp_tx_buffer = resp_tx_buffer; + this_spi->resp_buff_size = resp_buff_size; + this_spi->resp_buff_tx_idx = 0u; + + fill_slave_tx_fifo(this_spi); + } +} + + +/***************************************************************************//** + * SPI_enable() + * See "core_spi.h" for details of how to use this function. + */ +void SPI_enable +( + spi_instance_t * this_spi +) +{ + HAL_ASSERT( NULL_INSTANCE != this_spi ); + + if( NULL_INSTANCE != this_spi ) + { + /* Disable the Core SPI while we configure */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, ENABLE ); + } +} + + +/***************************************************************************//** + * SPI_disable() + * See "core_spi.h" for details of how to use this function. + */ +void SPI_disable +( + spi_instance_t * this_spi +) +{ + HAL_ASSERT( NULL_INSTANCE != this_spi ); + + if( NULL_INSTANCE != this_spi ) + { + /* Disable the Core SPI while we configure */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, DISABLE ); + } +} + + +/***************************************************************************//** + * SPI interrupt service routine. + */ +void SPI_isr +( + spi_instance_t * this_spi +) +{ + uint32_t rx_frame; + int32_t guard; + +/* + * The assert and the NULL check here can be commented out to reduce the interrupt + * latency once you are sure the interrupt vector code is correct. + */ + HAL_ASSERT( NULL_INSTANCE != this_spi ); + if( NULL_INSTANCE != this_spi ) + { + /* Handle receive. */ + if( ENABLE == HAL_get_8bit_reg_field( this_spi->base_addr, INTMASK_RXDATA ) ) + { + /* + * Service receive data according to transfer mode in operation. + * + * We check block mode first as this is most likely to have back to back + * transfers with multiple bytes. + * + * Note the order of the checks here will effect interrupt latency and + * for critical timing the mode you are using most often should probably be + * be the first checked. + */ + if( SPI_SLAVE_XFER_BLOCK == this_spi->slave_xfer_mode ) /* Block handling mode. */ + { + while( 0u == HAL_get_8bit_reg_field( this_spi->base_addr, STATUS_RXEMPTY ) ) + { + /* Read irrespective to clear the RX IRQ */ + rx_frame = HAL_get_32bit_reg( this_spi->base_addr, RXDATA ); + if( this_spi->slave_rx_idx < this_spi->slave_rx_size ) + { + this_spi->slave_rx_buffer[this_spi->slave_rx_idx] = (uint8_t)rx_frame; + } + ++this_spi->slave_rx_idx; + } + /* + * Now handle updating of tx FIFO to keep the data flowing. + * First see if there is anything in slave_tx_buffer to send. + */ + while( ( this_spi->slave_tx_idx < this_spi->slave_tx_size ) + && ( 0u == HAL_get_8bit_reg_field( this_spi->base_addr, STATUS_TXFULL ) ) ) + { + HAL_set_32bit_reg( this_spi->base_addr, TXDATA, (uint32_t)this_spi->slave_tx_buffer[this_spi->slave_tx_idx] ); + ++this_spi->slave_tx_idx; + } + /* + * Next see if there is anything in resp_tx_buffer to send. + */ + if( this_spi->slave_tx_idx >= this_spi->slave_tx_size ) + { + while( ( this_spi->resp_buff_tx_idx < this_spi->resp_buff_size ) + && ( 0u == HAL_get_8bit_reg_field( this_spi->base_addr, STATUS_TXFULL ) ) ) + { + HAL_set_32bit_reg( this_spi->base_addr, TXDATA, (uint32_t)this_spi->resp_tx_buffer[this_spi->resp_buff_tx_idx] ); + ++this_spi->resp_buff_tx_idx; + } + } + /* + * Lastly, see if we are ready to pad with 0s . + */ + if( this_spi->cmd_done && ( this_spi->slave_tx_idx >= this_spi->slave_tx_size ) && + ( this_spi->resp_buff_tx_idx >= this_spi->resp_buff_size ) ) + { + guard = 1 + ((int32_t)this_spi->fifo_depth / 4); + while( ( 0u == HAL_get_8bit_reg_field( this_spi->base_addr, STATUS_TXFULL ) ) + && ( 0 != guard ) ) + { + /* + * Pad TX FIFO with 0s for consistent behaviour if the master + * tries to transfer more than we expected. + */ + HAL_set_32bit_reg(this_spi->base_addr, TXDATA, 0x00u); + /* + * We use the guard count to cover the event that we are never + * seeing the TX FIFO full because the data is being pulled + * out as fast as we can stuff it in. In this case we never spend + * more than our allocated time spinning here. + */ + guard--; + } + } + } + else if( SPI_SLAVE_XFER_FRAME == this_spi->slave_xfer_mode ) /* Single frame handling mode. */ + { + while( 0u == HAL_get_8bit_reg_field( this_spi->base_addr, STATUS_RXEMPTY ) ) + { + rx_frame = HAL_get_32bit_reg( this_spi->base_addr, RXDATA ); + if( NULL_FRAME_HANDLER != this_spi->frame_rx_handler ) + { + this_spi->frame_rx_handler( rx_frame ); + } + } + } + else /* Slave transfer mode not set up so discard anything in RX FIFO */ + { + HAL_set_8bit_reg( this_spi->base_addr, CMD, CMD_RXFIFORST_MASK ); + } + + HAL_set_8bit_reg_field( this_spi->base_addr, INTCLR_RXDATA, ENABLE ); + } + + /* Handle transmit. */ + if( ENABLE == HAL_get_8bit_reg_field( this_spi->base_addr, INTMASK_TXDONE ) ) + { + /* + * Note, the driver only currently uses the txdone interrupt when + * in frame transmit mode. In block mode all TX handling is done by the + * receive interrupt handling code as we know that for every frame received + * a frame must be placed in the TX FIFO. + */ + if( SPI_SLAVE_XFER_FRAME == this_spi->slave_xfer_mode ) + { + /* Execute the user callback to update the slave_tx_frame */ + if( NULL_SLAVE_TX_UPDATE_HANDLER != this_spi->slave_tx_frame_handler ) + { + this_spi->slave_tx_frame_handler ( this_spi ); + } + + /* Reload slave tx frame into Tx data register. */ + HAL_set_32bit_reg( this_spi->base_addr, TXLAST, this_spi->slave_tx_frame ); + } + else if( SPI_SLAVE_XFER_BLOCK != this_spi->slave_xfer_mode ) + { + /* Slave transfer mode not set up so discard anything in TX FIFO */ + HAL_set_8bit_reg( this_spi->base_addr, CMD, CMD_TXFIFORST_MASK ); + } + else + { + /* Nothing to do, no slave mode configured */ + } + + HAL_set_8bit_reg_field( this_spi->base_addr, INTCLR_TXDONE, ENABLE ); + } + + + /* Handle receive overflow. */ + if( ENABLE == HAL_get_8bit_reg_field(this_spi->base_addr, INTMASK_RXOVERFLOW)) + { + HAL_set_8bit_reg(this_spi->base_addr, CMD, CMD_RXFIFORST_MASK); + HAL_set_8bit_reg_field(this_spi->base_addr, INTCLR_RXOVERFLOW, ENABLE); + } + + /* Handle transmit under run. */ + if( ENABLE == HAL_get_8bit_reg_field( this_spi->base_addr, INTMASK_TXUNDERRUN ) ) + { + HAL_set_8bit_reg( this_spi->base_addr, CMD, CMD_TXFIFORST_MASK ); + HAL_set_8bit_reg_field( this_spi->base_addr, INTCLR_TXUNDERRUN, ENABLE ); + } + + /* Handle command interrupt. */ + if( ENABLE == HAL_get_8bit_reg_field( this_spi->base_addr, INTMASK_CMDINT ) ) + { + read_slave_rx_fifo( this_spi ); + + /* + * Call the command handler if one exists. + */ + if( NULL_SLAVE_CMD_HANDLER != this_spi->cmd_handler ) + { + this_spi->cmd_handler( this_spi->slave_rx_buffer, this_spi->slave_rx_idx ); + } + this_spi->cmd_done = 1u; + /* Disable command interrupt until slave select becomes de-asserted to avoid retriggering. */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL2_INTCMD, DISABLE ); + HAL_set_8bit_reg_field( this_spi->base_addr, INTCLR_CMDINT, ENABLE ); + } + + /* Handle slave select becoming de-asserted. */ + if( ENABLE == HAL_get_8bit_reg_field( this_spi->base_addr, INTMASK_SSEND) ) + { + /* Only supposed to do all this if transferring blocks... */ + if(SPI_SLAVE_XFER_BLOCK == this_spi->slave_xfer_mode) + { + uint32_t rx_size; + + /* Empty any remaining bytes in RX FIFO */ + read_slave_rx_fifo( this_spi ); + rx_size = this_spi->slave_rx_idx; + /* + * Re-enable command interrupt if required. + * Must be done before re loading FIFO to ensure stale response + * data is not pushed into the FIFO. + */ + if(NULL_SLAVE_CMD_HANDLER != this_spi->cmd_handler) + { + this_spi->cmd_done = 0u; + this_spi->resp_tx_buffer = 0u; + this_spi->resp_buff_size = 0u; + this_spi->resp_buff_tx_idx = 0u; + HAL_set_8bit_reg_field( this_spi->base_addr, INTCLR_CMDINT, ENABLE ); + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL2_INTCMD, ENABLE ); + } + /* + * Reset the transmit index to 0 to restart transmit at the start of the + * transmit buffer in the next transaction. This also requires flushing + * the Tx FIFO and refilling it with the start of Tx data buffer. + */ + this_spi->slave_tx_idx = 0u; + HAL_set_8bit_reg( this_spi->base_addr, CMD, CMD_TXFIFORST_MASK | CMD_RXFIFORST_MASK ); + fill_slave_tx_fifo( this_spi ); + + /* Prepare to receive next packet. */ + this_spi->slave_rx_idx = 0u; + /* + * Call the receive handler if one exists. + */ + if( NULL_BLOCK_HANDLER != this_spi->block_rx_handler ) + { + this_spi->block_rx_handler( this_spi->slave_rx_buffer, rx_size ); + } + + HAL_set_8bit_reg_field( this_spi->base_addr, INTCLR_RXDATA, ENABLE ); + } + + HAL_set_8bit_reg_field( this_spi->base_addr, INTCLR_SSEND, ENABLE ); + } + } +} + +/******************************************************************************* + * Local function definitions + */ + +/***************************************************************************//** + * Fill the transmit FIFO (used for slave block transfers). + */ +static void fill_slave_tx_fifo +( + spi_instance_t * this_spi +) +{ + /* First see if slave_tx_buffer needs transmitting */ + while( ( this_spi->slave_tx_idx < this_spi->slave_tx_size ) && + !HAL_get_8bit_reg_field( this_spi->base_addr, STATUS_TXFULL ) ) + { + HAL_set_32bit_reg( this_spi->base_addr, TXDATA, (uint32_t)this_spi->slave_tx_buffer[this_spi->slave_tx_idx] ); + ++this_spi->slave_tx_idx; + } + + /* Then see if it is safe to look at putting resp_tx_buffer in FIFO? */ + if( this_spi->slave_tx_idx >= this_spi->slave_tx_size ) + { + while( ( this_spi->resp_buff_tx_idx < this_spi->resp_buff_size ) && + !HAL_get_8bit_reg_field( this_spi->base_addr, STATUS_TXFULL ) ) + { + HAL_set_32bit_reg( this_spi->base_addr, TXDATA, (uint32_t)this_spi->resp_tx_buffer[this_spi->resp_buff_tx_idx] ); + ++this_spi->resp_buff_tx_idx; + } + } +} + +/***************************************************************************//** + * + */ +static void read_slave_rx_fifo +( + spi_instance_t * this_spi +) +{ + uint32_t rx_frame; + + if( SPI_SLAVE_XFER_BLOCK == this_spi->slave_xfer_mode ) /* Block handling mode. */ + { + while( !HAL_get_8bit_reg_field( this_spi->base_addr, STATUS_RXEMPTY ) ) + { + rx_frame = HAL_get_32bit_reg( this_spi->base_addr, RXDATA ); /* Read irresepective to clear the RX IRQ */ + if( this_spi->slave_rx_idx < this_spi->slave_rx_size ) + { + this_spi->slave_rx_buffer[this_spi->slave_rx_idx] = (uint8_t)rx_frame; + } + ++this_spi->slave_rx_idx; + } + } + else if( SPI_SLAVE_XFER_FRAME == this_spi->slave_xfer_mode ) /* Frame handling mode */ + { + while( !HAL_get_8bit_reg_field( this_spi->base_addr, STATUS_RXEMPTY ) ) + { + /* Single frame handling mode. */ + rx_frame = HAL_get_32bit_reg( this_spi->base_addr, RXDATA ); + if( NULL_FRAME_HANDLER != this_spi->frame_rx_handler ) + { + this_spi->frame_rx_handler( rx_frame ); + } + } + } + else /* Slave transfer mode not set up so discard anything in RX FIFO */ + { + HAL_set_8bit_reg( this_spi->base_addr, CMD, CMD_RXFIFORST_MASK ); + } +} + +/***************************************************************************//** + * This function is to recover the CoreSPI from receiver overflow. + * It temporarily disables the CoreSPI from interacting with external world, flushes + * the transmit and receiver FIFOs, clears all interrupts and then re-enables + * the CoreSPI instance referred by this_spi parameter. + */ +static void recover_from_rx_overflow +( + const spi_instance_t * this_spi +) +{ + /* Disable CoreSPI */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, DISABLE ); + + /* Reset TX and RX FIFOs */ + HAL_set_8bit_reg( this_spi->base_addr, CMD, CMD_TXFIFORST_MASK | CMD_RXFIFORST_MASK ); + + /* Clear all interrupts */ + HAL_set_8bit_reg( this_spi->base_addr, INTCLR, SPI_ALL_INTS ); + + /* Enable CoreSPI */ + HAL_set_8bit_reg_field( this_spi->base_addr, CTRL1_ENABLE, ENABLE ); +} + + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSPI/core_spi.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSPI/core_spi.h new file mode 100644 index 0000000..ef16fef --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSPI/core_spi.h @@ -0,0 +1,1324 @@ +/***************************************************************************//** + * Copyright 2013-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Core SPI bare metal software driver public API. + * + * This Core SPI driver provides functions for implementing SPI master or + * SPI slave operations with the CoreSPI version 4.2.xxx It is not compatible + * with CoreSPI version 3.0.xxx. + * + * The Core SPI driver supports two classes of data transfer operation: + * SPI frame operation or SPI block transfer operations. + * + * Frame operations allow transferring individual SPI frames from 4 to 32-bits + * in length. Block operations allow transferring blocks of data organized as + * 8-bit frames. + * + * @file core_spi.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreSPI prototypes + * + */ + +/*=========================================================================*//** + @mainpage Core SPI Bare Metal Driver. + + ============================================================================== + Introduction + ============================================================================== + CoreSPI is an IP component that implements a full-duplex, synchronous, and + configurable serial peripheral interface (SPI) with frame sizes from 4 to 32 + bits and bus interface sizes of 8-, 16-, or 32-bit. Each CoreSPI instance + communicates with up to eight slave devices. + + This driver provides a set of functions for controlling CoreSPI as part of + the bare metal system where no operating system is available. These drivers + can be adapted to be used as a part of an operating system, but the + implementation of the adaptation layer between driver and the operating + system's driver model is outside the scope of this User's Guide. + + ============================================================================== + Driver Configuration + ============================================================================== + Your application software should configure the CoreSPI driver through calls + to the SPI_init() function for each CoreSPI instance in the hardware design. + This function configures a default set of parameters that include a CoreSPI + hardware instance base address and the depth of the FIFOs for this instance. + + The CoreSPI instance is configured at the time of instantiation in hardware + design for APB width, frame size, FIFO depth, serial clock speed, serial + clock polarity, serial clock phase, and slave select state parameters. + + CoreSPI can communicate with up to eight different slave devices that match + the CoreSPI configuration at the time of hardware instantiation. + + The functions SPI_configure_slave_mode() and SPI_configure_master_mode() are + used to configure the CoreSPI instance as a master or slave as required by + the application. + + When CoreSPI wishes to communicate with a specific slave device, call + the SPI_set_slave_select() function with the slave number as an argument. + This function selects the slave device. A previously selected slave gets + unselected by calling the SPI_clear_slave_select() function. + + ============================================================================== + Theory of Operation + ============================================================================== + The CoreSPI driver functions are grouped into the following categories: + • Initialization + • Configuration for either master or slave operations + • SPI master frame transfer control + • SPI master block transfer control + • SPI slave frame transfer control + • SPI slave block transfer control + + Frame transfers allow CoreSPI to write or read up to 32-bits of data in a + single SPI transaction. For example, a frame transfer of 12-bits might be used + to read the result of an ADC conversion from a SPI analog to digital converter. + + Block transfers allow CoreSPI to write and/or read a number of bytes in a single + SPI transaction. With the driver as is, block transfer transactions allow data + transfers in multiples of 8-bits (8, 16, 24, 32, 40,...) and the CoreSPI instance + has to be configured for 8-bit frames. For other frame sizes, the + SPI_transfer_block() code can act as a template for developing a frame block + transfer function. + Block transfers are typically used with byte oriented devices like SPI + FLASH devices. + + Note: The CoreSPI instance in the hardware design must be configured for + the frame size required by the application; configuration by driver is + not possible. + + -------------------------------- + Initialization + -------------------------------- + The CoreSPI driver is initialized through a call to the SPI_init() function. + The SPI_init() function takes a pointer to the global CoreSPI instance data + structure of type spi_instance_t and the base address of the CoreSPI instance + as defined by the hardware design. The CoreSPI instance global data structure + is used by the driver to store state information for each CoreSPI instance. + A pointer to these data structures is also used as the first parameter to + any of the driver functions to identify which CoreSPI will be used by the + called function. It is the responsibility of the application programmer to + create and maintain these global CoreSPI instance data structures. Any call + to a CoreSPI driver function should be of the form SPI_function_name + ( &g_core_spi0, ... ). + The SPI_init() function resets the transmit and receives FIFOs of CoreSPI + instance being initialized. + The SPI_init() function must be called before any other CoreSPI driver + functions can be called. + + ---------------------------------------------------- + Configuration + ---------------------------------------------------- + A CoreSPI instance can operate either as a master or as a slave SPI device. + There are two distinct functions for configuring a CoreSPI instance for + master or slave operations. + + ## Master Configuration + The SPI_configure_master_mode() function configures the specified CoreSPI + block for operations as an SPI master. This function must be called once + before the CoreSPI block communicates with an SPI slave device. + + ## Slave Configuration + The SPI_configure_slave_mode() function configures the specified CoreSPI + block for operations as a SPI slave. This function must be called after + calling the SPI_init() to configure the CoreSPI instance referred by this_spi + parameter to operate in the slave mode. + + ------------------------------------- + SPI Master Frame Transfer Control + ------------------------------------- + The following functions are used as a part of the SPI master frame transfers: + • SPI_set_slave_select() + • SPI_transfer_frame() + • SPI_clear_slave_select() + + The master must first select the target slave or slaves to be addressed by + calling the SPI_set_slave_select() function. This causes the relevant select + line(s) to become asserted while data is clocked out onto the SPI data line. + + A function call is then made to SPI_transfer_frame() specifying the value + of the data frame to be sent and returning the value read. + + After the transfer is complete, use the SPI_clear_slave_select() function + to prevent this slave select line from being asserted during subsequent SPI + transactions. A call to this function is required only if the master is + communicating with multiple slave devices. + + ------------------------------------- + SPI Master Block Transfer Control + ------------------------------------- + The following functions are used as a part of the SPI master block transfers: + • SPI_set_slave_select() + • SPI_transfer_block() + • SPI_clear_slave_select() + + The master must first select the target slave or slaves by calling + SPI_set_slave_select(). This causes the relevant slave select line(s) to + become asserted while data is clocked out onto the SPI data line. + Alternatively, a general purpose input/output (GPIO) can be used to control + the state of the target slave device's chip select signal. + + A call is then made to the SPI_transfer_block() function. The parameters of + this function specify the following: + • The number of bytes to be transmitted + • A pointer to the buffer containing the data to be transmitted + • The number of bytes to be received + • A pointer to the buffer where the received data gets stored + + The number of bytes to be transmitted can be set to zero to indicate that the + transfer is purely a block read transfer. The number of bytes to be received + can be set to zero to specify that the transfer is purely a block write + transfer. + + Block mode transfers as implemented by the driver are effectively half duplex + as we do not store the values received from the slave device whilst we are + transmitting. If full duplex operation is required, the driver + SPI_transfer_block() function can serve as a starting point for implementing + full duplex block transfers. + + The SPI_clear_slave_select() function can be used after the transfer is + complete to prevent this slave select line from being asserted during + subsequent SPI transactions. A call to this function is only required if + the master is communicating with multiple slave devices. + + ------------------------------------- + SPI Slave Frame Transfer Control + ------------------------------------- + The following functions are used as a part of the SPI slave frame transfers: + • SPI_set_frame_rx_handler() + • SPI_set_slave_tx_frame() + + The SPI_set_frame_rx_handler() function specifies the receive handler + function that is called when a frame of data has been received by the + SPI when it is configured as a slave. The receive handler function specified + through this call processes the frame data written over the SPI bus to the + SPI slave by the remote SPI master. The receive handler function must be + implemented as part of the application. It is only required if the SPI slave + is the target of SPI frame write transactions. + + The SPI_set_slave_tx_frame() function specifies the frame data that is + returned to the SPI master. The frame data specified through this function + is the value that will be read over the SPI bus by the remote SPI master, + when it initiates a transaction. Call the SPI_set_slave_tx_frame() function + only if the SPI slave is the target of SPI read transactions. That is, if + data is meant to be read over CoreSPI. + + If both frame handlers are required, call the SPI_set_frame_rx_handler() + first, otherwise the initial TX frame gets discarded when SPI_set_frame_rx_handler() + clears the FIFOs as part of its initialization. + + ------------------------------------- + SPI Slave Block Transfer Control + ------------------------------------- + The following functions are used as a part of the SPI slave block transfers: + • SPI_set_slave_block_buffers() + • SPI_set_cmd_handler() + • SPI_set_cmd_response() + + The SPI_set_slave_block_buffers() function is used to configure an SPI slave + for block transfer operations. It specifies the following: + • The buffer containing the data that will be returned to the remote SPI + master + • The buffer where data received from the remote SPI master will be + stored + • The optional handler function that will be called after the receive + buffer is filled + + The SPI_set_cmd_handler() function specifies a command handler function that + is called by the driver once a specific number of frames have been + received after the SPI chip select signal becomes active. The number of + bytes making up the command part of the transaction is specified as part of + the parameters to the SPI_set_cmd_handler() function. The command handler + function is implemented as a part of the application making use of the SPI + driver and typically calls the SPI_set_cmd_response() function. + + The SPI_set_cmd_response() function specifies the data that gets returned + to the master. Typically, the SPI_set_slave_block_buffers() function is + called as a part of the system initialisation to specify the data sent to + the master while the command bytes are being received. The transmit buffer + specified through calling the SPI_set_slave_block_buffers() function would + also typically include one or more bytes allowing the turn around time + for the command handler function to execute and call the + SPI_set_cmd_response() function. + + *//*=========================================================================*/ +#ifndef CORE_SPI_H_ +#define CORE_SPI_H_ + +#ifndef LEGACY_DIR_STRUCTURE +#include "hal/hal.h" + +#else +#include "hal.h" +#include "hal_assert.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/*-------------------------------------------------------------------------*//** + SPI FIFO Depth + ======================================= + SPI_MAX_FIFO_DEPTH & SPI_MIN_FIFO_DEPTH constants define the maximum and minimum + FIFO depths allowed for the CoreSPI instance. User need to inform the driver of + the FIFO depth for each CoreSPI instance to ensure that the FIFOs are managed correctly. + + | Constant | Description | + |--------------------|----------------------------------------------------| + | SPI_MAX_FIFO_DEPTH | Maximum FIFO depth allowed for the CoreSPI instance| + | SPI_MIN_FIFO_DEPTH | Minimum FIFO depth allowed for the CoreSPI instance| + + */ +#define SPI_MAX_FIFO_DEPTH 32u +#define SPI_MIN_FIFO_DEPTH 1u + +/***************************************************************************//** + Instances of this structure are used to identify the specific CoreSPI hardware + instances. A pointer to an instance of the spi_instance_t structure is passed + as the first parameter to the CoreSPI driver functions to identify which SPI + performs the requested operation. + */ +typedef struct spi_instance spi_instance_t; + +/***************************************************************************//** + This function pointer type is to assign a callback function for TX interrupt + when slave wants to send the next updated frame. + + Declaring and Implementing Slave Frame Transmit Handler Functions: + Slave transmit frame update handler functions should follow the following + prototype: + void slave_tx_frame_update_handler ( spi_instance_t * this_spi ); + The actual name of the transmit handler is unimportant. You can use any name + of your choice for the frame update handler. + + A common handler function may be used when multiple CoreSPI instances are + configured as slave, as the particular slave device currently requiring + service is indicated by the function parameter. + */ +typedef void (*spi_slave_frame_tx_handler_t)( spi_instance_t * this_spi ); + +/***************************************************************************//** + This defines the function prototype that must be followed by the SPI slave + frame receive handler functions. These functions are registered with the SPI + driver through the SPI_set_frame_rx_handler() function. + + Declaring and Implementing the Slave Frame Receive Handler Functions: + The Slave frame receive handler functions should follow the following + prototype: + void slave_frame_receive_handler(uint32_t rx_frame); + The actual name of the receive handler is unimportant. You can use any name + of your choice for the receive frame handler. The rx_frame parameter contains + the value of the received frame. + + Separate handler functions are required for each slave instance as there is + no indication of the slave requiring service passed to the handler. + + */ +typedef void (*spi_frame_rx_handler_t)( uint32_t rx_frame ); + +/***************************************************************************//** + This defines the function prototype that must be followed by SPI slave + block receive handler functions. These functions are registered with the + SPI driver through the SPI_set_slave_block_buffers() function. + + Declaring and Implementing Slave Block Receive Handler Functions: + Slave block receive handler functions should follow the following prototype: + void spi_block_rx_handler ( uint8_t * rx_buff, uint16_t rx_size ); + The actual name of the receive handler is unimportant. You can use any name + of your choice for the receive frame handler. The rx_buff parameter contains + a pointer to the start of the received block. The rx_size parameter contains + the number of bytes of the received block. + + Separate handler functions are required for each slave instance as there is + no indication of the slave requiring service passed to the handler. + + */ +typedef void (*spi_block_rx_handler_t)( uint8_t * rx_buff, uint32_t rx_size ); + +/***************************************************************************//** + This enumeration is used to select a specific SPI slave device (0 to 7). It is + used as a parameter to the SPI_configure_master_mode(), SPI_set_slave_select(), + and SPI_clear_slave_select() functions. + */ +typedef enum __spi_slave_t +{ + SPI_SLAVE_0 = 0, + SPI_SLAVE_1 = 1, + SPI_SLAVE_2 = 2, + SPI_SLAVE_3 = 3, + SPI_SLAVE_4 = 4, + SPI_SLAVE_5 = 5, + SPI_SLAVE_6 = 6, + SPI_SLAVE_7 = 7, + SPI_MAX_NB_OF_SLAVES = 8 +} spi_slave_t; + +/***************************************************************************//** + This enumeration is used to indicate the current slave mode transfer type so + that we are not relying on buffer comparisons to dictate the logic of the driver. + */ +typedef enum __spi_sxfer_mode_t +{ + SPI_SLAVE_XFER_NONE = 0, /* Not configured yet */ + SPI_SLAVE_XFER_BLOCK = 1, /* Block transfers, with SSEND delimiting end of block */ + SPI_SLAVE_XFER_FRAME = 2 /* Single frame transfers */ +} spi_sxfer_mode_t; + +/***************************************************************************//** + There is one instance of this structure for each of the core SPIs. Instances + of this structure are used to identify a specific SPI. A pointer to an + instance of the spi_instance_t structure is passed as the first parameter to + SPI driver functions to identify which SPI should perform the requested operation. + */ +struct spi_instance{ + + /* Base address in the processor's memory map for the + registers of the CoreSPI instance being initialized */ + addr_t base_addr; /* Base address of SPI hardware instance */ + + uint32_t rx_frame; /* received data */ + + /* Internal transmit state: */ + const uint8_t * slave_tx_buffer; /* Pointer to slave transmit buffer */ + uint32_t slave_tx_size; /* Size of slave transmit buffer */ + uint32_t slave_tx_idx; /* Current index into slave transmit buffer */ + + /* Slave command response buffer: */ + const uint8_t * resp_tx_buffer; + uint32_t resp_buff_size; + uint32_t resp_buff_tx_idx; + spi_block_rx_handler_t cmd_handler; + uint32_t cmd_done; /* Flag which indicates response has been set up and + it is safe to pad with 0s once the response is sent */ + + /* Internal receive state: */ + uint8_t * slave_rx_buffer; /* Pointer to buffer where data received by a slave will be stored */ + uint32_t slave_rx_size; /* Slave receive buffer size */ + uint32_t slave_rx_idx; /* Current index into slave receive buffer */ + + /* Slave received frame handler: */ + spi_frame_rx_handler_t frame_rx_handler; /* Pointer to function that will be called when a frame + is received when the SPI block is configured as slave */ + /* Slave transmitted frame handler: */ + uint32_t slave_tx_frame; /* Value of the data frame that will be transmitted + when the SPI block is configured as slave */ + spi_slave_frame_tx_handler_t slave_tx_frame_handler; /* Callback function pointer to update slave_tx_frame */ + + /* Slave block rx handler: */ + spi_block_rx_handler_t block_rx_handler; /* Pointer to the function that will be called when a data block has been received */ + + /* Per instance specific hardware information that the driver needs to know */ + uint16_t fifo_depth; /* Depth of RX and TX FIFOs in frames */ + + /* How we are expecting to deal with slave transfers */ + spi_sxfer_mode_t slave_xfer_mode; /* Current slave mode transfer configuration */ +}; + +/*------------------------Public Function-------------------------------------*/ + +/***************************************************************************//** + The SPI_init() function initializes the hardware and data structures of a + CoreSPI instance referenced by this_spi parameter. This function must be + called for each CoreSPI instance with a unique this_spi and base_addr + parameter combination. The SPI_init() function must be called before any + other CoreSPI driver functions are called. + + After calling the SPI_init() the CoreSPI is configured as a master, + all interrupt sources will be masked and all the slaves are deselected. + + @param this_spi + The this_spi parameter is a pointer to a spi_instance_t structure that identifies + the CoreSPI hardware block to be initialized. This parameter must point to + the g_core_spi global data structure defined within the application code. + + @param base_addr + The base_addr parameter is the base address in the processor's memory map for + the registers of the CoreSPI instance being initialized. It is assumed that + any non NULL value passed in here points to a valid instance of a CoreSPI as + the driver has no way of verifying this. Failure to pass in a valid address + can result in system instability. + + @param fifo_depth + The fifo_depth parameter specifies the number of frames in the receive + and transmit FIFOs of the CoreSPI instance being initialized. + + @return + This function does not return any value. + + @example + @code + #define SPI0_BASE_ADDR 0xC2000000 + + spi_instance_t g_spi0; + SPI_init( &g_spi0, SPI0_BASE_ADDR, 8 ); + @endcode + */ +void SPI_init +( + spi_instance_t * this_spi, + addr_t base_addr, + uint16_t fifo_depth +); + +/***************************************************************************//** + The SPI_configure_slave_mode() function is used when a CoreSPI instance is + to be configured as a SPI slave. + + @param this_spi + The this_spi parameter is a pointer to a spi_instance_t structure that identifies + the CoreSPI hardware block to be configured. This parameter must point to + the g_core_spi global data structure defined within the application code. + + @return + This function does not return any value. + + @example + @code + #define SPI0_BASE_ADDR 0xC2000000 + + spi_instance_t g_spi0; + + int main(void) + { + SPI_init( &g_spi0, SPI0_BASE_ADDR, 8 ); + SPI_configure_slave_mode ( &g _spi0 ); + } + @endcode + */ +void SPI_configure_slave_mode +( + spi_instance_t * this_spi +); + +/***************************************************************************//** + The SPI_configure_master_mode() function is used when a CoreSPI instance is + to be configured as a SPI master. + + @param this_spi + The this_spi parameter is a pointer to a spi_instance_t structure that identifies + the CoreSPI hardware block to be configured. This parameter must point to + a g_core_spi global data structure defined within the application code. + + @return + This function does not return any value. + + @example + @code + #define SPI0_BASE_ADDR 0xC2000000 + + spi_instance_t g_spi0; + + int main(void) + { + SPI_init( &g_spi0, SPI0_BASE_ADDR, 8 ); + SPI_configure_master_mode ( &g _spi0 ); + } + @endcode + */ +void SPI_configure_master_mode +( + spi_instance_t * this_spi +); + +/***************************************************************************//** + The SPI_set_slave_select() function is used by a CoreSPI master to select a + specific slave. This function causes the relevant slave select signal to be + asserted while data is clocked out onto the SPI data line. + + @param this_spi + The this_spi parameter is a pointer to a spi_instance_t structure that identifies + the CoreSPI hardware block to operate on. This parameter must point to + a g_core_spi global data structure defined within the application code. + + @param slave + The slave parameter is one of the spi_slave_t enumerated constants + identifying a slave. + + @return + This function does not return any value. + + @example + @code + #define SPI0_BASE_ADDR 0xC2000000 + + spi_instance_t g_spi0; + const uint32_t master_tx_frame = 0x0100A0E1; + + SPI_init( &g_spi0, SPI0_BASE_ADDR, 1 ); + SPI_configure_master_mode( &g_spi0 ); + + SPI_set_slave_select( &g_spi0, SPI_SLAVE_0 ); + SPI_transfer_frame( &g_spi0, master_tx_frame ); + SPI_clear_slave_select( &g_spi0, SPI_SLAVE_0 ); + + @endcode + */ +void SPI_set_slave_select +( + spi_instance_t * this_spi, + spi_slave_t slave +); + +/***************************************************************************//** + The SPI_clear_slave_select() function is used by a CoreSPI master to + deselect a specific slave. This function causes the relevant slave select + signal to be de-asserted. + + @param this_spi + The this_spi parameter is a pointer to a spi_instance_t structure that identifies + the CoreSPI hardware block to operate on. This parameter must point to + a g_core_spi global data structure defined within the application code. + + @param slave + The slave parameter is one of the spi_slave_t enumerated constants + identifying a slave. + + @return + This function does not return any value. + + @example + @code + #define SPI0_BASE_ADDR 0xC2000000 + + spi_instance_t g_spi0; + const uint32_t master_tx_frame = 0x0100A0E1; + + SPI_init( &g_spi0, SPI0_BASE_ADDR, 1 ); + SPI_configure_master_mode( &g_spi0 ); + + SPI_set_slave_select( &g_spi0, SPI_SLAVE_0 ); + SPI_transfer_frame( &g_spi0, master_tx_frame ); + SPI_clear_slave_select( &g_spi0, SPI_SLAVE_0 ); + @endcode + */ +void SPI_clear_slave_select +( + spi_instance_t * this_spi, + spi_slave_t slave +); + +/***************************************************************************//** + The SPI_transfer_frame() function is used by a SPI master to transmit and + receive a single frame of the size that has been configured at the time of + CoreSPI hardware instantiation. This function is typically used for + transactions with a SPI slave where the number of transmit and receive bits + are not divisible by 8 or where full duplex exchange of frames is required. + + @param this_spi + The this_spi parameter is a pointer to a spi_instance_t structure that identifies + the CoreSPI hardware block to operate on. This parameter must point to + a g_core_spi global data structure defined within the application code. + + @param tx_bits + The tx_bits parameter is a 32-bit word containing the value that will be + transmitted. If the frame size configured for the CoreSPI is less than 32-bits, + the upper bits will be ignored. + Note: The bit length of the value to be transmitted to the slave is + set when the CoreSPI is instantiated in the hardware design. + + @return + This function returns a 32-bit word containing the value that is received + from the slave. If the frame size configured for the CoreSPI in question is + less that 32-bits, the upper bits will be 0. + + @example + @code + #define SPI0_BASE_ADDR 0xC2000000 + + spi_instance_t g_spi0 ; + const uint32_t master_tx_frame = 0x0100A0E1; + uint32_t master_rx; + + SPI_init(&g_spi0, SPI0_BASE_ADDR, 1 ); + SPI_configure_master_mode( &g_spi0 ); + SPI_set_slave_select( &g_spi0, SPI_SLAVE_0 ); + master_rx = SPI_transfer_frame( &g_spi0, master_tx_frame ); + SPI_clear_slave_select( &g_spi0, SPI_SLAVE_0 ); + @endcode + */ +uint32_t SPI_transfer_frame +( + spi_instance_t * this_spi, + uint32_t tx_bits +); + +/***************************************************************************//** + The SPI_transfer_block() function is used by the SPI master to transmit and + receive blocks of data organized as a specified number of 8-bit frames. It + can be used for the following: + • Writing a data block to a slave + • Reading a data block from a slave + • Sending a command to a slave followed by reading the outcome of + the command in a single SPI transaction. + + @param this_spi + The this_spi parameter is a pointer to a spi_instance_t structure that identifies + the CoreSPI hardware block to operate on. This parameter must point to + a g_core_spi global data structure defined within the application code. + + @param cmd_buffer + The cmd_buffer parameter is a pointer to the buffer that contains the data + sent by the master from the beginning of the transfer. This pointer can be + null (0) if the master does not need to send a command before reading data. + + @param cmd_byte_size + The cmd_byte_size parameter specifies the number of bytes in cmd_buffer that + will be sent. A value ‘0’ indicates that no data needs to be sent to the slave. + + @param rx_buffer + The rx_buffer parameter is a pointer to the buffer that stores the data received + from the slave after sending the command. This pointer can be null (0) if the + master does not receive any data from the slave. + + @param rx_byte_size + The rx_byte_size parameter specifies the number of bytes received from + the slave and stored in the rx_buffer. A value ‘0’ indicates that no data is + to be read from the slave. + + @return + This function does not return any value. + + @example + @code + Polled write transfer example + #define SPI0_BASE_ADDR 0xC2000000 + + spi_instance_t g_spi0 ; + + uint8_t master_tx_buffer[MASTER_TX_BUFFER] = + { + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A + }; + SPI_init( &g_spi0, SPI0_BASE_ADDR, 8 ); + + SPI_configure_master_mode( &g_spi0 ); + + SPI_set_slave_select( &g_spi0, SPI_SLAVE_0) ; + SPI_transfer_block + ( + &g_spi0, + master_tx_buffer, + sizeof(master_tx_buffer), + 0, + 0 + ); + SPI_clear_slave_select(&g_spi0, SPI_SLAVE_0 ); + @endcode + */ +void SPI_transfer_block +( + spi_instance_t * this_spi, + const uint8_t * cmd_buffer, + uint16_t cmd_byte_size, + uint8_t * rx_buffer, + uint16_t rx_byte_size +); + +/***************************************************************************//** + The SPI_transfer_block_store_all_resp() function is used by the SPI master + to transmit and receive blocks of data organized as a specified number + of 8-bit frames. It can be used for the following: + • Writing a data block to a slave + • Reading a data block from a slave + • Sending a command to a slave followed by reading the outcome of + the command in a single SPI transaction + + @param this_spi + The this_spi parameter is a pointer to a spi_instance_t structure that identifies + the CoreSPI hardware block to operate on. This parameter must point to + a g_core_spi global data structure defined within the application code. + + @param cmd_buffer + The cmd_buffer parameter is a pointer to the buffer that contains the data sent by + the master from the beginning of the transfer. This pointer can be null (0) + if the master does not need to send a command before reading data. + + @param cmd_byte_size + The cmd_byte_size parameter specifies the number of bytes contained in + cmd_buffer that will be sent. A value ‘0’ indicates that no data needs + to be sent to the slave. + + @param rx_data_buffer + The rx_data_buffer parameter is a pointer to the buffer that stores the data received + from the slave after sending the command. This pointer can be null (0) if the + master does not receive any data from the slave. + + @param rx_byte_size + The rx_byte_size parameter specifies the number of bytes received from + the slave and stores in the rx_buffer. A value ‘0’ indicates that no data is + to be read from the slave. + + @param cmd_response_buffer + The cmd_response_buffer parameter is a pointer to the buffer which stores the + command response from the slave, while the master is transmitting the number + of bytes indicated by cmd_byte_size parameter. + + @return + This function does not return any value. + + @example + @code + Polled write transfer example + #define SPI0_BASE_ADDR 0xC2000000 + + spi_instance_t g_spi0 ; + + uint8_t master_tx_buffer[MASTER_TX_BUFFER] = + { + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A + }; + + uint8_t cmd_rx_buffer[CMD_RX_BUFFER]={0}; + + SPI_init( &g_spi0, SPI0_BASE_ADDR, 8 ); + + SPI_configure_master_mode( &g_spi0 ); + + SPI_set_slave_select( &g_spi0, SPI_SLAVE_0) ; + SPI_transfer_block_store_all_resp + ( + &g_spi0, + master_tx_buffer, + sizeof(master_tx_buffer), + 0, + 0, + cmd_response_buffer + ); + SPI_clear_slave_select(&g_spi0, SPI_SLAVE_0 ); + @endcode + */ +void SPI_transfer_block_store_all_resp +( + spi_instance_t * this_spi, + const uint8_t * cmd_buffer, + uint16_t cmd_byte_size, + uint8_t * rx_data_buffer, + uint16_t rx_byte_size, + uint8_t * cmd_response_buffer +); + +/***************************************************************************//** + The SPI_set_frame_rx_handler() function is used by the SPI slaves to specify + the receive handler function that is called by the SPI driver interrupt + handler when a frame of data is received by the SPI slave. + + @param this_spi + The this_spi parameter is a pointer to a spi_instance_t structure that identifies + the CoreSPI hardware block to operate on. This parameter must point to + a g_core_spi global data structure defined within the application code. + + @param rx_handler + The rx_handler parameter is a pointer to the frame receive handler that must + be called when a frame is received by the CoreSPI slave. Passing in a NULL + pointer disables the receive handler but does enable the receive interrupt to + ensure the RX FIFO is empty each time a frame is received. + + @return + This function does not return any value. + + @example + @code + #define SPI0_BASE_ADDR 0xC2000000 + + uint32_t g_slave_rx_frame = 0; + spi_instance_t g_spi0; + + void slave_frame_handler(uint32_t rx_frame) + { + g_slave_rx_frame = rx_frame; + } + int setup_slave( void ) + { + SPI_init( &g_spi0, SPI0_BASE_ADDR, 1 ); + SPI_configure_slave_mode( &g_spi0 ); + SPI_set_frame_rx_handler( &g_spi0, slave_frame_handler ); + } + @endcode + */ +void SPI_set_frame_rx_handler +( + spi_instance_t * this_spi, + spi_frame_rx_handler_t rx_handler +); + +/***************************************************************************//** + The SPI_set_slave_tx_frame() function is used by the SPI slaves to specify + the frame that gets transmitted when a transaction is initiated by the SPI + master. This function allows you to assign a slave_tx_frame_handler function, + which will be executed upon transmit interrupt when the SPI is in slave mode. + + @param this_spi + The this_spi parameter is a pointer to a spi_instance_t structure that identifies + the CoreSPI hardware block to operate on. This parameter must point to + a g_core_spi global data structure defined within the application code. + + @param frame_value + The frame_value parameter contains the value of the frame to be sent to the + master. + Note: The bit length of the value to be transmitted to the master is + set when the CoreSPI is instantiated in the hardware design. + + @param slave_tx_frame_handler + The slave_tx_frame_handler function pointer is executed upon occurrence of + transmit interrupt when CoreSPI is operating in slave mode. This parameter + is optional and if set to NULL it is assumed that the frame value is static or + updated asynchronously. + + @return + This function does not return any value. + + @example + @code + #define SPI0_BASE_ADDR 0xC2000000 + + spi_instance_t g_spi0 ; + const uint32_t slave_tx_frame[2] = { 0x0110F761, 0x0110F671 }; + uint32_t master_rx; + uint32_t slave_frame_idx = 0 ; + + slave_frame_update( spi_instance_t * this_spi ) + { + this_spi->slave_tx_frame = slave_tx_frame[slave_frame_idx++]; + if( slave_frame_idx > 2 ) + slave_frame_idx = 0; + } + main() + { + SPI_init( &g_spi0, SPI0_BASE_ADDR, 1 ); + SPI_configure_master_mode( &g_spi0 ) ; + SPI_set_slave_tx_frame( &g_spi0, slave_tx_frame[slave_frame_idx++], + &slave_frame_update ); + } + @endcode + */ +void SPI_set_slave_tx_frame +( + spi_instance_t * this_spi, + uint32_t frame_value, + spi_slave_frame_tx_handler_t slave_tx_frame_handler +); + +/***************************************************************************//** + The SPI_set_slave_block_buffers() function is used to configure an SPI slave + for block transfer operations. It specifies one or more of the following: + • The data that is transmitted when accessed by a master. + • The buffer where the data received from a master is stored. + • The handler function that must be called after the receive buffer has + been filled. + • The number of bytes that must be received from the master before calling + the recieve handler function. + These parameters allow the following use cases: + • Slave performing an action after receiving a block of data from a + master containing a command. This action is performed by the + receive handler based on the content of the receive data buffer. + • Slave returning a block of data to the master. The type of information + is always the same but the actual values change over time. For example, + returning the voltage of a predefined set of analog inputs. + • Slave returning data based on a command contained in the first part of + the SPI transaction. For example, reading the voltage of the analog + input specified by the first data byte by the master. This is achieved + by using the SPI_set_slave_block_buffers() function in conjunction with + functions SPI_set_cmd_handler() and SPI_set_cmd_response(). + + See the SPI_set_cmd_handler() function description for details of + this use case. + + @param this_spi + The this_spi parameter is a pointer to a spi_instance_t structure that identifies + the CoreSPI hardware block to operate on. This parameter must point to + a g_core_spi global data structure defined within the application code. + + @param tx_buffer + The tx_buffer parameter is a pointer to a buffer containing the data that + will be sent to the master. This parameter can be set to ‘0’ if the SPI + slave is not intended to be the target of SPI read transactions. + + @param tx_buff_size + The tx_buff_size parameter specifies the number of bytes that are + transmitted by the SPI slave. It is the number of bytes contained in the + tx_buffer. This parameter can be set to ‘0’ if the SPI slave is not + intended to be the target of SPI read transactions. The driver returns 0s + to the master if there is no buffer specified or the master reads beyond the + end of the buffer. + Note: If SPI_transfer_block() is used to read from this slave and there is + no command handler involved, the buffer size here must be at least the + combined length of the command and response specified by the master. + On receiving this data, the master discards the data bytes equal to + command length bytes from the start of the received buffer. + + @param rx_buffer + The rx_buffer parameter is a pointer to the buffer where data received + from the master is stored. This parameter can be set to ‘0’ if the + SPI slave is not intended to be the target of SPI write or write-read + transactions. + + @param rx_buff_size + The rx_buff_size parameter specifies the size of the receive buffer. It is + also the number of bytes that must be received before the receive handler + is called, if a receive handler is specified using the block_rx_handler + parameter. Any bytes received in excess of the size specified by the + rx_buff_size parameter are discarded. This parameter can be set to ‘0’ + if the SPI slave is not intended to be the target of SPI write or + write-read transactions. + + @param block_rx_handler + The block_rx_handler parameter is a pointer to a function that is called + when receive buffer has been filled or the slave select has been de-asserted. + This parameter can be set to ‘0’ if the SPI slave is not intended to be the + target of SPI write or write-read transactions. + + @return + This function does not return any value. + + @example + @code + Slave Performing Operation Based on Master Command: + In this example the SPI slave is configured to receive 10 bytes of data + or command from the SPI slave, and process the data received from the master. + + #define SPI0_BASE_ADDR 0xC2000000 + + uint32_t nb_of_rx_handler_calls = 0; + spi_instance_t g_spi0; + + void spi1_block_rx_handler_b + ( + uint8_t * rx_buff, + uint16_t rx_size + ) + { + ++nb_of_rx_handler_calls; + } + + void setup_slave( void ) + { + uint8_t slave_rx_buffer[10] = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + SPI_init( &g_spi0, SPI0_BASE_ADDR, 8 ); + SPI_configure_slave_mode( &g_spi0 ); + + SPI_set_slave_block_buffers + ( + &g_spi0, + 0, + 0, + slave_rx_buffer, + sizeof( master_tx_buffer ), + spi1_block_rx_handler_b + ); + } + @endcode + */ +void SPI_set_slave_block_buffers +( + spi_instance_t * this_spi, + const uint8_t * tx_buffer, + uint32_t tx_buff_size, + uint8_t * rx_buffer, + uint32_t rx_buff_size, + spi_block_rx_handler_t block_rx_handler +); + +/***************************************************************************//** + The SPI_isr() function is the top level interrupt handler function for the + CoreSPI driver. You must call SPI_isr() from the system level + (CoreInterrupt and NVIC level) interrupt handler assigned to the interrupt + triggered by the CoreSPI SPIINT signal. Your system level interrupt handler + must also clear the system level interrupt triggered by the CoreSPI SPIINT + signal before returning, to prevent a re-assertion of the same interrupt. + + This function supports all types of interrupt triggered by CoreSPI. It is not + a complete interrupt handler by itself; rather, it is a top level wrapper that + abstracts CoreSPI command interrupt and slave mode transmit interrupt handling + by calling lower level handler functions specific to each type of CoreSPI + interrupt. You must create the lower level handler functions to suit your + application and register them with the driver through calling the + SPI_set_cmd_handler(), SPI_set_cmd_response(), and SPI_set_slave_tx_frame() + functions. + + @param this_spi + The this_spi parameter is a pointer to a spi_instance_t structure that identifies + the CoreSPI hardware block to operate on. This parameter must point to + the g_core_spi global data structure defined within the application code. + + @return + This function does not return any value. + + @example + @code + + Example of configuring a CoreInterrupt connected to the Fabric Interrupt on a + SmartFusion device to handle CoreSPI interrupt. + + #define #define SPI1_INT_IRQ_NB 0 + spi_instance_t g_spi0; + + Void CIC_irq1_handler(void) + { + SPI_isr( &g_spi0 ); + } + + void Fabric_IRQHandler( void ) + { + // Call the CoreInterrupt driver ISR to determine the source of the + // interrupt and call the relevant ISR registered to it. + CIC_irq_handler(); + + // Clear NVIC interrupt status to allow further interrupts + NVIC_ClearPendingIRQ( Fabric_IRQn ); + } + + main() + { + ... + + CIC_init( CIC_BASE_ADDR ); + + // Install handler for SPI IRQ + CIC_set_irq_handler( SPI0_INT_IRQ_NB, CIC_irq1_handler ); + + NVIC_ClearPendingIRQ( Fabric_IRQn ); + NVIC_EnableIRQ( Fabric_IRQn ); + + CIC_enable_irq( SPI1_INT_IRQ_NB ); + + ... + } + @endcode + */ +void SPI_isr +( + spi_instance_t * this_spi +); + +/***************************************************************************//** + The SPI_set_cmd_handler() function specifies a command handler function that + will be called when the number of bytes received reaches the command size + specified as cmd_size parameter. + + This function is used by the SPI slaves performing block transfers. Its + purpose is to allow an SPI slave to decide the data that will be returned to + the master while an SPI transaction is taking place. Typically, one or more + command bytes are sent by the master to request some specific data. The slave + interprets the command byte(s) while one or more turn-around bytes are + transmitted. The slave adjusts its transmit data buffer based on the command + during the turn around time. + + The following table provides an example of the use of this function where the + SPI slave returns data bytes D0 to D6 based on the value of a command. The + 3 bytes long command is made up of a command opcode byte followed by an + address byte followed by a size byte. The cmd_handler() function specified + through an earlier call to SPI_set_cmd_handler() is called by the CoreSPI + driver once the third byte is received. The cmd_handler() function + interprets the command bytes and calls SPI_set_cmd_response() to set the SPI + slave's response transmit buffer with the data to be transmitted after the + turnaround bytes (T0 to T3). The number of turnaround bytes must be + sufficient to give enough time for the cmd_handler() to execute. The number + of turnaround bytes is specified by the protocol used on top of the SPI + transport layer so that master and slave agree on the number of turn around + bytes. + +|Timestamp| SPI Transaction | Bytes | Comments | +|---------|-------------|----------------------|---------------| +| t0|COMMAND|C A S |C - command opcode byte, A - address byte, S - size byte| +|t1| TURN-AROUND|T0 T1 |cmd_handler() called here (T0 to T3 are TURN-AROUND bytes)| +|t2|TURN-AROUND|T2 T3|SPI_set_cmd_response() called here by implementation of cmd_handler() +|| | |to set the data that will be transmitted by the SPI slave.| +|t3| DATA | D0 D1 D2 D3 D4 D5 D6 |Data transmition (SPI slave return data bytes)| + + + @param this_spi + The this_spi parameter is a pointer to a spi_instance_t structure identifying + the CoreSPI hardware block to operate on. This parameter must point to + the g_core_spi global data structure defined within the application code. + + @param cmd_handler + The cmd_handler parameter is a pointer to a function with the prototype: + void cmd_handler(uint8_t * rx_buff, uint32_t rx_size); + It specifies the function that will be called when the number of bytes + specified by the cmd_size parameter has been received. + + @param cmd_size + The cmd_size parameter specifies the number of bytes that must be received + before calling the command handler function specified by the cmd_handler. + The CoreSPI supports cmd_size values in the range 1 to 7. + + @return + This function does not return any value. + + @example + @code + The following example demonstrates how to configure CoreSPI to implement + the protocol given as an example above. The configure_slave() function + configures CoreSPI. It sets receive and transmit buffers. The transmit + buffer specified through the call to SPI_set_slave_block_buffers() function + specifies the data that will be returned to the master in bytes between + t0 and t3. These bytes will be sent to the master while the master transmits + the command and dummy bytes. The spi_slave_cmd_handler() function will be + called by the driver at time t1 after the 3 command bytes have been received. + The spi_block_rx_handler() function will be called by the driver at time t4, + when the transaction completes and the slave select signal becomes + de-asserted. + + #define SPI0_BASE_ADDR 0xC2000000 + #define COMMAND_SIZE 3 + #define NB_OF_DUMMY_BYTES 4 + #define MAX_TRANSACTION_SIZE 16 + + spi_instance_t g_spi0; + uint8_t slave_tx_buffer[COMMAND_SIZE + NB_OF_DUMMY_BYTES]; + uint8_t slave_rx_buffer[MAX_TRANSACTION_SIZE]; + + void configure_slave( void ) + { + SPI_init( &g_spi0, SPI0_BASE_ADDR, 8 ); + SPI_configure_slave_mode( &g_spi0 ); + SPI_set_slave_block_buffers + ( + &g_spi0, + slave_tx_buffer, + COMMAND_SIZE + NB_OF_DUMMY_BYTES, + slave_rx_buffer, + sizeof(slave_rx_buffer), + spi_block_rx_handler + ); + + SPI_set_cmd_handler + ( + &g_spi0, + spi_slave_cmd_handler, + COMMAND_SIZE + ); + } + + void spi_slave_cmd_handler + ( + uint8_t * rx_buff, + uint32_t rx_size + ) + { + uint8_t command; + uint8_t address; + uint8_t size; + uint8_t * p_response; + uint32_t response_size; + + command = rx_buff[0]; + address = rx_buff[1]; + size = rx_buff[2]; + + p_response = get_response_data( command, address, size, &response_size ); + SPI_set_cmd_response( &g_spi0, p_response, response_size ); + } + + void spi_block_rx_handler + ( + uint8_t * rx_buff, + uint32_t rx_size + ) + { + process_rx_data( rx_buff, rx_size ); + } + @endcode + */ +void SPI_set_cmd_handler +( + spi_instance_t * this_spi, + spi_block_rx_handler_t cmd_handler, + uint32_t cmd_size +); + +/***************************************************************************//** + The SPI_set_cmd_response() function specifies the data that will be returned + to the master. See the description of SPI_set_cmd_handler() for details. + + @param this_spi + The this_spi parameter is a pointer to a spi_instance_t structure that identifies + the CoreSPI hardware block to operate on. This parameter must point to + a g_core_spi global data structure defined within the application code. + + @param resp_tx_buffer + The resp_tx_buffer parameter is a pointer to the buffer containing the data + that must be returned to the host in the data phase of an SPI transaction. + + @param resp_buff_size + The resp_buff_size parameter specifies the size of the buffer pointed by the + resp_tx_buffer parameter. + + @return + This function does not return any value. + */ +void SPI_set_cmd_response +( + spi_instance_t * this_spi, + const uint8_t * resp_tx_buffer, + uint32_t resp_buff_size +); + +/***************************************************************************//** + The SPI_enable() function enables the CoreSPI and allows it to respond to the external + signals. It is usually called to re-enable a CoreSPI instance which has been + disabled previously via by calling the SPI_disable() as the normal state of a CoreSPI + after enabling the initialization. + + @param this_spi + The this_spi parameter is a pointer to a spi_instance_t structure that identifies + the CoreSPI hardware block to operate on. This parameter must point to + the g_core_spi global data structure defined within the application code. + + @return + This function does not return any value. + */ +void SPI_enable +( + spi_instance_t * this_spi +); + +/***************************************************************************//** + The SPI_disable() function disables the CoreSPI and stops responding to the + external signals. + + @param this_spi + The this_spi parameter is a pointer to a spi_instance_t structure that identifies + the CoreSPI hardware block to operate on. This parameter must point to + the g_core_spi global data structure defined within the application code. + + @return + This function does not return any value. + */ +void SPI_disable +( + spi_instance_t * this_spi +); + +#ifdef __cplusplus +} +#endif + +#endif /* CORE_SPI_H_*/ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSPI/corespi_regs.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSPI/corespi_regs.h new file mode 100644 index 0000000..4b09272 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSPI/corespi_regs.h @@ -0,0 +1,270 @@ +/***************************************************************************//** + * Copyright 2011-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file corespi_regs.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreSPI memory map + * + */ + +#ifndef CORESPI_REGS_H_ +#define CORESPI_REGS_H_ + +/******************************************************************************* + * Control register 1: + *------------------------------------------------------------------------------ + */ +#define CTRL1_REG_OFFSET 0x00u + +#define CTRL1_ENABLE_OFFSET 0x00u +#define CTRL1_ENABLE_MASK 0x01u +#define CTRL1_ENABLE_SHIFT 0x00 + +#define CTRL1_MASTER_OFFSET 0x00u +#define CTRL1_MASTER_MASK 0x02u +#define CTRL1_MASTER_SHIFT 0x01 + +#define CTRL1_INTRXDATA_OFFSET 0x00u +#define CTRL1_INTRXDATA_MASK 0x04u +#define CTRL1_INTRXDATA_SHIFT 0x02 + +#define CTRL1_INTTXDONE_OFFSET 0x00u +#define CTRL1_INTTXDONE_MASK 0x08u +#define CTRL1_INTTXDONE_SHIFT 0x03 + +#define CTRL1_INTRXOVFLOW_OFFSET 0x00u +#define CTRL1_INTRXOVFLOW_MASK 0x10u +#define CTRL1_INTRXOVFLOW_SHIFT 0x04 + +#define CTRL1_INTTXURUN_OFFSET 0x00u +#define CTRL1_INTTXURUN_MASK 0x20u +#define CTRL1_INTTXURUN_SHIFT 0x05 + +#define CTRL1_FRAMEURUN_OFFSET 0x00u +#define CTRL1_FRAMEURUN_MASK 0x40u +#define CTRL1_FRAMEURUN_SHIFT 0x06 + +#define CTRL1_OENOFF_OFFSET 0x00u +#define CTRL1_OENOFF_MASK 0x80u +#define CTRL1_OENOFF_SHIFT 0x07 + +/******************************************************************************* + * Interrupt clear register: + *------------------------------------------------------------------------------ + */ +#define INTCLR_REG_OFFSET 0x04u + +#define INTCLR_TXDONE_OFFSET 0x04u +#define INTCLR_TXDONE_MASK 0x01u +#define INTCLR_TXDONE_SHIFT 0x00 + +#define INTCLR_RXDONE_OFFSET 0x04u +#define INTCLR_RXDONE_MASK 0x02u +#define INTCLR_RXDONE_SHIFT 0x01 + +#define INTCLR_RXOVERFLOW_OFFSET 0x04u +#define INTCLR_RXOVERFLOW_MASK 0x04u +#define INTCLR_RXOVERFLOW_SHIFT 0x02 + +#define INTCLR_TXUNDERRUN_OFFSET 0x04u +#define INTCLR_TXUNDERRUN_MASK 0x08u +#define INTCLR_TXUNDERRUN_SHIFT 0x03 + +#define INTCLR_CMDINT_OFFSET 0x04u +#define INTCLR_CMDINT_MASK 0x10u +#define INTCLR_CMDINT_SHIFT 0x04 + +#define INTCLR_SSEND_OFFSET 0x04u +#define INTCLR_SSEND_MASK 0x20u +#define INTCLR_SSEND_SHIFT 0x05 + +#define INTCLR_RXDATA_OFFSET 0x04u +#define INTCLR_RXDATA_MASK 0x40u +#define INTCLR_RXDATA_SHIFT 0x06 + +#define INTCLR_TXDATA_OFFSET 0x04u +#define INTCLR_TXDATA_MASK 0x80u +#define INTCLR_TXDATA_SHIFT 0x07 + +/******************************************************************************* + * Receive data register: + *------------------------------------------------------------------------------ + */ +#define RXDATA_REG_OFFSET 0x08u + +/******************************************************************************* + * Transmit data register: + *------------------------------------------------------------------------------ + */ +#define TXDATA_REG_OFFSET 0x0Cu + +/******************************************************************************* + * Masked interrupt status register: + *------------------------------------------------------------------------------ + */ +#define INTMASK_REG_OFFSET 0x10u + +#define INTMASK_TXDONE_OFFSET 0x10u +#define INTMASK_TXDONE_MASK 0x01u +#define INTMASK_TXDONE_SHIFT 0x00 + +#define INTMASK_RXDONE_OFFSET 0x10u +#define INTMASK_RXDONE_MASK 0x02u +#define INTMASK_RXDONE_SHIFT 0x01 + +#define INTMASK_RXOVERFLOW_OFFSET 0x10u +#define INTMASK_RXOVERFLOW_MASK 0x04u +#define INTMASK_RXOVERFLOW_SHIFT 0x02 + +#define INTMASK_TXUNDERRUN_OFFSET 0x10u +#define INTMASK_TXUNDERRUN_MASK 0x08u +#define INTMASK_TXUNDERRUN_SHIFT 0x03 + +#define INTMASK_CMDINT_OFFSET 0x10u +#define INTMASK_CMDINT_MASK 0x10u +#define INTMASK_CMDINT_SHIFT 0x04 + +#define INTMASK_SSEND_OFFSET 0x10u +#define INTMASK_SSEND_MASK 0x20u +#define INTMASK_SSEND_SHIFT 0x05 + +#define INTMASK_RXDATA_OFFSET 0x10u +#define INTMASK_RXDATA_MASK 0x40u +#define INTMASK_RXDATA_SHIFT 0x06 + +#define INTMASK_TXDATA_OFFSET 0x10u +#define INTMASK_TXDATA_MASK 0x80u +#define INTMASK_TXDATA_SHIFT 0x07 + +/******************************************************************************* + * Raw interrupt status register: + *------------------------------------------------------------------------------ + */ +#define INTRAW_REG_OFFSET 0x14u + +#define INTRAW_TXDONE_OFFSET 0x14u +#define INTRAW_TXDONE_MASK 0x01u +#define INTRAW_TXDONE_SHIFT 0x00 + +#define INTRAW_RXDONE_OFFSET 0x14u +#define INTRAW_RXDONE_MASK 0x02u +#define INTRAW_RXDONE_SHIFT 0x01 + +#define INTRAW_RXOVERFLOW_OFFSET 0x14u +#define INTRAW_RXOVERFLOW_MASK 0x04u +#define INTRAW_RXOVERFLOW_SHIFT 0x02 + +#define INTRAW_TXUNDERRUN_OFFSET 0x14u +#define INTRAW_TXUNDERRUN_MASK 0x08u +#define INTRAW_TXUNDERRUN_SHIFT 0x03 + +#define INTRAW_CMDINT_OFFSET 0x14u +#define INTRAW_CMDINT_MASK 0x10u +#define INTRAW_CMDINT_SHIFT 0x04 + +#define INTRAW_SSEND_OFFSET 0x14u +#define INTRAW_SSEND_MASK 0x20u +#define INTRAW_SSEND_SHIFT 0x05 + +#define INTRAW_RXDATA_OFFSET 0x14u +#define INTRAW_RXDATA_MASK 0x40u +#define INTRAW_RXDATA_SHIFT 0x06 + +#define INTRAW_TXDATA_OFFSET 0x14u +#define INTRAW_TXDATA_MASK 0x80u +#define INTRAW_TXDATA_SHIFT 0x07 + +/******************************************************************************* + * Control register 2: + *------------------------------------------------------------------------------ + */ +#define CTRL2_REG_OFFSET 0x18u + +#define CTRL2_CMDSIZE_OFFSET 0x18u +#define CTRL2_CMDSIZE_MASK 0x07u +#define CTRL2_CMDSIZE_SHIFT 0x00 + +#define CTRL2_INTCMD_OFFSET 0x18u +#define CTRL2_INTCMD_MASK 0x10u +#define CTRL2_INTCMD_SHIFT 0x04 + +#define CTRL2_INTSSEND_OFFSET 0x18u +#define CTRL2_INTSSEND_MASK 0x20u +#define CTRL2_INTSSEND_SHIFT 0x05 + +#define CTRL2_INTRXDATA_OFFSET 0x18u +#define CTRL2_INTRXDATA_MASK 0x40u +#define CTRL2_INTRXDATA_SHIFT 0x06 + +#define CTRL2_INTTXDATA_OFFSET 0x18u +#define CTRL2_INTTXDATA_MASK 0x80u +#define CTRL2_INTTXDATA_SHIFT 0x07 + +/******************************************************************************* + * Command register: + *------------------------------------------------------------------------------ + */ +#define CMD_REG_OFFSET 0x1Cu + +#define CMD_RXFIFORST_OFFSET 0x1Cu +#define CMD_RXFIFORST_MASK 0x01u +#define CMD_RXFIFORST_SHIFT 0x00 + +#define CMD_TXFIFORST_OFFSET 0x1Cu +#define CMD_TXFIFORST_MASK 0x02u +#define CMD_TXFIFORST_SHIFT 0x01 + +/******************************************************************************* + * Status register: + *------------------------------------------------------------------------------ + */ +#define STATUS_REG_OFFSET 0x20u + +#define STATUS_FIRSTFRAME_OFFSET 0x20u +#define STATUS_FIRSTFRAME_MASK 0x01u +#define STATUS_FIRSTFRAME_SHIFT 0x00 + +#define STATUS_DONE_OFFSET 0x20u +#define STATUS_DONE_MASK 0x02u +#define STATUS_DONE_SHIFT 0x01 + +#define STATUS_RXEMPTY_OFFSET 0x20u +#define STATUS_RXEMPTY_MASK 0x04u +#define STATUS_RXEMPTY_SHIFT 0x02 + +#define STATUS_TXFULL_OFFSET 0x20u +#define STATUS_TXFULL_MASK 0x08u +#define STATUS_TXFULL_SHIFT 0x03 + +#define STATUS_RXOVFLOW_OFFSET 0x20u +#define STATUS_RXOVFLOW_MASK 0x10u +#define STATUS_RXOVFLOW_SHIFT 0x04 + +#define STATUS_TXUNDERRUN_OFFSET 0x20u +#define STATUS_TXUNDERRUN_MASK 0x20u +#define STATUS_TXUNDERRUN_SHIFT 0x05 + +#define STATUS_SSEL_OFFSET 0x20u +#define STATUS_SSEL_MASK 0x40u +#define STATUS_SSEL_SHIFT 0x06 + +#define STATUS_ACTIVE_OFFSET 0x20u +#define STATUS_ACTIVE_MASK 0x80u +#define STATUS_ACTIVE_SHIFT 0x07 + +/******************************************************************************* + * Slave select register: + *------------------------------------------------------------------------------ + */ +#define SSEL_REG_OFFSET 0x24u + +/******************************************************************************* + * Transmit data last register: + *------------------------------------------------------------------------------ + */ +#define TXLAST_REG_OFFSET 0x28u + + +#endif /*CORESPI_REGS_H_*/ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSysServices_PF/core_sysservices_pf.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSysServices_PF/core_sysservices_pf.c new file mode 100644 index 0000000..3b3e8ce --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSysServices_PF/core_sysservices_pf.c @@ -0,0 +1,889 @@ +/******************************************************************************* + * Copyright 2019-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * PF_System_Services driver implementation. See file "core_syservices_pf.h" for + * description of the functions implemented in this file. + * + */ + +#include "core_sysservices_pf.h" +#include "coresysservicespf_regs.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NULL_BUFFER (( uint8_t* ) 0) + +static uint8_t execute_ss_command +( + uint8_t cmd_opcode, + const uint8_t* cmd_data, + uint16_t cmd_data_size, + const uint8_t* p_response, + uint16_t response_size, + uint16_t mb_offset, + uint16_t response_offset +); + +uint32_t g_css_pf_base_addr = 0u; + +/***************************************************************************//** + * SYS_init() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +void +SYS_init +( + uint32_t base_addr +) +{ + g_css_pf_base_addr = base_addr; +} + +/***************************************************************************//** + * SYS_get_serial_number() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +uint8_t +SYS_get_serial_number +( + const uint8_t * p_serial_number, + uint16_t mb_offset +) +{ + uint8_t status = SYS_PARAM_ERR; + + if (p_serial_number == NULL_BUFFER) + { + return status; + } + + status = execute_ss_command(SERIAL_NUMBER_REQUEST_CMD, + NULL_BUFFER, + 0u, + p_serial_number, + SERIAL_NUMBER_RESP_LEN, + mb_offset, + 0u); + + return status; +} + +/***************************************************************************//** + * SYS_get_user_code() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +uint8_t +SYS_get_user_code +( + const uint8_t * p_user_code, + uint16_t mb_offset +) +{ + uint8_t status = SYS_PARAM_ERR; + + if(p_user_code == NULL_BUFFER) + { + return status; + } + + status = execute_ss_command(USERCODE_REQUEST_CMD, + NULL_BUFFER, + 0u, + p_user_code, + USERCODE_RESP_LEN, + mb_offset, + 0u); + return status; +} + +/***************************************************************************//** + * SYS_get_design_info() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +uint8_t +SYS_get_design_info +( + const uint8_t * p_design_info, + uint16_t mb_offset +) +{ + uint8_t status = SYS_PARAM_ERR; + + if(p_design_info == NULL_BUFFER) + { + return status; + } + + status = execute_ss_command(DESIGN_INFO_REQUEST_CMD, + NULL_BUFFER, + 0u, + p_design_info, + DESIGN_INFO_RESP_LEN, + mb_offset, + 0u); + return status; +} + +/***************************************************************************//** + * SYS_get_device_certificate() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +uint8_t +SYS_get_device_certificate +( + const uint8_t * p_device_certificate, + uint16_t mb_offset +) +{ + uint8_t status = SYS_PARAM_ERR; + + if(p_device_certificate == NULL_BUFFER) + { + return status; + } + + status = execute_ss_command(DEVICE_CERTIFICATE_REQUEST_CMD, + NULL_BUFFER, + 0u, + p_device_certificate, + DEVICE_CERTIFICATE_RESP_LEN, + mb_offset, + 0u); + return status; +} + +/***************************************************************************//** + * SYS_read_digest() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +uint8_t SYS_read_digest +( + const uint8_t * p_digest, + uint16_t mb_offset +) +{ + uint8_t status = SYS_PARAM_ERR; + + if(p_digest == NULL_BUFFER) + { + return status; + } + +#ifndef CORESYSSERVICES_MPFS + status = execute_ss_command(READ_DIGEST_REQUEST_CMD, + NULL_BUFFER, + 0u, + p_digest, + READ_DIGEST_RESP_LEN, + mb_offset, + 0u); +#else + status = execute_ss_command(READ_DIGEST_REQUEST_CMD, + NULL_BUFFER, + 0u, + p_digest, + READ_DIGEST_MPFS_RESP_LEN, + mb_offset, + 0u); +#endif + return status; + +} + +/***************************************************************************//** + * SYS_query_security() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +uint8_t SYS_query_security +( + uint8_t * p_security_locks, + uint16_t mb_offset +) +{ + uint8_t status = SYS_PARAM_ERR; + uint8_t idx = 0u; + + if(p_security_locks == NULL_BUFFER) + { + return status; + } + +#ifndef CORESYSSERVICES_MPFS + uint8_t buf[12] = {0}; + /* Actual QUERY_SECURITY_RESP_LEN is 9 or 33 but PF_System_Services core + * needs number of words instead of number of bytes to be written to or read + * from MailBox */ + status = execute_ss_command(QUERY_SECURITY_REQUEST_CMD, + NULL_BUFFER, + 0u, + buf, + (QUERY_SECURITY_RESP_LEN + 3u), + mb_offset, + 0u); + + for (idx = 0u; idx < 9u; idx++) + { + *(p_security_locks+idx) = buf[idx]; + } + +#else + uint8_t buf[36] = {0}; + + status = execute_ss_command(QUERY_SECURITY_REQUEST_CMD, + NULL_BUFFER, + 0u, + buf, + (QUERY_SECURITY_MPFS_RESP_LEN + 3u), + mb_offset, + 0u); + + for (idx = 0u; idx < 33u; idx++) + { + *(p_security_locks+idx) = buf[idx]; + } + +#endif + + return status; +} + +/***************************************************************************//** + * SYS_read_debug_info() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +uint8_t SYS_read_debug_info +( + const uint8_t * p_debug_info, + uint16_t mb_offset +) +{ + uint8_t status = SYS_PARAM_ERR; + + if(p_debug_info == NULL_BUFFER) + { + return status; + } + + status = execute_ss_command(READ_DEBUG_INFO_REQUEST_CMD, + NULL_BUFFER, + 0u, + p_debug_info, + READ_DEBUG_INFO_RESP_LEN, + mb_offset, + 0u); + return status; +} + +#ifdef CORESYSSERVICES_MPFS +/***************************************************************************//** + * SYS_read_envm_parameter() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +uint8_t SYS_read_envm_parameter +( + uint8_t * p_envm_param, + uint16_t mb_offset +) +{ + uint8_t status = SYS_PARAM_ERR; + + if(p_envm_param == NULL_BUFFER) + { + return status; + } + + status = execute_ss_command(READ_ENVM_PARAM_REQUEST_CMD, + NULL_BUFFER, + 0, + p_envm_param, + READ_ENVM_PARAM_RESP_LEN, + mb_offset, + 0); + return status; +} + +#endif + +/***************************************************************************//** + * SYS_puf_emulation_service() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +uint8_t SYS_puf_emulation_service +( + const uint8_t * p_challenge, + uint8_t op_type, + uint8_t* p_response, + uint16_t mb_offset +) +{ + uint8_t status = SYS_PARAM_ERR; + uint8_t mb_format[20] = {0x00}; + uint8_t index = 0u; + + if((p_response == NULL_BUFFER) || (p_challenge == NULL_BUFFER)) + { + return status; + } + + /* Frame the data required for mailbox */ + mb_format[index] = op_type; + + for (index = 4u; index < 20u; index++) + { + mb_format[index] = p_challenge[index - 4u]; + } + + status = execute_ss_command(PUF_EMULATION_SERVICE_REQUEST_CMD, + mb_format, + PUF_EMULATION_SERVICE_CMD_LEN, + p_response, + PUF_EMULATION_SERVICE_RESP_LEN, + mb_offset, + 5u); /* mentioning offset to number of words instead of bytes */ + + return status; +} + +/***************************************************************************//** + * SYS_digital_signature_service() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +uint8_t SYS_digital_signature_service +( + const uint8_t* p_hash, + uint8_t format, + uint8_t* p_response, + uint16_t mb_offset +) +{ + uint8_t status = SYS_PARAM_ERR; + + if((p_hash == NULL_BUFFER) || (p_response == NULL_BUFFER)) + { + return status; + } + + if (format == DIGITAL_SIGNATURE_RAW_FORMAT_REQUEST_CMD) + { + status = execute_ss_command(DIGITAL_SIGNATURE_RAW_FORMAT_REQUEST_CMD, + p_hash, + DIGITAL_SIGNATURE_HASH_LEN, + p_response, + DIGITAL_SIGNATURE_RAW_FORMAT_RESP_SIZE, + mb_offset, + 12u); /* mentioning offset to number of words instead of bytes */ + } + else + { + status = execute_ss_command(DIGITAL_SIGNATURE_DER_FORMAT_REQUEST_CMD, + p_hash, + DIGITAL_SIGNATURE_HASH_LEN, + p_response, + DIGITAL_SIGNATURE_DER_FORMAT_RESP_SIZE, + mb_offset, + 12u); /* mentioning offset to number of words instead of bytes */ + } + + return status; +} + +/***************************************************************************//** + * SYS_secure_nvm_write() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +uint8_t SYS_secure_nvm_write +( + uint8_t format, + uint8_t snvm_module, + const uint8_t* p_data, + const uint8_t* p_user_key, + uint16_t mb_offset +) +{ + uint8_t frame[256] = {0x00}; + uint8_t* p_frame = &frame[0]; + uint16_t index = 0u; + uint8_t status = SYS_PARAM_ERR; + + HAL_ASSERT(!(NULL_BUFFER == p_data)); + HAL_ASSERT(!(snvm_module >= 221u)); + if (format != SNVM_NON_AUTHEN_TEXT_REQUEST_CMD) + { + HAL_ASSERT(!(NULL_BUFFER == p_user_key)); + } + + if ((p_data == NULL_BUFFER) || (snvm_module >= 221)) + { + return status; + } + + if ((format != SNVM_NON_AUTHEN_TEXT_REQUEST_CMD) + && (p_user_key == NULL_BUFFER)) + { + return status; + } + + if ((format != SNVM_NON_AUTHEN_TEXT_REQUEST_CMD) + && (format != SNVM_AUTHEN_TEXT_REQUEST_CMD) + && (format != SNVM_AUTHEN_CIPHERTEXT_REQUEST_CMD)) + { + return status; + } + + *p_frame = snvm_module; /* SNVMADDR - SNVM module */ + + p_frame += 4u; /* Next 3 bytes RESERVED - For alignment */ + + /* Copy user key and send the command/data to mailbox. */ + if ((format == SNVM_AUTHEN_TEXT_REQUEST_CMD) || + (format == SNVM_AUTHEN_CIPHERTEXT_REQUEST_CMD)) + { + /* Copy user data */ + for (index = 0u; index < (AUTHENTICATED_TEXT_DATA_LEN - USER_SECRET_KEY_LEN - 4u); index++) + { + *p_frame = p_data[index]; + p_frame++; + } + + /* Copy user key */ + for (index = 0u; index < USER_SECRET_KEY_LEN; index++) + { + *p_frame = p_user_key[index]; + p_frame++; + } + + status = execute_ss_command(format, + &frame[0], + AUTHENTICATED_TEXT_DATA_LEN, + NULL_BUFFER, + 0u, + mb_offset, + 0u); + } + else + { + /* Copy user data */ + for (index = 0u; index < (NON_AUTHENTICATED_TEXT_DATA_LEN - 4u); index++) + { + *(p_frame+index) = p_data[index]; + } + + status = execute_ss_command(format, + &frame[0], + NON_AUTHENTICATED_TEXT_DATA_LEN, + NULL_BUFFER, + 0u, + mb_offset, + 0u); + } + + return status; +} + +/***************************************************************************//** + * SYS_secure_nvm_read() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +uint8_t SYS_secure_nvm_read +( + uint8_t snvm_module, + const uint8_t* p_user_key, + uint8_t* p_admin, + uint8_t* p_data, + uint16_t data_len, + uint16_t mb_offset +) +{ + /* Frame the message. */ + uint8_t frame[16] = {0x00u}; + uint8_t* p_frame = &frame[0u]; + uint8_t status = SYS_PARAM_ERR; + uint8_t response[256] = {0x00u}; + uint16_t index = 0u; + + HAL_ASSERT(!(NULL_BUFFER == p_data)); + HAL_ASSERT(!(NULL_BUFFER == p_admin)); + HAL_ASSERT(!(snvm_module > 221u)); + + HAL_ASSERT(data_len == 236u || data_len == 252u); + + if((p_data == NULL_BUFFER) || + (snvm_module >= 221) || + (p_admin == NULL_BUFFER)) + { + return status; + } + + *p_frame = snvm_module; /* SNVMADDR - SNVM module */ + + p_frame += 4u; /* RESERVED - For alignment */ + + /* Copy user key */ + if (236u == data_len) + { + HAL_ASSERT(p_user_key != NULL_BUFFER); + + if(p_user_key == NULL_BUFFER) + { + return status; + } + + for (index = 0u; index < 12u; index++) + { + *p_frame = p_user_key[index]; + p_frame++; + } + } + else + { + p_frame += 12u; + } + + status = execute_ss_command(SNVM_READ_REQUEST_CMD, + &frame[0], + 16u, + response, + (data_len + 4u), + mb_offset, + 4u); /* mentioning offset to number of words instead of bytes */ + + if (SYS_SUCCESS == status) + { + for (index = 0u; index < 4u; index++) + { + *(p_admin+index) = (uint32_t)response[index]; + } + + + /* Copy data into user buffer. */ + for (index = 4u; index < (data_len + 4u); index++) + { + *(p_data + (index - 4u)) = response[index]; + } + } + else + { + ; + } + + return status; +} + +/***************************************************************************//** + * SYS_nonce_service() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +uint8_t SYS_nonce_service +( + const uint8_t * p_nonce, + uint16_t mb_offset +) +{ + uint8_t status = SYS_PARAM_ERR; + + if((p_nonce == NULL_BUFFER)) + { + return status; + } + + status = execute_ss_command(NONCE_SERVICE_REQUEST_CMD, + NULL_BUFFER, + 0u, + p_nonce, + NONCE_SERVICE_RESP_LEN, + mb_offset, + 0u); + + return status; +} + +/***************************************************************************//** + * SYS_bitstream_authenticate_service() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +uint8_t SYS_bitstream_authenticate_service +( + uint32_t spi_flash_address, + uint16_t mb_offset +) +{ + uint8_t status = SYS_PARAM_ERR; + uint32_t l_spi_flash_address = spi_flash_address; + status = execute_ss_command(BITSTREAM_AUTHENTICATE_CMD, + (uint8_t* )&l_spi_flash_address, + 4u, + NULL_BUFFER, + 0u, + mb_offset, + 0u); + + return status; +} + +/***************************************************************************//** + * SYS_IAP_image_authenticate_service() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +uint8_t SYS_IAP_image_authenticate_service +( + uint8_t spi_idx +) +{ + uint8_t status = SYS_PARAM_ERR; + + HAL_ASSERT(!(spi_idx == 1u)); + + if (spi_idx == 1u) + { + return status; + } + + status = execute_ss_command(IAP_BITSTREAM_AUTHENTICATE_CMD, + NULL_BUFFER, + 0u, + NULL_BUFFER, + 0u, + spi_idx, + 0u); + + return status; +} + +/***************************************************************************//** + * SYS_digest_check_service() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +uint8_t SYS_digest_check_service +( + uint32_t options, + uint16_t mb_offset +) +{ + uint8_t status = SYS_PARAM_ERR; + uint32_t l_options = options; + + status = execute_ss_command(DIGEST_CHECK_CMD, + (uint8_t* )&l_options, + 4u, + NULL_BUFFER, + 0u, + mb_offset, + 0u); + return status; +} + +/***************************************************************************//** + * SYS_iap_service() + * See "core_sysservices_pf.h" for details of how to use this function. + */ +uint8_t SYS_iap_service +( + uint8_t iap_cmd, + uint32_t spiaddr, + uint16_t mb_offset +) +{ + uint8_t status = SYS_PARAM_ERR; + uint16_t l_mb_offset = 0u; + uint16_t cmd_data_size = 0u; + uint8_t* cmd_data = NULL_BUFFER; + bool invalid_param = false; + + if (((IAP_PROGRAM_BY_SPIIDX_CMD == iap_cmd) + || (IAP_VERIFY_BY_SPIIDX_CMD == iap_cmd)) + && (1u == spiaddr)) + { + invalid_param = true; + HAL_ASSERT(!invalid_param); + } + + if (!invalid_param) + { + switch(iap_cmd) + { + case IAP_PROGRAM_BY_SPIIDX_CMD: + case IAP_VERIFY_BY_SPIIDX_CMD: + /*In SPI_IDX based program and verify commands, + * Mailbox is not Required. Instead of mailbox offset + * SPI_IDX is passed as parameter.*/ + l_mb_offset = (uint16_t)(0xFFu & spiaddr); + break; + + case IAP_PROGRAM_BY_SPIADDR_CMD: + case IAP_VERIFY_BY_SPIADDR_CMD: + /*In SPI_ADDR based program and verify commands, + * Mailbox is Required*/ + l_mb_offset = mb_offset; + /*command data size is four bytes holding the + * SPI Address in it.*/ + cmd_data_size = 4u; + cmd_data = (uint8_t*)&spiaddr; + break; + + case IAP_AUTOUPDATE_CMD: + /*In auto update command Mailbox is not Required*/ + l_mb_offset = 0u; + break; + + default: + l_mb_offset = 0u; + + } + + status = execute_ss_command( + (uint8_t)iap_cmd, + cmd_data, + cmd_data_size, + NULL_BUFFER, + 0, + (uint16_t)l_mb_offset, + 0); + } + + return status; +} + +/***************************************************************************//** + Internal functions. +*/ +/* +This function executes the SS command. If Mailbox input data is required by the +it will first load it from cmd_data into the Mailbox. If the service requires +the response data to be read from mailbox, it will do so and store it in p_response. +*/ +static uint8_t execute_ss_command +( + uint8_t cmd_opcode, + const uint8_t* cmd_data, + uint16_t cmd_data_size, + const uint8_t* p_response, + uint16_t response_size, + uint16_t mb_offset, + uint16_t response_offset +) +{ + /* Pointer used during Writing to Mailbox memory. */ + uint32_t status = 0u; + uint16_t idx = 0u; + uint16_t ss_command = 0u; + uint32_t* word_buf; + uint16_t timeout_count = SS_TIMEOUT_COUNT; + + /* making sure that the system controller is not executing any service i.e. + SS_USER_BUSY is gone 0 */ + + while (1u == HAL_get_32bit_reg_field(g_css_pf_base_addr, SS_USER_BUSY)) + { + --timeout_count; + + if (timeout_count == 0) + { + return SS_USER_BUSY_TIMEOUT; + } + } + + /* Form the SS command: bit 0to6 is the opcode, bit 7to15 is the Mailbox offset + For some services this field has another meaning + (e.g. for IAP bitstream auth. it means spi_idx) */ + ss_command = ((mb_offset << 7u) | (cmd_opcode & 0x7Fu)); + + /* Load the command register with the SS request command code*/ + HAL_set_32bit_reg(g_css_pf_base_addr, SS_CMD, ss_command); + + if (cmd_data_size > 0u) + { + HAL_ASSERT(!(NULL_BUFFER == cmd_data)); + HAL_ASSERT(!(cmd_data_size % 4u)); + + /* Load the MBX_WCNT register with number of words */ + HAL_set_32bit_reg( g_css_pf_base_addr, MBX_WCNT, (cmd_data_size/4u)); + + /* Load the MBX_WADDR register with offset of input data (write to Mailbox) + For all the services this offset remains either 0 or Not applicable + for the services in which no Mailbox write is required.*/ + HAL_set_32bit_reg( g_css_pf_base_addr, MBX_WADDR, (0x00u + mb_offset)); + + } + + if (response_size > 0u) + { + HAL_ASSERT(!(NULL_BUFFER == p_response)); + HAL_ASSERT(!(response_size % 4u)); + + /* + Load the MBX_RWCNT register with number of words to be read from Mailbox + */ + HAL_set_32bit_reg( g_css_pf_base_addr, MBX_RCNT, (response_size/4u)); + + /* + Load the MBX_RADRDESC register with offset address within the mailbox + format for that particular service. + It will be 0 for the services where there is no output data from G5CONTROL + is expected. + This function assumes that this value is pre-calculated by service specific + functions as this value is fixed for each service. + */ + HAL_set_32bit_reg( g_css_pf_base_addr, MBX_RADDR, (response_offset + mb_offset)); + } + + /*Set the request bit in SYS_SERV_REQ register to start the service*/ + HAL_set_32bit_reg_field(g_css_pf_base_addr, SS_REQ_REQ, 0x01u); + + if (cmd_data_size > 0u) + { + word_buf = (uint32_t*)cmd_data; + + /* Write the user data into mail box. */ + for (idx = 0u; idx < (cmd_data_size/4u); idx++) + { + HAL_set_32bit_reg( g_css_pf_base_addr, MBX_WDATA, word_buf[idx]); + } + } + + timeout_count = SS_TIMEOUT_COUNT; + if (response_size > 0u) + { + word_buf = (uint32_t*)p_response; + + for (idx = 0u; idx < (response_size/4u); idx++) + { + while (0u == HAL_get_32bit_reg_field(g_css_pf_base_addr, + SS_USER_RDVLD)) + { + --timeout_count; + + if (timeout_count == 0) + { + return SS_USER_RDVLD_TIMEOUT; + } + } + word_buf[idx] = HAL_get_32bit_reg(g_css_pf_base_addr, MBX_RDATA); + } + } + + timeout_count = SS_TIMEOUT_COUNT; + /* make sure that service is complete i.e. SS_USER_BUSY is gone 0 */ + while (1u == HAL_get_32bit_reg_field(g_css_pf_base_addr, SS_USER_BUSY)) + { + --timeout_count; + + if (timeout_count == 0) + { + return SS_USER_RDVLD_TIMEOUT; + } + } + + /* Read the status returned by System Controller */ + status = HAL_get_32bit_reg(g_css_pf_base_addr, SS_STAT); + + return (uint8_t)status; +} + +#ifdef __cplusplus +} +#endif diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSysServices_PF/core_sysservices_pf.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSysServices_PF/core_sysservices_pf.h new file mode 100644 index 0000000..fad81b7 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSysServices_PF/core_sysservices_pf.h @@ -0,0 +1,1249 @@ +/******************************************************************************* + * Copyright 2019-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * This file contains the application programming interface for the + * CoreSysServices_PF bare metal driver. + */ +/*=========================================================================*//** + @mainpage CoreSysServices_PF Bare Metal Driver. + + @section intro_sec Introduction + The PolarFire System Services (PF_SYSTEM_SERVICES) SgCore enables executing + the system services on the PolarFire and PolarFire SoC device. The system services + are the system controller actions initiated by the System Controller's System + Service Interface (SSI). The PolarFire System Services "SgCore" provides a + method to initiate these system services. The PF_SYSTEM_SERVICES interacts + with the system controller on SSI and Mailbox interface to initiate system + services, exchange data required for that services, and to know the successful + completion or error status. + + The PF_SYSTEM_SERVICES provides an APB interface for controlling the registers + functions for controlling the PF_SYSTEM_SERVICES as part of a bare metal system + register implemented within it. This software driver provides a set of where no + part of an operating system but the implementation of the adaptation layer + operating system is available. This driver is adapted for use in + between this driver and the operating system's driver model is outside the + scope of this driver. + + ## Features + The CoreSysServices_PF driver provides the following features: + - Executing device and design information services + - Executing design services + - Executing data security services + - Executing Fabric services + + The CoreSysServices_PF driver is provided as C source code. + + @section Driver Configuration + The application software should configure the CoreSysServices_PF driver through + calling the SYS_init() function. Only one instance of PF_SYSTEM_SERVICES SgCore is + supported. No additional configuration files are required to use the driver. + If using this driver on RT PolarFire device FPGA, define RT_DEVICE_FAMILY + macro in application. + + @section theory_op Theory of Operation + The CoreSysServices_PF driver provides access to the PolarFire system services. + These system services are grouped into the following categories: + + Device and Design Information Service + - Serial Number Service + - USERCODE Service + - Design Info Service + - Device Certificate Services + - Read Digests + - Query Security + - Read Debug Info + - Read eNVM param + + Design Services + - Bitstream authentication service + - IAP bitstream authentication service + + Data Security Services + - Digital Signature Service + - Secure NVM (SNVM) Functions + - PUF Emulation Service + - Nonce Service + + Fabric Services + - Digest Check Service + - In Application programming(IAP)/Auto-Update service + + Initialization and Configuration + + The CoreSysServices_PF driver is initialized by calling the SYS_init() + function. The SYS_init() function must be called before calling any other + CoreSysServices_PF driver functions. + + Device and Design Information Services + + The CoreSysServices_PF driver is used to read information about the device + and the design using the following functions: + - SYS_get_serial_number() + - SYS_get_user_code() + - SYS_get_design_info() + - SYS_get_device_certificate() + - SYS_read_digest() + - SYS_query_security() + - SYS_read_debug_info() + + Design Authentication Services + + The CoreSysServices_PF driver is used to execute design services using the + following functions: + - SYS_bitstream_authenticate_service() + - SYS_IAP_image_authenticate_service() + + Data Security Services + + The CoreSysServices_PF driver is used to execute data security services + using the following functions: + - SYS_digital_signature_service() + - SYS_secure_nvm_write() + - SYS_secure_nvm_read() + - SYS_puf_emulation_service () + - SYS_nonce_service () + + Executing Fabric Services + + The CoreSysServices_PF driver is used to execute fabric services using the + following functions: + - SYS_digest_check_service() + - SYS_iap_service() + + All the service execution functions return the 8-bit status, which is returned + by the system controller on executing the given service. A '0' value indicates + successful execution of that service. A non-zero value indicates error. + The error codes for each service are different. See individual function + description to know the exact meanings of the error codes for each service. + + The function descriptions in this file mainly focus on the details required + by the user to use the APIs provided by this driver to execute the services. + To know the complete details of the system services, see the + PolarFire FPGA and PolarFire SoC FPGA System Services [document](https://onlinedocs.microchip.com/pr/GUID-1409CF11-8EF9-4C24-A94E-70979A688632-en-US-3/index.html) + + *//*=========================================================================*/ +#ifndef __CORE_SYSSERV_PF_H +#define __CORE_SYSSERV_PF_H 1 + +#ifndef LEGACY_DIR_STRUCTURE +#include "hal/hal.h" + +#else +#include "hal.h" +#include "hal_assert.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** +* # Service Execution Success and Error Status Codes +* +* The following status codes are the return values from the system service functions. +* For any service, a return value '0' indicates that the service was executed +* successfully. A non-zero return value indicates that the service was not +* executed successfully. For all the services, the return value represents the +* status code returned by the system controller for the respective service, +* except the values SYS_PARAM_ERR, SS_USER_BUSY_TIMEOUT, and SS_USER_RDVLD_TIMEOUT. +* These three values indicate the error conditions detected by this driver and +* they do not overlap with the status code returned by the system controller for +* any of the system service. +* +* SYS_SUCCESS +* System service executed successfully +* +* SYS_PARAM_ERR +* System service cannot be executed as one or more parameters are not as +* expected by this driver. No read/write access is performed with the +* IP. +* +* SS_USER_BUSY_TIMEOUT +* The System service request is initiated and the driver timed-out while +* waiting for the system service to complete. The System Service +* completion is indicated by de-assertion of the SS_USER_BUSY bit by the +* IP. +* +* SS_USER_RDVLD_TIMEOUT +* The System service request is initiated and the driver timed-out while +* waiting for SS_USER_RDVLD bit, which indicates availability of data to +* be read from the mailbox, to become active. +*/ +#define SYS_SUCCESS 0u +#define SYS_PARAM_ERR 0xFFu +#define SS_USER_BUSY_TIMEOUT 0xFAu +#define SS_USER_RDVLD_TIMEOUT 0xFBu + +/** +* # System Service Timeout Count +* +* The SS_TIMEOUT_COUNT value is used by the driver as a timeout count +* while waiting for either the SS_USER_BUSY or SS_USER_RDVLD. This empirical +* value is sufficiently large so that the operations are falsely +* timeout in the normal circumstance. It is provided as a way to provide +* more debug information to the application in case there are some +* unforeseen issues. You may change this value for your need based on your +* system design. +*/ +#define SS_TIMEOUT_COUNT 40000u +/* + * SYS_DCF_DEVICE_MISMATCH + * Public key or FSN do not match device + * + * + * SYS_DCF_INVALID_SIGNATURE + * Certificate signature is invalid + * + * SYS_DCF_SYSTEM_ERROR + * PUF or storage failure + */ +#define SYS_DCF_DEVICE_MISMATCH 1u +#define SYS_DCF_INVALID_SIGNATURE 2u +#define SYS_DCF_SYSTEM_ERROR 3u + +/* + * SYS_NONCE_PUK_FETCH_ERROR + * Error fetching PUK + * + * SYS_NONCE_SEED_GEN_ERROR + * Error generating seed + */ +#define SYS_NONCE_PUK_FETCH_ERROR 1u +#define SYS_NONCE_SEED_GEN_ERROR 2u + +/** + * # Secure Nvm Write Error Codes + * + * SNVM_WRITE_INVALID_SNVMADDR + * Illegal page address + * + * SNVM_WRITE_FAILURE + * PNVM program/verify failed + * + * SNVM_WRITE_SYSTEM_ERROR + * PUF or storage failure + * + * SNVM_WRITE_NOT_PERMITTED + * Write is not permitted + */ +#define SNVM_WRITE_INVALID_SNVMADDR 1u +#define SNVM_WRITE_FAILURE 2u +#define SNVM_WRITE_SYSTEM_ERROR 3u +#define SNVM_WRITE_NOT_PERMITTED 4u + +/** + * # Secure Nvm Read Error Codes + * + * SNVM_READ_INVALID_SNVMADDR + * Illegal page address + * + * SNVM_READ_AUTHENTICATION_FAILURE + * Storage corrupt or incorrect USK + * + * SNVM_READ_SYSTEM_ERROR + * PUF or storage failure + * + */ +#define SNVM_READ_INVALID_SNVMADDR 1u +#define SNVM_READ_AUTHENTICATION_FAILURE 2u +#define SNVM_READ_SYSTEM_ERROR 3u + +/** + * # Digital Signature Service Error Codes + * + * DIGITAL_SIGNATURE_FEK_FAILURE_ERROR + * Error retrieving FEK + * + * DIGITAL_SIGNATURE_DRBG_ERROR + * Failed to generate nonce + * + * DIGITAL_SIGNATURE_ECDSA_ERROR + * ECDSA failed + */ +#define DIGITAL_SIGNATURE_FEK_FAILURE_ERROR 1u +#define DIGITAL_SIGNATURE_DRBG_ERROR 2u +#define DIGITAL_SIGNATURE_ECDSA_ERROR 3u + +/** + * # Digest Check Error Codes + * + * NOTE: When these error occur, the DIGEST tamper flag is triggered. + * + * DIGEST_CHECK_FABRICERR + * Fabric digest check error + * + * DIGEST_CHECK_CCERR + * UFS Fabric Configuration (CC) segment digest check error + * + * DIGEST_CHECK_SNVMERR + * ROM digest in SNVM segment digest check error + * + * DIGEST_CHECK_ULERR + * UFS UL segment digest check error + * + * DIGEST_CHECK_UK0ERR + * UKDIGEST0 in User Key segment digest check error + * + * DIGEST_CHECK_UK1ERR + * UKDIGEST1 in User Key segment digest check error + * + * DIGEST_CHECK_UK2ERR + * UKDIGEST2 in User Key segment (UPK1) digest check error + * + * DIGEST_CHECK_UK3ERR + * UKDIGEST3 in User Key segment (UK1) digest check error + * + * DIGEST_CHECK_UK4ERR + * UKDIGEST4 in User Key segment (DPK) digest check error + * + * DIGEST_CHECK_UK5ERR + * UKDIGEST5 in User Key segment (UPK2) digest check error + * + * DIGEST_CHECK_UK6ERR + * UKDIGEST6 in User Key segment (UK2) digest check error + * + * DIGEST_CHECK_UPERR + * UFS Permanent Lock (UPERM) segment digest check error + * + * DIGEST_CHECK_SYSERR + * M3 ROM, Factory and Factory Key Segments digest check error + * + */ +#define DIGEST_CHECK_FABRICERR 0x00u +#define DIGEST_CHECK_CCERR 0x01u +#define DIGEST_CHECK_SNVMERR 0x02u +#define DIGEST_CHECK_ULERR 0x03u +#define DIGEST_CHECK_UK0ERR 0x04u +#define DIGEST_CHECK_UK1ERR 0x05u +#define DIGEST_CHECK_UK2ERR 0x06u +#define DIGEST_CHECK_UK3ERR 0x07u +#define DIGEST_CHECK_UK4ERR 0x08u +#define DIGEST_CHECK_UK5ERR 0x09u +#define DIGEST_CHECK_UK6ERR 0x10u +#define DIGEST_CHECK_UPERR 0x11u +#define DIGEST_CHECK_SYSERR 0x12u + +/** + * # Bitstream Authentication and Iap Bitstream Authentication Return Status + * + * BSTREAM_AUTH_CHAINING_MISMATCH_ERR + * Validator or hash chaining mismatch. Incorrectly constructed bitstream or + * wrong key used. + * + * BSTREAM_AUTH_UNEXPECTED_DATA_ERR + * Unexpected data received. + * Additional data received after end of EOB component. + * + * BSTREAM_AUTH_INVALID_ENCRY_KEY_ERR + * Invalid/corrupt encryption key. + * The requested key mode is disabled or the key could not be read/reconstructed. + * + * BSTREAM_AUTH_INVALID_HEADER_ERR + * Invalid component header + * + * BSTREAM_AUTH_BACK_LEVEL_NOT_SATISFIED_ERR + * Back level not satisfied + * + * BSTREAM_AUTH_ILLEGAL_BITSTREAM_MODE_ERR + * Illegal bitstream mode. + * Requested bitstream mode is disabled by user security. + * + * BSTREAM_AUTH_DNS_BINDING_MISMATCH_ERR + * DSN binding mismatch + * + * BSTREAM_AUTH_ILLEGAL_COMPONENT_SEQUENCE_ERR + * Illegal component sequence + * + * BSTREAM_AUTH_INSUFF_DEVICE_CAPAB_ERR + * Insufficient device capabilities + * + * BSTREAM_AUTH_INCORRECT_DEVICEID_ERR + * Incorrect DEVICEID + * + * BSTREAM_AUTH_PROTOCOL_VERSION_ERR + * Unsupported bitstream protocol version (regeneration required) + * + * BSTREAM_AUTH_VERIFY_ERR + * Verify not permitted on this bitstream + * + * BSTREAM_AUTH_INVALID_DEV_CERT_ERR + * Invalid Device Certificate. + * Device SCAC is invalid or not present. + * + * BSTREAM_AUTH_INVALID_DIB_ERR + * Invalid DIB + * + * BSTREAM_AUTH_SPI_NOT_MASTER_ERR + * Device not in SPI Master Mode. + * Error may occur only when bitstream is executed through IAP mode. + * + * BSTREAM_AUTH_AUTOIAP_NO_VALID_IMAGE_ERR + * No valid images found. + * Error may occur when bitstream is executed through Auto Update mode. + * Occurs when no valid image pointers are found. + * + * BSTREAM_AUTH_INDEXIAP_NO_VALID_IMAGE_ERR + * No valid images found. + * Error may occur when bitstream is executed through IAP mode via Index Mode. + * Occurs when No valid image pointers are found. + * + * BSTREAM_AUTH_NEWER_DESIGN_VERSION_ERR + * Programmed design version is newer than AutoUpdate image found. + * Error may occur when bitstream is executed through Auto Update mode. + * + * BSTREAM_AUTH_INVALID_IMAGE_ERR + * Selected image was invalid and no recovery was performed due to valid design + * in device. + * Error may occur only when bitstream is executed through Auto Update or IAP mode + * (This error is here for completeness but only can be observed by running the + * READ_DEBUG_INFO instruction and looking at IAP Error code field). + * + * BSTREAM_AUTH_IMAGE_PROGRAM_FAILED_ERR + * Selected and Recovery image failed to program. + * Error may occur only when bitstream is executed through Auto Update or + * IAP mode + * (This error is here for completeness but only can be observed by running the + * READ_DEBUG_INFO instruction and looking at IAP Error code field). + * + * BSTREAM_AUTH_ABORT_ERR + * Abort. + * Non-bitstream instruction executed during bitstream loading. + * + * BSTREAM_AUTH_NVMVERIFY_ERR + * Fabric/UFS verification failed (min or weak limit) + * + * BSTREAM_AUTH_PROTECTED_ERR + * Device security prevented modification of non-volatile memory + * + * BSTREAM_AUTH_NOTENA + * Programming mode not enabled + * + * BSTREAM_AUTH_PNVMVERIFY + * pNVM verify operation failed + * + * BSTREAM_AUTH_SYSTEM + * System hardware error (PUF or DRBG) + * + * BSTREAM_AUTH_BADCOMPONENT + * An internal error was detected in a component payload + * + * BSTREAM_AUTH_HVPROGERR + * HV programming subsystem failure (pump failure) + * + * BSTREAM_AUTH_HVSTATE + * HV programming subsystem in unexpected state (internal error) + * + */ +#define BSTREAM_AUTH_CHAINING_MISMATCH_ERR 1 +#define BSTREAM_AUTH_UNEXPECTED_DATA_ERR 2 +#define BSTREAM_AUTH_INVALID_ENCRY_KEY_ERR 3 +#define BSTREAM_AUTH_INVALID_HEADER_ERR 4 +#define BSTREAM_AUTH_BACK_LEVEL_NOT_SATISFIED_ERR 5 +#define BSTREAM_AUTH_ILLEGAL_BITSTREAM_MODE_ERR 6 +#define BSTREAM_AUTH_DNS_BINDING_MISMATCH_ERR 7 +#define BSTREAM_AUTH_ILLEGAL_COMPONENT_SEQUENCE_ERR 8 +#define BSTREAM_AUTH_INSUFF_DEVICE_CAPAB_ERR 9 +#define BSTREAM_AUTH_INCORRECT_DEVICEID_ERR 10 +#define BSTREAM_AUTH_PROTOCOL_VERSION_ERR 11 +#define BSTREAM_AUTH_VERIFY_ERR 12 +#define BSTREAM_AUTH_INVALID_DEV_CERT_ERR 13 +#define BSTREAM_AUTH_INVALID_DIB_ERR 14 +#define BSTREAM_AUTH_SPI_NOT_MASTER_ERR 21 +#define BSTREAM_AUTH_AUTOIAP_NO_VALID_IMAGE_ERR 22 +#define BSTREAM_AUTH_INDEXIAP_NO_VALID_IMAGE_ERR 23 +#define BSTREAM_AUTH_NEWER_DESIGN_VERSION_ERR 24 +/* 25 Reserved */ +#define BSTREAM_AUTH_INVALID_IMAGE_ERR 26 +#define BSTREAM_AUTH_IMAGE_PROGRAM_FAILED_ERR 27 +#define BSTREAM_AUTH_ABORT_ERR 127 +#define BSTREAM_AUTH_NVMVERIFY_ERR 128 +#define BSTREAM_AUTH_PROTECTED_ERR 129 +#define BSTREAM_AUTH_NOTENA 130 +#define BSTREAM_AUTH_PNVMVERIFY 131 +#define BSTREAM_AUTH_SYSTEM 132 +#define BSTREAM_AUTH_BADCOMPONENT 133 +#define BSTREAM_AUTH_HVPROGERR 134 +#define BSTREAM_AUTH_HVSTATE 135 + +/***************************************************************************//** + * # Mailbox ECC Status + * Provides ECC status when the mailbox is read. The values are as follows: + * 00: No ECC errors detected, data is correct. + * 01: Exactly one bit error occurred and has been corrected. + * 10: Exactly two bits error occurred and no correction performed. + * 11: Reserved. + */ +#define SYS_MBOX_ECC_NO_ERROR_MASK 0x00u +#define SYS_MBOX_ONEBIT_ERROR_CORRECTED_MASK 0x40u +#define SYS_MBOX_TWOBIT_ERROR_MASK 0xC0u + +/***************************************************************************//** + * Service request command opcodes: +*/ +#define SERIAL_NUMBER_REQUEST_CMD 0x00u +#define USERCODE_REQUEST_CMD 0x01u +#define DESIGN_INFO_REQUEST_CMD 0x02u +#define DEVICE_CERTIFICATE_REQUEST_CMD 0x03u +#define READ_DIGEST_REQUEST_CMD 0x04u +#define QUERY_SECURITY_REQUEST_CMD 0x05u +#define READ_DEBUG_INFO_REQUEST_CMD 0x06u +#define READ_ENVM_PARAM_REQUEST_CMD 0x07u +#define SNVM_NON_AUTHEN_TEXT_REQUEST_CMD 0x10u +#define SNVM_AUTHEN_TEXT_REQUEST_CMD 0x11u +#define SNVM_AUTHEN_CIPHERTEXT_REQUEST_CMD 0x12u +#define SNVM_READ_REQUEST_CMD 0x18u +#define DIGITAL_SIGNATURE_RAW_FORMAT_REQUEST_CMD 0x19u +#define PUF_EMULATION_SERVICE_REQUEST_CMD 0x20u +#define NONCE_SERVICE_REQUEST_CMD 0x21u +#define DIGITAL_SIGNATURE_DER_FORMAT_REQUEST_CMD 0x1Au + +#define BITSTREAM_AUTHENTICATE_CMD 0x23u +#define IAP_BITSTREAM_AUTHENTICATE_CMD 0x22u + +#define DIGEST_CHECK_CMD 0x47u + +#define IAP_PROGRAM_BY_SPIIDX_CMD 0x42u +#define IAP_VERIFY_BY_SPIIDX_CMD 0x44u +#define IAP_PROGRAM_BY_SPIADDR_CMD 0x43u +#define IAP_VERIFY_BY_SPIADDR_CMD 0x45u +#define IAP_AUTOUPDATE_CMD 0x46u + +/***************************************************************************//** + * Service request Mailbox return data length + */ +#define SERIAL_NUMBER_RESP_LEN 16u +#define USERCODE_RESP_LEN 4u +#define DESIGN_INFO_RESP_LEN 36u +#define DEVICE_CERTIFICATE_RESP_LEN 1024u +#define READ_DIGEST_RESP_LEN 416u +#define QUERY_SECURITY_RESP_LEN 9u +#define READ_DEBUG_INFO_RESP_LEN 76u +#define READ_ENVM_PARAM_RESP_LEN 256u +#define NONCE_SERVICE_RESP_LEN 32u + +#define PUF_EMULATION_SERVICE_CMD_LEN 20u +#define PUF_EMULATION_SERVICE_RESP_LEN 32u + +#define DIGITAL_SIGNATURE_HASH_LEN 48u +#define DIGITAL_SIGNATURE_RAW_FORMAT_RESP_SIZE 96u +#define DIGITAL_SIGNATURE_DER_FORMAT_RESP_SIZE 104u + +#define USER_SECRET_KEY_LEN 12u + +/* Same driver can be used on PolarFire SoC platform and the response length + * is different for PolarFire SoC. Constants defined below are used only when the + * PF System services driver is used with PolarFire SoC Platform. + */ +#define READ_DIGEST_MPFS_RESP_LEN 576u +#define QUERY_SECURITY_MPFS_RESP_LEN 33u + +/* SNVM Input data length from sNVM write. */ +#ifndef RT_DEVICE_FAMILY +/* SNVMADDR + RESERVED + PT + USK */ +#define NON_AUTHENTICATED_TEXT_DATA_LEN 256u + +/* SNVMADDR + RESERVED + PT */ +#define AUTHENTICATED_TEXT_DATA_LEN 252u +#else +/* SNVMADDR + RESERVED + PT + USK */ +#define NON_AUTHENTICATED_TEXT_DATA_LEN 224u + +/* SNVMADDR + RESERVED + PT */ +#define AUTHENTICATED_TEXT_DATA_LEN 220u +#endif + +/** + * # Digest Check Input Options + * + * DIGEST_CHECK_FABRIC + * Carry out digest check on Fabric + * + * DIGEST_CHECK_CC + * Carry out digest check on UFS Fabric Configuration (CC) segment + * + * DIGEST_CHECK_SNVM + * Carry out digest check on ROM digest in SNVM segment + * + * DIGEST_CHECK_UL + * Carry out digest check on UFS UL segment + * + * DIGEST_CHECK_UKDIGEST0 + * Carry out digest check on UKDIGEST0 in User Key segment + * + * DIGEST_CHECK_UKDIGEST1 + * Carry out digest check on UKDIGEST1 in User Key segment + * + * DIGEST_CHECK_UKDIGEST2 + * Carry out digest check on UKDIGEST2 in User Key segment (UPK1) + * + * DIGEST_CHECK_UKDIGEST3 + * Carry out digest check on UKDIGEST3 in User Key segment (UK1) + * + * DIGEST_CHECK_UKDIGEST4 + * Carry out digest check on UKDIGEST4 in User Key segment (DPK) + * + * DIGEST_CHECK_UKDIGEST5 + * Carry out digest check on UKDIGEST5 in User Key segment (UPK2) + * + * DIGEST_CHECK_UKDIGEST6 + * Carry out digest check on UKDIGEST6 in User Key segment (UK2) + * + * DIGEST_CHECK_UPERM + * Carry out digest check on UFS Permanent lock (UPERM) segment + * + * DIGEST_CHECK_SYS + * Carry out digest check on Factory and Factory Key Segments + * + */ +#define DIGEST_CHECK_FABRIC (0x01<<0x00u) /*Fabric digest*/ +#define DIGEST_CHECK_CC (0x01<<0x01u) /*UFS Fabric Configuration (CC) segment*/ +#define DIGEST_CHECK_SNVM (0x01<<0x02u) /*ROM digest in SNVM segment*/ +#define DIGEST_CHECK_UL (0x01<<0x03u) /*UFS UL segment*/ +#define DIGEST_CHECK_UKDIGEST0 (0x01<<0x04u) /*UKDIGEST0 in User Key segment*/ +#define DIGEST_CHECK_UKDIGEST1 (0x01<<0x05u) /*UKDIGEST1 in User Key segment*/ +#define DIGEST_CHECK_UKDIGEST2 (0x01<<0x06u) /*UKDIGEST2 in User Key segment (UPK1)*/ +#define DIGEST_CHECK_UKDIGEST3 (0x01<<0x07u) /*UKDIGEST3 in User Key segment (UK1)*/ +#define DIGEST_CHECK_UKDIGEST4 (0x01<<0x08u) /*UKDIGEST4 in User Key segment (DPK)*/ +#define DIGEST_CHECK_UKDIGEST5 (0x01<<0x09u) /*UKDIGEST5 in User Key segment (UPK2)*/ +#define DIGEST_CHECK_UKDIGEST6 (0x01<<0x0au) /*UKDIGEST6 in User Key segment (UK2)*/ +#define DIGEST_CHECK_UPERM (0x01<<0x0bu) /*UFS Permanent lock (UPERM) segment*/ +#define DIGEST_CHECK_SYS (0x01<<0x0cu) /*Factory and Factory Key Segments.*/ + +/***************************************************************************//** + * The function SYS_init() is used to initialize the internal data structures of + * this driver. Currently this function is empty. + * + * @param base_addr The base_addr parameter specifies the base address of the + * PF_System_services core. + * + * @return This function does not return a value. + */ +void +SYS_init +( + uint32_t base_addr +); + +/***************************************************************************//** + * The function SYS_get_serial_number() is used to execute "serial number" system + * service. + * + * @param p_serial_number The p_serial_number parameter is a pointer to a buffer + * in which the data returned by system controller + * is copied. + * + * @param mb_offset The mb_offset parameter specifies the offset from + * the start of Mailbox where the data related to this service + * is available. Note that all accesses to the mailbox + * are of word length (4 bytes). Value '10' of this parameter + * means that the data access area for this service + * starts from 11th word (offset 10) in the Mailbox. + * + * @return This function returns the status code returned by the + * system controller for this service. A '0' status code + * means that the service was executed successfully. + */ +uint8_t +SYS_get_serial_number +( + const uint8_t * p_serial_number, + uint16_t mb_offset +); + +/***************************************************************************//** + * The function SYS_get_user_code() is used to execute "USERCODE" system + * service. + * @param p_user_code The p_user_code parameter is a pointer to a buffer + * in which the data returned by system controller is + * copied. + * + * @param mb_offset The mb_offset parameter specifies the offset from + * the start of Mailbox where the data related to this service + * is available. Note that all accesses to the mailbox + * are of word length (4 bytes). Value '10' of this parameter + * means that the data access area for this service + * starts from 11th word (offset 10) in the Mailbox. + * + * @return This function returns the status code returned by the + * system controller for this service. A '0' status code + * means that the service was executed successfully. + */ +uint8_t +SYS_get_user_code +( + const uint8_t * p_user_code, + uint16_t mb_offset +); + +/***************************************************************************//** + * The function SYS_get_design_info() is used to execute "Get Design Info" system + * service. + * + * @param p_design_info The p_design_info parameter is a pointer to a buffer + * in which the data returned by system controller is + * copied. Total size of debug information is 36 bytes. + * The data from the system controller includes the 256-bit + * user-defined design ID, 16-bit design version, and 16-bit + * design back level. + * + * @param mb_offset The mb_offset parameter specifies the offset from + * the start of Mailbox where the data related to this service + * is available. Note that all accesses to the mailbox + * are of word length (4 bytes). Value '10' of this parameter + * means that the data access area for this service + * starts from 11th word (offset 10) in the Mailbox. + * + * @return This function returns the status code returned by the + * system controller for this service. A '0' status code + * means that the service was executed successfully. + */ +uint8_t +SYS_get_design_info +( + const uint8_t * p_design_info, + uint16_t mb_offset +); + +/***************************************************************************//** + * The function SYS_get_device_certificate() is used to execute "Get Device + * Certificate" system service. + * + * @param p_device_certificate The p_device_certificate parameter is a pointer + * to a buffer in which the data returned by the + * system controller is copied. + * + * @param mb_offset The mb_offset parameter specifies the offset from + * the start of Mailbox where the data related to this service + * is available. Note that all accesses to the mailbox + * are of word length (4 bytes). Value '10' of this parameter + * means that the data access area for this service + * starts from 11th word (offset 10) in the Mailbox. + * + * @return This function returns the status code returned by the + * system controller for this service. A '0' status code means that + * the service was executed successfully. + * + */ +uint8_t +SYS_get_device_certificate +( + const uint8_t * p_device_certificate, + uint16_t mb_offset +); + +/***************************************************************************//** + * The function SYS_read_digest() is used to execute "Read Digest" system service. + * + * @param p_digest The p_digest parameter is a pointer to a buffer + * in which the data returned by system controller is + * copied. + * + * @param mb_offset The mb_offset parameter specifies the offset from + * the start of Mailbox where the data related to this service + * is available. Note that all accesses to the mailbox + * are of word length (4 bytes). Value '10' of this parameter + * means that the data access area for this service + * starts from 11th word (offset 10) in the Mailbox. + * + * @return This function returns the status code returned by the + * system controller for this service. A '0' status code + * means that the service was executed successfully. + */ +uint8_t SYS_read_digest +( + const uint8_t * p_digest, + uint16_t mb_offset +); + +/***************************************************************************//** + * The function SYS_query_security() is used to execute "Query Security" system + * service. + * + * @param p_security_locks The p_security_locks parameter is a pointer to a buffer + * in which the data returned by system controller is copied. + * + * @param mb_offset The mb_offset parameter specifies the offset from + * the start of Mailbox where the data related to this service + * is available. Note that all accesses to the mailbox + * are of word length (4 bytes). Value '10' of this parameter + * means that the data access area for this service + * starts from 11th word (offset 10) in the Mailbox. + * + * @return This function returns the status code returned by the + * system controller for this service. A '0' status code means that + * the service was executed successfully. + */ +uint8_t SYS_query_security +( + uint8_t * p_security_locks, + uint16_t mb_offset +); + +/***************************************************************************//** + * The function SYS_read_debug_info() is used to execute "Read Debug info" system + * service. + * + * @param p_debug_info The p_debug_info parameter is a pointer to a buffer + * in which the data returned by system controller is + * copied. + * + * @param mb_offset The mb_offset parameter specifies the offset from + * the start of Mailbox where the data related to this service + * is available. Note that all accesses to the mailbox + * are of word length (4 bytes). Value '10' of this parameter + * means that the data access area for this service + * starts from 11th word (offset 10) in the Mailbox. + * + * @return This function returns the status code returned by the + * system controller for this service. A '0' status code + * means that the service was executed successfully. + */ +uint8_t SYS_read_debug_info +( + const uint8_t * p_debug_info, + uint16_t mb_offset +); + +#ifdef CORESYSSERVICES_PFSOC +/***************************************************************************//** + * The function SYS_read_envm_parameter() is used to retrieve all parameters needed + * for the eNVM operation and programming. + * + * NOTE: This service is available only on PolarFire SoC Platform. + * This service is not yet supported by PF_SYSTEM_SERVICES 3.0.100. + * + * @param p_envm_param The p_envm_param parameter is a pointer to a buffer + * in which the data returned by system controller is copied. + * This buffer stores all the eNVM parameters. + * + * @param mb_offset The mb_offset parameter specifies the offset from + * the start of Mailbox where the data related to this service + * is available. Note that all accesses to the mailbox + * are of word length (4 bytes). Value '10' of this parameter + * means that the data access area for this service + * starts from 11th word (offset 10) in the Mailbox. + * + * @return The SYS_read_envm_parameter service will return zero if the + * service executed successfully, otherwise, it will return + * one indicating error. + */ +uint8_t SYS_read_envm_parameter +( + uint8_t * p_envm_param, + uint16_t mb_offset +); +#endif +/***************************************************************************//** + * The function SYS_puf_emulation_service() is used to authenticate a device. + * + * The SYS_puf_emulation_service() function accept a challenge comprising a + * 8-bit optype and 128-bit challenge and return a 256-bit response unique to + * the given challenge and the device. + * + * @param p_challenge The p_challenge parameter specifies the 128-bit challenge + * to generate the 256-bits unique response. + * + * @param op_type The op_type parameter specifies the operational parameter + * to generate the 256-bits unique response. + * + * @param p_response The p_response parameter is a pointer to a buffer where + * the data returned which is the response by system controller + * is copied. + * + * @param mb_offset The mb_offset parameter specifies the offset from + * the start of Mailbox where the data related to this service + * is available. Note that all accesses to the mailbox + * are of word length (4 bytes). Value '10' of this parameter + * means that the data access area for this service + * starts from 11th word (offset 10) in the Mailbox. + * + * @return The SYS_puf_emulation_service function will return zero + * if the service executed successfully, otherwise, it will + * return one indicating error. + */ +uint8_t SYS_puf_emulation_service +( + const uint8_t * p_challenge, + uint8_t op_type, + uint8_t* p_response, + uint16_t mb_offset +); + +/***************************************************************************//** + * The SYS_digital_signature_service() function is used to generate P-384 ECDSA + * signature based on SHA384 hash value. + * + * @param p_hash The p_hash parameter is a pointer to the buffer which + * contain the 48 bytes SHA384 Hash value (input value). + * + * @param format The format parameter specifies the output format of + * generated SIGNATURE field. The different types of output + * signature formats are as follow: + * - DIGITAL_SIGNATURE_RAW_FORMAT + * - DIGITAL_SIGNATURE_DER_FORMAT + * + * @param p_response The p_response parameter is a pointer to a buffer that + * contains the generated ECDSA signature. The field may be + * 96 bytes or 104 bytes depending upon the output format. + * + * @param mb_offset The mb_offset parameter specifies the offset from + * the start of Mailbox where the data related to this service + * is available. Note that all accesses to the mailbox + * are of word length (4 bytes). Value '10' of this parameter + * means that the data access area for this service + * starts from 11th word (offset 10) in the Mailbox. + * + * @return The SYS_digital_signature_service function returns + * zero if the service executed successfully, otherwise, it + * returns non-zero values indicating error. + */ +uint8_t SYS_digital_signature_service +( + const uint8_t* p_hash, + uint8_t format, + uint8_t* p_response, + uint16_t mb_offset +); + +/***************************************************************************//** + * The SYS_secure_nvm_write() function writes data in the sNVM region. + * Data gets stored in the following format: + * - Non-authenticated plaintext + * - Authenticated plaintext + * - Authenticated ciphertext + * + * Note: If you are executing this function with Authenticated plaintext + * or Authenticated ciphertext on a device whose sNVM was never previously + * written to, then the service may fail. For it to work, you must first write + * Authenticated data to the sNVM using Libero along with USK client and + * custom security. This flow generates the SMK. See UG0753 PolarFire FPGA + * Security User Guide for further details. + + * @param format The format parameter specifies the format used to write + * data in sNVM region. The different type of text formats + * are as follow: + * - NON_AUTHENTICATED_PLAINTEXT_FORMAT + * - AUTHENTICATED_PLAINTEXT_FORMAT + * - AUTHENTICATED_CIPHERTEXT_FORMAT + * + * @param snvm_module The snvm_module parameter specifies the the sNVM module + * in which the data need to be written. + * + * @param p_data The p_data parameter is a pointer to a buffer which + * contains the data to be stored in sNVM region. The data + * length to be written is fixed depending on the format + * parameter. If NON_AUTHENTICATED_PLAINTEXT_FORMAT is + * selected, then you can write 252 bytes in the sNVM module. + * For other two formats the data length is 236 bytes. + * + * @param p_user_key The p_user_key parameter is a pointer to a buffer which + * contain the 96-bit key USK (user secret key). This user + * secret key will enhance the security when authentication + * is used. That is, when Authenticated plaintext and + * Authenticated ciphertext format is selected. + * + * @param mb_offset The mb_offset parameter specifies the offset from + * the start of Mailbox where the data related to this service + * is available. Note that all accesses to the mailbox + * are of word length (4 bytes). Value '10' of this parameter + * means that the data access area for this service + * starts from 11th word (offset 10) in the Mailbox. + * + * @return The SYS_digital_signature_service function returns + * zero if the service executed successfully, otherwise, it + * returns non-zero values indicating error. + */ +uint8_t SYS_secure_nvm_write +( + uint8_t format, + uint8_t snvm_module, + const uint8_t* p_data, + const uint8_t* p_user_key, + uint16_t mb_offset +); + +/***************************************************************************//** + * The SYS_secure_nvm_read() function is used to read data present in sNVM region. + * User should provide USK key, if the data was programmed using authentication. + * If the data was written in the sNVM using the authenticated plaintext or the + * authenticated ciphertext service option then this service will return the + * valid data only when authentication is successful. For more details, see + * SYS_secure_nvm_write() function. If the data was written in + * the sNVM using the authenticated plaintext or the authenticated ciphertext + * service option then this service will return the valid data only when + * authentication is successful. For more details, see SYS_secure_nvm_write() + * function and its parameter description. + * + * @param snvm_module The snvm_module parameter specifies the sNVM module + * from which the data need to be read. + * + * @param p_user_key The p_user_key parameter is a pointer to a buffer which + * contain the 96-bit key USK (user secret key). User should + * provide same secret key which is previously used for + * authentication while writing data in sNVM region. + * + * @param p_admin The p_admin parameter is a pointer to the buffer where + * the output page admin data is stored. The page admin + * data is 4 bytes long. + * + * @param p_data The p_data parameter is a pointer to a buffer which + * contains the data read from sNVM region. User should + * provide the buffer large enough to store the read data. + * + * @param data_len The data_len parameter specifies the number of bytes to be + * read from sNVM. + * The application should know whether the data written in the + * chose sNVM module was previously stored using Authentication + * or not. + * The data_len should be 236 bytes, for authenticated data. + * For not authenticated data the data_len should be 252 bytes. + * + * @param mb_offset The mb_offset parameter specifies the offset from + * the start of Mailbox where the data related to this service + * is available. Note that all accesses to the mailbox + * are of word length (4 bytes). Value '10' of this parameter + * means that the data access area for this service + * starts from 11th word (offset 10) in the Mailbox. + * + * @return The SYS_digital_signature_service function returns + * zero if the service executed successfully, otherwise, it + * returns non-zero values indicating error. + */ +uint8_t SYS_secure_nvm_read +( + uint8_t snvm_module, + const uint8_t* p_user_key, + uint8_t* p_admin, + uint8_t* p_data, + uint16_t data_len, + uint16_t mb_offset +); + +/***************************************************************************//** + * The function SYS_nonce_service() is used to issue "Nonce Service" system + * service to the system controller. + * + * @param p_nonce The p_nonce parameter is a pointer to a buffer + * in which the data returned by system controller is copied. + * + * @param mb_offset The mb_offset parameter specifies the offset from + * the start of Mailbox where the data related to this service + * is available. Note that all accesses to the mailbox + * are of word length (4 bytes). Value '10' of this parameter + * means that the data access area for this service + * starts from 11th word (offset 10) in the Mailbox. + * + * @return This function returns the status code returned by the + * system controller for this service. A '0' status code means + * that the service was executed successfully and a non-zero + * value indicates error. See the document link + * provided in the theory of operation section to know more + * about the service and service response. + */ +uint8_t SYS_nonce_service +( + const uint8_t * p_nonce, + uint16_t mb_offset +); + +/***************************************************************************//** + * The SYS_bitstream_authenticate_service() function is used to authenticate + * the Bitstream which is located in SPI through a system service routine. Prior + * to using the IAP service, it may be required to first validate the new + * bitstream before committing the device to reprogramming, thus avoiding the + * need to invoke recovery procedures if the bitstream is invalid. + * + * This service is applicable to bitstreams stored in SPI Flash memory only. + * + * @param spi_flash_address + * The spi_flash_address parameter specifies the address within + * SPI Flash memory where the bit-stream is stored. + * + * @param mb_offset The mb_offset parameter specifies the offset from + * the start of Mailbox where the data related to this service + * is available. Note that all accesses to the mailbox + * are of word length (4 bytes). Value '10' of this parameter + * means that the data access area for this service + * starts from 11th word (offset 10) in the Mailbox. + * + * @return The SYS_bitstream_authenticate_service function will return + * zero if the service executed successfully and the non-zero + * response from system controller indicates error. See + * the document link provided in the theory of + * operation section to know more about the service and service + * response. + */ +uint8_t SYS_bitstream_authenticate_service +( + uint32_t spi_flash_address, + uint16_t mb_offset +); + +/***************************************************************************//** + * The SYS_IAP_image_authenticate_service() function is used to authenticate + * the IAP image which is located in SPI through a system service routine. The + * service checks the image descriptor and the referenced bitstream and optional + * initialization data. If the image is authenticated successfully, then the + * image is guaranteed to be valid when used by an IAP function. + * + * This service is applicable to bitstreams stored in SPI Flash memory only. + * + * @param spi_idx + * The spi_idx parameter specifies the index in the SPI directory to + * be used where the IAP bit-stream is stored. + * Note: To support recovery SPI_IDX=1 should be an empty slot and the recovery + * image should be located in SPI_IDX=0. Since SPI_IDX=1 should be an + * empty slot, it shouldn’t be passed into the system service. + * + * @return The SYS_IAP_image_authenticate_service function will return + * zero if the service executed successfully the non-zero + * response from system controller indicates error. Please + * refer to the document link provided in the theory of + * operation section to know more about the service and service + * response. + */ +uint8_t SYS_IAP_image_authenticate_service +( + uint8_t spi_idx +); + +/***************************************************************************//** + * The SYS_digest_check_service() function is used to Recalculates and compares + * digests of selected non-volatile memories. If the fabric digest is to be + * checked, then the user design must follow all prerequisite steps for the + * FlashFreeze service before invoking this service. + * This service is applicable to bitstreams stored in SPI Flash memory only. + * @param options + * The options parameter specifies the digest check options which + * indicate the area on which the digest check should be performed. + * Below is the list of options. You can OR these options to indicate + * to perform digest check on multiple segments. + * Note: The options parameter is of 2 bytes when used with PF + * device and 4 bytes when used with PolarFire SoC device. + * Options[i] | Description + * ---------------|---------------------------------- + * 0x01 | Fabric digest + * 0x02 | Fabric Configuration (CC) segment + * 0x04 | ROM digest in SNVM segment + * 0x08 | UL segment + * 0x10 | UKDIGEST0 in User Key segment + * 0x20 | UKDIGEST1 in User Key segment + * 0x40 | UKDIGEST2 in User Key segment (UPK1) + * 0x80 | UKDIGEST3 in User Key segment (UK1) + * 0x100 | UKDIGEST4 in User Key segment (DPK) + * 0x200 | UKDIGEST5 in User Key segment (UPK2) + * 0x400 | UKDIGEST6 in User Key segment (UK2) + * 0x800 | UFS Permanent lock (UPERM) segment + * 0x1000 | Factory and Factory Key Segments. + * 0x2000 | UKDIGEST7 in User Key segment (HWM) (PFSoC) + * 0x4000 | ENVMDIGEST (PFSoC only) + * 0x8000 | UKDIGEST8 for MSS Boot Info (PFSoC only) + * 0x10000 | SNVM_RW_ACCESS_MAP Digest (PFSoC only) + * 0x20000 | SBIC revocation digest (PFSoC only) + * + * @param mb_offset The mb_offset parameter specifies the offset from + * the start of Mailbox where the data related to this service + * is available. Note that all accesses to the mailbox + * are of word length (4 bytes). Value '10' of this parameter + * means that the data access area for this service + * starts from 11th word (offset 10) in the Mailbox. + * + * @return The SYS_digest_check_service function will return + * zero if the service executed successfully the non-zero + * response from system controller indicates error. Pleaes + * refer to the document link provided in the theory of + * operation section to know more about the service and service + * response. + */ +uint8_t SYS_digest_check_service +( + uint32_t options, + uint16_t mb_offset +); + +/***************************************************************************//** + * The SYS_iap_service() function is used to IAP service. The IAP service allows + * the user to reprogram the device without the need for an external master. The + * user design writes the bitstream to be programmed into a SPI Flash connected + * to the SPI port. When the service is invoked, the System Controller + * automatically reads the bitstream from the SPI flash and programs the device. + * The service allows the image to be executed in either VERIFY or PROGRAM modes. + * Another option for IAP is to perform the auto-update sequence. In this case + * the newest image of the first two images in the SPI directory is chosen to be + * programmed. + * + * @param iap_cmd + * The iap_cmd parameter specifies the specific IAP command which + * depends upon VERIFY or PROGRAM modes and the SPI address method. + * iap_cmd | Description + * -----------------------|------------ + * IAP_PROGRAM_BY_SPIIDX_CMD | IAP program. + * IAP_VERIFY_BY_SPIIDX_CMD | Fabric Configuration (CC) segment + * IAP_PROGRAM_BY_SPIADDR_CMD | ROM digest in SNVM segment + * IAP_VERIFY_BY_SPIADDR_CMD | UL segment + * IAP_AUTOUPDATE_CMD | UKDIGEST0 in User Key segment + * + * @param spiaddr + * The spiaddr parameter specifies either the index + * in the SPI directory or the SPI address in the SPI Flash memory. + * Below is the list of the possible meaning of spiaddr parameter + * in accordance with the iap_cmd parameter. + * iap_cmd | spiaddr + * ----------------------|----------------- + * IAP_PROGRAM_BY_SPIIDX_CMD | Index in the SPI directory. + * IAP_VERIFY_BY_SPIIDX_CMD | Index in the SPI directory. + * IAP_PROGRAM_BY_SPIADDR_CMD | SPI address in the SPI Flash memory + * IAP_VERIFY_BY_SPIADDR_CMD | SPI address in the SPI Flash memory + * IAP_AUTOUPDATE_CMD | spiaddr is ignored as No index/address required for this command. + * + * @param mb_offset The mb_offset parameter specifies the offset from + * the start of Mailbox where the data related to this service + * is available. Note that all accesses to the mailbox + * are of word length (4 bytes). Value '10' of this parameter + * means that the data access area for this service + * starts from 11th word (offset 10) in the Mailbox. + * Note: For the IAP services with command IAP_PROGRAM_BY_SPIIDX_CMD and + * IAP_VERIFY_BY_SPIIDX_CMD To support recovery SPI_IDX=1 should be an + * empty slot and the recovery image should be located in SPI_IDX=0. + * Since SPI_IDX=1 should be an empty slot it shouldn’t be passed into + * the system service. + * + * @return The SYS_iap_service function will return zero if the service + * executed successfully and the non-zero response from system + * controller indicates error. Please refer to the document + * link provided in the theory of operation section to know + * more about the service and service response. + */ +uint8_t SYS_iap_service +( + uint8_t iap_cmd, + uint32_t spiaddr, + uint16_t mb_offset +); + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_SYSSERV_PF_H */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSysServices_PF/coresysservicespf_regs.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSysServices_PF/coresysservicespf_regs.h new file mode 100644 index 0000000..239b03e --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreSysServices_PF/coresysservicespf_regs.h @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright 2019-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Register bit offsets and masks definitions for CoreSysServices_PF driver. + */ + +#ifndef __CORE_SYSSERV_PF_REGISTERS +#define __CORE_SYSSERV_PF_REGISTERS 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/*------------------------------------------------------------------------------ + * SYS_SERV_CMD (offset 0x04) register details + */ +#define SS_CMD_REG_OFFSET 0x04u + +#define SS_CMD_OFFSET 0x04 +#define SS_CMD_MASK 0x0000FFFFu +#define SS_CMD_SHIFT 0u + +/*------------------------------------------------------------------------------ + * SYS_SERV_STAT (offset 0x08) register details + */ +#define SS_STAT_REG_OFFSET 0x08u + +#define SS_STAT_OFFSET 0x08 +#define SS_STAT_MASK 0x0000FFFFu +#define SS_STAT_SHIFT 0u + +/*------------------------------------------------------------------------------ + * SYS_SERV_REQ (offset 0x0C) register details + */ +#define SS_REQ_REG_OFFSET 0x0Cu + + +#define SS_REQ_REQ_OFFSET 0x0Cu +#define SS_REQ_REQ_MASK 0x00000001UL +#define SS_REQ_REQ_SHIFT 0u + +#define SS_REQ_ABUSY_OFFSET 0x0Cu +#define SS_REQ_ABUSY_MASK 0x00000002UL +#define SS_REQ_ABUSY_SHIFT 1u + +#define SS_REQ_NABUSY_OFFSET 0x0Cu +#define SS_REQ_NABUSY_MASK 0x00000004UL +#define SS_REQ_NABUSY_SHIFT 2u + +#define SS_REQ_SSBUSY_OFFSET 0x0Cu +#define SS_REQ_SSBUSY_MASK 0x00000008UL +#define SS_REQ_SSBUSY_SHIFT 3u + +#define SS_REQ_AREQ_OFFSET 0x0Cu +#define SS_REQ_AREQ_MASK 0x00000010UL +#define SS_REQ_AREQ_SHIFT 4u + +#define SS_REQ_NAREQ_OFFSET 0x0Cu +#define SS_REQ_NAREQ_MASK 0x00000020UL +#define SS_REQ_NAREQ_SHIFT 5u +/*------------------------------------------------------------------------------ + * MBX_ECCSTATUS (offset 0x10) register details + */ +#define MBX_ECCSTATUS_REG_OFFSET 0x10u + +#define MBX_ECCSTATUS_OFFSET 0x10 +#define MBX_ECCSTATUS_MASK 0x03u +#define MBX_ECCSTATUS_SHIFT 0u + + +/*------------------------------------------------------------------------------ + * MBX_WCNT (offset 0x14) register details + */ +#define MBX_WCNT_REG_OFFSET 0x14u + +#define MBX_WCNT_OFFSET 0x14 +#define MBX_WCNT_MASK 0x000001FFu +#define MBX_WCNT_SHIFT 0u + +/*------------------------------------------------------------------------------ + * MBX_RWCNT (offset 0x18) register details + */ +#define MBX_RCNT_REG_OFFSET 0x18u + +#define MBX_RCNT_OFFSET 0x18 +#define MBX_RCNT_MASK 0x000001FFu +#define MBX_RCNT_SHIFT 0u + +/*------------------------------------------------------------------------------ + * MBX_WADRDESC (offset 0x1C) register details + */ +#define MBX_WADDR_REG_OFFSET 0x1Cu + +#define MBX_WADDR_OFFSET 0x1C +#define MBX_WADDR_MASK 0x000001FFu +#define MBX_WADDR_SHIFT 0u + +/*------------------------------------------------------------------------------ + * MBX_RADRDESC (offset 0x20) register details + */ +#define MBX_RADDR_REG_OFFSET 0x20u + +#define MBX_RADDR_OFFSET 0x20 +#define MBX_RADDR_MASK 0x000001FFu +#define MBX_RADDR_SHIFT 0u + +/*------------------------------------------------------------------------------ + * MBX_WDATA (offset 0x28) register details + */ +#define MBX_WDATA_REG_OFFSET 0x28u + +#define MBX_WDATA_OFFSET 0x28 +#define MBX_WDATA_MASK 0xFFFFFFFFu +#define MBX_WDATA_SHIFT 0u + + +/*------------------------------------------------------------------------------ + * MBX_RDATA (offset 0x2C) register details + */ +#define MBX_RDATA_REG_OFFSET 0x2Cu + +#define MBX_RDATA_OFFSET 0x2C +#define MBX_RDATA_MASK 0xFFFFFFFFu +#define MBX_RDATA_SHIFT 0u + +/*------------------------------------------------------------------------------ + * SS_USER (offset 0x30) register details + */ +#define SS_USER_REG_OFFSET 0x30u + +#define SS_USER_BUSY_OFFSET 0x30 +#define SS_USER_BUSY_MASK 0x00000001u +#define SS_USER_BUSY_SHIFT 0u + +#define SS_USER_RDVLD_OFFSET 0x30 +#define SS_USER_RDVLD_MASK 0x00000002u +#define SS_USER_RDVLD_SHIFT 1u + +#define SS_USER_CMDERR_OFFSET 0x30 +#define SS_USER_CMDERR_MASK 0x00000004u +#define SS_USER_CMDERR_SHIFT 2u + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_SYSSERV_PF_REGISTERS */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/core_tse.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/core_tse.c new file mode 100644 index 0000000..7e128f4 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/core_tse.c @@ -0,0 +1,1671 @@ + +/******************************************************************************* + * Copyright 2014 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core_tse.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreTSE 10/100/1000 Mbps Ethernet MAC bare metal software driver implementation. + * + */ + +#include "coretse_regs.h" +#include "core_tse.h" +#include "phy.h" +#include "crc32.h" + +#include "hal/hal.h" +#include "hal/hal_assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**************************************************************************/ +/* Preprocessor Macros */ +/**************************************************************************/ +#define NULL_POINTER 0 + +#define MAC_ADDRESS_LENGTH 6u + +#define DUPLEX_MODE_MASK 0x01u + +#define BROADCAST_COMPARE_BITS 0xFFu +#define MULTICAST_ADDRESS_MASK 0x01u +#define MULTICAST_ADDRESS 0x01u +#define UNICAST_ADDRESS 0x00u + +#if !defined(NDEBUG) +#define IS_STATE(x) (((x) == TSE_ENABLE) || ((x) == TSE_DISABLE)) + +#define IS_WORD_ALIGNED(x) ((uint32_t)0 == ((uint32_t)x & (uint32_t)3)) +#endif /* NDEBUG */ + +#define INVALID_INDEX (-1) + +#define DMA_TX_DISABLED 0u +#define DMA_TX_ENABLED 1u + +#define PHY_ADDRESS_MIN 0 +#define PHY_ADDRESS_MAX 31 + +#define TSE_MII 0x00 +#define TSE_GMII 0x01 +#define TSE_SGMII 0x02 +#define TSE_1000BASEX 0x03 +#define TSE_RGMII 0x04 +#define TSE_RMII 0x05 + +/******************************************************************************* + * MAC interrupt definitions + */ +#define TSE_TXPKTSENT_IRQ 0u +#define TSE_TXUNDRRUN_IRQ 1u +#define TSE_TXBUSERR_IRQ 3u +#define TSE_RXPKTRCVD_IRQ 4u +#define TSE_RXOVRFLOW_IRQ 6u +#define TSE_RXBUSERR_IRQ 7u +#define TSE_INVALID_IRQ 255u + +#define TSE_TXPKTSENT_IRQ_MASK 0x01u +#define TSE_RXPKTRCVD_IRQ_MASK ((uint32_t)0x01u << TSE_RXPKTRCVD_IRQ) + +#define MSGMII_PHY_STATUS 0x0F +#define MSGMII_1000BASEX_FD 0x8000 +#define MSGMII_1000BASEX_HD 0x4000 + +#define MSGMII_AUTO_NEGOTIATION_COMPLETE 0x0020u +/**************************************************************************/ +/* Private Functions declarations */ +/**************************************************************************/ +static void mac_reset(tse_instance_t *this_tse); +static void config_mac_hw(tse_instance_t *this_tse, const tse_cfg_t *cfg); +static void tx_desc_ring_init(tse_instance_t *this_tse); +static void rx_desc_ring_init(tse_instance_t *this_tse); +static void assign_station_addr(tse_instance_t *this_tse, const uint8_t mac_addr[6]); +static void rxpkt_handler(tse_instance_t *this_tse); +static void txpkt_handler(tse_instance_t *this_tse); +static void update_mac_cfg(tse_instance_t *this_tse, uint8_t phy_addr); +static uint8_t phy_probe(tse_instance_t *this_tse); + +#if ((TSE_PHY_INTERFACE == TSE_SGMII) || (TSE_PHY_INTERFACE == TSE_1000BASEX)) +static void msgmii_init(tse_instance_t *this_tse); +static void msgmii_autonegotiate(tse_instance_t *this_tse); +#endif + +#if (TSE_PHY_INTERFACE == TSE_1000BASEX) +static uint8_t msgmii_get_link_status(tse_instance_t *this_tse, + tse_speed_t *speed, + uint8_t *fullduplex); +#endif + +#if (TSE_PHY_INTERFACE == TSE_RGMII) +static void rgmii_set_link_speed(tse_instance_t *this_tse, tse_speed_t speed); +#endif + +/**************************************************************************/ +/* Public Functions */ +/**************************************************************************/ + +/**************************************************************************/ /** + * See core_tse.h for + * details of how to + * use this function. + */ +void +TSE_init(tse_instance_t *this_tse, uint32_t base_addr, tse_cfg_t *cfg) +{ + HAL_ASSERT(cfg != NULL_POINTER); + + HAL_set_32bit_reg_field(base_addr, CFG2_IF_MODE, 0x00u); + if (cfg != NULL_POINTER) + { + HAL_set_32bit_reg(base_addr, DMAINTRMASK, 0x00000000UL); + + this_tse->base_addr = base_addr; + mac_reset(this_tse); + + HAL_set_32bit_reg_field(base_addr, CFG2_IF_MODE, 0x00u); + + config_mac_hw(this_tse, cfg); + + /* Assign MAC station address */ + assign_station_addr(this_tse, cfg->mac_addr); + + /* Intialize Tx & Rx descriptor rings */ + tx_desc_ring_init(this_tse); + rx_desc_ring_init(this_tse); + + /* Initialize Tx descriptors related variables. */ + this_tse->first_tx_index = INVALID_INDEX; + this_tse->last_tx_index = INVALID_INDEX; + this_tse->next_tx_index = 0; + this_tse->nb_available_tx_desc = TSE_TX_RING_SIZE; + + /* Initialize Rx descriptors related variables. */ + this_tse->nb_available_rx_desc = TSE_RX_RING_SIZE; + this_tse->next_free_rx_desc_index = 0; + this_tse->first_rx_desc_index = INVALID_INDEX; + + /* initialize default interrupt handlers */ + this_tse->tx_complete_handler = NULL_POINTER; + this_tse->pckt_rx_callback = NULL_POINTER; + + /* Detect PHY */ + if (cfg->phy_addr == TSE_PHY_ADDRESS_AUTO_DETECT) + { + this_tse->phy_addr = phy_probe(this_tse); + HAL_ASSERT(this_tse->phy_addr <= PHY_ADDRESS_MAX); + } + else + { + if (cfg->phy_addr <= PHY_ADDRESS_MAX) + { + this_tse->phy_addr = cfg->phy_addr; + } + else + { + HAL_ASSERT(0); /*User provided a Invalid PHY address*/ + } + } +#if (TSE_PHY_INTERFACE != TSE_1000BASEX) + /* Initialize PHY interface */ + TSE_phy_init(this_tse, this_tse->phy_addr); +#endif + +#if ((TSE_PHY_INTERFACE == TSE_SGMII) || (TSE_PHY_INTERFACE == TSE_1000BASEX)) + msgmii_init(this_tse); +#endif + +#if (TSE_PHY_INTERFACE != TSE_1000BASEX) + TSE_phy_set_link_speed(this_tse, this_tse->phy_addr, cfg->speed_duplex_select); + + if (TSE_ENABLE == cfg->aneg_enable) + { + TSE_phy_autonegotiate(this_tse, this_tse->phy_addr); + } +#endif + +#if ((TSE_PHY_INTERFACE == TSE_SGMII) || (TSE_PHY_INTERFACE == TSE_1000BASEX)) + if (TSE_ENABLE == cfg->aneg_enable) + { + msgmii_autonegotiate(this_tse); + } +#endif + + update_mac_cfg(this_tse, this_tse->phy_addr); + + /*Enable Stats module*/ + HAL_set_32bit_reg_field(base_addr, IFC_STATS_EN, 0x01u); + + /*Need to write 1 and then 0 to correctly clear and start all counters*/ + HAL_set_32bit_reg_field(base_addr, IFC_STATS_CLR_ALL, 0x01u); + HAL_set_32bit_reg_field(base_addr, IFC_STATS_CLR_ALL, 0x00u); + + /* Enable default Flow Control at MAC level. */ + HAL_set_32bit_reg(base_addr, FPC, cfg->framefilter); + + /* Enable transmission at MAC level. */ + HAL_set_32bit_reg_field(base_addr, CFG1_TX_EN, 0x01u); + + /* Enable reception at MAC level. */ + HAL_set_32bit_reg_field(base_addr, CFG1_RX_EN, 0x01u); + } +} + +static void +update_mac_cfg(tse_instance_t *this_tse, uint8_t phy_addr) +{ + tse_speed_t speed; + uint8_t fullduplex; + uint8_t link_up; + +#if (TSE_PHY_INTERFACE == TSE_1000BASEX) + link_up = msgmii_get_link_status(this_tse, &speed, &fullduplex); +#else + link_up = TSE_phy_get_link_status(this_tse, phy_addr, &speed, &fullduplex); +#endif + + if (TSE_LINK_DOWN != link_up) + { +#if (TSE_PHY_INTERFACE == TSE_RGMII) + rgmii_set_link_speed(this_tse, speed); +#endif + + /* Set byte/nibble mode based on interface type and link speed. */ + HAL_set_32bit_reg_field(this_tse->base_addr, CFG2_IF_MODE, 0x00u); + + if (TSE_MAC1000MBPS == speed) + { + /* Set interface to byte mode. */ + HAL_set_32bit_reg_field(this_tse->base_addr, CFG2_IF_MODE, 0x02); + + /* RB */ + HAL_set_32bit_reg(this_tse->base_addr, MISCC, SPEED_1000M); + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG5_CFGBYTEMODE, 0x01); + } + else + { + /* Set interface to nibble mode. */ + HAL_set_32bit_reg_field(this_tse->base_addr, CFG2_IF_MODE, 0x00u); + + if (TSE_MAC100MBPS == speed) + { + HAL_set_32bit_reg(this_tse->base_addr, MISCC, SPEED_100M); + HAL_set_32bit_reg_field(this_tse->base_addr, CFG2_IF_MODE, 0x01); + } + else + { + HAL_set_32bit_reg(this_tse->base_addr, MISCC, SPEED_10M); + } + + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG5_CFGBYTEMODE, 0x00); + } + + /* Configure duplex mode */ + if (TSE_HALF_DUPLEX == fullduplex) + { + /* half duplex */ + HAL_set_32bit_reg_field(this_tse->base_addr, CFG2_FDX, 0x0); + } + else + { + /* full duplex */ + HAL_set_32bit_reg_field(this_tse->base_addr, CFG2_FDX, 0x01); + } + } +} + +/**************************************************************************/ /** + * See core_tse.h for + * details of how to + * use this function. + */ +uint8_t +TSE_get_link_status(tse_instance_t *this_tse, tse_speed_t *speed, uint8_t *fullduplex) +{ + static tse_speed_t previous_speed = TSE_INVALID_SPEED; + static uint8_t previous_duplex = 0xAA; + tse_speed_t link_speed; + uint8_t link_fullduplex; + uint8_t link_up; + uint16_t phy_reg; + +#if (TSE_PHY_INTERFACE == TSE_1000BASEX) + link_up = msgmii_get_link_status(this_tse, &link_speed, &link_fullduplex); +#else + link_up = TSE_phy_get_link_status(this_tse, this_tse->phy_addr, &link_speed, &link_fullduplex); +#endif + + if (link_up != TSE_LINK_DOWN) + { + if (link_speed != previous_speed) + { + /* Set byte/nibble mode based on interface type and link speed. */ + HAL_set_32bit_reg_field(this_tse->base_addr, CFG2_IF_MODE, 0x00u); + + if (TSE_MAC1000MBPS == link_speed) + { + /* Set interface to byte mode. */ + HAL_set_32bit_reg_field(this_tse->base_addr, CFG2_IF_MODE, 0x02); + + /* RB */ + HAL_set_32bit_reg(this_tse->base_addr, MISCC, SPEED_1000M); + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG5_CFGBYTEMODE, 0x01); + } + else + { + /* Set interface to nibble mode. */ + HAL_set_32bit_reg_field(this_tse->base_addr, CFG2_IF_MODE, 0x00u); + + if (TSE_MAC100MBPS == link_speed) + { + HAL_set_32bit_reg(this_tse->base_addr, MISCC, SPEED_100M); + HAL_set_32bit_reg_field(this_tse->base_addr, CFG2_IF_MODE, 0x01); + } + else + { + HAL_set_32bit_reg(this_tse->base_addr, MISCC, SPEED_10M); + } + + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG5_CFGBYTEMODE, 0x00); + } +#if (TSE_PHY_INTERFACE == TSE_RGMII) + rgmii_set_link_speed(this_tse, link_speed); +#endif + } + + previous_speed = link_speed; + + if (link_fullduplex != previous_duplex) + { + /* Configure duplex mode */ + if (TSE_HALF_DUPLEX == link_fullduplex) + { + /* half duplex */ + HAL_set_32bit_reg_field(this_tse->base_addr, CFG2_FDX, 0x0); + } + else + { + /* full duplex */ + HAL_set_32bit_reg_field(this_tse->base_addr, CFG2_FDX, 0x01); + } + } + + previous_duplex = link_fullduplex; + + /* Return current link speed and duplex mode.*/ + if (speed != 0u) + { + *speed = link_speed; + } + + if (fullduplex != 0u) + { + *fullduplex = link_fullduplex; + } + + /* Check if ANEG was originally enabled. */ + phy_reg = TSE_read_phy_reg(this_tse, TSE_MSGMII_ADDR, MII_BMCR); + + if ((phy_reg & BMCR_ANENABLE) && + ((TSE_PHY_INTERFACE == TSE_SGMII) || (TSE_PHY_INTERFACE == TSE_1000BASEX))) + { + uint16_t sgmii_link_up; + + /* Find out if link is up on SGMII link between MAC and external PHY. */ + phy_reg = TSE_read_phy_reg(this_tse, TSE_MSGMII_ADDR, MII_BMSR); + sgmii_link_up = phy_reg & BMSR_LSTATUS; + + if (TSE_LINK_DOWN == sgmii_link_up) + { + /* Initiate auto-negotiation on the SGMII link. */ + phy_reg |= BMCR_ANRESTART; + TSE_write_phy_reg(this_tse, TSE_MSGMII_ADDR, MII_BMCR, phy_reg); + } + } + } + + return link_up; +} + +/**************************************************************************/ /** + * See core_tse.h for + * details of how to + * use this function. + */ +void +TSE_cfg_struct_def_init(tse_cfg_t *cfg) +{ + const uint8_t g_default_mac_address[6] = {0xC0u, 0xB1u, 0x3Cu, 0x88u, 0x88u, 0x88u}; + + HAL_ASSERT(NULL_POINTER != cfg); + + if (NULL_POINTER != cfg) + { + cfg->framefilter = TSE_FC_DEFAULT_MASK; + cfg->aneg_enable = TSE_ENABLE; + cfg->speed_duplex_select = TSE_ANEG_ALL_SPEEDS; + cfg->phy_addr = 0u; + cfg->tx_edc_enable = TSE_ERR_DET_CORR_ENABLE; + cfg->rx_edc_enable = TSE_ERR_DET_CORR_ENABLE; + cfg->preamble_length = TSE_PREAMLEN_DEFVAL; + cfg->hugeframe_enable = TSE_HUGE_FRAME_DISABLE; + cfg->length_field_check = TSE_LENGTH_FIELD_CHECK_ENABLE; + cfg->pad_n_CRC = TSE_PAD_N_CRC_ENABLE; + cfg->append_CRC = TSE_CRC_ENABLE; + cfg->fullduplex = TSE_FULLDUPLEX_ENABLE; + cfg->loopback = TSE_LOOPBACK_DISABLE; + cfg->rx_flow_ctrl = TSE_RX_FLOW_CTRL_DISABLE; + cfg->tx_flow_ctrl = TSE_TX_FLOW_CTRL_DISABLE; + cfg->min_IFG = TSE_MINIFG_DEFVAL; + cfg->btb_IFG = TSE_BTBIFG_DEFVAL; + cfg->max_retx_tries = TSE_MAXRETX_DEFVAL; + cfg->excessive_defer = TSE_EXSS_DEFER_DISABLE; + cfg->nobackoff = TSE_NO_BACKOFF_DISABLE; + cfg->backpres_nobackoff = TSE_BACKPRESS_NO_BACKOFF_DISABLE; + cfg->ABEB_enable = TSE_ABEB_DISABLE; + cfg->ABEB_truncvalue = TSE_ABEBTRUNC_DEFVAL; + cfg->phyclk = TSE_DEF_PHY_CLK; + cfg->supress_preamble = TSE_SUPPRESS_PREAMBLE_DISABLE; + cfg->autoscan_phys = TSE_PHY_AUTOSCAN_DISABLE; + cfg->max_frame_length = TSE_MAXFRAMELEN_DEFVAL; + cfg->non_btb_IFG = TSE_NONBTBIFG_DEFVAL; + cfg->slottime = TSE_SLOTTIME_DEFVAL; + cfg->framedrop_mask = TSE_DEFVAL_FRAMEDROP_MASK; + cfg->wol_enable = TSE_WOL_DETECT_DISABLE; + + cfg->mac_addr[0] = g_default_mac_address[0]; + cfg->mac_addr[1] = g_default_mac_address[1]; + cfg->mac_addr[2] = g_default_mac_address[2]; + cfg->mac_addr[3] = g_default_mac_address[3]; + cfg->mac_addr[4] = g_default_mac_address[4]; + cfg->mac_addr[5] = g_default_mac_address[5]; + } +} + +/**************************************************************************/ /** + * See core_tse.h for + * details of how to + * use this function. + */ +static void +mac_reset(tse_instance_t *this_tse) +{ + /* Reset MIIMGMT */ + HAL_set_32bit_reg_field(this_tse->base_addr, MIIMGMT_RESET_MII_MGMT, 0x01u); + + /* Reset FIFO watermark module */ + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG0_WMM_RST, 0x01u); + + /* Reset FIFO Rx system module */ + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG0_RSYS_RST, 0x01u); + + /* Reset FIFO Rx fab module */ + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG0_RFAB_RST, 0x01u); + + /* Reset FIFO Tx system module */ + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG0_TSYS_RST, 0x01u); + + /* Reset FIFO Tx system module */ + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG0_TFAB_RST, 0x01u); +} + +static void +config_mac_hw(tse_instance_t *this_tse, const tse_cfg_t *cfg) +{ + uint32_t tempreg; + + /* Check for validity of configuration parameters */ + HAL_ASSERT(IS_STATE(cfg->tx_edc_enable)); + HAL_ASSERT(IS_STATE(cfg->rx_edc_enable)); + HAL_ASSERT(TSE_PREAMLEN_MAXVAL >= cfg->preamble_length); + HAL_ASSERT(IS_STATE(cfg->hugeframe_enable)); + HAL_ASSERT(IS_STATE(cfg->length_field_check)); + HAL_ASSERT(IS_STATE(cfg->pad_n_CRC)); + HAL_ASSERT(IS_STATE(cfg->append_CRC)); + HAL_ASSERT(IS_STATE(cfg->loopback)); + HAL_ASSERT(IS_STATE(cfg->rx_flow_ctrl)); + HAL_ASSERT(IS_STATE(cfg->tx_flow_ctrl)); + + HAL_ASSERT(TSE_BTBIFG_MAXVAL >= cfg->btb_IFG); + HAL_ASSERT(TSE_MAXRETX_MAXVAL >= cfg->max_retx_tries); + HAL_ASSERT(IS_STATE(cfg->excessive_defer)); + HAL_ASSERT(IS_STATE(cfg->nobackoff)); + HAL_ASSERT(IS_STATE(cfg->backpres_nobackoff)); + HAL_ASSERT(IS_STATE(cfg->ABEB_enable)); + HAL_ASSERT(TSE_ABEBTRUNC_MAXVAL >= cfg->ABEB_truncvalue); + HAL_ASSERT(TSE_BY28_PHY_CLK >= cfg->phyclk); + HAL_ASSERT(IS_STATE(cfg->supress_preamble)); + HAL_ASSERT(IS_STATE(cfg->autoscan_phys)); + HAL_ASSERT(TSE_MAXFRAMELEN_MAXVAL >= cfg->max_frame_length); + HAL_ASSERT(TSE_SLOTTIME_MAXVAL >= cfg->slottime); + + /* Configure PHY related MII MGMT registers */ + tempreg = (uint32_t)cfg->phyclk & MII_CLOCK_SELECT_MASK; + + if (TSE_ENABLE == cfg->supress_preamble) + { + tempreg |= MII_PREAM_SUPRESS_MASK; + } + + if (TSE_ENABLE == cfg->autoscan_phys) + { + tempreg |= MII_SCAN_AUTO_INC_MASK; + } + + HAL_set_32bit_reg(this_tse->base_addr, MIIMGMT, tempreg); + + /* Clear all reset bits */ + + /* Clear MIIMGMT */ + HAL_set_32bit_reg_field(this_tse->base_addr, MIIMGMT_RESET_MII_MGMT, 0x00u); + + /* Clear FIFO resets. */ + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG0_WMM_RST, 0x00u); + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG0_RSYS_RST, 0x00u); + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG0_RFAB_RST, 0x00u); + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG0_TSYS_RST, 0x00u); + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG0_TFAB_RST, 0x00u); + + /* configure loppback and flow control enables. */ + if (TSE_ENABLE == cfg->loopback) + { + tempreg = CFG1_LOOPBACK_MASK; + } + else + { + tempreg = 0u; + } + + if (TSE_ENABLE == cfg->rx_flow_ctrl) + { + tempreg |= CFG1_RX_FCTL_MASK; + } + + if (TSE_ENABLE == cfg->tx_flow_ctrl) + { + tempreg |= CFG1_TX_FCTL_MASK; + } + + HAL_set_32bit_reg(this_tse->base_addr, CFG1, tempreg); + + if (TSE_ENABLE == cfg->fullduplex) + { + tempreg = CFG2_FDX_MASK; + } + else + { + tempreg = 0u; + } + + if (TSE_ENABLE == cfg->append_CRC) + { + tempreg |= CFG2_CRC_EN_MASK; + } + + if (TSE_ENABLE == cfg->pad_n_CRC) + { + tempreg |= CFG2_PAD_CRC_EN_MASK; + } + + if (TSE_ENABLE == cfg->length_field_check) + { + tempreg |= CFG2_LEN_CHECK_MASK; + } + + if (TSE_ENABLE == cfg->hugeframe_enable) + { + tempreg |= CFG2_HUGE_FRAME_EN_MASK; + } + + if ((TSE_PHY_INTERFACE == TSE_MII) || (TSE_PHY_INTERFACE == TSE_RMII)) + { + /* MII and RMII use nibble mode interface. */ + tempreg |= CFG2_NIBBLE_MASK; + } + else + { + /* TBI and GMII use byte interface. */ + tempreg |= CFG2_BYTE_MASK; + } + + tempreg |= (((uint32_t)cfg->preamble_length) << CFG2_PREAM_LEN); + HAL_set_32bit_reg(this_tse->base_addr, CFG2, tempreg); + + tempreg = cfg->btb_IFG; + tempreg |= ((uint32_t)cfg->min_IFG << IFG_MINIFGENF); + tempreg |= ((uint32_t)cfg->non_btb_IFG << IFG_NONBTBIPG); + + HAL_set_32bit_reg(this_tse->base_addr, IFG, tempreg); + + tempreg = (uint32_t)cfg->slottime & HALF_DUPLEX_SLOTTIME_MASK; + + tempreg |= (uint32_t)cfg->max_retx_tries << HALF_DUPLEX_RETX_MAX_OFFSET; + + if (TSE_ENABLE == cfg->excessive_defer) + { + tempreg |= HALF_DUPLEX_EXCS_DEFER_MASK; + } + + if (TSE_ENABLE == cfg->nobackoff) + { + tempreg |= HALF_DUPLEX_NO_BACKOFF_MASK; + } + + if (TSE_ENABLE == cfg->backpres_nobackoff) + { + tempreg |= HALF_DUPLEX_BACKPRES_NOBACKOFF_MASK; + } + + if (TSE_ENABLE == cfg->ABEB_enable) + { + tempreg |= HALF_DUPLEX_ABEB_ENABLE_MASK; + } + + tempreg |= (uint32_t)cfg->ABEB_truncvalue << HALF_DUPLEX_ABEB_TUNC_OFFSET; + + HAL_set_32bit_reg(this_tse->base_addr, HDX, tempreg); + HAL_set_32bit_reg(this_tse->base_addr, FREMLEN, (uint32_t)cfg->max_frame_length); + + /*Enable WoL*/ + if (TSE_WOL_DETECT_DISABLE != cfg->wol_enable) + { + if (TSE_WOL_UNICAST_FRAME_DETECT_EN & cfg->wol_enable) + { + HAL_set_32bit_reg_field(this_tse->base_addr, IFC_MCXWOL_UMATCH_EN, 0x01u); + } + + if (TSE_WOL_MAGIC_FRAME_DETECT_EN & cfg->wol_enable) + { + HAL_set_32bit_reg_field(this_tse->base_addr, IFC_MCXWOL_MAGIC_EN, 0x01u); + } + } + + /*-------------------------------------------------------------------------- + * Configure FIFOs + */ + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG0_WMM_EN, 0x01); + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG0_RSYS_EN, 0x01); + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG0_RFAB_EN, 0x01); + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG0_TSYS_EN, 0x01); + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG0_TFAB_EN, 0x01); + + /* RX FIFO size : 8KB */ + HAL_set_32bit_reg(this_tse->base_addr, FIFOCFG1, FIFO_CFG1_DEFVAL); + + /* Rx FIFO watermark */ + HAL_set_32bit_reg(this_tse->base_addr, FIFOCFG2, FIFO_CFG2_DEFVAL); + + /* Tx FIFO watermark: 4KB Tx FIFO */ + HAL_set_32bit_reg(this_tse->base_addr, FIFOCFG3, FIFO_CFG3_DEFVAL); + + tempreg = HAL_get_32bit_reg_field(this_tse->base_addr, FIFOCFG5_HSTFLTRFRMDC); + + tempreg |= 0x0003FFFF; + + tempreg &= ~(cfg->framedrop_mask); + + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG5_HSTFLTRFRMDC, tempreg); + + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG4_HSTFLTRFRM, cfg->framedrop_mask); + + if (cfg->framedrop_mask & TSE_SHORT_FRAME_FRAMEDROP_MASK) + { + HAL_set_32bit_reg_field(this_tse->base_addr, FIFOCFG5_HSTDRPLT64, 0x01); + } +} + +/**************************************************************************/ /** + * See core_tse.h for + * details of how to + * use this function. + */ +void +TSE_write_phy_reg(tse_instance_t *this_tse, uint8_t phyaddr, uint8_t regaddr, uint16_t regval) +{ + HAL_ASSERT(TSE_PHYADDR_MAXVAL >= phyaddr); + HAL_ASSERT(TSE_PHYREGADDR_MAXVAL >= regaddr); + + /* + Write PHY address in MII Mgmt address register. Makes previous register + address 0 & invalid. + */ + if ((TSE_PHYADDR_MAXVAL >= phyaddr) && (TSE_PHYREGADDR_MAXVAL >= regaddr)) + { + /* Wait for MII Mgmt interface to complete previous operation. */ + do + { + ; + } while (HAL_get_32bit_reg_field(this_tse->base_addr, MIIMIND_BUSY)); + + /* Load PHY address in MII Mgmt address register */ + HAL_set_32bit_reg_field(this_tse->base_addr, MIIMADDR_PHY_ADDR, phyaddr); + + /* Load register address in MII Mgmt address register */ + HAL_set_32bit_reg_field(this_tse->base_addr, MIIMADDR_REG_ADDR, regaddr); + + /* Load value to be written in MII Mgmt control register */ + HAL_set_32bit_reg_field(this_tse->base_addr, MIIMCTRL_PHY_CTRL, regval); + } +} + +/**************************************************************************/ /** + * See core_tse.h for + * details of how to + * use this function. + */ +uint16_t +TSE_read_phy_reg(tse_instance_t *this_tse, uint8_t phyaddr, uint8_t regaddr) +{ + uint32_t timeout = 100000u; + uint16_t read_val = 0; + + HAL_ASSERT(TSE_PHYADDR_MAXVAL >= phyaddr); + HAL_ASSERT(TSE_PHYREGADDR_MAXVAL >= regaddr); + + /* + Write PHY address in MII Mgmt address register. Makes previous register + address 0 & invalid. + */ + if ((TSE_PHYADDR_MAXVAL >= phyaddr) && (TSE_PHYREGADDR_MAXVAL >= regaddr)) + { + volatile uint32_t mii_not_valid; + volatile uint32_t mii_busy; + + /* Wait for MII Mgmt interface to complete previous operation. */ + do + { + ; + } while (HAL_get_32bit_reg_field(this_tse->base_addr, MIIMIND_BUSY)); + + HAL_set_32bit_reg_field(this_tse->base_addr, MIIMADDR_PHY_ADDR, phyaddr); + + /* Load PHY register address in MII Mgmt address register */ + HAL_set_32bit_reg_field(this_tse->base_addr, MIIMADDR_REG_ADDR, regaddr); + + /* Issue read command to PHY */ + HAL_set_32bit_reg_field(this_tse->base_addr, MIIMCMD_READ_CYCLE, 0x01); + + /* Poll till PHY read cycle is completed or timeout expired */ + do + { + --timeout; + mii_not_valid = HAL_get_32bit_reg_field(this_tse->base_addr, MIIMIND_NV); + mii_busy = HAL_get_32bit_reg_field(this_tse->base_addr, MIIMIND_BUSY); + } while ((timeout > 0u) && ((mii_busy != 0u) || (mii_not_valid != 0u))); + + /* + Read value from MII Mgmt status register only if timeout is not expired + but the read the read cycle is completed + */ + HAL_ASSERT(timeout > 0u); + + if (timeout != 0u) + { + read_val = HAL_get_32bit_reg_field(this_tse->base_addr, MIIMSTATUS_PHY_STATUS); + } + } + + HAL_set_32bit_reg_field(this_tse->base_addr, MIIMCMD_READ_CYCLE, 0x00); + + return read_val; +} + +/**************************************************************************/ /** + * See core_tse.h for + * details of how to + * use this function. + */ +uint32_t +TSE_read_stat(tse_instance_t *this_tse, tse_stat_t stat) +{ + uint32_t stat_val = 0u; + uint32_t cnt = 0; + + if (TSE_MAC_LAST_STAT < stat) + { + cnt = this_tse->base_addr + TR64_REG_OFFSET + cnt; + } + + switch (stat) + { + case TSE_FRAME_CNT_64: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TR64); + break; + case TSE_FRAME_CNT_127: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TR127); + break; + case TSE_FRAME_CNT_255: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TR255); + break; + case TSE_FRAME_CNT_511: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TR511); + break; + case TSE_FRAME_CNT_1K: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TR1K); + break; + case TSE_FRAME_CNT_MAX: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TRMAX); + break; + case TSE_FRAME_CNT_VLAN: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TRMGV); + break; + case TSE_RX_BYTE_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, RBYT); + break; + case TSE_RX_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, RPKT); + break; + case TSE_RX_FCS_ERR_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, RFCS); + break; + case TSE_RX_MULTICAST_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, RMCA); + break; + case TSE_RX_BROADCAST_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, RBCA); + break; + case TSE_RX_CTRL_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, RXCF); + break; + case TSE_RX_PAUSE_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, RXPF); + break; + case TSE_RX_UNKNOWN_OPCODE_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, RXUO); + break; + case TSE_RX_ALIGN_ERR_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, RALN); + break; + case TSE_RX_FRAMELENGTH_ERR_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, RFLR); + break; + case TSE_RX_CODE_ERR_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, RCDE); + break; + case TSE_RX_CS_ERR_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, RCSE); + break; + case TSE_RX_UNDERSIZE_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, RUND); + break; + case TSE_RX_OVERSIZE_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, ROVR); + break; + case TSE_RX_FRAGMENT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, RFRG); + break; + case TSE_RX_JABBER_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, RJBR); + break; + case TSE_RX_DROP_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, RDRP); + break; + case TSE_TX_BYTE_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TBYT); + break; + case TSE_TX_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TPKT); + break; + case TSE_TX_MULTICAST_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TMCA); + break; + case TSE_TX_BROADCAST_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TBCA); + break; + case TSE_TX_PAUSE_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TXPF); + break; + case TSE_TX_DEFFERAL_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TDFR); + break; + case TSE_TX_EXCS_DEFFERAL_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TEDF); + break; + case TSE_TX_SINGLE_COLL_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TSCL); + break; + case TSE_TX_MULTI_COLL_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TMCL); + break; + case TSE_TX_LATE_COLL_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TLCL); + break; + case TSE_TX_EXCSS_COLL_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TXCL); + break; + case TSE_TX_TOTAL_COLL_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TNCL); + break; + case TSE_TX_PAUSE_HONORED_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TPFH); /*This is not used*/ + break; + case TSE_TX_DROP_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TDRP); + break; + case TSE_TX_JABBER_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TJBR); + break; + case TSE_TX_FCS_ERR_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TFCS); + break; + case TSE_TX_CNTRL_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TXCF); + break; + case TSE_TX_OVERSIZE_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TOVR); + break; + case TSE_TX_UNDERSIZE_PKT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TUND); + break; + case TSE_TX_FRAGMENT_CNT: + stat_val = HAL_get_32bit_reg(this_tse->base_addr, TFRG); + break; + default: + HAL_ASSERT(TSE_MAC_LAST_STAT > stat); + } + + return stat_val; +} + +/***************************************************************************/ /** + See core_tse.h for details of how to use this function + */ +void +TSE_clear_statistics(tse_instance_t *instance) +{ + HAL_set_32bit_reg_field(instance->base_addr, IFC_STATS_CLR_ALL, 0x01u); + HAL_set_32bit_reg_field(instance->base_addr, IFC_STATS_CLR_ALL, 0x00u); +} + +/**************************************************************************/ /** + * See core_tse.h for + * details of how to + * use this function. + */ +uint8_t +TSE_receive_pkt(tse_instance_t *this_tse, uint8_t *rx_pkt_buffer, void *p_user_data) +{ + uint8_t status = TSE_FAILED; + + /* Make this function atomic w.r.to RxPktRcvd interrupt */ + HAL_set_32bit_reg_field(this_tse->base_addr, DMAINTRMASK_RXPKT_RCVD, 0x0); + + HAL_ASSERT(NULL_POINTER != rx_pkt_buffer); + HAL_ASSERT(IS_WORD_ALIGNED(rx_pkt_buffer)); + + if (this_tse->nb_available_rx_desc > 0) + { + int16_t next_rx_desc_index; + + --this_tse->nb_available_rx_desc; + next_rx_desc_index = this_tse->next_free_rx_desc_index; + + this_tse->rx_desc_tab[next_rx_desc_index].pkt_start_addr = (uint32_t)rx_pkt_buffer; + + this_tse->rx_desc_tab[next_rx_desc_index].caller_info = p_user_data; + + /* Set own flag of this descriptor to indicate ready for RX */ + this_tse->rx_desc_tab[next_rx_desc_index].pkt_size |= DMA_DESC_EMPTY_FLAG_MASK; + + /* + If the RX is found disabled, then it might be because this is the + first time a packet is scheduled for reception or the RX ENABLE is + made zero by RX overflow or RX bus error. In either case, this + function tries to schedule the current packet for reception. + */ + if (0 == HAL_get_32bit_reg_field(this_tse->base_addr, DMARXCTRL_RX_EN)) + { + /* Make current descriptor to point to the packet requested */ + this_tse->first_rx_desc_index = next_rx_desc_index; + + HAL_set_32bit_reg(this_tse->base_addr, + DMARXDESC, + (uint32_t)&this_tse->rx_desc_tab[next_rx_desc_index]); + + HAL_set_32bit_reg_field(this_tse->base_addr, DMARXSTATUS_RXPKT_OVR, 0x01); + HAL_set_32bit_reg_field(this_tse->base_addr, DMARXCTRL_RX_EN, 0x01); + } + + /* + Point the next_rx_desc to next free descriptor in the ring. Wrap around + in case next descriptor is pointing to last in the ring + */ + ++this_tse->next_free_rx_desc_index; + + if (this_tse->next_free_rx_desc_index >= TSE_RX_RING_SIZE) + { + this_tse->next_free_rx_desc_index = 0; + } + + /* + Packet scheduled for reception successfully only when there is no new + RX bus error + */ + if (0u == HAL_get_32bit_reg_field(this_tse->base_addr, DMARXSTATUS_BUSERR)) + { + status = TSE_SUCCESS; + } + } + + /* RxPktRcvd Interrupt Enable */ + HAL_set_32bit_reg_field(this_tse->base_addr, DMAINTRMASK_RXPKT_RCVD, 0x1); + + return status; +} + +/**************************************************************************/ /** + * See core_tse.h for + * details of how to + * use this function. + */ +uint8_t +TSE_send_pkt(tse_instance_t *this_tse, + uint8_t const *tx_buffer, + uint32_t tx_length, + void *p_user_data) +{ + uint8_t status = TSE_FAILED; + + /* Make this function atomic w.r.to TxPktSent interrupt */ + HAL_set_32bit_reg_field(this_tse->base_addr, DMAINTRMASK_TXPKT_SENT, 0x0); + HAL_ASSERT(NULL_POINTER != tx_buffer); + HAL_ASSERT(0 != tx_length); + HAL_ASSERT(IS_WORD_ALIGNED(tx_buffer)); + + if (this_tse->nb_available_tx_desc > 0) + { + tse_desc_t *p_next_tx_desc; + + --this_tse->nb_available_tx_desc; + + if (INVALID_INDEX == this_tse->first_tx_index) + { + this_tse->first_tx_index = this_tse->next_tx_index; + } + + this_tse->last_tx_index = this_tse->next_tx_index; + + p_next_tx_desc = &this_tse->tx_desc_tab[this_tse->next_tx_index]; + p_next_tx_desc->pkt_start_addr = (uint32_t)tx_buffer; + p_next_tx_desc->caller_info = p_user_data; + + /* Set the packet length, packet overrides and clear descriptor empty: */ + p_next_tx_desc->pkt_size = tx_length; + + /* + If TX is found disabled, this might be because this is the first + time packet is being sent or the DMA completed previous transfer and + stopped transmission by itself caused by TX underrun or bus error. + This function neglects the errors and tries to send the current packet. + */ + if (0 == HAL_get_32bit_reg_field(this_tse->base_addr, DMATXCTRL_TX_EN)) + { + HAL_set_32bit_reg(this_tse->base_addr, DMATXDESC, (uint32_t)p_next_tx_desc); + } + + /* + Enable DMA transmit anyway to cover the case where Tx completed after + the read of DMA_TX_CTRL. + */ + HAL_set_32bit_reg_field(this_tse->base_addr, DMATXCTRL_TX_EN, 0x01); + + /* + Point the next_tx_desc to next free descriptor in the ring. Wrap around + in case next descriptor is pointing to last in the ring + */ + if ((TSE_TX_RING_SIZE - 1) == this_tse->next_tx_index) + { + this_tse->next_tx_index = 0; + } + else + { + ++this_tse->next_tx_index; + } + + /* + Packet scheduled for transmission successfully only when there is no + TX bus error + */ + if (0 == HAL_get_32bit_reg_field(this_tse->base_addr, DMARXSTATUS_BUSERR)) + { + status = TSE_SUCCESS; + } + } + + /* TxPktSent Interrupt Enable. */ + HAL_set_32bit_reg_field(this_tse->base_addr, DMAINTRMASK_TXPKT_SENT, 0x1); + + return status; +} + +/**************************************************************************/ /** + * See core_tse.h for + * details of how to + * use this function. + */ +void +TSE_isr(tse_instance_t *this_tse) +{ + uint32_t dma_irq; + uint32_t packet_sent; + uint32_t packet_received; + + dma_irq = HAL_get_32bit_reg(this_tse->base_addr, DMAINTR); + + /* Transmit packet sent interrupt */ + packet_sent = dma_irq & TSE_TXPKTSENT_IRQ_MASK; + + if (packet_sent != 0u) + { + txpkt_handler(this_tse); + } + + /* Packet received interrupt */ + packet_received = dma_irq & TSE_RXPKTRCVD_IRQ_MASK; + + if (packet_received != 0u) + { + rxpkt_handler(this_tse); + } + + if (dma_irq & (1u << TSE_TXUNDRRUN_IRQ)) + { + /* Clear the tx packet sent interrupt. */ + HAL_set_32bit_reg_field(this_tse->base_addr, DMATXSTATUS_TXPKT_UR, 0x1); + } + + if (dma_irq & (1u << TSE_TXBUSERR_IRQ)) + { + /* Clear the tx packet sent interrupt. */ + HAL_set_32bit_reg_field(this_tse->base_addr, DMATXSTATUS_BUSERR, 0x1); + } + + if (dma_irq & (1u << TSE_RXOVRFLOW_IRQ)) + { + HAL_set_32bit_reg_field(this_tse->base_addr, DMARXSTATUS_RXPKT_OVR, 0x1); + HAL_set_32bit_reg_field(this_tse->base_addr, DMARXCTRL_RX_EN, 0x1); + } + + if (dma_irq & (1u << TSE_RXBUSERR_IRQ)) + { + /* + Clear the rx packet received interrupt once. If this bit still persists, + then another rx packet received interrupt will be generated. Rx count + will be decremented. + */ + HAL_set_32bit_reg_field(this_tse->base_addr, DMARXSTATUS_BUSERR, 0x1); + } + + if (HAL_get_32bit_reg_field(this_tse->base_addr, IFS_WOL_DET)) + { + /*call the application callback handler*/ + if (NULL_POINTER != this_tse->wol_callback) + { + this_tse->wol_callback(); + } + + /* Clear the WoL interrupt */ + HAL_set_32bit_reg_field(this_tse->base_addr, IFC_MCXWOL_ST_CLR, 0x01); + HAL_set_32bit_reg_field(this_tse->base_addr, IFC_MCXWOL_ST_CLR, 0x00); + } +} + +/**************************************************************************/ /** + * See core_tse.h for + * details of how to + * use this function. + */ +void +TSE_set_tx_callback(tse_instance_t *this_tse, tse_transmit_callback_t tx_complete_handler) +{ + this_tse->tx_complete_handler = tx_complete_handler; +} + +/**************************************************************************/ /** + * See core_tse.h for + * details of how to + * use this function. + */ +void +TSE_set_rx_callback(tse_instance_t *this_tse, tse_receive_callback_t rx_callback) +{ + this_tse->pckt_rx_callback = rx_callback; +} + +/**************************************************************************/ /** + * See core_tse.h for + * details of how to + * use this function. + */ +void +TSE_set_wol_callback(tse_instance_t *this_tse, tse_wol_callback_t wol_callback) +{ + this_tse->wol_callback = wol_callback; +} +/***************************************************************************/ /** + See core_tse.h for details of how to use this function + */ +void +TSE_set_address_filter(tse_instance_t *this_tse, + const uint8_t *mac_addresses, + uint32_t nb_addresses) +{ + uint32_t reg_value; + uint8_t hash_mulicast_set = TSE_DISABLE; + uint8_t hash_unicast_set = TSE_DISABLE; + uint8_t broadcast_set = TSE_DISABLE; + uint8_t unicast_set = TSE_DISABLE; + uint16_t count; + uint32_t hash_table_index = TSE_DISABLE; + uint32_t mac_hash_table[4] = {TSE_DISABLE}; + + HAL_ASSERT(mac_addresses != NULL_POINTER); + + HAL_ASSERT((nb_addresses != 0)); + + for (count = 0; count < (nb_addresses * MAC_ADDRESS_LENGTH); count += MAC_ADDRESS_LENGTH) + { + /* Checking whether the MAC address is Broadcast or not */ + if ((BROADCAST_COMPARE_BITS == mac_addresses[count]) && + (BROADCAST_COMPARE_BITS == mac_addresses[count + 1]) && + (BROADCAST_COMPARE_BITS == mac_addresses[count + 2]) && + (BROADCAST_COMPARE_BITS == mac_addresses[count + 3]) && + (BROADCAST_COMPARE_BITS == mac_addresses[count + 4]) && + (BROADCAST_COMPARE_BITS == mac_addresses[count + 5])) + { + if (broadcast_set == TSE_DISABLE) + { + broadcast_set = TSE_ENABLE; + } + } + + /*Checking whether the MAC address is local base station address or not*/ + else if ((mac_addresses[count] == + (uint8_t)(HAL_get_32bit_reg(this_tse->base_addr, STADDRH) >> 16)) && + (mac_addresses[count + 1] == + (uint8_t)(HAL_get_32bit_reg(this_tse->base_addr, STADDRH) >> 24)) && + (mac_addresses[count + 2] == + (uint8_t)(HAL_get_32bit_reg(this_tse->base_addr, STADDRL))) && + (mac_addresses[count + 3] == + (uint8_t)(HAL_get_32bit_reg(this_tse->base_addr, STADDRL) >> 8)) && + (mac_addresses[count + 4] == + (uint8_t)(HAL_get_32bit_reg(this_tse->base_addr, STADDRL) >> 16)) && + (mac_addresses[count + 5] == + (uint8_t)(HAL_get_32bit_reg(this_tse->base_addr, STADDRL) >> 24))) + { + if (unicast_set == TSE_DISABLE) + { + unicast_set = TSE_ENABLE; + } + } + else + { + if ((hash_mulicast_set == TSE_DISABLE) || (hash_unicast_set == TSE_DISABLE)) + { + /* Checking whether the MAC address is Multicast or not*/ + if (((mac_addresses[count] & MULTICAST_ADDRESS_MASK) == MULTICAST_ADDRESS) && + (hash_mulicast_set == TSE_DISABLE)) + { + hash_mulicast_set = TSE_ENABLE; + } + + /* Checking whether the MAC address is Unicast or not */ + if ((((mac_addresses[count] & MULTICAST_ADDRESS_MASK) == UNICAST_ADDRESS) && + (hash_unicast_set == TSE_DISABLE))) + { + hash_unicast_set = TSE_ENABLE; + } + } + + /* CRC calculation for MAC DA */ + hash_table_index = TSE_ethernet_crc(&mac_addresses[count], MAC_ADDRESS_LENGTH); + + /* Using upper 7-bits of the CRC */ + hash_table_index = hash_table_index >> 25u; + mac_hash_table[hash_table_index / 0x20u] |= 1u << (hash_table_index & 0x1Fu); + } + } + + reg_value = + (uint32_t)((hash_unicast_set << FPC_HASH_UNICAST_SHIFT) | + (hash_mulicast_set << FPC_HASH_MULTICAST_SHIFT) | + (unicast_set << FPC_UNICAST_SHIFT) | (broadcast_set << FPC_BROADCAST_SHIFT)); + + /* Enable Hash-multicast, Hash-unicast, unicast and broadcast frames */ + HAL_set_32bit_reg(this_tse->base_addr, FPC, reg_value); + + /* Set the Hash table for Hash uni/multicast frames */ + HAL_set_32bit_reg(this_tse->base_addr, HASHTAB0, mac_hash_table[0]); + HAL_set_32bit_reg(this_tse->base_addr, HASHTAB1, mac_hash_table[1]); + HAL_set_32bit_reg(this_tse->base_addr, HASHTAB2, mac_hash_table[2]); + HAL_set_32bit_reg(this_tse->base_addr, HASHTAB3, mac_hash_table[3]); +} + +/**************************************************************************/ +/* Private Function definitions */ +/**************************************************************************/ + +/**************************************************************************/ /** + * This is default + * "Receive packet + * interrupt handler. + * This function finds + * the descriptor that + * received the packet + * and caused the + * interrupt. This + * informs the received + * packet size to the + * application and + * relinquishes the + * packet buffer from + * the associated DMA + * descriptor. + */ +static void +rxpkt_handler(tse_instance_t *this_tse) +{ + uint32_t rxcnt = 0u; + + /* + Execution comes here because at-least one packet is received. + Hence rxcnt can be read after freeing one descriptor + */ + do + { + tse_desc_t *cdesc = &this_tse->rx_desc_tab[this_tse->first_rx_desc_index]; + + /* + Point the curr_rx_desc to next descriptor in the ring. + Wrap around in case next descriptor is pointing to last in the ring + */ + ++this_tse->first_rx_desc_index; + + if (this_tse->first_rx_desc_index >= TSE_RX_RING_SIZE) + { + this_tse->first_rx_desc_index = 0; + } + + /* + Clear the rx packet received interrupt once. If this bit still persists, + then another rx packet received interrupt will be generated. Rx count + will be decremented. + */ + HAL_set_32bit_reg_field(this_tse->base_addr, DMARXSTATUS_RXPKT_RCVD, 0x1); + + /* Get the latest current received count */ + rxcnt = HAL_get_32bit_reg_field(this_tse->base_addr, DMARXSTATUS_RXPKT_CNT); + + ++this_tse->nb_available_rx_desc; + + /* Pass received packet up to application layer. */ + if (NULL_POINTER != this_tse->pckt_rx_callback) + { + uint8_t *p_rx_packet; + uint32_t pckt_length; + + p_rx_packet = (uint8_t *)cdesc->pkt_start_addr; + pckt_length = (cdesc->pkt_size & DMA_DESC_PKT_SIZE_MASK) - 4u; + + if (NULL_POINTER != this_tse->pckt_rx_callback) + { + this_tse->pckt_rx_callback(p_rx_packet, pckt_length, cdesc->caller_info); + } + } + + } while (0u != rxcnt); +} + +/**************************************************************************/ /** + * This is default + * "Transmit packet + * interrupt handler. + * This function finds + * the descriptor that + * transmitted the + * packet and caused + * the interrupt. This + * relinquishes the + * packet buffer from + * the associated DMA + * descriptor. + */ +static void +txpkt_handler(tse_instance_t *this_tse) +{ + uint32_t empty_flag; + int16_t index; + uint32_t completed = 0u; + + HAL_ASSERT(this_tse->first_tx_index != INVALID_INDEX); + HAL_ASSERT(this_tse->last_tx_index != INVALID_INDEX); + + index = this_tse->first_tx_index; + do + { + ++this_tse->nb_available_tx_desc; + + /* Call packet Tx completion handler if it exists. */ + if (NULL_POINTER != this_tse->tx_complete_handler) + { + this_tse->tx_complete_handler(this_tse->tx_desc_tab[index].caller_info); + } + + if (index == this_tse->last_tx_index) + { + /* all pending tx packets sent. */ + this_tse->first_tx_index = INVALID_INDEX; + this_tse->last_tx_index = INVALID_INDEX; + completed = 1u; + } + else + { + /* Move on to next transmit descriptor. */ + ++index; + + if (index >= TSE_TX_RING_SIZE) + { + index = 0; + } + + this_tse->first_tx_index = index; + + /* Check if we reached a descriptor still pending tx. */ + empty_flag = this_tse->tx_desc_tab[index].pkt_size & DMA_DESC_EMPTY_FLAG_MASK; + + if (0u == empty_flag) + { + completed = 1u; + } + } + + /* + Clear the tx packet sent interrupt. Please note that this must be done + for every packet sent as it decrements the TXPKTCOUNT. + */ + HAL_set_32bit_reg_field(this_tse->base_addr, DMATXSTATUS_TXPKT_SENT, 0x1); + + } while (0u == completed); +} + +static void +tx_desc_ring_init(tse_instance_t *this_tse) +{ + int32_t inc; + + for (inc = 0; inc < TSE_TX_RING_SIZE; ++inc) + { + this_tse->tx_desc_tab[inc].pkt_start_addr = 0u; + this_tse->tx_desc_tab[inc].pkt_size = DMA_DESC_EMPTY_FLAG_MASK; + + if ((TSE_TX_RING_SIZE - 1) == inc) + { + this_tse->tx_desc_tab[inc].next_desriptor = (uint32_t)&this_tse->tx_desc_tab[0]; + } + else + { + this_tse->tx_desc_tab[inc].next_desriptor = (uint32_t)&this_tse->tx_desc_tab[inc + 1]; + } + + this_tse->tx_desc_tab[inc].index = inc; + } +} + +static void +rx_desc_ring_init(tse_instance_t *this_tse) +{ + int32_t inc; + + for (inc = 0; inc < TSE_RX_RING_SIZE; ++inc) + { + this_tse->rx_desc_tab[inc].pkt_start_addr = 0u; + this_tse->rx_desc_tab[inc].pkt_size = 0u; + + if ((TSE_RX_RING_SIZE - 1) == inc) + { + this_tse->rx_desc_tab[inc].next_desriptor = (uint32_t)&this_tse->rx_desc_tab[0]; + } + else + { + this_tse->rx_desc_tab[inc].next_desriptor = (uint32_t)&this_tse->rx_desc_tab[inc + 1]; + } + + this_tse->rx_desc_tab[inc].index = inc; + } +} + +static void +assign_station_addr(tse_instance_t *this_tse, const uint8_t mac_addr[6]) +{ + uint32_t address32; + + HAL_ASSERT(NULL_POINTER != mac_addr); + + if (NULL_POINTER != mac_addr) + { + address32 = ((uint32_t)mac_addr[5]) << 24u; + address32 |= ((uint32_t)mac_addr[4]) << 16u; + address32 |= ((uint32_t)mac_addr[3]) << 8u; + address32 |= ((uint32_t)mac_addr[2]); + + HAL_set_32bit_reg(this_tse->base_addr, STADDRL, address32); + + address32 = ((uint32_t)mac_addr[1]) << 24u; + address32 |= ((uint32_t)mac_addr[0]) << 16u; + + HAL_set_32bit_reg(this_tse->base_addr, STADDRH, address32); + } +} + +/***************************************************************************/ /** + * Probe used PHY. + * + * return PHY + * address. If PHY is + * not found, function + * returns 255. + */ +static uint8_t +phy_probe(tse_instance_t *this_tse) +{ + uint8_t phy; + uint8_t phy_address = 0; + uint16_t reg; + + for (phy = PHY_ADDRESS_MIN; phy <= PHY_ADDRESS_MAX; phy++) + { + reg = TSE_read_phy_reg(this_tse, phy, MII_PHYSID1); + + if ((reg != 0x0000ffffUL) && (reg != 0x00000000UL)) + { + phy_address = phy; + phy = PHY_ADDRESS_MAX + 1; + } + } + + return phy_address; +} + +/***************************************************************************/ /** + This function initializes the TBI/1000BaseX module + */ +#if ((TSE_PHY_INTERFACE == TSE_SGMII) || (TSE_PHY_INTERFACE == TSE_1000BASEX)) +static void +msgmii_init(tse_instance_t *this_tse) +{ + volatile uint16_t phy_reg; + + /* Reset M-SGMII. */ + TSE_write_phy_reg(this_tse, TSE_MSGMII_ADDR, 0x00, 0x8000u); + +#if (TSE_PHY_INTERFACE == TSE_1000BASEX) + /*Configure MSGMII 1000BaseX Advertisement reg*/ + TSE_write_phy_reg(this_tse, TSE_MSGMII_ADDR, MII_ADVERTISE, 0x0020u); + + /*Configure MSGMII TBI control reg for 1000BaseX*/ + TSE_write_phy_reg(this_tse, TSE_MSGMII_ADDR, 0x11, 0x0800u); +#endif + +#if (TSE_PHY_INTERFACE == TSE_SGMII) + /* Register 0x04 of M-SGMII must be always be set to 0x0001. */ + TSE_write_phy_reg(this_tse, TSE_MSGMII_ADDR, MII_ADVERTISE, 0x0001); +#endif + + /*Enable auto-negotiation inside MSGMII block.*/ + phy_reg = TSE_read_phy_reg(this_tse, TSE_MSGMII_ADDR, 0x00); + phy_reg |= 0x1000; + TSE_write_phy_reg(this_tse, TSE_MSGMII_ADDR, 0x00, phy_reg); +} + +/***************************************************************************/ /** + * + */ +static void +msgmii_autonegotiate(tse_instance_t *this_tse) +{ + volatile uint16_t phy_reg; + uint16_t autoneg_complete; + uint32_t volatile aneg_timeout = 1000000u; + + phy_reg = TSE_read_phy_reg(this_tse, TSE_MSGMII_ADDR, MII_BMCR); + phy_reg |= (BMCR_ANENABLE | BMCR_ANRESTART); + TSE_write_phy_reg(this_tse, TSE_MSGMII_ADDR, MII_BMCR, phy_reg); + + /* Wait for msgmii auto-negotiation to complete. */ + do + { + phy_reg = TSE_read_phy_reg(this_tse, TSE_MSGMII_ADDR, MII_BMSR); + autoneg_complete = phy_reg & MSGMII_AUTO_NEGOTIATION_COMPLETE; + --aneg_timeout; + } while ((!autoneg_complete && aneg_timeout != 0u) || (0xFFFF == phy_reg)); +} + +#endif /*#if ((TSE_PHY_INTERFACE == TSE_SGMII) || (TSE_PHY_INTERFACE == TSE_1000BASEX))*/ + +#if (TSE_PHY_INTERFACE == TSE_1000BASEX) +static uint8_t +msgmii_get_link_status(tse_instance_t *this_tse, tse_speed_t *speed, uint8_t *fullduplex) +{ + volatile uint16_t phy_reg; + uint16_t link_up; + uint8_t link_status; + + /* Find out if link is up.*/ + phy_reg = TSE_read_phy_reg(this_tse, TSE_MSGMII_ADDR, MII_BMSR); + link_up = phy_reg & BMSR_LSTATUS; + + if (link_up != TSE_LINK_DOWN) + { + /* Link is up. */ + link_status = TSE_LINK_UP; + + phy_reg = TSE_read_phy_reg(this_tse, TSE_MSGMII_ADDR, MSGMII_PHY_STATUS); + + if (phy_reg & MSGMII_1000BASEX_FD) + { + *fullduplex = TSE_FULL_DUPLEX; + } + else if (phy_reg & MSGMII_1000BASEX_HD) + { + *fullduplex = TSE_HALF_DUPLEX; + } + + *speed = TSE_MAC1000MBPS; + } + else + { + /* Link is down. */ + link_status = TSE_LINK_DOWN; + } + + return link_status; +} +#endif /*#if(TSE_PHY_INTERFACE == TSE_1000BASEX)*/ + +#if (TSE_PHY_INTERFACE == TSE_RGMII) +static void +rgmii_set_link_speed(tse_instance_t *this_tse, tse_speed_t speed) +{ + if (TSE_MAC1000MBPS == speed) + { + TSE_write_phy_reg(this_tse, TSE_RGMII_MDIO_ADDR, 0x0u, 0x0040u); + } + if (TSE_MAC100MBPS == speed) + { + TSE_write_phy_reg(this_tse, TSE_RGMII_MDIO_ADDR, 0x0u, 0x2000u); + } + if (TSE_MAC10MBPS == speed) + { + TSE_write_phy_reg(this_tse, TSE_RGMII_MDIO_ADDR, 0x0u, 0x0000u); + } +} +#endif /*#if (TSE_PHY_INTERFACE == TSE_RGMII)*/ + +#ifdef __cplusplus +} +#endif + +/******************************** END OF FILE ******************************/ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/core_tse.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/core_tse.h new file mode 100644 index 0000000..7b7036c --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/core_tse.h @@ -0,0 +1,1790 @@ +/** + * Copyright 2014 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * CoreTSE 10/100/1000 Mbps Ethernet MAC bare metal software driver public API. + * + * @file core_tse.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreTSE prototypes + * + */ + +/** + @mainpage CoreTSE Bare Metal Driver + + @section intro Introduction + + The Core Triple Speed Ethernet (CoreTSE) is a hardware soft IP + core that implements 10/100/1000 Mbps Ethernet Media Access Control (MAC). + The CoreTSE supports MII/GMII/TBI interfaces to the physical + layer devices (PHY). + + The only difference between CoreTSE_AHB and CoreTSE soft IP is that the CoreTSE_AHB + has DMA with an AHB interface for transmitting and receiving data packets whereas + the CoreTSE does not. For CoreTSE, no firmware intervention is needed for packet + transmit/receive DMA operations. All the other operations are the same for + CoreTSE_AHB and CoreTSE soft IP and this driver can be used to execute them. + + Note: + This document covers the driver functionality for the CoreTSE_AHB and CoreTSE. + For brevity, we use the term CoreTSE to describe both in this document, unless + we are describing functionality specific to the CoreTSE_AHB. + + This software driver provides a set of functions for controlling the + CoreTSE as part of a bare metal system where no operating system + is available. This driver can be adapted for use as part of an operating + system, but the implementation of the adaptation layer between the driver + and the operating system's driver model is outside the scope of the driver. + + @section hw_flow_dependencies Hardware Flow Dependencies + + The configuration of all features of the CoreTSE_AHB or CoreTSE soft IP is + covered by this driver except for the selection of the Ethernet + PHY, the PHY interface, the size of transmit and receive rings, and the MDIO + address of the TBI/1000BaseX module within CoreTSE. + + The PHY interface type is selected using the Firmware Catalogue configuration + dialogue window. + This driver supports MII, GMII and TBI interfaces. + See the "PHY Interface Types" sub-section for an explanation of the PHY interface. + + The size of the transmit and receive rings defines the maximum number of transmit + and receive packets that can be queued. + + The MDIO address of the TBI module within CoreTSE is also selected using the Firmware + Catalogue configuration dialogue window. + + Note: + This selection is applicable only when CoreTSE is configured to operate in TBI + mode and is ignored when CoreTSE is configured to operate in G/MII mode. + + The system may use the CoreTSE in GMII mode and use an external + CoreRGMII soft IP to interface with a PHY using RGMII interface type. If the + CoreRGMII is being used in the system, then the MDIO address of the CoreRGMII + also needs to be configured here for this driver. + + Your application software should configure the CoreTSE driver by calling the + `TSE_init()` function for each CoreTSE instance in the hardware design. + + @subsection phy_if_types PHY Interface Types + + - MII: CoreTSE operates in MII mode and interfaces with MII PHY directly. + + - GMII: CoreTSE operates in GMII mode and interfaces with GMII PHY directly. + + - TBI: CoreTSE operates in TBI mode (Internal TBI/1000BaseX module is + enabled). + + @section theory_op Theory of Operation + + The CoreTSE software driver is designed to allow the control of multiple instances + of CoreTSE. Each instance of CoreTSE in the hardware design is associated with + a single instance of the tse_instance_t structure in the software. + You need to allocate memory for one unique tse_instance_t structure instance for + each CoreTSE hardware instance. The contents of these data structures are initialized + by calling `TSE_init()`. + A pointer to this structure is passed to subsequent driver functions to identify + the CoreTSE hardware instance you wish to perform the requested operation on. + + Note: + Do not attempt to directly manipulate the content of tse_instance_t + structures. This structure is only intended to be modified by the driver + function. + + The CoreTSE driver functions are grouped into the following categories: + - Initialization and configuration + - Transmit operations + - Receive operations + - Reading link status and statistics + - Address-based frame filtering + - Wake up on LAN (WoL) with unicast match and AMD magic packet detection + - Jumbo Frames + + @subsection Configuration + + The CoreTSE driver is initialized and configured by calling the `TSE_init()` + function. The `TSE_init()` function takes a pointer to a configuration data + structure as a parameter. This data structure contains all the configuration + information required to initialize and configure the CoreTSE. + + + @subsection Initialization + + The CoreTSE driver provides the `TSE_cfg_struct_def_init()` function to + initialize the configuration data structure to the default value. It is + recommended using this function to retrieve the default configuration and then + overwrite the defaults with the application-specific settings such as PHY + address, allowed link speeds, link duplex mode, and MAC address. + + The `TSE_init()` function must be called before any other CoreTSE driver + functions. The `TSE_cfg_struct_def_init()` is the only function which can be + called before calling the `TSE_init()` function. + + The following functions are used as part of the initialization and configuration + process: + - `TSE_cfg_struct_def_init()` + - `TSE_init()` + + @subsection tx_op Transmit Operations + + The CoreTSE driver transmit operations are interrupt driven. The application + must register a transmit call-back function with the driver using the + `TSE_set_tx_callback()` function. + The CoreTSE driver calls this call-back function every time a packet is sent. + + The application must call the `TSE_send_pkt()` function every time the user wants + to transmit a packet. The application passes a pointer to the buffer + containing the packet to send. It is the user's responsibility to + manage the memory allocated to store the transmit packets. The CoreTSE + driver only requires a pointer to the buffer containing the packet and the + packet size. The CoreTSE driver calls the transmit call-back function + registered using the `TSE_set_tx_callback()` function once a packet is sent. + The transmit call-back function is supplied by the application and can be + used to release the memory used to store the packet that was sent. + + The following functions are used as part of transmit and receive operations: + - `TSE_send_pkt()` + - `TSE_set_tx_callback()` + + Note: + This operation is applicable only for CoreTSE_AHB and this operation is not supported + on CoreTSE soft IP. + + @subsection rx_op Receive Operations + + The CoreTSE driver receive operations are interrupt driven. The application + must first register a receive call-back function using the + `TSE_set_rx_callback()` function. The application can then allocate receive + buffers to the CoreTSE driver by calling the `TSE_receive_pkt()` function. + This function can be called multiple times to allocate more than one receive + buffer. The CoreTSE driver calls the receive call-back function whenever a packet + is received into one of the receive buffer. + The driver hands back the receive buffer to the application for packet processing. + The CoreTSE driver won't reuse the receive buffer unless a call to `TSE_receive_pkt()` + re-allocates it to the driver. + + The following functions are used as part of transmit and receive operations: + - `TSE_receive_pkt()` + - `TSE_set_rx_callback()` + + Note: + This operation is applicable only for CoreTSE_AHB and this operation is not supported + on CoreTSE soft IP. + + @subsection status Statistics and Link Status + + The CoreTSE driver provides the following functions to retrieve the current + link status and statistics. + - `TSE_get_link_status()` + - `TSE_read_stat()` + + @subsection addr_filtering Address-based Frame Filtering + + The CoreTSE performs frame filtering based on the destination MAC + address of the received frame. The `TSE_init()` function initializes the + CoreTSE hardware to a default filtering mode where only broadcast + frames and frames with a destination address equal to the local base station + MAC address are passed to the MAC. The Broadcast frames are usually required + by an application to obtain an IP address. This default configuration does not + need the frame filtering hash table to be filled. This default configuration + is returned with the `TSE_cfg_struct_def_init()` function. You may change the + configuration before passing it to the `TSE_init()` function. + + The application can use the `TSE_set_address_filter()` function to overwrite + the frame filter choice, which was selected at initialization time. The + application must provide the list of MAC addresses from which it + wants to receive frames (allowed MAC addresses list) to this driver using + the `TSE_set_address_filter()` function. If a received frame contains one of + the MAC addresses contained in the allowed MAC addresses list then that + frame is passed. + + The setting of the broadcast, hash-unicast/multicast configuration in Frame Pass + Control register is inferred from the values of the MAC addresses passed to the + driver in the allowed MAC addresses list. + + - If all MAC addresses included in the allowed MAC addresses list are unicast + then hash-unicast filtering is selected. This results in more unwanted frames + being rejected. + - If all MAC addresses included in the allowed MAC addresses list are multicast + then hash-multicast filtering is selected. + - If the allowed MAC addresses list contains a mix of unicast and multicast MAC + addresses, then hash-unicast and hash-multicast filtering is selected. + - Broadcast filtering is selected if the broadcast MAC address `(FF:FF:FF:FF:FF:FF)` + is included in the allowed MAC addresses list. + + If the allowed MAC addresses list contains a mix of broadcast, unicast, and multicast + MAC addresses then broadcast, hash-unicast and hash-multicast filtering is selected. + The local base station MAC address must be included in the allowed MAC addresses + list if the application wants to receive frames addressed to it after calling the + `TSE_set_address_filter()` function. + + + The filtering is not perfect since the filtering hardware uses a hash table. + Therefore, some frames with addresses are not included in the allowed MAC + addresses list are still passed because the hash value for their MAC address + is identical to the hash value of an allowed MAC address. The hash + unicast/multicast setting allows further reduction of the number of unwanted + frames passed by both checking the MAC address against the hash table and + checking the received MAC address unicast/multicast bit against the hash + unicast/multicast setting of the filtering IP. + + The following functions is used as part of the frame filtering + - `TSE_set_address_filter()` + + @subsection frame_drop Frame Drop Criterion + + Apart from the address-based frame filtering, the CoreTSE + provides the facility to drop frames based on certain criteria. + The criteria for the frame drop logic can be configured using the framedrop_mask + element of the tse_cfg_t structure. The value of this element is set to + TSE_DEFVAL_FRAMEDROP_MASK by the `TSE_cfg_struct_def_init()` function. + This configuration value enables CoreTSE to drop frames which + have CRC errors, code errors, and frames with unsupported op-codes. You may + change the configuration before passing it to the `TSE_init()` function. + + @subsection wol Wake on LAN (WoL) + The CoreTSE supports the Wake on LAN feature with unicast match + frames and AMD magic packets. The WoL interrupt is generated when this feature + is enabled and one of the two frames mentioned above is received. + + `TSE_cfg_struct_def_init()` disables WoL. You may change the configuration + before passing it to the `TSE_init()` function. You can enable this feature using + TSE_WOL_UNICAST_FRAME_DETECT_EN or TSE_WOL_MAGIC_FRAME_DETECT_EN or both the + constants with a bitwise OR operation. + + The callback handler function must be provided to this driver using + `TSE_set_wol_callback()` function. A function of type tse_wol_callback_t must + be provided as a parameter to this function. + The driver calls this function when the WoL event happens. + + The following function is used as part of WoL: + - `TSE_set_wol_callback()` + + @subsection jumbo_frames Jumbo Frames + + The CoreTSE_AHB supports jumbo frames that exceed the 1500 byte max of the standard + Ethernet frame, up to 4000 bytes long. + This driver provides TSE_JUMBO_PACKET_SIZE constant which can be used to define + the Tx-Rx buffer size where jumbo frame support is required. + +*/ +#ifndef CORE_TSE_H_ +#define CORE_TSE_H_ + +#include "coretse_types.h" +#include "coretse_user_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + # State definitions + + | Constant | Description | + | ----------- | ------------------- | + | TSE_DISABLE | Disable the CoreTSE | + | TSE_ENABLE | Enable the CoreTSE | + */ +#define TSE_DISABLE ((uint8_t)0) +#define TSE_ENABLE ((uint8_t)1) + +/** + # Packet Transmission + + The following definitions are used with functions `TSE_send_pkt()` and + `TSE_receive_pkt()` to report success or failure in assigning the packet memory + buffer to a transmit/receive descriptor. + + | Constant | Description | + | ----------- | --------------------------------------------------------------------------------- | + | TSE_SUCCESS | The memory buffer was successfully assigned to a transmit/receive descriptor. | + | TSE_FAILED | The memory buffer was not successfully assigned to a transmit/receive descriptor. | + +*/ +#define TSE_SUCCESS TSE_ENABLE +#define TSE_FAILED TSE_DISABLE + +/** + # Link Status + + The following definitions are used with `TSE_get_link_status()` to report the link status. + + | Constant | Description | + | --------------- | -----------------------------------------------------------------------| + | TSE_LINK_DOWN | Ethernet link is down. There is no connection to a remote device. | + | TSE_LINK_UP | Ethernet link is up. A connection is established with a remote device. | + | TSE_HALF_DUPLEX | Connection is half-duplex. | + | TSE_FULL_DUPLEX | Connection is full-duplex. | + +*/ + +#define TSE_LINK_DOWN 0u +#define TSE_LINK_UP 1u + +#define TSE_HALF_DUPLEX 0u +#define TSE_FULL_DUPLEX 1u + +/** + # Maximum MAC frame size (packet size) + + | Constant | Description | + | --------------------- | ------------------------------------------------- | + | TSE_MAX_PACKET_SIZE | Maximum packet size of a standard Ethernet frame. | + | TSE_JUMBO_PACKET_SIZE | Maximum size of Jumbo frame packet. | + */ +#define TSE_MAX_PACKET_SIZE 1518u +#define TSE_JUMBO_PACKET_SIZE 9000u + +/** + # Transmit and receive packet buffer sizes + + | Constant | Description | + | ------------------- | ----------------------------- | + | TSE_MAX_TX_BUF_SIZE | Maximum transmit buffer size. | + | TSE_MAX_RX_BUF_SIZE | Maximum receive buffer size. | + */ +#define TSE_MAX_TX_BUF_SIZE TSE_MAX_PACKET_SIZE +#define TSE_MAX_RX_BUF_SIZE TSE_MAX_PACKET_SIZE + +/** + # Configuration Parameter Definitions + + */ + +/** + ## FIFO error detection & correction + + | Constant | Description | + | ------------------------ | -------------------------------------------- | + | TSE_ERR_DET_CORR_ENABLE | Enable FIFO error detection and correction. | + | TSE_ERR_DET_CORR_DISABLE | Disable FIFO error detection and correction. | + */ +#define TSE_ERR_DET_CORR_ENABLE TSE_ENABLE +#define TSE_ERR_DET_CORR_DISABLE TSE_DISABLE + +/** + ## Huge frame support + + | Constant | Description | + | ---------------------- | --------------------------- | + | TSE_HUGE_FRAME_ENABLE | Enable Huge frame support. | + | TSE_HUGE_FRAME_DISABLE | Disable Huge frame support. | + */ +#define TSE_HUGE_FRAME_ENABLE TSE_ENABLE +#define TSE_HUGE_FRAME_DISABLE TSE_DISABLE + +/** + ## Length field checking + + | Constant | Description | + | ------------------------------ | ------------------------------ | + | TSE_LENGTH_FIELD_CHECK_ENABLE | Enable length field checking. | + | TSE_LENGTH_FIELD_CHECK_DISABLE | Disable length field checking. | + */ +#define TSE_LENGTH_FIELD_CHECK_ENABLE TSE_ENABLE +#define TSE_LENGTH_FIELD_CHECK_DISABLE TSE_DISABLE + +/** + ## Padding and CRC + + | Constant | Description | + | ---------------------- | ------------------------ | + | TSE_PAD_N_CRC_ENABLE | Enable padding and CRC. | + | TSE_PAD_N_CRC_DISABLE | Disable padding and CRC. | +*/ +#define TSE_PAD_N_CRC_ENABLE TSE_ENABLE +#define TSE_PAD_N_CRC_DISABLE TSE_DISABLE + +/** + ## Append CRC + + | Constant | Description | + | --------------- | ---------------------- | + | TSE_CRC_ENABLE | Enable CRC appending. | + | TSE_CRC_DISABLE | Disable CRC appending. | + */ +#define TSE_CRC_ENABLE TSE_ENABLE +#define TSE_CRC_DISABLE TSE_DISABLE + +/** + ## Full-duplex mode + + | Constant | Description | + | ---------------------- | ------------------------ | + | TSE_FULLDUPLEX_ENABLE | Enable full-duplex mode. | + | TSE_FULLDUPLEX_DISABLE | Disable full-duplex mode. | + */ +#define TSE_FULLDUPLEX_ENABLE TSE_ENABLE +#define TSE_FULLDUPLEX_DISABLE TSE_DISABLE + +/** + ## Loopback mode + + | Constant | Description | + | -------------------- | ---------------------- | + | TSE_LOOPBACK_ENABLE | Enable loopback mode. | + | TSE_LOOPBACK_DISABLE | Disable loopback mode. | + */ +#define TSE_LOOPBACK_ENABLE TSE_ENABLE +#define TSE_LOOPBACK_DISABLE TSE_DISABLE + +/** + ## Receiver flow control + + | Constant | Description | + | ------------------------ | ------------------------------ | + | TSE_RX_FLOW_CTRL_ENABLE | Enable receiver flow control. | + | TSE_RX_FLOW_CTRL_DISABLE | Disable receiver flow control. | + */ +#define TSE_RX_FLOW_CTRL_ENABLE TSE_ENABLE +#define TSE_RX_FLOW_CTRL_DISABLE TSE_DISABLE + +/** + ## Transmission flow control + + | Constant | Description | + | ------------------------ | ---------------------------------- | + | TSE_TX_FLOW_CTRL_ENABLE | Enable Transmission flow control. | + | TSE_TX_FLOW_CTRL_DISABLE | Disable Transmission flow control. | + */ +#define TSE_TX_FLOW_CTRL_ENABLE TSE_ENABLE +#define TSE_TX_FLOW_CTRL_DISABLE TSE_DISABLE + +/** + ## Excessive defer + + | Constant | Description | + | ---------------------- | ------------------------ | + | TSE_EXSS_DEFER_ENABLE | Enable excessive defer. | + | TSE_EXSS_DEFER_DISABLE | Disable excessive defer. | + + */ +#define TSE_EXSS_DEFER_ENABLE TSE_ENABLE +#define TSE_EXSS_DEFER_DISABLE TSE_DISABLE + +/** + ## No-backoff + + | Constant | Description | + | ---------------------- | ------------------- | + | TSE_NO_BACKOFF_ENABLE | Enable no-backoff. | + | TSE_NO_BACKOFF_DISABLE | Disable no-backoff. | + */ +#define TSE_NO_BACKOFF_ENABLE TSE_ENABLE +#define TSE_NO_BACKOFF_DISABLE TSE_DISABLE + +/** + ## Backpressure no-backoff + + | Constant | Description | + | -------------------------------- | -------------------------------- | + | TSE_BACKPRESS_NO_BACKOFF_ENABLE | Enable backpressure no-backoff. | + | TSE_BACKPRESS_NO_BACKOFF_DISABLE | Disable backpressure no-backoff. | + */ +#define TSE_BACKPRESS_NO_BACKOFF_ENABLE TSE_ENABLE +#define TSE_BACKPRESS_NO_BACKOFF_DISABLE TSE_DISABLE + +/** + ## Alternative binary exponential backoff + + | Constant | Description | + | ---------------- | ----------------------------------------------- | + | TSE_ABEB_ENABLE | Enable alternative binary exponential backoff. | + | TSE_ABEB_DISABLE | Disable alternative binary exponential backoff. | + */ +#define TSE_ABEB_ENABLE TSE_ENABLE +#define TSE_ABEB_DISABLE TSE_DISABLE + +/** + ## Suppress preamble + + | Constant | Description | + | ----------------------------- | ----------------------------- | + | TSE_SUPPRESS_PREAMBLE_ENABLE | Enable preamble suppression. | + | TSE_SUPPRESS_PREAMBLE_DISABLE | Disable preamble suppression. | + */ +#define TSE_SUPPRESS_PREAMBLE_ENABLE TSE_ENABLE +#define TSE_SUPPRESS_PREAMBLE_DISABLE TSE_DISABLE + +/** + ## Auto-scan PHYs + + | Constant | Description | + | ------------------------ | ----------------------- | + | TSE_PHY_AUTOSCAN_ENABLE | Enable PHYs auto-scan. | + | TSE_PHY_AUTOSCAN_DISABLE | Disable PHYs auto-scan. | + */ +#define TSE_PHY_AUTOSCAN_ENABLE TSE_ENABLE +#define TSE_PHY_AUTOSCAN_DISABLE TSE_DISABLE + +/** + ## WoL event detection masks + + | Constant | Description | + | ------------------------------- | --------------------------------------- | + | TSE_WOL_UNICAST_FRAME_DETECT_EN | WoL with unicast match frame detection. | + | TSE_WOL_MAGIC_FRAME_DETECT_EN | WoL with AMD magic packet detection. | + | TSE_WOL_DETECT_DISABLE | WoL detection disabled. | + */ +#define TSE_WOL_UNICAST_FRAME_DETECT_EN (1u << 0) +#define TSE_WOL_MAGIC_FRAME_DETECT_EN (1u << 1) +#define TSE_WOL_DETECT_DISABLE TSE_DISABLE + +/** + ## Preamble length default value and maximum value + + | Constant | Description | + | ------------------- | ------------------------ | + | TSE_PREAMLEN_DEFVAL | Default preamble length. | + | TSE_PREAMLEN_MAXVAL | Maximum preamble length. | + */ +#define TSE_PREAMLEN_DEFVAL ((uint8_t)(0x07)) +#define TSE_PREAMLEN_MAXVAL ((uint8_t)(0x0F)) + +/** + ## Byte/Nibble mode + + | Constant | Description | + | --------------- | ------------ | + | TSE_NIBBLE_MODE | Nibble mode. | + | TSE_BYTE_MODE | Byte mode. | + */ +#define TSE_NIBBLE_MODE ((uint8_t)(0x00)) +#define TSE_BYTE_MODE ((uint8_t)(0x01)) + +/** + # IPG/IFG values + + | Constant | Description | + | -------------------- | -------------------------------------------------| + | TSE_MINIFG_MAXVAL | Minimum interframe gap - maximum value. | + | TSE_MINIFG_DEFVAL | Minimum interframe gap - default value. | + | TSE_BTBIFG_MAXVAL | Back-to-back interframe gap - maximum value | + | TSE_BTBIFG_DEFVAL | Back-to-back interframe gap - default value. | + | TSE_NONBTBIFG_DEFVAL | Non-back-to-back interframe gap - default value. | + | TSE_NONBTBIFG_MAXVAL | Non-back-to-back interframe gap - maximum value. | + */ +#define TSE_MINIFG_MAXVAL ((uint8_t)(0xFF)) +#define TSE_MINIFG_DEFVAL ((uint8_t)(0x50)) +#define TSE_BTBIFG_MAXVAL ((uint8_t)(0x7F)) +#define TSE_BTBIFG_DEFVAL ((uint8_t)(0x60)) +#define TSE_NONBTBIFG_DEFVAL ((uint16_t)((0x4000) | 0x0060)) +#define TSE_NONBTBIFG_MAXVAL ((uint16_t)(0x3FFF)) + +/** + # Number of maximum retransmission tries + + | Constant | Description | + | -------------------- | ---------------------------------------------------------------- | + | TSE_MAXRETX_MAXVAL | Maximum number of retransmission attempts - maximum value. | + | TSE_MAXRETX_DEFVAL | Maximum number of retransmission attempts - default value. | + | TSE_ABEBTRUNC_MAXVAL | Alternate Binary Exponential Backoff Truncation - maximum value. | + | TSE_ABEBTRUNC_DEFVAL | Alternate Binary Exponential Backoff Truncation - default value. | + */ +#define TSE_MAXRETX_MAXVAL ((uint8_t)(0x1F)) +#define TSE_MAXRETX_DEFVAL ((uint8_t)(0x0F)) +#define TSE_ABEBTRUNC_MAXVAL ((uint8_t)(0x0F)) +#define TSE_ABEBTRUNC_DEFVAL ((uint8_t)(0x0A)) + +/** + # PHY clock divider values + + | Constant | Description | + | ---------------- | --------------------------- | + | TSE_DEF_PHY_CLK | Default source clock. | + | TSE_BY4_PHY_CLK | Source clock divided by 4. | + | TSE_BY6_PHY_CLK | Source clock divided by 6. | + | TSE_BY8_PHY_CLK | Source clock divided by 8. | + | TSE_BY10_PHY_CLK | Source clock divided by 10. | + | TSE_BY14_PHY_CLK | Source clock divided by 14. | + | TSE_BY20_PHY_CLK | Source clock divided by 20. | + | TSE_BY28_PHY_CLK | Source clock divided by 28. | + */ +#define TSE_DEF_PHY_CLK ((uint8_t)(0x07)) +#define TSE_BY4_PHY_CLK ((uint8_t)(0x01)) +#define TSE_BY6_PHY_CLK ((uint8_t)(0x02)) +#define TSE_BY8_PHY_CLK ((uint8_t)(0x03)) +#define TSE_BY10_PHY_CLK ((uint8_t)(0x04)) +#define TSE_BY14_PHY_CLK ((uint8_t)(0x05)) +#define TSE_BY20_PHY_CLK ((uint8_t)(0x06)) +#define TSE_BY28_PHY_CLK ((uint8_t)(0x07)) + +/** + # PHY addresses + + | Constant | Description | + | --------------------- | ------------------------------------- | + | TSE_DEFAULT_PHY | Default PHY address. | + | TSE_PHYADDR_MAXVAL | PHY address - maximum value. | + | TSE_PHYREGADDR_MAXVAL | PHY register address - maximum value. | + | TSE_PHY_ADDR_DEFVAL | PHY address - default value. | + | TSE_PHYREGADDR_DEFVAL | PHY register address - default value. | + */ +#define TSE_DEFAULT_PHY (DP83848) +#define TSE_PHYADDR_MAXVAL ((uint8_t)(0x1F)) +#define TSE_PHYREGADDR_MAXVAL ((uint8_t)(0x1F)) +#define TSE_PHY_ADDR_DEFVAL ((uint8_t)(0x00)) +#define TSE_PHYREGADDR_DEFVAL ((uint8_t)(0x00)) + +/** + # Maximum frame length + + | Constant | Description | + | ---------------------- | ------------------------------------- | + | TSE_MAXFRAMELEN_DEFVAL | Maximum frame length - default value. | + | TSE_MAXFRAMELEN_MAXVAL | Maximum frame length - maximum value. | + */ +#define TSE_MAXFRAMELEN_DEFVAL ((uint16_t)(0x0600)) +#define TSE_MAXFRAMELEN_MAXVAL ((uint16_t)(0x0600)) + +/** + # Slot Time (Collision window) + + | Constant | Description | + | ------------------- | ------------------------------------------- | + | TSE_SLOTTIME_DEFVAL | Slot time (Collision window) default value. | + | TSE_SLOTTIME_MAXVAL | Slot time (Collision window) maximum value. | + */ +#define TSE_SLOTTIME_DEFVAL ((uint16_t)(0x0037)) +#define TSE_SLOTTIME_MAXVAL ((uint16_t)(0x03FF)) + +/** + # Auto-negotiation options + + | Constant | Description | + | ---------------- | ------------------------- | + | TSE_ANEG_ENABLE | Enable auto-negotiation. | + | TSE_ANEG_DISABLE | Disable auto-negotiation. | + */ +#define TSE_ANEG_ENABLE TSE_ENABLE +#define TSE_ANEG_DISABLE TSE_DISABLE + +/** + # Speed configuration options + +| Constant | Description | +| ------------------- | ---------------------------------------- | +| TSE_ANEG_10M_FD | Auto-negotiate at 10 Mbps full-duplex. | +| TSE_ANEG_10M_HD | Auto-negotiate at 10 Mbps half-duplex. | +| TSE_ANEG_100M_FD | Auto-negotiate at 100 Mbps full-duplex. | +| TSE_ANEG_100M_HD | Auto-negotiate at 100 Mbps half-duplex. | +| TSE_ANEG_1000M_FD | Auto-negotiate at 1000 Mbps full-duplex. | +| TSE_ANEG_ALL_SPEEDS | Auto-negotiate at all speeds. | + + */ +#define TSE_ANEG_10M_FD 0x00000001u +#define TSE_ANEG_10M_HD 0x00000002u +#define TSE_ANEG_100M_FD 0x00000004u +#define TSE_ANEG_100M_HD 0x00000008u +#define TSE_ANEG_1000M_FD 0x00000010u +#define TSE_ANEG_1000M_HD 0x00000020u +#define TSE_ANEG_ALL_SPEEDS \ + (TSE_ANEG_10M_FD | TSE_ANEG_10M_HD | TSE_ANEG_100M_FD | TSE_ANEG_100M_HD | TSE_ANEG_1000M_FD | \ + TSE_ANEG_1000M_HD) + +/** + # Frame Filter configuration options + +| Constant | Description | +| -------------------------------- |----------------------------------------------------------------------------------- | +| TSE_FC_PASS_BROADCAST_MASK | Pass all broadcast frames. | +| TSE_FC_PASS_MULTICAST_MASK | Pass all broadcast frames. | +| TSE_FC_PASS_UNICAST_MASK | Pass the frame if its Unicast-DA matches the configured destination address. | +| TSE_FC_PROMISCOUS_MODE_MASK | Promiscuous mode, allow all the frames to pass. | +| TSE_FC_PASS_UNICAST_HASHT_MASK | Pass the frame if the hash table entry matches for Unicast destination addresses. | +| TSE_FC_PASS_MULTICAST_HASHT_MASK | Pass the frame if the hash table entry matches for Multicast destination address. | +| TSE_FC_DEFAULT_MASK | Default frame filtering mask. | + */ +#define TSE_FC_PASS_BROADCAST_MASK (1u << 0) +#define TSE_FC_PASS_MULTICAST_MASK (1u << 1) +#define TSE_FC_PASS_UNICAST_MASK (1u << 2) +#define TSE_FC_PROMISCOUS_MODE_MASK (1u << 3) +#define TSE_FC_PASS_UNICAST_HASHT_MASK (1u << 4) +#define TSE_FC_PASS_MULTICAST_HASHT_MASK (1u << 5) +#define TSE_FC_DEFAULT_MASK (TSE_FC_PASS_BROADCAST_MASK | TSE_FC_PASS_UNICAST_MASK) + +/** + # Default framedrop mask + + | Constant | Description | + | ------------------------- | ------------------------------------------------------ | + | TSE_DEFVAL_FRAMEDROP_MASK | Generic default value applicable to most applications. | + */ +#define TSE_DEFVAL_FRAMEDROP_MASK \ + (TSE_CRC_ERR_FRAMEDROP_MASK | TSE_CODE_ERR_FRAMEDROP_MASK | \ + TSE_UNSUPPORTED_OPCODE_FRAMEDROP_MASK) + +/** + ## Framedrop if IFG is small + + | Constant | Description | + | ---------------------------- | -------------------------------- | + | TSE_SMALL_AFG_FRAMEDROP_MASK | Drop the packet if IFG is small. | + */ +#define TSE_SMALL_AFG_FRAMEDROP_MASK (1u << 0) + +/** + ## Framedrop if short RXDV Event + + | Constant | Description | + | ----------------------------- | -------------------------------------------------- | + | TSE_EXDV_EVENT_FRAMEDROP_MASK | Drop the packet if a short RXDV event is received. | + + Short RXDV event: Indicates that the last received event seen was not long enough to be a valid + packet. + */ +#define TSE_EXDV_EVENT_FRAMEDROP_MASK (1u << 1) + +/** + ## Framedrop if False Carrier + + | Constant | Description | + | -------------------------------- | --------------------------------------------- | + | TSE_FALSE_CARRIER_FRAMEDROP_MASK | Drop the packet if a False Carrier is received. | + + ### False Carrier + Indicates that at some time since the last receive statistics vector, a false + carrier was detected, noted, and reported with the next receive statistics. + The false carrier is not associated with this packet. + False carrier is an activity on the receive channel that does not result in a + packet receive attempt being made. + + Defined to be RXER = 1, RXDV = 0, RXD[3:0] = 0xE, RXD[7:0] = 0x0E. + */ +#define TSE_FALSE_CARRIER_FRAMEDROP_MASK (1u << 2) + +/** + ## Framedrop if Code error + + | Constant | Description | + | --------------------------- | -------------------------------------------- | + | TSE_CODE_ERR_FRAMEDROP_MASK | Drop the packet if a code error is received. | + + Code Error: One or more nibbles were signalled as errors during the reception of the packet. + */ +#define TSE_CODE_ERR_FRAMEDROP_MASK (1u << 3) + +/** + ## Framedrop if CRC Error + + | Constant | Description | + | -------------------------- | ------------------------------------------- | + | TSE_CRC_ERR_FRAMEDROP_MASK | Drop the packet if a CRC Error is received. | + + CRC Error: The packet's CRC did not match the internally generated CRC. + */ +#define TSE_CRC_ERR_FRAMEDROP_MASK (1u << 4) + +/** + ## Framedrop if Length check error + + | Constant | Description | + | ----------------------------- | --------------------------------------------------- | + | TSE_LEN_CHKERR_FRAMEDROP_MASK | Drop the frame if a length check error is received. | + + ### Length Check Error + Indicates that the frame length field value in the packet does not match the actual + data byte length and is not a type field. + */ +#define TSE_LEN_CHKERR_FRAMEDROP_MASK (1u << 5) + +/** + ## Framedrop if Length Out of Range + + | Constant | Description | + | --------------------------------- | ----------------------------------------------------------------- | + | TSE_OUTOFRANGE_LEN_FRAMEDROP_MASK | Drop the frame if a frame with Length Out of Range is received. | + + ### Length Out of Range + Indicates that the frames length was larger than 1518 bytes but smaller than + the hosts maximum frame length value (type field). + */ +#define TSE_OUTOFRANGE_LEN_FRAMEDROP_MASK (1u << 6) + +/** + ## Framedrop if an OK frame + + | Constant | Description | + | --------------------------- | ------------------------------------------- | + | TSE_OK_FRAME_FRAMEDROP_MASK | Drop the frame if an OK frame is received. | + + ### OK frame + The frame contained a valid CRC and did not have a code error. + */ +#define TSE_OK_FRAME_FRAMEDROP_MASK (1u << 7) + +/** + ## Framedrop if Multicast frame + + | Constant | Description | + | ---------------------------- | ------------------------------------------------ | + | TSE_MULTICAST_FRAMEDROP_MASK | Drop the frame if a Multicast frame is received. | + + ### Multicast frame + The packet's destination address contained a multicast address. + */ +#define TSE_MULTICAST_FRAMEDROP_MASK (1u << 8) + +/** + ## Framedrop if Broadcast frame + + | Constant | Description | + | ---------------------------- | ------------------------------------------------ | + | TSE_BROADCAST_FRAMEDROP_MASK | Drop the frame if a Broadcast frame is received. | + + ### Broadcast frame + The packet's destination address contained the broadcast address. + */ +#define TSE_BROADCAST_FRAMEDROP_MASK (1u << 9) + +/** + ## Framedrop if Dribble Nibble + + | Constant | Description | + | --------------------------------- | ----------------------------------------------- | + | TSE_DRIBBLE_NIBBLE_FRAMEDROP_MASK | Drop the frame if a Dribble Nibble is received. | + + ### Dribble Nibble + Indicates that after the end of the packet, an additional 1 to 7 bits were received. + A single nibble, called the dribble nibble, is formed but not sent to the system + (10/100 Mbps only). + */ +#define TSE_DRIBBLE_NIBBLE_FRAMEDROP_MASK (1u << 10) + +/** + ## Framedrop if Control Frame + + | Constant | Description | + | -------------------------------- | ---------------------------------------------- | + | TSE_CONTROL_FRAME_FRAMEDROP_MASK | Drop the frame if a Control Frame is received. | + + ### Control Frame + The current frame was recognized as a Control frame for having a valid + Type-Length designation. + */ +#define TSE_CONTROL_FRAME_FRAMEDROP_MASK (1u << 11) + +/** + ## Framedrop if PAUSE Control Frame + + | Constant | Description | + | ------------------------------ | ---------------------------------------------------- | + | TSE_PAUSE_FRAME_FRAMEDROP_MASK | Drop the frame if a PAUSE Control Frame is received. | + + ### PAUSE Control frame + Current frame was recognized as a Control frame containing a valid PAUSE Frame + Op-code and a valid address. + */ +#define TSE_PAUSE_FRAME_FRAMEDROP_MASK (1u << 12) + +/** + ## Framedrop if Unsupported Op-code + + | Constant | Description | + | ------------------------------------- | ----------------------------------------------------- | + | TSE_UNSUPPORTED_OPCODE_FRAMEDROP_MASK | Drop the frame if an Unsupported Op-code is received. | + + ### Unsupported Op-code + The current frame was recognized as a Control frame by the PEMCS, but it contains an unknown + Op-code. + */ +#define TSE_UNSUPPORTED_OPCODE_FRAMEDROP_MASK (1u << 13) + +/** + ## Framedrop if VLAN Tag + + | Constant | Description | + | --------------------------- | --------------------------------------- | + | TSE_VLAN_TAG_FRAMEDROP_MASK | Drop the frame if VLAN Tag is detected. | + + ### VLAN Tag: Received frames length/type field that contains 0x8100, which is the VLAN Protocol + Identifier. + */ +#define TSE_VLAN_TAG_FRAMEDROP_MASK (1u << 14) + +/** + ## Framedrop if a Long Event + + | Constant | Description | + | ----------------------------- | ------------------------------------------- | + | TSE_LONG_EVENT_FRAMEDROP_MASK | Drop the frame if a Long Event is received. | + */ +#define TSE_LONG_EVENT_FRAMEDROP_MASK (1u << 15) + +/** + ## Framedrop if a Truncated frame + + | Constant | Description | + | ----------------------------- | ------------------------------------------- | + | TSE_LONG_EVENT_FRAMEDROP_MASK | Drop the frame if a Long Event is received. | + */ +#define TSE_TRUNCKATED_FRAME_FRAMEDROP_MASK (1u << 16) + +/** + ## Framedrop if Unicast frame detected + + | Constant | Description | + | ------------------------------------- | -------------------------------------------------------------------------------------------- | + | TSE_UNICAST_NO_SAMATCH_FRAMEDROP_MASK | Drop the frame if a Unicast frame is detected but did not match configured station address. | + */ +#define TSE_UNICAST_NO_SAMATCH_FRAMEDROP_MASK (1u << 17) + +/** + ## Framedrop if a short frame received + + | Constant | Description | + | ------------------------------ | ------------------------------------------------------------------------- | + | TSE_SHORT_FRAME_FRAMEDROP_MASK | Drop the frame if a short frame less than 64 bytes in length is received. | + */ +#define TSE_SHORT_FRAME_FRAMEDROP_MASK (1u << 18) + +/** + # PHY Address + + The application can use this constant to indicate to the driver to probe the PHY + and auto-detect the PHY address that it is configured with. If you already + know the PHY address configured in your hardware system, you can provide that + address to the driver instead of this constant. That way the `TSE_init()` function + would be faster because the auto-detection process of the PHY address is now + avoided. + + Note: + To auto-detect the PHY address, the driver scans the valid MDIO addresses starting + from -0- for valid data. + If CoreRGMII and PHY are connected to the MAC's management interface, + then you must make sure that the PHY device MDIO address is less than the + CoreRGMII MDIO address. + + | Constant | Value | + | --------------------------- | ----- | + | TSE_PHY_ADDRESS_AUTO_DETECT | 255 | + + */ +#define TSE_PHY_ADDRESS_AUTO_DETECT 255 + +/** + # Per Packet Override Masks + + A 5-bit field containing the per-packet override flags signalled to the FIFO during + packet transmission. + The bits are encoded as follows: + + | Constant | Value | + | ----------------------------- | ----------------------------------------- | + | TSE_FIFO_TX_CTRL_FRAME | FIFO Transmit Control Frame | + | TSE_FIFO_TX_NO_CTRL_FRAME | Not FIFO Transmit Control Frame | + | TSE_FIFO_TX_PERPKT_PAD_FCS | FIFO Transmit Per-Packet PAD Mode | + | TSE_FIFO_TX_PERPKT_NO_PAD_FCS | Not FIFO Transmit Per-Packet PAD Mode | + | TSE_FIFO_TX_PERPKT_FCS | FIFO Transmit Per-Packet Generate FCS | + | TSE_FIFO_TX_PERPKT_NO_FCS | Not FIFO Transmit Per-Packet Generate FCS | + | TSE_FIFO_TX_PERPKT_ENABLE | FIFO Transmit Per-Packet Generate FCS | + | TSE_FIFO_TX_PERPKT_DISABLE | Not FIFO Transmit Per-Packet Generate FCS | + */ + +#define TSE_FIFO_TX_CTRL_FRAME ((uint32_t)0x00100000) +#define TSE_FIFO_TX_NO_CTRL_FRAME (~TSE_FIFO_TX_CTRL_FRAME) +#define TSE_FIFO_TX_PERPKT_PAD_FCS ((uint32_t)0x00040000) +#define TSE_FIFO_TX_PERPKT_NO_PAD_FCS (~TSE_FIFO_TX_PERPKT_PAD_FCS) +#define TSE_FIFO_TX_PERPKT_FCS ((uint32_t)0x00020000) +#define TSE_FIFO_TX_PERPKT_NO_FCS (~TSE_FIFO_TX_PERPKT_FCS) +#define TSE_FIFO_TX_PERPKT_ENABLE ((uint32_t)0x00010000) +#define TSE_FIFO_TX_PERPKT_DISABLE (~TSE_FIFO_TX_PERPKT_ENABLE) + +/*------------------------Public Function-------------------------------------*/ + +/** + + `TSE_cfg_struct_def_init()` initializes a tse_cfg_t configuration + data structure to default values. The default configuration uses the MII + interface connected to a PHY at address 0x00 which is set to auto-negotiate + at all available speeds up to 1000Mbps. This default configuration can then + be used as a parameter to `TSE_init()`. Typically the default configuration + would be modified to suit the application before being passed to `TSE_init()`. + + @param cfg + The cfg parameter is a pointer to a tse_cfg_t data structure used as a parameter + to function `TSE_init()`. + + @return + This function does not return a value. + + @example + The example below demonstrates the use of the `TSE_cfg_struct_def_init()` + function. It retrieves the default MAC configuration and modifies it to + connect through an MII Ethernet PHY at MII management interface address 0x01. + + This example also demonstrates how to assign the device's MAC address and + force a 100Mbps full duplex link. + + @code + #define TSEMAC_BASE 0x30000000 + tse_cfg_t g_tse_config; + tse_instance_t g_tse; + + TSE_cfg_struct_def_init(&g_tse_config); + + g_tse_config.phy_addr = TSE_PHY_ADDRESS_AUTO_DETECT; + g_tse_config.speed_duplex_select = TSE_ANEG_100M_FD; + g_tse_config.mac_addr[0] = 0xC0; + g_tse_config.mac_addr[1] = 0xB1; + g_tse_config.mac_addr[2] = 0x3C; + g_tse_config.mac_addr[3] = 0x60; + g_tse_config.mac_addr[4] = 0x60; + g_tse_config.mac_addr[5] = 0x60; + + TSE_init(&g_tse, TSEMAC_BASE, &g_tse_config); + + @endcode + */ + +void TSE_cfg_struct_def_init(tse_cfg_t *cfg); + +/** + `TSE_init()` initializes the CoreTSE hardware and + driver internal data structures. + This configuration data structure contains all the information required to + configure the CoreTSE. The `TSE_init()` function initializes the + descriptor rings and their pointers to initial values. `TSE_init()` enables DMA + Rx packet received and Tx packet sent interrupts. + + The configuration passed to the `TSE_init()` function specifies the type of + interface used to connect the CoreTSE and Ethernet PHY as well + as the PHY MII management interface address. The application may pass the + TSE_PHY_ADDRESS_AUTO_DETECT constant as a configuration parameter to indicate + to the driver to probe for the PHY address. If you know the hardware + configuration PHY address of the onboard PHY in your system, you may choose + to provide that instead. + When the user supplies a PHY address value other than TSE_PHY_ADDRESS_AUTO_DETECT, + the driver will use the supplied value directly, provided it is within the valid + PHY address range (0u - 31u). + + The `TSE_init()` function also specifies the allowed link speeds and duplex + modes. It is at this point that the application chooses to auto-negotiate the + link speeds and duplex modes with the link partner or force a specific speed and + duplex mode. + + @param this_tse + The this_tse parameter is a pointer to the tse_instance_t data structure + instance holding all data regarding the CoreTSE hardware instance + being initialized. A pointer to the same data structure must be provided in + subsequent calls to the various CoreTSE driver functions to identify the CoreTSE + instance that should perform the operation + implemented by the called driver function. + + @param base_addr + The base_addr parameter is the base address in the processor's memory map + for the registers of the CoreTSE instance being initialized. + + @param cfg + The cfg parameter is a pointer to a data structure of type tse_cfg_t + containing the CoreTSE's requested configuration. You must + initialize this data structure by first calling the `TSE_cfg_struct_def_init()` + function to fill the configuration data structure with default values. You + can then overwrite some of the default settings with the ones specific to your + application before passing this data structure as a parameter to the `TSE_init()` + function. + + Note: + At a minimum the `mac_addr[5]` array of the configuration data structure must + be overwritten so a unique value can be used as the device's MAC address. + + @return + This function does not return a value. + + @example + + This example demonstrates the use of the `TSE_init()` function to configure + the CoreTSE with the default configuration. + + Note: + A unique MAC address must always be assigned through the configuration data + passed as a parameter to the `TSE_init()` function. + + @code + #define TSEMAC_BASE 0x30000000 + tse_cfg_t g_tse_config; + tse_instance_t g_tse; + + TSE_cfg_struct_def_init(&g_tse_config); + + g_tse_config.phy_addr = TSE_PHY_ADDRESS_AUTO_DETECT; + g_tse_config.speed_duplex_select = TSE_ANEG_100M_FD; + g_tse_config.mac_addr[0] = 0xC0; + g_tse_config.mac_addr[1] = 0xB1; + g_tse_config.mac_addr[2] = 0x3C; + g_tse_config.mac_addr[3] = 0x60; + g_tse_config.mac_addr[4] = 0x60; + g_tse_config.mac_addr[5] = 0x60; + + TSE_init(&g_tse, TSEMAC_BASE, &g_tse_config); + + @endcode + + */ + +void TSE_init(tse_instance_t *this_tse, uint32_t base_addr, tse_cfg_t *cfg); + +/** + `TSE_set_tx_callback()` registers the function that the CoreTSE driver + calls when a packet is sent. + + Note: + This function is applicable only for CoreTSE_AHB. + + @param this_tse + The this_tse parameter is a pointer to the tse_instance_t structure holding + all data regarding the CoreTSE instance controlled through this + function call. + + @param tx_complete_handler + The tx_complete_handler parameter is a pointer to the call-back function + that is called when the CoreTSE sends a packet. + + @return + This function does not return a value. + + @example + This example demonstrates the use of the `TSE_set_tx_callback()` function. + The application registers the `tx_complete_callback()` transmit completion + callback function with the CoreTSE driver by a call to `TSE_set_tx_callback()`. + The application dynamically allocates memory for an application-defined packet_t + data structure, builds a packet, and calls send_packet(). + `send_packet()` extracts the pointer to the buffer containing the + data to transmit and its length from the tx_packet data structure and passes + these to `TSE_send_pkt()`. It also passes the pointer to tx_packet as the + p_user_data parameter. The CoreTSE driver calls `tx_complete_callback()` once + the packet is sent. The `tx_complete_callback()` function uses the p_user_data, + which points to tx_packet, to release memory allocated by the application + to store the transmit packet. + + @code + void tx_complete_callback(void * p_user_data); + tse_instance_t g_tse; + + void init(void) + { + TSE_set_tx_callback((&g_tse, tx_complete_callback); + } + + void tx_complete_callback (void * p_user_data) + { + release_packet_memory(p_user_data); + } + + void send_packet(packet_t * tx_packet) + { + TSE_send_pkt(&g_tse, tx_packet->buffer, tx_packet->length, tx_packet); + } + @endcode + */ + +void TSE_set_tx_callback(tse_instance_t *this_tse, tse_transmit_callback_t tx_complete_handler); + +/** + + `TSE_set_rx_callback()` registers the function the CoreTSE driver calls when a + packet is received. + + Note: + This function is applicable only for CoreTSE_AHB. + + @param this_tse + The this_tse parameter is a pointer to the tse_instance_t structure holding + all data regarding the CoreTSE instance controlled through this + function call. + + @param rx_callback + The rx_callback parameter is a pointer to the function that is called + when the packet is received by CoreTSE. + + @example + The example below demonstrates the use of the `TSE_set_rx_callback()` function. + The `init()` function calls the `TSE_set_rx_callback()` function to register + the `rx_callback()` receive callback function with the CoreTSE driver. The + `TSE_receive_pkt()` function is then called to assign the rx_buffer to an + CoreTSE_AHB descriptor for packet reception. + Once a packet is received into the rx_buffer, the CoreTSE driver calls + the rx_callback function. + The `rx_callback()` function calls the `process_rx_packet()` application + function to process the received packet then calls `TSE_receive_pkt()` to + reallocate rx_buffer to receive another packet. + Every time a packet is received the `rx_callback()` function is called to process + the received packet and reallocate rx_buffer for packet reception. + + @code + uint8_t rx_buffer[TSE_MAX_RX_BUF_SIZE]; + + void rx_callback + ( + uint8_t * p_rx_packet, + uint32_t pckt_length, + void * p_user_data + ) + { + process_rx_packet(p_rx_packet, pckt_length); + TSE_receive_pkt(rx_buffer, (void *)0); + } + + void init(void) + { + TSE_set_rx_callback(rx_callback); + TSE_receive_pkt(rx_buffer, (void *)0); + } + @endcode + */ + +void TSE_set_rx_callback(tse_instance_t *this_tse, tse_receive_callback_t rx_callback); + +/** + + The `TSE_set_wol_callback()` function registers the function that is + called by the CoreTSE driver when Wake on LAN (WoL) feature is enabled and + WoL event is detected. The WoL event happens when either a Unicast match frame + or AMD magic packet frame is detected by CoreTSE. The wol_enable + parameter in tse_cfg_t structure decides which type of packets can cause the + WoL event. + + @param this_tse + The this_tse parameter is a pointer to the tse_instance_t structure holding + all data regarding the CoreTSE instance controlled through this + function call. + + @param wol_callback + The wol_callback parameter is a pointer to the function that is called + when Wake on LAN (WoL) feature is enabled and WoL event happens. + + @return + This function does not return a value. + + @example + The example below demonstrates the use of the `TSE_set_wol_callback()` function. + The `init()` function calls the `TSE_set_wol_callback()` function to register the + wol_callback() receive callback function with the CoreTSE driver. The wol_callback + function is called by the CoreTSE driver once a WoL event is detected. + `wol_callback()` can implement the application-specific action on + detecting the WoL event. + + @code + tse_instance_t g_tse; + + void wol_callback + ( + void * p_user_data + ) + { + //Process WoL interrupt here. + } + void init(void) + { + TSE_set_wol_callback(&g_tse, wol_callback); + } + @endcode + */ +void TSE_set_wol_callback(tse_instance_t *this_tse, tse_wol_callback_t wol_callback); + +/** + + The `TSE_send_pkt()` function initiates the transmission of a packet. It places + the buffer containing the packet to send into one of the CoreTSE_AHB's transmit + descriptors. This function is non-blocking. It returns immediately + without waiting for the packet to be sent. The CoreTSE driver indicates that + the packet is sent by calling the transmit completion handler registered by + a call to `TSE_set_tx_callback()`. + + Note: + This function is applicable only for CoreTSE_AHB. + + @param this_tse + The this_tse parameter is a pointer to the tse_instance_t structure holding + all data regarding the CoreTSE_AHB instance controlled through this function + call. + + @param tx_buffer + The tx_buffer parameter is a pointer to the buffer containing the packet to + send. + + @param tx_length + The tx_length parameter specifies the length in bytes of the packet to send. + + @param p_user_data + This parameter is a pointer to an optional, application-defined data + structure. Its usage is left up to the application. It is intended to help the + application manage memory allocated to store packets. The CoreTSE driver + does not make use of this pointer. The CoreTSE driver passes back this + pointer to the application as part of the call to the transmit completion + handler registered by the application. + + @return + This function returns 1 on successfully assigning the packet to a transmit + descriptor. It returns 0 otherwise. + + @example + This example demonstrates the use of the `TSE_send_pkt()` function. + The application registers the `tx_complete_callback()` transmit completion + callback function with the CoreTSE driver by a call to `TSE_set_tx_callback()`. + The application dynamically allocates memory for an application-defined + packet_t data structure builds a packet and calls send_packet(). + `send_packet()` extracts the pointer to the buffer containing the + data to transmit and its length from the tx_packet data structure and passes + these to `TSE_send_pkt()`. + It also passes the pointer to tx_packet as the p_user_data parameter. + The CoreTSE driver call `tx_complete_callback()` once the packet is sent. + The `tx_complete_callback()` function uses the p_user_data, which points to + tx_packet, to release memory allocated by the application to store the transmit + packet. + + @code + void tx_complete_callback(void * p_user_data); + tse_instance_t g_tse; + + void init(void) + { + TSE_set_tx_callback((&g_tse, tx_complete_callback); + } + + void tx_complete_callback (void * p_user_data) + { + release_packet_memory(p_user_data); + } + + void send_packet(packet_t * tx_packet) + { + TSE_send_pkt(&g_tse, tx_packet->buffer, tx_packet->length, tx_packet); + } + @endcode + + */ +uint8_t TSE_send_pkt(tse_instance_t *this_tse, + uint8_t const *tx_buffer, + uint32_t tx_length, + void *p_user_data); + +/** + + `TSE_receive_pkt()` assigns a buffer to one of the CoreTSE_AHB + receive descriptors. The receive buffer specified as a parameter receives one + single packet. The receive buffer is handed back to the + application via a call to the receive callback function, assigned through a + call to `TSE_set_rx_callback()`. `TSE_receive_pkt()` needs to + be called again pointing to the same buffer if more packets are to be + received into this same buffer after the packet is processed by the + application. + + `TSE_receive_pkt()` is non-blocking. It returns immediately + and does not wait for a packet to be received. The application needs to + implement a receive callback function to be notified that a packet is + received. + + The p_user_data parameter can optionally be used to point to a memory + management data structure managed by the application. + + Note: + This function is applicable only for CoreTSE_AHB. + + @param this_tse + The this_tse parameter is a pointer to the tse_instance_t structure holding + all data regarding the CoreTSE_AHB instance controlled through this function + call. + + @param rx_pkt_buffer + The rx_pkt_buffer parameter is a pointer to a memory buffer. It points to + the memory that is assigned to one of the CoreTSE_AHB's receive + descriptors. It must point to a buffer large enough to contain the largest + possible packet. + + @param p_user_data + The p_user_data parameter is intended to help the application manage memory. + Its usage is left up to the application. The CoreTSE driver does not make use + of this pointer. The CoreTSE driver passes this pointer back to the + application as part of the call to the application receive callback + function to help the application associate the received packet with the + memory it allocated before the call to `TSE_receive_pkt()`. + + @return + This function returns 1 on successfully assigning the buffer to a receive + descriptor. It returns 0 otherwise. + + @example + The example below demonstrates the use of `TSE_receive_pkt()` to + handle packet reception using two receive buffers. The `init()` function calls + `TSE_set_rx_callback()` to register the `rx_callback()` receive + callback function with the CoreTSE driver. `TSE_receive_pkt()` + is then called twice to assign rx_buffer_1 and rx_buffer_2 to CoreTSE + descriptors for packet reception. The rx_callback function is called + by the CoreTSE driver once a packet is received into one of the + receive buffers. The `rx_callback()` function calls the `process_rx_packet()` + application function to process the received packet then calls + `TSE_receive_pkt()` to reallocate the receive buffer to receive another packet. + The `rx_callback()` function is called every time a packet is + received to process the received packet and reallocate rx_buffer for packet + reception. + + Note: + The use of the p_user_data parameter to handle the buffer reassignment to the + CoreTSE_AHB as part of the `rx_callback()` function. + This is a simplistic use of p_user_data. + It is more likely that p_user_data would be useful to keep track of a + pointer to a TCP/IP stack packet container data structure dynamically allocated. + In this more complex use case, the first parameter of `TSE_receive_pkt()` would + point to the actual receive buffer and the second parameter would point to a + data structure used to free the receive buffer memory once the packet has + been consumed by the TCP/IP stack. + + @code + uint8_t rx_buffer_1[TSE_MAX_RX_BUF_SIZE]; + uint8_t rx_buffer_2[TSE_MAX_RX_BUF_SIZE]; + tse_instance_t g_tse; + + void rx_callback + ( + uint8_t * p_rx_packet, + uint32_t pckt_length, + void * p_user_data + ) + { + process_rx_packet(p_rx_packet, pckt_length); + TSE_receive_pkt((&g_tse, (uint8_t *)p_user_data, p_user_data); + } + + void init(void) + { + TSE_set_rx_callback((&g_tse, rx_callback); + TSE_receive_pkt(rx_buffer_1, (void *)rx_buffer_1); + TSE_receive_pkt(rx_buffer_2, (void *)rx_buffer_2); + } + @endcode + + */ +uint8_t TSE_receive_pkt(tse_instance_t *this_tse, uint8_t *rx_pkt_buffer, void *p_user_data); + +/** + + `TSE_get_link_status()` retrieves the status of the link from + the Ethernet PHY. It returns the current state of the Ethernet link. + The speed and duplex mode of the link are also returned via the two pointers + passed as parameters - if the link is up. + + This function also adjusts the CoreTSE's internal configuration + if some of the link characteristics have changed since the previous call to + this function. + As such, it is recommended to call this funciton periodically, so that the driver + can automatically adapt to changes in the network link status. + + @param this_tse + The this_tse parameter is a pointer to the tse_instance_t structure holding + all data regarding the CoreTSE instance controlled through this + function call. + + @param speed + The speed parameter is a pointer to a variable of type tse_speed_t where the + current link speed is stored if the link is up. This variable is not + updated if the link is down. This parameter can be set to zero if the caller + does not need to find out the link speed. + + @param fullduplex + The full-duplex parameter is a pointer to an unsigned character where the + current link duplex mode is stored if the link is up. This variable is + not updated if the link is down. + + @return + This function returns 1 if the link is up. It returns 0 if the link is down. + + @example + @code + uint8_t link_up; + tse_speed_t speed; + uint8_t full_duplex + tse_instance_t g_tse; + + link_up = TSE_get_link_status(&g_tse, &speed, &full_duplex); + @endcode + + */ +uint8_t TSE_get_link_status(tse_instance_t *this_tse, tse_speed_t *speed, uint8_t *fullduplex); + +/** + + `TSE_read_stat()` reads the transmit and receive statistics of + the CoreTSE. This function can be used to read one of seventeen receiver + statistics, twenty transmitter statistics and seven frame-type statistics as defined + in the tse_stat_t enumeration. + + @param this_tse + The this_tse parameter is a pointer to the tse_instance_t structure holding + all data regarding the CoreTSE instance controlled through this + function call. + + @param stat + This parameter of type tse_stat_t identifies the statistic that is read. + Refer to the tse_stat_t type definition to see the allowed values. + + @return + This function returns the value of the requested statistic. + + @example + @code + uint32_t tx_pkts_cnt = 0; + tse_instance_t g_tse; + + tx_pkts_cnt = TSE_read_stat(&g_tse, TSE_TX_PKT_CNT); + + @endcode + */ +uint32_t TSE_read_stat(tse_instance_t *this_tse, tse_stat_t stat); + +/** + + `TSE_clear_statistics()` clears all forty-four statistics counter + registers. + + @param this_tse + The this_tse parameter is a pointer to the tse_instance_t structure holding + all data regarding the CoreTSE instance controlled through this + function call. + + @return + This function does not return a value. + + @example + @code + uint32_t tx_pkts_cnt = 0; + + tx_pkts_cnt = TSE_clear_statistics(&g_tse); + + @endcode + */ +void TSE_clear_statistics(tse_instance_t *instance); + +/** + `TSE_read_phy_reg()` reads the Ethernet PHY register specified as + a parameter. It uses the MII management interface to communicate with the + Ethernet PHY. This function is part of the Ethernet PHY drivers provided + alongside the CoreTSE driver. You only need to use this function if writing + your own Ethernet PHY driver. + + @param this_tse + The this_tse parameter is a pointer to the tse_instance_t structure holding + all data regarding the CoreTSE instance controlled through this + function call. + + @param phyaddr + The phyaddr parameter is the 5-bit address of the Ethernet PHY on the MII + management interface. This address is typically defined through Ethernet PHY + hardware configuration signals. Please refer to the Ethernet PHY's datasheet + for details of how this address is assigned. + + @param regaddr + The regaddr parameter is the address of the Ethernet register that is + read. This address is the offset of the register within the Ethernet PHY's + register map. + + @return + This function returns the 16-bit value of the requested register. + + @example + @code + #include -phy.h- + tse_instance_t g_tse; + + uint16_t read_phy_status(uint8_t phy_addr) + { + uint16_t phy_status = TSE_read_phy_reg(&g_tse, phy_addr , MII_BMSR); + return phy_status; + } + @endcode + + */ +uint16_t TSE_read_phy_reg(tse_instance_t *this_tse, uint8_t phyaddr, uint8_t regaddr); + +/** + + `TSE_write_phy_reg()` writes a 16-bit value to the specified + Ethernet PHY register. It uses the MII management interface to communicate + with the Ethernet PHY. This function is part of the Ethernet PHY drivers + provided alongside the CoreTSE driver. You only need to use this function if + writing your own Ethernet PHY driver. + + @param this_tse + The this_tse parameter is a pointer to the tse_instance_t structure holding + all data regarding the CoreTSE instance controlled through this + function call. + + @param phyaddr + The phyaddr parameter is the 5-bit address of the Ethernet PHY on the MII + management interface. This address is typically defined through Ethernet PHY + hardware configuration signals. Please refer to the Ethernet PHY's datasheet + for details of how this address is assigned. + + @param regaddr + The regaddr parameter is the address of the Ethernet register that is + written. This address is the offset of the register within the Ethernet + PHY's register map. + + @param regval + The regval parameter is the 16-bit value that is written into the + specified PHY register. + + @return + This function does not return a value. + + @example + @code + #include "phy.h" + tse_instance_t g_tse; + + void rest_sgmii_phy(void) + { + TSE_write_phy_reg(&g_tse, SGMII_PHY_ADDR, MII_BMCR, 0x8000); + } + @endcode + + */ +void TSE_write_phy_reg(tse_instance_t *this_tse, uint8_t phyaddr, uint8_t regaddr, uint16_t regval); + +/** + + `TSE_isr()` is the top-level interrupt handler function for the + CoreTSE driver. You must call `TSE_isr()` from the system-level + (CoreInterrupt and NVIC level) interrupt handler assigned to the interrupt + triggered by the CoreTSE INTR signal. Your system-level interrupt + handler must also clear the system-level interrupt triggered by the + CoreTSE INTR signal before returning, to prevent a re-assertion + of the same interrupt. + + @param this_tse + The this_tse parameter is a pointer to the tse_instance_t structure holding + all data regarding the CoreTSE instance controlled through this + function call. + + @return + This function does not return a value. + + @example + @code + + tse_instance_t g_tse; + + void FabricIrq0_IRQHandler + ( + void + ) + { + TSE_isr(&g_tse); + } + @endcode + */ +void TSE_isr(tse_instance_t *this_tse); + +/** + + `TSE_set_address_filter()` implements the frame filtering + functionality of the driver. This function specifies the list of + destination MAC addresses of received frames that are passed to the MAC. + This function takes an array of MAC addresses as a parameter and generates the + correct hash table for that list of addresses. It also + manages the setting of the Broadcast-DA, Multicast-DA/Unicast-DA hardware IP + configuration bits based on the value of the MAC addresses passed as a + parameter. + For example, if the list contains one or more unicast addresses then + unicast hash match filtering is enabled. Likewise, multicast hash match + filtering is enabled if the list contains one or more multicast addresses. + It enables broadcast filtering if the broadcast address is included in the + allowed MAC addresses list passed as a parameter. It also enables passing + frames addressed to the local MAC address (perfect unicast match) if the + local MAC address is included in the allowed MAC addresses list. + + Note: + The address filtering choice can also be selected using + `TSE_cfg_struct_def_init()` function. The configuration value returned by this + function can be modified and then passed on to the `TSE_init()` function. If + The `TSE_set_address_filter()` function is used, the original filter setting + chosen at the initialization time gets overwritten. + + @param this_tse + The this_tse parameter is a pointer to the tse_instance_t structure holding + all data regarding the CoreTSE instance controlled through this + function call. + + @param mac_addresses + The mac_addresses parameter is a pointer to the buffer containing the MAC + addresses that are used to generate the MAC address hash table. + + @param nb_addresses + The nb_addresses parameter specifies the number of MAC addresses being + passed in the buffer pointed by the mac_addresses buffer pointer. + + Note: + Each MAC address consists of six octets. Each MAC address must be placed in + the buffer starting with the first octet of the MAC address. + + Note: + Each MAC address consists of six octets and must be placed in the + buffer starting with the first (most significant) octet of the MAC address. + + @return + This function does not return a value. + + @example + + This example demonstrates the use of the `TSE_set_address_filter()` function to + handle frame filtering. The list of MAC addresses passed to + `TSE_set_address_filter()` function includes unicast, multicast, local base + station and broadcast MAC addresses. The `TSE_set_address_filter()` function + sets up the hash-unicast, hash-multicast, broadcast and perfect unicast match + (local base station address) filtering to pass frames with these destination + addresses to the MAC. + + @code + #define TSEMAC_BASE 0x30000000 + tse_instance_t g_tse; + tse_cfg_t g_tse_config; + + uint8_t mac_data[4][6] = {{0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, + {0x43, 0x40, 0x40, 0x40, 0x40, 0x43}, + {0xC0, 0xB1, 0x3C, 0x60, 0x60, 0x60}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; + + g_tse_config.phy_addr = TSE_PHY_ADDRESS_AUTO_DETECT; + g_tse_config.speed_duplex_select = TSE_ANEG_100M_FD; + g_tse_config.mac_addr[0] = 0xC0; + g_tse_config.mac_addr[1] = 0xB1; + g_tse_config.mac_addr[2] = 0x3C; + g_tse_config.mac_addr[3] = 0x60; + g_tse_config.mac_addr[4] = 0x60; + g_tse_config.mac_addr[5] = 0x60; + + TSE_init(&g_tse, TSEMAC_BASE, &g_tse_config); + + TSE_set_address_filter(&g_tse, mac_data[0], 4); + @endcode + */ +void TSE_set_address_filter(tse_instance_t *this_tse, + const uint8_t *mac_addresses, + uint32_t nb_addresses); + +#ifdef __cplusplus +} +#endif + +#endif /* CORE_TSE_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/coretse_regs.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/coretse_regs.h new file mode 100644 index 0000000..b8a4653 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/coretse_regs.h @@ -0,0 +1,757 @@ +/******************************************************************************* + * Copyright 2014 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file coretse_regs.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Register bit offsets and masks definitions for CoreTSE. + * + */ +#ifndef CORE_TSE_REGS_H_ +#define CORE_TSE_REGS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*------------------------------------------------------------------------------ + * MAC Configuration register 2 + */ +#define CFG2_PREAM_LEN 12u + +#define CFG2_BYTE_MASK ((uint32_t)1u << 9) +#define CFG2_NIBBLE_MASK ((uint32_t)1u << 8) + +/*------------------------------------------------------------------------------ + * MAC IPG/IFG register + */ +#define IFG_MINIFGENF 8u +#define IFG_NONBTBIPG 16u + +/*------------------------------------------------------------------------------ + * MAC half-duplex register + */ +#define HALF_DUPLEX_SLOTTIME 0u +#define HALF_DUPLEX_RETX_MAX_OFFSET 12u +#define HALF_DUPLEX_EXCS_DEFER_MASK ((uint32_t)1u << 16) +#define HALF_DUPLEX_NO_BACKOFF_MASK ((uint32_t)1u << 17) +#define HALF_DUPLEX_BACKPRES_NOBACKOFF_MASK ((uint32_t)1u << 18) +#define HALF_DUPLEX_ABEB_ENABLE_MASK ((uint32_t)1u << 19) +#define HALF_DUPLEX_ABEB_TUNC_OFFSET 20u +#define HALF_DUPLEX_SLOTTIME_MASK ((uint32_t)0x2FF) + +/*------------------------------------------------------------------------------ + * MAC MII Management configuration register + */ +#define MII_CLOCK_SELECT_MASK ((uint32_t)7u) +#define MII_PREAM_SUPRESS_MASK ((uint32_t)1u << 4) +#define MII_SCAN_AUTO_INC_MASK ((uint32_t)1u << 5) +#define MII_RESET_MII_MGMT_MASK ((uint32_t)1u << 31) + +/*------------------------------------------------------------------------------ + * DMA descriptor definitions + */ +#define DMA_DESC_EMPTY_FLAG_MASK ((uint32_t)1u << 31u) +#define DMA_DESC_PKT_SIZE_MASK 0xFFFu + +/*------------------------------------------------------------------------------ + * DMA descriptor packet size + */ +#define DMA_PKTCOUNT_MASK ((uint32_t)0xFFu << 16) + +/*------------------------------------------------------------------------------ + * FIFO control registers definitions + */ +#define FIFO_CFG0_DEFVAL 0x0000FF00u +#define FIFO_CFG1_DEFVAL 0x0FFF0000u +#define FIFO_CFG2_DEFVAL 0x04000180u + +#define FIFO_CFG4_DEFVAL 0x0000FFFFu /*Setting this bit means Drop frame*/ +#define FIFO_CFG5_DEFVAL 0x0007EFEFu /*Setting this bit means Don't Care*/ + +/*Changed cfg0 and cfg3 after looking at simulation environment.*/ +#define FIFO_CFG3_DEFVAL 0x0680FFFFu + +/*------------------------------------------------------------------------------ + * Interface control register + */ +#define INF_STATS_MODULE (1u << 2) +#define STATS_COUNTER_CLEAR_MASK (1u << 3) +#define STATS_COUNTER_AUTO_CLEAR_MASK (1u << 4) + +/*------------------------------------------------------------------------------ + * MAC Test register + */ +#define TEST_PAUSE 2u + +/*------------------------------------------------------------------------------ + * These constants correspond to the Misc Control register(0x1D4) of CoreTSE. + These constants are used to fill the Misc Control register depending on the + current link speed. + These register bits correspond to the TSM_CONTROL[0:1] signals which can + in-turn be used to generate the appropriate TX-RX clocks in the Libero Design + depending on the Link Speed. + */ +#define SPEED_10M 2u +#define SPEED_100M 1u +#define SPEED_1000M 0u + +/*------------------------------------------------------------------------------ + * CoreTSE register offset, mask and shift definitions + */ +#define CFG1_REG_OFFSET 0x00u + +#define CFG1_TX_EN_OFFSET 0x00u +#define CFG1_TX_EN_MASK 0x00000001UL +#define CFG1_TX_EN_SHIFT 0u + +#define CFG1_SYNC_TX_EN_OFFSET 0x00u +#define CFG1_SYNC_TX_EN_MASK 0x00000002UL +#define CFG1_SYNC_TX_EN_SHIFT 1u + +#define CFG1_RX_EN_OFFSET 0x00u +#define CFG1_RX_EN_MASK 0x00000004UL +#define CFG1_RX_EN_SHIFT 2u + +#define CFG1_SYNC_RX_EN_OFFSET 0x00u +#define CFG1_SYNC_RX_EN_MASK 0x00000008UL +#define CFG1_SYNC_RX_EN_SHIFT 3u + +#define CFG1_TX_FCTL_OFFSET 0x00u +#define CFG1_TX_FCTL_MASK 0x00000010UL +#define CFG1_TX_FCTL_SHIFT 4u + +#define CFG1_RX_FCTL_OFFSET 0x00u +#define CFG1_RX_FCTL_MASK 0x00000020UL +#define CFG1_RX_FCTL_SHIFT 5u + +#define CFG1_LOOPBACK_OFFSET 0x00u +#define CFG1_LOOPBACK_MASK 0x00000100UL +#define CFG1_LOOPBACK_SHIFT 8u + +#define CFG2_REG_OFFSET 0x04u + +#define CFG2_FDX_OFFSET 0x04u +#define CFG2_FDX_MASK 0x00000001UL +#define CFG2_FDX_SHIFT 0u + +#define CFG2_CRC_EN_OFFSET 0x04u +#define CFG2_CRC_EN_MASK 0x00000002UL +#define CFG2_CRC_EN_SHIFT 1u + +#define CFG2_PAD_CRC_EN_OFFSET 0x04u +#define CFG2_PAD_CRC_EN_MASK 0x00000004UL +#define CFG2_PAD_CRC_EN_SHIFT 2u + +#define CFG2_LEN_CHECK_OFFSET 0x04u +#define CFG2_LEN_CHECK_MASK 0x00000010UL +#define CFG2_LEN_CHECK_SHIFT 4u + +#define CFG2_HUGE_FRAME_EN_OFFSET 0x04u +#define CFG2_HUGE_FRAME_EN_MASK 0x00000020UL +#define CFG2_HUGE_FRAME_EN_SHIFT 5u + +#define CFG2_IF_MODE_OFFSET 0x04u +#define CFG2_IF_MODE_MASK 0x00000300UL +#define CFG2_IF_MODE_SHIFT 8u + +#define CFG2_PREAM_LEN_OFFSET 0x04u +#define CFG2_PREAM_LEN_MASK 0x0000F000UL +#define CFG2_PREAM_LEN_SHIFT 12u + +#define IFG_REG_OFFSET 0x08u + +#define IFG_BTBIPG_OFFSET 0x08u +#define IFG_BTBIPG_MASK 0x0000003FUL +#define IFG_BTBIPG_SHIFT 0u + +#define IFG_MINIFGENF_OFFSET 0x08u +#define IFG_MINIFGENF_MASK 0x0000FF00UL +#define IFG_MINIFGENF_SHIFT 8u + +#define IFG_NONBTBIPG_P2_OFFSET 0x08u +#define IFG_NONBTBIPG_P2_MASK 0x007F0000UL +#define IFG_NONBTBIPG_P2_SHIFT 16u + +#define IFG_NONBTBIPG_P1_OFFSET 0x08u +#define IFG_NONBTBIPG_P1_MASK 0x7F000000UL +#define IFG_NONBTBIPG_P1_SHIFT 24u + +#define HDX_REG_OFFSET 0x0Cu + +#define HDX_COL_WIN_OFFSET 0x0Cu +#define HDX_COL_WIN_MASK 0x000003FFUL +#define HDX_COL_WIN_SHIFT 0u + +#define HDX_RETX_MAX_OFFSET 0x0Cu +#define HDX_RETX_MAX_MASK 0x0000F000UL +#define HDX_RETX_MAX_SHIFT 12u + +#define HDX_EX_DEF_OFFSET 0x0Cu +#define HDX_EX_DEF_MASK 0x00010000UL +#define HDX_EX_DEF_SHIFT 16u + +#define HDX_NOBACKOF_OFFSET 0x0Cu +#define HDX_NOBACKOF_MASK 0x00020000UL +#define HDX_NOBACKOF_SHIFT 17u + +#define HDX_BP_NOBACKOF_OFFSET 0x0Cu +#define HDX_BP_NOBACKOF_MASK 0x00040000UL +#define HDX_BP_NOBACKOF_SHIFT 18u + +#define HDX_ALTB_EXP_BACK_EN_OFFSET 0x0Cu +#define HDX_ALTB_EXP_BACK_EN_MASK 0x00080000UL +#define HDX_ALTB_EXP_BACK_EN_SHIFT 19u + +#define HDX_ALTB_EXP_BACK_TRUN_OFFSET 0x0Cu +#define HDX_ALTB_EXP_BACK_TRUN_MASK 0x00F00000UL +#define HDX_ALTB_EXP_BACK_TRUN_SHIFT 20u + +#define FREMLEN_REG_OFFSET 0x10u + +#define FREMLEN_MAX_FL_OFFSET 0x10u +#define FREMLEN_MAX_FL_MASK 0x0000FFFFUL +#define FREMLEN_MAX_FL_SHIFT 0u + +#define CFEP_REG_OFFSET 0x14u + +#define CFEP_CFEP_OFFSET 0x14u +#define CFEP_CFEP_MASK 0x0000FFFFUL +#define CFEP_CFEP_SHIFT 0u + +#define CFP_REG_OFFSET 0x18u + +#define CFP_CFPT_OFFSET 0x18u +#define CFP_CFPT_MASK 0x0000FFFFUL +#define CFP_CFPT_SHIFT 0u + +#define TEST_REG_OFFSET 0x1Cu + +#define MIIMGMT_REG_OFFSET 0x20u + +#define MIIMGMT_CLK_SEL_OFFSET 0x20u +#define MIIMGMT_CLK_SEL_MASK 0x00000007UL +#define MIIMGMT_CLK_SEL_SHIFT 0u + +#define MIIMGMT_PREAM_SUPRESS_OFFSET 0x20u +#define MIIMGMT_PREAM_SUPRESS_MASK 0x00000010UL +#define MIIMGMT_PREAM_SUPRESS_SHIFT 4u + +#define MIIMGMT_SCAN_AUTO_INC_OFFSET 0x20u +#define MIIMGMT_SCAN_AUTO_INC_MASK 0x00000020UL +#define MIIMGMT_SCAN_AUTO_INC_SHIFT 5u + +#define MIIMGMT_RESET_MII_MGMT_OFFSET 0x20u +#define MIIMGMT_RESET_MII_MGMT_MASK 0x80000000UL +#define MIIMGMT_RESET_MII_MGMT_SHIFT 31u + +#define MIIMCMD_REG_OFFSET 0x24u + +#define MIIMCMD_READ_CYCLE_OFFSET 0x24u +#define MIIMCMD_READ_CYCLE_MASK 0x00000001UL +#define MIIMCMD_READ_CYCLE_SHIFT 0u + +#define MIIMCMD_SCAN_CYCLE_OFFSET 0x24u +#define MIIMCMD_SCAN_CYCLE_MASK 0x00000002UL +#define MIIMCMD_SCAN_CYCLE_SHIFT 1u + +#define MIIMADDR_REG_OFFSET 0x28u + +#define MIIMADDR_REG_ADDR_OFFSET 0x28u +#define MIIMADDR_REG_ADDR_MASK 0x0000001FUL +#define MIIMADDR_REG_ADDR_SHIFT 0u + +#define MIIMADDR_PHY_ADDR_OFFSET 0x28u +#define MIIMADDR_PHY_ADDR_MASK 0x00001F00UL +#define MIIMADDR_PHY_ADDR_SHIFT 8u + +#define MIIMCTRL_REG_OFFSET 0x2Cu + +#define MIIMCTRL_PHY_CTRL_OFFSET 0x2Cu +#define MIIMCTRL_PHY_CTRL_MASK 0x0000FFFFUL +#define MIIMCTRL_PHY_CTRL_SHIFT 0u + +#define MIIMSTATUS_REG_OFFSET 0x30u + +#define MIIMSTATUS_PHY_STATUS_OFFSET 0x30u +#define MIIMSTATUS_PHY_STATUS_MASK 0x0000FFFFUL +#define MIIMSTATUS_PHY_STATUS_SHIFT 0u + +#define MIIMIND_REG_OFFSET 0x34u + +#define MIIMIND_BUSY_OFFSET 0x34u +#define MIIMIND_BUSY_MASK 0x00000001UL +#define MIIMIND_BUSY_SHIFT 0u + +#define MIIMIND_SCANNING_OFFSET 0x34u +#define MIIMIND_SCANNING_MASK 0x00000002UL +#define MIIMIND_SCANNING_SHIFT 1u + +#define MIIMIND_NV_OFFSET 0x34u +#define MIIMIND_NV_MASK 0x00000004UL +#define MIIMIND_NV_SHIFT 2u + +#define IFC_REG_OFFSET 0x38u + +#define IFC_STATS_EN_OFFSET 0x38u +#define IFC_STATS_EN_MASK 0x00000004UL +#define IFC_STATS_EN_SHIFT 2u + +#define IFC_STATS_CLR_ALL_OFFSET 0x38u +#define IFC_STATS_CLR_ALL_MASK 0x00000008UL +#define IFC_STATS_CLR_ALL_SHIFT 3u + +#define IFC_STATS_AUTO_CLR_EN_OFFSET 0x38u +#define IFC_STATS_AUTO_CLR_EN_MASK 0x00000010UL +#define IFC_STATS_AUTO_CLR_EN_SHIFT 4u + +#define IFC_MCXWOL_ST_CLR_OFFSET 0x38u +#define IFC_MCXWOL_ST_CLR_MASK 0x00000020UL +#define IFC_MCXWOL_ST_CLR_SHIFT 5u + +#define IFC_MCXWOL_MAGIC_EN_OFFSET 0x38u +#define IFC_MCXWOL_MAGIC_EN_MASK 0x00000040UL +#define IFC_MCXWOL_MAGIC_EN_SHIFT 6u + +#define IFC_MCXWOL_UMATCH_EN_OFFSET 0x38u +#define IFC_MCXWOL_UMATCH_EN_MASK 0x00000080UL +#define IFC_MCXWOL_UMATCH_EN_SHIFT 7u + +#define IFS_REG_OFFSET 0x3Cu + +#define IFS_LINK_FL_OFFSET 0x3Cu +#define IFS_LINK_FL_MASK 0x00000008UL +#define IFS_LINK_FL_SHIFT 3u + +#define IFS_EXC_DEF_OFFSET 0x3Cu +#define IFS_EXC_DEF_MASK 0x00000200UL +#define IFS_EXC_DEF_SHIFT 9u + +#define IFS_WOL_DET_OFFSET 0x3Cu +#define IFS_WOL_DET_MASK 0x00000400UL +#define IFS_WOL_DET_SHIFT 10u + +#define STADDRL_REG_OFFSET 0x40u + +#define STADDRH_REG_OFFSET 0x44u + +#define FIFOCFG0_REG_OFFSET 0x48u + +#define FIFOCFG0_WMM_RST_OFFSET 0x48u +#define FIFOCFG0_WMM_RST_MASK 0x00000001UL +#define FIFOCFG0_WMM_RST_SHIFT 0u + +#define FIFOCFG0_RSYS_RST_OFFSET 0x48u +#define FIFOCFG0_RSYS_RST_MASK 0x00000002UL +#define FIFOCFG0_RSYS_RST_SHIFT 1u + +#define FIFOCFG0_RFAB_RST_OFFSET 0x48u +#define FIFOCFG0_RFAB_RST_MASK 0x00000004UL +#define FIFOCFG0_RFAB_RST_SHIFT 2u + +#define FIFOCFG0_TSYS_RST_OFFSET 0x48u +#define FIFOCFG0_TSYS_RST_MASK 0x00000008UL +#define FIFOCFG0_TSYS_RST_SHIFT 3u + +#define FIFOCFG0_TFAB_RST_OFFSET 0x48u +#define FIFOCFG0_TFAB_RST_MASK 0x00000010UL +#define FIFOCFG0_TFAB_RST_SHIFT 4u + +#define FIFOCFG0_WMM_EN_OFFSET 0x48u +#define FIFOCFG0_WMM_EN_MASK 0x00000100UL +#define FIFOCFG0_WMM_EN_SHIFT 8u + +#define FIFOCFG0_RSYS_EN_OFFSET 0x48u +#define FIFOCFG0_RSYS_EN_MASK 0x00000200UL +#define FIFOCFG0_RSYS_EN_SHIFT 9u + +#define FIFOCFG0_RFAB_EN_OFFSET 0x48u +#define FIFOCFG0_RFAB_EN_MASK 0x00000400UL +#define FIFOCFG0_RFAB_EN_SHIFT 10u + +#define FIFOCFG0_TSYS_EN_OFFSET 0x48u +#define FIFOCFG0_TSYS_EN_MASK 0x00000800UL +#define FIFOCFG0_TSYS_EN_SHIFT 11u + +#define FIFOCFG0_TFAB_EN_OFFSET 0x48u +#define FIFOCFG0_TFAB_EN_MASK 0x00001000UL +#define FIFOCFG0_TFAB_EN_SHIFT 12u + +#define FIFOCFG0_WMM_RPLY_OFFSET 0x48u +#define FIFOCFG0_WMM_RPLY_MASK 0x00010000UL +#define FIFOCFG0_WMM_RPLY_SHIFT 16u + +#define FIFOCFG0_RSYS_RPLY_OFFSET 0x48u +#define FIFOCFG0_RSYS_RPLY_MASK 0x00020000UL +#define FIFOCFG0_RSYS_RPLY_SHIFT 17u + +#define FIFOCFG0_RFAB_RPLY_OFFSET 0x48u +#define FIFOCFG0_RFAB_RPLY_MASK 0x00040000UL +#define FIFOCFG0_RFAB_RPLY_SHIFT 18u + +#define FIFOCFG0_TSYS_RPLY_OFFSET 0x48u +#define FIFOCFG0_TSYS_RPLY_MASK 0x00080000UL +#define FIFOCFG0_TSYS_RPLY_SHIFT 19u + +#define FIFOCFG0_TFAB_RPLY_OFFSET 0x48u +#define FIFOCFG0_TFAB_RPLY_MASK 0x00100000UL +#define FIFOCFG0_TFAB_RPLY_SHIFT 20u + +#define FIFOCFG1_REG_OFFSET 0x4Cu + +#define FIFOCFG1_CFGXOFFRTX_OFFSET 0x4Cu +#define FIFOCFG1_CFGXOFFRTX_MASK 0x0000FFFFUL +#define FIFOCFG1_CFGXOFFRTX_SHIFT 0u + +#define FIFOCFG1_CFGSRTH_OFFSET 0x4Cu +#define FIFOCFG1_CFGSRTH_MASK 0x0FFF0000UL +#define FIFOCFG1_CFGSRTH_SHIFT 16u + +#define FIFOCFG2_REG_OFFSET 0x50u + +#define FIFOCFG2_CFGLWM_OFFSET 0x50u +#define FIFOCFG2_CFGLWM_MASK 0x00001FFFUL +#define FIFOCFG2_CFGLWM_SHIFT 0u + +#define FIFOCFG2_CFGHWM_OFFSET 0x50u +#define FIFOCFG2_CFGHWM_MASK 0x1FFF0000UL +#define FIFOCFG2_CFGHWM_SHIFT 16u + +#define FIFOCFG3_REG_OFFSET 0x54u + +#define FIFOCFG3_CFGFTTH_OFFSET 0x54u +#define FIFOCFG3_CFGFTTH_MASK 0x00000FFFUL +#define FIFOCFG3_CFGFTTH_SHIFT 0u + +#define FIFOCFG3_CFGHWMFT_OFFSET 0x54u +#define FIFOCFG3_CFGHWMFT_MASK 0x0FFF0000UL +#define FIFOCFG3_CFGHWMFT_SHIFT 16u + +#define FIFOCFG4_REG_OFFSET 0x58u + +#define FIFOCFG4_HSTFLTRFRM_OFFSET 0x58u +#define FIFOCFG4_HSTFLTRFRM_MASK 0x0003FFFFUL +#define FIFOCFG4_HSTFLTRFRM_SHIFT 0u + +#define FIFOCFG5_REG_OFFSET 0x5Cu + +#define FIFOCFG5_HSTFLTRFRMDC_OFFSET 0x5Cu +#define FIFOCFG5_HSTFLTRFRMDC_MASK 0x0003FFFFUL +#define FIFOCFG5_HSTFLTRFRMDC_SHIFT 0u + +#define FIFOCFG5_HSTDRPLT64_OFFSET 0x5Cu +#define FIFOCFG5_HSTDRPLT64_MASK 0x00040000UL +#define FIFOCFG5_HSTDRPLT64_SHIFT 18u + +#define FIFOCFG5_CFGBYTEMODE_OFFSET 0x5Cu +#define FIFOCFG5_CFGBYTEMODE_MASK 0x00080000UL +#define FIFOCFG5_CFGBYTEMODE_SHIFT 19u + +#define FIFOCFG5_HSTSRFULLCLR_OFFSET 0x5Cu +#define FIFOCFG5_HSTSRFULLCLR_MASK 0x00100000UL +#define FIFOCFG5_HSTSRFULLCLR_SHIFT 20u + +#define FIFOCFG5_HSFULL_OFFSET 0x5Cu +#define FIFOCFG5_HSFULL_MASK 0x00200000UL +#define FIFOCFG5_HSFULL_SHIFT 21u + +#define FIFOCFG5_CFGHDPLX_OFFSET 0x5Cu +#define FIFOCFG5_CFGHDPLX_MASK 0x00400000UL +#define FIFOCFG5_CFGHDPLX_SHIFT 22u + +#define FIFORAM0_REG_OFFSET 0x60u + +#define FIFORAM0_HSTTRAMWADX_OFFSET 0x60u +#define FIFORAM0_HSTTRAMWADX_MASK 0x00001FFFUL +#define FIFORAM0_HSTTRAMWADX_SHIFT 0u + +#define FIFORAM0_HSTTRAMWDAT_OFFSET 0x60u +#define FIFORAM0_HSTTRAMWDAT_MASK 0x00FF0000UL +#define FIFORAM0_HSTTRAMWDAT_SHIFT 16u + +#define FIFORAM0_HSTTRAMWACK_OFFSET 0x60u +#define FIFORAM0_HSTTRAMWACK_MASK 0x40000000UL +#define FIFORAM0_HSTTRAMWACK_SHIFT 30u + +#define FIFORAM0_HSTTRAMWREQ_OFFSET 0x60u +#define FIFORAM0_HSTTRAMWREQ_MASK 0x80000000UL +#define FIFORAM0_HSTTRAMWREQ_SHIFT 31u + +#define FIFORAM1_REG_OFFSET 0x64u + +#define FIFORAM2_REG_OFFSET 0x68u + +#define FIFORAM2_HSTTRAMRADX_OFFSET 0x68u +#define FIFORAM2_HSTTRAMRADX_MASK 0x00001FFFUL +#define FIFORAM2_HSTTRAMRADX_SHIFT 0u + +#define FIFORAM2_HSTTRAMRDAT_OFFSET 0x68u +#define FIFORAM2_HSTTRAMRDAT_MASK 0x00FF0000UL +#define FIFORAM2_HSTTRAMRDAT_SHIFT 16u + +#define FIFORAM2_HSTTRAMRACK_OFFSET 0x68u +#define FIFORAM2_HSTTRAMRACK_MASK 0x40000000UL +#define FIFORAM2_HSTTRAMRACK_SHIFT 30u + +#define FIFORAM2_HSTTRAMRREQ_OFFSET 0x68u +#define FIFORAM2_HSTTRAMRREQ_MASK 0x80000000UL +#define FIFORAM2_HSTTRAMRREQ_SHIFT 31u + +#define FIFORAM3_REG_OFFSET 0x6Cu + +#define FIFORAM4_REG_OFFSET 0x70u + +#define FIFORAM4_HSTTRAMWADX_OFFSET 0x70u +#define FIFORAM4_HSTTRAMWADX_MASK 0x00003FFFUL +#define FIFORAM4_HSTTRAMWADX_SHIFT 0u + +#define FIFORAM4_HSTRRAMWDAT_OFFSET 0x70u +#define FIFORAM4_HSTRRAMWDAT_MASK 0x00FF0000UL +#define FIFORAM4_HSTRRAMWDAT_SHIFT 16u + +#define FIFORAM4_HSTRRAMWACK_OFFSET 0x70u +#define FIFORAM4_HSTRRAMWACK_MASK 0x40000000UL +#define FIFORAM4_HSTRRAMWACK_SHIFT 30u + +#define FIFORAM4_HSTRRAMWREQ_OFFSET 0x70u +#define FIFORAM4_HSTRRAMWREQ_MASK 0x80000000UL +#define FIFORAM4_HSTRRAMWREQ_SHIFT 31u + +#define FIFORAM5_REG_OFFSET 0x74u + +#define FIFORAM6_REG_OFFSET 0x78u + +#define FIFORAM6_HSTRRAMRADX_OFFSET 0x78u +#define FIFORAM6_HSTRRAMRADX_MASK 0x00003FFFUL +#define FIFORAM6_HSTRRAMRADX_SHIFT 0u + +#define FIFORAM6_HSTRRAMRDAT_OFFSET 0x78u +#define FIFORAM6_HSTRRAMRDAT_MASK 0x00FF0000UL +#define FIFORAM6_HSTRRAMRDAT_SHIFT 16u + +#define FIFORAM6_HSTRRAMRACK_OFFSET 0x78u +#define FIFORAM6_HSTRRAMRACK_MASK 0x40000000UL +#define FIFORAM6_HSTRRAMRACK_SHIFT 30u + +#define FIFORAM6_HSTRRAMRREQ_OFFSET 0x78u +#define FIFORAM6_HSTRRAMRREQ_MASK 0x80000000UL +#define FIFORAM6_HSTRRAMRREQ_SHIFT 31u + +#define FIFORAM7_REG_OFFSET 0x7Cu + +#define TR64_REG_OFFSET 0x80u +#define TR127_REG_OFFSET 0x84u +#define TR255_REG_OFFSET 0x88u +#define TR511_REG_OFFSET 0x8Cu +#define TR1K_REG_OFFSET 0x90u +#define TRMAX_REG_OFFSET 0x94u +#define TRMGV_REG_OFFSET 0x98u +#define RBYT_REG_OFFSET 0x9Cu +#define RPKT_REG_OFFSET 0xA0u +#define RFCS_REG_OFFSET 0xA4u +#define RMCA_REG_OFFSET 0xA8u +#define RBCA_REG_OFFSET 0xACu +#define RXCF_REG_OFFSET 0xB0u +#define RXPF_REG_OFFSET 0xB4u +#define RXUO_REG_OFFSET 0xB8u /*Unknown Opco*/ +#define RALN_REG_OFFSET 0xBCu /*Unknown Err*/ +#define RFLR_REG_OFFSET 0xC0u +#define RCDE_REG_OFFSET 0xC4u +#define RCSE_REG_OFFSET 0xC8u +#define RUND_REG_OFFSET 0xCCu +#define ROVR_REG_OFFSET 0xD0u +#define RFRG_REG_OFFSET 0xD4u +#define RJBR_REG_OFFSET 0xD8u +#define RDRP_REG_OFFSET 0xDCu +#define TBYT_REG_OFFSET 0xE0u +#define TPKT_REG_OFFSET 0xE4u +#define TMCA_REG_OFFSET 0xE8u +#define TBCA_REG_OFFSET 0xECu +#define TXPF_REG_OFFSET 0xF0u +#define TDFR_REG_OFFSET 0xF4u +#define TEDF_REG_OFFSET 0xF8u +#define TSCL_REG_OFFSET 0xFCu +#define TMCL_REG_OFFSET 0x100u +#define TLCL_REG_OFFSET 0x104u +#define TXCL_REG_OFFSET 0x108u +#define TNCL_REG_OFFSET 0x10Cu +#define TPFH_REG_OFFSET 0x110u /*0x110 -- Not used but defined for continuity*/ +#define TDRP_REG_OFFSET 0x114u +#define TJBR_REG_OFFSET 0x118u +#define TFCS_REG_OFFSET 0x11Cu +#define TXCF_REG_OFFSET 0x120u +#define TOVR_REG_OFFSET 0x124u +#define TUND_REG_OFFSET 0x128u +#define TFRG_REG_OFFSET 0x12Cu + +#define CAR1_REG_OFFSET 0x130u +#define CAR2_REG_OFFSET 0x134u +#define CAM1_REG_OFFSET 0x138u +#define CAM2_REG_OFFSET 0x13Cu + +#define DMATXCTRL_REG_OFFSET 0x180u + +#define DMATXCTRL_TX_EN_OFFSET 0x180u +#define DMATXCTRL_TX_EN_MASK 0x00000001UL +#define DMATXCTRL_TX_EN_SHIFT 0u + +#define DMATXCTRL_TXIRQ_COAL_CNT_OFFSET 0x180u +#define DMATXCTRL_TXIRQ_COAL_CNT_MASK 0x0000FF00UL +#define DMATXCTRL_TXIRQ_COAL_CNT_SHIFT 8u + +#define DMATXDESC_REG_OFFSET 0x184u + +#define DMATXSTATUS_REG_OFFSET 0x188u + +#define DMATXSTATUS_TXPKT_SENT_OFFSET 0x188u +#define DMATXSTATUS_TXPKT_SENT_MASK 0x00000001UL +#define DMATXSTATUS_TXPKT_SENT_SHIFT 0u + +#define DMATXSTATUS_TXPKT_UR_OFFSET 0x188u +#define DMATXSTATUS_TXPKT_UR_MASK 0x00000002UL +#define DMATXSTATUS_TXPKT_UR_SHIFT 1u + +#define DMATXSTATUS_BUSERR_OFFSET 0x188u +#define DMATXSTATUS_BUSERR_MASK 0x00000008UL +#define DMATXSTATUS_BUSERR_SHIFT 3u + +#define DMATXSTATUS_TXPKT_CNT_OFFSET 0x188u +#define DMATXSTATUS_TXPKT_CNT_MASK 0x00FF0000UL +#define DMATXSTATUS_TXPKT_CNT_SHIFT 16u + +#define DMARXCTRL_REG_OFFSET 0x18Cu + +#define DMARXCTRL_RX_EN_OFFSET 0x18Cu +#define DMARXCTRL_RX_EN_MASK 0x00000001UL +#define DMARXCTRL_RX_EN_SHIFT 0u + +#define DMARXCTRL_RXIRQ_COAL_CNT_OFFSET 0x18Cu +#define DMARXCTRL_RXIRQ_COAL_CNT_MASK 0x0000FF00UL +#define DMARXCTRL_RXIRQ_COAL_CNT_SHIFT 8u + +#define DMARXDESC_REG_OFFSET 0x190u + +#define DMARXSTATUS_REG_OFFSET 0x194u + +#define DMARXSTATUS_RXPKT_RCVD_OFFSET 0x194u +#define DMARXSTATUS_RXPKT_RCVD_MASK 0x00000001UL +#define DMARXSTATUS_RXPKT_RCVD_SHIFT 0u + +#define DMARXSTATUS_RXPKT_OVR_OFFSET 0x194u +#define DMARXSTATUS_RXPKT_OVR_MASK 0x00000004UL +#define DMARXSTATUS_RXPKT_OVR_SHIFT 2u + +#define DMARXSTATUS_BUSERR_OFFSET 0x194u +#define DMARXSTATUS_BUSERR_MASK 0x00000008UL +#define DMARXSTATUS_BUSERR_SHIFT 3u + +#define DMARXSTATUS_RXPKT_CNT_OFFSET 0x194u +#define DMARXSTATUS_RXPKT_CNT_MASK 0x00FF0000UL +#define DMARXSTATUS_RXPKT_CNT_SHIFT 16u + +#define DMAINTRMASK_REG_OFFSET 0x198u + +#define DMAINTRMASK_TXPKT_SENT_OFFSET 0x198u +#define DMAINTRMASK_TXPKT_SENT_MASK 0x00000001UL +#define DMAINTRMASK_TXPKT_SENT_SHIFT 0u + +#define DMAINTRMASK_TX_UR_OFFSET 0x198u +#define DMAINTRMASK_TX_UR_MASK 0x00000002UL +#define DMAINTRMASK_TX_UR_SHIFT 1u + +#define DMAINTRMASK_TX_BUSERR_OFFSET 0x198u +#define DMAINTRMASK_TX_BUSERR_MASK 0x00000008UL +#define DMAINTRMASK_TX_BUSERR_SHIFT 3u + +#define DMAINTRMASK_RXPKT_RCVD_OFFSET 0x198u +#define DMAINTRMASK_RXPKT_RCVD_MASK 0x00000010UL +#define DMAINTRMASK_RXPKT_RCVD_SHIFT 4u + +#define DMAINTRMASK_RXPKT_OVR_OFFSET 0x198u +#define DMAINTRMASK_RXPKT_OVR_MASK 0x00000040UL +#define DMAINTRMASK_RXPKT_OVR_SHIFT 6u + +#define DMAINTRMASK_RX_BUSERR_OFFSET 0x198u +#define DMAINTRMASK_RX_BUSERR_MASK 0x00000080UL +#define DMAINTRMASK_RX_BUSERR_SHIFT 7u + +#define DMAINTRMASK_TXIRQ_COAL_OFFSET 0x198u +#define DMAINTRMASK_TXIRQ_COAL_MASK 0x00000100UL +#define DMAINTRMASK_TXIRQ_COAL_SHIFT 8u + +#define DMAINTRMASK_RXIRQ_COAL_OFFSET 0x198u +#define DMAINTRMASK_RXIRQ_COAL_MASK 0x00000200UL +#define DMAINTRMASK_RXIRQ_COAL_SHIFT 9u + +#define DMAINTR_REG_OFFSET 0x19Cu + +#define DMAINTR_TXPKT_SENT_OFFSET 0x19Cu +#define DMAINTR_TXPKT_SENT_MASK 0x00000001UL +#define DMAINTR_TXPKT_SENT_SHIFT 0u + +#define DMAINTR_TX_UR_OFFSET 0x19Cu +#define DMAINTR_TX_UR_MASK 0x00000002UL +#define DMAINTR_TX_UR_SHIFT 1u + +#define DMAINTR_TX_BUSERR_OFFSET 0x19Cu +#define DMAINTR_TX_BUSERR_MASK 0x00000008UL +#define DMAINTR_TX_BUSERR_SHIFT 3u + +#define DMAINTR_RXPKT_RCVD_OFFSET 0x19Cu +#define DMAINTR_RXPKT_RCVD_MASK 0x00000010UL +#define DMAINTR_RXPKT_RCVD_SHIFT 4u + +#define DMAINTR_RXPKT_OVR_OFFSET 0x19Cu +#define DMAINTR_RXPKT_OVR_MASK 0x00000040UL +#define DMAINTR_RXPKT_OVR_SHIFT 6u + +#define DMAINTR_RX_BUSERR_OFFSET 0x19Cu +#define DMAINTR_RX_BUSERR_MASK 0x00000080UL +#define DMAINTR_RX_BUSERR_SHIFT 7u + +#define DMAINTR_TXIRQ_COAL_OFFSET 0x19Cu +#define DMAINTR_TXIRQ_COAL_MASK 0x00000100UL +#define DMAINTR_TXIRQ_COAL_SHIFT 8u + +#define DMAINTR_RXIRQ_COAL_OFFSET 0x19Cu +#define DMAINTR_RXIRQ_COAL_MASK 0x00000200UL +#define DMAINTR_RXIRQ_COAL_SHIFT 9u + +#define FPC_REG_OFFSET 0x1C0u + +#define FPC_BROADCAST_OFFSET 0x1C0u +#define FPC_BROADCAST_MASK 0x00000001UL +#define FPC_BROADCAST_SHIFT 0u + +#define FPC_MULTICAST_OFFSET 0x1C0u +#define FPC_MULTICAST_MASK 0x00000002UL +#define FPC_MULTICAST_SHIFT 1u + +#define FPC_UNICAST_OFFSET 0x1C0u +#define FPC_UNICAST_MASK 0x00000004UL +#define FPC_UNICAST_SHIFT 2u + +#define FPC_PROMISCUOUS_OFFSET 0x1C0u +#define FPC_PROMISCUOUS_MASK 0x00000008UL +#define FPC_PROMISCUOUS_SHIFT 3u + +#define FPC_HASH_UNICAST_OFFSET 0x1C0u +#define FPC_HASH_UNICAST_MASK 0x00000010UL +#define FPC_HASH_UNICAST_SHIFT 4u + +#define FPC_HASH_MULTICAST_OFFSET 0x1C0u +#define FPC_HASH_MULTICAST_MASK 0x00000020UL +#define FPC_HASH_MULTICAST_SHIFT 5u + +#define HASHTAB0_REG_OFFSET 0x1C4u +#define HASHTAB1_REG_OFFSET 0x1C8u +#define HASHTAB2_REG_OFFSET 0x1CCu +#define HASHTAB3_REG_OFFSET 0x1D0u +#define MISCC_REG_OFFSET 0x1D4u + +#ifdef __cplusplus +} +#endif + +#endif /* CORE_TSE_REGS_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/coretse_types.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/coretse_types.h new file mode 100644 index 0000000..9b7a418 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/coretse_types.h @@ -0,0 +1,643 @@ +/** + * Copyright 2014 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * CoreTSE 10/100/1000 Mbps Ethernet MAC bare metal software driver public API. + * + * @file coretse_types.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreTSE prototypes + * + */ +#ifndef CORE_TSE_TYPES_H_ +#define CORE_TSE_TYPES_H_ + +#include "hal/cpu_types.h" +#include "coretse_user_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + This type definition provides various MAC interface speeds supported by + CoreTSE IP. + */ +typedef enum tse_speed +{ + TSE_MAC10MBPS = 0x00, + TSE_MAC100MBPS = 0x01, + TSE_MAC1000MBPS = 0x02, + TSE_INVALID_SPEED = 0x03 +} tse_speed_t; + +/** + This type definition provides various transmit and receive statistics supported by this driver. + The desired statistics value to be read must be passed to the `TSE_read_stat()` function. + The width of returned statistic value is mentioned in the comment against the statistic. + */ +typedef enum tse_stat +{ + TSE_FRAME_CNT_64, /**< 18-bit */ + TSE_FRAME_CNT_127, /**< 18-bit */ + TSE_FRAME_CNT_255, /**< 18-bit */ + TSE_FRAME_CNT_511, /**< 18-bit */ + TSE_FRAME_CNT_1K, /**< 18-bit */ + TSE_FRAME_CNT_MAX, /**< 18-bit */ + TSE_FRAME_CNT_VLAN, /**< 18-bit */ + TSE_RX_BYTE_CNT, /**< 24-bit */ + TSE_RX_PKT_CNT, /**< 18-bit */ + TSE_RX_FCS_ERR_CNT, /**< 12-bit */ + TSE_RX_MULTICAST_PKT_CNT, /**< 18-bit */ + TSE_RX_BROADCAST_PKT_CNT, /**< 22-bit */ + TSE_RX_CTRL_PKT_CNT, /**< 18-bit */ + TSE_RX_PAUSE_PKT_CNT, /**< 12-bit */ + TSE_RX_UNKNOWN_OPCODE_CNT, /**< 12-bit */ + TSE_RX_ALIGN_ERR_CNT, /**< 12-bit */ + TSE_RX_FRAMELENGTH_ERR_CNT, /**< 16-bit */ + TSE_RX_CODE_ERR_CNT, /**< 12-bit */ + TSE_RX_CS_ERR_CNT, /**< 12-bit */ + TSE_RX_UNDERSIZE_PKT_CNT, /**< 12-bit */ + TSE_RX_OVERSIZE_PKT_CNT, /**< 12-bit */ + TSE_RX_FRAGMENT_CNT, /**< 12-bit */ + TSE_RX_JABBER_CNT, /**< 12-bit */ + TSE_RX_DROP_CNT, /**< 12-bit */ + TSE_TX_BYTE_CNT, /**< 24-bit */ + TSE_TX_PKT_CNT, /**< 18-bit */ + TSE_TX_MULTICAST_PKT_CNT, /**< 18-bit */ + TSE_TX_BROADCAST_PKT_CNT, /**< 18-bit */ + TSE_TX_PAUSE_PKT_CNT, /**< 12-bit */ + TSE_TX_DEFFERAL_PKT_CNT, /**< 12-bit */ + TSE_TX_EXCS_DEFFERAL_PKT_CNT, /**< 12-bit */ + TSE_TX_SINGLE_COLL_PKT_CNT, /**< 12-bit */ + TSE_TX_MULTI_COLL_PKT_CNT, /**< 12-bit */ + TSE_TX_LATE_COLL_PKT_CNT, /**< 12-bit */ + TSE_TX_EXCSS_COLL_PKT_CNT, /**< 12-bit */ + TSE_TX_TOTAL_COLL_PKT_CNT, /**< 13-bit */ + TSE_TX_PAUSE_HONORED_CNT, /**< 12-bit */ + TSE_TX_DROP_CNT, /**< 12-bit */ + TSE_TX_JABBER_CNT, /**< 12-bit */ + TSE_TX_FCS_ERR_CNT, /**< 12-bit */ + TSE_TX_CNTRL_PKT_CNT, /**< 12-bit */ + TSE_TX_OVERSIZE_PKT_CNT, /**< 12-bit */ + TSE_TX_UNDERSIZE_PKT_CNT, /**< 12-bit */ + TSE_TX_FRAGMENT_CNT, /**< 12-bit */ + TSE_MAC_LAST_STAT +} tse_stat_t; + +/** + The application must use this function prototype to define the transmit + completion handler call-back function, which can be provided as a parameter + to `TSE_set_tx_callback()`. + */ +typedef void (*tse_transmit_callback_t)(void *p_user_data); + +/** + The application must use this function prototype to define the receive + call-back listener function, which can be provided as a parameter to `TSE_set_rx_callback()`. + */ +typedef void (*tse_receive_callback_t)(uint8_t *p_rx_packet, + uint32_t pckt_length, + void *p_user_data); + +/** + The application must use this function prototype to define the WoL event + handler function, which can be provided as a parameter to `TSE_set_wol_callback()`. + */ +typedef void (*tse_wol_callback_t)(void); + +/** + The driver creates and manages two DMA descriptor rings for transmission and reception. + */ +typedef struct tse_desc +{ + uint32_t pkt_start_addr; /**< Packet start address */ + volatile uint32_t pkt_size; /**< Packet size & Per packet override flags */ + uint32_t next_desriptor; /**< Link to next descriptor */ + uint32_t index; /**< Index: helps in handling interrupts */ + void *caller_info; /**< Pointer to user specific data */ +} tse_desc_t; + +/** + * **CoreTSE Configuration Structure** + + You need to create a record of this type to hold the configuration of the + CoreTSE. `TSE_cfg_struct_def_init()` must be used to + initialize the configuration record to the default values. Later, the configuration + elements in the record can be changed to the desired values. + + * **framefilter** + + The framefilter configuration parameter specifies the address-based frame + filtering choice. The framefilter configuration can be set to a bitmask of the + following defines to specify the address-based frame filtering mode: + - TSE_FC_PASS_BROADCAST_MASK + - TSE_FC_PASS_MULTICAST_MASK + - TSE_FC_PASS_UNICAST_MASK + - TSE_FC_PROMISCOUS_MODE_MASK + - TSE_FC_PASS_UNICAST_HASHT_MASK + - TSE_FC_PASS_MULTICAST_HASHT_MASK + - TSE_FC_DEFAULT_MASK + + `TSE_cfg_struct_def_init()` sets this configuration parameter to + TSE_FC_DEFAULT_MASK to indicate that only frames with broadcast and unicast match + should be received - all other frames are dropped. + + Note: + `TSE_set_address_filter()` must be used to set the list of + destination MAC addresses which are passed when the constants + TSE_FC_PASS_UNICAST_HASHT_MASK and/or TSE_FC_PASS_MULTICAST_HASHT_MASK is/are + specified. + + * **speed_duplex_select** + + The speed_duplex_select configuration parameter specifies the allowed link + speeds. It is a bit-mask of the allowed link speed and duplex modes. The + speed_duplex_select configuration can be set to a bitmask of the following + defines to specify the allowed link speed and duplex mode: + - TSE_ANEG_10M_FD + - TSE_ANEG_10M_HD + - TSE_ANEG_100M_FD + - TSE_ANEG_100M_HD + - TSE_ANEG_1000M_FD + + `TSE_cfg_struct_def_init()` sets this configuration parameter to + TSE_ANEG_ALL_SPEEDS indicating that a link is set up for the best available + speed and duplex combination. + + * **mac_addr** + + The mac_addr configuration parameter is a 6-byte array containing the local + MAC address of CoreTSE. + + * **phy_address** + + The phy_address parameter specifies the address of the PHY device, set in + hardware by the address pins of the PHY device. + + * **tx_edc_enable** + + The tx_edc_enable parameter specifies whether to enable or disable error detection and + correction for Tx FIFOs. The allowed values for the tx_edc_enable configuration + parameter are: + + - TSE_ERR_DET_CORR_ENABLE + - TSE_ERR_DET_CORR_DISABLE + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_ERR_DET_CORR_ENABLE. + + * **rx_edc_enable** + + The rx_edc_enable parameter specifies whether to enable or disable error detection and + correction for Rx FIFOs. The allowed values for the rx_edc_enable configuration + parameter are: + + - TSE_ERR_DET_CORR_ENABLE + - TSE_ERR_DET_CORR_DISABLE + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_ERR_DET_CORR_ENABLE. + + * **preamble_length** + + The preamble_length parameter specifies the length of the preamble field of + the packet in bytes. The allowed values for the preamble_length configuration + parameter are: + + - TSE_PREAMLEN_DEFVAL + - TSE_PREAMLEN_MAXVAL + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_PREAMLEN_DEFVAL. + + * **hugeframe_enable** + + The hugeframe_enable parameter specifies whether to enable or disable huge frame support. + When enabled, it allows frames longer than the standard maximum frame length to + be transmitted and received. + When disabled, the hardware limits the length of frames at the maximum frame length. + The allowed values for the `hugeframe_enable` configuration parameter are: + + - TSE_HUGE_FRAME_ENABLE + - TSE_HUGE_FRAME_DISABLE + + `TSE_cfg_struct_def_init()` sets this configuration parameter to SE_HUGE_FRAME_DISABLE. + + * **length_field_check** + + The length_field_check parameter specifies whether to enable or disable the length + field check. When enabled, the CoreTSE checks the frame’s length field + to ensure it matches the actual data field length. The allowed values for the + length_field_check configuration parameter are: + + - TSE_LENGTH_FIELD_CHECK_ENABLE + - TSE_LENGTH_FIELD_CHECK_DISABLE + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_LENGTH_FIELD_CHECK_ENABLE. + + * **pad_n_CRC** + + The pad_n_CRC parameter specifies whether to enable or disable short frame + padding and automatic CRC insertion functionality. + When enabled, the CoreTSE pads all the short frames and appends a + CRC to every frame whether or not padding is required. When disabled, frames presented + to the CoreTSE have a valid length and contain a CRC. + The allowed values for the pad_n_CRC configuration parameter are: + + - TSE_PAD_N_CRC_ENABLE + - TSE_PAD_N_CRC_DISABLE + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_PAD_N_CRC_ENABLE. + + * **append_CRC** + + The append_CRC parameter specifies whether to enable or disable appending CRC. When + enabled, the CoreTSE appends a CRC to all the frames. When disabled, + frames presented to the CoreTSE, have a valid length and contain a + valid CRC. The allowed values for the append_CRC configuration parameter are: + - TSE_CRC_ENABLE + - TSE_CRC_DISABLE + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_CRC_ENABLE. + + * **fullduplex** + + The fullduplex parameter specifies whether to enable or disable full duplex. When enabled, + the CoreTSE operates in full-duplex mode. When disabled, the MAC + operates in half-duplex mode. The allowed values for the fullduplex + configuration parameter are: + + - TSE_FULLDUPLEX_ENABLE + - TSE_FULLDUPLEX_DISABLE + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_FULLDUPLEX_ENABLE. + + * **loopback** + + The loopback parameter specifies whether to enable or disable loopback mode. When + enabled, the CoreTSE_AHB/CoreTSE’s transmit outputs are looped back to its + receiving inputs. The allowed values for the loopback configuration parameter + are: + + - TSE_LOOPBACK_ENABLE + - TSE_LOOPBACK_DISABLE + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_LOOPBACK_DISABLE. + + * **rx_flow_ctrl** + + The rx_flow_ctrl parameter specifies whether to enable or disable the receiver + flow control functionality. + When enabled, the CoreTSE detects and acts on PAUSE flow control + frames. When disabled, it ignores PAUSE flow control frames. The allowed values + for the rx_flow_ctrl configuration parameter are: + + - TSE_RX_FLOW_CTRL_ENABLE + - TSE_RX_FLOW_CTRL_DISABLE + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_RX_FLOW_CTRL_ENABLE. + + * **tx_flow_ctrl** + + The tx_flow_ctrl parameter specifies whether to enable or disable transmitter flow control. + When enabled, the transmitter sends PAUSE flow control frames when requested + by the system. When disabled, the transmitter is prevented from sending flow control + frames. + The allowed values for the tx_flow_ctrl configuration parameter are: + + - TSE_TX_FLOW_CTRL_ENABLE + - TSE_TX_FLOW_CTRL_DISABLE + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_TX_FLOW_CTRL_ENABLE. + + * **min_IFG** + + The min_IFG parameter specifies the minimum size of the interframe gap (IFG) to enforce + between frames (expressed in bit times). The allowed values for the min_IFG + configuration parameter are: + + - TSE_MINIFG_DEFVAL + - TSE_MINIFG_MAXVAL + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_MINIFG_DEFVAL. + + * **btb_IFG** + + The btb_IFG parameter specifies the Interframe gap between back-to-back + packets (expressed in bit times), used exclusively in full-duplex mode when + two transmit packets are sent back-to-back. The allowed values for the btb_IFG + configuration parameter are: + + - TSE_BTBIFG_DEFVAL + - TSE_BTBIFG_MAXVAL + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_BTBIFG_DEFVAL. + + * **max_retx_tries** + + The max_retx_tries parameter specifies the number of retransmission attempts + following a collision before aborting the packet due to excessive collisions. + The allowed values for the max_retx_tries configuration parameter are: + + - TSE_MAXRETX_DEFVAL + - TSE_MAXRETX_MAXVAL + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_MAXRETX_DEFVAL. + + * **excessive_defer** + + The excessive_defer parameter specifies whether to enable or disable the transmission + of packets that exceeded maximum collisions. When enabled, the transmitter allows + the transmission of a packet that has been excessively deferred. When disabled, + the transmitter aborts the transmission of a packet that has been excessively + deferred. The allowed values for the excessive_defer configuration parameter + are: + + - TSE_EXSS_DEFER_ENABLE + - TSE_EXSS_DEFER_DISABLE + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_EXSS_DEFER_DISABLE. + + * **nobackoff** + + The nobackoff parameter specifies whether to enable or disable the back-off functionality. + When enabled, the transmitter immediately re-transmits following a collision. + When disabled, the transmitter follows the binary exponential backoff rule. + The allowed values for the nobackoff configuration parameter are: + + - TSE_NO_BACKOFF_ENABLE + - TSE_NO_BACKOFF_DISABLE + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_NO_BACKOFF_DISABLE. + + * **backpres_nobackoff** + + The backpres_nobackoff parameter specifies whether to enable or disable back-off + in back pressure mode. + When enabled, the transmitter immediately re-transmits following a collision during + back a pressure operation. When disabled, the transmitter follows the binary exponential + backoff rule. + The allowed values for the backpres_nobackoff configuration parameter are: + + - TSE_BACKPRESS_NO_BACKOFF_ENABLE + - TSE_BACKPRESS_NO_BACKOFF_DISABLE + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_BACKPRESS_NO_BACKOFF_DISABLE. + + * **ABEB_enable** + + The ABEB_enable parameter specifies whether to enable or disable arbitrary binary + exponential back-off. When enabled, it configures the transmitter to use the + ABEB_truncvalue value instead of the 802.3 standard 10 collisions. When + disabled, it causes the transmitter to follow the 802.3 standard binary + exponential backoff rule. The allowed values for the ABEB_enable configuration + parameter are: + + - TSE_ABEB_ENABLE + - TSE_ABEB_DISABLE + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_ABEB_DISABLE. + + * **ABEB_truncvalue** + + The ABEB_truncvalue parameter specifies alternative binary exponential back-off + value. This value is used when the ABEB_enable parameter is enabled. The allowed + values for the ABEB_truncvalue configuration parameter are: + + - TSE_ABEBTRUNC_DEFVAL + - TSE_ABEBTRUNC_MAXVAL + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_ABEBTRUNC_DEFVAL. + + * **phyclk** + + The phyclk parameter specifies the MII management clock divider value. + PCLK is the source clock. + The allowed values for the phyclk configuration parameter are: + + - TSE_DEF_PHY_CLK + - TSE_BY4_PHY_CLK + - TSE_BY6_PHY_CLK + - TSE_BY8_PHY_CLK + - TSE_BY10_PHY_CLK + - TSE_BY14_PHY_CLK + - TSE_BY20_PHY_CLK + - TSE_BY28_PHY_CLK + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_DEF_PHY_CLK. + + * **supress_preamble** + + The supress_preamble parameter specifies whether to enable or disable preamble suppression + at the PHY. When enabled, MII Management suppresses preamble generation and reduces + the Management cycle from 64 clocks to 32 clocks. When disabled, + MII Management performs Management read/write cycles with the 64 clocks + of preamble. The allowed values for the supress_preamble configuration parameter + are: + + - TSE_SUPPRESS_PREAMBLE_ENABLE + - TSE_SUPPRESS_PREAMBLE_DISABLE + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_SUPPRESS_PREAMBLE_DISABLE. + + * **autoscan_phys** + + The autoscan_phys parameter specifies whether to enable or disable continuous + reading from a set of PHYs of contiguous address space. When enabled, it causes + MDIO management to continually read from a set of PHYs of contiguous address space. + The starting address of the PHY is specified by the content of the PHY address + field. The next PHY to be read will be PHY address + 1. The last PHY to be + queried in this read sequence will be the one residing at address 0x31, after + which the read sequence returns to the PHY specified by the PHY address field. + When disabled, MDIO management performs only one read operation at the specified + PHY address. The allowed values for the autoscan_phys configuration parameter + are: + + - TSE_PHY_AUTOSCAN_ENABLE + - TSE_PHY_AUTOSCAN_DISABLE + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_PHY_AUTOSCAN_DISABLE. + + * **max_frame_length** + + The max_frame_length parameter specifies the maximum frame size in both the + transmit and receive directions. The allowed values for the max_frame_length + configuration parameter are: + + - TSE_MAXFRAMELEN_DEFVAL + - TSE_MAXFRAMELEN_MAXVAL + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_MAXFRAMELEN_DEFVAL. + + * **non_btb_IFG** + + The non_btb_IFG parameter specifies the non-back-to-back interframe gap value. + The allowed values for the non_btb_IFG configuration parameter are: + + - TSE_NONBTBIFG_DEFVAL + - TSE_NONBTBIFG_MAXVAL + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_NONBTBIFG_DEFVAL. + + * **slottime** + + The slottime parameter specifies the slot time or collision window during + which collisions might occur in a properly configured network. The allowed + values for the slot time configuration parameter are: + + - TSE_SLOTTIME_DEFVAL + - TSE_SLOTTIME_MAXVAL + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_SLOTTIME_DEFVAL. + + * **framedrop_mask** + + The framedrop_mask parameter specifies to drop frames based on receive + statistics. The allowed values for the framedrop_mask configuration parameter + are: + + - TSE_DEFVAL_FRAMEDROP_MASK + - TSE_SMALL_AFG_FRAMEDROP_MASK + - TSE_EXDV_EVENT_FRAMEDROP_MASK + - TSE_FALSE_CARRIER_FRAMEDROP_MASK + - TSE_CODE_ERR_FRAMEDROP_MASK + - TSE_CRC_ERR_FRAMEDROP_MASK + - TSE_LEN_CHKERR_FRAMEDROP_MASK + - TSE_OUTOFRANGE_LEN_FRAMEDROP_MASK + - TSE_OK_FRAME_FRAMEDROP_MASK + - TSE_MULTICAST_FRAMEDROP_MASK + - TSE_BROADCAST_FRAMEDROP_MASK + - TSE_DRIBBLE_NIBBLE_FRAMEDROP_MASK + - TSE_CONTROL_FRAME_FRAMEDROP_MASK + - TSE_PAUSE_FRAME_FRAMEDROP_MASK + - TSE_UNSUPPORTED_OPCODE_FRAMEDROP_MASK + - TSE_VLAN_TAG_FRAMEDROP_MASK + - TSE_LONG_EVENT_FRAMEDROP_MASK + - TSE_TRUNCKATED_FRAME_FRAMEDROP_MASK + - TSE_UNICAST_NO_SAMATCH_FRAMEDROP_MASK + - TSE_SHORT_FRAME_FRAMEDROP_MASK + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_DEFVAL_FRAMEDROP_MASK. + + * **wol_enable** + + The wol_enable parameter specifies whether to enable or disable the Wake on LAN + (WoL) functionality. + When enabled, CoreTSE can detect either the unicast match frame or + AMD magic packet frame and assert the WoL interrupt. When disabled, + the WoL interrupt is not asserted even though unicast match frame or AMD + magic packet frame is detected. The allowed values for the supress_preamble + configuration parameter are: + + - TSE_WOL_UNICAST_FRAME_DETECT_EN + - TSE_WOL_MAGIC_FRAME_DETECT_EN + - TSE_WOL_DETECT_DISABLE + + * **aneg_enable** + + The aneg_enable parameter specifies whether to enable or disable the + auto-negotiation functionality in the PHY and “TBI/1000BaseX†module. + When enabled, CoreTSE enables auto-negotiation in the PHY and “TBI/1000BaseX†+ module. + When disabled, CoreTSE does not enable auto-negotiation in PHY and + “TBI/1000BaseX†module. Disabling auto-negotiation allows the user to use the + CoreTSE in point-to-point topology where speed/duplex configuration + is known and auto-negotiation is not required. + + `TSE_cfg_struct_def_init()` sets this configuration parameter to TSE_ENABLE. + + */ +typedef struct tse_cfg +{ + uint32_t framefilter; /**< Address based frame filter configuration*/ + uint32_t speed_duplex_select; /**< Link speed and duplex mode allowed to set up a link. */ + uint8_t mac_addr[6]; /**< Station's MAC address */ + uint8_t phy_addr; /**< Address of Ethernet PHY on MII management interface. */ + uint8_t tx_edc_enable; /**< Enable/disable error detection and correction for Tx FIFOs */ + uint8_t rx_edc_enable; /**< Enable/disable error detection and correction for Rx FIFOs */ + uint8_t preamble_length; /**< 4-bit Length of preamble field: default value is 0x7 */ + uint8_t hugeframe_enable; /**< Enable/disable huge frame support: default is disable 0 */ + uint8_t length_field_check; /**< Enable/disable length field checking */ + uint8_t pad_n_CRC; /**< Enable/disable padding and appending CRC */ + uint8_t append_CRC; /**< Enable/disable appending CRC */ + uint8_t fullduplex; /**< Enable/disable full duplex: default is disable 0 */ + uint8_t loopback; /**< Enable/disable loopback mode: default is disable 0 */ + uint8_t rx_flow_ctrl; /**< Enable/disable receiver flow control: default is disable 0 */ + uint8_t tx_flow_ctrl; /**< Enable/disable transmitter flow control: default is disable 0 */ + uint8_t min_IFG; /**< 8-bit minimum interframe gap value */ + uint8_t btb_IFG; /**< 7-bit back to back interframe gap value */ + uint8_t max_retx_tries; /**< 5-bit maximum retransmission tries value: default is 0xF */ + uint8_t excessive_defer; /**< Enable/disable transmission of packets that exceeded max + collisions: default is disable 0 */ + uint8_t nobackoff; /**< Enable/disable back-off. default is disable 0 */ + uint8_t backpres_nobackoff; /**< Enable/disable back-off in back pressure mode: default is + disable 0 */ + uint8_t ABEB_enable; /**< Enable/disable arbitrary binary exponential back-off: default is + disable 0 */ + uint8_t + ABEB_truncvalue; /**< 4-bit alternative binary exponential back-off value: default is 0xA */ + uint8_t phyclk; /**< 3-bit MGMT clock divider value */ + uint8_t + supress_preamble; /**< Enable/disable preamble suppression at PHY: default is disable 0 */ + uint8_t autoscan_phys; /**< Enable/disable auto scanning of PHYs with programmed addresses: + default is disable 0 */ + uint16_t max_frame_length; /**< Maximum frame length: default value is 0x0600(1536d) */ + uint16_t non_btb_IFG; /**< 14-bit non-back-to-back interframe gap value */ + uint16_t slottime; /**< 10-bit collision window value : default is 0x37 */ + uint32_t framedrop_mask; /**< 18-bit mask to drop frames based on receive statistics */ + uint8_t wol_enable; /**< Enable/disable WoL event detect feature */ + uint8_t aneg_enable; /**< Enable/disable auto-negotiation in MSGMII module as well as phy (if + PHY is in the system) */ +} tse_cfg_t; + +/** +* **CoreTSE Hardware Instance** + + The CoreTSE hardware instance identifies the various CoreTSE hardware + instances in your system. Your application software must declare one instance + of this structure for each instance of CoreTSE in your system. + `TSE_init()` initializes this structure. A pointer to an initialized + instance of the structure must be passed as the first parameter to the CoreTSE + driver functions, to identify which CoreTSE hardware instances + should perform the requested operation. + */ +typedef struct tse_instance +{ + uint32_t base_addr; + tse_desc_t tx_desc_tab[TSE_TX_RING_SIZE]; /**< Transmit descriptor table */ + tse_desc_t rx_desc_tab[TSE_RX_RING_SIZE]; /**< Receive descriptor table */ + tse_transmit_callback_t tx_complete_handler; + tse_receive_callback_t pckt_rx_callback; + int16_t nb_available_tx_desc; + int16_t first_tx_index; + int16_t last_tx_index; + int16_t next_tx_index; + int16_t nb_available_rx_desc; + int16_t next_free_rx_desc_index; + int16_t first_rx_desc_index; + uint8_t phy_addr; /**< PHY address for this instance of CoreTSE*/ + tse_wol_callback_t wol_callback; +} tse_instance_t; + +#ifdef __cplusplus +} +#endif + +#endif /* CORE_TSE_TYPES_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/coretse_user_config.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/coretse_user_config.h new file mode 100644 index 0000000..2719eac --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/coretse_user_config.h @@ -0,0 +1,28 @@ + +/******************************************************************************* + * Copyright 2014 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file coretse_user_config.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Firmware:CORE_TSE_Driver:1.0.1 configuration + * + */ + +#ifndef ACTEL__FIRMWARE__CORE_TSE_DRIVER__1_0_1_CONFIGURATION_HEADER +#define ACTEL__FIRMWARE__CORE_TSE_DRIVER__1_0_1_CONFIGURATION_HEADER + +#define CORE_VENDOR "Actel" +#define CORE_LIBRARY "Firmware" +#define CORE_NAME "SmartFusion2_CORE_TSE_Driver" +#define CORE_VERSION "1.0.1" + +#define TSE_PHY_INTERFACE 2 +#define TSE_MSGMII_ADDR 18 +#define TSE_PHY 1 +#define TSE_RGMII_MDIO_ADDR 20 +#define TSE_RX_RING_SIZE 2 +#define TSE_TX_RING_SIZE 2 + +#endif // ACTEL__FIRMWARE__CORE_TSE_DRIVER__1_0_1_CONFIGURATION_HEADER diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/crc32.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/crc32.c new file mode 100644 index 0000000..c5a8143 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/crc32.c @@ -0,0 +1,57 @@ + +/******************************************************************************* + * Copyright 2014 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file crc32.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief crc32 source file + * + * CRC-32-IEEE 802.3 + * x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + + * x^4 +*x^2 + x + 1 + * + */ + +#include "crc32.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Calculates 32 bits CRC value of given data.*/ +uint32_t +TSE_crc32(uint32_t value, const uint8_t *data, uint32_t data_length) +{ + int i, j; + unsigned int CRC = 0xffffffff; + + for (i = 0; i < data_length; i++) + { + unsigned char byte = data[i]; + + for (j = 0; j < 8; j++) + { + if ((byte & 0x1) ^ (CRC >> 31)) + CRC = (CRC << 1) ^ 0x04c11db7; + else + CRC <<= 1; + byte >>= 1; + } + } + return CRC; +} + +/*Calculates 32 bit CRC value of given data, using standard Ethernet CRC function*/ +uint32_t +TSE_ethernet_crc(const uint8_t *data, uint32_t data_length) +{ + return TSE_crc32(0xffffffffUL, data, data_length); +} + +#ifdef __cplusplus +} +#endif + +/******************************** END OF FILE ******************************/ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/crc32.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/crc32.h new file mode 100644 index 0000000..fc6ec7b --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/crc32.h @@ -0,0 +1,32 @@ + +/******************************************************************************* + * Copyright 2014 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file crc32.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief crc32 header file + * + */ + +#ifndef __CRC32_H +#define __CRC32_H 1 + +#include "hal/cpu_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Calculates 32 bits CRC value of given data */ +uint32_t TSE_crc32(uint32_t value, const uint8_t *data, uint32_t data_length); + +/*Calculates 32 bit CRC value of given data,using standard Ethernet CRC function*/ +uint32_t TSE_ethernet_crc(const uint8_t *data, uint32_t data_length); + +#ifdef __cplusplus +} +#endif + +#endif /* __CRC32_H */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/m88e1111_phy.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/m88e1111_phy.c new file mode 100644 index 0000000..3456593 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/m88e1111_phy.c @@ -0,0 +1,231 @@ + +/******************************************************************************* + * Copyright 2014 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file m88e1111_phy.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief Marvell's M88E1111 PHY interface driver source code. + * + */ + +#include "phy.h" + +#include "core_tse.h" +#include "coretse_regs.h" +#include "coretse_user_config.h" +#include "hal/hal.h" +#include "hal/hal_assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(M88E1111_PHY) + +/**************************************************************************/ +/* Preprocessor Macros */ +/**************************************************************************/ +#define TSE_RGMII 0x04 + +#define SF2_MSGMII_PHY_ADDR TSE_MSGMII_ADDR +#define SF2_MSGMII_PHY_RESET_MASK 0x8000u +#define SF2_MSGMII_TBI_CONTROL 0x11 + +#define BMSR_AUTO_NEGOTIATION_COMPLETE 0x0020u + +#define ANEG_REQUESTED 0x80000000u +#define FORCED_CFG_REQUESTED 0x40000000u + +/*------------------------------------------------------------------------------ + * M88E1111 PHY specific register offsets and bit definitions + */ +#define M88E1111_EXT_ADDR_PAGE_CR 0x16 +#define PAGE_0 0x00 +#define PAGE_1 0x01 + +#define M88E1111_PHY_EXT_SR 0x1b + +#define M88E1111_PHY_STATUS 0x11 +#define M88E1111_PHY_STATUS_1000 0x8000 +#define M88E1111_PHY_STATUS_100 0x4000 +#define M88E1111_PHY_STATUS_10 0x0000 +#define M88E1111_PHY_STATUS_SPD_MASK 0xc000 +#define M88E1111_PHY_STATUS_FULLDUPLEX 0x2000 +#define M88E1111_PHY_STATUS_RESOLVED 0x0800 +#define M88E1111_PHY_STATUS_LINK 0x0400 + +/**************************************************************************/ /** + * See m88e1111_phy.h + * for details of how + * to use this + * function. + */ +void +TSE_phy_init(tse_instance_t *this_tse, uint8_t phy_addr) +{ + volatile uint16_t phy_reg; + +#if (TSE_INTERFACE == TSE_RGMII) + /* + * Enable phy-local clock shift + */ + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_EXTEND); + phy_reg |= 0x0002; + TSE_write_phy_reg(this_tse, phy_addr, MII_EXTEND, phy_reg); +#endif /*(TSE_INTERFACE == TSE_RGMII)*/ + + /* Reset the PHY. */ + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_BMCR); + phy_reg |= BMCR_RESET; + TSE_write_phy_reg(this_tse, phy_addr, MII_BMCR, phy_reg); + + /* Power up the PHY. */ + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_BMCR); + phy_reg &= ~BMCR_PDOWN; + TSE_write_phy_reg(this_tse, phy_addr, MII_BMCR, phy_reg); +} + +/**************************************************************************/ /** + * + */ +void +TSE_phy_set_link_speed(tse_instance_t *this_tse, uint8_t phy_addr, uint32_t speed_duplex_select) +{ + uint16_t phy_reg; + uint32_t inc; + uint32_t speed_select; + uint16_t const mii_advertise_bits[4] = {ADVERTISE_10FULL, + ADVERTISE_10HALF, + ADVERTISE_100FULL, + ADVERTISE_100HALF}; + + /* Set 10Mbps and 100Mbps advertisement. */ + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_ADVERTISE); + phy_reg &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF | ADVERTISE_100FULL); + + speed_select = speed_duplex_select; + for (inc = 0u; inc < 4u; ++inc) + { + uint32_t advertise; + advertise = speed_select & 0x00000001u; + if (advertise != 0u) + { + phy_reg |= mii_advertise_bits[inc]; + } + speed_select = speed_select >> 1u; + } + + TSE_write_phy_reg(this_tse, phy_addr, MII_ADVERTISE, phy_reg); + + /* Set 1000Mbps advertisement. */ + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_CTRL1000); + phy_reg &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); + + if ((speed_duplex_select & TSE_ANEG_1000M_FD) != 0u) + { + phy_reg |= ADVERTISE_1000FULL; + } + + TSE_write_phy_reg(this_tse, phy_addr, MII_CTRL1000, phy_reg); +} + +/**************************************************************************/ /** + * + */ +void +TSE_phy_autonegotiate(tse_instance_t *this_tse, uint8_t phy_addr) +{ + uint16_t volatile phy_reg; + uint16_t autoneg_complete; + uint32_t volatile copper_aneg_timeout = 1000000u; + + /* Enable auto-negotiation. */ + TSE_write_phy_reg(this_tse, phy_addr, M88E1111_EXT_ADDR_PAGE_CR, PAGE_0); + + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_BMCR); + phy_reg |= 0x9340; + TSE_write_phy_reg(this_tse, phy_addr, MII_BMCR, phy_reg); + + /* Wait for copper auto-negotiation to complete. */ + do + { + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_BMSR); + autoneg_complete = phy_reg & BMSR_AUTO_NEGOTIATION_COMPLETE; + --copper_aneg_timeout; + } while ((!autoneg_complete && copper_aneg_timeout != 0u) || (0xFFFF == phy_reg)); +} + +/***************************************************************************/ /** + * + */ +uint8_t +TSE_phy_get_link_status(tse_instance_t *this_tse, + uint8_t phy_addr, + tse_speed_t *speed, + uint8_t *fullduplex) +{ + uint16_t phy_reg; + uint16_t link_up; + uint8_t link_status; + + /* Find out if link is up between Marvell PHY and remote device.*/ + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_BMSR); + link_up = phy_reg & BMSR_LSTATUS; + + if (link_up != TSE_LINK_DOWN) + { + uint16_t duplex; + uint16_t phy_speed; + + /* Link is up. */ + link_status = TSE_LINK_UP; + + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, M88E1111_PHY_STATUS); + duplex = phy_reg & M88E1111_PHY_STATUS_FULLDUPLEX; + phy_speed = phy_reg & M88E1111_PHY_STATUS_SPD_MASK; + + if (TSE_HALF_DUPLEX == duplex) + { + *fullduplex = TSE_HALF_DUPLEX; + } + else + { + *fullduplex = TSE_FULL_DUPLEX; + } + + switch (phy_speed) + { + case M88E1111_PHY_STATUS_1000: + *speed = TSE_MAC1000MBPS; + break; + + case M88E1111_PHY_STATUS_100: + *speed = TSE_MAC100MBPS; + break; + + case M88E1111_PHY_STATUS_10: + *speed = TSE_MAC10MBPS; + break; + default: + *speed = TSE_INVALID_SPEED; + break; + } + } + else + { + /* Link is down. */ + link_status = TSE_LINK_DOWN; + } + + return link_status; +} + +#endif + +#ifdef __cplusplus +} +#endif + +/******************************** END OF FILE ******************************/ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/m88e1340_phy.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/m88e1340_phy.c new file mode 100644 index 0000000..e09b69e --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/m88e1340_phy.c @@ -0,0 +1,229 @@ + +/******************************************************************************* + * Copyright 2014 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file m88e1340_phy.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief Marvell's M88E1340 PHY interface driver implementation. + * + */ + +#include "phy.h" +#include "core_tse.h" +#include "coretse_regs.h" +#include "hal/hal.h" +#include "hal/hal_assert.h" + +#ifndef LEGACY_DIR_STRUCTURE +#include "fpga_design_config/fpga_design_config.h" +#else +#include "hw_platform.h" +#endif /*LEGACY_DIR_STRUCTURE*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +#if defined(M88E1340_PHY) +/***************************************************************************/ /** + * M88E1340 PHY + * specific register + * offsets and bit + * definitions + */ +#ifdef SYS_CLK_FREQ +#define SYSTEM_CORE_CLOCK (SYS_CLK_FREQ) +#endif +#ifndef SYS_CLK_FREQ +#define SYSTEM_CORE_CLOCK (LIBERO_SETTING_MSS_APB_AHB_CLK) +#endif + +#define M88E1340_EXT_ADDR_PAGE_CR 0x16 +#define PAGE_0 0x00 + +#define M88E1340_PHY_STATUS 0x11 +#define M88E1340_PHY_STATUS_1000 0x8000 +#define M88E1340_PHY_STATUS_100 0x4000 +#define M88E1340_PHY_STATUS_10 0x0000 +#define M88E1340_PHY_STATUS_SPD_MASK 0xc000 +#define M88E1340_PHY_STATUS_FULLDUPLEX 0x2000 +#define M88E1340_PHY_STATUS_RESOLVED 0x0800 +#define M88E1340_PHY_STATUS_LINK 0x0400 + +/***************************************************************************/ /** + * Preprocessor Macros + */ +#define BMSR_AUTO_NEGOTIATION_COMPLETE 0x0020u + +/***************************************************************************/ /** + * + */ +void +TSE_phy_init(tse_instance_t *this_tse, uint8_t phy_addr) +{ + volatile uint16_t phy_reg; + + /* Reset the PHY. */ + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_BMCR); + phy_reg |= BMCR_RESET; + TSE_write_phy_reg(this_tse, phy_addr, MII_BMCR, phy_reg); + + /* Power up the PHY. */ + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_BMCR); + phy_reg &= ~BMCR_PDOWN; + TSE_write_phy_reg(this_tse, phy_addr, MII_BMCR, phy_reg); +} + +/**************************************************************************/ /** + * + */ +void +TSE_phy_set_link_speed(tse_instance_t *this_tse, uint8_t phy_addr, uint32_t speed_duplex_select) +{ + uint16_t phy_reg; + uint32_t inc; + uint32_t speed_select; + uint16_t const mii_advertise_bits[4] = {ADVERTISE_10FULL, + ADVERTISE_10HALF, + ADVERTISE_100FULL, + ADVERTISE_100HALF}; + + /* Set 10Mbps and 100Mbps advertisement. */ + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_ADVERTISE); + phy_reg &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF | ADVERTISE_100FULL); + + speed_select = speed_duplex_select; + for (inc = 0u; inc < 4u; ++inc) + { + uint32_t advertise; + advertise = speed_select & 0x00000001u; + if (advertise != 0u) + { + phy_reg |= mii_advertise_bits[inc]; + } + speed_select = speed_select >> 1u; + } + + TSE_write_phy_reg(this_tse, phy_addr, MII_ADVERTISE, phy_reg); + + /* Set 1000Mbps advertisement. */ + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_CTRL1000); + phy_reg &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); + + if ((speed_duplex_select & TSE_ANEG_1000M_FD) != 0u) + { + phy_reg |= ADVERTISE_1000FULL; + } + + TSE_write_phy_reg(this_tse, phy_addr, MII_CTRL1000, phy_reg); +} + +/**************************************************************************/ /** + * + */ +void +TSE_phy_autonegotiate(tse_instance_t *this_tse, uint8_t phy_addr) +{ + volatile uint16_t phy_reg; + uint16_t autoneg_complete; + uint32_t volatile copper_aneg_timeout = 1000000u; + + /* Enable auto-negotiation. */ + TSE_write_phy_reg(this_tse, phy_addr, M88E1340_EXT_ADDR_PAGE_CR, PAGE_0); + + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_BMCR); + phy_reg |= (BMCR_ANENABLE | BMCR_ANRESTART); + TSE_write_phy_reg(this_tse, phy_addr, MII_BMCR, phy_reg); + + /* Wait for copper auto-negotiation to complete. */ + do + { + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_BMSR); + autoneg_complete = phy_reg & BMSR_AUTO_NEGOTIATION_COMPLETE; + --copper_aneg_timeout; + } while ((!autoneg_complete && copper_aneg_timeout != 0u) || (0xFFFF == phy_reg)); + + { + volatile uint32_t delay = SYSTEM_CORE_CLOCK / 256u; + while (delay != 0) + { + --delay; + } + } +} + +/***************************************************************************/ /** + * + */ +uint8_t +TSE_phy_get_link_status(tse_instance_t *this_tse, + uint8_t phy_addr, + tse_speed_t *speed, + uint8_t *fullduplex) +{ + volatile uint16_t phy_reg; + uint16_t copper_link_up; + uint8_t link_status; + + /* Find out if link is up between Marvell PHY and remote device.*/ + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_BMSR); + copper_link_up = phy_reg & BMSR_LSTATUS; + + if (copper_link_up != TSE_LINK_DOWN) + { + uint16_t duplex; + uint16_t phy_speed; + + /* Link is up. */ + link_status = TSE_LINK_UP; + + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, M88E1340_PHY_STATUS); + duplex = phy_reg & M88E1340_PHY_STATUS_FULLDUPLEX; + phy_speed = phy_reg & M88E1340_PHY_STATUS_SPD_MASK; + + if (TSE_HALF_DUPLEX == duplex) + { + *fullduplex = TSE_HALF_DUPLEX; + } + else + { + *fullduplex = TSE_FULL_DUPLEX; + } + + switch (phy_speed) + { + case M88E1340_PHY_STATUS_1000: + *speed = TSE_MAC1000MBPS; + break; + + case M88E1340_PHY_STATUS_100: + *speed = TSE_MAC100MBPS; + break; + + case M88E1340_PHY_STATUS_10: + *speed = TSE_MAC10MBPS; + break; + default: + *speed = TSE_INVALID_SPEED; + break; + } + } + else + { + /* Link is down. */ + link_status = TSE_LINK_DOWN; + } + + return link_status; +} + +#endif + +#ifdef __cplusplus +} +#endif + +/******************************** END OF FILE ******************************/ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/null_phy.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/null_phy.c new file mode 100644 index 0000000..ef141dc --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/null_phy.c @@ -0,0 +1,74 @@ + +/******************************************************************************* + * Copyright 2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file null_phy.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief NULL PHY implementation + * + */ + +#include "phy.h" +#include "core_tse.h" +#include "coretse_regs.h" +#include "hal/hal.h" +#include "hal/hal_assert.h" + +#ifndef LEGACY_DIR_STRUCTURE +#include "fpga_design_config/fpga_design_config.h" +#else +#include "hw_platform.h" +#endif /*LEGACY_DIR_STRUCTURE*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(M88E1340_PHY) && !defined(M88E1111_PHY) && !defined(VSC8575_PHY) + +void +TSE_phy_init(tse_instance_t *this_tse, uint8_t phy_addr) +{ + /* Nothing to see here... */ +} + +/***************************************************************************/ /** + + */ +void +TSE_phy_set_link_speed(tse_instance_t *this_tse, uint8_t phy_addr, uint32_t speed_duplex_select) +{ + /* Nothing to see here... */ +} + +/***************************************************************************/ /** + + */ +void +TSE_phy_autonegotiate(tse_instance_t *this_tse, uint8_t phy_addr) +{ + /* Nothing to see here... */ +} + +/***************************************************************************/ /** + + */ +uint8_t +TSE_phy_get_link_status(tse_instance_t *this_tse, + uint8_t phy_addr, + tse_speed_t *speed, + uint8_t *fullduplex) +{ + /* Nothing to see here... */ + return 0; +} + +#endif + +#ifdef __cplusplus +} +#endif + +/******************************** END OF FILE ******************************/ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/phy.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/phy.h new file mode 100644 index 0000000..bcaaf95 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/phy.h @@ -0,0 +1,235 @@ + +/******************************************************************************* + * Copyright 2014 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file phy.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Register bit definitions for MII STA (station management entity) standard + * interface. All basic MII register bits and enhanced capability register bits + * are defined. + * Complies with Clauses 22, 28, 37, 40 of IEEE RFC 802.3 + * + */ + +#ifndef PHY_H_ +#define PHY_H_ + +#include "coretse_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**************************************************************************/ +/* Public definitions */ +/**************************************************************************/ + +/* Generic MII registers. */ +#define MII_BMCR 0x00u /* Basic mode control register */ +#define MII_BMSR 0x01u /* Basic mode status register */ +#define MII_PHYSID1 0x02u /* PHYS ID 1 */ +#define MII_PHYSID2 0x03u /* PHYS ID 2 */ +#define MII_ADVERTISE 0x04u /* Advertisement control reg */ +#define MII_LPA 0x05u /* Link partner ability reg */ +#define MII_EXPANSION 0x06u /* Expansion register */ +#define MII_NPAR 0x07u +#define MII_LPNPA 0x08u +#define MII_CTRL1000 0x09u /* 1000BASE-T control */ +#define MII_STAT1000 0x0au /* 1000BASE-T status */ +#define MII_ESTATUS 0x0fu /* Extended Status */ +#define MII_DCOUNTER 0x12u /* Disconnect counter */ +#define MII_FCSCOUNTER 0x13u /* False carrier counter */ +#define MII_EXTEND 0x14u /* extended PHY specific ctrl */ +#define MII_RERRCOUNTER 0x15u /* Receive error counter */ +#define MII_SREVISION 0x16u /* Silicon revision */ +#define MII_RESV1 0x17u /* Reserved... */ +#define MII_LBRERROR 0x18u /* Lpback, rx, bypass error */ +#define MII_PHYADDR 0x19u /* PHY address */ +#define MII_RESV2 0x1au /* Reserved... */ +#define MII_TPISTATUS 0x1bu /* TPI status for 10mbps */ +#define MII_NCONFIG 0x1cu /* Network interface config */ +#define MII_LMCS 0x1du +#define MII_PHYCTRL1 0x1eu +#define MII_PHYCTRL2 0x1fu + +/* Basic mode control register. */ +#define BMCR_RESV 0x003fu /* Unused... */ +#define BMCR_SPEED1000 0x0040u /* MSB of Speed (1000) */ +#define BMCR_CTST 0x0080u /* Collision test */ +#define BMCR_FULLDPLX 0x0100u /* Full duplex */ +#define BMCR_ANRESTART 0x0200u /* Auto negotiation restart */ +#define BMCR_ISOLATE 0x0400u /* Disconnect DP83840 from MII */ +#define BMCR_PDOWN 0x0800u /* Powerdown the DP83840 */ +#define BMCR_ANENABLE 0x1000u /* Enable auto negotiation */ +#define BMCR_SPEED100 0x2000u /* Select 100Mbps */ +#define BMCR_LOOPBACK 0x4000u /* TXD loopback bits */ +#define BMCR_RESET 0x8000u /* Reset the DP83840 */ + +/* Basic mode status register. */ +#define BMSR_ERCAP 0x0001u /* Ext-reg capability */ +#define BMSR_JCD 0x0002u /* Jabber detected */ +#define BMSR_LSTATUS 0x0004u /* Link status */ +#define BMSR_ANEGCAPABLE 0x0008u /* Able to do auto-negotiation */ +#define BMSR_RFAULT 0x0010u /* Remote fault detected */ +#define BMSR_ANEGCOMPLETE 0x0020u /* Auto-negotiation complete */ +#define BMSR_RESV 0x00c0u /* Unused... */ +#define BMSR_ESTATEN 0x0100u /* Extended Status in R15 */ +#define BMSR_100HALF2 0x0200u /* Can do 100BASE-T2 HDX */ +#define BMSR_100FULL2 0x0400u /* Can do 100BASE-T2 FDX */ +#define BMSR_10HALF 0x0800u /* Can do 10mbps, half-duplex */ +#define BMSR_10FULL 0x1000u /* Can do 10mbps, full-duplex */ +#define BMSR_100HALF 0x2000u /* Can do 100mbps, half-duplex */ +#define BMSR_100FULL 0x4000u /* Can do 100mbps, full-duplex */ +#define BMSR_100BASE4 0x8000u /* Can do 100mbps, 4k packets */ + +/* Advertisement control register. */ +#define ADVERTISE_SLCT 0x001fu /* Selector bits */ +#define ADVERTISE_CSMA 0x0001u /* Only selector supported */ +#define ADVERTISE_10HALF 0x0020u /* Try for 10mbps half-duplex */ +#define ADVERTISE_1000XFULL 0x0020u /* Try for 1000BASE-X full-duplex */ +#define ADVERTISE_10FULL 0x0040u /* Try for 10mbps full-duplex */ +#define ADVERTISE_1000XHALF 0x0040u /* Try for 1000BASE-X half-duplex */ +#define ADVERTISE_100HALF 0x0080u /* Try for 100mbps half-duplex */ +#define ADVERTISE_1000XPAUSE 0x0080u /* Try for 1000BASE-X pause */ +#define ADVERTISE_100FULL 0x0100u /* Try for 100mbps full-duplex */ +#define ADVERTISE_1000XPSE_ASYM 0x0100u /* Try for 1000BASE-X asym pause */ +#define ADVERTISE_100BASE4 0x0200u /* Try for 100mbps 4k packets */ +#define ADVERTISE_PAUSE_CAP 0x0400u /* Try for pause */ +#define ADVERTISE_PAUSE_ASYM 0x0800u /* Try for asymetric pause */ +#define ADVERTISE_RESV 0x1000u /* Unused... */ +#define ADVERTISE_RFAULT 0x2000u /* Say we can detect faults */ +#define ADVERTISE_LPACK 0x4000u /* Ack link partners response */ +#define ADVERTISE_NPAGE 0x8000u /* Next page bit */ + +#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | ADVERTISE_CSMA) +#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF | ADVERTISE_100FULL) + +/* Link partner ability register. */ +#define LPA_SLCT 0x001fu /* Same as advertise selector */ +#define LPA_10HALF 0x0020u /* Can do 10mbps half-duplex */ +#define LPA_1000XFULL 0x0020u /* Can do 1000BASE-X full-duplex */ +#define LPA_10FULL 0x0040u /* Can do 10mbps full-duplex */ +#define LPA_1000XHALF 0x0040u /* Can do 1000BASE-X half-duplex */ +#define LPA_100HALF 0x0080u /* Can do 100mbps half-duplex */ +#define LPA_1000XPAUSE 0x0080u /* Can do 1000BASE-X pause */ +#define LPA_100FULL 0x0100u /* Can do 100mbps full-duplex */ +#define LPA_1000XPAUSE_ASYM 0x0100u /* Can do 1000BASE-X pause asym*/ +#define LPA_100BASE4 0x0200u /* Can do 100mbps 4k packets */ +#define LPA_PAUSE_CAP 0x0400u /* Can pause */ +#define LPA_PAUSE_ASYM 0x0800u /* Can pause asymetrically */ +#define LPA_RESV 0x1000u /* Unused... */ +#define LPA_RFAULT 0x2000u /* Link partner faulted */ +#define LPA_LPACK 0x4000u /* Link partner acked us */ +#define LPA_NPAGE 0x8000u /* Next page bit */ + +#define LPA_DUPLEX (LPA_10FULL | LPA_100FULL) +#define LPA_100 (LPA_100FULL | LPA_100HALF | LPA_100BASE4) + +/* Expansion register for auto-negotiation. */ +#define EXPANSION_NWAY 0x0001u /* Can do N-way auto-nego */ +#define EXPANSION_LCWP 0x0002u /* Got new RX page code word */ +#define EXPANSION_ENABLENPAGE 0x0004u /* This enables npage words */ +#define EXPANSION_NPCAPABLE 0x0008u /* Link partner supports npage */ +#define EXPANSION_MFAULTS 0x0010u /* Multiple faults detected */ +#define EXPANSION_RESV 0xffe0u /* Unused... */ + +#define ESTATUS_1000_TFULL 0x2000u /* Can do 1000BT Full */ +#define ESTATUS_1000_THALF 0x1000u /* Can do 1000BT Half */ + +/* N-way test register. */ +#define NWAYTEST_RESV1 0x00ffu /* Unused... */ +#define NWAYTEST_LOOPBACK 0x0100u /* Enable loopback for N-way */ +#define NWAYTEST_RESV2 0xfe00u /* Unused... */ + +/* 1000BASE-T Control register */ +#define ADVERTISE_1000FULL 0x0200u /* Advertise 1000BASE-T full duplex */ +#define ADVERTISE_1000HALF 0x0100u /* Advertise 1000BASE-T half duplex */ + +/* 1000BASE-T Status register */ +#define LPA_1000LOCALRXOK 0x2000u /* Link partner local receiver status */ +#define LPA_1000REMRXOK 0x1000u /* Link partner remote receiver status */ +#define LPA_1000FULL 0x0800u /* Link partner 1000BASE-T full duplex */ +#define LPA_1000HALF 0x0400u /* Link partner 1000BASE-T half duplex */ + +/* Indicates what features are supported by the interface. */ +#define SUPPORTED_10baseT_Half (1 << 0) +#define SUPPORTED_10baseT_Full (1 << 1) +#define SUPPORTED_100baseT_Half (1 << 2) +#define SUPPORTED_100baseT_Full (1 << 3) +#define SUPPORTED_1000baseT_Half (1 << 4) +#define SUPPORTED_1000baseT_Full (1 << 5) +#define SUPPORTED_Autoneg (1 << 6) +#define SUPPORTED_TP (1 << 7) +#define SUPPORTED_AUI (1 << 8) +#define SUPPORTED_MII (1 << 9) +#define SUPPORTED_FIBRE (1 << 10) +#define SUPPORTED_BNC (1 << 11) +#define SUPPORTED_10000baseT_Full (1 << 12) +#define SUPPORTED_Pause (1 << 13) +#define SUPPORTED_Asym_Pause (1 << 14) +#define SUPPORTED_2500baseX_Full (1 << 15) +#define SUPPORTED_Backplane (1 << 16) +#define SUPPORTED_1000baseKX_Full (1 << 17) +#define SUPPORTED_10000baseKX4_Full (1 << 18) +#define SUPPORTED_10000baseKR_Full (1 << 19) +#define SUPPORTED_10000baseR_FEC (1 << 20) + +/* Indicates what features are advertised by the interface. */ +#define ADVERTISED_10baseT_Half (1 << 0) +#define ADVERTISED_10baseT_Full (1 << 1) +#define ADVERTISED_100baseT_Half (1 << 2) +#define ADVERTISED_100baseT_Full (1 << 3) +#define ADVERTISED_1000baseT_Half (1 << 4) +#define ADVERTISED_1000baseT_Full (1 << 5) +#define ADVERTISED_Autoneg (1 << 6) +#define ADVERTISED_TP (1 << 7) +#define ADVERTISED_AUI (1 << 8) +#define ADVERTISED_MII (1 << 9) +#define ADVERTISED_FIBRE (1 << 10) +#define ADVERTISED_BNC (1 << 11) +#define ADVERTISED_10000baseT_Full (1 << 12) +#define ADVERTISED_Pause (1 << 13) +#define ADVERTISED_Asym_Pause (1 << 14) +#define ADVERTISED_2500baseX_Full (1 << 15) +#define ADVERTISED_Backplane (1 << 16) +#define ADVERTISED_1000baseKX_Full (1 << 17) +#define ADVERTISED_10000baseKX4_Full (1 << 18) +#define ADVERTISED_10000baseKR_Full (1 << 19) +#define ADVERTISED_10000baseR_FEC (1 << 20) + +/**************************************************************************/ +/* Public function declarations */ +/**************************************************************************/ + +/***************************************************************************/ /** + + */ +void TSE_phy_init(tse_instance_t *this_tse, uint8_t phy_addr); + +/***************************************************************************/ /** + + */ +void TSE_phy_set_link_speed(tse_instance_t *this_tse, + uint8_t phy_addr, + uint32_t speed_duplex_select); + +/***************************************************************************/ /** + + */ +void TSE_phy_autonegotiate(tse_instance_t *this_tse, uint8_t phy_addr); + +/***************************************************************************/ /** + + */ +uint8_t TSE_phy_get_link_status(tse_instance_t *this_tse, + uint8_t phy_addr, + tse_speed_t *speed, + uint8_t *fullduplex); + +#ifdef __cplusplus +} +#endif + +#endif /* PHY_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/vsc8575.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/vsc8575.c new file mode 100644 index 0000000..f4deef2 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTSE/vsc8575.c @@ -0,0 +1,245 @@ + +/******************************************************************************* + * Copyright 2018 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file vsc8575.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief Microchip's VSC8575 PHY driver implementation. + * + */ + +#include "phy.h" +#include "core_tse.h" +#include "coretse_regs.h" +#include "hal/hal.h" +#include "hal/hal_assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(VSC8575_PHY) + +#define PAGE_0 0x00 + +/* function prototypes ********************************************************/ +static void set_page(tse_instance_t *this_tse, uint8_t phy_addr, uint16_t page); + +/* functions ******************************************************************/ +void +TSE_phy_init(tse_instance_t *this_tse, uint8_t phy_addr) +{ + volatile uint16_t phy_reg; + volatile uint16_t id1 = 0, id2 = 0; + + set_page(this_tse, phy_addr, 0x0000); + + /* 16E3 bit 7 setting to 1 for SERDES MAC AN EN */ + set_page(this_tse, phy_addr, 0x0003); + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, 0x10); + phy_reg |= 0x80; + TSE_write_phy_reg(this_tse, phy_addr, 0x10, phy_reg); + + /* set microprocessor command to enable two MAC SGMII ports (reg 18G) */ + set_page(this_tse, phy_addr, 0x0010); + phy_reg = 0x80F0; + TSE_write_phy_reg(this_tse, phy_addr, 0x12, phy_reg); + phy_reg = 0x8000; + while ((phy_reg & 0x8000) != 0) + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, 0x12); + + /* phy software reset (mode control register) */ + set_page(this_tse, phy_addr, 0x0000); + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, 0x00); + phy_reg = phy_reg | 0x8000; + TSE_write_phy_reg(this_tse, phy_addr, 0x00, phy_reg); + phy_reg = 0x8000; + while ((phy_reg & 0x8000) != 0) + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, 0x00); + + set_page(this_tse, phy_addr, 0x0000); + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, 0x04); + phy_reg &= ~(0x1E); + TSE_write_phy_reg(this_tse, phy_addr, 0x04, phy_reg); + + /* 1000Base Control: FDX */ + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, 0x09); + phy_reg |= 0x200; + TSE_write_phy_reg(this_tse, phy_addr, 0x09, phy_reg); +} + +void +TSE_phy_set_link_speed(tse_instance_t *this_tse, uint8_t phy_addr, uint32_t speed_duplex_select) +{ + uint16_t phy_reg; + uint32_t inc; + uint32_t speed_select; + uint16_t const mii_advertise_bits[4] = {ADVERTISE_10FULL, + ADVERTISE_10HALF, + ADVERTISE_100FULL, + ADVERTISE_100HALF}; + + /* Set 10Mbps and 100Mbps advertisement. */ + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_ADVERTISE); + phy_reg &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF | ADVERTISE_100FULL); + + speed_select = speed_duplex_select; + + for (inc = 0u; inc < 4u; ++inc) + { + uint32_t advertise; + advertise = speed_select & 0x00000001u; + if (advertise != 0u) + phy_reg |= mii_advertise_bits[inc]; + + speed_select = speed_select >> 1u; + } + + TSE_write_phy_reg(this_tse, phy_addr, MII_ADVERTISE, phy_reg); + + /* Set 1000Mbps advertisement. */ + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_CTRL1000); + phy_reg &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); + + if ((speed_duplex_select & TSE_ANEG_1000M_FD) != 0u) + phy_reg |= ADVERTISE_1000FULL; + + if ((speed_duplex_select & TSE_ANEG_1000M_HD) != 0u) + phy_reg |= ADVERTISE_1000HALF; + + TSE_write_phy_reg(this_tse, phy_addr, MII_CTRL1000, phy_reg); +} + +void +TSE_phy_autonegotiate(tse_instance_t *this_tse, uint8_t phy_addr) +{ + uint32_t phy_reg; + uint16_t autoneg_complete; + volatile uint32_t copper_aneg_timeout = 1000000u; + volatile uint32_t sgmii_aneg_timeout = 1000000u; + + set_page(this_tse, phy_addr, 0x0000); + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, 0x00); + phy_reg |= 0x1200; + TSE_write_phy_reg(this_tse, phy_addr, 0x00, phy_reg); + + do + { + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, 0x01); + autoneg_complete = phy_reg & 0x0020u; + --copper_aneg_timeout; + } while (!autoneg_complete && (copper_aneg_timeout != 0u)); + + /* if copper link up */ + if ((phy_reg & 0x0004) != 0u) + { + uint8_t phy2_addr = 0x12; + /* enable autonegotiation */ + phy_reg = TSE_read_phy_reg(this_tse, phy2_addr, 0x00); + phy_reg |= 0x1000; + TSE_write_phy_reg(this_tse, phy2_addr, 0x00, phy_reg); + + /* restart autonegotiation */ + phy_reg = TSE_read_phy_reg(this_tse, phy2_addr, 0x00); + phy_reg |= 0x0200; + TSE_write_phy_reg(this_tse, phy2_addr, 0x00, phy_reg); + + do + { + phy_reg = TSE_read_phy_reg(this_tse, phy2_addr, 0x01); + autoneg_complete = phy_reg & 0x0020; + --sgmii_aneg_timeout; + } while ((!autoneg_complete) && (sgmii_aneg_timeout != 0u)); + } +} + +uint8_t +TSE_phy_get_link_status(tse_instance_t *this_tse, + uint8_t phy_addr, + tse_speed_t *speed, + uint8_t *fullduplex) +{ + volatile uint16_t phy_reg; + uint16_t copper_link_up; + uint8_t link_status; + + /* Find out if link is up between PHY and remote device.*/ + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, MII_BMSR); + copper_link_up = phy_reg & BMSR_LSTATUS; + + if (copper_link_up != TSE_LINK_DOWN) + { + uint16_t duplex; + uint16_t phy_speed; + + /* Link is up. */ + link_status = TSE_LINK_UP; + + /* Reg 28: aux. ctrl & stats */ + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, 0x1c); + /* FDX status - Bit 5 */ + duplex = phy_reg & (1 << 5); + /* Speed status - Bit 4..3 */ + phy_speed = phy_reg & 0x0018; + + if (TSE_HALF_DUPLEX == duplex) + *fullduplex = TSE_HALF_DUPLEX; + else + *fullduplex = TSE_FULL_DUPLEX; + + switch (phy_speed) + { + case 0x0010: + *speed = TSE_MAC1000MBPS; + break; + + case 0x0008: + *speed = TSE_MAC100MBPS; + break; + + case 0x0000: + *speed = TSE_MAC10MBPS; + break; + default: + *speed = TSE_INVALID_SPEED; + break; + } + } + else + { /* Link is down. */ + link_status = TSE_LINK_DOWN; + } + + return link_status; +} + +static void +set_page(tse_instance_t *this_tse, uint8_t phy_addr, uint16_t page) +{ + TSE_write_phy_reg(this_tse, phy_addr, 0x1F, page); +} + +void +phy_far_end_loopback(tse_instance_t *this_tse, uint8_t phy_addr, int mode) +{ + uint16_t phy_reg; + + phy_reg = TSE_read_phy_reg(this_tse, phy_addr, 0x17); + + if (mode == 1) + phy_reg = phy_reg | 0x0008; + else + phy_reg = phy_reg & ~0x0008; + + TSE_write_phy_reg(this_tse, phy_addr, 0x17, phy_reg); +} + +#endif + +#ifdef __cplusplus +} +#endif + +/******************************** END OF FILE ******************************/ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTimer/core_timer.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTimer/core_timer.c new file mode 100644 index 0000000..6e05bdb --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTimer/core_timer.c @@ -0,0 +1,158 @@ +/******************************************************************************* + * (c) Copyright 2007 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core_timer.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreTimer bare metal driver public API. + * + */ + +#include "core_timer.h" +#include "coretimer_regs.h" + +#ifndef NDEBUG +static timer_instance_t* NULL_timer_instance; +#endif + +/***************************************************************************//** + * TMR_init() + * See "core_timer.h" for details of how to use this function. + */ +void +TMR_init +( + timer_instance_t * this_timer, + addr_t address, + uint8_t mode, + uint32_t prescale, + uint32_t load_value +) +{ + HAL_ASSERT( this_timer != NULL_timer_instance ); + HAL_ASSERT( prescale <= PRESCALER_DIV_1024 ); + HAL_ASSERT( load_value != 0 ); + + this_timer->base_address = address; + + /* Disable interrupts. */ + HAL_set_32bit_reg_field( address, InterruptEnable,0 ); + + /* Disable timer. */ + HAL_set_32bit_reg_field( address, TimerEnable, 0 ); + + /* Clear pending interrupt. */ + HAL_set_32bit_reg( address, TimerIntClr, 1 ); + + /* Configure prescaler and load value. */ + HAL_set_32bit_reg( address, TimerPrescale, prescale ); + HAL_set_32bit_reg( address, TimerLoad, load_value ); + + /* Set the interrupt mode. */ + if ( mode == TMR_CONTINUOUS_MODE ) + { + HAL_set_32bit_reg_field( address, TimerMode, 0 ); + } + else + { + /* TMR_ONE_SHOT_MODE */ + HAL_set_32bit_reg_field( address, TimerMode, 1 ); + } +} + +/***************************************************************************//** + * TMR_start() + * See "core_timer.h" for details of how to use this function. + */ +void +TMR_start +( + timer_instance_t * this_timer +) +{ + HAL_ASSERT( this_timer != NULL_timer_instance ); + + HAL_set_32bit_reg_field( this_timer->base_address, TimerEnable, 1 ); +} + +/***************************************************************************//** + * TMR_stop() + * See "core_timer.h" for details of how to use this function. + */ +void +TMR_stop +( + timer_instance_t * this_timer +) +{ + HAL_ASSERT( this_timer != NULL_timer_instance ); + + HAL_set_32bit_reg_field( this_timer->base_address, TimerEnable, 0 ); +} + + +/***************************************************************************//** + * TMR_enable_int() + * See "core_timer.h" for details of how to use this function. + */ +void +TMR_enable_int +( + timer_instance_t * this_timer +) +{ + HAL_ASSERT( this_timer != NULL_timer_instance ); + + HAL_set_32bit_reg_field( this_timer->base_address, InterruptEnable, 1 ); +} + +/***************************************************************************//** + * TMR_clear_int() + * See "core_timer.h" for details of how to use this function. + */ +void +TMR_clear_int +( + timer_instance_t * this_timer +) +{ + HAL_ASSERT( this_timer != NULL_timer_instance ); + + HAL_set_32bit_reg( this_timer->base_address, TimerIntClr, 0x01 ); +} + +/***************************************************************************//** + * TMR_current_value() + * See "core_timer.h" for details of how to use this function. + */ +uint32_t +TMR_current_value +( + timer_instance_t * this_timer +) +{ + uint32_t value = 0; + HAL_ASSERT( this_timer != NULL_timer_instance ); + + value = HAL_get_32bit_reg( this_timer->base_address, TimerValue ); + + return value; +} + +/***************************************************************************//** + * TMR_reload() + * See "core_timer.h" for details of how to use this function. + */ +void TMR_reload +( + timer_instance_t * this_timer, + uint32_t load_value +) +{ + HAL_ASSERT( this_timer != NULL_timer_instance ); + HAL_ASSERT( load_value != 0 ); + + HAL_set_32bit_reg(this_timer->base_address, TimerLoad, load_value ); +} + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTimer/core_timer.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTimer/core_timer.h new file mode 100644 index 0000000..a92a926 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTimer/core_timer.h @@ -0,0 +1,322 @@ +/***************************************************************************//** + * Copyright 2007 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Core Timer bare metal software driver public API. + * + * @file core_timer.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreTimer prototypes + * + */ + +/*=========================================================================*//** + @mainpage CoreTimer Bare Metal Driver. + + ============================================================================== + Introduction + ============================================================================== + + The CoreTimer is an IP component that provides access to an + interrupt-generating, programmable decrementing counter over an Advanced + Peripheral Bus (APB) slave interface. + + This driver provides a set of functions for controlling CoreTimer as part of + the bare metal system where no operating system is available. + This driver can be adapted to be used as a part of an operating system, but + the implementation of the adaptation layer between the driver and the + operating system's driver model is outside the scope of this user guide. + + ============================================================================== + Driver Configuration + ============================================================================== + Your application software must configure the CoreTimer driver through calls + to TMR_init() for each CoreTimer instance in the hardware design. + This function configures a default set of parameters that include a CoreTimer + hardware instance base address, operating mode, prescaler value, and timer + load value. + + The CoreTimer supports the following two modes of operation: + - Continuous mode + - One-Shot Timer mode + + Continuous mode: + This is the default mode. + When zero is reached, the counter is reloaded with the start value, which is + stored in a programmable register, and continues to count down. + If the interrupt is enabled, this mode generates an interrupt at a regular + interval. + + One-Shot Timer mode: + The counter decrements from its high value and halts at zero. + The timer must be reprogrammed to begin counting down again. + This can be achieved by either loading a new value to the timer's counter + using TMR_reload() or by changing the operating mode through a call to + TMR_init(). + + Either mode of operation can be set during initialization using TMR_init(). + If interrupts are enabled, an interrupt is generated when the decrementing + counter reaches zero. + + ============================================================================== + Theory of Operation + ============================================================================== + + The timer is loaded by writing to the load register and then counts down to + zero if enabled. + To load the timer with an initial value to decrement from, use TMR_init(). + To enable the timer to count down to zero, use TMR_start(). + + The prescaler value can be set to divide down the clock to decrement the + CoreTimer counter. + The prescaler values can be set to trigger a pulse at every 2, 4, 8, 16, 32, + 64, 128, 256, 512, or 1024 clock periods. + + Use TMR_reload() to load new values into the timer's load register. + If TMR_reload() is called while the timer is already running, the timer + immediately begins to decrement from the new value. + + If interrupts are enabled with TMR_enable_int(), an interrupt is generated + when the timer reaches zero. + To clear the interrupt, write to the interrupt clear register using TMR_clear_int(). + + If the timer is operating in One-Shot Timer mode, it halts on reaching zero + until the timer is reloaded using TMR_reload() or until the Operating mode is + changed to Continuous mode. When operating in Continuous mode, the timer + reloads the count value that was initially set using TMR_init() and continues + to decrement. + The counter effectively generates a periodic interrupt when operating in + Continuous mode. + + To read the current counter value at any time, use TMR_current_value(). + + *//*=========================================================================*/ +#ifndef CORE_TIMER_H_ +#define CORE_TIMER_H_ + +#ifndef LEGACY_DIR_STRUCTURE +#include "hal/hal.h" + +#else +#include "hal.h" +#include "hal_assert.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + Timer Operating Mode + ======================================= + The following definitions select the operating mode of the CoreTimer driver. + They allow for selecting Continuous or One-Shot Timer modes. + + | Constant | Description | + | ------------------- | -------------------------------------------- | + | TMR_CONTINUOUS_MODE | Coretimer will operate in Continuous mode | + | TMR_ONE_SHOT_MODE | Coretimer will operate in One-Shot Timer mode | + + */ +#define TMR_CONTINUOUS_MODE 0 +#define TMR_ONE_SHOT_MODE 1 + + +/***************************************************************************//** + Prescaler Value + ======================================= + The following definitions configure the CoreTimer prescaler. + The prescaler divides down the clock to decrement the CoreTimer counter. + It can be configured to divide the clock by 2, 4, 8, 16, 32, 64, 128, 256, + 512, or 1024 clock periods. + + | Constant | Description | + | ------------------ | ----------------------------------------------| + | PRESCALER_DIV_2 | Scale down the timer's clock frequency by 2 | + | PRESCALER_DIV_4 | Scale down the timer's clock frequency by 4 | + | PRESCALER_DIV_8 | Scale down the timer's clock frequency by 8 | + | PRESCALER_DIV_16 | Scale down the timer's clock frequency by 16 | + | PRESCALER_DIV_32 | Scale down the timer's clock frequency by 32 | + | PRESCALER_DIV_64 | Scale down the timer's clock frequency by 64 | + | PRESCALER_DIV_128 | Scale down the timer's clock frequency by 128 | + | PRESCALER_DIV_256 | Scale down the timer's clock frequency by 256 | + | PRESCALER_DIV_512 | Scale down the timer's clock frequency by 512 | + | PRESCALER_DIV_1024 | Scale down the timer's clock frequency by 1024 | + +*/ +#define PRESCALER_DIV_2 0 +#define PRESCALER_DIV_4 1 +#define PRESCALER_DIV_8 2 +#define PRESCALER_DIV_16 3 +#define PRESCALER_DIV_32 4 +#define PRESCALER_DIV_64 5 +#define PRESCALER_DIV_128 6 +#define PRESCALER_DIV_256 7 +#define PRESCALER_DIV_512 8 +#define PRESCALER_DIV_1024 9 + +/***************************************************************************//** + There must be one instance of this structure for each instance of CoreTimer in + your system. + TMR_init() initializes this structure. + It identifies the various CoreTimer hardware instances in your system. + An initialized timer instance structure must be passed as the first parameter + to CoreTimer driver functions to identify which CoreTimer instance must perform + the requested operation. + Software using this driver must only create one single instance of this data + structure for each hardware timer instance in the system. + */ +typedef struct __timer_instance_t +{ + addr_t base_address; +} timer_instance_t; + +/*------------------------Public Function-------------------------------------*/ + +/***************************************************************************//** + This function initializes the data structure and sets relevant CoreTimer + registers. + It prepares the timer for use in a given hardware or software configuration. + It must be called before any other timer API function. + The timer won't start counting down immediately after this function is + called. + It is necessary to call TMR_start() to start the timer decrementing. + The CoreTimer interrupt is disabled as part of this function. + + + + @param this_timer Pointer to a timer_instance_t structure holding all + relevant data associated with the target timer hardware + instance. This pointer identifies the target CoreTimer + hardware instance in subsequent calls to the CoreTimer + functions. + @param address Base address in the processor's memory map of the + registers of the CoreTimer instance being initialized. + @param mode This parameter selects the operating mode of the timer + instance. This can be either TMR_CONTINUOUS_MODE + or TMR_ONE_SHOT_MODE. + @param prescaler This parameter selects the prescaler divider that divides + down the clock used to decrement the timer's + counter. This can be set using one of the + `PRESCALER_DIV_` definitions, where `` is the + divider's value. + @param load_value This parameter sets the timer's load value, from which the + CoreTimer counter decrements + In Continuous mode, this value reloads the timer's counter + whenever it reaches zero. + @return This function does not return any value. + */ +void TMR_init(timer_instance_t *this_timer, + addr_t address, + uint8_t mode, + uint32_t prescaler, + uint32_t load_value); + +/***************************************************************************//** + This function starts the timer counting down. + This function must be called after calling TMR_init(). + It does not need to be called after each call to TMR_reload() when the timer is + used in One-Shot Timer mode. + + @param this_timer Pointer to a timer_instance_t structure holding all + relevant data associated with the target timer hardware + instance. This pointer identifies the target + CoreTimer hardware instance. + @return This function does not return any value. + */ +void TMR_start(timer_instance_t *this_timer); + +/***************************************************************************//** + This function stops the timer from counting down. + It stops interrupts from being generated when Continuous mode is used + and interrupts must be paused. + + @param this_timer Pointer to a timer_instance_t structure holding all + relevant data associated with the target timer hardware + instance. This pointer identifies the target + CoreTimer hardware instance. + @return This function does not return any value. + */ +void TMR_stop(timer_instance_t *this_timer); + +/***************************************************************************//** + This function enables the timer interrupt. It allows the interrupt signal + coming out of CoreTimer to be asserted. + + @param this_timer Pointer to a timer_instance_t structure holding all + relevant data associated with the target timer hardware + instance. This pointer identifies the target + CoreTimer hardware instance. + @return This function does not return any value. + */ +void TMR_enable_int(timer_instance_t *this_timer); + +/***************************************************************************//** + This function clears the timer interrupt. + It must be called within the interrupt handler, servicing interrupts from the + timer. + Failure to clear the timer interrupt results in the interrupt signal + generated from CoreTimer remaining asserted. This assertion may cause the + interrupt service routine to be continuously called, causing the system to + lock up. + + @param this_timer Pointer to a timer_instance_t structure holding all + relevant data associated with the target timer hardware + instance. This pointer identifies the target + CoreTimer hardware instance. + @return This function does not return any value. + */ +void TMR_clear_int(timer_instance_t *this_timer); + +/***************************************************************************//** + This function returns the current value of the counter. + + @param this_timer Pointer to a timer_instance_t structure holding all + relevant data associated with the target timer hardware + instance. This pointer identifies the target + CoreTimer hardware instance. + + @return Returns the current value of the timer counter value. + */ +uint32_t TMR_current_value(timer_instance_t *this_timer); + +/***************************************************************************//** + This function is used in One-Shot Timer mode. + It reloads the timer counter with the value passed as a parameter. + The timer counter begins to decrement again after calling TMR_reload(), + it is not required to call TMR_start() again. + + @param this_timer Pointer to a timer_instance_t structure holding all + relevant data associated with the target timer hardware + instance. This pointer identifies the target + CoreTimer hardware instance. + @param load_value This parameter sets the timer's load value, from which the + CoreTimer counter decrements. + @return This function does not return any value. + */ +void TMR_reload(timer_instance_t *this_timer, uint32_t load_value); + +#ifdef __cplusplus +} +#endif + +#endif /* CORE_TIMER_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTimer/coretimer_regs.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTimer/coretimer_regs.h new file mode 100644 index 0000000..06d515c --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreTimer/coretimer_regs.h @@ -0,0 +1,113 @@ +/******************************************************************************* + * (c) Copyright 2007 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file coretimer_regs.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreTimer register definitions + * + */ + +#ifndef __CORE_TIMER_REGISTERS +#define __CORE_TIMER_REGISTERS 1 + +/*------------------------------------------------------------------------------ + * TimerLoad register details + */ +#define TimerLoad_REG_OFFSET 0x00 + +/* + * LoadValue bits. + */ +#define LoadValue_OFFSET 0x00 +#define LoadValue_MASK 0xFFFFFFFF +#define LoadValue_SHIFT 0 + +/*------------------------------------------------------------------------------ + * TimerValue register details + */ +#define TimerValue_REG_OFFSET 0x04 + +/* + * CurrentValue bits. + */ +#define CurrentValue_OFFSET 0x04 +#define CurrentValue_MASK 0xFFFFFFFF +#define CurrentValue_SHIFT 0 + +/*------------------------------------------------------------------------------ + * TimerControl register details + */ +#define TimerControl_REG_OFFSET 0x08 + +/* + * TimerEnable bits. + */ +#define TimerEnable_OFFSET 0x08 +#define TimerEnable_MASK 0x00000001 +#define TimerEnable_SHIFT 0 + +/* + * InterruptEnable bits. + */ +#define InterruptEnable_OFFSET 0x08 +#define InterruptEnable_MASK 0x00000002 +#define InterruptEnable_SHIFT 1 + +/* + * TimerMode bits. + */ +#define TimerMode_OFFSET 0x08 +#define TimerMode_MASK 0x00000004 +#define TimerMode_SHIFT 2 + +/*------------------------------------------------------------------------------ + * TimerPrescale register details + */ +#define TimerPrescale_REG_OFFSET 0x0C + +/* + * Prescale bits. + */ +#define Prescale_OFFSET 0x0C +#define Prescale_MASK 0x0000000F +#define Prescale_SHIFT 0 + +/*------------------------------------------------------------------------------ + * TimerIntClr register details + */ +#define TimerIntClr_REG_OFFSET 0x10 + +/* + * TimerIntClr bits. + */ +#define TimerIntClr_OFFSET 0x10 +#define TimerIntClr_MASK 0xFFFFFFFF +#define TimerIntClr_SHIFT 0 + +/*------------------------------------------------------------------------------ + * TimerRIS register details + */ +#define TimerRIS_REG_OFFSET 0x14 + +/* + * RawTimerInterrupt bits. + */ +#define RawTimerInterrupt_OFFSET 0x14 +#define RawTimerInterrupt_MASK 0x00000001 +#define RawTimerInterrupt_SHIFT 0 + +/*------------------------------------------------------------------------------ + * TimerMIS register details + */ +#define TimerMIS_REG_OFFSET 0x18 + +/* + * TimerInterrupt bits. + */ +#define TimerInterrupt_OFFSET 0x18 +#define TimerInterrupt_MASK 0x00000001 +#define TimerInterrupt_SHIFT 0 + +#endif /* __CORE_TIMER_REGISTERS */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreUARTapb/core_uart_apb.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreUARTapb/core_uart_apb.c new file mode 100644 index 0000000..3e90942 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreUARTapb/core_uart_apb.c @@ -0,0 +1,297 @@ +/******************************************************************************* + * (c) Copyright 2007-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file core_uart_apb.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreUARTapb driver implementation. See file "core_uart_apb.h" for + * description of the functions implemented in this file. + * + */ + +#include "coreuartapb_regs.h" +#include "core_uart_apb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define NULL_INSTANCE ( ( UART_instance_t* ) 0 ) +#define NULL_BUFFER ( ( uint8_t* ) 0 ) + +#define MAX_LINE_CONFIG ( ( uint8_t )( DATA_8_BITS | ODD_PARITY ) ) +#define MAX_BAUD_VALUE ( ( uint16_t )( 0x1FFF ) ) +#define STATUS_ERROR_MASK ( ( uint8_t )( STATUS_PARITYERR_MASK | \ + STATUS_OVERFLOW_MASK | \ + STATUS_FRAMERR_MASK ) ) +#define BAUDVALUE_LSB ( (uint16_t) (0x00FF) ) +#define BAUDVALUE_MSB ( (uint16_t) (0xFF00) ) +#define BAUDVALUE_SHIFT ( (uint8_t) (5) ) + +#define STATUS_ERROR_OFFSET STATUS_PARITYERR_SHIFT + +/***************************************************************************//** + * UART_init() + * See "core_uart_apb.h" for details of how to use this function. + */ +void +UART_init +( + UART_instance_t * this_uart, + addr_t base_addr, + uint16_t baud_value, + uint8_t line_config +) +{ + uint8_t rx_full; + + HAL_ASSERT( this_uart != NULL_INSTANCE ) + HAL_ASSERT( line_config <= MAX_LINE_CONFIG ) + HAL_ASSERT( baud_value <= MAX_BAUD_VALUE ) + + if( ( this_uart != NULL_INSTANCE ) && + ( line_config <= MAX_LINE_CONFIG ) && + ( baud_value <= MAX_BAUD_VALUE ) ) + { + /* + * Store lower 8-bits of baud value in CTRL1. + */ + HAL_set_8bit_reg( base_addr, CTRL1, (uint_fast8_t)(baud_value & + BAUDVALUE_LSB ) ); + + /* + * Extract higher 5-bits of baud value and store in higher 5-bits + * of CTRL2, along with line configuration in lower 3 three bits. + */ + HAL_set_8bit_reg( base_addr, CTRL2, (uint_fast8_t)line_config | + (uint_fast8_t)((baud_value & + BAUDVALUE_MSB) >> BAUDVALUE_SHIFT ) ); + + this_uart->base_address = base_addr; +#ifndef NDEBUG + { + uint8_t config; + uint8_t temp; + uint16_t baud_val; + baud_val = HAL_get_8bit_reg( this_uart->base_address, CTRL1 ); + config = HAL_get_8bit_reg( this_uart->base_address, CTRL2 ); + /* + * To resolve operator precedence between & and << + */ + temp = ( config & (uint8_t)(CTRL2_BAUDVALUE_MASK ) ); + baud_val |= (uint16_t)( (uint16_t)(temp) << BAUDVALUE_SHIFT ); + config &= (uint8_t)(~CTRL2_BAUDVALUE_MASK); + HAL_ASSERT( baud_val == baud_value ); + HAL_ASSERT( config == line_config ); + } +#endif + + /* + * Flush the receive FIFO of data that may have been received before the + * driver was initialized. + */ + rx_full = HAL_get_8bit_reg( this_uart->base_address, STATUS ) & + STATUS_RXFULL_MASK; + while ( rx_full ) + { + HAL_get_8bit_reg( this_uart->base_address, RXDATA ); + rx_full = HAL_get_8bit_reg( this_uart->base_address, STATUS ) & + STATUS_RXFULL_MASK; + } + + /* + * Clear status of the UART instance. + */ + this_uart->status = (uint8_t)0; + } +} + +/***************************************************************************//** + * UART_send() + * See "core_uart_apb.h" for details of how to use this function. + */ +void +UART_send +( + UART_instance_t * this_uart, + const uint8_t * tx_buffer, + size_t tx_size +) +{ + size_t char_idx; + uint8_t tx_ready; + + HAL_ASSERT( this_uart != NULL_INSTANCE ) + HAL_ASSERT( tx_buffer != NULL_BUFFER ) + HAL_ASSERT( tx_size > 0 ) + + if( (this_uart != NULL_INSTANCE) && + (tx_buffer != NULL_BUFFER) && + (tx_size > (size_t)0) ) + { + for ( char_idx = (size_t)0; char_idx < tx_size; char_idx++ ) + { + /* Wait for UART to become ready to transmit. */ + do { + tx_ready = HAL_get_8bit_reg( this_uart->base_address, STATUS ) & + STATUS_TXRDY_MASK; + } while ( !tx_ready ); + /* Send next character in the buffer. */ + HAL_set_8bit_reg( this_uart->base_address, TXDATA, + (uint_fast8_t)tx_buffer[char_idx] ); + } + } +} + +/***************************************************************************//** + * UART_fill_tx_fifo() + * See "core_uart_apb.h" for details of how to use this function. + */ +size_t +UART_fill_tx_fifo +( + UART_instance_t * this_uart, + const uint8_t * tx_buffer, + size_t tx_size +) +{ + uint8_t tx_ready; + size_t size_sent = 0u; + + HAL_ASSERT( this_uart != NULL_INSTANCE ) + HAL_ASSERT( tx_buffer != NULL_BUFFER ) + HAL_ASSERT( tx_size > 0 ) + + /* Fill the UART's Tx FIFO until the FIFO is full or the complete input + * buffer has been written. */ + if( (this_uart != NULL_INSTANCE) && + (tx_buffer != NULL_BUFFER) && + (tx_size > 0u) ) + { + tx_ready = HAL_get_8bit_reg( this_uart->base_address, STATUS ) & + STATUS_TXRDY_MASK; + if ( tx_ready ) + { + do { + HAL_set_8bit_reg( this_uart->base_address, TXDATA, + (uint_fast8_t)tx_buffer[size_sent] ); + size_sent++; + tx_ready = HAL_get_8bit_reg( this_uart->base_address, STATUS ) & + STATUS_TXRDY_MASK; + } while ( (tx_ready) && ( size_sent < tx_size ) ); + } + } + return size_sent; +} + +/***************************************************************************//** + * UART_get_rx() + * See "core_uart_apb.h" for details of how to use this function. + */ +size_t +UART_get_rx +( + UART_instance_t * this_uart, + uint8_t * rx_buffer, + size_t buff_size +) +{ + uint8_t new_status; + uint8_t rx_full; + size_t rx_idx = 0u; + + HAL_ASSERT( this_uart != NULL_INSTANCE ) + HAL_ASSERT( rx_buffer != NULL_BUFFER ) + HAL_ASSERT( buff_size > 0 ) + + if( (this_uart != NULL_INSTANCE) && + (rx_buffer != NULL_BUFFER) && + (buff_size > 0u) ) + { + rx_idx = 0u; + new_status = HAL_get_8bit_reg( this_uart->base_address, STATUS ); + this_uart->status |= new_status; + rx_full = new_status & STATUS_RXFULL_MASK; + while ( ( rx_full ) && ( rx_idx < buff_size ) ) + { + rx_buffer[rx_idx] = HAL_get_8bit_reg( this_uart->base_address, + RXDATA ); + rx_idx++; + new_status = HAL_get_8bit_reg( this_uart->base_address, STATUS ); + this_uart->status |= new_status; + rx_full = new_status & STATUS_RXFULL_MASK; + } + } + return rx_idx; +} + +/***************************************************************************//** + * UART_polled_tx_string() + * See "core_uart_apb.h" for details of how to use this function. + */ +void +UART_polled_tx_string +( + UART_instance_t * this_uart, + const uint8_t * p_sz_string +) +{ + uint32_t char_idx; + uint8_t tx_ready; + + HAL_ASSERT( this_uart != NULL_INSTANCE ) + HAL_ASSERT( p_sz_string != NULL_BUFFER ) + + if( ( this_uart != NULL_INSTANCE ) && ( p_sz_string != NULL_BUFFER ) ) + { + char_idx = 0U; + while( 0U != p_sz_string[char_idx] ) + { + /* Wait for UART to become ready to transmit. */ + do { + tx_ready = HAL_get_8bit_reg( this_uart->base_address, STATUS ) & + STATUS_TXRDY_MASK; + } while ( !tx_ready ); + /* Send next character in the buffer. */ + HAL_set_8bit_reg( this_uart->base_address, TXDATA, + (uint_fast8_t)p_sz_string[char_idx] ); + char_idx++; + } + } +} + +/***************************************************************************//** + * UART_get_rx_status() + * See "core_uart_apb.h" for details of how to use this function. + */ +uint8_t +UART_get_rx_status +( + UART_instance_t * this_uart +) +{ + uint8_t status = UART_APB_INVALID_PARAM; + + HAL_ASSERT( this_uart != NULL_INSTANCE ) + /* + * Extract UART error status and place in lower bits of "status". + * Bit 0 - Parity error status + * Bit 1 - Overflow error status + * Bit 2 - Frame error status + */ + if( this_uart != NULL_INSTANCE ) + { + status = ( ( this_uart->status & STATUS_ERROR_MASK ) >> + STATUS_ERROR_OFFSET ); + /* + * Clear the sticky status for this instance. + */ + this_uart->status = (uint8_t)0; + } + return status; +} + +#ifdef __cplusplus +} +#endif diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreUARTapb/core_uart_apb.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreUARTapb/core_uart_apb.h new file mode 100644 index 0000000..c310592 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreUARTapb/core_uart_apb.h @@ -0,0 +1,451 @@ +/******************************************************************************* + * (c) Copyright 2007-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * @file core_uart_apb.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief This file contains the application programming interface for the + * CoreUARTapb bare metal driver. + * + */ +/*=========================================================================*//** + @mainpage CoreUARTapb Bare Metal Driver. + + @section intro_sec Introduction + CoreUARTapb is an implementation of the Universal Asynchronous + Receiver/Transmitter aimed at minimal FPGA tile usage within a Microchip FPGA. + The CoreUARTapb bare metal software driver is designed to be used in systems + with no operating system. + + The CoreUARTapb driver provides functions for basic polled transmitting and + receiving operations. It also provide functions that allow the use of the + CoreUARTapb in interrupt-driven mode but leaves the management of interrupts + to the calling application, as interrupt enabling and disabling are not + controlled through the CoreUARTapb registers. The CoreUARTapb driver is + provided as C source code. + + @section driver_configuration Driver Configuration + Your application software should configure the CoreUARTapb driver by calling + the UART_init() function for each CoreUARTapb instance in the hardware design. + The configuration parameters include the CoreUARTapb hardware instance base + address and other runtime parameters, such as baud rate, bit width, and + parity. No CoreUARTapb hardware configuration parameters are needed by the + driver, apart from the CoreUARTapb hardware instance base address. Hence, no + additional configuration files are required to use the driver. + + A CoreUARTapb hardware instance is generated with fixed baud rate, character + size, and parity configuration settings as part of the hardware flow. The + baud_value and line_config parameter values passed to the UART_init() function + have no effect if fixed values were selected for the baud rate, character + size, and parity in the hardware configuration of CoreUARTapb. When fixed + values are selected for these hardware configuration parameters, the driver is + unable to overwrite the fixed values in the CoreUARTapb control registers, + CTRL1 and CTRL2. + + @section theory_op Theory of Operation + The CoreUARTapb software driver is designed to allow the control of multiple + instances of CoreUARTapb. Each instance of CoreUARTapb in the hardware design + is associated with a single instance of the UART_instance_t structure in the + software. You need to allocate memory for one unique UART_instance_t structure + instance for each CoreUARTapb hardware instance. The contents of these data + structures are initialized while calling the UART_init() function. A pointer + to the structure is passed to the subsequent driver functions in order to + identify the CoreUARTapb hardware instance you wish to perform the requested + operation on. + + Note: Do not attempt to directly manipulate the content of UART_instance_t + structures. This structure is only intended to be modified by the driver + function. + + Once initialized, the driver transmits and receives data. Transmit is + performed using the UART_send() function. If this function blocks, then it + returns only when the data passed to it has been sent to the CoreUARTapb + hardware. Data received by the CoreUARTapb hardware is read by the user + application using the UART_get_rx() function. + + The UART_fill_tx_fifo() function is also provided as a part of the + interrupt-driven transmit. This function fills the CoreUARTapb hardware + transmit FIFO with the content of a data buffer passed as a parameter before + returning. The control of the interrupts must be implemented outside the + driver, as the CoreUARTapb hardware does not provide the ability to enable + or disable its interrupt sources. + + The UART_polled_tx_string() function is provided to transmit a NULL-terminated + string in polled mode. If this function blocks, then it returns only when the + data passed to it has been sent to the CoreUARTapb hardware. + + The UART_get_rx_status() function returns the error status of the CoreUARTapb + receiver. This is used by applications to take appropriate action in case of + receiver errors. +*//*=========================================================================*/ +#ifndef __CORE_UART_APB_H +#define __CORE_UART_APB_H 1 + +#ifndef LEGACY_DIR_STRUCTURE +#include "hal/hal.h" + +#else +#include "hal.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + Data Bits Length Defines + ======================== + These constants define the data length in a UART packet. + | Constant | Description | + |-------------|---------------------------------------| + | DATA_7_BITS | Data length is 7-bits | + | DATA_8_BITS | Data length is 8-bits | + */ +#define DATA_7_BITS 0x00u +#define DATA_8_BITS 0x01u + +/***************************************************************************//** + Parity Defines + ============== + These constants define parity check options. + | Constant | Description | + |-------------|---------------------------------------| + | NO_PARITY | No Parity bit | + | EVEN_PARITY | Even Parity bit | + | ODD_PARITY | ODD Parity bit | + */ +#define NO_PARITY 0x00u +#define EVEN_PARITY 0x02u +#define ODD_PARITY 0x06u + +/***************************************************************************//** + Error Status Definitions + ======================== + These constants define the different types of possible errors in UART + transmission of data. + | Constant | Description | + |-------------------------|---------------------------------------| + | UART_APB_PARITY_ERROR | Data parity error | + | UART_APB_OVERFLOW_ERROR | Data overflow error | + | UART_APB_FRAMING_ERROR | Data framing error | + | UART_APB_NO_ERROR | No error | + | UART_APB_INVALID_PARAM | Invalid parameter | + */ +#define UART_APB_PARITY_ERROR 0x01u +#define UART_APB_OVERFLOW_ERROR 0x02u +#define UART_APB_FRAMING_ERROR 0x04u +#define UART_APB_NO_ERROR 0x00u +#define UART_APB_INVALID_PARAM 0xFFu + +/***************************************************************************//** + * There should be one instance of this structure for each instance of + * CoreUARTapb in your system. This structure instance identifies various UARTs + * in a system and should be passed as first parameter to UART functions to + * identify which UART performs the requested operation. The 'status' element in + * the structure is used to provide sticky status information. + */ +typedef struct +{ + addr_t base_address; + uint8_t status; +} UART_instance_t; + +/***************************************************************************//** + * The UART_init() function initializes the UART with the configuration passed + * as parameters. The configuration parameters are the baud_value that generates + * the baud rate and the line configuration (bit length and parity). + * + * @param this_uart The this_uart parameter is a pointer to the + * UART_instance_t structure, which holds all data regarding + * this instance of the CoreUARTapb. This pointer is used to + * identify the target CoreUARTapb hardware instance in + * subsequent calls to the CoreUARTapb functions. + * @param base_addr The base_address parameter is the base address in the + * processor's memory map for the registers of the + * CoreUARTapb instance being initialized. + * @param baud_value The baud_value parameter selects the baud rate for the + * UART. The baud value is calculated from the frequency of + * the system clock in hertz and the desired baud rate using + * the following equation: + * baud_value = (clock / (baud_rate * 16)) - 1. + * The baud_value parameter must be a value in the range 0 + * to 8191 (or 0x0000 to 0x1FFF). + * @param line_config This parameter is the line configuration, specifies the + * bit length and parity settings. This is the logical OR of: + * - DATA_7_BITS + * - DATA_8_BITS + * - NO_PARITY + * - EVEN_PARITY + * - ODD_PARITY + * For example, 8 bits even parity would be specified as + * (DATA_8_BITS | EVEN_PARITY). + * @return This function does not return a value. + * @example + * @code + * #define BAUD_VALUE_57600 25 + * + * #define COREUARTAPB0_BASE_ADDR 0xC3000000UL + * + * UART_instance_t g_uart; + * int main() + * { + * UART_init(&g_uart, COREUARTAPB0_BASE_ADDR, + BAUD_VALUE_57600, (DATA_8_BITS | EVEN_PARITY)); + * } + * @endcode + */ +void +UART_init +( + UART_instance_t * this_uart, + addr_t base_addr, + uint16_t baud_value, + uint8_t line_config +); + +/***************************************************************************//** + * The UART_send() function is used to transmit data. It transfers the content + * of the transmitter data buffer, passed as a function parameter, into the + * UART's hardware transmitter FIFO. It returns when the full content of the + * transmitter data buffer has been transferred to the UART's transmitter FIFO. + * + * Note: You should not assume that the data you are sending using this function + * has been received at the other end by the time this function returns. The + * actual transmission over the serial connection is still be taking place at + * the time of the function return. It is safe to release or reuse the memory + * used as the transmit buffer once this function returns. + * + * @param this_uart The this_uart parameter is a pointer to the + * UART_instance_t structure, which holds all data + * regarding this instance of the CoreUARTapbUART. + * @param tx_buffer The tx_buffer parameter is a pointer to a buffer that + * contains the data to be transmitted. + * @param tx_size The tx_size parameter is the size in bytes of the + * transmitted data. + * + * @return This function does not return a value. + * + * @example + * @code + * uint8_t testmsg1[] = {"\n\r\n\r\n\rUART_send() test message 1"}; + * UART_send(&g_uart,(const uint8_t *)&testmsg1,sizeof(testmsg1)); + * @endcode + */ +void +UART_send +( + UART_instance_t * this_uart, + const uint8_t * tx_buffer, + size_t tx_size +); + +/***************************************************************************//** + * The UART_fill_tx_fifo() function fills the UART's transmitter hardware FIFO + * with the data found in the transmitter buffer that is passed in as a + * function parameter. The function returns either when the FIFO is full or + * when the complete contents of the transmitter buffer have been copied into + * the FIFO. It returns the number of bytes copied into the UART's transmitter + * hardware FIFO. This function is intended to be used as part of + * interrupt-driven transmission. + * + * Note: You should not assume that the data you transmit using this function + * has been received at the other end by the time this function returns. The + * actual transmission over the serial connection is still be taking place at + * the time of the function return. + * + * @param this_uart The this_uart parameter is a pointer to the + * UART_instance_t structure, which holds all data + * regarding this instance of the UART. + * @param tx_buffer The tx_buffer parameter is a pointer to a buffer that + * contains the data to be transmitted. + * @param tx_size The tx_size parameter is the size in bytes of the + * transmitted data. + * @return This function returns the number of bytes copied into + * the UART's transmitter hardware FIFO. + * + * @example + * @code + * void send_using_interrupt + * ( + * uint8_t * pbuff, + * size_t tx_size + * ) + * { + * size_t size_in_fifo; + * size_in_fifo = UART_fill_tx_fifo( &g_uart, pbuff, tx_size ); + * } + * @endcode + */ +size_t +UART_fill_tx_fifo +( + UART_instance_t * this_uart, + const uint8_t * tx_buffer, + size_t tx_size +); + +/***************************************************************************//** + * The UART_get_rx() function reads the content of the UART's receiver hardware + * FIFO and stores it in the receiver buffer that is passed in as a function + * parameter. It copies either the full contents of the FIFO into the receiver + * buffer, or just enough data from the FIFO to fill the receiver buffer, + * depending on the size of the receiver buffer. The size of the receiver + * buffer is passed in as a function parameter. UART_get_rx() returns the number + * of bytes copied into the receiver buffer. If no data was received at the time + * the function is called, the function returns 0. + * + * Note: This function reads and accumulates the receiver status of the + * CoreUARTapb instance before reading each byte from the receiver's + * data register/FIFO. This allows the driver to maintain a sticky + * record of any receiver errors that occur as the UART receives each + * data byte; receiver errors would otherwise be lost after each read + * from the receiver's data register. A call to the UART_get_rx_status() + * function returns any receiver errors accumulated during the execution + * of the UART_get_rx() function. + * + * Note: When FIFO mode is disabled in the CoreUARTapb hardware configuration, + * the driver accumulates a sticky record of any parity errors, framing + * errors, or overflow errors. When FIFO mode is enabled, the driver + * accumulates a sticky record of overflow errors only; in this case, + * interrupts must be used to handle parity errors or framing errors. + * + * @param this_uart The this_uart parameter is a pointer to the + * UART_instance_t structure, which holds all data + * regarding this instance of the UART. + * @param rx_buffer The rx_buffer parameter is a pointer to a buffer where + * the received data is copied. + * @param buff_size The buff_size parameter is the size of the receive + * buffer in bytes. + * @return This function returns the number of bytes copied into + * the receive buffer. + * + * @example + * @code + * #define MAX_RX_DATA_SIZE 256 + * + * uint8_t rx_data[MAX_RX_DATA_SIZE]; + * uint8_t rx_size = 0; + * + * rx_size = UART_get_rx( &g_uart, rx_data, sizeof(rx_data) ); + * @endcode + */ +size_t +UART_get_rx +( + UART_instance_t * this_uart, + uint8_t * rx_buffer, + size_t buff_size +); + +/***************************************************************************//** + * The UART_polled_tx_string() function is used to transmit a NULL ('\0') + * terminated string. Internally, it polls for the transmit ready status and + * transfers the text starting at the address pointed by p_sz_string into + * the UART's hardware transmitter FIFO. It is a blocking function and returns + * only when the complete string has been transferred to the UART's transmit + * FIFO. + * + * Note: You should not assume that the data you transmit using this function + * has been received at the other end by the time this function returns. The + * actual transmission over the serial connection is still be taking place at + * the time of the function return. + * + * @param this_uart The this_uart parameter is the pointer to a + * UART_instance_t structure, which holds all data + * regarding this instance of the UART. + * @param p_sz_string The p_sz_string parameter is a pointer to a buffer + * containing the NULL ('\0') terminated string to be + * transmitted. + * @return This function does not return a value. + * + * @example + * @code + * uint8_t testmsg1[] = {"\r\n\r\nUART_polled_tx_string() test message 1\0"}; + * UART_polled_tx_string(&g_uart,(const uint8_t *)&testmsg1); + * @endcode + */ +void +UART_polled_tx_string +( + UART_instance_t * this_uart, + const uint8_t * p_sz_string +); + +/***************************************************************************//** + * The UART_get_rx_status() function returns the receiver error status of the + * CoreUARTapb instance. It reads both the current error status of the receiver + * and the accumulated error status from preceding calls to the UART_get_rx() + * function and combines them using a bitwise OR. It returns the cumulative + * parity, framing, and overflow error status of the receiver, since the + * previous call to UART_get_rx_status() as an 8-bit encoded value. + * + * Note: The UART_get_rx() function reads and accumulates the receiver status + * of the CoreUARTapb instance before reading each byte from the receiver's data + * register/FIFO. The driver maintains a sticky record of the cumulative error + * status, which persists after the UART_get_rx() function returns. The + * UART_get_rx_status() function clears this accumulated record of receiver + * errors before returning. + * + * @param this_uart The this_uart parameter is a pointer to a + * UART_instance_t structure which holds all data regarding + * this instance of the UART. + * @return This function returns the UART receiver error status as + * an 8-bit encoded value. The return value is 0, if there + * are no receiver errors occurred. The driver provides a + * set of bit mask constants, which should be compared with + * and/or used to mask the returned value to determine the + * receiver error status. + * When the return value is compared to the following bit + * masks, a non-zero result indicates that the + * corresponding error occurred: + * UART_APB_PARITY_ERROR (bit mask = 0x01) + * UART_APB_OVERFLOW_ERROR (bit mask = 0x02) + * UART_APB_FRAMING_ERROR (bit mask = 0x04) + * When the return value is compared to the following bit + * mask, a non-zero result indicates that no error + * occurred: + * UART_APB_NO_ERROR (0x00) + * + * @example + * @code + * UART_instance_t g_uart; + * uint8_t rx_data[MAX_RX_DATA_SIZE]; + * uint8_t err_status; + * err_status = UART_get_err_status(&g_uart); + * + * if(UART_APB_NO_ERROR == err_status ) + * { + * rx_size = UART_get_rx( &g_uart, rx_data, MAX_RX_DATA_SIZE ); + * } + * @endcode + */ +uint8_t +UART_get_rx_status +( + UART_instance_t * this_uart +); + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_UART_APB_H */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreUARTapb/coreuartapb_regs.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreUARTapb/coreuartapb_regs.h new file mode 100644 index 0000000..f22f6c5 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/CoreUARTapb/coreuartapb_regs.h @@ -0,0 +1,133 @@ +/******************************************************************************* + * (c) Copyright 2007-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file coreuartapb_regs.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief CoreUARTapb register definitions + */ + +#ifndef __CORE_UART_APB_REGISTERS +#define __CORE_UART_APB_REGISTERS 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/*------------------------------------------------------------------------------ + * TxData register details + */ +#define TXDATA_REG_OFFSET 0x0u + +/* + * TxData bits. + */ +#define TXDATA_OFFSET 0x0u +#define TXDATA_MASK 0xFFu +#define TXDATA_SHIFT 0u + +/*------------------------------------------------------------------------------ + * RxData register details + */ +#define RXDATA_REG_OFFSET 0x4u + +/* + * RxData bits. + */ +#define RXDATA_OFFSET 0x4u +#define RXDATA_MASK 0xFFu +#define RXDATA_SHIFT 0u + +/*------------------------------------------------------------------------------ + * ControReg1 register details + */ +#define CTRL1_REG_OFFSET 0x8u + +/* + * Baud value (Lower 8-bits) + */ +#define CTRL1_BAUDVALUE_OFFSET 0x8u +#define CTRL1_BAUDVALUE_MASK 0xFFu +#define CTRL1_BAUDVALUE_SHIFT 0u + +/*------------------------------------------------------------------------------ + * ControReg2 register details + */ +#define CTRL2_REG_OFFSET 0xCu + +/* + * Bit length + */ +#define CTRL2_BIT_LENGTH_OFFSET 0xCu +#define CTRL2_BIT_LENGTH_MASK 0x01u +#define CTRL2_BIT_LENGTH_SHIFT 0u + +/* + * Parity enable. + */ +#define CTRL2_PARITY_EN_OFFSET 0xCu +#define CTRL2_PARITY_EN_MASK 0x02u +#define CTRL2_PARITY_EN_SHIFT 1u + +/* + * Odd/even parity selection. + */ +#define CTRL2_ODD_EVEN_OFFSET 0xCu +#define CTRL2_ODD_EVEN_MASK 0x04u +#define CTRL2_ODD_EVEN_SHIFT 2u + +/* + * Baud value (Higher 5-bits) + */ +#define CTRL2_BAUDVALUE_OFFSET 0xCu +#define CTRL2_BAUDVALUE_MASK 0xF8u +#define CTRL2_BAUDVALUE_SHIFT 3u + +/*------------------------------------------------------------------------------ + * StatusReg register details + */ +#define StatusReg_REG_OFFSET 0x10u + +#define STATUS_REG_OFFSET 0x10u + +/* + * Transmit ready. + */ +#define STATUS_TXRDY_OFFSET 0x10u +#define STATUS_TXRDY_MASK 0x01u +#define STATUS_TXRDY_SHIFT 0u + +/* + * Receive full. + */ +#define STATUS_RXFULL_OFFSET 0x10u +#define STATUS_RXFULL_MASK 0x02u +#define STATUS_RXFULL_SHIFT 1u + +/* + * Parity error. + */ +#define STATUS_PARITYERR_OFFSET 0x10u +#define STATUS_PARITYERR_MASK 0x04u +#define STATUS_PARITYERR_SHIFT 2u + +/* + * Overflow. + */ +#define STATUS_OVERFLOW_OFFSET 0x10u +#define STATUS_OVERFLOW_MASK 0x08u +#define STATUS_OVERFLOW_SHIFT 3u + +/* + * Frame Error. + */ +#define STATUS_FRAMERR_OFFSET 0x10u +#define STATUS_FRAMERR_MASK 0x10u +#define STATUS_FRAMERR_SHIFT 4u + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_UART_APB_REGISTERS */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_i2c/miv_i2c.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_i2c/miv_i2c.c new file mode 100644 index 0000000..a2f4911 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_i2c/miv_i2c.c @@ -0,0 +1,765 @@ +/******************************************************************************* + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Mi-V I2C Soft IP bare-metal driver. This module is delivered as part of + * Extended Sub System(ESS) MIV_ESS. + * Please refer to miv_i2c.h file for more information. + */ + +#include "miv_i2c.h" + +#define MIV_I2C_ERROR 0xFFu + +/*------------------------------------------------------------------------------ + * MIV I2C transaction direction. + */ +#define MIV_I2C_WRITE_DIR 0u +#define MIV_I2C_READ_DIR 1u + +/* -- TRANSACTIONS TYPES -- */ +#define MIV_I2C_NO_TRANSACTION 0u +#define MIV_I2C_MASTER_WRITE_TRANSACTION 1u +#define MIV_I2C_MASTER_READ_TRANSACTION 2u +#define MIV_I2C_MASTER_SEQUENTIAL_READ_TRANSACTION 3u + +/*------------------------------------------------------------------------------ + * MIV I2C HW states + */ +#define MIV_I2C_IDLE 0x00u +#define MIV_I2C_TX_STA_CB 0x01u +#define MIV_I2C_TX_DATA 0x02u +#define MIV_I2C_RX_DATA 0x03u + +/*-------------------------------------------------------------------------*//** + The MIV_I2C_disable_irq() disables the Mi-V I2C interrupt. + */ +void +MIV_I2C_disable_irq +( + void +); + +/*-------------------------------------------------------------------------*//** + The MIV_I2C_enable_irq() enables the Mi-V I2C interrupt. + */ +void +MIV_I2C_enable_irq +( + void +); + +/* + * Please refer to miv_i2c.h for more info + */ +void +MIV_I2C_init +( + miv_i2c_instance_t *this_i2c, + addr_t base_addr +) +{ + /* Assign the base address + * Clock Prescale value set + * MIV_I2C interrupt enabled + * I2C core enable + */ + psr_t processor_state; + + /* Disabling the interrupts */ + processor_state = HAL_disable_interrupts(); + + /* + * Initialize all items of the this_miv_i2c data structure to zero. This + * initializes all state variables to their init value. It relies on + * the fact that NO_TRANSACTION, MIV_I2C_SUCCESS and I2C_RELEASE_BUS all + * have an actual value of zero. + */ + memset(this_i2c, 0, sizeof(miv_i2c_instance_t)); + + this_i2c->base_addr = base_addr; + + HAL_restore_interrupts(processor_state); +} + +/* + * Please refer to miv_i2c.h for more info + */ +void +MIV_I2C_config +( + miv_i2c_instance_t *this_i2c, + uint16_t clk_prescale +) +{ + /* Assign the base address + * Clock Prescale value set + * MIV_I2C interrupt enabled + * I2C core enable + */ + psr_t processor_state; + + /* Disabling the interrupts */ + processor_state = HAL_disable_interrupts(); + + /* Before writing to prescale reg, the core enable must be zero */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CTRL_CORE_EN, 0x00u); + + /* Set the prescale value */ + HAL_set_16bit_reg(this_i2c->base_addr, PRESCALE, clk_prescale); + + /* Enable the MIV I2C interrupts */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CTRL_IRQ_EN, 0x01u); + + /* Enable the MIV I2C core */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CTRL_CORE_EN, 0x01u); + + this_i2c->master_state = MIV_I2C_IDLE; + + HAL_restore_interrupts(processor_state); +} + +/* + * Please refer to miv_i2c.h for more info + */ +uint8_t +MIV_I2C_start +( + miv_i2c_instance_t *this_i2c +) +{ + psr_t processor_state; + + processor_state = HAL_disable_interrupts(); + + /* Generate I2C start condition */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_STA, 0x01u); + + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_WR, 0x01); + + HAL_restore_interrupts(processor_state); + + return 0u; +} + +/* + * Please refer to miv_i2c.h for more info + */ +uint8_t +MIV_I2C_stop +( + miv_i2c_instance_t *this_i2c +) +{ + psr_t processor_state; + + processor_state = HAL_disable_interrupts(); + + /* Generate I2C stop condition */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_STO, 0x01u); + + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_WR, 0x01); + + HAL_restore_interrupts(processor_state); + + return 0u; +} + +/* + * Please refer to miv_i2c.h for more info + */ +void +MIV_I2C_write +( + miv_i2c_instance_t *this_i2c, + uint8_t i2c_target_addr, + const uint8_t *write_buffer, + uint16_t write_size, + uint8_t bus_options, + uint8_t ack_polling_options +) +{ + psr_t processor_state; + + processor_state = HAL_disable_interrupts(); + + /* I2C write flow + * + * Check I2C status for ongoing transaction + * Populate the structure with input data + * Generate start condition + * Set the write_direction and target address. + */ + + /* Update the transaction only when there is no ongoing I2C transaction */ + if (this_i2c->transaction == MIV_I2C_NO_TRANSACTION) + { + this_i2c->transaction = MIV_I2C_MASTER_WRITE_TRANSACTION; + } + + /* Update the Pending transaction information so that transaction can restarted */ + this_i2c->pending_transaction = MIV_I2C_MASTER_WRITE_TRANSACTION ; + + /* Populate the i2c instance structure */ + + /* Set the target addr */ + this_i2c->target_addr = i2c_target_addr; + this_i2c->dir = MIV_I2C_WRITE_DIR; + + /* Set up the tx buffer */ + this_i2c->master_tx_buffer = write_buffer; + this_i2c->master_tx_size = write_size; + this_i2c->master_tx_idx = 0u; + + /* Set the I2C status in progress and setup the options */ + this_i2c->bus_options = bus_options; + this_i2c->ack_polling_options = ack_polling_options; + this_i2c->master_status = MIV_I2C_IN_PROGRESS; + + + /* Generate I2C start condition */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_STA, 0x01u); + + /* write target address and write bit */ + HAL_set_8bit_reg_field(this_i2c->base_addr, TX_DIR, MIV_I2C_WRITE_DIR); + HAL_set_8bit_reg_field(this_i2c->base_addr, TX_TARGET_ADDR, i2c_target_addr); + + /* Set WR bit to transmit start condition and control byte */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_WR, 0x01); + + /* Set current master hw state -> transmitted start condition and + * control byte + */ + this_i2c->master_state = MIV_I2C_TX_STA_CB; + + /* + * Clear interrupts if required (depends on repeated starts). + * Since the Bus is on hold, only then prior status needs to + * be cleared. + */ + if ( MIV_I2C_HOLD_BUS == this_i2c->bus_status ) + { + /* Must toggle IACK bit to clear the MIV_I2C IRQ*/ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_IACK, 0x01); + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_IACK, 0x00); + } + + MIV_I2C_enable_irq(); + + HAL_restore_interrupts(processor_state); + +} + +/* + * Please refer to miv_i2c.h for more info + */ +void +MIV_I2C_read +( + miv_i2c_instance_t *this_i2c, + uint8_t i2c_target_addr, + uint8_t *read_buffer, + uint16_t read_size, + uint8_t bus_options, + uint8_t ack_polling_options +) +{ + psr_t processor_state; + uint8_t status = MIV_I2C_SUCCESS; + + processor_state = HAL_disable_interrupts(); + + /* MIV I2C Read operation flow + * + * Check for ongoing transaction + * Populate the i2c instance structure + * Generate the start condition + * Set the READ_direction bit and target addr + */ + + uint8_t read_stat = HAL_get_8bit_reg_field(this_i2c->base_addr, STAT_TIP); + + /* Update the transaction only when there is no ongoing I2C transaction */ + if (this_i2c->transaction == MIV_I2C_NO_TRANSACTION) + { + this_i2c->transaction = MIV_I2C_MASTER_READ_TRANSACTION; + } + + this_i2c->pending_transaction = MIV_I2C_MASTER_READ_TRANSACTION; + + /* Populate the MIV I2C instance structure */ + + this_i2c->target_addr = i2c_target_addr; + this_i2c->dir = MIV_I2C_READ_DIR; + + /* Populate read buffer */ + this_i2c->master_rx_buffer = read_buffer; + this_i2c->master_rx_size = read_size; + this_i2c->master_rx_idx = 0u; + + /* Set the BUS and ACK polling options */ + this_i2c->bus_options = bus_options; + this_i2c->ack_polling_options = ack_polling_options; + + /* Generate the start condition */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_STA, 0x01u); + + /* Set the DIR bit and target addr */ + HAL_set_8bit_reg_field(this_i2c->base_addr, TX_DIR, MIV_I2C_READ_DIR); + HAL_set_8bit_reg_field(this_i2c->base_addr, TX_TARGET_ADDR, i2c_target_addr); + + /* Set the WR bit to transmit the start condition and command byte */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_WR, 0x01u); + + /* Set the i2c master state and status transmitting STA and Command Byte */ + this_i2c->master_state = MIV_I2C_TX_STA_CB; + this_i2c->master_status = MIV_I2C_IN_PROGRESS; + + /* Toggle the IACK bit if required */ + /* + * Clear interrupts if required (depends on repeated starts). + * Since the Bus is on hold, only then prior status needs to + * be cleared. + */ + if ( MIV_I2C_HOLD_BUS == this_i2c->bus_status ) + { + /* Must toggle IACK bit to clear the MIV_I2C IRQ*/ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_IACK, 0x01); + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_IACK, 0x00); + } + /* Enable the I2C interrupt */ + MIV_I2C_enable_irq(); + + HAL_restore_interrupts(processor_state); +} + +/* + * Please refer to miv_i2c.h for more info + */ +void +MIV_I2C_write_read +( + miv_i2c_instance_t *this_i2c, + uint8_t target_addr, + const uint8_t *write_buffer, + uint16_t write_size, + uint8_t *read_buffer, + uint16_t read_size, + uint8_t bus_options, + uint8_t ack_polling_options +) +{ + uint8_t status = MIV_I2C_SUCCESS; + psr_t processor_state; + + processor_state = HAL_disable_interrupts(); + + uint8_t read_stat = HAL_get_8bit_reg_field(this_i2c->base_addr, STAT_TIP); + + /* I2C write read operation flow + * + * Used to read the data from set address offset + * + * Configure the i2c instance structure + * generate the start and configure the dir and target addr + * set wr bit to transmit the start and command byte + * + */ + + /* Update the transaction only when there is no ongoing I2C transaction */ + if (this_i2c->transaction == MIV_I2C_NO_TRANSACTION) + { + this_i2c->transaction = MIV_I2C_MASTER_READ_TRANSACTION; + } + + this_i2c->pending_transaction = MIV_I2C_MASTER_READ_TRANSACTION; + + /* Populate the I2C instance */ + + this_i2c->target_addr = target_addr; + + /* setup the i2c direction */ + this_i2c->dir = MIV_I2C_WRITE_DIR; + + /* set up transmit buffer */ + this_i2c->master_tx_buffer = write_buffer; + this_i2c->master_tx_size = write_size; + this_i2c->master_tx_idx = 0u; + + /* set up receive buffer */ + this_i2c->master_rx_buffer = read_buffer; + this_i2c->master_rx_size = read_size; + this_i2c->master_rx_idx = 0u; + + /* Set the bus and ack polling options */ + this_i2c->bus_options = bus_options; + this_i2c->ack_polling_options = ack_polling_options; + + /* Generate the start command */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_STA, 0x01u); + + /* Set the DIR and target addr */ + HAL_set_8bit_reg_field(this_i2c->base_addr, TX_TARGET_ADDR, target_addr); + HAL_set_8bit_reg_field(this_i2c->base_addr, TX_DIR, this_i2c->dir); + + /* Set the WR bit to transmit the start command and command byte */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_WR, 0x01); + + /* Set the i2c master state and status transmitting STA and Command Byte */ + this_i2c->master_state = MIV_I2C_TX_STA_CB; + this_i2c->master_status = MIV_I2C_IN_PROGRESS; + + /* + * Clear interrupt if required + */ + if ( MIV_I2C_HOLD_BUS == this_i2c->bus_status ) + { + /* Must toggle IACK bit to clear the MIV_I2C IRQ*/ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_IACK, 0x01u); + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_IACK, 0x00u); + } + + /* Enable the I2C interrupt */ + MIV_I2C_enable_irq(); + + HAL_restore_interrupts(processor_state); +} + +/* MIV_I2C_isr() + * Please refer to miv_i2c.h for more info + */ +void +MIV_I2C_isr +( + miv_i2c_instance_t *this_i2c +) +{ + uint8_t i2c_state; + uint8_t i2c_ack_status; + uint8_t i2c_al_status; + uint8_t hold_bus; + + /* Read the I2C master state */ + i2c_state = this_i2c->master_state; + + /* Read the ack and al status */ + i2c_ack_status = HAL_get_8bit_reg_field(this_i2c->base_addr, STAT_RXACK); + i2c_al_status = HAL_get_8bit_reg_field(this_i2c->base_addr, STAT_AL); + + switch (i2c_state) + { + /* I2C ISR State Machine + * + * Cases: + * - Transmit start condition and control byte + * - Received ACK and bus arbitration was not lost (Read or Write) + * - Received NACK + * - Bus arbitration lost + * + * - Transmit data + * - Received ACK and bus arbitration was not lost (Read or Write) + * - Received NACK + * - Bus arbitration lost + * + * - Receive data + * - Received ACK and bus arbitration was not lost (Read or Write) + * - Bus arbitration lost + */ + + case MIV_I2C_TX_STA_CB: + + /* Received ACK from target and I2C bus arbitration is not lost */ + if (i2c_ack_status == 0u && i2c_al_status == 0u) + { + /* If I2C master write operation */ + if (this_i2c->dir == MIV_I2C_WRITE_DIR) + { + /* write first byte of data and set the WR bit to transfer the data */ + HAL_set_8bit_reg(this_i2c->base_addr, TRANSMIT, + this_i2c->master_tx_buffer[this_i2c->master_tx_idx]); + + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_WR, 0x01u); + + /* Increment the index */ + this_i2c->master_tx_idx++; + + /* Set the master state to TX data */ + this_i2c->master_state = MIV_I2C_TX_DATA; + } + /* Master read operation */ + else + { + if (this_i2c->master_rx_size == 1u) + { + /* Send the ACK if the rx size is 1, transmit NACK to slave + * after receiving 1 byte to indicate slave to stop sending + * the data + */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_ACK, 0x01u); + } + + /* Send the RD command to slave */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_RD, 0x01u); + + /* Increment the index */ + this_i2c->master_rx_idx++; + + /* Change state to receive data */ + this_i2c->master_state = MIV_I2C_RX_DATA; + } + } + else if (i2c_ack_status == 1u) + { + if (this_i2c->ack_polling_options == MIV_I2C_ACK_POLLING_ENABLE) + { + /* Target responded with NACK and ACK polling option is enabled + * + * Re-send the start condition and control byte + * + * TO-DO: This might become infinite loop check for timeout + * options. + */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_STA, 0x01u); + HAL_set_8bit_reg_field(this_i2c->base_addr, TX_TARGET_ADDR, this_i2c->target_addr); + HAL_set_8bit_reg_field(this_i2c->base_addr, TX_DIR, this_i2c->dir); + + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_WR, 0x01u); + + this_i2c->master_tx_idx = 0u; + this_i2c->master_state = MIV_I2C_TX_STA_CB; + } + + else + { + /* Target responded with NACK and ACK polling is disabled + * Abort the transaction and move to IDLE state + */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_STO, 0x01u); + + this_i2c->master_status = MIV_I2C_FAILED; + this_i2c->transaction = MIV_I2C_NO_TRANSACTION; + + this_i2c->master_state = MIV_I2C_IDLE; + } + } + + else if (i2c_al_status == 1u) + { + /* Arbitration was lost on the BUS during the transmission of + * previous start condition and control byte. + * Re-send the STA and CB + */ + + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_STA, 0x01u); + HAL_set_8bit_reg_field(this_i2c->base_addr, TX_TARGET_ADDR, this_i2c->target_addr); + HAL_set_8bit_reg_field(this_i2c->base_addr, TX_DIR, this_i2c->dir); + + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_WR, 0x01u); + + this_i2c->master_state = MIV_I2C_TX_STA_CB; + } + + break; + + /* Transmit master data */ + case MIV_I2C_TX_DATA: + + /* ACK received and arbitration was not lost */ + if (i2c_ack_status == 0u && i2c_al_status == 0u) + { + uint8_t tx_buff[this_i2c->master_tx_size]; + if (this_i2c->master_tx_idx < this_i2c->master_tx_size) + { + HAL_set_8bit_reg(this_i2c->base_addr, TRANSMIT, + this_i2c->master_tx_buffer[this_i2c->master_tx_idx]); + + tx_buff[this_i2c->master_tx_idx] = this_i2c->master_tx_buffer[this_i2c->master_tx_idx]; + + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_WR, 0x01u); + + /* Increment the index */ + this_i2c->master_tx_idx++; + + /* Set the master state to TX data */ + this_i2c->master_state = MIV_I2C_TX_DATA; + } + + /* All the bytes are transmitted */ + else if (this_i2c->master_tx_idx == this_i2c->master_tx_size) + { + /* If this is a MASTER_READ_TRANSACTION, hold bus and start a + new transfer in read mode now that the read address has been + written to the slave */ + if(this_i2c->transaction == MIV_I2C_MASTER_READ_TRANSACTION) + + { + //Switch direction to READ + this_i2c->dir = MIV_I2C_READ_DIR; + + // Set the STA bit + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_STA, 0x01u); + + /* Set the DIR bit and target addr */ + HAL_set_8bit_reg_field(this_i2c->base_addr, TX_DIR, MIV_I2C_READ_DIR); + HAL_set_8bit_reg_field(this_i2c->base_addr, TX_TARGET_ADDR, this_i2c->target_addr); + + /* Set the WR bit to transmit the start condition and command byte */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_WR, 0x01u); + + // Reset the buffer index + this_i2c->master_tx_idx = 0u; + this_i2c->master_rx_idx = 0u; + + /* Set the master state to RX data */ + this_i2c->master_state = MIV_I2C_RX_DATA; + } + + else + { + /* If releasing the bus, transmit the stop condition at the end + * of the transfer. + */ + hold_bus = this_i2c->bus_status & MIV_I2C_HOLD_BUS; + + if (hold_bus == 0) + { + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_STO, 0x01u); + } + else + { + MIV_I2C_disable_irq(); + } + this_i2c->master_status = MIV_I2C_SUCCESS; + this_i2c->transaction = MIV_I2C_NO_TRANSACTION; + this_i2c->master_state = MIV_I2C_IDLE; + } + } + } + + else if (i2c_ack_status == 1u) + { + /* Received NACK from target device + * + * Release the bus and end the transfer + */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_STO, 0x01u); + + this_i2c->master_status = MIV_I2C_FAILED; + this_i2c->transaction = MIV_I2C_NO_TRANSACTION; + + this_i2c->master_state = MIV_I2C_IDLE; + } + + else if (i2c_al_status == 1u) + { + /* Arbitration was lost on the BUS during the transmission of + * previous start condition and control byte. + * Re-send the STA and CB + */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_STA, 0x01u); + + HAL_set_8bit_reg_field(this_i2c->base_addr, TX_TARGET_ADDR, + this_i2c->target_addr); + HAL_set_8bit_reg_field(this_i2c->base_addr, TX_DIR, this_i2c->dir); + + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_WR, 0x01); + + this_i2c->master_state = MIV_I2C_TX_STA_CB; + } + + break; + + /* Receive target device data */ + case MIV_I2C_RX_DATA: + + if (i2c_al_status == 0u) + { + if (this_i2c->master_rx_idx < this_i2c->master_rx_size) + { + this_i2c->master_rx_buffer[this_i2c->master_rx_idx - 1u] = + HAL_get_8bit_reg(this_i2c->base_addr, RECEIVE); + + /* If next byte is last one + * Send NACK to target device to stop sending data + */ + if (this_i2c->master_rx_idx == (this_i2c->master_rx_size - 1u)) + { + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_ACK, 0x01u); + } + + else + { + /* Send ACK to receive next bytes */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_ACK, 0x00u); + } + + /* Set RD bit to receive next byte */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_RD, 0x01u); + + this_i2c->master_rx_idx++; + } + + /* Received all bytes */ + else //if (this_i2c->master_rx_idx == this_i2c->master_rx_size) + { + this_i2c->master_rx_buffer[this_i2c->master_rx_idx - 1] = + HAL_get_8bit_reg(this_i2c->base_addr, RECEIVE); + + hold_bus = this_i2c->bus_status & MIV_I2C_HOLD_BUS; + + if (hold_bus == 0) + { + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_STO, 0x01u); + } + else + { + MIV_I2C_disable_irq(); + } + this_i2c->master_status = MIV_I2C_SUCCESS; + this_i2c->transaction = MIV_I2C_NO_TRANSACTION; + + this_i2c->master_state = MIV_I2C_IDLE; + } + } + + else if (i2c_al_status == 1u) + { + /* Arbitration was lost on the BUS during the transmission of + * previous start condition and control byte. + * Re-send the STA and CB + */ + HAL_set_8bit_reg_field(this_i2c->base_addr, TX_TARGET_ADDR, + this_i2c->target_addr); + HAL_set_8bit_reg_field(this_i2c->base_addr, TX_DIR, this_i2c->dir); + + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_STA, 0x01u); + + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_WR, 0x01); + + this_i2c->master_state = MIV_I2C_TX_STA_CB; + } + + break; + } + + /* Toggle the IACK bit to clear interrupt */ + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_IACK, 0x01u); + HAL_set_8bit_reg_field(this_i2c->base_addr, CMD_IACK, 0x00u); +} + +/* + * Please refer to miv_i2c.h for more info + */ +uint8_t +MIV_I2C_get_status +( + miv_i2c_instance_t *this_i2c +) +{ + uint8_t i2c_status; + + i2c_status = HAL_get_8bit_reg(this_i2c->base_addr, STATUS); + + return i2c_status; +} diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_i2c/miv_i2c.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_i2c/miv_i2c.h new file mode 100644 index 0000000..c5e704d --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_i2c/miv_i2c.h @@ -0,0 +1,854 @@ +/******************************************************************************* + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * This file contains the application programming interface for the MI-V Soft IP + * I2C module driver. This module is delivered as a part of Mi-V Extended + * Sub-System(MIV_ESS). + */ + +/*=========================================================================*//** + @mainpage Mi-V I2C Bare Metal Driver + + ============================================================================== + Introduction + ============================================================================== + The Mi-V I2C driver provides a set of functions for controlling the Mi-V I2C + Soft-IP module. This module is delivered as a part of the Mi-V Extended + Sub System(MIV_ESS). The driver provides a minimal APB-driven I2C interface, + supporting initiator read and write access to peripheral I2C devices. + + The major features provided by the Mi-V I2C driver are: + - Support for configuring the I2C instance. + - I2C master operations. + - I2C ISR. + + This driver can be used as part of a bare metal system where no operating + system is available. The driver can be adapted for use as part of an + operating system, but the implementation of the adaptation layer between the + driver and the operating system's driver model is outside the scope of this + driver. + + ============================================================================== + Hardware Flow Dependencies + ============================================================================== + The application software should initialize and configure the Mi-V I2C through + the call to the MIV_I2C_init() and MIV_I2C_config() function for Mi-V I2C + instance in the design. The configuration parameter include base address and + Prescaler value. + + ------------------------------ + Interrupt Control + ------------------------------ + The Mi-V I2C driver has to enable and disable the generation of interrupts by + Mi-V I2C at various times while operating. This enabling and disabling of the + interrupts must be done through the Mi-V RV32 HAL provided interrupt handlers. + For that reason, the method controlling the Mi-V I2C interrupts is system + specific and it is necessary to customize the MIV_I2C_enable_irq() and + MIV_I2C_disable_irq() functions as per requirement. + + The implementation of MIV_I2C_enable_irq() should permit the interrupts + generated by the Mi-V I2C to the processor through a call to respective miv-hal + interrupt handler. The implementation of MIV_I2C_disable_irq() should prevent + the interrupts generated by a Mi-V I2C from interrupting the processor. + Please refer to the miv_i2c_interrupt.c for more information about the + implementation. + + No MIV_I2C hardware configuration parameters are used by the driver, apart + from the MIV_I2C base address. Hence, no additional configuration files + are required to use the driver. + + ============================================================================== + Theory of Operation + ============================================================================== + The Mi-V I2C software driver is designed to allow the control of multiple + instances of Mi-V I2C. Each instance of Mi-V I2C in the hardware design is + associated with a single instance of the miv_i2c_instance_t structure in the + software. User must allocate memory for one unique miv_i2c_instance_t + structure for each instance of Mi-V I2C in the hardware. + A pointer to the structure is passed to the subsequent driver functions in + order to identify the MIV_I2C hardware instance and to perform requested + operation. + + Note: Do not attempt to directly manipulate the contents of the + miv_i2c_instance_t structure. These structures are only intended to be modified + by the driver functions. + + The Mi-V I2C driver functions are grouped into following categories: + - Initialization and configuration + - I2C master operation functions to handle write, read and write_read + operations. + - Interrupt control + + -------------------------------- + Initialization and configuration + -------------------------------- + The Mi-V I2C device is first initialized by the call to MIV_I2C_init(). This + function initializes the instance of Mi-V I2C with the base address. + MIV_I2C_init() function must be called before any other Mi-V I2C driver API. + + The configuration of the Mi-V I2C instance is done via call to the + MIV_I2C_config() function. This function will set the prescale value which is + used to set the frequency of the I2C clock(SCLK) generated by I2C module. + + --------------------------------- + Transaction types + --------------------------------- + The driver is designed to handle three types of transactions: + - Write transactions + - Read transactions + - Write-Read transaction + + ### Write Transaction + The write transaction begins with master sending a start condition, followed + by device address byte with the R/W bit set to logic '0', and then by the + word address bytes. The slave acknowledges the receipt of its address with + acknowledge bit. The master sends one byte at a time to the slave, which must + acknowledge the receipt of each byte for the next byte to be sent. The master + sends STOP condition to complete the transaction. The slave can abort the + transaction by replying with negative acknowledge. + + The application programmer can choose not to send the STOP bit at the end of + the transaction causing repetitive start conditions. + + ### Read Transaction + The master I2C device initiates a read transaction by sending a START bit + as soon as the bus becomes free. The start condition is followed by the + control byte which contains 7-bit slave address followed by R/W bit set to + logic '1'. The slave sends data one byte at a time to the master, which must + acknowledge receipt of each byte for the next byte to be sent. The master + sends a non-acknowledge bit following the last byte it wishes to read + followed by a STOP bit. + + The application programmer can choose not to send a STOP bit at the end of + the transaction causing the next transaction to begin with a repeated + START bit. + + ### Write-Read Transaction + The write read transaction is a combination of a write transaction + immediately followed by a read transaction. There is no STOP condition sent + between the write and read phase of write-read transaction. A repeated START + condition is sent between the write and read phases. + + Whilst the write handler is being executed, the slave holds the clock line + low to stretch the clock until the response is ready. + + The write-read transaction is typically used to send an memory/register + address in the write transaction specifying the start address of the data to + be transferred during the read phase. + + The application programmer can choose not to send a STOP bit at the end of + the transaction causing the next transaction to begin with a repeated + START bit. + + ------------------------------------- + Interrupt Control + ------------------------------------- + The Mi-V I2C driver is interrupt driven and it uses the MIV_I2C_irq() function + to drive the ISR state machine which is at the heart of the driver. The + application is responsible for providing the link between the interrupt + generating hardware and the Mi-V I2C interrupt handler and must ensure that + the MIV_I2C_isr() function is called with the correct miv_i2c_instance_t + structure pointer for the Mi-V I2C instance initiating the interrupt. + +*//*=========================================================================*/ +#ifndef MIV_I2C_H_ +#define MIV_I2C_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "miv_i2c_regs.h" +#include + +#ifndef LEGACY_DIR_STRUCTURE +#include "hal/hal.h" +#else +#include "hal.h" +#endif + +/*-------------------------------------------------------------------------*//** + The miv_i2c_status_t type is used to report the status of I2C transactions. + */ +typedef enum miv_i2c_status +{ + MIV_I2C_SUCCESS = 0u, + MIV_I2C_IN_PROGRESS, + MIV_I2C_FAILED, + MIV_I2C_TIMED_OUT +}miv_i2c_status_t; + +/*-------------------------------------------------------------------------*//** + This structure is used to identify the MIV_I2C hardware instances in a system. + Your application software should declare one instance of this structure for + each instance of the MIV_I2C in your system. The function MIV_I2C_init() + Initializes this structure. A pointer to an initialised instance of the structure + should be passed as the first parameter to the MIV_I2C driver functions, to + identify which MIV_I2C hardware instance should perform the requested operation. + + The contents of this data structure should not be modified or used outside of + the MIV_I2C driver. Software using the MIV_I2C driver should only need to + create one single instance of this data structure for each MIV_I2C hardware + instance in the system, then pass a pointer to these data structures with each + call to the MIV_I2C driver in order to identify the MIV_I2C hardware instance + it wishes to use. +*/ + +typedef struct miv_i2c_instance +{ + addr_t base_addr; + + /* Transmit related info:*/ + uint_fast8_t target_addr; + + /* Current transaction type */ + uint8_t transaction; + + uint8_t bus_options; + + uint8_t ack_polling_options; + + /* Current State of the I2C master */ + uint8_t master_state; + + /* Master TX INFO: */ + const uint8_t * master_tx_buffer; + uint_fast16_t master_tx_size; + uint_fast16_t master_tx_idx; + uint_fast8_t dir; + + /* Master RX INFO: */ + uint8_t * master_rx_buffer; + uint_fast16_t master_rx_size; + uint_fast16_t master_rx_idx; + + /* Master Status */ + volatile miv_i2c_status_t master_status; + uint32_t master_timeout_ms; + + /* user specific data */ + void *p_user_data ; + + /* I2C bus status */ + uint8_t bus_status; + + /* Is transaction pending flag */ + uint8_t is_transaction_pending; + + /* I2C Pending transaction */ + uint8_t pending_transaction; + +}miv_i2c_instance_t; + + +/*-------------------------------------------------------------------------*//** + MIV_I2C_RELEASE_BUS + ===================== + The MIV_I2C_RELEASE_BUS constant is used to specify the bus_options parameter + for MIV_I2C_read(), MIV_I2C_write() and MIV_I2C_write_read() to indicate + that a STOP bit must be generated at the end of the I2C transaction to release + the bus. + */ +#define MIV_I2C_RELEASE_BUS 0x00u + + +/*-------------------------------------------------------------------------*//** + MIV_I2C_HOLD_BUS + ===================== + The MIV_I2C_HOLD_BUS constant is used to specify the bus_optionsparameter + for MIV_I2C_read(), MIV_I2C_write() and MIV_I2C_write_read() to indicate + that a STOP bit must not be generated at the end of the I2C transaction in + order to retain the bus ownership. This causes the next transaction to + begin with a repeated START bit and no STOP bit between the transactions. + */ +#define MIV_I2C_HOLD_BUS 0x01u + +/*-------------------------------------------------------------------------*//** + MIV_I2C_ACK_POLLING_DISABLE + ===================== + The MIV_I2C_ACK_POLLING_DISABLE constant is used to specify the + ack_polling_options parameter to functions MIV_I2C_write(), + MIV_I2C_write_read() and MIV_I2C_read(). Acknowledgment polling is used when + working with I2C memory devices such as EEPROM, which feature an internal + write cycle. + + With acknowledgment polling disabled, if the target slave device responds to the + control byte with a NACK, the MIV_I2C will abort the transfer. + */ +#define MIV_I2C_ACK_POLLING_DISABLE 0x00u + +/*-------------------------------------------------------------------------*//** + MIV_I2C_ACK_POLLING_ENABLE + ===================== + The MIV_I2C_ACK_POLLING_ENABLE constant is used to specify the + ack_polling_options parameter to functions MIV_I2C_write(), + MIV_I2C_write_read() and MIV_I2C_read(). acknowledgment polling is used when + working with I2C memory devices such as EEPROM, which feature an internal + write cycle. + + With acknowledgment polling enabled, if the slave device responds to the + control byte with a NACK, the MIV_I2C will repeatedly transmit another control + byte until the slave device accepts the connection with an ACK, or the timeout + specified in the MIV_I2C_wait_complete() function is reached. Acknowledgment + polling allows for the next read/write operation to be started as soon as the + EEPROM has completed its internal write cycle. + */ +#define MIV_I2C_ACK_POLLING_ENABLE 0x01u + +/*--------------------------------Public APIs---------------------------------*/ + +/*-------------------------------------------------------------------------*//** + The MIV_I2C_init() function is used to initialize the Mi-V I2C module instance + with the base address. + + Note: This function should be called before calling any other Mi-V I2C + functions. + + @param this_i2c + A pointer to the miv_i2c_instance_t data structure which + will hold all the data related to the Mi-V I2C module + instance being used. A pointer to this structure is passed to + rest of the Mi-V I2C driver functions for operation. + @param base_addr + Base address of the Mi-V I2C module instance in the MIV_ESS + soft IP. + + @return + This function does not return any value. + + Example: + @code + #define MIV_I2C_BASE_ADDR 0x7A000000u + + miv_i2c_instance_t g_miv_i2c_inst; + + void main( void ) + { + MIV_I2C_init( &g_miv_i2c_inst, MIV_I2C_BASE_ADDR); + } + @endcode + */ +void +MIV_I2C_init +( + miv_i2c_instance_t *this_i2c, + addr_t base_addr +); + +/*-------------------------------------------------------------------------*//** + The MIV_I2C_config() function is used to configure the Mi-V I2C module. This + function will set the prescale value which is used to set the frequency of + the I2C clock(SCLK) generated by I2C module and also enables the I2C core and + interrupts. + + @param this_i2c + A pointer to the miv_i2c_instance_t data structure which + will hold all the data related to the Mi-V I2C module + instance being used. A pointer to this structure is passed to + rest of the Mi-V I2C driver functions for operation. + + @param clk_prescale + The value used to set the frequency of Mi-V I2C serial clock + (SCLK) generated by the Mi-V I2C module instance. The + prescaler value required to set particular frequency of + Mi-V I2C can be calculated using following formula: + + prescaler = (System Clock Frequency) / (5 * (Desired I2C Clock Frequency)) - 1 + + @return + This function does not return any value. + + + Example: + @code + #define MIV_I2C_BASE_ADDR 0x7A000000u + + miv_i2c_instance_t g_miv_i2c_inst; + + void main( void ) + { + MIV_I2C_init( &g_miv_i2c_inst, MIV_I2C_BASE_ADDR); + + Configuring Mi-V I2C core at Normal Speed (100MHz) for 50MHz Sys clock. + MIV_I2C_config(&g_miv_i2c_inst, 0x63); + } + @endcode + */ +void +MIV_I2C_config +( + miv_i2c_instance_t *this_i2c, + uint16_t clk_prescale +); + + +uint8_t +MIV_I2C_start +( + miv_i2c_instance_t *this_i2c +); + + +uint8_t +MIV_I2C_stop +( + miv_i2c_instance_t *this_i2c +); + +/*-------------------------------------------------------------------------*//** + The MIV_I2C_write() is used to set up and start the Mi-V I2C master write + transaction. This function is used for all Mi-V master write operation. + + For more information about the operation, please refer to the 'theory of + operations' section at the start of this document. + + This function returns immediately after initiating the transaction. The content + of the write buffer passed as parameter should not be modified until the write + transaction completes. It also means that the memory allocated for the write + buffer should not be freed or should not go out of scope before the write + completes. + You can check for the write transaction completion by polling the master_status + from miv_i2c_instance_t structure as shown in the sample code. + + @param this_i2c + A pointer to the miv_i2c_instance_t data structure which + will hold all the data related to the Mi-V I2C module + instance being used. A pointer to this structure is passed to + rest of the Mi-V I2C driver functions for operation. + + @param i2c_target_addr + This parameter specifies the serial address for the slave + device. + + @param write_buffer + This parameter is a pointer to the buffer holding data to be + written to target I2C device. + Care must be taken not to release the memory used by this + buffer before the write transaction completes. + + @param write_size + Number of bytes held in the write_buffer to be written to the + I2C device. + @param bus_options: + The bus_options parameter is used to indicate if the I2C bus + should be released on completion of the write transaction. + Using the MIV_I2C_RELEASE_BUS constant for the bus_options + parameter causes a STOP bit to be generated at the end of the + write transaction causing the bus to be released for other I2C + devices to use. Using the MIV_I2C_HOLD_BUS constant as + bus_options parameter prevents a STOP bit from being generated + at the end of the write transaction, preventing other I2C + devices from initiating a bus transaction. + + @param ack_polling_options: + The ack_polling_options parameter is used to indicate how the + MIV_I2C will respond if the slave device transmits a NACK to + the I2C control byte. Using the MIV_I2C_ACK_POLLING_DISABLE + constant for the ack_polling_options parameter causes the + MIV_I2C to abort the transfer if the slave device responds to + the I2C control byte with a NACK. Using the + MIV_I2C_ACK_POLLING_ENABLE constant for the ack_polling_options + parameter causes the MIV_I2C to repeatedly transmit a control + byte to the slave device until the slave device responds with + an ACK. + @return + This function does not return any value. + + + Example: + @code + #define MIV_I2C_BASE_ADDR 0x7A000000u + + miv_i2c_instance_t g_miv_i2c_inst; + + void main( void ) + { + MIV_I2C_init( &g_miv_i2c_inst, MIV_I2C_BASE_ADDR); + + Configuring Mi-V I2C core at Normal Speed (100MHz) for 50MHz Sys clock. + MIV_I2C_config(&g_miv_i2c_inst, 0x63); + + MIV_I2C_write (&g_miv_i2c_inst, + DUALEE_SLAVEADDRESS_1, + i2c_tx_buffer, + transfer_size, + MIV_I2C_RELEASE_BUS, + MIV_I2C_ACK_POLLING_ENABLE + ); + + // Wait till the miv i2c status changes + do { + miv_i2c_status = miv_i2c.master_status; + }while (MIV_I2C_IN_PROGRESS == miv_i2c_status); + } + @endcode + */ +void +MIV_I2C_write +( + miv_i2c_instance_t *this_i2c, + uint8_t i2c_target_addr, + const uint8_t *write_buffer, + uint16_t write_size, + uint8_t bus_options, + uint8_t ack_polling_options +); + +/*-------------------------------------------------------------------------*//** + The MIV_I2C_read() is used to set up and start the Mi-V I2C master read + transaction. This function is used for all MIV_I2C master read operation. + + For more information about the operation, please refer to the 'theory of + operations' section at the start of this document. + + This function returns immediately after initiating the transaction. The content + of the read buffer passed as parameter should not be modified until the write + transaction completes. It also means that the memory allocated for the read + buffer should not be freed or should not go out of scope before the read + completes. + You can check for the write transaction completion by polling the master_status + from miv_i2c_instance_t structure as shown in the sample code. + + @param this_i2c + A pointer to the miv_i2c_instance_t data structure which + will hold all the data related to the Mi-V I2C module + instance being used. A pointer to this structure is passed to + rest of the Mi-V I2C driver functions for operation. + + @param i2c_target_addr + This parameter specifies the serial address for the slave + device. + + @param read_buffer + This parameter is a pointer to the buffer where the data + received from the I2C slave device is stored. + Care must be taken not to release the memory used by this + buffer before the write transaction completes. + + @param read_size + Number of bytes held in the write_buffer to be read from the + I2C device. + + @param bus_options: + The bus_options parameter is used to indicate if the I2C bus + should be released on completion of the write transaction. + Using the MIV_I2C_RELEASE_BUS constant for the bus_options + parameter causes a STOP bit to be generated at the end of the + write transaction causing the bus to be released for other I2C + devices to use. Using the MIV_I2C_HOLD_BUS constant as + bus_options parameter prevents a STOP bit from being generated + at the end of the write transaction, preventing other I2C + devices from initiating a bus transaction. + + @param ack_polling_options: + The ack_polling_options parameter is used to indicate how the + MIV_I2C will respond if the slave device transmits a NACK to + the I2C control byte. Using the MIV_I2C_ACK_POLLING_DISABLE + constant for the ack_polling_options parameter causes the + MIV_I2C to abort the transfer if the slave device responds to + the I2C control byte with a NACK. Using the + MIV_I2C_ACK_POLLING_ENABLE constant for the ack_polling_options + parameter causes the MIV_I2C to repeatedly transmit a control + byte to the slave device until the slave device responds with + an ACK. + @return + This function does not return any value. + + + Example: + @code + #define MIV_I2C_BASE_ADDR 0x7A000000u + + miv_i2c_instance_t g_miv_i2c_inst; + + void main( void ) + { + MIV_I2C_init( &g_miv_i2c_inst, MIV_I2C_BASE_ADDR); + + Configuring Mi-V I2C core at Normal Speed (100MHz) for 50MHz Sys clock. + MIV_I2C_config(&g_miv_i2c_inst, 0x63); + + MIV_I2C_write (&g_miv_i2c_inst, + DUALEE_SLAVEADDRESS_1, + i2c_tx_buffer, + transfer_size, + MIV_I2C_RELEASE_BUS, + MIV_I2C_ACK_POLLING_ENABLE + ); + + // Wait till the miv i2c status changes + do { + miv_i2c_status = miv_i2c.master_status; + }while (MIV_I2C_IN_PROGRESS == miv_i2c_status); + + // reset miv_i2c_status variable + miv_i2c_status = 0u; + + MIV_I2C_read (&g_miv_i2c_inst, + DUALEE_SLAVEADDRESS_1, + i2c_rx_buffer, + transfer_size, + MIV_I2C_RELEASE_BUS, + MIV_I2C_ACK_POLLING_ENABLE + ); + + // Wait till the miv i2c status changes + do { + miv_i2c_status = miv_i2c.master_status; + }while (MIV_I2C_IN_PROGRESS == miv_i2c_status); + } + @endcode + */ +void +MIV_I2C_read +( + miv_i2c_instance_t *this_i2c, + uint8_t i2c_target_addr, + uint8_t *read_buffer, + uint16_t read_size, + uint8_t bus_options, + uint8_t ack_polling_options +); + +/*-------------------------------------------------------------------------*//** + The MIV_I2C_write_read() is used to set up and start the Mi-V I2C master + write_read transaction. This function is used for all MIV_I2C master write_read + operation. + + This function is used in cases where data is being requested from a specific + address offset inside the target I2C slave device. + In this type of I2C operation, the I2C master starts by initiating a write + operation. During this write operation, the specific address offset is written + to the I2C slave. Once the address offset has been written to the I2C slave, + the I2C master transmits a repeated start, and initiates a read operation to + read data from the set address. + + For more information about the operation, please refer to the 'theory of + operations' section at the start of this document. + + This function returns immediately after initiating the transaction. The content + of the write and read buffer passed as parameter should not be modified until + the write transaction completes. It also means that the memory allocated for + the write and read buffer should not be freed or should not go out of scope + before the operation completes. + You can check for the write_read transaction completion by polling the + master_status from miv_i2c_instance_t structure. + + @param this_i2c + A pointer to the miv_i2c_instance_t data structure which + will hold all the data related to the Mi-V I2C module + instance being used. A pointer to this structure is passed to + rest of the Mi-V I2C driver functions for operation. + + @param i2c_target_addr + This parameter specifies the serial address for the slave + device. + + @param write_buffer + This parameter is a pointer to the buffer holding data to be + written to target I2C device. + Care must be taken not to release the memory used by this + buffer before the write transaction completes. + + @param write_size + Number of bytes held in the write_buffer to be written to the + I2C device. + + @param read_buffer + This parameter is a pointer to the buffer where the data + received from the I2C slave device is stored. + Care must be taken not to release the memory used by this + buffer before the write transaction completes. + + @param read_size + Number of bytes held in the write_buffer to be read from the + I2C device. + + @param bus_options: + The bus_options parameter is used to indicate if the I2C bus + should be released on completion of the write transaction. + Using the MIV_I2C_RELEASE_BUS constant for the bus_options + parameter causes a STOP bit to be generated at the end of the + write transaction causing the bus to be released for other I2C + devices to use. Using the MIV_I2C_HOLD_BUS constant as + bus_options parameter prevents a STOP bit from being generated + at the end of the write transaction, preventing other I2C + devices from initiating a bus transaction. + + @param ack_polling_options: + The ack_polling_options parameter is used to indicate how the + MIV_I2C will respond if the slave device transmits a NACK to + the I2C control byte. Using the MIV_I2C_ACK_POLLING_DISABLE + constant for the ack_polling_options parameter causes the + MIV_I2C to abort the transfer if the slave device responds to + the I2C control byte with a NACK. Using the + MIV_I2C_ACK_POLLING_ENABLE constant for the ack_polling_options + parameter causes the MIV_I2C to repeatedly transmit a control + byte to the slave device until the slave device responds with + an ACK or the timeout specified in the MIV_I2C_wait_complete() + function is reached. + @return + This function does not return any value. + + + Example: + @code + #define MIV_I2C_BASE_ADDR 0x7A000000u + + miv_i2c_instance_t g_miv_i2c_inst; + + void main( void ) + { + MIV_I2C_init( &g_miv_i2c_inst, MIV_I2C_BASE_ADDR); + + Configuring Mi-V I2C core at Normal Speed (100MHz) for 50MHz Sys clock. + MIV_I2C_config(&g_miv_i2c_inst, 0x63); + + MIV_I2C_write (&g_miv_i2c_inst, + DUALEE_SLAVEADDRESS_1, + i2c_tx_buffer, + transfer_size, + MIV_I2C_RELEASE_BUS, + MIV_I2C_ACK_POLLING_ENABLE + ); + + // Wait till the miv i2c status changes + do { + miv_i2c_status = miv_i2c.master_status; + }while (MIV_I2C_IN_PROGRESS == miv_i2c_status); + + // reset miv_i2c_status variable + miv_i2c_status = 0u; + + uint8_t addr_offset[2] = {0x00, 0x00}; + MIV_I2C_write_read(&miv_i2c, + DUALEE_SLAVEADDRESS_1, + addr_offset, + sizeof(addr_offset), + i2c_rx_buffer, + transfer_size, + MIV_I2C_RELEASE_BUS, + MIV_I2C_ACK_POLLING_ENABLE + ); + + // Wait till the miv i2c status changes + do { + miv_i2c_status = miv_i2c.master_status; + }while (MIV_I2C_IN_PROGRESS == miv_i2c_status); + } + @endcode + */ +void +MIV_I2C_write_read +( + miv_i2c_instance_t *this_i2c, + uint8_t target_addr, + const uint8_t *write_buffer, + uint16_t write_size, + uint8_t *read_buffer, + uint16_t read_size, + uint8_t bus_options, + uint8_t ack_polling_options +); + +/*-------------------------------------------------------------------------*//** + The MIV_I2C_isr() function contains the MIV_I2C's interrupt service routine. + This ISR is at the heart of the MIV_I2C driver, and is used to control the + interrupt-driven, byte-by-byte I2C read and write operations. + + The ISR operates as a Finite State Machine (FSM), which uses the previously + completed I2C operation and its result to determine which I2C operation will + be performed next. + + The ISR operation is divided into following categories: + - MIV_I2C_IDLE + - MIV_I2C_TX_STA_CB + - MIV_I2C_TX_DATA + - MIV_I2C_RX_DATA + + ##### MIV_I2C_IDLE + The MIV_I2C_IDLE is entered on reset, or when an I2C master operation has been + completed or aborted. + Upon entering, the FSM will remain in this state until a write, read, or + write-read operation is requested + + ##### MIV_I2C_STA_CB + The MIV_I2C_TX_STA_CB operation is performed when the start condition and + control byte(i2c target address(7-bit) and direction of transaction(1-bit)) is + transmitted by the Mi-V I2C master device to the slave. + If the target I2C slave device responded to the previous START Condition + + Control Byte with an ACK, the MIV_I2C will start the requested I2C + read/write operation. + If the target slave I2C slave device responds with NACK, the MIV_I2C will + remain in this state or return to the idle state based on ack_polling + configuration. + + ##### MIV_I2C_TX_DATA + The MIV_I2C_TX_DATA state is entered after the target slave device accepts a + write request with an ACK. + This state is used to handle the byte-by-byte MIV_I2C write operations. + The FSM will remain in this state until either all data bytes have been + written to the target slave device, or an error occurs during the write + operation. + + ##### MIV_I2C_RX_DATA + The MIV_I2C_RX_DATA state is entered after the target slave device accepts a + read request with an ACK. + This state is used to handle the byte-by-byte MIV_I2C read operations. + The FSM will remain in this state until either all data bytes have been + received from the target slave device, or an error occurs. + + @param this_i2c + A pointer to the miv_i2c_instance_t data structure which + will hold all the data related to the Mi-V I2C module + instance being used. A pointer to this structure is passed to + rest of the Mi-V I2C driver functions for operation. + */ +void +MIV_I2C_isr +( + miv_i2c_instance_t *this_i2c +); + +/*-------------------------------------------------------------------------*//** + The MIV_I2C_get_status() returns the 8-bit Mi-V I2C status register value. + + @param this_i2c + A pointer to the miv_i2c_instance_t data structure which + will hold all the data related to the Mi-V I2C module + instance being used. A pointer to this structure is passed to + rest of the Mi-V I2C driver functions for operation. + @return + This function returns 8-bit Mi-V I2C status register value. + */ +uint8_t +MIV_I2C_get_status +( + miv_i2c_instance_t *this_i2c +); + +#ifdef __cplusplus +} +#endif + +#endif /* MIV_I2C_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_i2c/miv_i2c_interrupt.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_i2c/miv_i2c_interrupt.c new file mode 100644 index 0000000..871eafe --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_i2c/miv_i2c_interrupt.c @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * This file contains functions used for MIV_I2C driver interrupt control. + * User should enable and disable the interrupts according to their design. + * Please refer to miv_i2c.h file for more information. + */ + +#include "miv_rv32_hal/miv_rv32_hal.h" + +void MIV_I2C_disable_irq(void) +{ +/* Disable I2C interrupt */ + MRV_disable_local_irq(MRV32_MSYS_EIE2_IRQn); +} + +void MIV_I2C_enable_irq(void) +{ +/* Enable I2C interrupt */ + MRV_enable_local_irq(MRV32_MSYS_EIE2_IRQn); +} + + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_i2c/miv_i2c_regs.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_i2c/miv_i2c_regs.h new file mode 100644 index 0000000..9a4bfbf --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_i2c/miv_i2c_regs.h @@ -0,0 +1,158 @@ + /******************************************************************************* + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * This file contains Register bit offsets and masks definitions for MI-V Soft + * IP I2C module driver. This module is delivered as a part of Mi-V extended + * Sub-System(MIV_ESS). + */ + +#ifndef MIV_I2C_APB_REGISTERS +#define MIV_I2C_APB_REGISTERS 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/*------------------------------------------------------------------------------ + * Prescale register details + */ +#define PRESCALE_REG_OFFSET 0x00u + +/* Prescale register bits */ +#define PRESCALE_OFFSET 0x00u +#define PRESCALE_MASK 0xFFFFu +#define PRESCALE_SHIFT 0u + +/*------------------------------------------------------------------------------ + * Control register details + */ +#define CONTROL_REG_OFFSET 0x04u + +/* Control register bits */ +#define CONTROL_OFFSET 0x04u +#define CONTROL_MASK 0xC0u +#define CONTROL_SHIFT 0u + +/* Control register Core Enable Bit */ +#define CTRL_CORE_EN_OFFSET 0x04u +#define CTRL_CORE_EN_MASK 0x80u +#define CTRL_CORE_EN_SHIFT 7u + +/* Control register IRQ Enable bit */ +#define CTRL_IRQ_EN_OFFSET 0x04u +#define CTRL_IRQ_EN_MASK 0x40u +#define CTRL_IRQ_EN_SHIFT 6u + +/*------------------------------------------------------------------------------ + * Transmit register details + */ +#define TRANSMIT_REG_OFFSET 0x08u + +/* Transmit register bits */ +#define TRANSMIT_OFFSET 0x08u +#define TRANSMIT_MASK 0xFFu +#define TRANSMIT_SHIFT 0u + +/* Transmit register DIR bit */ +#define TX_DIR_OFFSET 0x08u +#define TX_DIR_MASK 0x01u +#define TX_DIR_SHIFT 0u + +/* Transmit register TARGET_ADDR bit */ +#define TX_TARGET_ADDR_OFFSET 0x08u +#define TX_TARGET_ADDR_MASK 0xFEu +#define TX_TARGET_ADDR_SHIFT 1u + +/*------------------------------------------------------------------------------ + * Receive register details + */ +#define RECEIVE_REG_OFFSET 0x0Cu + +/* Receive register bits */ +#define RECEIVE_OFFSET 0x0Cu +#define RECEIVE_MASK 0xFFu +#define RECEIVE_SHIFT 0u + +/*------------------------------------------------------------------------------ + * Command register details + */ +#define COMMAND_REG_OFFSET 0x10u + +/* Command register bits */ +#define COMMAND_OFFSET 0x10u +#define COMMAND_MASK 0xF9u +#define COMMAND_SHIFT 0u + +/* Command register IACK bit */ +#define CMD_IACK_OFFSET 0x10u +#define CMD_IACK_MASK 0x01u +#define CMD_IACK_SHIFT 0u + +/* Command register ACK bit */ +#define CMD_ACK_OFFSET 0x10u +#define CMD_ACK_MASK 0x08u +#define CMD_ACK_SHIFT 3u + +/* Command register WR bit */ +#define CMD_WR_OFFSET 0x10u +#define CMD_WR_MASK 0x10u +#define CMD_WR_SHIFT 4u + +/* Command register RD bit */ +#define CMD_RD_OFFSET 0x10u +#define CMD_RD_MASK 0x20u +#define CMD_RD_SHIFT 5u + +/* Command register STO bit */ +#define CMD_STO_OFFSET 0x10u +#define CMD_STO_MASK 0x40u +#define CMD_STO_SHIFT 6u + +/* Command register STA bit */ +#define CMD_STA_OFFSET 0x10u +#define CMD_STA_MASK 0x80u +#define CMD_STA_SHIFT 7u + +/*------------------------------------------------------------------------------ + * Status register details + */ +#define STATUS_REG_OFFSET 0x14u + +/* Command register bits */ +#define STATUS_OFFSET 0x14u +#define STATUS_MASK 0xFFu +#define STATUS_SHIFT 0u + +/* Status register Interrupt Flag(IF) bit */ +#define STAT_IF_OFFSET 0x14u +#define STAT_IF_MASK 0x01u +#define STAT_IF_SHIFT 0u + +/* Status register Transfer in Progress(TIP) bit */ +#define STAT_TIP_OFFSET 0x14u +#define STAT_TIP_MASK 0x02u +#define STAT_TIP_SHIFT 1u + +/* Status register Arbitration Lost(AL) bit */ +#define STAT_AL_OFFSET 0x14u +#define STAT_AL_MASK 0x20u +#define STAT_AL_SHIFT 5u + +/* Status register Busy(BUSY) bit */ +#define STAT_BUSY_OFFSET 0x14u +#define STAT_BUSY_MASK 0x40u +#define STAT_BUSY_SHIFT 6u + +/* Status register Ack received(RXACK) bit */ +#define STAT_RXACK_OFFSET 0x14u +#define STAT_RXACK_MASK 0x80u +#define STAT_RXACK_SHIFT 7u + + +#ifdef __cplusplus +} +#endif + +#endif /* MIV_I2C_APB_REGISTERS */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_plic/miv_plic.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_plic/miv_plic.c new file mode 100644 index 0000000..903f029 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_plic/miv_plic.c @@ -0,0 +1,283 @@ +/******************************************************************************* + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * MI-V Soft IP fabric bare-metal driver for Mi-V PLIC module. This module is + * delivered as a part of Mi-V Extended Sub System(MIV_ESS). + * Please refer to miv_plic.h file for more information. + */ + +#include "miv_plic.h" + +/***************************************************************************//** + * Mi-V PLIC interrupt handler function declaration. + * These functions are called by the external interrupt handler of the MIV_RV32 + * core base on the PLIC source causing the interrupt. + */ +uint8_t Invalid_IRQHandler(void); +uint8_t MIV_PLIC_EXT0_IRQHandler(void); +uint8_t MIV_PLIC_EXT1_IRQHandler(void); +uint8_t MIV_PLIC_EXT2_IRQHandler(void); +uint8_t MIV_PLIC_EXT3_IRQHandler(void); +uint8_t MIV_PLIC_EXT4_IRQHandler(void); +uint8_t MIV_PLIC_EXT5_IRQHandler(void); +uint8_t MIV_PLIC_EXT6_IRQHandler(void); +uint8_t MIV_PLIC_EXT7_IRQHandler(void); +uint8_t MIV_PLIC_EXT8_IRQHandler(void); +uint8_t MIV_PLIC_EXT9_IRQHandler(void); +uint8_t MIV_PLIC_EXT10_IRQHandler(void); +uint8_t MIV_PLIC_EXT11_IRQHandler(void); +uint8_t MIV_PLIC_EXT12_IRQHandler(void); +uint8_t MIV_PLIC_EXT13_IRQHandler(void); +uint8_t MIV_PLIC_EXT14_IRQHandler(void); +uint8_t MIV_PLIC_EXT15_IRQHandler(void); +uint8_t MIV_PLIC_EXT16_IRQHandler(void); +uint8_t MIV_PLIC_EXT17_IRQHandler(void); +uint8_t MIV_PLIC_EXT18_IRQHandler(void); +uint8_t MIV_PLIC_EXT19_IRQHandler(void); +uint8_t MIV_PLIC_EXT20_IRQHandler(void); +uint8_t MIV_PLIC_EXT21_IRQHandler(void); +uint8_t MIV_PLIC_EXT22_IRQHandler(void); +uint8_t MIV_PLIC_EXT23_IRQHandler(void); +uint8_t MIV_PLIC_EXT24_IRQHandler(void); +uint8_t MIV_PLIC_EXT25_IRQHandler(void); +uint8_t MIV_PLIC_EXT26_IRQHandler(void); +uint8_t MIV_PLIC_EXT27_IRQHandler(void); +uint8_t MIV_PLIC_EXT28_IRQHandler(void); +uint8_t MIV_PLIC_EXT29_IRQHandler(void); +uint8_t MIV_PLIC_EXT30_IRQHandler(void); + +/***************************************************************************//** + * MIV_PLIC interrupt handler for external interrupts. + * The array of the function pointers pointing to the weak handler of the Mi-V + * PLIC interrupt handlers. + * These functions are called by the external interrupt handler of the MIV_RV32 + * core base on the PLIC source causing the interrupt. + */ +uint8_t (* const ext_irq_handler_table[32]) (void) = +{ + Invalid_IRQHandler, + MIV_PLIC_EXT0_IRQHandler, + MIV_PLIC_EXT1_IRQHandler, + MIV_PLIC_EXT2_IRQHandler, + MIV_PLIC_EXT3_IRQHandler, + MIV_PLIC_EXT4_IRQHandler, + MIV_PLIC_EXT5_IRQHandler, + MIV_PLIC_EXT6_IRQHandler, + MIV_PLIC_EXT7_IRQHandler, + MIV_PLIC_EXT8_IRQHandler, + MIV_PLIC_EXT9_IRQHandler, + MIV_PLIC_EXT10_IRQHandler, + MIV_PLIC_EXT11_IRQHandler, + MIV_PLIC_EXT12_IRQHandler, + MIV_PLIC_EXT13_IRQHandler, + MIV_PLIC_EXT14_IRQHandler, + MIV_PLIC_EXT15_IRQHandler, + MIV_PLIC_EXT16_IRQHandler, + MIV_PLIC_EXT17_IRQHandler, + MIV_PLIC_EXT18_IRQHandler, + MIV_PLIC_EXT19_IRQHandler, + MIV_PLIC_EXT20_IRQHandler, + MIV_PLIC_EXT21_IRQHandler, + MIV_PLIC_EXT22_IRQHandler, + MIV_PLIC_EXT23_IRQHandler, + MIV_PLIC_EXT24_IRQHandler, + MIV_PLIC_EXT25_IRQHandler, + MIV_PLIC_EXT26_IRQHandler, + MIV_PLIC_EXT27_IRQHandler, + MIV_PLIC_EXT28_IRQHandler, + MIV_PLIC_EXT29_IRQHandler, + MIV_PLIC_EXT30_IRQHandler +}; + +/* Mi-V PLIC interrupt weak handlers */ +__attribute__((weak)) uint8_t Invalid_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT0_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT1_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT2_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT3_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT4_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT5_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT6_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT7_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT8_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT9_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT10_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT11_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT12_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT13_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT14_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT15_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT16_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT17_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT18_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT19_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT20_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT21_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT22_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT23_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT24_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT25_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT26_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT27_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT28_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT29_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t MIV_PLIC_EXT30_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +/*-------------------------------------------------------------------------*//** + * Please refer to miv_plic.h for more information about this function. +*/ +void +MIV_PLIC_isr +( + miv_plic_instance_t *this_plic +) +{ + unsigned long hart_id = read_csr(mhartid); + + /* claim the interrupt from PLIC controller */ + + uint32_t int_num = HAL_get_32bit_reg(this_plic->base_addr + + (0x1000 * hart_id), INT_CLAIM_COMPLETE); + + uint8_t disable = EXT_IRQ_KEEP_ENABLED; + disable = ext_irq_handler_table[int_num](); + + /* Indicate the PLIC controller that the interrupt is processed and claim is + * complete. */ + HAL_set_32bit_reg(this_plic->base_addr + + (0x1000 * hart_id), INT_CLAIM_COMPLETE, int_num); + + if (EXT_IRQ_DISABLE == disable) + { + MIV_PLIC_disable_irq(this_plic, (miv_plic_irq_num_t)int_num); + } +} diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_plic/miv_plic.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_plic/miv_plic.h new file mode 100644 index 0000000..f5d64cd --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_plic/miv_plic.h @@ -0,0 +1,425 @@ +/******************************************************************************* + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * This file contains the application programming interface for the MI-V Soft IP + * PLIC module driver. This module is delivered as a part of Mi-V Extended + * Sub-System(ESS). + */ + /*=========================================================================*//** + @mainpage Mi-V PLIC Bare Metal Driver + + ============================================================================== + Introduction + ============================================================================== + The Mi-V driver provides a set of functions for controlling the Mi-V PLIC + (platform level interrupt controller) soft-IP module. This module is delivered + as a part of the MIV_ESS. The PLIC multiplexes external interrupt signals into + a single interrupt signal that is connected to an external interrupt of the + processor. + + The major features provided by the driver are: + - Support for configuring the PLIC instances. + - Enabling and Disabling interrupts + - Interrupt Handling + + This driver can be used as part of a bare metal system where no operating + system is available. The driver can be adapted for use as part of an + operating system, but the implementation of the adaptation layer between the + driver and the operating system's driver model is outside the scope of this + driver. + + ============================================================================== + Hardware Flow Dependencies + ============================================================================== + The application software should initialize the Mi-V PLIC through the call to + the MIV_PLIC_init() function for Mi-V PLIC instance in the design. + + No Mi-V PLIC hardware configuration parameters are used by the driver, apart + from the Mi-V PLIC base address. Hence, no additional configuration files + are required to use the driver. + + ============================================================================== + Theory of Operation + ============================================================================== + The operation of Mi-V PLIC driver is divided into following steps: + - Initialization + - Enabling and Disabling interrupts + - Interrupt control + + -------------------------------------------- + Initialization + -------------------------------------------- + The Mi-V PLIC module is first initialized by the call to MIV_PLIC_init(). This + function takes a pointer to the Mi-V PLIC instance data structure and the base + address of the Mi-V PLIC instance is defined by the hardware design. The + instance data structure is used to store the base address of the Mi-V PLIC + module and a pointer to the Mi-V PLIC register data structure. The Mi-V PLIC + register data structure maps the address of the Mi-V PLIC registers. + + --------------------------------------------- + Enabling and Disabling interrupts + --------------------------------------------- + The MIV_PLIC_enable_irq() function enables the specific interrupt provided by + user. A call to this function will allow the enabling of each of the global + interrupts corresponding to the bit in the interrupt enable register of Mi-V + PLIC. + The MIV_PLIC_disable_irq() function disables the specific interrupt provided + by the user. This function can be used to disable the interrupts from outside + of the external interrupt handler. + + ---------------------------------------- + Interrupt Control + ---------------------------------------- + When an interrupt occurs on an enabled interrupt, the PLIC gateway captures + the interrupt and asserts the corresponding interrupt pending bit. Once + the enable bit and pending bit are asserted, then the PLIC_IRQ signal asserts + until the interrupt is claimed by the driver interrupt handler MIV_PLIC_irq() + function. + When multiple interrupts assert then the lowest interrupt number will be + serviced first, for example, if interrupt 1 and 6 assert at the same time, + interrupt 1 will be serviced first, followed by interrupt 6. + +*/ + +#ifndef MIV_PLIC_H_ +#define MIV_PLIC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "miv_plic_regs.h" + +#ifndef LEGACY_DIR_STRUCTURE +#include "hal/hal.h" +#include "miv_rv32_hal/miv_rv32_hal.h" +#else +#include "hal.h" +#include "miv_rv32_hal.h" +#endif + +/*-------------------------------------------------------------------------*//** + This enumeration is used to select a specific Mi-V PLIC interrupt. It is + used as a parameter to enable or disable the interrupt. +*/ +typedef enum miv_plic_irq_num +{ + NoInterrupt_IRQn = 0, + MIV_PLIC_EXT0_IRQn = 1, + MIV_PLIC_EXT1_IRQn = 2, + MIV_PLIC_EXT2_IRQn = 3, + MIV_PLIC_EXT3_IRQn = 4, + MIV_PLIC_EXT4_IRQn = 5, + MIV_PLIC_EXT5_IRQn = 6, + MIV_PLIC_EXT6_IRQn = 7, + MIV_PLIC_EXT7_IRQn = 8, + MIV_PLIC_EXT8_IRQn = 9, + MIV_PLIC_EXT9_IRQn = 10, + MIV_PLIC_EXT10_IRQn = 11, + MIV_PLIC_EXT11_IRQn = 12, + MIV_PLIC_EXT12_IRQn = 13, + MIV_PLIC_EXT13_IRQn = 14, + MIV_PLIC_EXT14_IRQn = 15, + MIV_PLIC_EXT15_IRQn = 16, + MIV_PLIC_EXT16_IRQn = 17, + MIV_PLIC_EXT17_IRQn = 18, + MIV_PLIC_EXT18_IRQn = 19, + MIV_PLIC_EXT19_IRQn = 20, + MIV_PLIC_EXT20_IRQn = 21, + MIV_PLIC_EXT21_IRQn = 22, + MIV_PLIC_EXT22_IRQn = 23, + MIV_PLIC_EXT23_IRQn = 24, + MIV_PLIC_EXT24_IRQn = 25, + MIV_PLIC_EXT25_IRQn = 26, + MIV_PLIC_EXT26_IRQn = 27, + MIV_PLIC_EXT27_IRQn = 28, + MIV_PLIC_EXT28_IRQn = 29, + MIV_PLIC_EXT29_IRQn = 30, + MIV_PLIC_EXT30_IRQn = 31 +} miv_plic_irq_num_t; + +/*--------------------------------------------------------------------------*//* + * This structure maps the priority threshold and claim complete register in + * the memory. + */ +typedef struct +{ + volatile uint32_t PRIORITY_THRESHOLD; + volatile uint32_t CLAIM_COMPLETE; + volatile uint32_t reserved[1022]; +} IRQ_Target_Type; + +/*--------------------------------------------------------------------------*//* + * This structure maps the Interrupt enable sources from 0 - 1023 for one + * context. + */ +typedef struct +{ + volatile uint32_t ENABLES[32]; +} Target_Enables_Type; + +/*-------------------------------------------------------------------------*//** + This structure holds the base address of the Mi-V PLIC module. This structure + is used by all the functions to access the Mi-V PLIC registers. +*/ +typedef struct miv_plic_instance +{ + addr_t base_addr; +} miv_plic_instance_t; + +/*-------------------------------------------------------------------------*//** + * The MIV_PLIC_isr is the top level interrupt handler function for the Mi-V PLIC + * driver. You must call the MIV_PLIC_isr() from the system level interrupt + * handler(External_IRQHandler). + * This function must be called from the external interrupt handler function + * provided by the processor hardware abstraction layer. In case of MIV_RV32 + * soft processor, it must be called from External_IRQHandler() function + * provided by MIV_RV32 HAL. + * + * The MIV_PLIC_isr() function claims the interrupt number + * that triggered the interrupt and then invokes the appropriate PLIC interrupt + * handler. + * After handling the PLIC interrupt, this function will complete the interrupt + * by clearing the claim complete bit for the particular interrupt source. + * + * @param this_plic + * A pointer to the miv_plic_instance_t data structure which + * will hold all the data related to the Mi-V PLIC instance + * being used. A pointer to this data structure is passed to + * rest of Mi-V PLIC driver functions for operation. + * + * @return + * This function does not return any value. + * + * Example: + * @code + * #define MIV_PLIC_BASE_ADDR 0x70000000 + * #define PLIC_EXT_INTR_SOURCES 31 + * + * miv_plic_instance_t g_plic; + * uint8_t MIV_PLIC_EXT0_IRQHandler(void) + * { + * *** ISR operation *** + * + * return(EXT_IRQ_KEEP_ENABLED); + * } + * + * void External_IRQHandler(void) + * { + * uint32_t reg_val = read_csr(mip); + * MIV_PLIC_isr(&g_plic); + * } + * + * void main(void) + * { + * MIV_PLIC_init(&g_plic, MIV_PLIC_BASE_ADDR, PLIC_EXT_INTR_SOURCES); + * + * MIV_PLIC_enable_irq(&g_plic, MIV_PLIC_EXT0_IRQn); + * MIV_PLIC_enable_irq(&g_plic, MIV_PLIC_EXT1_IRQn); + * MIV_PLIC_enable_irq(&g_plic, MIV_PLIC_EXT2_IRQn); + * MIV_PLIC_enable_irq(&g_plic, MIV_PLIC_EXT3_IRQn); + * } + * @endcode + */ +void MIV_PLIC_isr(miv_plic_instance_t *this_plic); + +/*-------------------------------------------------------------------------*//** + * The MIV_PLIC_init() function initializes the Mi-V PLIC instance with base + * address. This function resets the PLIC controller by disabling all the PLIC + * interrupts. + * + * Note: This function must be called before calling any other Mi-V PLIC driver + * function. + * + * @param this_plic + * A pointer to the miv_plic_instance_t data structure which + * will hold all the data related to the Mi-V PLIC instance + * being used. A pointer to this data structure is passed to + * rest of Mi-V PLIC driver functions for operation. + * + * @param base_addr + * Base address of the Mi-V PLIC instance in the MIV_ESS soft-IP. + * + * @param ext_intr_sources + * Number of interrupts initialized in the design. + * + * @return + * This function does not return any value. + * + * Example + * @code + * #define MIV_PLIC_BASE_ADDR 0x70000000 + * #define PLIC_EXT_INTR_SOURCES 31 + * + * miv_plic_instance_t g_plic; + * + * void main(void) + * { + * MIV_PLIC_init(&g_plic, MIV_PLIC_BASE_ADDR, PLIC_EXT_INTR_SOURCES); + * } + * @endcode + */ +static inline void +MIV_PLIC_init +( + miv_plic_instance_t *this_plic, + addr_t base_addr, + uint8_t ext_intr_sources +) +{ + uint32_t inc; + unsigned long hart_id = read_csr(mhartid); + + this_plic->base_addr = base_addr; + + /* Disable all interrupts for the current hart. + * The PLIC_EXT_INTR_SOURCES should be defined in the hw_platform.h. This + * macro holds the number of PLIC interrupts enabled in the design. + */ + for(inc = 0; inc < ((ext_intr_sources + 32u) / 32u); ++inc) + { + HAL_set_32bit_reg( + (this_plic->base_addr + inc + (hart_id * 128)), INT_ENABLE , 0x0u); + } +} + +/*-------------------------------------------------------------------------*//** + * The MIV_PLIC_enable_irq() function enables the PLIC interrupt provided with + * IRQn parameter. + * + * @param this_plic + * A pointer to the miv_plic_instance_t data structure which + * will hold all the data related to the Mi-V PLIC instance + * being used. A pointer to this data structure is passed to + * rest of Mi-V PLIC driver functions for operation. + * @param IRQn + * Number of PLIC interrupt to enable. + * + * @return + * This function does not return any value. + * + * Example + * @code + * #define MIV_PLIC_BASE_ADDR 0x70000000 + * #define PLIC_EXT_INTR_SOURCES 31 + * + * miv_plic_instance_t g_plic; + * + * void main(void) + * { + * MIV_PLIC_init(&g_plic, MIV_PLIC_BASE_ADDR, PLIC_EXT_INTR_SOURCES); + * + * MIV_PLIC_enable_irq(&g_plic, MIV_PLIC_EXT0_IRQn); + * MIV_PLIC_enable_irq(&g_plic, MIV_PLIC_EXT1_IRQn); + * MIV_PLIC_enable_irq(&g_plic, MIV_PLIC_EXT2_IRQn); + * MIV_PLIC_enable_irq(&g_plic, MIV_PLIC_EXT3_IRQn); + * } + * @endcode + */ +static inline void +MIV_PLIC_enable_irq +( + miv_plic_instance_t *this_plic, + miv_plic_irq_num_t IRQn +) +{ + unsigned long hart_id = read_csr(mhartid); + + uint32_t current = HAL_get_32bit_reg( + (this_plic->base_addr + (IRQn/32) + (hart_id * 128)) , INT_ENABLE); + + current |= (uint32_t)1 << (IRQn % 32); + + HAL_set_32bit_reg( + (this_plic->base_addr + (IRQn/32) + (hart_id * 128)), INT_ENABLE, current); + +} + +/*-------------------------------------------------------------------------*//** + * The MIV_PLIC_disable_irq() function disables the PLIC interrupt provided with + * IRQn parameter. + * + * NOTE: + * This function can be used to disable the PLIC interrupt from outside the + * external interrupt handler functions. + * If you wish to disable the PLIC interrupt from the external interrupt handler, + * you should use the return value of EXT_IRQ_DISABLE. This will disable the + * selected PLIC interrupt from the Mi-V PLIC driver interrupt handler. + * + * @param this_plic + * A pointer to the miv_plic_instance_t data structure which + * will hold all the data related to the Mi-V PLIC instance + * being used. A pointer to this data structure is passed to + * rest of Mi-V PLIC driver functions for operation. + * @param IRQn + * Number of PLIC interrupt to disable. + * + * @return + * This function does not return any value. + * + * Example + * @code + * #define MIV_PLIC_BASE_ADDR 0x70000000 + * #define PLIC_EXT_INTR_SOURCES 31 + * + * miv_plic_instance_t g_plic; + * + * void main(void) + * { + * MIV_PLIC_init(&g_plic, MIV_PLIC_BASE_ADDR, PLIC_EXT_INTR_SOURCES); + * + * MIV_PLIC_enable_irq(&g_plic, MIV_PLIC_EXT0_IRQn); + * MIV_PLIC_enable_irq(&g_plic, MIV_PLIC_EXT1_IRQn); + * MIV_PLIC_enable_irq(&g_plic, MIV_PLIC_EXT2_IRQn); + * MIV_PLIC_enable_irq(&g_plic, MIV_PLIC_EXT3_IRQn); + * + * MIV_PLIC_disable_irq(&g_plic, MIV_PLIC_EXT0_IRQn); + * MIV_PLIC_disable_irq(&g_plic, MIV_PLIC_EXT1_IRQn); + * MIV_PLIC_disable_irq(&g_plic, MIV_PLIC_EXT2_IRQn); + * MIV_PLIC_disable_irq(&g_plic, MIV_PLIC_EXT3_IRQn); + * } + * @endcode + */ +static inline void +MIV_PLIC_disable_irq +( + miv_plic_instance_t *this_plic, + miv_plic_irq_num_t IRQn +) +{ + unsigned long hart_id = read_csr(mhartid); + + uint32_t current = HAL_get_32bit_reg((this_plic->base_addr + (IRQn/32) + (hart_id * 128)) , INT_ENABLE); + + current &= ~((uint32_t)1 << (IRQn % 32)); + + HAL_set_32bit_reg((this_plic->base_addr + (IRQn/32) + (hart_id * 128)), INT_ENABLE, current); + +} + +#ifdef __cplusplus +} +#endif + +#endif /* MIV_PLIC_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_plic/miv_plic_regs.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_plic/miv_plic_regs.h new file mode 100644 index 0000000..76cbc0b --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_plic/miv_plic_regs.h @@ -0,0 +1,31 @@ + /******************************************************************************* + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * This file contains Register bit offsets and masks definitions for MI-V Soft + * IP PLIC module driver. This module is delivered as a part of Mi-V extended + * Sub-System(MIV_ESS). + */ + +#ifndef MIV_PLIC_REGISTERS +#define MIV_PLIC_REGISTERS 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Interrupt pending register offset */ +#define INT_PENDING_REG_OFFSET 0x1000u + +/* Interrupt enable register */ +#define INT_ENABLE_REG_OFFSET 0x2000u + +/* Interrupt claim complete register */ +#define INT_CLAIM_COMPLETE_REG_OFFSET 0x200004u + +#ifdef __cplusplus +} +#endif + +#endif /* MIV_PLIC_REGISTERS */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_timer/miv_timer.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_timer/miv_timer.h new file mode 100644 index 0000000..5f00889 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_timer/miv_timer.h @@ -0,0 +1,329 @@ +/******************************************************************************* + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Mi-V Timer Soft IP bare-metal driver. This module is delivered as part of + * the Mi-V Extended Sub System(ESS) MIV_ESS. + */ + +/*=========================================================================*//** + @mainpage Mi-V Timer Bare Metal Driver. + The Mi-V Timer bare metal software driver supports the timer module which + serves as a system timer for the Mi-V Extended Sub System(ESS). + + @section intro_sec Introduction + The MI-V Timer driver supports set of functions for controlling the Mi-V + Timer module. + The Mi-V Timer can generate a timer interrupt signal for the system based on + special system clock intervals specified by the parameters that can be passed + in by the user. + + The major features provided by Mi-V Timer driver are: + - Support for Mi-V Timer instance for each Mi-V Timer peripheral. + - Read current time + - Write to the machine time compare register + + @section hw_dependencies Hardware Flow dependency + The application should configure the Mi-V Timer driver through calls to + MIV_TIMER_init() functions for each MIV_TIMER instance in the hardware + design. The configuration parameter include the MIV_TIMER hardware instance, + base address and number of ticks to generate timer interrupt. + + MIV_RV32 core offers flexibility in terms of generating the MTIME and MTIMECMP + registers internal to the core or using external time reference. + When MIV_ESS is interfaced with MIV_RV32 core, the timer module in the MIV_ESS + can be configured as follows: + - Internal MTIME External MTIME IRQ + Generate the MTIME internally(MIV_RV32) and have a timer interrupt input + to the core as external pin(from MIV_ESS). + + - External MTIME Internal MTIME IRQ + Generate the time value externally(from MIV_ESS), in this case a 64-bit + port will open in the MIV_RV32 core as input and MIV_ESS will output the + 64-bit TIME_COUNT value. The generation of mtimecmp and interrupt is + done internally(MIV_RV32). + + - External MTIME External MTIME IRQ + Generate both the time and timer interrupt externally. + In this case 64-bit port will be available on the Mi-V RV32 core as input + and a 1 pin port will be available for timer interrupt. + + The design must be configured accordingly to use these combinations in the + firmware. + + No MIV_TIMER hardware configuration parameters are used by the driver, apart + from MIV_TIMER base address. Hence, no additional configuration files are + required to use the driver. + + @section theory_op Theory of Operation + + The MIV_TIMER module is a simple systick timer which can generate a timer + interrupt signal for the system at specific intervals specified by the + parameters that can be passed by the user. + These interrupt signal are then fed to the MIV_RV32 core via timer interrupt. + + The operation of MIV_TIMER is divided into following steps: + - Initialization + - Configuration + - Read/Write TIME + + ## Initialization + The MIV_TIMER is first initialized by a call to MIV_TIMER_init(). This + function initializes the instance of Mi-V TIMER with the base address. + The MIV_TIMER_init() function must be called before any other Mi-V Timer driver + function. + + ## Configuration + The Mi-V TIMER configuration includes writing the mtimecmp register with the + initial time value at which timer interrupt should be generated. + When the mtime register value becomes greater than or equal to mtimecmp value, + a timer interrupt signal(TIMER_IRQ) is generated. + + ## Read/Write TIME + The time value can be read by reading the mtime register via call to the + MIV_TIMER_read_mtime(). This function reads the MTIME register which contains + the 64-bit value of the timer count. The count increments by 1 every time the + prescale ticks. This function returns 64-bit MTIME_COUNT value which is the + current value of timer count. + + The time value read in the MIV_TIMER_read_mtime() function can be written to + the mtimecmp register by calling MIV_TIMER_write_mtimecmp() to generate + periodic interrupts. + The writing of the mtimecmp register should be done in the systick_handler() + function. + */ + +#ifndef MIV_TIMER_H_ +#define MIV_TIMER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef LEGACY_DIR_STRUCTURE +#include "hal/hal.h" + +#else +#include "hal.h" +#endif +/*-------------------------------------------------------------------------*//** +MIV_TIMER_SUCCESS +===================== + +The MIV_TIMER_SUCCESS constant indicates successful configuration of +Mi-V Timer module. +*/ +#define MIV_TIMER_SUCCESS 0u + +/*-------------------------------------------------------------------------*//** +MIV_TIMER_ERROR +===================== + +The MIV_TIMER_ERROR constant indicates that there is an error with +configuring the Mi-V Timer module. +*/ +#define MIV_TIMER_ERROR 1u + +/*-------------------------------------------------------------------------*//* +MIV_TIMER_MASK_32BIT +===================== + +32-bit mask constant used in calculation of 64-bit register value. +*/ +#define MIV_TIMER_MASK_32BIT 0xFFFFFFFFu + +/*-------------------------------------------------------------------------*//* +Mi-V Timer register offsets +===================== +The MTIMECMP is the 64-bit timer compare register, it pre-sets the threshold +which needs to be reached by the timer count register. +This 64-bit register is accessed with 2 32-bit address offset, lower 32-bits +and higher 32-bits. + - MIV_TIMER_MTIMECMP_L_REG_OFFSET + - MIV_TIMER_MTIMECMP_H_REG_OFFSET + +The MTIME is the 64-bit register that contains the 64-bit timer count. The +count increments by 1 every time the prescaler ticks. +This 64-bit register is accessed with 2 32-bit address offset, lower 32-bits +and higher 32-bits. + - MIV_TIMER_MTIME_L_REG_OFFSET + - MIV_TIMER_MTIME_H_REG_OFFSET + +The PRESCALE register is used to determine the amount of clock cycles the +selected clock needs to go through, for MTIME register to increment count. + - MIV_TIMER_PRESCALAR_REG_OFFSET +*/ + +/// @cond private +#define MIV_TIMER_MTIMECMP_L_REG_OFFSET 0x4000u +#define MIV_TIMER_MTIMECMP_H_REG_OFFSET 0x4004u + +#define MIV_TIMER_MTIME_L_REG_OFFSET 0xBFF8u +#define MIV_TIMER_MTIME_H_REG_OFFSET 0xBFFCu + +#define MIV_TIMER_PRESCALAR_REG_OFFSET 0x5000u +/// @endcond + +/*-------------------------------------------------------------------------*//** + This structure holds the base address of the Mi-V Timer module and instance + of the Mi-V Timer register structure. +*/ +typedef struct miv_timer_instance +{ + addr_t base_addr; +} miv_timer_instance_t; + +/** The MIV_TIMER_init() is used to initialize the Mi-V Timer module. This + function will assign the base addresses of the Mi-V Timer module. + User should call this function before calling any of the Mi-V Timer driver + APIs. + + @param this_timer + Timer structure which holds the base address for the Mi-V Timer hardware + instance. + + @param base_address + Base address of the Mi-V Timer module. + + @return + This function does not return any value. + */ +static inline void +MIV_TIMER_init +( + miv_timer_instance_t* this_timer, + addr_t base_addr +) +{ + this_timer->base_addr = base_addr; +} + +/** MIV_TIMER_read_current_time() is used to read the mtimecmp register values. + + @param this_timer + Timer structure which holds the base address for the Mi-V Timer hardware + instance. + + @return + This function returns 64-bit mtimecmp register value. + */ +static inline uint64_t +MIV_TIMER_read_current_time +( + miv_timer_instance_t* this_timer +) +{ + volatile uint64_t read_data = 0u; + volatile uint32_t mtime_hi = 0u; + volatile uint32_t mtime_lo = 0u; + + /* when mtime lower word is 0xFFFFFFFF, there will be rollover and + * returned value could be wrong. */ + do { + mtime_hi = HAL_get_32bit_reg(this_timer->base_addr, MIV_TIMER_MTIME_H); + mtime_lo = HAL_get_32bit_reg(this_timer->base_addr, MIV_TIMER_MTIME_L); + + } while(mtime_hi != HAL_get_32bit_reg(this_timer->base_addr, MIV_TIMER_MTIME_H)); + + read_data = mtime_hi; + + return(((read_data) << 32u) | mtime_lo); +} + +/** MIV_TIMER_write_compare_time() is used to write to the MTIMECMP register in + the event of interrupt. User must use this function in the interrupt handler + to de-assert the MIV_TIMER interrupt. + + @param this_timer + Timer structure which holds the base address for the Mi-V Timer hardware + instance. + + @param write_value + Value to write into the mtimecmp register. + + @return + This function does not return any value. + */ +static inline void +MIV_TIMER_write_compare_time +( + miv_timer_instance_t* this_timer, + uint64_t compare_reg_value +) +{ + HAL_set_32bit_reg(this_timer->base_addr, MIV_TIMER_MTIMECMP_H, MIV_TIMER_MASK_32BIT); + + HAL_set_32bit_reg(this_timer->base_addr, MIV_TIMER_MTIMECMP_L, + (compare_reg_value & MIV_TIMER_MASK_32BIT)); + + HAL_set_32bit_reg(this_timer->base_addr, MIV_TIMER_MTIMECMP_H, + ((compare_reg_value >> 32u) & MIV_TIMER_MASK_32BIT)); +} + +/** The MIV_TIMER_config() is used to configure the MIV_ESS Timer module. The + prescale value serves to divide the count of clock cycles for the timer and + provides control over what point in time, the timer interrupt gets + asserted. + + @param this_timer + Timer structure which holds the base address for the Mi-V Timer hardware + instance. + + @param ticks + Number of ticks after which interrupt will be generated. + + @return + This function returns Mi-V Timer configuration status. + */ +static inline uint32_t +MIV_TIMER_config +( + miv_timer_instance_t* this_timer, + uint64_t ticks +) +{ + uint32_t ret_val = MIV_TIMER_ERROR; + uint64_t mtime_val = 0u; + uint32_t prescalar = 0u; + uint64_t miv_timer_increment = 0U; + + prescalar = HAL_get_32bit_reg(this_timer->base_addr, MIV_TIMER_PRESCALAR); + + miv_timer_increment = (uint64_t)(ticks) / prescalar; + + if (miv_timer_increment > 0U) + { + mtime_val = MIV_TIMER_read_current_time(this_timer); + + MIV_TIMER_write_compare_time(this_timer ,(mtime_val + miv_timer_increment)); + + ret_val = MIV_TIMER_SUCCESS; + } + + return ret_val; +} + +#ifdef __cplusplus +} +#endif + +#endif /* MIV_TIMER_H */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_udma/miv_udma.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_udma/miv_udma.c new file mode 100644 index 0000000..cbd9652 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_udma/miv_udma.c @@ -0,0 +1,109 @@ +/******************************************************************************* + * (c) Copyright 2022-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Mi-V uDMA Soft IP bare-metal driver. This module is delivered as part of + * Mi-V Extended Sub System(MIV_ESS) + */ + +#include "miv_udma_regs.h" +#include "miv_udma.h" + +/***************************************************************************//** + * MIV_uDMA_init() + * See "miv_udma.h" for details of how to use this function. + */ +void +MIV_uDMA_init +( + miv_udma_instance_t* this_udma, + addr_t base_addr +) +{ + /* Assign the Mi-V uDMA base address to the uDMA instance structure */ + this_udma->base_address = base_addr; +} + +/***************************************************************************//** + * MIV_uDMA_config() + * See "miv_udma.h" for details of how to use this function. + */ +void +MIV_uDMA_config +( + miv_udma_instance_t* this_udma, + addr_t src_addr, + addr_t dest_addr, + uint32_t transfer_size, + uint32_t irq_config +) +{ + /* Source memory start address */ + HAL_set_32bit_reg(this_udma->base_address, SRC_START_ADDR, src_addr); + + /* Destination memory start address */ + HAL_set_32bit_reg(this_udma->base_address, DEST_START_ADDR, dest_addr); + + /* Data transfer size */ + HAL_set_32bit_reg(this_udma->base_address, BLK_SIZE, transfer_size); + + /* Configure the uDMA IRQ */ + HAL_set_32bit_reg(this_udma->base_address, IRQ_CFG, irq_config); +} + +/***************************************************************************//** + * MIV_uDMA_start() + * See "miv_udma.h" for details of how to use this function. + */ +void +MIV_uDMA_start +( + miv_udma_instance_t* this_udma +) +{ + /* Start the uDMA transfer */ + HAL_set_32bit_reg(this_udma->base_address, CONTROL_SR, CTRL_START_TX_MASK); +} + +/***************************************************************************//** + * MIV_uDMA_reset() + * See "miv_udma.h" for details of how to use this function. + */ +void +MIV_uDMA_reset +( + miv_udma_instance_t* this_udma +) +{ + /* Toggle the uDMA_reset bit to reset the uDMA. + * Resetting the uDMA will clear all the configuration made by + * MIV_uDMA_config(). + * + * This function should be called from the interrupt handler to clear the + * IRQ. + */ + HAL_set_32bit_reg_field(this_udma->base_address, CTRL_RESET_TX, 0x1u); + HAL_set_32bit_reg_field(this_udma->base_address, CTRL_RESET_TX, 0x0u); +} + +/***************************************************************************//** + * MIV_uDMA_read_status() + * See "miv_udma.h" for details of how to use this function. + */ +uint32_t +MIV_uDMA_read_status +( + miv_udma_instance_t* this_udma +) +{ + uint32_t status = 0u; + + /* Read the status of the uDMA transfer. + * The transfer status register can be Error or Busy depending on the + * current uDMA transfer. + */ + status = HAL_get_32bit_reg(this_udma->base_address, TX_STATUS); + + return status; +} diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_udma/miv_udma.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_udma/miv_udma.h new file mode 100644 index 0000000..efa8731 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_udma/miv_udma.h @@ -0,0 +1,290 @@ +/******************************************************************************* + * Copyright 2022-2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * This file contains the application programming interface for the MI-V Soft IP + * uDMA module driver. This module is delivered as a part of Mi-V Extended + * Sub-System(MIV_ESS). + */ + +/*=========================================================================*//** + @mainpage Mi-V uDMA Bare Metal Driver. + The Mi-V uDMA bare metal software driver. + + @section intro_sec Introduction + The Mi-V uDMA driver provides a set of functions to control the Mi-V uDMA + module in the Mi-V Extended Subsystem (MIV_ESS) soft-IP. The Mi-V uDMA module + allows peripherals with AHB interfaces to transfer data independently of the + MIV_RV32 RISC-V processor. + + Following are the major features provided by the Mi-V uDMA driver: + - Initialization and configuration + - Start and reset the transaction + + This driver can be used as part of a bare metal system where no operating + system is available. The driver can be adapted for use as part of an + operating system, but the implementation of the adaptation layer between the + driver and the operating system's driver model is outside the scope of this + driver. + + @section hw_dependencies Hardware Flow Dependency + The application software should initialize and configure the Mi-V uDMA through + calling the MIV_uDMA_init() and MIV_uDMA_config() functions for each Mi-V + uDMA instance in the design. + + The uDMA can operate in two possible transfer configurations: + + - AHBL Read -> AHBL Write: + In this configuration, the uDMA reads data from the source memory over an + AHBL (mirrored main/initiator) read interface and writes data to the + destination memory over an AHBL (mirrored main/initiator) write interface. + + - AHBL Read -> TAS Write: + In this configuration, the uDMA reads data from the source memory over an + AHBL (mirrored main/initiator) read interface and writes data to the + destination memory over the TAS (mirrored main/initiator) write interface. + + Note: The AHBL Read -> TAS Write configuration is out of scope for this + driver. + + @section theory_op Theory of Operation + The uDMA module in the Mi-V Extended Sub System (MIV_ESS) is a single-channel + uDMA module that allows peripherals to perform read-write operations between + source and destination memory. The Mi-V uDMA driver is used in + interrupt-driven mode and uses the Mi-V uDMA IRQ signal to drive the + interrupt service routine (ISR), which signifies a transfer has completed. + The status is checked in the ISR to ensure the transfer is completed + successfully. + The reset operation in the ISR resets the Mi-V uDMA controller. Once the Mi-V + uDMA transfer is complete, Mi-V uDMA retires. To initiate another + transaction, Mi-V uDMA needs to be configured again. + + The operation of the Mi-V uDMA driver is divided into the following + categories: + - Initialization + - Configuration + - Start and reset the transfer + + Initialization and configuration: + Mi-V uDMA is first initialized by calling MIV_uDMA_init() function. This + function initializes the instance of Mi-V uDMA with the base address. The + MIV_uDMA_init() function must be called before calling any other Mi-V uDMA + driver functions. + + The Mi-V uDMA is configured by calling MIV_uDMA_config() function. This + function configures the source_addr and dest_addr registers of the Mi-V + uDMA with source and destination addresses for Mi-V uDMA transfers. + This function also configures the transfer size and interrupt preference for + successful transfers using Mi-V uDMA. + + Start and reset the transfer: + Once the Mi-V uDMA is configured, initiate the transfers by calling the + MIV_uDMA_start() function. Once the Mi-V uDMA transfer is started, it cannot + be aborted, and the status of the transfer should be read from the ISR by + calling the MIV_uDMA_read_status() function. + + Reset the Mi-V uDMA to the default state by calling the MIV_uDMA_reset() + function. After performing the reset operation, reconfigure the Mi-V uDMA to + perform transfers as MIV_uDMA_reset() resets the Mi-V uDMA controller. + */ + +#ifndef MIV_uDMA_H_ +#define MIV_uDMA_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef LEGACY_DIR_STRUCTURE +#include "hal/hal.h" +#include "hal/cpu_types.h" + +#else +#include "hal.h" +#include "cpu_types.h" +#endif + +/*-------------------------------------------------------------------------*//** + MIV_uDMA_CTRL_IRQ_CONFIG + ===================== + + The MIV_uDMA_CTRL_IRQ_CONFIG macro is used to assert the uDMA IRQ when an error + occurs during a uDMA transfer or on the completion of a uDMA transfer. + */ +#define MIV_uDMA_CTRL_IRQ_CONFIG 1u + +/*-------------------------------------------------------------------------*//** + MIV_uDMA_STATUS_BUSY + ===================== + + The MIV_uDMA_STATUS_BUSY macro is used to indicate that the uDMA transfer is + in progress. + */ +#define MIV_uDMA_STATUS_BUSY 1u + +/*-------------------------------------------------------------------------*//** + MIV_uDMA_STATUS_ERROR + ===================== + + The MIV_uDMA_STATUS_ERROR macro is used to indicate that the last uDMA + transfer has caused an error. + */ +#define MIV_uDMA_STATUS_ERROR 2u + +/***************************************************************************//** + * This structure holds the base of the Mi-V uDMA module, which is used in the + * other functions of the driver to access the uDMA registers. + */ +typedef struct miv_udma_instance +{ + addr_t base_address; +} miv_udma_instance_t; + +/***************************************************************************//** + * The MIV_uDMA_init() function assigns the base address of the Mi-V uDMA module + * to the uDMA instance structure. + * This address is used in a later part of the driver to access the uDMA + * registers. + * + * @param this_udma + * This parameter is a pointer to the miv_udma_instance_t structure. + * + * @param base_addr + * Base address of the Mi-V uDMA module. + * + * @return + * This function does not return a value. + */ +void +MIV_uDMA_init +( + miv_udma_instance_t* this_udma, + addr_t base_addr +); + +/***************************************************************************//** + * The MIV_uDMA_config() function is used to configure the Mi-V uDMA controller. + * This function will set the source address, destination address, block size, + * and IRQ configuration register. + * + * @param this_udma + * This parameter is a pointer to the miv_udma_instance_t structure, which + * holds the base address of the Mi-V uDMA module. + * + * @param base_addr + * Base address of the Mi-V uDMA. + * + * @param src_addr + * Source address of memory from where the uDMA reads the data. + * + * @param dest_addr + * Destination address where the data is written from src_addr. + * + * @param transfer_size + * Number of 32-bit words to transfer. + * + * @param irq_config + * uDMA IRQ configuration + * - When set, the IRQ is asserted when an error occurs during a uDMA + * transfer or on the completion of the uDMA transfer. + * - When clear, the IRQ is only asserted when an error occurs during a + * uDMA transfer. + * + * @return + * This function does not return any value. + */ +void +MIV_uDMA_config +( + miv_udma_instance_t* this_udma, + addr_t src_addr, + addr_t dest_addr, + uint32_t transfer_size, + uint32_t irq_config +); + +/***************************************************************************//** + * The MIV_uDMA_start() function is used to start the uDMA transfer. + * + * @param this_udma + * This parameter is a pointer to the miv_udma_instance_t structure, which + * holds the base address of the Mi-V uDMA module. + * + * @return + * This function does not return any value. + */ +void +MIV_uDMA_start +( + miv_udma_instance_t* this_udma +); + +/***************************************************************************//** + * The MIV_uDMA_reset() function is used to clear the uDMA interrupt and reset + * the uDMA transfer. + * + * This function should be called from the interrupt handler to reset the values + * set during MIV_uDMA_config(). + * + * @param this_udma + * This parameter is a pointer to the miv_udma_instance_t structure, which + * holds the base address of the Mi-V uDMA module. + * + * @return + * This function does not return any value. + */ +void +MIV_uDMA_reset +( + miv_udma_instance_t* this_udma +); + +/***************************************************************************//** + * The MIV_uDMA_read_status() function is used to read the status of the uDMA + * transfer. When interrupt is enabled, this function can be called from the + * interrupt handler to know the reason for a uDMA interrupt. + * + * @param this_udma + * This parameter is a pointer to the miv_udma_instance_t structure, which + * holds the base address of the Mi-V uDMA module. + * + * @return + * The return value indicates an error due to the busy status of the uDMA + * channel. + * + * |Bit Number| Name | Description | + * |----------|---------|------------------------------------------------------| + * | 0 | Busy | When set indicates that uDMA transfer is in progress| + * | 1 | Error | When set indicates that last uDMA transfer caused an| + * | | | error. | + */ +uint32_t +MIV_uDMA_read_status +( + miv_udma_instance_t* this_pdma +); + +#ifdef __cplusplus +} +#endif + +#endif /* MIV_uDMA_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_udma/miv_udma_regs.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_udma/miv_udma_regs.h new file mode 100644 index 0000000..14d0759 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_udma/miv_udma_regs.h @@ -0,0 +1,94 @@ + /******************************************************************************* + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * This file contains Register bit offsets and masks definitions for MI-V Soft + * IP uDMA module driver. This module is delivered as a part of Mi-V + * extended Sub-System(ESS) MIV_ESS. + */ + +#ifndef MIV_UDMA_APB_REGISTERS +#define MIV_UDMA_APB_REGISTERS 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/**************************************************************************//** + * Control start/Reset register details + */ + +#define CONTROL_SR_REG_OFFSET 0x0u + +/* Control start/Reset register bits */ +#define CONTROL_SR_OFFSET 0x00u +#define CONTROL_SR_MASK 0x03u +#define CONTROL_SR_SHIFT 0u + +/* uDMA Control Start Transfer */ + +#define CTRL_START_TX_OFFSET 0x00u +#define CTRL_START_TX_MASK 0x01u +#define CTRL_START_TX_SHIFT 0u + +/* uDMA Control Reset Transfer */ +#define CTRL_RESET_TX_OFFSET 0x00u +#define CTRL_RESET_TX_MASK 0x02u +#define CTRL_RESET_TX_SHIFT 1u + +/**************************************************************************//** + * IRQ Configuration register details + */ +#define IRQ_CFG_REG_OFFSET 0x4u + +/* Control start/Reset register bits */ +#define IRQ_CFG_OFFSET 0x04u +#define IRQ_CFG_MASK 0x01u +#define IRQ_CFG_SHIFT 0u + +/***************************************************************************//** + * Transfer Status register details + */ +#define TX_STATUS_REG_OFFSET 0x08u + +/* Transfer status register bits */ +#define TX_STATUS_OFFSET 0x08u +#define TX_STATUS_MASK 0x03u +#define TX_STATUS_SHIFT 0u + +/***************************************************************************//** + * Source Memory Start Address Register + */ +#define SRC_START_ADDR_REG_OFFSET 0x0cu + +/* Source Memory Start Address Register bits */ +#define SRC_START_ADDR_OFFSET 0x0cu +#define SRC_START_ADDR_MASK 0xFFFFFFFFu +#define SRC_START_ADDR_SHIFT 0u + +/***************************************************************************//** + * Destination Memory Start Address register details + */ +#define DEST_START_ADDR_REG_OFFSET 0x10u + +/* Destination Memory Start Address register bits */ +#define DEST_START_ADDR_OFFSET 0x10u +#define DEST_START_ADDR_MASK 0xFFFFFFFFu +#define DEST_START_ADDR_SHIFT 0x0u + +/***************************************************************************//** + * Block Size register details + */ +#define BLK_SIZE_REG_OFFSET 0x14u + +/* Destination Memory Start Address register bits */ +#define BLK_SIZE_OFFSET 0x14u +#define BLK_SIZE_MASK 0xFFFFFFFFu +#define BLK_SIZE_SHIFT 0x0u + +#ifdef __cplusplus +} +#endif + +#endif /* MIV_UDMA_APB_REGISTERS */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_watchdog/miv_watchdog.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_watchdog/miv_watchdog.c new file mode 100644 index 0000000..525928a --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_watchdog/miv_watchdog.c @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Mi-V Watchdog Soft IP bare-metal driver. This module is delivered as part of + * Extended Sub System(ESS) MIV_ESS. + * Please refer to miv_watchdog.h file for more information. + */ + +#include "miv_watchdog.h" + +addr_t g_this_wdog; + +/***************************************************************************//* + * Please refer to miv_watchdog.h for more info about this function + */ +void +MIV_WDOG_init +( + addr_t base_addr +) +{ + /* Register the Mi-V Watchdog base address to the driver */ + g_this_wdog = base_addr; +} + +/***************************************************************************//* + * Please refer to miv_watchdog.h for more info about this function + */ +uint8_t MIV_WDOG_configure +( + const miv_wdog_config_t *config +) +{ + uint8_t error = 0u; + + /* check load value and trigger max value */ + if (config->timeout_val <= MIV_WDOG_TRIGGER_MAX) + { + HAL_set_32bit_reg(g_this_wdog, WDOGTRIG,(config->timeout_val)); + } + else + { + error = 1u; + } + + if (config->time_val <= MIV_WDOG_TIMER_MAX) + { + HAL_set_32bit_reg(g_this_wdog, WDOGMSVP,(config->mvrp_val)); + } + else + { + error = 1u; + } + + HAL_set_32bit_reg_field(g_this_wdog, WDOGCNTL_NEXT_ENFORBIDDEN, + config->forbidden_en); + + /* Reload watchdog with new load if it is not in forbidden window */ + if (!(WDOGSTAT_FORBIDDEN_MASK & (HAL_get_32bit_reg(g_this_wdog, WDOGSTAT)))) + { + HAL_set_32bit_reg(g_this_wdog, WDOGRFSH, MIV_WDOG_REFRESH_KEY); + } + else + { + error = 1u; + } + + return (error); +} + +/***************************************************************************//* + * Please refer to miv_watchdog.h for more info about this function + */ +void MIV_WDOG_get_config +( + miv_wdog_config_t *config +) +{ + if (0 != g_this_wdog) + { + + config->time_val = HAL_get_32bit_reg(g_this_wdog, WDOGTIME); + + config->timeout_val = HAL_get_32bit_reg(g_this_wdog, WDOGTRIG); + + config->mvrp_val = HAL_get_32bit_reg(g_this_wdog, WDOGMSVP); + + config->forbidden_en = HAL_get_32bit_reg_field(g_this_wdog, + WDOGCNTL_NEXT_ENFORBIDDEN); + } +} + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_watchdog/miv_watchdog.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_watchdog/miv_watchdog.h new file mode 100644 index 0000000..8877e55 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_watchdog/miv_watchdog.h @@ -0,0 +1,553 @@ +/******************************************************************************* + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * This file contains the application programming interface for the MI-V Soft IP + * Watchdog module driver. This module is delivered as a part of Mi-V Extended + * Sub-System(MIV_ESS). + */ +/*=========================================================================*//** + @mainpage MiV Watchdog Bare Metal Driver + + ============================================================================== + Introduction + ============================================================================== + The Mi-V Watchdog module in the MIV_ESS is used to generate a reset for the + system automatically if the software doesn't periodically update or refresh + the timer countdown register. This software driver provides set of functions + for controlling Mi-V Watchdog module as a part of bare metal system where no + operating system is available. The driver can be adapted for use as a part of + an operating system, but the implementation of the adaptation layer between + the driver and the operating system's driver model is outside the scope of the + driver. + + Mi-V Watchdog provides following features: + - Initializing the Mi-V Watchdog + - Reading current value and status of watchdog timer + - Refreshing the watchdog timer value + - Enabling, disabling and clearing timeout and Maximum Value up to which + Refresh is Permitted (MVRP) interrupts. + + ============================================================================== + Hardware Flow Dependencies + ============================================================================== + The application software should initialize and configure the Mi-V Watchdog + module the the call to the MIV_WDOG_init() and MIV_WDOG_configure() functions. + + No MIV_WDOG hardware configuration parameter are used by the driver, apart + from the MIV_WDOG base address. Hence, no additional configuration files are + required to use the driver. + + ============================================================================== + Theory of Operation + ============================================================================== + The Mi-V Watchdog driver functions are grouped into the following categories: + - Initialization and configuration + - Reading the current value and status of the watchdog timer + - Refreshing the watchdog timer value + - Support for enabling, disabling and clearing time-out and MVRP interrupts. + + -------------------------------- + Initialization and Configuration + -------------------------------- + The MIV_WDOG_init() function stores the base of MIV_WDT module in the MIV_ESS. + This base address is used by rest of the functions to access the Mi-V Watchdog + registers. Please make call this function before calling any other function + from this driver. + Note: The Mi-V Watchdog driver supports only one instance of MIV_WDT in the + hardware. + The Mi-V Watchdog driver provides the MIV_WDOG_configure() function to + configure the MIV_WDOG module with desired configuration values. It also + provides the MIV_WDOG_get_config() to read back the current configuration of + the MIV_WDOG. You can use this function to retrieve the current configurations + and then overwrite them with the application specific values, such as initial + watchdog timer value, Maximum Value (up to which) Refresh (is) Permitted, + watchdog time-out value, enable/disable forbidden region, enable/disable + MVRP interrupt and interrupt type. + + -------------------------------------------- + Reading the Watchdog Timer Value and Status + -------------------------------------------- + Mi-V Watchdog is a down counter. A refresh forbidden window can be created by + configuring the watchdog Maximum Value up to which Refresh is Permitted (MVRP). + When the current value of the watchdog timer is greater than the MVRP value, + refreshing the watchdog is forbidden. Attempting to refresh the watchdog timer + in the forbidden window will assert a timeout interrupt. The + MIV_WDOG_forbidden_status() function can be used to know whether the watchdog + timer is in forbidden window or has crossed it. By default, the forbidden + window is disabled. It can be enabled by providing an appropriate value as + parameter to the MIV_WDOG_configure() function. When the forbidden window is + disabled, any attempt to refresh the watchdog timer is ignored and the counter + keeps on down counting. + + The current value of the watchdog timer can be read using the + MIV_WDOG_current_value() function. This function can be called at any time. + + -------------------------------------------- + Refreshing the Watchdog Timer Value + -------------------------------------------- + The watchdog timer value is refreshed using the MIV_WDOG_reload() function. + The value reloaded into the watchdog timer down-counter is specified at the + configuration time with an appropriate value as parameter to the + MIV_WDOG_get_config() function. + + -------------------------------------------- + Interrupt Control + -------------------------------------------- + The Mi-V Watchdog generates two interrupts, The MVRP interrupt and + the timeout interrupt. + The MVRP interrupt is generated when the watchdog down-counter crosses the + Maximum Value up to which Refresh is Permitted (MVRP). Following functions to + control MVRP interrupt: + - MIV_WDOG_enable_mvrp_irq + - MIV_WDOG_disable_mvrp_irq + - MIV_WDOG_clear_mvrp_irq + + The timeout interrupt is generated when the watchdog down-counter crosses the + watchdog timeout value. The timeout value is a non-zero value and it can be + set to a maximum of MIV_WDOG_TRIGGER_MAX. The non-maskable interrupt is + generated when the watchdog crosses this timeout value, the down counter + keeps on down counting and a reset signal is generated when reaches zero. + Following functions to control timeout interrupt: + - MIV_WDOG_enable_timeout_irq + - MIV_WDOG_disable_timeout_irq + - MIV_WDOG_clear_timeout_irq + +*//*=========================================================================*/ + +#ifndef MIV_WATCHDOG_H_ +#define MIV_WATCHDOG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "miv_watchdog_regs.h" + +#ifndef LEGACY_DIR_STRUCTURE +#include "hal/hal.h" +#else +#include "hal.h" +#endif + +/****************************************************************************//* + * The following constants can be used to configure the Mi-V Watchdog where a + * zero or non-zero value such as enable or disable is to be provided as a input + * parameter as shown below: + */ +#define MIV_WDOG_ENABLE 1u +#define MIV_WDOG_DISABLE 0u + +/***************************************************************************//** + The miv_wdog_config_t type for the watchdog Configuration structure. This + type is used as a parameter for the MIV_WDOG_configure() and the + MIV_WDOG_get_config() functions. + + Following are the values as part of this structure +| Parameter | Description | +|------------------|-----------------------------------------------------------| +| time_val | The value from which the watchdog timer counts down | +| mvrp_val | The Watchdog MVRP value | +| timeout_val | The watchdog timeout value | +| forbidden_en | Enable/disable the forbidden window | +| | When set, if a refresh occurs in the forbidden window, | +| | the watchdog timeout interrupt will be generated. | + +Time calculation example: + + time_val = 0xFFFFF0u + mvrp_val = 0x989680u + timeout_val = 0x3e8u + + A prescaler = 256 is used. + Considering clock = 50Mhz + + The MVRP interrupt will happen after + (0xFFFFF0 - 0x989680) * ( 1/(50MHz/256)) + mvrp interrupt will happen after 34 sec. after system reset + + (0xFFFFF0 - 0x3e8) * ( 1/(50MHz/256)) + timeout interrupt will happen after 85 sec. after system reset + */ +typedef struct miv_wdog_config +{ + uint32_t time_val; + uint32_t mvrp_val; + uint32_t timeout_val; + uint32_t forbidden_en; + uint32_t intr_type; +}miv_wdog_config_t; + +extern addr_t g_this_wdog; + +/***************************************************************************//* + Internal constants and types +*******************************************************************************/ + +/// @cond private +#define MIV_WDOG_TRIGGER_MAX 4095u +#define MIV_WDOG_TIMER_MAX 16777200u +/// @endcond + +/*-------------------------------------------------------------------------*//* +MIV_WDOG_REFRESH_KEY +===================== + +The MIV_WDOG_REFRESH_KEY macro holds the magic value which will cause a +reload of the watchdog's down counter when written to the watchdog's +WDOGREFRESH register. +*/ +#define MIV_WDOG_REFRESH_KEY (uint32_t)0xDEADC0DEU + +/*-------------------------------------------------------------------------*//* +MIV_WDOG_FORCE_RESET_KEY +===================== +The MIV_WDOG_FORCE_RESET_KEY macro holds the magic value which will force a +reset if the watchdog is already timeout. Writing any other value or writing +TRIGGER register at other times will trigger the watchdog NMI sequence +(i.e raise a timeout interrupt) + */ +#define MIV_WDOG_FORCE_RESET_KEY (uint32_t)0xDEADU + +/***************************************************************************//** + * The MIV_WDOG_init() is used to register the Mi-V Watchdog module base + * address to the driver. + * + * Note: User should call this function before calling any other Mi-V watchdog + * driver function. + * + * @param base_addr + * The base address of the Mi-V watchdog module. This address is used by + * rest of the watchdog driver functions to access the registers. + * + * @return + * This function does not return any value. + */ +void +MIV_WDOG_init +( + addr_t base_addr +); + +/***************************************************************************//** + * The MIV_WDOG_get_config() function returns the current configurations of the + * Mi-V Watchdog. The Mi-V Watchdog is pre-initialized by the flash + * bits at the design time. When used for the first time before calling the + * MIV_WDOG_configure() function, this function will return the default + * configurations as configured at the design time. + * + * @param config + * The config parameter is used to store the current configuration of the Mi-V + * Watchdog. + * + * Please see the description of miv_wdog_config_t for details. + * + * @return + * This function does not return any value. + * + * Example: + */ +void +MIV_WDOG_get_config +( + miv_wdog_config_t* config +); + +/***************************************************************************//** + * The MIV_WDOG_configure() function configures the watchdog module. The + * Watchdog module is pre-initialized by the flash bits at the design time to the + * default values. You can reconfigure the Watchdog module using + * MIV_WDOG_configure() function. + * + * Note that the MIV_WDOG_configure() function can be used only once, as it + * writes into the TIME register. After a write into the TIME register, the TIME, + * TRIGGER and MSVP register values are frozen and can't be altered again unless + * a system reset happens. + * + * Note also that the Mi-V Watchdog is not enabled at reset, calling this function + * will start the watchdog, it cannot then be disabled and must be refreshed + * periodically. + * + * @param config + * The config parameter is the input parameter in which the configurations to + * be applied to the watchdog module are provided by the application. + * Please see the description of miv_wdog_config_t for details. + * + * @return + * This function returns a zero value when executed successfully. A non-zero + * value is returned when the configuration values are out of bound. + * + * Example: + */ +uint8_t +MIV_WDOG_configure +( + const miv_wdog_config_t * config +); + +/***************************************************************************//** + * The MIV_WDOG_reload() function causes the watchdog to reload its down-counter + * timer with the load value configured through interrupt handler. This function + * must be called regularly to avoid a system reset or a watchdog interrupt. + * + * @param + * Void + * + * @return + * This function does not return a value. + */ +static inline void +MIV_WDOG_reload +( + void +) +{ + if (0 != g_this_wdog) + { + HAL_set_32bit_reg(g_this_wdog, WDOGRFSH, MIV_WDOG_REFRESH_KEY); + } +} + +/***************************************************************************//** + * The MIV_WDOG_current_value() function returns the current value of the + * watchdog's down-counter. + * + * @param + * Void + * + * @return + * This function returns the current value of the watchdog’s down-counter as + * a 32-bit unsigned integer. + */ +static inline uint32_t +MIV_WDOG_current_value +( + void +) +{ + return (HAL_get_32bit_reg(g_this_wdog, WDOGRFSH)); +} + +/***************************************************************************//** + * The MIV_WDOG_forbidden_status() function returns the refresh status of the + * Mi-V Watchdog. + * + * @param + * Void + * + * @return + * This function returns the refresh status of the watchdog. A value of 1 + * indicates that watchdog's down-counter is within the forbidden window and + * that a reload should not be done. A value of 0 indicates that the watchdog's + * down counter is within the permitted window and that a reload is allowed. + */ +static inline uint32_t +MIV_WDOG_forbidden_status +( + void +) +{ + return (HAL_get_32bit_reg_field(g_this_wdog, WDOGSTAT_FORBIDDEN)); +} + +/***************************************************************************//** + * The MIV_WDOG_enable_mvrp_irq() function enables the MVRP interrupt. + * This interrupt is asserted when the timer countdown register leaves the + * maximum value up to which refresh is permitted (MVRP) window. + * + * @param + * Void + * + * @return + * This function does not return a value. + * + * Example: + */ +static inline void +MIV_WDOG_enable_mvrp_irq +( + void +) +{ + + if (0 != g_this_wdog) + { + HAL_set_32bit_reg_field(g_this_wdog, WDOGCNTL_NEXT_INTENT_MSVP, 0x01u); + } +} + +/***************************************************************************//** + * The MIV_WDOG_disable_mvrp_irq() function disables the generation of the + * MVRP interrupt. + * + * @param + * Void + * + * @return + * This function does not return a value. + */ +static inline void +MIV_WDOG_disable_mvrp_irq +( + void +) +{ + if (0 != g_this_wdog) + { + HAL_set_32bit_reg_field(g_this_wdog, WDOGCNTL_NEXT_INTENT_MSVP, 0x0u); + } +} + +/***************************************************************************//** + * The MIV_WDOG_clear_timeout_irq() function clears the watchdog’s timeout + * interrupt which is connected to the MIV-RV32 interrupt. Calling + * MIV_WDOG_clear_timeout_irq() results in clearing the MIV-RV32 interrupt. + * Note: You must call the MIV_WDOG_clear_timeout_irq() function as part of your + * implementation of the interrupt handler in order to prevent the same + * interrupt event re-triggering a call to the timeout ISR. + * + * @param + * Void + * + * @return + * This function does not return any value. + * + */ +static inline void +MIV_WDOG_clear_timeout_irq +( + void +) +{ + if (0 != g_this_wdog) + { + HAL_set_32bit_reg_field(g_this_wdog, WDOGSTAT_WDOG_TRIPPED, 0x01u); + /* + * Perform a second write to ensure that the first write completed before + * returning from this function. This is to account for posted writes across + * the AHB matrix. The second write ensures that the first write has + * completed and that the interrupt line has been de-asserted by the time + * the function returns. Omitting the second write may result in a delay + * in the de-assertion of the interrupt line going to the RISC-V and a + * retriggering of the interrupt. + */ + HAL_set_32bit_reg_field(g_this_wdog, WDOGSTAT_WDOG_TRIPPED, 0x01u); + } +} + +/***************************************************************************//** + * The MIV_WDOG_clear_mvrp_irq() function clears the mvrp interrupt. + * + * Note: You must call the MIV_WDOG_clear_mvrp_irq() function as part of your + * implementation of the interrupt service routine (ISR) in order to + * prevent the same interrupt event re-triggering a call to the mvrp ISR. + * + * @param + * Void + * + * @return + * This function does not return a value. + */ +static inline void +MIV_WDOG_clear_mvrp_irq +( + void +) +{ + if (0 != g_this_wdog) + { + HAL_set_32bit_reg_field(g_this_wdog, WDOGSTAT_MSVP_TRIPPED, 0x01u); + /* + * Perform a second write to ensure that the first write completed before + * returning from this function. This is to account for posted writes across + * the AHB matrix. The second write ensures that the first write has + * completed and that the interrupt line has been de-asserted by the time + * the function returns. Omitting the second write may result in a delay + * in the de-assertion of the interrupt line going to the RISC-V and a + * re-triggering of the interrupt. + */ + HAL_set_32bit_reg_field(g_this_wdog, WDOGSTAT_MSVP_TRIPPED, 0x01u); + } +} + +/***************************************************************************//** + * The MIV_WDOG_timeout_occured() function reports the occurrence of a timeout + * event. + * + * @param + * Void + * + * @return + * A zero value indicates no watchdog timeout event occurred. A value of 1 + * indicates that a timeout event occurred. + */ +static inline uint32_t +MIV_WDOG_timeout_occured +( + void +) +{ + return (HAL_get_32bit_reg_field(g_this_wdog, WDOGSTAT_TRIGGERED)); +} + +/***************************************************************************//** + * The MIV_WDOG_force_reset() function is used to force an immediate reset + * if the watchdog has already triggered. Writing any value in this condition + * will result in watchdog timeout. + * The time out interrupt WDOG_IRQ will be set to high and watchdog timer + * countdown register updated with watchdog trigger timeout register value. + * If the Watchdog has timed out, a special 16-bit value needs to be written + * to the register to force a reset on CPU_RESETN, 0xDEAD + * Then the Watchdog countdown is reset/updated with the top Watchdog Runtime + * register value. + * + * @param + * Void + * + * @return + * This function does not return a value. + */ +static inline void +MIV_WDOG_force_reset +( + void +) +{ + if (WDOGSTAT_TRIGGERED_MASK == + HAL_get_32bit_reg_field(g_this_wdog, WDOGSTAT_TRIGGERED)) + + { + HAL_set_32bit_reg(g_this_wdog, WDOGFORCE, MIV_WDOG_FORCE_RESET_KEY); + } + + else + { + HAL_set_32bit_reg(g_this_wdog, WDOGFORCE, 0x0u); + } +} + +#ifdef __cplusplus +} +#endif + +#endif /* MIV_WATCHDOG_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_watchdog/miv_watchdog_regs.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_watchdog/miv_watchdog_regs.h new file mode 100644 index 0000000..2fca983 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/drivers/fpga_ip/miv_watchdog/miv_watchdog_regs.h @@ -0,0 +1,122 @@ + /******************************************************************************* + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * This file contains Register bit offsets and masks definitions for MI-V Soft + * IP watchdog module driver. This module is delivered as a part of Mi-V + * extended Sub-System(ESS) MIV_ESS. + */ + +#ifndef MIV_WDOG_REGISTERS +#define MIV_WDOG_REGISTERS 1u + +#ifdef __cplusplus +extern "C" { +#endif + +/*------------------------------------------------------------------------------ + * Refresh register details + */ +#define WDOGRFSH_REG_OFFSET 0x00u + +/* Refresh register bits */ +#define WDOGRFSH_OFFSET 0x00u +#define WDOGRFSH_MASK 0xFFFFFFFFu +#define WDOGRFSH_SHIFT 0u + +/*------------------------------------------------------------------------------ + * Control register details + */ +#define WDOGCNTL_REG_OFFSET 0x04u + +/* Control register next intent msvp bit */ +#define WDOGCNTL_NEXT_INTENT_MSVP_OFFSET 0x04u +#define WDOGCNTL_NEXT_INTENT_MSVP_MASK 0x01u +#define WDOGCNTL_NEXT_INTENT_MSVP_SHIFT 0u + +/* Control register next intent wdog bit */ +#define WDOGCNTL_NEXT_INTENT_WDOG_OFFSET 0x04u +#define WDOGCNTL_NEXT_INTENT_WDOG_MASK 0x02u +#define WDOGCNTL_NEXT_INTENT_WDOG_SHIFT 1u + +/* Control register next enforbidden bit */ +#define WDOGCNTL_NEXT_ENFORBIDDEN_OFFSET 0x04u +#define WDOGCNTL_NEXT_ENFORBIDDEN_MASK 0x10u +#define WDOGCNTL_NEXT_ENFORBIDDEN_SHIFT 4u + +/*------------------------------------------------------------------------------ + * Watchdog status register + */ +#define WDOGSTAT_REG_OFFSET 0x08u + +/* msvp_tripped bit */ +#define WDOGSTAT_MSVP_TRIPPED_OFFSET 0x08u +#define WDOGSTAT_MSVP_TRIPPED_MASK 0x01u +#define WDOGSTAT_MSVP_TRIPPED_SHIFT 0u + +/* WDOG Tripped bit */ +#define WDOGSTAT_WDOG_TRIPPED_OFFSET 0x08u +#define WDOGSTAT_WDOG_TRIPPED_MASK 0x02u +#define WDOGSTAT_WDOG_TRIPPED_SHIFT 1u + +/* Forbidden bit */ +#define WDOGSTAT_FORBIDDEN_OFFSET 0x08u +#define WDOGSTAT_FORBIDDEN_MASK 0x04u +#define WDOGSTAT_FORBIDDEN_SHIFT 2u + +/* Triggered bit */ +#define WDOGSTAT_TRIGGERED_OFFSET 0x08u +#define WDOGSTAT_TRIGGERED_MASK 0x08u +#define WDOGSTAT_TRIGGERED_SHIFT 3u + +/* wdoglocked bit */ +#define WDOGSTAT_WDOGLOCKED_OFFSET 0x08u +#define WDOGSTAT_WDOGLOCKED_MASK 0x10u +#define WDOGSTAT_WDOGLOCKED_SHIFT 4u + +/*------------------------------------------------------------------------------ + * Watchdog runtime register + */ +#define WDOGTIME_REG_OFFSET 0x0Cu + +/* wdogmsvp bit */ +#define WDOGTIME_WDOGVALUE_OFFSET 0x0Cu +#define WDOGTIME_WDOGVALUE_MASK 0xFFFFFFu +#define WDOGTIME_WDOGVALUE_SHIFT 0u + +/*------------------------------------------------------------------------------ + * Watchdog MVRP register + */ +#define WDOGMSVP_REG_OFFSET 0x10u + +/* wdogmsvp bit */ +#define WDOGMSVP_OFFSET 0x10u +#define WDOGMSVP_MASK 0xFFFFFFu +#define WDOGMSVP_SHIFT 0u + +/*------------------------------------------------------------------------------ + * Watchdog Trigger Timeout register + */ +#define WDOGTRIG_REG_OFFSET 0x14u + +/* wdogmsvp bit */ +#define WDOGTRIG_WDOGRST_OFFSET 0x14u +#define WDOGTRIG_WDOGRST_MASK 0xFFFFFFu +#define WDOGTRIG_WDOGRST_SHIFT 0u + +/*------------------------------------------------------------------------------ + * Watchdog Force Reset register details + */ +#define WDOGFORCE_REG_OFFSET 0x18u + +/* Refresh register bits */ +#define WDOGFORCE_OFFSET 0x18u +#define WDOGFORCE_MASK 0xFFFFFFFFu +#define WDOGFORCE_SHIFT 0u + +#ifdef __cplusplus +} +#endif + +#endif /* MIV_WATCHDOG_REGS_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/cpu_types.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/cpu_types.h new file mode 100644 index 0000000..be9d510 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/cpu_types.h @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright 2019-2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file cpu_types.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Type definitions which can be commonly used by the fabric-ip drivers. + * + */ +#ifndef __CPU_TYPES_H +#define __CPU_TYPES_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned int size_t; + +/*------------------------------------------------------------------------------ + * addr_t: address type. + * Used to specify the address of peripherals present in the processor's memory + * map. + */ +typedef unsigned int addr_t; + +/*------------------------------------------------------------------------------ + * psr_t: processor state register. + * Used by HAL_disable_interrupts() and HAL_restore_interrupts() to store the + * processor's state between disabling and restoring interrupts. + */ +typedef unsigned int psr_t; + +#ifdef __cplusplus +} +#endif + +#endif /* CPU_TYPES_H */ + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hal.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hal.h new file mode 100644 index 0000000..1476107 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hal.h @@ -0,0 +1,235 @@ +/***************************************************************************//** + * Copyright 2019-2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * @file hal.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Hardware abstraction layer functions for peripheral register accesses. + * + */ +#ifndef __HAL_H +#define __HAL_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include "cpu_types.h" +#include "hw_reg_access.h" +#include "hal_assert.h" +/***************************************************************************//** + * Enable all interrupts at the processor level. + */ +void HAL_enable_interrupts( void ); + +/***************************************************************************//** + * Disable all interrupts at the processor core level. + * Return the interrupts enable state before disabling occurred so that it can + * later be restored. + */ +psr_t HAL_disable_interrupts( void ); + +/***************************************************************************//** + * Restore the interrupts enable state at the processor core level. + * This function is normally passed the value returned from a previous call to + * HAL_disable_interrupts(). + */ +void HAL_restore_interrupts( psr_t saved_psr ); + +/***************************************************************************//** + */ +#define FIELD_OFFSET(FIELD_NAME) (FIELD_NAME##_OFFSET) +#define FIELD_SHIFT(FIELD_NAME) (FIELD_NAME##_SHIFT) +#define FIELD_MASK(FIELD_NAME) (FIELD_NAME##_MASK) + +/***************************************************************************//** + * The macro HAL_set_32bit_reg() allows writing a 32 bits wide register. + * + * BASE_ADDR: A variable of type addr_t specifying the base address of the + * peripheral containing the register. + * REG_NAME: A string identifying the register to write. These strings are + * specified in a header file associated with the peripheral. + * VALUE: A variable of type uint32_t containing the value to write. + */ +#define HAL_set_32bit_reg(BASE_ADDR, REG_NAME, VALUE) \ + (HW_set_32bit_reg( ((BASE_ADDR) + (REG_NAME##_REG_OFFSET)), (VALUE) )) + +/***************************************************************************//** + * The macro HAL_get_32bit_reg() is used to read the value of a 32 bits wide + * register. + * + * BASE_ADDR: A variable of type addr_t specifying the base address of the + * peripheral containing the register. + * REG_NAME: A string identifying the register to read. These strings are + * specified in a header file associated with the peripheral. + * RETURN: This function-like macro returns a uint32_t value. + */ +#define HAL_get_32bit_reg(BASE_ADDR, REG_NAME) \ + (HW_get_32bit_reg( ((BASE_ADDR) + (REG_NAME##_REG_OFFSET)) )) + +/***************************************************************************//** + * The macro HAL_set_32bit_reg_field() is used to write a field within a + * 32 bits wide register. The field written can be one or more bits. + * + * BASE_ADDR: A variable of type addr_t specifying the base address of the + * peripheral containing the register. + * FIELD_NAME: A string identifying the register field to write. These strings + * are specified in a header file associated with the peripheral. + * VALUE: A variable of type uint32_t containing the field value to write. + */ +#define HAL_set_32bit_reg_field(BASE_ADDR, FIELD_NAME, VALUE) \ + (HW_set_32bit_reg_field(\ + (BASE_ADDR) + FIELD_OFFSET(FIELD_NAME),\ + FIELD_SHIFT(FIELD_NAME),\ + FIELD_MASK(FIELD_NAME),\ + (VALUE))) + +/***************************************************************************//** + * The macro HAL_get_32bit_reg_field() is used to read a register field from + * within a 32 bit wide peripheral register. The field can be one or more bits. + * + * BASE_ADDR: A variable of type addr_t specifying the base address of the + * peripheral containing the register. + * FIELD_NAME: A string identifying the register field to write. These strings + * are specified in a header file associated with the peripheral. + * RETURN: This function-like macro returns a uint32_t value. + */ +#define HAL_get_32bit_reg_field(BASE_ADDR, FIELD_NAME) \ + (HW_get_32bit_reg_field(\ + (BASE_ADDR) + FIELD_OFFSET(FIELD_NAME),\ + FIELD_SHIFT(FIELD_NAME),\ + FIELD_MASK(FIELD_NAME))) + +/***************************************************************************//** + * The macro HAL_set_16bit_reg() allows writing a 16 bits wide register. + * + * BASE_ADDR: A variable of type addr_t specifying the base address of the + * peripheral containing the register. + * REG_NAME: A string identifying the register to write. These strings are + * specified in a header file associated with the peripheral. + * VALUE: A variable of type uint_fast16_t containing the value to write. + */ +#define HAL_set_16bit_reg(BASE_ADDR, REG_NAME, VALUE) \ + (HW_set_16bit_reg( ((BASE_ADDR) + (REG_NAME##_REG_OFFSET)), (VALUE) )) + +/***************************************************************************//** + * The macro HAL_get_16bit_reg() is used to read the value of a 16 bits wide + * register. + * + * BASE_ADDR: A variable of type addr_t specifying the base address of the + * peripheral containing the register. + * REG_NAME: A string identifying the register to read. These strings are + * specified in a header file associated with the peripheral. + * RETURN: This function-like macro returns a uint16_t value. + */ +#define HAL_get_16bit_reg(BASE_ADDR, REG_NAME) \ + (HW_get_16bit_reg( (BASE_ADDR) + (REG_NAME##_REG_OFFSET) )) + +/***************************************************************************//** + * The macro HAL_set_16bit_reg_field() is used to write a field within a + * 16 bits wide register. The field written can be one or more bits. + * + * BASE_ADDR: A variable of type addr_t specifying the base address of the + * peripheral containing the register. + * FIELD_NAME: A string identifying the register field to write. These strings + * are specified in a header file associated with the peripheral. + * VALUE: A variable of type uint16_t containing the field value to write. + */ +#define HAL_set_16bit_reg_field(BASE_ADDR, FIELD_NAME, VALUE) \ + (HW_set_16bit_reg_field(\ + (BASE_ADDR) + FIELD_OFFSET(FIELD_NAME),\ + FIELD_SHIFT(FIELD_NAME),\ + FIELD_MASK(FIELD_NAME),\ + (VALUE))) + +/***************************************************************************//** + * The macro HAL_get_16bit_reg_field() is used to read a register field from + * within a 8 bit wide peripheral register. The field can be one or more bits. + * + * BASE_ADDR: A variable of type addr_t specifying the base address of the + * peripheral containing the register. + * FIELD_NAME: A string identifying the register field to write. These strings + * are specified in a header file associated with the peripheral. + * RETURN: This function-like macro returns a uint16_t value. + */ +#define HAL_get_16bit_reg_field(BASE_ADDR, FIELD_NAME) \ + (HW_get_16bit_reg_field(\ + (BASE_ADDR) + FIELD_OFFSET(FIELD_NAME),\ + FIELD_SHIFT(FIELD_NAME),\ + FIELD_MASK(FIELD_NAME))) + +/***************************************************************************//** + * The macro HAL_set_8bit_reg() allows writing a 8 bits wide register. + * + * BASE_ADDR: A variable of type addr_t specifying the base address of the + * peripheral containing the register. + * REG_NAME: A string identifying the register to write. These strings are + * specified in a header file associated with the peripheral. + * VALUE: A variable of type uint_fast8_t containing the value to write. + */ +#define HAL_set_8bit_reg(BASE_ADDR, REG_NAME, VALUE) \ + (HW_set_8bit_reg( ((BASE_ADDR) + (REG_NAME##_REG_OFFSET)), (VALUE) )) + +/***************************************************************************//** + * The macro HAL_get_8bit_reg() is used to read the value of a 8 bits wide + * register. + * + * BASE_ADDR: A variable of type addr_t specifying the base address of the + * peripheral containing the register. + * REG_NAME: A string identifying the register to read. These strings are + * specified in a header file associated with the peripheral. + * RETURN: This function-like macro returns a uint8_t value. + */ +#define HAL_get_8bit_reg(BASE_ADDR, REG_NAME) \ + (HW_get_8bit_reg( (BASE_ADDR) + (REG_NAME##_REG_OFFSET) )) + +/***************************************************************************//** + */ +#define HAL_set_8bit_reg_field(BASE_ADDR, FIELD_NAME, VALUE) \ + (HW_set_8bit_reg_field(\ + (BASE_ADDR) + FIELD_OFFSET(FIELD_NAME),\ + FIELD_SHIFT(FIELD_NAME),\ + FIELD_MASK(FIELD_NAME),\ + (VALUE))) + +/***************************************************************************//** + * The macro HAL_get_8bit_reg_field() is used to read a register field from + * within a 8 bit wide peripheral register. The field can be one or more bits. + * + * BASE_ADDR: A variable of type addr_t specifying the base address of the + * peripheral containing the register. + * FIELD_NAME: A string identifying the register field to write. These strings + * are specified in a header file associated with the peripheral. + * RETURN: This function-like macro returns a uint8_t value. + */ +#define HAL_get_8bit_reg_field(BASE_ADDR, FIELD_NAME) \ + (HW_get_8bit_reg_field(\ + (BASE_ADDR) + FIELD_OFFSET(FIELD_NAME),\ + FIELD_SHIFT(FIELD_NAME),\ + FIELD_MASK(FIELD_NAME))) + +#ifdef __cplusplus +} +#endif + +#endif /*HAL_H*/ + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hal_assert.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hal_assert.h new file mode 100644 index 0000000..4bbb933 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hal_assert.h @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright 2019-2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file hal_assert.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief HAL assert functions + */ +#ifndef __HAL_ASSERT_HEADER +#define __HAL_ASSERT_HEADER 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Disable assertions if we do not recognize the compiler. */ +#if defined ( __GNUC__ ) +#if defined(NDEBUG) +/***************************************************************************//** + * HAL_ASSERT() is defined out when the NDEBUG symbol is used. + ******************************************************************************/ +#define HAL_ASSERT(CHECK) + +#else + +/***************************************************************************//** + * Default behavior for HAL_ASSERT() macro: + *------------------------------------------------------------------------------ + The behavior is toolchain specific and project setting specific. + ******************************************************************************/ +#define HAL_ASSERT(CHECK)\ + do { \ + if (!(CHECK)) \ + { \ + __asm__ volatile ("ebreak"); \ + }\ + } while(0); + +#endif /* NDEBUG */ +#endif /*__GNUC__*/ + +#ifdef __cplusplus +} +#endif +#endif /* __HAL_ASSERT_HEADER */ + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hal_irq.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hal_irq.c new file mode 100644 index 0000000..21cc6cf --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hal_irq.c @@ -0,0 +1,45 @@ +/***************************************************************************//** + * Copyright 2019-2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file hal_irq.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief Legacy interrupt control functions for the Microchip driver library + * hardware abstraction layer. + * + */ +#include "hal.h" +#include "miv_rv32_hal/miv_rv32_hal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*------------------------------------------------------------------------------ + * + */ +void HAL_enable_interrupts(void) { + MRV_enable_interrupts(); +} + +/*------------------------------------------------------------------------------ + * + */ +psr_t HAL_disable_interrupts(void) { + psr_t psr; + psr = read_csr(mstatus); + MRV_disable_interrupts(); + return(psr); +} + +/*------------------------------------------------------------------------------ + * + */ +void HAL_restore_interrupts(psr_t saved_psr) { + write_csr(mstatus, saved_psr); +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hw_macros.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hw_macros.h new file mode 100644 index 0000000..248ade0 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hw_macros.h @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright 2019-2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file hw_macros.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Hardware registers access macros. + * + * THE MACROS DEFINED IN THIS FILE ARE DEPRECATED. DO NOT USE FOR NEW + * DEVELOPMENT. + * + * These macros are used to access peripheral registers. They allow access to + * 8, 16 and 32 bit wide registers. All accesses to peripheral registers should + * be done through these macros in order to ease porting across different + * processors/bus architectures. + * + * Some of these macros also allow access to a specific register field. + * + */ +#ifndef __HW_REGISTER_MACROS_H +#define __HW_REGISTER_MACROS_H 1 +#ifdef __cplusplus +extern "C" { +#endif + +/*------------------------------------------------------------------------------ + * 32 bits registers access: + */ +#define HW_get_uint32_reg(BASE_ADDR, REG_OFFSET) (*((uint32_t volatile *)(BASE_ADDR + REG_OFFSET##_REG_OFFSET))) + +#define HW_set_uint32_reg(BASE_ADDR, REG_OFFSET, VALUE) (*((uint32_t volatile *)(BASE_ADDR + REG_OFFSET##_REG_OFFSET)) = (VALUE)) + +#define HW_set_uint32_reg_field(BASE_ADDR, FIELD, VALUE) \ + (*((uint32_t volatile *)(BASE_ADDR + FIELD##_OFFSET)) = \ + ( \ + (uint32_t) \ + ( \ + (*((uint32_t volatile *)(BASE_ADDR + FIELD##_OFFSET))) & ~FIELD##_MASK) | \ + (uint32_t)(((VALUE) << FIELD##_SHIFT) & FIELD##_MASK) \ + ) \ + ) + +#define HW_get_uint32_reg_field( BASE_ADDR, FIELD ) \ + (( (*((uint32_t volatile *)(BASE_ADDR + FIELD##_OFFSET))) & FIELD##_MASK) >> FIELD##_SHIFT) + +/*------------------------------------------------------------------------------ + * 32 bits memory access: + */ +#define HW_get_uint32(BASE_ADDR) (*((uint32_t volatile *)(BASE_ADDR))) + +#define HW_set_uint32(BASE_ADDR, VALUE) (*((uint32_t volatile *)(BASE_ADDR)) = (VALUE)) + +/*------------------------------------------------------------------------------ + * 16 bits registers access: + */ +#define HW_get_uint16_reg(BASE_ADDR, REG_OFFSET) (*((uint16_t volatile *)(BASE_ADDR + REG_OFFSET##_REG_OFFSET))) + +#define HW_set_uint16_reg(BASE_ADDR, REG_OFFSET, VALUE) (*((uint16_t volatile *)(BASE_ADDR + REG_OFFSET##_REG_OFFSET)) = (VALUE)) + +#define HW_set_uint16_reg_field(BASE_ADDR, FIELD, VALUE) \ + (*((uint16_t volatile *)(BASE_ADDR + FIELD##_OFFSET)) = \ + ( \ + (uint16_t) \ + ( \ + (*((uint16_t volatile *)(BASE_ADDR + FIELD##_OFFSET))) & ~FIELD##_MASK) | \ + (uint16_t)(((VALUE) << FIELD##_SHIFT) & FIELD##_MASK) \ + ) \ + ) + +#define HW_get_uint16_reg_field( BASE_ADDR, FIELD ) \ + (( (*((uint16_t volatile *)(BASE_ADDR + FIELD##_OFFSET))) & FIELD##_MASK) >> FIELD##_SHIFT) + +/*------------------------------------------------------------------------------ + * 8 bits registers access: + */ +#define HW_get_uint8_reg(BASE_ADDR, REG_OFFSET) (*((uint8_t volatile *)(BASE_ADDR + REG_OFFSET##_REG_OFFSET))) + +#define HW_set_uint8_reg(BASE_ADDR, REG_OFFSET, VALUE) (*((uint8_t volatile *)(BASE_ADDR + REG_OFFSET##_REG_OFFSET)) = (VALUE)) + +#define HW_set_uint8_reg_field(BASE_ADDR, FIELD, VALUE) \ + (*((uint8_t volatile *)(BASE_ADDR + FIELD##_OFFSET)) = \ + ( \ + (uint8_t) \ + ( \ + (*((uint8_t volatile *)(BASE_ADDR + FIELD##_OFFSET))) & ~FIELD##_MASK) | \ + (uint8_t)(((VALUE) << FIELD##_SHIFT) & FIELD##_MASK) \ + ) \ + ) + +#define HW_get_uint8_reg_field( BASE_ADDR, FIELD ) \ + (( (*((uint8_t volatile *)(BASE_ADDR + FIELD##_OFFSET))) & FIELD##_MASK) >> FIELD##_SHIFT) + +/*------------------------------------------------------------------------------ + * 8 bits memory access: + */ +#define HW_get_uint8(BASE_ADDR) (*((uint8_t volatile *)(BASE_ADDR))) + +#define HW_set_uint8(BASE_ADDR, VALUE) (*((uint8_t volatile *)(BASE_ADDR)) = (VALUE)) + +#ifdef __cplusplus +extern "C" { +#endif + +#endif /* __HW_REGISTER_MACROS_H */ + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hw_reg_access.S b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hw_reg_access.S new file mode 100644 index 0000000..41c7bbf --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hw_reg_access.S @@ -0,0 +1,215 @@ +/***************************************************************************//** + * Copyright 2019-2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file hw_reg_access.S + * @author Microchip FPGA Embedded Systems Solutions + * @brief Hardware registers access functions. + * The implementation of these function is platform and toolchain specific. + * The functions declared here are implemented using assembler as part of the + * processor/toolchain specific HAL. This implementation is for the combination + * of the 32 bit RISC-V processors and GNU tool chain. + * + */ + +.section .text + .globl HW_set_32bit_reg + .globl HW_get_32bit_reg + .globl HW_set_32bit_reg_field + .globl HW_get_32bit_reg_field + .globl HW_set_16bit_reg + .globl HW_get_16bit_reg + .globl HW_set_16bit_reg_field + .globl HW_get_16bit_reg_field + .globl HW_set_8bit_reg + .globl HW_get_8bit_reg + .globl HW_set_8bit_reg_field + .globl HW_get_8bit_reg_field + + +/***************************************************************************//** + * HW_set_32bit_reg is used to write the content of a 32 bits wide peripheral + * register. + * + * a0: addr_t reg_addr + * a1: uint32_t value + */ +HW_set_32bit_reg: + sw a1, 0(a0) + ret + +/***************************************************************************//** + * HW_get_32bit_reg is used to read the content of a 32 bits wide peripheral + * register. + * + * a0: addr_t reg_addr + + * @return 32 bits value read from the peripheral register. + */ +HW_get_32bit_reg: + lw a0, 0(a0) + ret + +/***************************************************************************//** + * HW_set_32bit_reg_field is used to set the content of a field in a 32 bits + * wide peripheral register. + * + * a0: addr_t reg_addr + * a1: int_fast8_t shift + * a2: uint32_t mask + * a3: uint32_t value + */ +HW_set_32bit_reg_field: + mv t3, a3 + sll t3, t3, a1 + and t3, t3, a2 + lw t1, 0(a0) + mv t2, a2 + not t2, t2 + and t1, t1, t2 + or t1, t1, t3 + sw t1, 0(a0) + ret + +/***************************************************************************//** + * HW_get_32bit_reg_field is used to read the content of a field out of a + * 32 bits wide peripheral register. + * + * a0: addr_t reg_addr + * a1: int_fast8_t shift + * a2: uint32_t mask + * + * @return 32 bits value containing the register field value specified + * as parameter. + */ +HW_get_32bit_reg_field: + lw a0, 0(a0) + and a0, a0, a2 + srl a0, a0, a1 + ret + +/***************************************************************************//** + * HW_set_16bit_reg is used to write the content of a 16 bits wide peripheral + * register. + * + * a0: addr_t reg_addr + * a1: uint_fast16_t value + */ +HW_set_16bit_reg: + sh a1, 0(a0) + ret + +/***************************************************************************//** + * HW_get_16bit_reg is used to read the content of a 16 bits wide peripheral + * register. + * + * a0: addr_t reg_addr + + * @return 16 bits value read from the peripheral register. + */ +HW_get_16bit_reg: + lh a0, (a0) + ret + +/***************************************************************************//** + * HW_set_16bit_reg_field is used to set the content of a field in a 16 bits + * wide peripheral register. + * + * a0: addr_t reg_addr + * a1: int_fast8_t shift + * a2: uint_fast16_t mask + * a3: uint_fast16_t value + * @param value Value to be written in the specified field. + */ +HW_set_16bit_reg_field: + mv t3, a3 + sll t3, t3, a1 + and t3, t3, a2 + lh t1, 0(a0) + mv t2, a2 + not t2, t2 + and t1, t1, t2 + or t1, t1, t3 + sh t1, 0(a0) + ret + +/***************************************************************************//** + * HW_get_16bit_reg_field is used to read the content of a field from a + * 16 bits wide peripheral register. + * + * a0: addr_t reg_addr + * a1: int_fast8_t shift + * a2: uint_fast16_t mask + * + * @return 16 bits value containing the register field value specified + * as parameter. + */ +HW_get_16bit_reg_field: + lh a0, 0(a0) + and a0, a0, a2 + srl a0, a0, a1 + ret + +/***************************************************************************//** + * HW_set_8bit_reg is used to write the content of a 8 bits wide peripheral + * register. + * + * a0: addr_t reg_addr + * a1: uint_fast8_t value + */ +HW_set_8bit_reg: + sb a1, 0(a0) + ret + +/***************************************************************************//** + * HW_get_8bit_reg is used to read the content of a 8 bits wide peripheral + * register. + * + * a0: addr_t reg_addr + + * @return 8 bits value read from the peripheral register. + */ +HW_get_8bit_reg: + lb a0, 0(a0) + ret + +/***************************************************************************//** + * HW_set_8bit_reg_field is used to set the content of a field in a 8 bits + * wide peripheral register. + * + * a0: addr_t reg_addr, + * a1: int_fast8_t shift + * a2: uint_fast8_t mask + * a3: uint_fast8_t value + */ +HW_set_8bit_reg_field: + mv t3, a3 + sll t3, t3, a1 + and t3, t3, a2 + lb t1, 0(a0) + mv t2, a2 + not t2, t2 + and t1, t1, t2 + or t1, t1, t3 + sb t1, 0(a0) + ret + +/***************************************************************************//** + * HW_get_8bit_reg_field is used to read the content of a field from a + * 8 bits wide peripheral register. + * + * a0: addr_t reg_addr + * a1: int_fast8_t shift + * a2: uint_fast8_t mask + * + * @return 8 bits value containing the register field value specified + * as parameter. + */ +HW_get_8bit_reg_field: + lb a0, 0(a0) + and a0, a0, a2 + srl a0, a0, a1 + ret + +.end diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hw_reg_access.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hw_reg_access.h new file mode 100644 index 0000000..97229c3 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/hal/hw_reg_access.h @@ -0,0 +1,239 @@ +/***************************************************************************//** + * Copyright 2019-2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file hw_reg_access.S + * @author Microchip FPGA Embedded Systems Solutions + * @brief Hardware registers access functions. + * The implementation of these function is platform and tool-chain specific. + * The functions declared here are implemented using assembler as part of the + * processor/tool-chain specific HAL. This implementation is for the combination + * of the 32 bit RISC-V processors and GNU tool chain. + * + */ +#ifndef __HW_REG_ACCESS +#define __HW_REG_ACCESS +#ifdef __cplusplus +extern "C" { +#endif + +#include "cpu_types.h" +/***************************************************************************//** + * HW_set_32bit_reg is used to write the content of a 32 bits wide peripheral + * register. + * + * @param reg_addr Address in the processor's memory map of the register to + * write. + * @param value Value to be written into the peripheral register. + */ +void +HW_set_32bit_reg +( + addr_t reg_addr, + uint32_t value +); + +/***************************************************************************//** + * HW_get_32bit_reg is used to read the content of a 32 bits wide peripheral + * register. + * + * @param reg_addr Address in the processor's memory map of the register to + * read. + * @return 32 bits value read from the peripheral register. + */ +uint32_t +HW_get_32bit_reg +( + addr_t reg_addr +); + +/***************************************************************************//** + * HW_set_32bit_reg_field is used to set the content of a field in a 32 bits + * wide peripheral register. + * + * @param reg_addr Address in the processor's memory map of the register to + * be written. + * @param shift Bit offset of the register field to be read within the + * register. + * @param mask Bit mask to be applied to the raw register value to filter + * out the other register fields values. + * @param value Value to be written in the specified field. + */ +void +HW_set_32bit_reg_field +( + addr_t reg_addr, + int_fast8_t shift, + uint32_t mask, + uint32_t value +); + +/***************************************************************************//** + * HW_get_32bit_reg_field is used to read the content of a field out of a + * 32 bits wide peripheral register. + * + * @param reg_addr Address in the processor's memory map of the register to + * read. + * @param shift Bit offset of the register field to be written within the + * register. + * @param mask Bit mask to be applied to the raw register value to filter + * out the other register fields values. + * + * @return 32 bits value containing the register field value specified + * as parameter. + */ +uint32_t +HW_get_32bit_reg_field +( + addr_t reg_addr, + int_fast8_t shift, + uint32_t mask +); + +/***************************************************************************//** + * HW_set_16bit_reg is used to write the content of a 16 bits wide peripheral + * register. + * + * @param reg_addr Address in the processor's memory map of the register to + * write. + * @param value Value to be written into the peripheral register. + */ +void +HW_set_16bit_reg +( + addr_t reg_addr, + uint_fast16_t value +); + +/***************************************************************************//** + * HW_get_16bit_reg is used to read the content of a 16 bits wide peripheral + * register. + * + * @param reg_addr Address in the processor's memory map of the register to + * read. + * @return 16 bits value read from the peripheral register. + */ +uint16_t +HW_get_16bit_reg +( + addr_t reg_addr +); + +/***************************************************************************//** + * HW_set_16bit_reg_field is used to set the content of a field in a 16 bits + * wide peripheral register. + * + * @param reg_addr Address in the processor's memory map of the register to + * be written. + * @param shift Bit offset of the register field to be read within the + * register. + * @param mask Bit mask to be applied to the raw register value to filter + * out the other register fields values. + * @param value Value to be written in the specified field. + */ +void HW_set_16bit_reg_field +( + addr_t reg_addr, + int_fast8_t shift, + uint_fast16_t mask, + uint_fast16_t value +); + +/***************************************************************************//** + * HW_get_16bit_reg_field is used to read the content of a field from a + * 16 bits wide peripheral register. + * + * @param reg_addr Address in the processor's memory map of the register to + * read. + * @param shift Bit offset of the register field to be written within the + * register. + * @param mask Bit mask to be applied to the raw register value to filter + * out the other register fields values. + * + * @return 16 bits value containing the register field value specified + * as parameter. + */ +uint16_t HW_get_16bit_reg_field +( + addr_t reg_addr, + int_fast8_t shift, + uint_fast16_t mask +); + +/***************************************************************************//** + * HW_set_8bit_reg is used to write the content of a 8 bits wide peripheral + * register. + * + * @param reg_addr Address in the processor's memory map of the register to + * write. + * @param value Value to be written into the peripheral register. + */ +void +HW_set_8bit_reg +( + addr_t reg_addr, + uint_fast8_t value +); + +/***************************************************************************//** + * HW_get_8bit_reg is used to read the content of a 8 bits wide peripheral + * register. + * + * @param reg_addr Address in the processor's memory map of the register to + * read. + * @return 8 bits value read from the peripheral register. + */ +uint8_t +HW_get_8bit_reg +( + addr_t reg_addr +); + +/***************************************************************************//** + * HW_set_8bit_reg_field is used to set the content of a field in a 8 bits + * wide peripheral register. + * + * @param reg_addr Address in the processor's memory map of the register to + * be written. + * @param shift Bit offset of the register field to be read within the + * register. + * @param mask Bit mask to be applied to the raw register value to filter + * out the other register fields values. + * @param value Value to be written in the specified field. + */ +void HW_set_8bit_reg_field +( + addr_t reg_addr, + int_fast8_t shift, + uint_fast8_t mask, + uint_fast8_t value +); + +/***************************************************************************//** + * HW_get_8bit_reg_field is used to read the content of a field from a + * 8 bits wide peripheral register. + * + * @param reg_addr Address in the processor's memory map of the register to + * read. + * @param shift Bit offset of the register field to be written within the + * register. + * @param mask Bit mask to be applied to the raw register value to filter + * out the other register fields values. + * + * @return 8 bits value containing the register field value specified + * as parameter. + */ +uint8_t HW_get_8bit_reg_field +( + addr_t reg_addr, + int_fast8_t shift, + uint_fast8_t mask +); + +#ifdef __cplusplus +} +#endif + +#endif /* __HW_REG_ACCESS */ + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv-rv32-execute-in-place.ld b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv-rv32-execute-in-place.ld new file mode 100644 index 0000000..474eb43 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv-rv32-execute-in-place.ld @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * file name : miv-rv32-execute-in-place.ld + * Mi-V soft processor linker script for creating a SoftConsole downloadable + * image executing from a one address space whereas the data, sdata and stack + * sections are placed in another address space. This could be used in cases such + * as: + * 1) When using MIV_RV32, the reset vector points to the LSRAM at address + * 0x80000000 and the data, sdata, bss and stack sections are placed in the + * TCM region. + * + * 2) Executing from a Non Volatile memory. The actual memory will depend on + * the FPGA platform. For exameple, it could be the eNVM on SmartFusion2, + * Igloo2 or on-board non-volatile memory which supports code execution. + * + * NOTE: Modify the memory section addresses and the sizes according to your + * Libero design. + * + * To know more about the memory map of the MIV_RV32 based Libero design, open + * the MIV_RV32 IP configurator and look for "Reset Vector Address" and the + * "Memory Map" tab. + * + */ + +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + + +MEMORY +{ + rom (rx) : ORIGIN = 0x80000000, LENGTH = 16k + ram (rwx) : ORIGIN = 0x80004000, LENGTH = 16k +} + +STACK_SIZE = 1k; /* needs to be calculated for your application */ +HEAP_SIZE = 1k; /* needs to be calculated for your application */ + +SECTIONS +{ + .entry : ALIGN(0x10) + { + KEEP (*(SORT_NONE(.entry))) + . = ALIGN(0x10); + } > rom + + .text : ALIGN(0x10) + { + KEEP (*(SORT_NONE(.text.entry))) + . = ALIGN(0x10); + *(.text .text.* .gnu.linkonce.t.*) + *(.plt) + . = ALIGN(0x10); + + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*crtend.o(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*crtend.o(.dtors)) + + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.gcc_except_table) + *(.eh_frame_hdr) + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + . = ALIGN(0x10); + + } >rom + + /* short/global data section */ + .sdata : ALIGN(0x10) + { + __sdata_load = LOADADDR(.sdata); + __sdata_start = .; + PROVIDE( __global_pointer$ = . + 0x800); + *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) + *(.srodata*) + *(.sdata .sdata.* .gnu.linkonce.s.*) + . = ALIGN(0x10); + __sdata_end = .; + } >ram AT>rom + + /* data section */ + .data : ALIGN(0x10) + { + __data_load = LOADADDR(.data); + __data_start = .; + *(.got.plt) *(.got) + *(.shdata) + *(.data .data.* .gnu.linkonce.d.*) + . = ALIGN(0x10); + __data_end = .; + } >ram AT>rom + + /* sbss section */ + .sbss : ALIGN(0x10) + { + __sbss_start = .; + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + . = ALIGN(0x10); + __sbss_end = .; + } > ram + + /* sbss section */ + .bss : ALIGN(0x10) + { + __bss_start = .; + *(.shbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(0x10); + __bss_end = .; + } > ram + + /* End of uninitialized data segment */ + _end = .; + + .heap : ALIGN(0x10) + { + __heap_start = .; + . += HEAP_SIZE; + __heap_end = .; + . = ALIGN(0x10); + _heap_end = __heap_end; + } > ram + + .stack : ALIGN(0x10) + { + __stack_bottom = .; + . += STACK_SIZE; + __stack_top = .; + } > ram +} + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv-rv32-ram.ld b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv-rv32-ram.ld new file mode 100644 index 0000000..53076a0 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv-rv32-ram.ld @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * file name : miv-rv32-ram.ld + * Mi-V soft processor linker script for creating a SoftConsole downloadable + * debug image executing in SRAM. + * + * This linker script assumes that a RAM is connected at on Mi-V soft processor + * memory space pointed by the reset vector address. + * + * NOTE : Modify the memory section address and the size according to your + * Libero design. + * For example: + * 1) If you want to download and step debug at a different RAM memory address in + * your design (For example TCM base address) than the one provided in this file. + * 2) The MIV_RV32, when used with MIV_ESS IP, provides ways to copy the executable + * HEX file from external Non-Volatile memory into the TCM at reset. In this + * case your executable must be linked to the TCM address. + * + * To know more about the memory map of the MIV_RV32 based Libero design, open + * the MIV_RV32 IP configurator and look for "Reset Vector Address" and the + * "Memory Map" tab. + * + */ + +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +MEMORY +{ + ram (rwx) : ORIGIN = 0x80000000, LENGTH = 32k +} + +STACK_SIZE = 2k; /* needs to be calculated for your application */ +HEAP_SIZE = 1k; /* needs to be calculated for your application */ + +SECTIONS +{ + .entry : ALIGN(0x10) + { + KEEP (*(SORT_NONE(.entry))) + . = ALIGN(0x10); + } > ram + + .text : ALIGN(0x10) + { + *(.text .text.* .gnu.linkonce.t.*) + *(.plt) + . = ALIGN(0x10); + + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*crtend.o(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*crtend.o(.dtors)) + + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.gcc_except_table) + *(.eh_frame_hdr) + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + . = ALIGN(0x10); + + } > ram + + /* short/global data section */ + .sdata : ALIGN(0x10) + { + __sdata_load = LOADADDR(.sdata); + __sdata_start = .; + PROVIDE( __global_pointer$ = . + 0x800); + *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) + *(.srodata*) + *(.sdata .sdata.* .gnu.linkonce.s.*) + . = ALIGN(0x10); + __sdata_end = .; + } > ram + + /* data section */ + .data : ALIGN(0x10) + { + __data_load = LOADADDR(.data); + __data_start = .; + *(.got.plt) *(.got) + *(.shdata) + *(.data .data.* .gnu.linkonce.d.*) + . = ALIGN(0x10); + __data_end = .; + } > ram + + /* sbss section */ + .sbss : ALIGN(0x10) + { + __sbss_start = .; + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + . = ALIGN(0x10); + __sbss_end = .; + } > ram + + /* sbss section */ + .bss : ALIGN(0x10) + { + __bss_start = .; + *(.shbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(0x10); + __bss_end = .; + } > ram + + /* End of uninitialized data segment */ + _end = .; + + .heap : ALIGN(0x10) + { + __heap_start = .; + . += HEAP_SIZE; + __heap_end = .; + . = ALIGN(0x10); + _heap_end = __heap_end; + } > ram + + .stack : ALIGN(0x10) + { + __stack_bottom = .; + . += STACK_SIZE; + __stack_top = .; + } > ram +} + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_assert.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_assert.h new file mode 100644 index 0000000..b3912ed --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_assert.h @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * MIV_RV32 HAL Embedded Software + * + */ +#ifndef MIV_RV32_ASSERT_HEADER +#define MIV_RV32_ASSERT_HEADER + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * ASSERT() implementation. + ******************************************************************************/ +/* Disable assertions if we do not recognize the compiler. */ +#if defined ( __GNUC__ ) +#if defined(NDEBUG) +#define ASSERT(CHECK) +#else +#define ASSERT(CHECK)\ + do { \ + if (!(CHECK)) \ + { \ + __asm__ volatile ("ebreak"); \ + }\ + } while(0); + +#endif /* NDEBUG check */ +#endif /* compiler check */ + +#ifdef __cplusplus +} +#endif + +#endif /* MIV_RV32_ASSERT_HEADER */ + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_entry.S b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_entry.S new file mode 100644 index 0000000..22ced32 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_entry.S @@ -0,0 +1,589 @@ +/******************************************************************************* + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file miv_rv32_entry.S + * @author Microchip FPGA Embedded Systems Solutions + * @brief Mi-V soft processor vectors, trap handling and startup code. + * + */ +#ifndef ENTRY_S +#define ENTRY_S + +#define A_EXTENSION_MASK 0x00000001u +#define MTVEC_MODE_BIT_MASK 0x00000003u +#define MTVEC_VECTORED_MODE_VAL 0x00000001u + +#define MTIMEH_ADDR 0x200BFFCu + + +#if __riscv_xlen == 64 +# define LREG ld +# define SREG sd +# define REGBYTES 8 +#else +# define LREG lw +# define SREG sw +# define REGBYTES 4 +#endif + +#if defined(MIV_FP_CONTEXT_SAVE) && defined(__riscv_flen) +#define SP_SHIFT_OFFSET 63 +#else +#define SP_SHIFT_OFFSET 32 +#endif + +.macro STORE_CONTEXT + addi sp, sp, -SP_SHIFT_OFFSET*REGBYTES + SREG x1, 0 * REGBYTES(sp) + SREG x2, 1 * REGBYTES(sp) + SREG x3, 2 * REGBYTES(sp) + SREG x4, 3 * REGBYTES(sp) + SREG x5, 4 * REGBYTES(sp) + SREG x6, 5 * REGBYTES(sp) + SREG x7, 6 * REGBYTES(sp) + SREG x8, 7 * REGBYTES(sp) + SREG x9, 8 * REGBYTES(sp) + SREG x10, 9 * REGBYTES(sp) + SREG x11, 10 * REGBYTES(sp) + SREG x12, 11 * REGBYTES(sp) + SREG x13, 12 * REGBYTES(sp) + SREG x14, 13 * REGBYTES(sp) + SREG x15, 14 * REGBYTES(sp) + SREG x16, 15 * REGBYTES(sp) + SREG x17, 16 * REGBYTES(sp) + SREG x18, 17 * REGBYTES(sp) + SREG x19, 18 * REGBYTES(sp) + SREG x20, 19 * REGBYTES(sp) + SREG x21, 20 * REGBYTES(sp) + SREG x22, 21 * REGBYTES(sp) + SREG x23, 22 * REGBYTES(sp) + SREG x24, 23 * REGBYTES(sp) + SREG x25, 24 * REGBYTES(sp) + SREG x26, 25 * REGBYTES(sp) + SREG x27, 26 * REGBYTES(sp) + SREG x28, 27 * REGBYTES(sp) + SREG x29, 28 * REGBYTES(sp) + SREG x30, 29 * REGBYTES(sp) + SREG x31, 30 * REGBYTES(sp) + + #ifdef __riscv_flen + #ifdef MIV_FP_CONTEXT_SAVE + fsw f0, 31*REGBYTES(sp) + fsw f1, 32*REGBYTES(sp) + fsw f2, 33*REGBYTES(sp) + fsw f3, 34*REGBYTES(sp) + fsw f4, 35*REGBYTES(sp) + fsw f5, 36*REGBYTES(sp) + fsw f6, 37*REGBYTES(sp) + fsw f7, 38*REGBYTES(sp) + fsw f8, 39*REGBYTES(sp) + fsw f9, 40*REGBYTES(sp) + fsw f10, 41*REGBYTES(sp) + fsw f11, 42*REGBYTES(sp) + fsw f12, 43*REGBYTES(sp) + fsw f13, 44*REGBYTES(sp) + fsw f14, 45*REGBYTES(sp) + fsw f15, 46*REGBYTES(sp) + fsw f16, 47*REGBYTES(sp) + fsw f17, 48*REGBYTES(sp) + fsw f18, 49*REGBYTES(sp) + fsw f19, 50*REGBYTES(sp) + fsw f20, 51*REGBYTES(sp) + fsw f21, 52*REGBYTES(sp) + fsw f22, 53*REGBYTES(sp) + fsw f23, 54*REGBYTES(sp) + fsw f24, 55*REGBYTES(sp) + fsw f25, 56*REGBYTES(sp) + fsw f26, 57*REGBYTES(sp) + fsw f27, 58*REGBYTES(sp) + fsw f28, 59*REGBYTES(sp) + fsw f29, 60*REGBYTES(sp) + fsw f30, 61*REGBYTES(sp) + fsw f31, 62*REGBYTES(sp) + #endif /* __riscv_flen */ + #endif /* MIV_FP_CONTEXT_SAVE */ +.endm + + .section .entry, "ax" + .globl _start + +_start: + j handle_reset + +/* Some of the Mi-V soft IP cores support compressed 'C' extension. If the Mi-V + core in your design doesn't support 'C' extension and you enable 'C' extension + in firmware project compiler options, then it would result in a trap. For this + case, we are avoiding compressed instruction here so you can put a breakpoint + at the jump and you can at least look at mcause, mepc and get some hints + about the crash. */ +trap_entry: +.option push +.option norvc +j generic_trap_handler +.option pop + .word 0 + .word 0 + +sw_trap_entry: + j vector_sw_trap_handler +#ifdef __riscv_compressed + .2byte 0 +#endif + .word 0 + .word 0 + .word 0 + +tmr_trap_entry: + j vector_tmr_trap_handler +#ifdef __riscv_compressed + .2byte 0 +#endif + .word 0 + .word 0 + .word 0 + +ext_trap_entry: + j vector_ext_trap_handler +#ifdef __riscv_compressed + .2byte 0 +#endif + .word 0 + .word 0 + .word 0 + .word 0 + +#ifndef MIV_LEGACY_RV32 +MGEUI_trap_entry: + j vector_MGEUI_trap_handler +#ifdef __riscv_compressed + .2byte 0 +#endif + +MGECI_trap_entry: + j vector_MGECI_trap_handler +#ifdef __riscv_compressed + .2byte 0 +#endif + .word 0 + .word 0 + .word 0 + .word 0 + +#ifndef MIV_RV32_V3_0 +MSYS_MIE22_trap_entry: +#ifndef MIV_RV32_V3_0 + j vector_SUBSYSR_IRQHandler +#endif /*MIV_RV32_V3_0*/ +#ifdef __riscv_compressed + .2byte 0 +#endif + +MSYS_MIE23_trap_entry: + j vector_SUBSYS_IRQHandler +#ifdef __riscv_compressed + .2byte 0 +#endif +#endif /*MIV_RV32_V3_0*/ + +MSYS_MIE24_trap_entry: + j vector_MSYS_EI0_trap_handler +#ifdef __riscv_compressed + .2byte 0 +#endif + +MSYS_MIE25_trap_entry: + j vector_MSYS_EI1_trap_handler +#ifdef __riscv_compressed + .2byte 0 +#endif + +MSYS_MIE26_trap_entry: + j vector_MSYS_EI2_trap_handler +#ifdef __riscv_compressed + .2byte 0 +#endif + +MSYS_MIE27_trap_entry: + j vector_MSYS_EI3_trap_handler +#ifdef __riscv_compressed + .2byte 0 +#endif + +MSYS_MIE28_trap_entry: + j vector_MSYS_EI4_trap_handler +#ifdef __riscv_compressed + .2byte 0 +#endif + +MSYS_MIE29_trap_entry: + j vector_MSYS_EI5_trap_handler +#ifdef __riscv_compressed + .2byte 0 +#endif + +MSYS_MIE30_trap_entry: +#ifndef MIV_RV32_V3_0 + j vector_MSYS_EI6_trap_handler +#else + j vector_SUBSYS_IRQHandler +#endif +#ifdef __riscv_compressed + .2byte 0 +#endif + +#ifndef MIV_RV32_V3_0 +MSYS_MIE31_trap_entry: + j vector_MSYS_EI7_trap_handler +#ifdef __riscv_compressed + .2byte 0 +#endif +#endif /* MIV_RV32_V3_0 */ +#endif /* MIV_LEGACY_RV32 */ + +.align 4 +generic_trap_handler: + STORE_CONTEXT + csrr a0, mcause + csrr a1, mepc + jal handle_trap + j generic_restore + +vector_sw_trap_handler: + STORE_CONTEXT + jal handle_m_soft_interrupt + j generic_restore + +vector_tmr_trap_handler: + STORE_CONTEXT + jal handle_m_timer_interrupt + j generic_restore + +vector_ext_trap_handler: + STORE_CONTEXT +#ifdef MIV_LEGACY_RV32 + jal handle_m_ext_interrupt +#else + jal External_IRQHandler +#endif /* MIV_LEGACY_RV32 */ + j generic_restore + +#ifndef MIV_LEGACY_RV32 +vector_MGEUI_trap_handler: + STORE_CONTEXT + jal MGEUI_IRQHandler + j generic_restore + +vector_MGECI_trap_handler: + STORE_CONTEXT + jal MGECI_IRQHandler + j generic_restore + +vector_MSYS_EI0_trap_handler: + STORE_CONTEXT + jal MSYS_EI0_IRQHandler + j generic_restore + +vector_MSYS_EI1_trap_handler: + STORE_CONTEXT + jal MSYS_EI1_IRQHandler + j generic_restore + +vector_MSYS_EI2_trap_handler: + STORE_CONTEXT + jal MSYS_EI2_IRQHandler + j generic_restore + +vector_MSYS_EI3_trap_handler: + STORE_CONTEXT + jal MSYS_EI3_IRQHandler + j generic_restore + +vector_MSYS_EI4_trap_handler: + STORE_CONTEXT + jal MSYS_EI4_IRQHandler + j generic_restore + +vector_MSYS_EI5_trap_handler: + STORE_CONTEXT + jal MSYS_EI5_IRQHandler + j generic_restore + +vector_SUBSYS_IRQHandler: + STORE_CONTEXT + jal SUBSYS_IRQHandler + j generic_restore + +#ifndef MIV_RV32_V3_0 +vector_MSYS_EI6_trap_handler: + STORE_CONTEXT + jal MSYS_EI6_IRQHandler + j generic_restore + +vector_MSYS_EI7_trap_handler: + STORE_CONTEXT + jal MSYS_EI7_IRQHandler + j generic_restore + + +vector_SUBSYSR_IRQHandler: + STORE_CONTEXT + jal SUBSYSR_IRQHandler + j generic_restore + +#endif /*MIV_RV32_V3_0*/ +#endif /* MIV_LEGACY_RV32 */ + +generic_restore: + LREG x1, 0 * REGBYTES(sp) + LREG x2, 1 * REGBYTES(sp) + LREG x3, 2 * REGBYTES(sp) + LREG x4, 3 * REGBYTES(sp) + LREG x5, 4 * REGBYTES(sp) + LREG x6, 5 * REGBYTES(sp) + LREG x7, 6 * REGBYTES(sp) + LREG x8, 7 * REGBYTES(sp) + LREG x9, 8 * REGBYTES(sp) + LREG x10, 9 * REGBYTES(sp) + LREG x11, 10 * REGBYTES(sp) + LREG x12, 11 * REGBYTES(sp) + LREG x13, 12 * REGBYTES(sp) + LREG x14, 13 * REGBYTES(sp) + LREG x15, 14 * REGBYTES(sp) + LREG x16, 15 * REGBYTES(sp) + LREG x17, 16 * REGBYTES(sp) + LREG x18, 17 * REGBYTES(sp) + LREG x19, 18 * REGBYTES(sp) + LREG x20, 19 * REGBYTES(sp) + LREG x21, 20 * REGBYTES(sp) + LREG x22, 21 * REGBYTES(sp) + LREG x23, 22 * REGBYTES(sp) + LREG x24, 23 * REGBYTES(sp) + LREG x25, 24 * REGBYTES(sp) + LREG x26, 25 * REGBYTES(sp) + LREG x27, 26 * REGBYTES(sp) + LREG x28, 27 * REGBYTES(sp) + LREG x29, 28 * REGBYTES(sp) + LREG x30, 29 * REGBYTES(sp) + LREG x31, 30 * REGBYTES(sp) + + #ifdef __riscv_flen + #ifdef MIV_FP_CONTEXT_SAVE + flw f0, 31*REGBYTES(sp) + flw f1, 32*REGBYTES(sp) + flw f2, 33*REGBYTES(sp) + flw f3, 34*REGBYTES(sp) + flw f4, 35*REGBYTES(sp) + flw f5, 36*REGBYTES(sp) + flw f6, 37*REGBYTES(sp) + flw f7, 38*REGBYTES(sp) + flw f8, 39*REGBYTES(sp) + flw f9, 40*REGBYTES(sp) + flw f10, 41*REGBYTES(sp) + flw f11, 42*REGBYTES(sp) + flw f12, 43*REGBYTES(sp) + flw f13, 44*REGBYTES(sp) + flw f14, 45*REGBYTES(sp) + flw f15, 46*REGBYTES(sp) + flw f16, 47*REGBYTES(sp) + flw f17, 48*REGBYTES(sp) + flw f18, 49*REGBYTES(sp) + flw f19, 50*REGBYTES(sp) + flw f20, 51*REGBYTES(sp) + flw f21, 52*REGBYTES(sp) + flw f22, 53*REGBYTES(sp) + flw f23, 54*REGBYTES(sp) + flw f24, 55*REGBYTES(sp) + flw f25, 56*REGBYTES(sp) + flw f26, 57*REGBYTES(sp) + flw f27, 58*REGBYTES(sp) + flw f28, 59*REGBYTES(sp) + flw f29, 60*REGBYTES(sp) + flw f30, 61*REGBYTES(sp) + flw f31, 62*REGBYTES(sp) + #endif /* __riscv_flen */ + #endif /* MIV_FP_CONTEXT_SAVE */ + + addi sp, sp, SP_SHIFT_OFFSET*REGBYTES + mret + + .section .text, "ax" +handle_reset: +/* Ensure instructions are not relaxed, since gp is not yet set */ +.option push +.option norelax + +#ifndef MIV_RV32_V3_0 + csrwi mstatus, 0 + csrwi mie, 0 + la ra, _start + +/* Clearnig this to be on safer side as RTL doesnt seem to clear it on reset. */ +#ifndef MIV_LEGACY_RV32 + li t0, MTIMEH_ADDR + sw x0, 0(t0) +#endif + + csrr t0, misa + andi t0, t0, A_EXTENSION_MASK + bnez t0, ima_cores_setup /* Jump to IMA core handling */ + + +/* For MIV_RV32 cores the mtvec exception base address is fixed at Reset vector + address + 0x4. Check the mode bits. */ +/* In the MIV_RV32 v3.1, the MTVEC exception base address is WARL, and can be + configured by the user at runtime */ + + csrr t0, mtvec + andi t0, t0, MTVEC_MODE_BIT_MASK + li t1, MTVEC_VECTORED_MODE_VAL + bne t0, t1, ima_cores_setup /* Jump to IMA core handling */ + + /* When mode = 1 => this is vectored mode on MIV_RV32 core. + Verify that the trap_handler address matches the configuration in MTVEC */ + csrr t0, mtvec + andi t0, t0, 0xFFFFFFFC + la t1, trap_entry + bne t0, t1, vector_address_not_matching + j generic_reset_handling + +#else /* MIV_RV32_V3_0 */ + +/* Clearnig this to be on safer side as RTL doesnt seem to clear it on reset. */ +#ifndef MIV_LEGACY_RV32 + li t0, MTIMEH_ADDR + sw x0, 0(t0) +#endif + +/* In the MIV_RV32 v3.1, the MTVEC exception base address is WARL, and can be + configured by the user at runtime */ + csrr t0, mtvec + andi t0, t0, MTVEC_MODE_BIT_MASK + li t1, MTVEC_VECTORED_MODE_VAL + bne t0, t1, ima_cores_setup /* Jump to IMA core handling */ + +/* When mode = 1 => this is vectored mode on MIV_RV32 core. + Verify that the trap_handler address matches the configuration in MTVEC */ + csrr t0, mtvec + andi t0, t0, 0xFFFFFFFC + la t1, trap_entry + bne t0, t1, vector_address_not_matching + j generic_reset_handling +#endif /*MIV_RV32_V3_0*/ + +ima_cores_setup: + la t0, trap_entry + +#ifdef MIV_LEGACY_RV32_VECTORED_INTERRUPTS + addi t0, t0, 0x01 /* Set the mode bit for IMA cores. + For both MIV_RV32 v3.1 and v3.0 cores this is done by configurator. */ +#endif + csrw mtvec, t0 + +generic_reset_handling: +/* Copy sdata section first so that the gp is set and linker relaxation can be + used */ + la a4, __sdata_load + la a5, __sdata_start + la a6, __sdata_end + beq a4, a5, 1f /* Exit if source and dest are same */ + beq a5, a6, 1f /* Exit if section start and end addresses are same */ + call block_copy + +1: + /* initialize global pointer */ + la gp, __global_pointer$ + +.option pop + +/* Floating point support configuration */ +#ifdef __riscv_flen + csrr t0, mstatus + lui t1, 0xffffa + addi t1, t1, -1 + and t0, t0, t1 + lui t1, 0x4 + or t1, t0, t1 + csrw mstatus, t1 + + lui t0, 0x0 + fscsr t0 +#endif + call initializations + /* Initialize stack pointer */ + la sp, __stack_top + + /* Jump into C code */ + j _init + +/* Error: trap_entry is not at the expected address of reset_vector+mtvec offset + as configured in the MIV_RV32 core vectored mode */ +vector_address_not_matching: + ebreak + +initializations: +/* Initialize the .bss section */ + mv t0, ra /* Store ra for future use */ + la a5, __bss_start + la a6, __bss_end + beq a5, a6, 1f /* Section start and end address are the same */ + call zeroize_block + +1: +/* Initialize the .sbss section */ + la a5, __sbss_start + la a6, __sbss_end + beq a5, a6, 1f /* Section start and end address are the same */ + call zeroize_block + +/* Clear heap */ + la a5, __heap_start + la a6, __heap_end + beq a5, a6, 1f /* Section start and end address are the same */ + call zeroize_block + +1: +/* Copy data section */ + la a4, __data_load + la a5, __data_start + la a6, __data_end + beq a4, a5, 1f /* Exit early if source and dest are same */ + beq a5, a6, 1f /* Section start and end addresses are the same */ + call block_copy + +1: + mv ra, t0 /* Retrieve ra */ + ret + +zeroize_block: + bltu a6, a5, block_copy_error /* Error. End address is less than start */ + or a7, a6, a5 /* Check if start or end is unalined */ + andi a7, a7, 0x03u + bgtz a7, block_copy_error /* Unaligned addresses error*/ +zeroize_loop: + sw x0, 0(a5) + add a5, a5, __SIZEOF_POINTER__ + blt a5, a6, zeroize_loop + ret + +block_copy: + bltu a6, a5, block_copy_error /* Error. End address is less than start */ + or a7, a6, a5 /* Check if start or end is unalined */ + andi a7, a7, 0x03u + bgtz a7, block_copy_error /* Unaligned addresses error*/ +block_copy_loop: + lw a7, 0(a4) + sw a7, 0(a5) + addi a5, a5, 0x04 + addi a4, a4, 0x04 + blt a5, a6, block_copy_loop + j block_copy_exit + +block_copy_error: + j block_copy_error + +block_copy_exit: + ret + +#endif /*ENTRY_S*/ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_hal.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_hal.c new file mode 100644 index 0000000..a112821 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_hal.c @@ -0,0 +1,410 @@ +/******************************************************************************* + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file miv_rv32_hal.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief Implementation of Hardware Abstraction Layer for Mi-V soft processors + * + */ +#include +#include "miv_rv32_hal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SUCCESS 0U +#define ERROR 1U +#define MASK_32BIT 0xFFFFFFFFu + +/*------------------------------------------------------------------------------ + * Write in a sequence recommended by privileged spec to avoid spurious + * interrupts + + # New comparand is in a1:a0. + li t0, -1 + sw t0, mtimecmp # No smaller than old value. + sw a1, mtimecmp+4 # No smaller than new value. + sw a0, mtimecmp # New value. + */ +#ifndef MIV_RV32_EXT_TIMECMP +#define WRITE_MTIMECMP(value) MTIMECMPH = MASK_32BIT; \ + MTIMECMP = value & MASK_32BIT;\ + MTIMECMPH = (value >> 32u) & MASK_32BIT; +#else +#define WRITE_MTIMECMP(value) +#endif + +#ifndef MIV_RV32_EXT_TIMER +#define WRITE_MTIME(value) MTIME = value & MASK_32BIT;\ + MTIMEH = (value >> 32u) & MASK_32BIT; +#else +#define WRITE_MTIME(value) +#endif + +extern void Software_IRQHandler(void); + +#ifdef MIV_LEGACY_RV32 +#define MTIME_PRESCALER 100UL +/*------------------------------------------------------------------------------ + * + */ +uint8_t Invalid_IRQHandler(void); +uint8_t External_1_IRQHandler(void); +uint8_t External_2_IRQHandler(void); +uint8_t External_3_IRQHandler(void); +uint8_t External_4_IRQHandler(void); +uint8_t External_5_IRQHandler(void); +uint8_t External_6_IRQHandler(void); +uint8_t External_7_IRQHandler(void); +uint8_t External_8_IRQHandler(void); +uint8_t External_9_IRQHandler(void); +uint8_t External_10_IRQHandler(void); +uint8_t External_11_IRQHandler(void); +uint8_t External_12_IRQHandler(void); +uint8_t External_13_IRQHandler(void); +uint8_t External_14_IRQHandler(void); +uint8_t External_15_IRQHandler(void); +uint8_t External_16_IRQHandler(void); +uint8_t External_17_IRQHandler(void); +uint8_t External_18_IRQHandler(void); +uint8_t External_19_IRQHandler(void); +uint8_t External_20_IRQHandler(void); +uint8_t External_21_IRQHandler(void); +uint8_t External_22_IRQHandler(void); +uint8_t External_23_IRQHandler(void); +uint8_t External_24_IRQHandler(void); +uint8_t External_25_IRQHandler(void); +uint8_t External_26_IRQHandler(void); +uint8_t External_27_IRQHandler(void); +uint8_t External_28_IRQHandler(void); +uint8_t External_29_IRQHandler(void); +uint8_t External_30_IRQHandler(void); +uint8_t External_31_IRQHandler(void); + + +/*------------------------------------------------------------------------------ + * RISC-V interrupt handler for external interrupts. + */ +uint8_t (* const mrv_ext_irq_handler_table[32])(void) = +{ + + Invalid_IRQHandler, + External_1_IRQHandler, + External_2_IRQHandler, + External_3_IRQHandler, + External_4_IRQHandler, + External_5_IRQHandler, + External_6_IRQHandler, + External_7_IRQHandler, + External_8_IRQHandler, + External_9_IRQHandler, + External_10_IRQHandler, + External_11_IRQHandler, + External_12_IRQHandler, + External_13_IRQHandler, + External_14_IRQHandler, + External_15_IRQHandler, + External_16_IRQHandler, + External_17_IRQHandler, + External_18_IRQHandler, + External_19_IRQHandler, + External_20_IRQHandler, + External_21_IRQHandler, + External_22_IRQHandler, + External_23_IRQHandler, + External_24_IRQHandler, + External_25_IRQHandler, + External_26_IRQHandler, + External_27_IRQHandler, + External_28_IRQHandler, + External_29_IRQHandler, + External_30_IRQHandler, + External_31_IRQHandler +}; + +#else +/*------------------------------------------------------------------------------ + * Interrupt handlers as mapped into the MIE register of the MIV_RV32 + */ +extern void Reserved_IRQHandler(void); +extern void External_IRQHandler(void); +extern void MGEUI_IRQHandler(void); +extern void MGECI_IRQHandler(void); +extern void MSYS_EI0_IRQHandler(void); +extern void MSYS_EI1_IRQHandler(void); +extern void MSYS_EI2_IRQHandler(void); +extern void MSYS_EI3_IRQHandler(void); +extern void MSYS_EI4_IRQHandler(void); +extern void MSYS_EI5_IRQHandler(void); +extern void SUBSYS_IRQHandler(void); + +#ifndef MIV_RV32_V3_0 /*For MIV_RV32 v3.1*/ +extern void MSYS_EI6_IRQHandler(void); +extern void MSYS_EI7_IRQHandler(void); +extern void SUBSYSR_IRQHandler(void); // @suppress("Unused function declaration") +#endif /*MIV_RV32_V3_0*/ + +#endif /* MIV_LEGACY_RV32 */ + +/*------------------------------------------------------------------------------ + * Increment value for the mtimecmp register in order to achieve a system tick + * interrupt as specified through the MRV_systick_config() function. + */ +static uint64_t g_systick_increment = 0U; +static uint64_t g_systick_cmp_value = 0U; + +/*------------------------------------------------------------------------------ + * Configure the machine timer to generate an interrupt. + */ +uint32_t MRV_systick_config(uint64_t ticks) +{ + uint32_t ret_val = ERROR; + uint64_t remainder = ticks; + g_systick_increment = 0U; + g_systick_cmp_value = 0U; + + while (remainder >= MTIME_PRESCALER) + { + remainder -= MTIME_PRESCALER; + g_systick_increment++; + } + + g_systick_cmp_value = g_systick_increment + MRV_read_mtime(); + + if (g_systick_increment > 0U) + { + WRITE_MTIMECMP(g_systick_cmp_value); + set_csr(mie, MIP_MTIP); + MRV_enable_interrupts(); + ret_val = SUCCESS; + } + + return ret_val; +} + +/*------------------------------------------------------------------------------ + * RISC-V interrupt handler for machine timer interrupts. + */ +void handle_m_timer_interrupt(void) +{ + clear_csr(mie, MIP_MTIP); + + uint64_t mtime_at_irq = MRV_read_mtime(); + +#ifndef NDEBUG + static volatile uint32_t d_tick = 0u; +#endif + + while(g_systick_cmp_value < (mtime_at_irq + MTIME_DELTA)) { + g_systick_cmp_value = g_systick_cmp_value + g_systick_increment; + +#ifndef NDEBUG + d_tick += 1; +#endif + } +/***************************************************************************//** + /* + * Note: If d_tick > 1 it means, that a system timer interrupt has been + * missed. + * Please ensure that interrupt handlers are as short as possible to prevent + * them stopping other interrupts from being handled. For example, if a + * system timer interrupt occurs during a software interrupt, the system + * timer interrupt will not be handled until the software interrupt handling + * is complete. If the software interrupt handling time is more than one + * systick interval, it will result in d_tick > 1. + * If you are running the program using the debugger and halt the CPU at a + * breakpoint, MTIME will continue to increment and interrupts will be + * missed; resulting in d_tick > 1. + */ + + WRITE_MTIMECMP(g_systick_cmp_value); + + SysTick_Handler(); + + set_csr(mie, MIP_MTIP); +} + +void handle_m_soft_interrupt(void) +{ + Software_IRQHandler(); + MRV_clear_soft_irq(); +} +/*------------------------------------------------------------------------------ + * RISC-V interrupt handler for software interrupts. + */ +#ifdef MIV_LEGACY_RV32 +void handle_m_ext_interrupt(void) +{ + unsigned long hart_id = read_csr(mhartid); + uint32_t int_num = PLIC->TARGET[hart_id].CLAIM_COMPLETE; + uint8_t disable = EXT_IRQ_KEEP_ENABLED; + + if (0u !=int_num) + { + disable = mrv_ext_irq_handler_table[int_num](); + + PLIC->TARGET[hart_id].CLAIM_COMPLETE = int_num; + + if(EXT_IRQ_DISABLE == disable) + { + MRV_PLIC_disable_irq((IRQn_Type)int_num); + } + } +} +#else + +/*------------------------------------------------------------------------------ + * MSYS local interrupts table + */ +void (* const local_irq_handler_table[16])(void) = +{ +#ifndef MIV_RV32_V3_0 + MGEUI_IRQHandler, + MGECI_IRQHandler, + SUBSYS_IRQHandler, + SUBSYSR_IRQHandler, + Reserved_IRQHandler, + Reserved_IRQHandler, + Reserved_IRQHandler, + Reserved_IRQHandler, + MSYS_EI0_IRQHandler, + MSYS_EI1_IRQHandler, + MSYS_EI2_IRQHandler, + MSYS_EI3_IRQHandler, + MSYS_EI4_IRQHandler, + MSYS_EI5_IRQHandler, + MSYS_EI6_IRQHandler, + MSYS_EI7_IRQHandler +#else + MGEUI_IRQHandler, + MGECI_IRQHandler, + Reserved_IRQHandler, + Reserved_IRQHandler, + Reserved_IRQHandler, + Reserved_IRQHandler, + Reserved_IRQHandler, + Reserved_IRQHandler, + MSYS_EI0_IRQHandler, + MSYS_EI1_IRQHandler, + MSYS_EI2_IRQHandler, + MSYS_EI3_IRQHandler, + MSYS_EI4_IRQHandler, + MSYS_EI5_IRQHandler, + SUBSYS_IRQHandler, + Reserved_IRQHandler, +#endif +}; + +/*------------------------------------------------------------------------------ + * Jump to interrupt table containing local interrupts + */ +void handle_local_ei_interrupts(uint8_t irq_no) +{ + uint64_t mhart_id = read_csr(mhartid); + ASSERT(irq_no <= MIV_LOCAL_IRQ_MAX) + ASSERT(irq_no >= MIV_LOCAL_IRQ_MIN) + + uint8_t ei_no = (uint8_t)(irq_no - MIV_LOCAL_IRQ_MIN); + (*local_irq_handler_table[ei_no])(); +} +#endif /* MIV_LEGACY_RV32 */ + + +/*------------------------------------------------------------------------------ + * Trap handler. This function is invoked in the non-vectored mode. + */ +void handle_trap(uintptr_t mcause, uintptr_t mepc) +{ + uint64_t is_interrupt = mcause & MCAUSE_INT; + + if (is_interrupt) + { +#ifndef MIV_LEGACY_RV32 + if (((mcause & MCAUSE_CAUSE) >= MIV_LOCAL_IRQ_MIN) && ((mcause & MCAUSE_CAUSE) <= MIV_LOCAL_IRQ_MAX)) + { + handle_local_ei_interrupts((uint8_t)(mcause & MCAUSE_CAUSE)); + } + else if ((mcause & MCAUSE_CAUSE) == IRQ_M_EXT) +#else + if ((mcause & MCAUSE_CAUSE) == IRQ_M_EXT) +#endif + { +#ifndef MIV_LEGACY_RV32 + External_IRQHandler(); +#else + handle_m_ext_interrupt(); +#endif + } + else if ((mcause & MCAUSE_CAUSE) == IRQ_M_SOFT) + { + handle_m_soft_interrupt(); + } + else if ((mcause & MCAUSE_CAUSE) == IRQ_M_TIMER) + { + handle_m_timer_interrupt(); + } + } + else + { +#ifndef NDEBUG + /* + Arguments supplied to this function are mcause, mepc (exception PC) and + stack pointer. + Based on privileged-isa specification mcause values and meanings are: + + 0 Instruction address misaligned (mtval/mtval is the address) + 1 Instruction access fault (mtval/mtval is the address) + 2 Illegal instruction (mtval/mtval contains the + offending instruction opcode) + 3 Breakpoint + 4 Load address misaligned (mtval/mtval is the address) + 5 Load address fault (mtval/mtval is the address) + 6 Store/AMO address fault (mtval/mtval is the address) + 7 Store/AMO access fault (mtval/mtval is the address) + 8 Environment call from U-mode + 9 Environment call from S-mode + A Environment call from M-mode + B Instruction page fault + C Load page fault (mtval/mtval is the address) + E Store page fault (mtval/mtval is the address) + + # Please note: mtval is the newer name for register mbadaddr + # If you get a compile failure here, use the older name. + # At this point, both are supported in latest compiler, older compiler + # versions only support mbadaddr. + # See: https://github.com/riscv/riscv-gcc/issues/133 + */ + + /* interrupt pending */ + uintptr_t mip = read_csr(mip); + + /* additional info and meaning depends on mcause */ + uintptr_t mtval = read_csr(mtval); + + /* trap vector */ + uintptr_t mtvec = read_csr(mtvec); + + /* temporary, sometimes might hold temporary value of a0 */ + uintptr_t mscratch = read_csr(mscratch); + + /* status contains many smaller fields: */ + uintptr_t mstatus = read_csr(mstatus); + + /* PC value when the exception was taken*/ + uintptr_t mmepc = read_csr(mepc); + + /* breakpoint */ + __asm__("ebreak"); +#else + _exit(1 + mcause); +#endif /* NDEBUG */ + } +} + +#ifdef __cplusplus +} +#endif + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_hal.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_hal.h new file mode 100644 index 0000000..9ce9ef6 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_hal.h @@ -0,0 +1,773 @@ +/******************************************************************************* + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file miv_rv32_hal.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Hardware Abstraction Layer functions for Mi-V soft processors + * + */ + +/*=========================================================================*//** + @mainpage MIV_RV32 Hardware Abstraction Layer + + ============================================================================== + Introduction + ============================================================================== + This document describes the Hardware Abstraction Layer (HAL) for the MIV_RV32 + Soft IP Core. This release of the HAL corresponds to the Soft IP core MIV_RV32 + v3.1 release. It also supports earlier versions of the MIV_RV32 as well as the + legacy RV32 IP cores. + The preprocessor macros provided with the MIV_RV32 HAL are used to customize + it to target the Soft Processor IP version being used in your project. + + The term "MIV_RV32" represents following two cores: + - MIV_RV32 v3.0 and later (the latest and greatest Mi-V soft processor) + - MIV_RV32IMC v2.1 (MIV_RV32 v3.0 is a drop in replacement for this core) + It is highly recommended to migrate your design to MIV_RV32 v3.1 + + The term, Legacy RV32 IP cores, represents following IP cores: + - MIV_RV32IMA_L1_AHB + - MIV_RV32IMA_L1_AXI + - MIV_RV32IMAF_L1_AHB + + These legacy RV32 IP cores are deprecated. It is highly recommended to migrate + your designs to MIV_RV32 v3.1 (and subsequent IP releases) for the latest + enhancements, bug fixes, and support. + + -------------------------------- + MIV_RV32 V3.1 + -------------------------------- + This is the latest release of the MIV_RV32 Soft IP core. For more details, + refer to the MIV_RV32 User [Guide](https://www.microchip.com/en-us/products/fpgas-and-plds/ip-core-tools/miv-rv32) + + The MIV_RV32 Core as well as this document use the terms defined below: + + -------------------------------- + - SUBSYS - Processor Subsystem for RISC-V + - OPSRV - Offload Processor Subsystem for RISC-V + - GPR - General Purpose Registers + - MGECIE - Machine GPR ECC Correctable Interrupt Enable + - MGEUIE - Machine GPR ECC Uncorrectable Interrupt Enable + - MTIE - Machine Timer Interrupt Enable + - MEIE - Machine External Interrupt Enable + - MSIE - Machine Software Interrupt Enable + - ISR - Interrupt Service Routine + + ============================================================================== + Customizing MIV_RV32 HAL + ============================================================================== + To use the HAL with older releases of MIV_RV32, preprocessor macros have been + provided. Using these macros, any of the IP version is targeted. + The HAL is used to target any of the mentioned platforms by adding the + following macros in the way : + Project Properties > C/C++ Build > Settings > Preprocessor in Assembler and + Compiler settings. + The table below shows the macros corresponding to the MIV Core being used in + your libero project. By default, the HAL targets v3.1 of the IP core and no + macros need to be set for this configutation. + + | Libero MI-V Soft IP Version | SoftConsole Macro | + |-----------------------------|-------------------| + | MIV_RV32 v3.1 | no macro required | + | MIV_RV32 v3.0 | MIV_CORE_V3_0 | + | Legacy RV32 Cores | MIV_LEGACY_RV32 | + + -------------------------------- + Interrupt Handling + -------------------------------- + The MIE Register is defined as a enum in the HAL, and the table below is used + as a reference when the vectored interrupts are enabled in the GUI core + configurator. + + The MIE register is a RISC-V Control and Status Register (CSR), which stands + for the Machine Interrupt Enable. This is used to enable the machine mode + interrupts in the MIV_RV32 hart. Refer to the RISC-V Priv spec for more details. + + The following table shows the trap entry addresses when an interrupt occurs and + the vectored interrupts are enabled in the GUI configurator. + + | MIE Register Bit | Interrupt Enable | Vector Address | + |-------------------|------------------|----------------| + | 31 | MSYS_IE7 | mtvec.BASE + 0x7C | + | 30 | MSYS_IE6 | mtvec.BASE + 0x78 | + | 29 | MSYS_IE5 | mtvec.BASE + 0x74 | + | 28 | MSYS_IE4 | mtvec.BASE + 0x70 | + | 27 | MSYS_IE3 | mtvec.BASE + 0x6C | + | 26 | MSYS_IE2 | mtvec.BASE + 0x68 | + | 25 | MSYS_IE1 | mtvec.BASE + 0x64 | + | 24 | MSYS_IE0 | mtvec.BASE + 0x60 | + | 23 | SUBSYS_EI | mtvec.BASE + 0x5C | + | 22 | SUBSYSR | mtvec.BASE + 0x58 | + | 17 | MGECIE | mtvec.BASE + 0x44 | + | 16 | MGEUIE | mtvec.BASE + 0x40 | + | 11 | MEIE | mtvec.BASE + 0x2C | + | 7 | MTIE | mtvec.BASE + 0x1C | + | 3 | MSIE | mtvec.BASE + 0x0C | + + + For changes in MIE register map, see the [MIE Register Map for MIV_RV32 v3.0] + (#mie-register-map-for-miv_rv32-v3.0) section. + + SUBSYSR is currently not being used by the core and is Reserved for future use. + + The mtvec.BASE field corresponds to the bits [31:2], where mtvec stands for + Machine Trap Vector, and all traps set the PC to the the value stored in the + mtvec.BASE field when in Non-Vectored mode. In this case, a generic trap + handler is as an interrupt service routine. + + When Vectored interrupts are enabled, use this formula to calculate the trap + address: (mtvec.BASE + 4*cause), where cause comes from the mcause CSR. The + mcause register is written with a code indicating the event that caused the trap. + For more details, see the RISC-V priv specification. + + The MIV_RV32 Soft IP core does not contain a Platfrom Level Interrup Controller + (PLIC). It is advised to use the PLIC contained within the MIV_ESS sub-system. + Connect the PLIC interrupt output of the MIV_ESS to the EXT_IRQ pin on the + MIV_RV32. + + The following table is the MIE register map for the MIV_RV32 Core V3.0. It only + highlights the differences between the V3.0 and V3.1 of the core. + + -------------------------------- + MIE Register Map for MIV_RV32 V3.0 + -------------------------------- + + | MIE Register Bit | Target Interrupt | Vector Address | + |-------------------|------------------|----------------| + | 31 | Not in use | top table | + | 30 | SUBSYS_EI | addr + 0x78 | + | 23 | Not in use | Not in use | + | 22 | Not in use | Not in use | + + Other interrupt bit postions like the MGEUIE and MSYS_IE5 to MSYS_IE0 remain + unchanged. + + -------------------------------- + Floating Point Interrupt Support + -------------------------------- + When an interrupt is taken and Floating Point instructions are used in the + ISR, the floating point register context must be saved to resume the application + correctly. To use this feature, enable the provided macro in the + Softconsole build settings. + This feature is turned off by default as it adds overhead which is not required + when the ISR does not used FP insturctions and saving the general purpose + register context is sufficient. + + | Macro Name | Definition | + |--------------------------|-------------------------------------------------| + | MIV_FP_CONTEXT_SAVE | Define to save the FP register file | + + + -------------------------------- + SUBSYS - SubSystem for RISC-V + -------------------------------- + SUBSYS stands for SubSystem for RISC-V. This was previously (MIV_RV32 v3.0) + known as OPSRV, which stands for "Offload Processor Subsystem + for RISC-V". See the earlier versions of the handbook for more details. + In the latest release of the MIV_RV32 IP core v3.1, OPSRV has been renamed to + SUBSYS. The MIV_RV32 HAL now uses SUBSYS instead of OPSRV. + + *//*=========================================================================*/ +#ifndef RISCV_HAL_H +#define RISCV_HAL_H + +#include "miv_rv32_regs.h" +#include "miv_rv32_plic.h" +#include "miv_rv32_assert.h" +#include "miv_rv32_subsys.h" + +#ifndef LEGACY_DIR_STRUCTURE +#include "fpga_design_config/fpga_design_config.h" +#else +#include "hw_platform.h" +#endif /*LEGACY_DIR_STRUCTURE*/ + +#ifdef __cplusplus +extern "C" { +#endif +/*-------------------------------------------------------------------------*//** + SUBSYS Backwards Compatibility + ======================================= + For application code using the older macro names and API functions, these macros + act as a compatibility layer and applications which use OPSRV API features work + due to these macro definitions. However, it is adviced to update your + application code to use the SUBSYS macros and API functions. + + | Macro Name | Now Called | + |-------------------------|--------------------------| + | OPSRV_TCM_ECC_CE_IRQ | SUBSYS_TCM_ECC_CE_IRQ | + | OPSRV_TCM_ECC_UCE_IRQ | SUBSYS_TCM_ECC_UCE_IRQ | + | OPSRV_AXI_WR_RESP_IRQ | SUBSYS_AXI_WR_RESP_IRQ | + | MRV32_MSYS_OPSRV_IRQn | MRV32_SUBSYS_IRQn | + | MRV32_opsrv_enable_irq | MRV32_subsys_enable_irq | + | MRV32_opsrv_disable_irq | MRV32_subsys_disable_irq | + | MRV32_opsrv_clear_irq | MRV32_subsys_clear_irq | + | OPSRV_IRQHandler | SUBSYS_IRQHandler | + */ + +/*-------------------------------------------------------------------------*//** + MTIME Timer Interrupt Constants + ======================================= + These values contain the register addresses for the registers used by the + machine timer interrupt + + MTIME_PRESCALER is not defined on the MIV_RV32IMC v2.0 and v2.1. By using this + definition the system crashes. For those core, use the following definition: + + #define MTIME_PRESCALER 100u + + MTIME and MTIMECMP + -------------------------------- + MIV_RV32 core offers flexibility in terms of generating MTIME and MTIMECMP + registers internal to the core or using external time reference. There four + possible combinations: + + - Internal MTIME and Internal MTIME IRQ enabled Generate the MTIME and MTIMECMP + registers internally. (The only combination available on legacy RV32 cores) + + - Internal MTIME enabled and Internal MTIME IRQ disabled Generate the MTIME + internally and have a timer interrupt input to the core as external pin. In + this case, 1 pin port will be available on MIV_RV32 for timer interrupt. + + - When the internal MTIME is disabled, and the Internal MTIME IRQ is enabled, the + system generates the time value externally and generates the mtimecmp and + interrupt internally (for example, a multiprocessor system with a shared time + between all cores). In this case, a 64-bit port is available on the MIV_RV32 + core as input. + + - Internal MTIME and Internal MTIME IRQ disabled Generate both the time and + timer interrupts externally. In this case a 64 bit port will be available on + the MIV_RV32 core as input, and a 1 pin port will be available for timer + interrupt. + + To handle all these combinations in the firmware, the following constants must + be defined in accordance with the configuration that you have made on your + MIV_RV32 core design. + + MIV_RV32_EXT_TIMER + -------------------------------- + When defined, it means that the MTIME register is not available internal to + the core. In this case, a 64 bit port will be available on the MIV_RV32 core as + input. When this macro is not defined, it means that the MTIME register is + available internally to the core. + + MIV_RV32_EXT_TIMECMP + -------------------------------- + When defined, it means the MTIMECMP register is not available internally to + the core and the Timer interrupt input to the core comes as an external pin. + When this macro is not defined it means the that MTIMECMP register exists + internal to the core and that the timer interrupt is generated internally. + +NOTE: All these macros must not be defined if you are using a MIV_RV32 core. + */ + +#define OPSRV_TCM_ECC_CE_IRQ SUBSYS_TCM_ECC_CE_IRQ +#define OPSRV_TCM_ECC_UCE_IRQ SUBSYS_TCM_ECC_UCE_IRQ +#define OPSRV_AXI_WR_RESP_IRQ SUBSYS_AXI_WR_RESP_IRQ +#define MRV32_MSYS_OPSRV_IRQn MRV32_SUBSYS_IRQn +#define MRV32_opsrv_enable_irq MRV32_subsys_enable_irq +#define MRV32_opsrv_disable_irq MRV32_subsys_disable_irq +#define MRV32_opsrv_clear_irq MRV32_subsys_clear_irq +#define OPSRV_IRQHandler SUBSYS_IRQHandler + +/*-------------------------------------------------------------------------*//** + External IRQ + ======================================= + Return value from External IRQ handler. This is used to disable the + External Interrupt. + + | Macro Name | Value | Description| + |-------------------|--------|----------------| + | EXT_IRQ_KEEP_ENABLED | 0 | Keep external interrupts enabled | + | EXT_IRQ_DISABLE | 1 | Disable external interrupts | + */ +#define EXT_IRQ_KEEP_ENABLED 0U +#define EXT_IRQ_DISABLE 1U + +#define MTIME_DELTA 5 +#ifdef MIV_LEGACY_RV32 +#define MSIP (*(uint32_t*)0x44000000UL) +#define MTIMECMP (*(uint32_t*)0x44004000UL) +#define MTIMECMPH (*(uint32_t*)0x44004004UL) +#define MTIME (*(uint32_t*)0x4400BFF8UL) +#define MTIMEH (*(uint32_t*)0x4400BFFCUL) + +/* To maintain backward compatibility with FreeRTOS config code */ +#define PRCI_BASE 0x44000000UL +#else /* MIV_LEGACY_RV32 */ + +/* To maintain backward compatibility with FreeRTOS config code */ +#define PRCI_BASE 0x02000000UL + +#ifndef MIV_RV32_EXT_TIMECMP +#define MTIMECMP (*(volatile uint32_t*)0x02004000UL) +#define MTIMECMPH (*(volatile uint32_t*)0x02004004UL) +#else +#define MTIMECMP (0u) +#define MTIMECMPH (0u) +#endif + +#define MTIME_PRESCALER (*(volatile uint32_t*)0x02005000UL) + +#ifndef MIV_RV32_EXT_TIMER +#define MTIME (*(volatile uint32_t*)0x0200BFF8UL) +#define MTIMEH (*(volatile uint32_t*)0x0200BFFCUL) + +/***************************************************************************//** + MIMPID Register + The MIMPID register is a RISC-V Control and Status Register In the v3.0 of + MIV_RV32, the value of `MIMPID = 0x000540AD`. In the v3.1 of MIV_RV32, the + value if `MIMPID = 0xE5010301` corresponding to (E)mbedded (5)ystem(01) core + version (03).(01) this terminology will be followed in the subsequent releases + of the core read the csr value and store it in a varible which may be used to + check the MIV_RV32 core version during runtime. + + Future releases of the core will increment the 03 and 01 as major and minor + releases respectively and the register can be read at runtime to find the + Soft IP core version. + + | Core Version | Register | Value | Notes | + |----------------|------------|---------|---------| + | MIV_RV32 V3.1 | mimpid | 0xE5010301 | implimentation ID | + | MIV_RV32 V3.0 | mimpid | 0x000540AD | implimentation ID | + */ +#define MIMPID read_csr(mimpid) + +/*Used as a mask to read and write to mte mtvec.BASE address*/ +#define MTVEC_BASE_ADDR_MASK 0xFFFFFFFC + +#else +#define MTIME (0u) +#define MTIMEH (0u) +#endif /*MIV_RV32_EXT_TIMER*/ + +/*-------------------------------------------------------------------------*//** + RISC-V Specification Interrupts + ======================================= + These definitions are provided for easy identification of the interrupt + in the MIE/MIP registers. + Apart from the standard software, timer, and external interrupts, the names + of the additional interrupts correspond to the names as used in the MIV_RV32 + handbook. Please refer the MIV_RV32 handbook for more details. + + All the interrups, provided by the MIV_RV32 core, follow the interrupt priority + order and register description as mentioned in the RISC-V spec. + + | Macro Name | Value | Description| + |-------------------|--------|----------------| + | MRV32_SOFT_IRQn | MIE_3_IRQn | Software interrupt enable | + | MRV32_TIMER_IRQn | MIE_7_IRQn | Timer interrupt enable | + | MRV32_EXT_IRQn | MIE_11_IRQn | External interrupt enable | + + */ +#define MRV32_SOFT_IRQn MIE_3_IRQn +#define MRV32_TIMER_IRQn MIE_7_IRQn +#define MRV32_EXT_IRQn MIE_11_IRQn + +/***************************************************************************//** + Interrupt numbers: + This enum represents the interrupt enable bits in the MIE register. + */ +enum +{ + MIE_0_IRQn = (0x01u), + MIE_1_IRQn = (0x01u<<1u), + MIE_2_IRQn = (0x01u<<2u), + MIE_3_IRQn = (0x01u<<3u), /*MSIE 0xC*/ + MIE_4_IRQn = (0x01u<<4u), + MIE_5_IRQn = (0x01u<<5u), + MIE_6_IRQn = (0x01u<<6u), + MIE_7_IRQn = (0x01u<<7u), /*MTIE 0x1C*/ + MIE_8_IRQn = (0x01u<<8u), + MIE_9_IRQn = (0x01u<<9u), + MIE_10_IRQn = (0x01u<<10u), + MIE_11_IRQn = (0x01u<<11u), /*MEIE 0x2C*/ + MIE_12_IRQn = (0x01u<<12u), + MIE_13_IRQn = (0x01u<<13u), + MIE_14_IRQn = (0x01u<<14u), + MIE_15_IRQn = (0x01u<<15u), + MIE_16_IRQn = (0x01u<<16u), /*MGEUIE ECC Uncorrectable 0x40*/ + MIE_17_IRQn = (0x01u<<17u), /*MGECIE ECC Correctable 0x44*/ + MIE_18_IRQn = (0x01u<<18u), + MIE_19_IRQn = (0x01u<<19u), + MIE_20_IRQn = (0x01u<<20u), + MIE_21_IRQn = (0x01u<<21u), + MIE_22_IRQn = (0x01u<<22u), /*SUBSYSR 0x58 (R)eserved*/ + MIE_23_IRQn = (0x01u<<23u), /*SUBSYS_IE 0x5C for MIV_RV32 v3.1*/ + MIE_24_IRQn = (0x01u<<24u), /*MSYS_IE0 0x60*/ + MIE_25_IRQn = (0x01u<<25u), /*MSYS_IE1 0x64*/ + MIE_26_IRQn = (0x01u<<26u), /*MSYS_IE2 0x68*/ + MIE_27_IRQn = (0x01u<<27u), /*MSYS_IE3 0x6C*/ + MIE_28_IRQn = (0x01u<<28u), /*MSYS_IE4 0x70*/ + MIE_29_IRQn = (0x01u<<29u), /*MSYS_IE5 0x74*/ + MIE_30_IRQn = (0x01u<<30u), /*MSYS_IE6 0x78, read comment below*/ + MIE_31_IRQn = (0x01u<<31u) /*MSYS_IE7 0x7C*/ +} MRV_LOCAL_IRQn_Type; + +#define MRV32_MGEUIE_IRQn MIE_16_IRQn +#define MRV32_MGECIE_IRQn MIE_17_IRQn +#define MRV32_MSYS_EIE0_IRQn MIE_24_IRQn +#define MRV32_MSYS_EIE1_IRQn MIE_25_IRQn +#define MRV32_MSYS_EIE2_IRQn MIE_26_IRQn +#define MRV32_MSYS_EIE3_IRQn MIE_27_IRQn +#define MRV32_MSYS_EIE4_IRQn MIE_28_IRQn +#define MRV32_MSYS_EIE5_IRQn MIE_29_IRQn +#ifndef MIV_RV32_V3_0 /*For MIV_RV32 v3.1*/ +#define MRV32_SUBSYSR_IRQn MIE_22_IRQn +#define MRV32_SUBSYS_IRQn MIE_23_IRQn +#define MRV32_MSYS_EIE6_IRQn MIE_30_IRQn +#define MRV32_MSYS_EIE7_IRQn MIE_31_IRQn +#else +#define MRV32_SUBSYS_IRQn MIE_30_IRQn +#endif /*MIV_RV32_V3_0*/ + +/*--------------------------------Public APIs---------------------------------*/ + +/***************************************************************************//** + The MRV32_clear_gpr_ecc_errors() function clears single bit ECC errors on the + GPRs. The ECC block does not write back corrected data to memory. Hence, when + ECC is enabled for the GPRs and if that data has a single bit error then the + data coming out of the ECC block is corrected and will not have the error, but + the data source will still have the error. Therefore, if data has a single bit + error, then the corrected data must be written back to prevent the single bit + error from becoming a double bit error. Clear the pending interrupt bit after + this using MRV32_mgeci_clear_irq() function to complete the ECC error handling. + + @param + This function does not take any parameters. + + @return + This functions returns the CORE_GPR_DED_RESET_REG bit value. + */ +static inline void MRV32_clear_gpr_ecc_errors(void) +{ + uint32_t temp; + + __asm__ __volatile__ ( + "sw x31, %0" + :"=m" (temp)); + + __asm__ volatile ( + "mv x31, x1;" + "mv x1, x31;" + + "mv x31, x2;" + "mv x2, x31;" + + "mv x31, x3;" + "mv x3, x31;" + + "mv x31, x4;" + "mv x4, x31;" + + "mv x31, x5;" + "mv x5, x31;" + + "mv x31, x6;" + "mv x6, x31;" + + "mv x31, x7;" + "mv x7, x31;" + + "mv x31, x8;" + "mv x8, x31;" + + "mv x31, x9;" + "mv x9, x31;" + + "mv x31, x10;" + "mv x10, x31;" + + "mv x31, x11;" + "mv x11, x31;" + + "mv x31, x12;" + "mv x12, x31;" + + "mv x31, x13;" + "mv x13, x31;" + + "mv x31, x14;" + "mv x14, x31;" + + "mv x31, x15;" + "mv x15, x31;" + + "mv x31, x16;" + "mv x16, x31;" + + "mv x31, x17;" + "mv x17, x31;" + + "mv x31, x18;" + "mv x18, x31;" + + "mv x31, x19;" + "mv x19, x31;" + + "mv x31, x20;" + "mv x20, x31;" + + "mv x31, x21;" + "mv x21, x31;" + + "mv x31, x22;" + "mv x22, x31;" + + "mv x31, x23;" + "mv x23, x31;" + + "mv x31, x24;" + "mv x24, x31;" + + "mv x31, x25;" + "mv x25, x31;" + + "mv x31, x26;" + "mv x26, x31;" + + "mv x31, x27;" + "mv x27, x31;" + + "mv x31, x28;" + "mv x28, x31;" + + "mv x31, x29;" + "mv x29, x31;" + + "mv x31, x30;" + "mv x30, x31;"); + + __asm__ __volatile__ ( + "lw x31, %0;" + : + :"m" (temp)); +} + + +/***************************************************************************//** + The MRV32_mgeui_clear_irq() function clears the GPR ECC Uncorrectable + Interrupt. MGEUI interrupt is available only when ECC is enabled in the MIV_RV32 + IP configurator. + + @return + This function does not return any value. + */ +static inline void MRV32_mgeui_clear_irq(void) +{ + clear_csr(mip, MRV32_MGEUIE_IRQn); +} + +/***************************************************************************//** + The MRV32_mgeci_clear_irq() function clears the GPR ECC Correctable Interrupt + MGECI interrupt is available only when ECC is enabled in the MIV_RV32 IP + configurator. + + @return + This function does not return any value. + */ +static inline void MRV32_mgeci_clear_irq(void) +{ + clear_csr(mip, MRV32_MGECIE_IRQn); +} + +/***************************************************************************//** + The MRV_enable_local_irq() function enables the local interrupts. It takes a + mask value as input. For each set bit in the mask value, the corresponding + interrupt bit in the MIE register is enabled. + + MRV_enable_local_irq( MRV32_SOFT_IRQn | MRV32_TIMER_IRQn | MRV32_EXT_IRQn | + MRV32_MSYS_EIE0_IRQn | + MRV32_MSYS_SUBSYS_IRQn); + */ +static inline void MRV_enable_local_irq(uint32_t mask) +{ + set_csr(mie, mask); +} + +/***************************************************************************//** + The MRV_disable_local_irq() function disables the local interrupts. It takes a + mask value as input. For each set bit in the mask value, the corresponding + interrupt bit in the MIE register is disabled. + + MRV_disable_local_irq( MRV32_SOFT_IRQn | MRV32_TIMER_IRQn | MRV32_EXT_IRQn | + MRV32_MSYS_EIE0_IRQn | + MRV32_MSYS_SUBSYS_IRQn); + */ +static inline void MRV_disable_local_irq(uint32_t mask) +{ + clear_csr(mie, mask); +} +#endif /* MIV_LEGACY_RV32 */ + +/***************************************************************************//** + The MRV_enable_interrupts() function enables all interrupts by setting the + machine mode interrupt enable bit in MSTATUS register. + + @param + This function does not take any parameters. + + @return + This functions returns the CORE_GPR_DED_RESET_REG bit value. + */ +static inline void MRV_enable_interrupts(void) +{ + set_csr(mstatus, MSTATUS_MIE); +} + +/***************************************************************************//** + The MRV_disable_interrupts() function disables all interrupts by clearing the + machine mode interrupt enable bit in MSTATUS register. + @param + This function does not take any parameters. + + @return + This functions returns the CORE_GPR_DED_RESET_REG bit value. + */ +static inline void MRV_disable_interrupts(void) +{ + clear_csr(mstatus, MSTATUS_MPIE); + clear_csr(mstatus, MSTATUS_MIE); +} + +/***************************************************************************//** + The MRV_read_mtvec_base() function reads the mtvec base value, which is the + addr used when an interrupt/trap occurs. In the mtvec register, [31:2] is the + BASE address. NOTE: The BASE address must be aligned on a 4B boundary. + + @param + The function does not take any parameters. + + @return + The function returns the value of the BASE field [31:2] as an unsigned 32-bit + value. + */ + +#ifndef MIV_LEGACY_RV32 +#ifndef MIV_RV32_v3_0 +static inline uint32_t MRV_read_mtvec_base (void) +{ + uint32_t mtvec_addr_base = read_csr(mtvec); + return mtvec_addr_base & MTVEC_BASE_ADDR_MASK; +} + +/***************************************************************************//** + The MRV_set_mtvec_base() function takes the mtvec_base address as a unsigned int + and writes the value into the BASE field [31:2] in the mtvec CSR, MODE[1:0] + is Read-only. BASE is 4B aligned, so the lowest 2 bits of mtvec_base are + ignored. + + @param mtvec_base + Any legal value is passed into the function, and it is used as the trap_entry + for interrupts. The PC jumps to this address provided when an interrupt occurs. + In case of vectored interrupts, the address value mentioned in the vector + table under the MIE Register Map is updated to the value passed to this + function parameter. + + @return + This function does not return any value. + */ +static inline void MRV_set_mtvec_base (uint32_t mtvec_base) +{ + mtvec_base = mtvec_base & MTVEC_BASE_ADDR_MASK; + write_csr(mtvec, mtvec_base); +} +#endif /*MIV_RV32_v3_0*/ +#endif /*MIV_LEGACY_RV32*/ + +/***************************************************************************//** + The MRV_read_mtime() function returns the current MTIME register value. + */ +static inline uint64_t MRV_read_mtime(void) +{ + volatile uint32_t hi = 0u; + volatile uint32_t lo = 0u; + + /* when mtime lower word is 0xFFFFFFFF, there will be rollover and + * returned value could be wrong. */ + do { + hi = MTIMEH; + lo = MTIME; + } while(hi != MTIMEH); + + return((((uint64_t)MTIMEH) << 32u) | lo); +} + +/***************************************************************************//** + The MRV_raise_soft_irq() function raises a synchronous software interrupt + by writing into the MSIP register. + @param + This function does not take any parameters. + + @return + This function does not return any value. + */ +static inline void MRV_raise_soft_irq(void) +{ + set_csr(mie, MIP_MSIP); /* Enable software interrupt bit */ + +#ifdef MIV_LEGACY_RV32 + /* You need to make sure that the global interrupt is enabled */ + MSIP = 0x01; /* raise soft interrupt */ +#else + /* Raise soft IRQ on MIV_RV32 processor */ + SUBSYS->soft_reg |= SUBSYS_SOFT_IRQ; +#endif +} + +/***************************************************************************//** + The MRV_clear_soft_irq() function clears a synchronous software interrupt + by clearing the MSIP register. + @param + This function does not take any parameters. + + @return + This function does not return any value. + */ +static inline void MRV_clear_soft_irq(void) +{ +#ifdef MIV_LEGACY_RV32 + MSIP = 0x00u; /* clear soft interrupt */ +#else + /* Clear soft IRQ on MIV_RV32 processor */ + SUBSYS->soft_reg &= ~SUBSYS_SOFT_IRQ; +#endif +} + +/***************************************************************************//** + System tick handler. This handler function gets called when the Machine + timer interrupt asserts. An implementation of this function must be provided + by the application to implement the application specific machine timer + interrupt handling. If application does not provide such implementation, the + weakly linked handler stub function implemented in riscv_hal_stubs.c gets + linked. + */ +void SysTick_Handler(void); + +/***************************************************************************//** + System timer tick configuration. + Configures the machine timer to generate a system tick interrupt at regular + intervals. Takes the number of system clock ticks between interrupts. + + Though this function can take any valid ticks value as parameter, we expect + that, for all practical purposes, a small tick value (to generate periodic + interrupts every few miliseconds) is passed. If you need to generate periodic + events in the range of seconds or more, you may use the SysTick_Handler() to + further count the number of interrupts and hence the larger time intervals. + + @param ticks + This is the number of ticks or clock cycles which are counted down from the + interrupt to be triggered. + + @return + Returns 0 if successful. + Returns 1 if the interrupt interval is not achieved. + */ +uint32_t MRV_systick_config(uint64_t ticks); + +#ifdef __cplusplus +} +#endif +#endif /* RISCV_HAL_H */ \ No newline at end of file diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_hal_version.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_hal_version.h new file mode 100644 index 0000000..0b50bec --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_hal_version.h @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright 2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file miv_rv32_hal_version.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Hardware Abstraction Layer functions for Mi-V soft processors + * + */ + +#ifndef MIV_RV32_HAL_VERSION_H +#define MIV_RV32_HAL_VERSION_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define MIV_RV32_HAL_VERSION_MAJOR 4 +#define MIV_RV32_HAL_VERSION_MINOR 2 +#define MIV_RV32_HAL_VERSION_PATCH 106 + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_init.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_init.c new file mode 100644 index 0000000..cc6aeaf --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_init.c @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file miv_rv32_init.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief Mi-V soft processor memory section initializations and start-up code. + * + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int main(void); + +void _init(void) +{ + /* This function is a placeholder for the case where some more hardware + * specific initializations are required before jumping into the application + * code. You can implement it here. */ + + /* Jump to the application code after all initializations are completed */ + int code = 0; + + code = main(); + _exit(code); +} + +/* Function called after main() finishes */ +void +_fini(void) +{ +} + +#ifdef __cplusplus +} +#endif + diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_plic.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_plic.h new file mode 100644 index 0000000..3fd4103 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_plic.h @@ -0,0 +1,214 @@ +/******************************************************************************* + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file miv_rv32_plic.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Mi-V legacy RV32 soft processor PLIC access data structures and + * functions. + * Legacy RV32 soft processors are DEPRICATED. + * Migrate to MIV_RV32 v3.0 or later. + * + */ +#ifndef RISCV_PLIC_H +#define RISCV_PLIC_H + +#include +#include "miv_rv32_regs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*============================================================================== + * Interrupt numbers: + */ +#ifdef MIV_LEGACY_RV32 +typedef enum +{ + MRV_NoInterrupt_IRQn = 0, + External_1_IRQn = 1, + External_2_IRQn = 2, + External_3_IRQn = 3, + External_4_IRQn = 4, + External_5_IRQn = 5, + External_6_IRQn = 6, + External_7_IRQn = 7, + External_8_IRQn = 8, + External_9_IRQn = 9, + External_10_IRQn = 10, + External_11_IRQn = 11, + External_12_IRQn = 12, + External_13_IRQn = 13, + External_14_IRQn = 14, + External_15_IRQn = 15, + External_16_IRQn = 16, + External_17_IRQn = 17, + External_18_IRQn = 18, + External_19_IRQn = 19, + External_20_IRQn = 20, + External_21_IRQn = 21, + External_22_IRQn = 22, + External_23_IRQn = 23, + External_24_IRQn = 24, + External_25_IRQn = 25, + External_26_IRQn = 26, + External_27_IRQn = 27, + External_28_IRQn = 28, + External_29_IRQn = 29, + External_30_IRQn = 30, + External_31_IRQn = 31 +} IRQn_Type; + +#define EXT_INTR_SOURCES 31 + +/*============================================================================== + * PLIC: Platform Level Interrupt Controller + */ +#define PLIC_BASE_ADDR 0x40000000UL + +typedef struct +{ + volatile uint32_t PRIORITY_THRESHOLD; + volatile uint32_t CLAIM_COMPLETE; + volatile uint32_t reserved[1022]; +} MRV_IRQ_Target_Type; + +typedef struct +{ + volatile uint32_t ENABLES[32]; +} MRV_Target_Enables_Type; + +typedef struct +{ + /*-------------------- Source Priority --------------------*/ + volatile uint32_t SOURCE_PRIORITY[1024]; + + /*-------------------- Pending array --------------------*/ + volatile const uint32_t PENDING_ARRAY[32]; + volatile uint32_t RESERVED1[992]; + + /*-------------------- Target enables --------------------*/ + volatile MRV_Target_Enables_Type TARGET_ENABLES[15808]; + + volatile uint32_t RESERVED2[16384]; + + /*--- Target Priority threshold and claim/complete---------*/ + MRV_IRQ_Target_Type TARGET[15872]; + +} PLIC_Type; + +#define PLIC ((PLIC_Type *)PLIC_BASE_ADDR) + +/*============================================================================== + * The function MRV_PLIC_init() initializes the PLIC controller and enables + * the global external interrupt bit. + */ +static inline void MRV_PLIC_init(void) +{ + uint32_t inc; + unsigned long hart_id = read_csr(mhartid); + + /* Disable all interrupts for the current hart. */ + for(inc = 0; inc < ((EXT_INTR_SOURCES + 32u) / 32u); ++inc) + { + PLIC->TARGET_ENABLES[hart_id].ENABLES[inc] = 0; + } + + /* Set priorities to zero. */ + for(inc = 0; inc < EXT_INTR_SOURCES; ++inc) + { + PLIC->SOURCE_PRIORITY[inc] = 0; + } + + /* Set the threshold to zero. */ + PLIC->TARGET[hart_id].PRIORITY_THRESHOLD = 0; + + /* Enable machine external interrupts. */ + set_csr(mie, MIP_MEIP); +} + +/*============================================================================== + * The function MRV_PLIC_enable_irq() enables the external interrupt for the + * interrupt number indicated by the parameter IRQn. + */ +static inline void MRV_PLIC_enable_irq(IRQn_Type IRQn) +{ + unsigned long hart_id = read_csr(mhartid); + uint32_t current = PLIC->TARGET_ENABLES[hart_id].ENABLES[IRQn / 32]; + current |= (uint32_t)1 << (IRQn % 32); + PLIC->TARGET_ENABLES[hart_id].ENABLES[IRQn / 32] = current; +} + +/*============================================================================== + * The function MRV_PLIC_disable_irq() disables the external interrupt for + * the interrupt number indicated by the parameter IRQn. + + * NOTE: + * This function can be used to disable the external interrupt from outside + * external interrupt handler function. + * This function MUST NOT be used from within the External Interrupt handler. + * If you wish to disable the external interrupt while the interrupt handler + * for that external interrupt is executing then you must use the return value + * EXT_IRQ_DISABLE to return from the extern interrupt handler. + */ +static inline void MRV_PLIC_disable_irq(IRQn_Type IRQn) +{ + unsigned long hart_id = read_csr(mhartid); + uint32_t current = PLIC->TARGET_ENABLES[hart_id].ENABLES[IRQn / 32]; + + current &= ~((uint32_t)1 << (IRQn % 32)); + + PLIC->TARGET_ENABLES[hart_id].ENABLES[IRQn / 32] = current; +} + +/*============================================================================== + * The function MRV_PLIC_set_priority() sets the priority for the external + * interrupt for the interrupt number indicated by the parameter IRQn. + */ +static inline void MRV_PLIC_set_priority(IRQn_Type IRQn, uint32_t priority) +{ + PLIC->SOURCE_PRIORITY[IRQn] = priority; +} + +/*============================================================================== + * The function MRV_PLIC_get_priority() returns the priority for the external + * interrupt for the interrupt number indicated by the parameter IRQn. + */ +static inline uint32_t MRV_PLIC_get_priority(IRQn_Type IRQn) +{ + return PLIC->SOURCE_PRIORITY[IRQn]; +} + +/***************************************************************************//** + * MRV_PLIC_clear_pending_irq(void) + * This is only called by the startup hart and only once + * Clears any pending interrupts as PLIC can be in unknown state on startup + */ +static inline void MRV_PLIC_clear_pending_irq(void) +{ + unsigned long hart_id = read_csr(mhartid); + volatile uint32_t int_num = PLIC->TARGET[hart_id].CLAIM_COMPLETE; + volatile int32_t wait_possible_int; + + while (MRV_NoInterrupt_IRQn != int_num) + { + PLIC->TARGET[hart_id].CLAIM_COMPLETE = int_num; + wait_possible_int = 0xFU; + while (wait_possible_int) + { + wait_possible_int--; + } + + int_num = PLIC->TARGET[hart_id].CLAIM_COMPLETE; + } +} + +#endif /* MIV_LEGACY_RV32 */ + +#ifdef __cplusplus +} +#endif + +#endif /* RISCV_PLIC_H */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_regs.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_regs.h new file mode 100644 index 0000000..07d58e7 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_regs.h @@ -0,0 +1,520 @@ +/******************************************************************************* + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file miv_rv32_regs.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Mi-V soft processor register bit mask and shift constants encodings. + * + */ +#ifndef MIV_RV32_REGS_H +#define MIV_RV32_REGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define MSTATUS_UIE 0x00000001UL +#define MSTATUS_SIE 0x00000002UL +#define MSTATUS_HIE 0x00000004UL +#define MSTATUS_MIE 0x00000008UL +#define MSTATUS_UPIE 0x00000010UL +#define MSTATUS_SPIE 0x00000020UL +#define MSTATUS_HPIE 0x00000040UL +#define MSTATUS_MPIE 0x00000080UL +#define MSTATUS_SPP 0x00000100UL +#define MSTATUS_HPP 0x00000600UL +#define MSTATUS_MPP 0x00001800UL +#define MSTATUS_FS 0x00006000UL +#define MSTATUS_XS 0x00018000UL +#define MSTATUS_MPRV 0x00020000UL +#define MSTATUS_SUM 0x00040000UL +#define MSTATUS_MXR 0x00080000UL +#define MSTATUS_TVM 0x00100000UL +#define MSTATUS_TW 0x00200000UL +#define MSTATUS_TSR 0x00400000UL +#define MSTATUS_RES 0x7F800000UL +#define MSTATUS32_SD 0x80000000UL +#define MSTATUS64_SD 0x8000000000000000UL + +#define MCAUSE32_CAUSE 0x7FFFFFFFUL +#define MCAUSE64_CAUSE 0x7FFFFFFFFFFFFFFFUL +#define MCAUSE32_INT 0x80000000UL +#define MCAUSE64_INT 0x8000000000000000UL + +#define MIP_SSIP (1u << IRQ_S_SOFT) +#define MIP_HSIP (1u << IRQ_H_SOFT) +#define MIP_MSIP (1u << IRQ_M_SOFT) +#define MIP_STIP (1u << IRQ_S_TIMER) +#define MIP_HTIP (1u << IRQ_H_TIMER) +#define MIP_MTIP (1u << IRQ_M_TIMER) +#define MIP_SEIP (1u << IRQ_S_EXT) +#define MIP_HEIP (1u << IRQ_H_EXT) +#define MIP_MEIP (1u << IRQ_M_EXT) + +#define PRV_M 3U + +#define VM_MBARE 0U +#define VM_MBB 1U +#define VM_MBBID 2U +#define VM_SV32 8U +#define VM_SV39 9U +#define VM_SV48 10U + +#define IRQ_S_SOFT 1U +#define IRQ_H_SOFT 2U +#define IRQ_M_SOFT 3U +#define IRQ_S_TIMER 5U +#define IRQ_H_TIMER 6U +#define IRQ_M_TIMER 7U +#define IRQ_S_EXT 9U +#define IRQ_H_EXT 10U +#define IRQ_M_EXT 11U + +#define DEFAULT_RSTVEC 0x00001000 +#define DEFAULT_NMIVEC 0x00001004 +#define DEFAULT_MTVEC 0x00001010 +#define CONFIG_STRING_ADDR 0x0000100C +#define EXT_IO_BASE 0x40000000 +#define DRAM_BASE 0x80000000 + +#ifdef __riscv + +#if __riscv_xlen == 64 +# define MSTATUS_SD MSTATUS64_SD +# define SSTATUS_SD SSTATUS64_SD +# define MCAUSE_INT MCAUSE64_INT +# define MCAUSE_CAUSE MCAUSE64_CAUSE +# define RISCV_PGLEVEL_BITS 9 +#else +# define MSTATUS_SD MSTATUS32_SD +# define SSTATUS_SD SSTATUS32_SD +# define RISCV_PGLEVEL_BITS 10 +# define MCAUSE_INT MCAUSE32_INT +# define MCAUSE_CAUSE MCAUSE32_CAUSE +#endif + +#define RISCV_PGSHIFT 12U +#define RISCV_PGSIZE (1U << RISCV_PGSHIFT) + +#ifndef __ASSEMBLER__ + +#ifdef __GNUC__ + +#define read_csr(reg) ({ unsigned long __tmp; \ + __asm__ volatile ("csrr %0, " #reg : "=r"(__tmp)); \ + __tmp; }) + +#define write_csr(reg, val) ({ \ + __asm__ volatile ("csrw " #reg ", %0" :: "rK"(val)); }) + +#define swap_csr(reg, val) ({ unsigned long __tmp; \ + __asm__ volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "rK"(val)); \ + __tmp; }) + +#define set_csr(reg, bit) ({ unsigned long __tmp; \ + __asm__ volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \ + __tmp; }) + +#define clear_csr(reg, bit) ({ unsigned long __tmp; \ + __asm__ volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \ + __tmp; }) + +#ifdef __riscv_atomic + +#define MASK(nr) (1UL << nr) +#define MASK_NOT(nr) (~(1UL << nr)) + +/** + * atomic_read - read atomic variable + * @v: pointer of type int + * + * Atomically reads the value of @v. + */ +static inline int atomic_read(const int *v) +{ + return *((volatile int *)(v)); +} + +/** + * atomic_set - set atomic variable + * @v: pointer of type int + * @i: required value + * + * Atomically sets the value of @v to @i. + */ +static inline void atomic_set(int *v, int i) +{ + *v = i; +} + +/** + * atomic_add - add integer to atomic variable + * @i: integer value to add + * @v: pointer of type int + * + * Atomically adds @i to @v. + */ +static inline void atomic_add(int i, int *v) +{ + __asm__ __volatile__ ( + "amoadd.w zero, %1, %0" + : "+A" (*v) + : "r" (i)); +} + +static inline int atomic_fetch_add(unsigned int mask, int *v) +{ + int out; + + __asm__ __volatile__ ( + "amoadd.w %2, %1, %0" + : "+A" (*v), "=r" (out) + : "r" (mask)); + return out; +} + +/** + * atomic_sub - subtract integer from atomic variable + * @i: integer value to subtract + * @v: pointer of type int + * + * Atomically subtracts @i from @v. + */ +static inline void atomic_sub(int i, int *v) +{ + atomic_add(-i, v); +} + +static inline int atomic_fetch_sub(unsigned int mask, int *v) +{ + int out; + + __asm__ __volatile__ ( + "amosub.w %2, %1, %0" + : "+A" (*v), "=r" (out) + : "r" (mask)); + return out; +} + +/** + * atomic_add_return - add integer to atomic variable + * @i: integer value to add + * @v: pointer of type int + * + * Atomically adds @i to @v and returns the result + */ +static inline int atomic_add_return(int i, int *v) +{ + register int c; + __asm__ __volatile__ ( + "amoadd.w %0, %2, %1" + : "=r" (c), "+A" (*v) + : "r" (i)); + return (c + i); +} + +/** + * atomic_sub_return - subtract integer from atomic variable + * @i: integer value to subtract + * @v: pointer of type int + * + * Atomically subtracts @i from @v and returns the result + */ +static inline int atomic_sub_return(int i, int *v) +{ + return atomic_add_return(-i, v); +} + +/** + * atomic_inc - increment atomic variable + * @v: pointer of type int + * + * Atomically increments @v by 1. + */ +static inline void atomic_inc(int *v) +{ + atomic_add(1, v); +} + +/** + * atomic_dec - decrement atomic variable + * @v: pointer of type int + * + * Atomically decrements @v by 1. + */ +static inline void atomic_dec(int *v) +{ + atomic_add(-1, v); +} + +static inline int atomic_inc_return(int *v) +{ + return atomic_add_return(1, v); +} + +static inline int atomic_dec_return(int *v) +{ + return atomic_sub_return(1, v); +} + +/** + * atomic_sub_and_test - subtract value from variable and test result + * @i: integer value to subtract + * @v: pointer of type int + * + * Atomically subtracts @i from @v and returns + * true if the result is zero, or false for all + * other cases. + */ +static inline int atomic_sub_and_test(int i, int *v) +{ + return (atomic_sub_return(i, v) == 0); +} + +/** + * atomic_inc_and_test - increment and test + * @v: pointer of type int + * + * Atomically increments @v by 1 + * and returns true if the result is zero, or false for all + * other cases. + */ +static inline int atomic_inc_and_test(int *v) +{ + return (atomic_inc_return(v) == 0); +} + +/** + * atomic_dec_and_test - decrement and test + * @v: pointer of type int + * + * Atomically decrements @v by 1 and + * returns true if the result is 0, or false for all other + * cases. + */ +static inline int atomic_dec_and_test(int *v) +{ + return (atomic_dec_return(v) == 0); +} + +/** + * atomic_add_negative - add and test if negative + * @i: integer value to add + * @v: pointer of type int + * + * Atomically adds @i to @v and returns true + * if the result is negative, or false when + * result is greater than or equal to zero. + */ +static inline int atomic_add_negative(int i, int *v) +{ + return (atomic_add_return(i, v) < 0); +} + +static inline int atomic_xchg(int *v, int n) +{ + register int c; + __asm__ __volatile__ ( + "amoswap.w %0, %2, %1" + : "=r" (c), "+A" (*v) + : "r" (n)); + return c; +} + +/** + * atomic_and - Atomically clear bits in atomic variable + * @mask: Mask of the bits to be retained + * @v: pointer of type int + * + * Atomically retains the bits set in @mask from @v + */ +static inline void atomic_and(unsigned int mask, int *v) +{ + __asm__ __volatile__ ( + "amoand.w zero, %1, %0" + : "+A" (*v) + : "r" (mask)); +} + +static inline int atomic_fetch_and(unsigned int mask, int *v) +{ + int out; + __asm__ __volatile__ ( + "amoand.w %2, %1, %0" + : "+A" (*v), "=r" (out) + : "r" (mask)); + return out; +} + +/** + * atomic_or - Atomically set bits in atomic variable + * @mask: Mask of the bits to be set + * @v: pointer of type int + * + * Atomically sets the bits set in @mask in @v + */ +static inline void atomic_or(unsigned int mask, int *v) +{ + __asm__ __volatile__ ( + "amoor.w zero, %1, %0" + : "+A" (*v) + : "r" (mask)); +} + +static inline int atomic_fetch_or(unsigned int mask, int *v) +{ + int out; + __asm__ __volatile__ ( + "amoor.w %2, %1, %0" + : "+A" (*v), "=r" (out) + : "r" (mask)); + return out; +} + +/** + * atomic_xor - Atomically flips bits in atomic variable + * @mask: Mask of the bits to be flipped + * @v: pointer of type int + * + * Atomically flips the bits set in @mask in @v + */ +static inline void atomic_xor(unsigned int mask, int *v) +{ + __asm__ __volatile__ ( + "amoxor.w zero, %1, %0" + : "+A" (*v) + : "r" (mask)); +} + +static inline int atomic_fetch_xor(unsigned int mask, int *v) +{ + int out; + __asm__ __volatile__ ( + "amoxor.w %2, %1, %0" + : "+A" (*v), "=r" (out) + : "r" (mask)); + return out; +} + +/** + * test_and_set_bit - Set a bit and return its old value + * @nr: Bit to set + * @addr: Address to count from + * + * This operation is atomic and cannot be reordered. + * It also implies a memory barrier. + */ +static inline int test_and_set_bit(int nr, volatile unsigned long *addr) +{ + unsigned long __res, __mask; + __mask = MASK(nr); + __asm__ __volatile__ ( \ + "amoor.w %0, %2, %1" \ + : "=r" (__res), "+A" (*addr) \ + : "r" (__mask)); \ + + return ((__res & __mask) != 0); +} + +/** + * test_and_clear_bit - Clear a bit and return its old value + * @nr: Bit to clear + * @addr: Address to count from + * + * This operation is atomic and cannot be reordered. + * It also implies a memory barrier. + */ +static inline int test_and_clear_bit(int nr, volatile unsigned long *addr) +{ + unsigned long __res, __mask; + __mask = MASK_NOT(nr); + __asm__ __volatile__ ( \ + "amoand.w %0, %2, %1" \ + : "=r" (__res), "+A" (*addr) \ + : "r" (__mask)); \ + + return ((__res & __mask) != 0); +} + +/** + * test_and_change_bit - Change a bit and return its old value + * @nr: Bit to change + * @addr: Address to count from + * + * This operation is atomic and cannot be reordered. + * It also implies a memory barrier. + */ +static inline int test_and_change_bit(int nr, volatile unsigned long *addr) +{ + + unsigned long __res, __mask; + __mask = MASK(nr); + __asm__ __volatile__ ( \ + "amoxor.w %0, %2, %1" \ + : "=r" (__res), "+A" (*addr) \ + : "r" (__mask)); \ + + return ((__res & __mask) != 0); +} + +/** + * set_bit - Atomically set a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + * + * This function is atomic and may not be reordered. + */ + +static inline void set_bit(int nr, volatile unsigned long *addr) +{ + __asm__ __volatile__ ( \ + "AMOOR.w zero, %1, %0" \ + : "+A" (*addr) \ + : "r" (MASK(nr))); +} + +/** + * clear_bit - Clears a bit in memory + * @nr: Bit to clear + * @addr: Address to start counting from + * + * clear_bit() is atomic and may not be reordered. + */ +static inline void clear_bit(int nr, volatile unsigned long *addr) +{ + __asm__ __volatile__ ( \ + "AMOAND.w zero, %1, %0" \ + : "+A" (*addr) \ + : "r" (MASK_NOT(nr))); +} + +/** + * change_bit - Toggle a bit in memory + * @nr: Bit to change + * @addr: Address to start counting from + * + * change_bit() is atomic and may not be reordered. + */ +static inline void change_bit(int nr, volatile unsigned long *addr) +{ + __asm__ __volatile__ ( \ + "AMOXOR.w zero, %1, %0" \ + : "+A" (*addr) \ + : "r" (MASK(nr))); +} + +#endif /* __riscv_atomic */ + +#endif /* __GNUC__ */ + +#endif /* __ASSEMBLER__ */ + +#endif /* __riscv */ + +#ifdef __cplusplus +} +#endif + +#endif /* RISCV_REGS_H */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_stubs.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_stubs.c new file mode 100644 index 0000000..e26ecfc --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_stubs.c @@ -0,0 +1,243 @@ +/******************************************************************************* + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file miv_rv32_stubs.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief Stubs for the Mi-V soft processor Interrupt handler. + * + * The functions below will only be linked with the application code if the user + * does not provide an implementation for these functions. These functions are + * defined with weak linking so that they can be overridden by a function with + * same prototype in the user's application code. + * + */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +__attribute__((weak)) void Software_IRQHandler(void) +{ + _exit(10); +} + +__attribute__((weak)) void SysTick_Handler(void) +{ + /* Default handler */ +} + +#ifdef MIV_LEGACY_RV32 +__attribute__((weak)) uint8_t Invalid_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_1_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_2_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_3_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_4_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_5_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_6_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_7_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_8_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_9_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_10_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_11_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_12_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_13_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_14_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_15_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_16_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_17_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_18_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_19_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_20_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_21_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_22_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_23_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_24_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_25_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_26_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_27_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_28_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_29_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_30_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +__attribute__((weak)) uint8_t External_31_IRQHandler(void) +{ + return(0U); /* Default handler */ +} + +#else +__attribute__((weak)) void External_IRQHandler(void) +{ +} +__attribute__((weak)) void MGECI_IRQHandler(void) +{ +} +__attribute__((weak)) void MGEUI_IRQHandler(void) +{ +} +__attribute__((weak)) void SUBSYS_IRQHandler(void) +{ +} +__attribute__((weak)) void MSYS_EI0_IRQHandler(void) +{ +} +__attribute__((weak)) void MSYS_EI1_IRQHandler(void) +{ +} +__attribute__((weak)) void MSYS_EI2_IRQHandler(void) +{ +} +__attribute__((weak)) void MSYS_EI3_IRQHandler(void) +{ +} +__attribute__((weak)) void MSYS_EI4_IRQHandler(void) +{ +} +__attribute__((weak)) void MSYS_EI5_IRQHandler(void) +{ +} +__attribute__((weak)) void Reserved_IRQHandler(void) +{ + _exit(10); +} +#ifndef MIV_RV32_V3_0 /* For MIV_RV32 v3.0 */ +__attribute__((weak)) void MSYS_EI6_IRQHandler(void) +{ +} +__attribute__((weak)) void MSYS_EI7_IRQHandler(void) +{ +} +__attribute__((weak)) void SUBSYSR_IRQHandler(void) +{ +} +#endif /* MIV_RV32_V3_0 */ +#endif /* MIV_LEGACY_RV32 */ + +#ifdef __cplusplus +} +#endif diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_subsys.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_subsys.h new file mode 100644 index 0000000..509f13b --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_subsys.h @@ -0,0 +1,293 @@ +/******************************************************************************* + * Copyright 2023 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file miv_rv32_subsys.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Mi-V soft processor SUBSYS regsiter description and API fuctions. + * + */ +#ifndef MIV_RV32_SUBSYS_H +#define MIV_RV32_SUBSYS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef MIV_LEGACY_RV32 +#define MGEUI 16U +#define MGECI 17U +#define MSYS_EI0 24U +#define MSYS_EI1 25U +#define MSYS_EI2 26U +#define MSYS_EI3 27U +#define MSYS_EI4 28U +#define MSYS_EI5 29U + +#define MGEUI_MEIP (1u << MGEUI) +#define MGECI_MEIP (1u << MGECI) +#define MSYS_EI0IP (1u << MSYS_EI0) +#define MSYS_EI1IP (1u << MSYS_EI1) +#define MSYS_EI2IP (1u << MSYS_EI2) +#define MSYS_EI3IP (1u << MSYS_EI3) +#define MSYS_EI4IP (1u << MSYS_EI4) +#define MSYS_EI5IP (1u << MSYS_EI5) + +#define MIV_LOCAL_IRQ_MIN 16 +#ifndef MIV_RV32_V3_0 /*For MIV_RV32 v3.1*/ +#define SUBSYSR 22U +#define SUBSYS_EI 23U +#define MSYS_EI6 30U +#define MSYS_EI7 31U +#define MIV_SUBSYSR (1u << SUBSYSR) +#define MIV_SUBSYS (1u << SUBSYS_EI) +#define MSYS_EI6IP (1u << MSYS_EI6) +#define MSYS_EI7IP (1u << MSYS_EI7) +#define MIV_LOCAL_IRQ_MAX 31 + +#else /* MIV_RV32_V3_0 */ +#define MIV_LOCAL_IRQ_MAX 29 +#define SUBSYS_EI 30U +#define MIP_SUBSYS_REG (1u << SUBSYS_EI) + +#endif /* MIV_RV32_V3_0 */ +#endif /* MIV_LEGACY_RV32 */ + +#ifdef MIV_LEGACY_RV32 +#define MIV_LOCAL_IRQ_MAX 0U +#define MIV_LOCAL_IRQ_MIN 0U +#endif /* MIV_LEGACY_RV32 */ + +#ifdef __cplusplus +} +#endif + +/*-------------------------------------------------------------------------*//** + SUBSYS Register Configuration + ======================================= + For the SUBSYS registers configutation, the following definitions are used in + the SUBSUS API functions. For example, to raise soft interrupts, enable parity + checks, soft reset, and so on. + + | Configuration | Value | Description | + |--------------------------|-------|------------------------------------------------| + | SUBSYS_SOFT_REG_GRP_DED | 0x04 | Mask for the Core GPR DED Reset Register | + | SUBSYS_CFG_PARITY_CHECK | 0x01 | Use to set or clear the parity check on the TCM | + | SUBSYS_SOFT_RESET | 0x01 | Use the SUBSYS soft reset the MIV_RV32 IP core | + | SUBSYS_SOFT_IRQ | 0x02 | Use to raise a software interrupt through SUBSYS| + */ +/*Mask for the Core GPR DED Reset Register*/ +#define SUBSYS_SOFT_REG_GRP_DED 0x04U + +/*Use to set or clear the parity check on the TCM*/ +#define SUBSYS_CFG_PARITY_CHECK 0x01U + +/*Use the SUBSYS soft reset the MIV_RV32 IP core*/ +#define SUBSYS_SOFT_RESET 0x01U + +/*Use to raise a software interrupt through SUBSYS*/ +#define SUBSYS_SOFT_IRQ 0x02U + +/*-------------------------------------------------------------------------*//** + SUBSYS Interrupt Request Masks + ======================================= + The following values correspond to the bit value of the SUBSYS interrupt + enable and interrupt pending register. + + | Interrupt Mask | Value | Description | + |----------------------------|---------|-------------------------------------| + | SUBSYS_TCM_ECC_CE_IRQ | 0x01u | TCM ECC controllable error irq enable | + | SUBSYS_TCM_ECC_UCE_IRQ | 0x02u | TCM ECC uncontrollable error irq enable | + | SUBSYS_AXI_WR_RESP_IRQ | 0x10u | AXI write response error irq enable | + | SUBSYS_ICACHE_ECC_CE_IRQ | 0x40u | Icache ECC Correctable error irq | + | SUBSYS_ICACHE_ECC_UCE_IRQ | 0x80u | Icache ECC Uncorrectable error irq | + | SUBSYS_BASE_ADDR | 0x6000u | Base address of the SUBSYS | + */ +/* TCM ECC correctable error irq enable mask value */ +#define SUBSYS_TCM_ECC_CE_IRQ 0x01u + +/* TCMECC uncorrectable error irq enable */ +#define SUBSYS_TCM_ECC_UCE_IRQ 0x02u + +/* AXI write response error irq enable */ +#define SUBSYS_AXI_WR_RESP_IRQ 0x10u + +/*Icache ECC Correctable error irq*/ +#define SUBSYS_ICACHE_ECC_CE_IRQ 0x40u + +/*Icache ECC Uncorrectable error irq*/ +#define SUBSYS_ICACHE_ECC_UCE_IRQ 0x80u + +/*Base address of the SUBSYS*/ +#define SUBSYS_BASE_ADDR 0x00006000UL + +/***************************************************************************//** + Subsys contains interrupt enable, interrupt pending and Subsys registers + which can be used to enable SUBSYS specific features such as ECC for vaious + memories. For more available features refer to the MIV_RV32 User Guide +*/ +typedef struct +{ + volatile uint32_t cfg; /*Parity is not supported by MIV_RV32 v3.1 and MIV_RV32 v3.0.100*/ + volatile uint32_t reserved0[3]; + volatile uint32_t irq_en; /*offset 0x10*/ + volatile uint32_t irq_pend; + volatile uint32_t reserved1[2]; + volatile uint32_t soft_reg; /*offset 0x20*/ +} SUBSYS_Type; + +#define SUBSYS ((SUBSYS_Type *)SUBSYS_BASE_ADDR) + +/***************************************************************************//** + The MRV32_subsys_enable_irq() function initializes the SUBSYS interrupts. It + takes the logical OR of the following defined IRQ masks as a parameter. + + @param irq_mask + | irq_mask | Value | + |------------------------|------------| + | SUBSYS_TCM_ECC_CE_IRQ | 0x01u | + | SUBSYS_TCM_ECC_UCE_IRQ | 0x02u | + | SUBSYS_AXI_WR_RESP_IRQ | 0x10u | + | SUBSYS_ICACHE_ECC_CE_IRQ | 0x40u | + | SUBSYS_ICACHE_ECC_UCE_IRQ | 0x80u | + Use logical OR values of one or more interrupts to enable them in the SUBSYS + @return + This function does not return any value. + */ +static inline void MRV32_subsys_enable_irq(uint32_t irq_mask) +{ + SUBSYS->irq_en |= irq_mask; +} + +/***************************************************************************//** + The MRV32_subsys_disable_irq() function disables the SUBSYS interrupts. It + takes the logical OR of the following defined IRQ masks as a parameter. + + @param irq_mask + | irq_mask | Value | + |------------------------|------------| + | SUBSYS_TCM_ECC_CE_IRQ | 0x01u | + | SUBSYS_TCM_ECC_UCE_IRQ | 0x02u | + | SUBSYS_AXI_WR_RESP_IRQ | 0x10u | + | SUBSYS_ICACHE_ECC_CE_IRQ | 0x40u | + | SUBSYS_ICACHE_ECC_UCE_IRQ | 0x80u | + Use logical OR values of one or more interrupts to disable them in the SUBSYS + @return + This function does not return any value. + */ +static inline void MRV32_subsys_disable_irq(uint32_t irq_mask) +{ + SUBSYS->irq_en &= ~irq_mask; +} + +/***************************************************************************//** + The MRV32_subsys_clear_irq() function clears the SUBSYS interrupts, which was + triggered. It takes the logical OR of the following defined IRQ masks as a + parameter. + @param irq_mask + | irq_mask | Value | + |------------------------|------------| + | SUBSYS_TCM_ECC_CE_IRQ | 0x01u | + | SUBSYS_TCM_ECC_UCE_IRQ | 0x02u | + | SUBSYS_AXI_WR_RESP_IRQ | 0x10u | + | SUBSYS_ICACHE_ECC_CE_IRQ | 0x40u | + | SUBSYS_ICACHE_ECC_UCE_IRQ | 0x80u | + Use logical OR values of one or more interrupts to disable them in the SUBSYS + @return + This function does not return any value. + */ +static inline void MRV32_subsys_clear_irq(uint32_t irq_mask) +{ + SUBSYS->irq_pend |= irq_mask; +} + +/***************************************************************************//** + The MRV32_subsys_irq_cause() function returns the irq_pend register value which + is present in the SUBSYS. This is be used to check which irq_mask value + caused the SUBSYS interrupt to occur. + @param + This function does not take any parameters + + @return + This function returns the irq_pend regsiter value. +*/ +static inline uint32_t MRV32_subsys_irq_cause() +{ + return SUBSYS->irq_pend; +} + +/***************************************************************************//** + The MRV32_is_gpr_ded() function returns the core_gpr_ded_reset_reg bit value. + When ECC is enabled, the core_gpr_ded_reset_reg is set when the core was + reset due to GPR DED error. + @param + This function does not take any parameters. + + @return + This functions returns the CORE_GPR_DED_RESET_REG bit value. + */ +static inline uint32_t MRV32_is_gpr_ded(void) +{ + return((SUBSYS->soft_reg & SUBSYS_SOFT_REG_GRP_DED) >> 0x02u); +} + +/***************************************************************************//** + The MRV32_clear_gpr_ded() function must be used to clear the + core_gpr_ded_reset_reg bit. When ECC is enabled, the core_gpr_ded_reset_reg is + set when the core was previously reset due to GPR DED error. + @param + This function does not take any parameters. + + @return + This function does not return any value. + */ +static inline void MRV32_clear_gpr_ded(void) +{ + SUBSYS->soft_reg &= ~SUBSYS_SOFT_REG_GRP_DED; +} + +/***************************************************************************//** + The MRV32_enable_parity_check() function is used to enable parity check on + the TCM and it's interface transactions. This feature is not available on + MIV_RV32 v3.1 and MIV_RV32 v3.0.100 + @param + This function does not take any parameters. + + @return + This function does not return any value. + */ +static inline void MRV32_enable_parity_check(void) +{ + SUBSYS->cfg |= SUBSYS_CFG_PARITY_CHECK; +} + +/***************************************************************************//** + The MRV32_disable_parity_check() function is used to disable parity check on + the TCM and it's interface transactions. + @param + This function does not take any parameters + + @return + This function does not return any value. + + */ +static inline void MRV32_disable_parity_check(void) +{ + SUBSYS->cfg &= ~SUBSYS_CFG_PARITY_CHECK; +} + +/***************************************************************************//** + The MRV32_cpu_soft_reset() function is used to cause a soft cpu reset on + the MIV_RV32 soft processor core. + @param + This function does not take any parameters. + + @return + This function does not return any value. + */ +static inline void MRV32_cpu_soft_reset(void) +{ + SUBSYS->soft_reg |= SUBSYS_SOFT_RESET; +} +#endif /* MIV_RV32_SUBSYS_H */ \ No newline at end of file diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_syscall.c b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_syscall.c new file mode 100644 index 0000000..bd2f881 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/miv_rv32_syscall.c @@ -0,0 +1,364 @@ +/******************************************************************************* + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file miv_rv32_syscall.c + * @author Microchip FPGA Embedded Systems Solutions + * @brief Stubs for system calls. + * + */ +#include +#include +#include +#include +#include "miv_rv32_hal.h" + +#ifdef MSCC_STDIO_THRU_CORE_UART_APB +#include + +#ifndef LEGACY_DIR_STRUCTURE +#include "drivers/fpga_ip/CoreUARTapb/core_uart_apb.h" +#else +#include "core_uart_apb.h" +#endif + +#endif /*MSCC_STDIO_THRU_CORE_UART_APB*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef MSCC_STDIO_THRU_CORE_UART_APB + +/*------------------------------------------------------------------------------ + * CoreUARTapb instance data for the CoreUARTapb instance used for standard + * output. + */ +static UART_instance_t g_stdio_uart; + +/*============================================================================== + * Flag used to indicate if the UART driver needs to be initialized. + */ +static int g_stdio_uart_init_done = 0; + +/* + * Disable semihosting apis + */ +#pragma import(__use_no_semihosting_swi) + +/*============================================================================== + * sendchar() + */ +int sendchar(int ch) +{ + /*-------------------------------------------------------------------------- + * Initialize the UART driver if it is the first time this function is + * called. + */ + if ( !g_stdio_uart_init_done ) + { + /****************************************************************************** + * Baud value: + * This value is calculated using the following equation: + * BAUD_VALUE = (CLOCK / (16 * BAUD_RATE)) - 1 + *****************************************************************************/ + UART_init( &g_stdio_uart, + MSCC_STDIO_UART_BASE_ADDR, + ((SYS_CLK_FREQ/(16 * MSCC_STDIO_BAUD_VALUE))-1), + (DATA_8_BITS | NO_PARITY)); + + g_stdio_uart_init_done = 1; + } + + /*-------------------------------------------------------------------------- + * Output text to the UART. + */ + UART_send( &g_stdio_uart, (uint8_t *)&ch, 1 ); + + return (ch); +} + +/*============================================================================== + * getachar() + */ +int getachar(void) +{ + uint8_t rx_size; + uint8_t rx_byte; + + if ( !g_stdio_uart_init_done ) + { + /****************************************************************************** + * Baud value: + * This value is calculated using the following equation: + * BAUD_VALUE = (CLOCK / (16 * BAUD_RATE)) - 1 + *****************************************************************************/ + UART_init( &g_stdio_uart, + MSCC_STDIO_UART_BASE_ADDR, + ((SYS_CLK_FREQ/(16 * MSCC_STDIO_BAUD_VALUE))-1), + (DATA_8_BITS | NO_PARITY)); + + g_stdio_uart_init_done = 1; + } + + do + { + rx_size = UART_get_rx(&g_stdio_uart, &rx_byte, 1); + } while(0u == rx_size); + + return rx_byte; +} + +#endif /*MSCC_STDIO_THRU_CORE_UART_APB*/ + +#undef errno +static int errno; + +static char *__env[1] = { 0 }; +char **environ = __env; + +void write_hex(int fd, uint32_t hex) +{ + char towrite; + + write( fd , "0x", 2U ); + + for (uint32_t ii = 8U ; ii > 0U; ii--) + { + uint32_t jj = ii-1U; + uint8_t digit = ((hex & (0xFU << (jj*4U))) >> (jj*4U)); + towrite = digit < 0xAU ? (0x48U + digit) : (0x65U + (digit - 0xAU)); + write( fd, &towrite, 1U); + } +} + + +#ifdef GDB_TESTING +void __attribute__((optimize("O0"))) _exit(int code) +#else +void _exit(int code) +#endif +{ +#ifdef MSCC_STDIO_THRU_CORE_UART_APB + const char * message = "\nProgam has exited with code:"; + + write(STDERR_FILENO, message, strlen(message)); + write_hex(STDERR_FILENO, code); +#endif + + while (1){}; +} + +void *_sbrk(ptrdiff_t incr) +{ + extern char _end; + extern char _heap_end; + extern char __heap_start; + extern char __heap_end; + static char *curbrk = &_end; + void * ret = NULL; + + /* + * Did we allocated memory for the heap in the linker script? + * You need to set HEAP_SIZE to a non-zero value in your linker script if + * the following assertion fires. + */ + ASSERT(&__heap_end > &__heap_start); + + if (((curbrk + incr) < &_end) || ((curbrk + incr) > &_heap_end)) + { + errno = ENOMEM; + ret = ((char *) - 1); + } + else + { + curbrk += incr; + ret = curbrk - incr; + } + + /* + * Did we run out of heap? + * You need to increase the heap size in the linker script if the following + * assertion fires. + * */ + ASSERT(curbrk <= &__heap_end); + + return(ret); +} + +int _isatty(int fd) +{ + int ret = 0; + + if (fd <= 2) /* one of stdin, stdout, stderr */ + { + ret = 1; + } + else + { + errno = EBADF; + ret = 0; + } + + return(ret); +} + +static int stub(int err) +{ + errno = err; + return -1; +} + +int _open(const char* name, int flags, int mode) +{ + return stub(ENOENT); +} + +int _openat(int dirfd, const char* name, int flags, int mode) +{ + return stub(ENOENT); +} + +int _close(int fd) +{ + return stub(EBADF); +} + +int _execve(const char* name, char* const argv[], char* const env[]) +{ + return stub(ENOMEM); +} + +int _fork(void) +{ + return stub(EAGAIN); +} + +int _fstat(int fd, struct stat *st) +{ + int ret = 0; + + if (isatty(fd)) + { + st->st_mode = S_IFCHR; + ret = 0; + } + else + { + ret = stub(EBADF); + } + + return ret; +} + +int _getpid(void) +{ + return 1; +} + +int _kill(int pid, int sig) +{ + return stub(EINVAL); +} + +int _link(const char *old_name, const char *new_name) +{ + return stub(EMLINK); +} + +off_t _lseek(int fd, off_t ptr, int dir) +{ + off_t ret = 0; + if (_isatty(fd)) + { + ret = 0; + } + else + { + ret = stub(EBADF); + } + + return ret; +} + +ssize_t _read(int fd, void* ptr, size_t len) +{ +#ifdef MSCC_STDIO_THRU_CORE_UART_APB + + char* ptr1 = (char*)ptr; + + if (_isatty(fd)) + { + int count; + + for (count = 0; count < len; count++) + { + ptr1[count] = getachar(); + sendchar(ptr1[count]); + + /* Return partial buffer if we get EOL */ + if (('\r' == ptr1[count])||('\n' == ptr1[count])) + { + ptr1[count] = '\n'; + return count; + } + } + + return count; /* Filled the buffer */ + } +#endif + + return stub(EBADF); +} + +int _stat(const char* file, struct stat* st) +{ + return stub(EACCES); +} + +clock_t _times(struct tms* buf) +{ + return stub(EACCES); +} + +int _unlink(const char* name) +{ + return stub(ENOENT); +} + +int _wait(int* status) +{ + return stub(ECHILD); +} + +ssize_t _write(int fd, const void* ptr, size_t len) +{ +#ifdef MSCC_STDIO_THRU_CORE_UART_APB + int count_out; + char* ptr1 = (char*)ptr; + + /*-------------------------------------------------------------------------- + * Output text to the UART. + */ + count_out = 0; + while(len--) + { + sendchar(ptr1[count_out]); + count_out++; + } + + errno = 0; + return count_out; + +#else /* MSCC_STDIO_THRU_CORE_UART_APB */ + + return stub(EBADF); + +#endif /* MSCC_STDIO_THRU_CORE_UART_APB */ + +} + +#ifdef __cplusplus +} +#endif diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/sample_fpga_design_config.h b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/sample_fpga_design_config.h new file mode 100644 index 0000000..a380d96 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/miv_rv32_hal/sample_fpga_design_config.h @@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright 2022 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * @file sample_fpga_design_config.h + * @author Microchip FPGA Embedded Systems Solutions + * @brief Sample design configuration settings + * + */ + /*========================================================================*//** + @mainpage + Example file detailing how the fpga_design_config.h should be constructed + for the SoftConsole project targeted for Mi-V processors. + + @section intro_sec Introduction + The SoftConsole project targeted for Mi-V processors now have an improved + folder structure. Detailed description of the folder structure is available + at https://github.com/Mi-V-Soft-RISC-V/miv-rv32-documentation. + + The fpga_design_config.h must be stored as shown below + /boards//fpga_design_config.h + + Currently this file must be hand crafted when using the Mi-V Soft Processor. + In future, all the design and soft IP configurations will be automatically + generated from the Libero design description data. + + You can use this sample file as an example. + Rename this file from sample_fpga_design_config.h to fpga_design_config.h + and then customize it per your hardware design. + + @section Project configuration Instructions + 1. Change SYS_CLK_FREQ define to frequency of Mi-V Soft processor clock + 2 Add all the soft IP core BASE addresses + 3. Add the peripheral Core Interrupts to Mi-V Soft processor IRQ number + mappings + 4. Define MSCC_STDIO_UART_BASE_ADDR if you want a CoreUARTapb mapped to + STDIO + + **NOTE** + In the legacy folder structures, the file hw_config.h as was used at the + root of the project folder. This file is now depricated. + +*//*=========================================================================*/ + +#ifndef FPGA_DESIGN_CONFIG_H_ +#define FPGA_DESIGN_CONFIG_H_ + +/***************************************************************************//** + * Soft-processor clock definition + * This is the only clock brought over from the Mi-V Libero design. + */ +#ifndef SYS_CLK_FREQ +#define SYS_CLK_FREQ 50000000UL +#endif + +/***************************************************************************//** + * Peripheral base addresses. + * Format of define is: + * __BASE_ADDR + * The field is optional if there is only one instance of the core + * in the design + * MIV_ESS is an extended peripheral subsystem IP core with peripherals + * connections as defined below. + * The system can be further extended by attaching APB peripherals to the + * empty APB slots. + */ +#define MIV_ESS_PLIC_BASE_ADDR 0x70000000UL +#define COREUARTAPB0_BASE_ADDR 0x71000000UL +#define MIV_MTIMER_BASE_ADDR 0x72000000UL +#define MIV_ESS_APBSLOT3_BASE_ADDR 0x73000000UL +#define MIV_ESS_APBSLOT4_BASE_ADDR 0x74000000UL +#define COREGPIO_OUT_BASE_ADDR 0x75000000UL +#define CORESPI_BASE_ADDR 0x76000000UL +#define MIV_ESS_uDMA_BASE_ADDR 0x78000000UL +#define MIV_ESS_WDOG_BASE_ADDR 0x79000000UL +#define MIV_ESS_I2C_BASE_ADDR 0x7A000000UL +#define MIV_ESS_APBSLOTB_BASE_ADDR 0x7B000000UL +#define MIV_ESS_APBSLOTC_BASE_ADDR 0x7C000000UL +#define MIV_ESS_APBSLOTD_BASE_ADDR 0x7D000000UL +#define MIV_ESS_APBSLOTE_BASE_ADDR 0x7E000000UL +#define MIV_ESS_APBSLOTF_BASE_ADDR 0x7F000000UL + +/***************************************************************************//** + * Peripheral Interrupts are mapped to the corresponding Mi-V Soft processor + * interrupt in the Libero design. + * + * On the legacy RV32 cores, there can be up to 31 external interrupts (IRQ[30:0] + * pins). The legacy RV32 Soft processor external interrupts are defined in the + miv_rv32_plic.h + * + * These are of the form + * typedef enum +{ + NoInterrupt_IRQn = 0, + External_1_IRQn = 1, + External_2_IRQn = 2, + . + . + . + External_31_IRQn = 31 +} IRQn_Type; + + On the legacy RV32 processors, the PLIC identifies the interrupt and passes it + on to the processor core. The interrupt 0 is not used. The pin IRQ[0] should + map to External_1_IRQn likewise IRQ[30] should map to External_31_IRQn + +e.g + +#define TIMER0_IRQn External_30_IRQn +#define TIMER1_IRQn External_31_IRQn + + The MIV_RV32 soft processor has up to six optional system interrupts, MSYS_EI[n] + in addition to one EXT_IRQ. + The MIV_RV32 does not have an inbuilt PLIC and all the interrupts are directly + delivered to the processor core, hence unlike legacy RV32 cores, no interrupt + number mapping is necessary on MIV_RV32 core. + */ + +/**************************************************************************** + * Baud value to achieve a 115200 baud rate with system clock defined by + * SYS_CLK_FREQ. + * This value is calculated using the following equation: + * BAUD_VALUE = (CLOCK / (16 * BAUD_RATE)) - 1 + *****************************************************************************/ +#define BAUD_VALUE_115200 ((SYS_CLK_FREQ / (16 * 115200)) - 1) + +/****************************************************************************** + * Baud value to achieve a 57600 baud rate with system clock defined by + * SYS_CLK_FREQ. + * This value is calculated using the following equation: + * BAUD_VALUE = (CLOCK / (16 * BAUD_RATE)) - 1 + *****************************************************************************/ + #define BAUD_VALUE_57600 ((SYS_CLK_FREQ / (16 * 57600)) - 1) + +/***************************************************************************//** + * Define MSCC_STDIO_THRU_CORE_UART_APB in the project settings if you want the + * standard IOs to be redirected to a terminal via UART. + */ +#ifdef MSCC_STDIO_THRU_CORE_UART_APB +/* + * A base address mapping for the STDIO printf/scanf mapping to CortUARTapb + * must be provided if it is being used + * + * e.g. #define MSCC_STDIO_UART_BASE_ADDR COREUARTAPB1_BASE_ADDR + */ +#define MSCC_STDIO_UART_BASE_ADDR COREUARTAPB0_BASE_ADDR + +#ifndef MSCC_STDIO_UART_BASE_ADDR +#error MSCC_STDIO_UART_BASE_ADDR not defined- e.g. #define MSCC_STDIO_UART_BASE_ADDR COREUARTAPB1_BASE_ADDR +#endif + +#ifndef MSCC_STDIO_BAUD_VALUE +/* + * The MSCC_STDIO_BAUD_VALUE define should be set in your project's settings to + * specify the baud value used by the standard output CoreUARTapb instance for + * generating the UART's baud rate if you want a different baud rate from the + * default of 115200 baud + */ +#define MSCC_STDIO_BAUD_VALUE 115200 +#endif /*MSCC_STDIO_BAUD_VALUE*/ + +#endif /* end of MSCC_STDIO_THRU_CORE_UART_APB */ +/******************************************************************************* + * End of user edit section + */ +#endif /* FPGA_DESIGN_CONFIG_H_ */ diff --git a/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/platform_config_reference/linker/miv-rv32-ram.ld b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/platform_config_reference/linker/miv-rv32-ram.ld new file mode 100644 index 0000000..53076a0 --- /dev/null +++ b/driver-examples/CoreMMC/miv-rv32-coremmc-mmc_read_back/src/platform/platform_config_reference/linker/miv-rv32-ram.ld @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright 2019 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * file name : miv-rv32-ram.ld + * Mi-V soft processor linker script for creating a SoftConsole downloadable + * debug image executing in SRAM. + * + * This linker script assumes that a RAM is connected at on Mi-V soft processor + * memory space pointed by the reset vector address. + * + * NOTE : Modify the memory section address and the size according to your + * Libero design. + * For example: + * 1) If you want to download and step debug at a different RAM memory address in + * your design (For example TCM base address) than the one provided in this file. + * 2) The MIV_RV32, when used with MIV_ESS IP, provides ways to copy the executable + * HEX file from external Non-Volatile memory into the TCM at reset. In this + * case your executable must be linked to the TCM address. + * + * To know more about the memory map of the MIV_RV32 based Libero design, open + * the MIV_RV32 IP configurator and look for "Reset Vector Address" and the + * "Memory Map" tab. + * + */ + +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +MEMORY +{ + ram (rwx) : ORIGIN = 0x80000000, LENGTH = 32k +} + +STACK_SIZE = 2k; /* needs to be calculated for your application */ +HEAP_SIZE = 1k; /* needs to be calculated for your application */ + +SECTIONS +{ + .entry : ALIGN(0x10) + { + KEEP (*(SORT_NONE(.entry))) + . = ALIGN(0x10); + } > ram + + .text : ALIGN(0x10) + { + *(.text .text.* .gnu.linkonce.t.*) + *(.plt) + . = ALIGN(0x10); + + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*crtend.o(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*crtend.o(.dtors)) + + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.gcc_except_table) + *(.eh_frame_hdr) + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + . = ALIGN(0x10); + + } > ram + + /* short/global data section */ + .sdata : ALIGN(0x10) + { + __sdata_load = LOADADDR(.sdata); + __sdata_start = .; + PROVIDE( __global_pointer$ = . + 0x800); + *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) + *(.srodata*) + *(.sdata .sdata.* .gnu.linkonce.s.*) + . = ALIGN(0x10); + __sdata_end = .; + } > ram + + /* data section */ + .data : ALIGN(0x10) + { + __data_load = LOADADDR(.data); + __data_start = .; + *(.got.plt) *(.got) + *(.shdata) + *(.data .data.* .gnu.linkonce.d.*) + . = ALIGN(0x10); + __data_end = .; + } > ram + + /* sbss section */ + .sbss : ALIGN(0x10) + { + __sbss_start = .; + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + . = ALIGN(0x10); + __sbss_end = .; + } > ram + + /* sbss section */ + .bss : ALIGN(0x10) + { + __bss_start = .; + *(.shbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(0x10); + __bss_end = .; + } > ram + + /* End of uninitialized data segment */ + _end = .; + + .heap : ALIGN(0x10) + { + __heap_start = .; + . += HEAP_SIZE; + __heap_end = .; + . = ALIGN(0x10); + _heap_end = __heap_end; + } > ram + + .stack : ALIGN(0x10) + { + __stack_bottom = .; + . += STACK_SIZE; + __stack_top = .; + } > ram +} +