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
+}
+