DRAMClickboard/DRAMClickboard_HighLevelApp/dram.c (348 lines of code) (raw):

/* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. */ #include "dram.h" // Dummy data #define DUMMY 0x00 // File descriptors - initialized to an invalid value static int spiFd = -1; int dram_init(int spi_interface, int cs_pin, int io3, int io2) { // Create SPI config object SPIMaster_Config spi_cfg; // Initialize SPI config object int ret = SPIMaster_InitConfig(&spi_cfg); if (ret != 0) { Log_Debug("ERROR: SPIMaster_InitConfig = %d errno = %s (%d)\n", ret, strerror(errno), errno); return -1; } // Set polarity spi_cfg.csPolarity = SPI_ChipSelectPolarity_ActiveLow; // Open SPI Master peripheral based on the interface specified spiFd = SPIMaster_Open(spi_interface, cs_pin, &spi_cfg); if (spiFd == -1) { Log_Debug("ERROR: SPIMaster_Open: errno=%d (%s)\n", errno, strerror(errno)); return -1; } // Set bus speed for SPI Master int result = SPIMaster_SetBusSpeed(spiFd, 39999999); // MT3620 SPI bus speed < 40MHz if (result != 0) { Log_Debug("ERROR: SPIMaster_SetBusSpeed: errno=%d (%s)\n", errno, strerror(errno)); return -1; } // Set mode of SPI communication, SPI_Mode_2 for Avnet rev1 kit // and SPI_Mode_0 for Avnet rev2 kit result = SPIMaster_SetMode(spiFd, SPI_Mode_2); if (result != 0) { Log_Debug("ERROR: SPIMaster_SetMode: errno=%d (%s)\n", errno, strerror(errno)); return -1; } // Configuring DRAM pins for QPI GPIO_OpenAsOutput(io3, GPIO_OutputMode_PushPull, GPIO_Value_Low); GPIO_OpenAsOutput(io2, GPIO_OutputMode_PushPull, GPIO_Value_Low); int exitCode = dram_reset(); // 50ns sleep before next command if (exitCode != 0) { Log_Debug("Software reset failure!\n"); return exitCode; } exitCode = dram_check_communication(); if (exitCode != 0) { Log_Debug("Communication check failure!\n"); return exitCode; } // DRAM chip default linear_burst_mode = true; max_per_transfer = 1000; return 0; } int dram_memory_write(uint32_t address, uint8_t *data_in, uint32_t len) { // Check validity of write address if ((data_in == NULL) || (address > DRAM_MAX_ADDRESS)) { Log_Debug("ERROR: No data input OR invalid address\n"); return -1; } // Check if payload size is valid if (len > DRAM_MAX_ADDRESS) { Log_Debug("ERROR: Data size exceeds chip size (8M)"); return -1; } // Check if overflow will occur if ((address + len - 1) > DRAM_MAX_ADDRESS) { unsigned long int ovf_addr = (address + len - 1) % DRAM_MAX_ADDRESS; Log_Debug("Overflow write! Data terminates at at %#08X\n", ovf_addr); } // 2 transfers per Transfer Sequential call (command & address bytes and data bytes) SPIMaster_Transfer transfers_array[2]; // init transfers int result = SPIMaster_InitTransfers(transfers_array, 2); if (result != 0) { Log_Debug("ERROR: SPIMaster_Open (dram_memory_write): errno=%d (%s)\n", errno, strerror(errno)); return -1; } // Mandatory transfer to signal a write to the starting address uint8_t data_buf[4] = {0}; data_buf[0] = DRAM_CMD_WRITE; data_buf[1] = (uint8_t)((address >> 16) & 0xFF); data_buf[2] = (uint8_t)((address >> 8) & 0xFF); data_buf[3] = (uint8_t)(address & 0xFF); // "write_data" is a pointer to an address on the Azure Sphere (not DRAM) that indicates where // to begin the transfer from const uint8_t *writeData; // Initiate transfer ssize_t tb; // "length" is an integer value of how many bytes to read starting from the "writeData" address size_t t_length; // Max number of data bytes per transfer uint32_t temp_max = max_per_transfer - sizeof(data_buf); // Number of calls to SPIMaster_Transfer_Sequential (transfer) depends of max bytes per transfer // (1000 for Linear Burst and 4096 for Wrap32) size_t transfer_count = (len % temp_max == 0) ? (len / temp_max) : ((len / temp_max) + 1); //(subtracting 4 because the first four bytes of each transfer // is dedicated to the command & address bytes) // Data address offset uint32_t offset = 0; // Number of bytes left to transfer uint32_t sizeLeft = len; // Add the rest of the bytes to the initial transfer (max_per_transfer - 4) for (uint16_t i = 1; i <= transfer_count; i++) { transfers_array[0].flags = SPI_TransferFlags_Write; // Signal a write transfers_array[0].writeData = data_buf; // Add data to write buff property transfers_array[0].length = sizeof(data_buf); // 24 bit address + 4 bit command code // Signal a write on the SPI bus transfers_array[1].flags = SPI_TransferFlags_Write; // Start writing from starting address + offset writeData = data_in + offset; transfers_array[1].writeData = writeData; // Data size for transfer is either temp max or sizeLeft % temp_max if (sizeLeft > temp_max) { t_length = temp_max; transfers_array[1].length = t_length; // Starting address (on DRAM) for the next transfer is the address immediately after the // last transfer address += temp_max; // Subtract transfer length from the number of bytes left to send sizeLeft -= temp_max; // Adjust the offset to start the data transfer at the the next byte after the maximum // transfer length (pointer arithmetic) offset += temp_max; } else { t_length = sizeLeft; transfers_array[1].length = t_length; } // number of bytes transferred tb = SPIMaster_TransferSequential(spiFd, transfers_array, 2); // Check transfer size if (!CheckTransferSize("SPIMaster_TransferSequential (dram_memory_write)", (sizeof(data_buf) + t_length), tb)) { return -1; } } return 0; } int dram_memory_read(uint32_t address, uint8_t *data_out, uint32_t len) { if ((data_out == NULL) || (address > DRAM_MAX_ADDRESS)) { Log_Debug("ERROR: No data input OR invalid address\n"); return -1; } if (len > DRAM_MAX_ADDRESS) { Log_Debug("ERROR: Data size exceeds chip size (8M)"); return -1; } if ((address + len - 1) > DRAM_MAX_ADDRESS) { unsigned long int ovf_addr = (address + len - 1) % DRAM_MAX_ADDRESS; Log_Debug("Overflow read! Data terminates at at %#08X\n", ovf_addr); } SPIMaster_Transfer transfers_array[2]; int result = SPIMaster_InitTransfers(transfers_array, 2); if (result != 0) { Log_Debug("ERROR: SPIMaster_Open (dram_memory_read): errno=%d (%s)\n", errno, strerror(errno)); return -1; } uint8_t data_buf[4] = {0}; data_buf[0] = DRAM_CMD_READ; data_buf[1] = (uint8_t)((address >> 16) & 0xFF); data_buf[2] = (uint8_t)((address >> 8) & 0xFF); data_buf[3] = (uint8_t)(address & 0xFF); uint8_t *readData; ssize_t tb; size_t t_length; uint32_t temp_max = max_per_transfer - sizeof(data_buf); size_t transfer_count = (len % temp_max == 0) ? (len / temp_max) : ((len / temp_max) + 1); uint32_t offset = 0; uint32_t sizeLeft = len; for (uint16_t i = 1; i <= transfer_count; i++) { transfers_array[0].flags = SPI_TransferFlags_Write; transfers_array[0].writeData = data_buf; transfers_array[0].length = sizeof(data_buf); transfers_array[1].flags = SPI_TransferFlags_Read; readData = data_out + offset; transfers_array[1].readData = readData; if (sizeLeft > temp_max) { t_length = temp_max; transfers_array[1].length = t_length; address += temp_max; sizeLeft -= temp_max; offset += temp_max; } else { t_length = sizeLeft; transfers_array[1].length = t_length; } result = SPIMaster_SetBusSpeed(spiFd, 33000000); if (result != 0) { Log_Debug("ERROR: SPIMaster_SetBusSpeed: errno=%d (%s)\n", errno, strerror(errno)); return -1; } tb = SPIMaster_TransferSequential(spiFd, transfers_array, 2); result = SPIMaster_SetBusSpeed(spiFd, 39999999); if (result != 0) { Log_Debug("ERROR: SPIMaster_SetBusSpeed: errno=%d (%s)\n", errno, strerror(errno)); return -1; } if (!CheckTransferSize("SPIMaster_TransferSequential (dram_memory_read)", (sizeof(data_buf) + t_length), tb)) { return -1; } } return 0; } int dram_memory_read_fast(uint32_t address, uint8_t *data_out, uint32_t len) { if ((data_out == NULL) || (address > DRAM_MAX_ADDRESS)) { Log_Debug("ERROR: No data input OR invalid address\n"); return -1; } if (len > DRAM_MAX_ADDRESS) { Log_Debug("ERROR: Data size exceeds chip size (8M)"); return -1; } if ((address + len - 1) > DRAM_MAX_ADDRESS) { unsigned long int ovf_addr = (address + len - 1) % DRAM_MAX_ADDRESS; Log_Debug("Overflow read! Data terminates at at %#08X\n", ovf_addr); } SPIMaster_Transfer transfers_array[2]; int result = SPIMaster_InitTransfers(transfers_array, 2); if (result < 0) { Log_Debug("ERROR: SPIMaster_Open (dram_memory_read_fast): errno=%d (%s)\n", errno, strerror(errno)); return -1; } uint8_t data_buf[5] = {0}; data_buf[0] = DRAM_CMD_FAST_READ; data_buf[1] = (uint8_t)((address >> 16) & 0xFF); data_buf[2] = (uint8_t)((address >> 8) & 0xFF); data_buf[3] = (uint8_t)(address & 0xFF); data_buf[4] = DUMMY; uint8_t *readData; ssize_t tb; size_t t_length; uint32_t temp_max = max_per_transfer - sizeof(data_buf); size_t transfer_count = (len % temp_max == 0) ? (len / temp_max) : ((len / temp_max) + 1); uint32_t offset = 0; uint32_t sizeLeft = len; for (uint16_t i = 1; i <= transfer_count; i++) { transfers_array[0].flags = SPI_TransferFlags_Write; transfers_array[0].writeData = data_buf; transfers_array[0].length = sizeof(data_buf); transfers_array[1].flags = SPI_TransferFlags_Read; readData = data_out + offset; transfers_array[1].readData = readData; if (sizeLeft > temp_max) { t_length = temp_max; transfers_array[1].length = t_length; address += temp_max; sizeLeft -= temp_max; offset += temp_max; } else { t_length = sizeLeft; transfers_array[1].length = t_length; } tb = SPIMaster_TransferSequential(spiFd, transfers_array, 2); if (!CheckTransferSize("SPIMaster_TransferSequential (dram_memory_read_fast)", (sizeof(data_buf) + t_length), tb)) { return -1; } } return 0; } int dram_reset() { const size_t transferCount = 1; SPIMaster_Transfer transfer1[transferCount]; SPIMaster_Transfer transfer2[transferCount]; // init transfers int result = SPIMaster_InitTransfers(transfer1, transferCount); if (result != 0) { Log_Debug("ERROR: SPIMaster_InitTransfers (dram_reset transfer1) errno=%d (%s)\n", errno, strerror(errno)); return -1; } result = SPIMaster_InitTransfers(transfer2, transferCount); if (result != 0) { Log_Debug("ERROR: SPIMaster_InitTransfers (dram_reset transfer2) errno=%d (%s)\n", errno, strerror(errno)); return -1; } uint8_t data = DRAM_CMD_RESET_ENABLE; uint8_t data2 = DRAM_CMD_RESET; transfer1[0].flags = SPI_TransferFlags_Write; // Signal a write transfer1[0].writeData = &data; // Add data to write buff property transfer1[0].length = 1; transfer2[0].flags = SPI_TransferFlags_Write; // Signal a write transfer2[0].writeData = &data2; // Add data to write buff property transfer2[0].length = 1; ssize_t transferredBytes = SPIMaster_TransferSequential(spiFd, transfer1, transferCount); ssize_t transferredBytes2 = SPIMaster_TransferSequential(spiFd, transfer2, transferCount); if (!CheckTransferSize("SPIMaster_TransferSequential (dram_reset) transfer1", sizeof(data), transferredBytes)) { return -1; } if (!CheckTransferSize("SPIMaster_TransferSequential (dram_reset) transfer2", sizeof(data), transferredBytes2)) { return -1; } // 50ns sleep time is required after reset operation const struct timespec sleepTime = {.tv_sec = 0, .tv_nsec = 50}; nanosleep(&sleepTime, NULL); return 0; } bool dram_toggle_wrap_boundary() { const size_t transferCount = 1; SPIMaster_Transfer transfers[transferCount]; // init transfers int result = SPIMaster_InitTransfers(transfers, transferCount); if (result == -1) { Log_Debug("ERROR: SPIMaster_InitTransfers (dram_toggle_wrap_boundary): errno=%d (%s)\n", errno, strerror(errno)); return -1; } uint8_t data = DRAM_CMD_WRAP_BOUNDARY_TOGGLE; transfers[0].flags = SPI_TransferFlags_Write; transfers[0].writeData = &data; transfers[0].length = 1; ssize_t transferredBytes = SPIMaster_TransferSequential(spiFd, transfers, transferCount); if (!CheckTransferSize("SPIMaster_TransferSequential (dram_toggle_wrap_boundary)", sizeof(data), transferredBytes)) { return -1; } // toggle state variable linear_burst_mode = !linear_burst_mode; if (linear_burst_mode) { Log_Debug("Linear Burst Mode is active.\n"); max_per_transfer = 1000; } else { Log_Debug("Wrap32 Mode is active.\n"); max_per_transfer = 4096; } return 0; } bool dram_read_id(uint8_t *device_id) { if (NULL == device_id) { Log_Debug("Device ID is null\n"); return false; } uint8_t data_buf[4] = {0}; data_buf[0] = DRAM_CMD_READ_ID; data_buf[1] = DUMMY; data_buf[2] = DUMMY; data_buf[3] = DUMMY; ssize_t transferredBytes = SPIMaster_WriteThenRead(spiFd, data_buf, sizeof(data_buf), device_id, sizeof(device_id)); if (!CheckTransferSize("SPIMaster_WriteThenRead (dram_read_id)", sizeof(data_buf) + sizeof(device_id), transferredBytes)) { return false; } return true; } int dram_check_communication() { uint8_t device_id[8] = {0}; if (dram_read_id(device_id)) { if (DRAM_MANUFACTURER_ID == device_id[0]) { return 0; } } return -1; } // Support functions /// <summary> /// Checks the number of transferred bytes for SPI functions and prints an error /// message if the functions failed or if the number of bytes is different than /// expected number of bytes to be transferred. /// </summary> /// <param name="desc"> /// String description to identify where errors happen should they occur /// </param> /// <param name="expectedBytes"> /// Number of bytes that were expected to be transferred /// </param> /// <param name="actualBytes"> /// Number of bytes that were transferred /// </param> /// <returns> /// true on success, or false on failure /// </returns> bool CheckTransferSize(const char *desc, size_t expectedBytes, ssize_t actualBytes) { if (actualBytes < 0) { Log_Debug("ERROR: %s: errno=%d (%s)\n", desc, errno, strerror(errno)); return false; } if (actualBytes != (ssize_t)expectedBytes) { Log_Debug("ERROR: %s: transferred %zd bytes; expected %zd\n", desc, actualBytes, expectedBytes); return false; } return true; }