blob: 0b5f3b412a9143ac744db34daef9f5bd2207262b [file] [log] [blame]
/******************************************************************************
*
* Copyright (C) 2011 - 2015 Xilinx, Inc. All rights reserved.
*
* 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.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* 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
* XILINX 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.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/*****************************************************************************/
/**
* @file xbram_selftest.c
* @addtogroup bram_v4_0
* @{
*
* The implementation of the XBram driver's self test function. This SelfTest
* is only applicable if ECC is enabled.
* If ECC is not enabled then this function will return XST_SUCCESS.
* See xbram.h for more information about the driver.
* Temp change
*
* @note
*
* None
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver Who Date Changes
* ----- ---- -------- -----------------------------------------------
* 1.00a sa 11/24/10 First release
* 3.01a sa 01/13/12 Changed Selftest API from
* XBram_SelfTest(XBram *InstancePtr) to
* XBram_SelfTest(XBram *InstancePtr, u8 IntMask) and
* fixed a problem with interrupt generation for CR 639274
* Modified Selftest example to return XST_SUCCESS when
* ECC is not enabled and return XST_FAILURE when ECC is
* enabled and Control Base Address is zero (CR 636581)
* Modified Selftest to use correct CorrectableCounterBits
* for CR 635655
* Updated to check CorrectableFailingDataRegs in the case
* of LMB BRAM.
* 3.02a sa 04/16/12 Added test of byte and halfword read-modify-write
* 3.03a bss 05/22/13 Added Xil_DCacheFlushRange in InjectErrors API to
* flush the Cache after writing to BRAM (CR #719011)
* </pre>
*****************************************************************************/
/***************************** Include Files ********************************/
#include "xbram.h"
#include "xil_cache.h"
/************************** Constant Definitions ****************************/
#define TOTAL_BITS 39
/**************************** Type Definitions ******************************/
/***************** Macros (Inline Functions) Definitions ********************/
#define RD(reg) XBram_ReadReg(InstancePtr->Config.CtrlBaseAddress, \
XBRAM_ ## reg)
#define WR(reg, data) XBram_WriteReg(InstancePtr->Config.CtrlBaseAddress, \
XBRAM_ ## reg, data)
#define CHECK(reg, data, result) if (result!=XST_SUCCESS || RD(reg)!=data) \
result = XST_FAILURE;
/************************** Variable Definitions ****************************/
static u32 PrngResult;
/************************** Function Prototypes *****************************/
static inline u32 PrngData(u32 *PrngResult);
static inline u32 CalculateEcc(u32 Data);
static void InjectErrors(XBram * InstancePtr, u32 Addr,
int Index1, int Index2, int Width,
u32 *ActualData, u32 *ActualEcc);
/*****************************************************************************/
/**
* Generate a pseudo random number.
*
* @param The PrngResult is the previous random number in the pseudo
* random sequence, also knwon as the seed. It is modified to
* the calculated pseudo random number by the function.
*
* @return The generated pseudo random number
*
* @note None.
*
******************************************************************************/
static inline u32 PrngData(u32 *PrngResult)
{
*PrngResult = *PrngResult * 0x77D15E25 + 0x3617C161;
return *PrngResult;
}
/*****************************************************************************/
/**
* Calculate ECC from Data.
*
* @param The Data Value
*
* @return The calculated ECC
*
* @note None.
*
******************************************************************************/
static inline u32 CalculateEcc(u32 Data)
{
unsigned char c[7], d[32];
u32 Result = 0;
int Index;
for (Index = 0; Index < 32; Index++) {
d[31 - Index] = Data & 1;
Data = Data >> 1;
}
c[0] = d[0] ^ d[1] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[10] ^ d[11] ^
d[13] ^ d[15] ^ d[17] ^ d[19] ^ d[21] ^ d[23] ^ d[25] ^ d[26] ^
d[28] ^ d[30];
c[1] = d[0] ^ d[2] ^ d[3] ^ d[5] ^ d[6] ^ d[9] ^ d[10] ^ d[12] ^
d[13] ^ d[16] ^ d[17] ^ d[20] ^ d[21] ^ d[24] ^ d[25] ^ d[27] ^
d[28] ^ d[31];
c[2] = d[1] ^ d[2] ^ d[3] ^ d[7] ^ d[8] ^ d[9] ^ d[10] ^ d[14] ^
d[15] ^ d[16] ^ d[17] ^ d[22] ^ d[23] ^ d[24] ^ d[25] ^ d[29] ^
d[30] ^ d[31];
c[3] = d[4] ^ d[5] ^ d[6] ^ d[7] ^ d[8] ^ d[9] ^ d[10] ^ d[18] ^
d[19] ^ d[20] ^ d[21] ^ d[22] ^ d[23] ^ d[24] ^ d[25];
c[4] = d[11] ^ d[12] ^ d[13] ^ d[14] ^ d[15] ^ d[16] ^ d[17] ^ d[18] ^
d[19] ^ d[20] ^ d[21] ^ d[22] ^ d[23] ^ d[24] ^ d[25];
c[5] = d[26] ^ d[27] ^ d[28] ^ d[29] ^ d[30] ^ d[31];
c[6] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[6] ^ d[7] ^
d[8] ^ d[9] ^ d[10] ^ d[11] ^ d[12] ^ d[13] ^ d[14] ^ d[15] ^
d[16] ^ d[17] ^ d[18] ^ d[19] ^ d[20] ^ d[21] ^ d[22] ^ d[23] ^
d[24] ^ d[25] ^ d[26] ^ d[27] ^ d[28] ^ d[29] ^ d[30] ^ d[31] ^
c[5] ^ c[4] ^ c[3] ^ c[2] ^ c[1] ^ c[0];
for (Index = 0; Index < 7; Index++) {
Result = Result << 1;
Result |= c[Index] & 1;
}
return Result;
}
/*****************************************************************************/
/**
* Get the expected actual data read in case of uncorrectable errors.
*
* @param The injected data value including errors (if any)
* @param The syndrome (calculated ecc ^ actual ecc read)
*
* @return The actual data value read
*
* @note None.
*
******************************************************************************/
static inline u32 UncorrectableData(u32 Data, u8 Syndrome)
{
switch (Syndrome) {
case 0x03: return Data ^ 0x00000034;
case 0x05: return Data ^ 0x001a2000;
case 0x09: return Data ^ 0x0d000000;
case 0x0d: return Data ^ 0x00001a00;
case 0x11: return Data ^ 0x60000000;
case 0x13: return Data ^ 0x00000003;
case 0x15: return Data ^ 0x00018000;
case 0x19: return Data ^ 0x00c00000;
case 0x1d: return Data ^ 0x00000180;
case 0x21: return Data ^ 0x80000000;
case 0x23: return Data ^ 0x00000008;
case 0x25: return Data ^ 0x00040000;
case 0x29: return Data ^ 0x02000000;
case 0x2d: return Data ^ 0x00000400;
case 0x31: return Data ^ 0x10000000;
case 0x35: return Data ^ 0x00004000;
case 0x39: return Data ^ 0x00200000;
case 0x3d: return Data ^ 0x00000040;
}
return Data;
}
/*****************************************************************************/
/**
* Inject errors using the hardware fault injection functionality, and write
* random data and read it back using the indicated location.
*
* @param InstancePtr is a pointer to the XBram instance to
* be worked on.
* @param The Addr is the indicated memory location to use
* @param The Index1 is the bit location of the first injected error
* @param The Index2 is the bit location of the second injected error
* @param The Width is the data byte width
* @param The ActualData is filled in with expected data for checking
* @param The ActualEcc is filled in with expected ECC for checking
*
* @return None
*
* @note None.
*
******************************************************************************/
static void InjectErrors(XBram * InstancePtr, u32 Addr,
int Index1, int Index2, int Width,
u32 *ActualData, u32 *ActualEcc)
{
u32 InjectedData = 0;
u32 InjectedEcc = 0;
u32 RandomData = PrngData(&PrngResult);
if (Index1 < 32) {
InjectedData = 1 << Index1;
} else {
InjectedEcc = 1 << (Index1 - 32);
}
if (Index2 < 32) {
InjectedData |= (1 << Index2);
} else {
InjectedEcc |= 1 << (Index2 - 32);
}
WR(FI_D_0_OFFSET, InjectedData);
WR(FI_ECC_0_OFFSET, InjectedEcc);
XBram_Out32(Addr, RandomData);
Xil_DCacheFlushRange(Addr, 4);
switch (Width) {
case 1: /* Byte - Write to do Read-Modify-Write */
XBram_Out8(Addr, PrngData(&PrngResult) & 0xFF);
break;
case 2: /* Halfword - Write to do Read-Modify-Write */
XBram_Out16(Addr, PrngData(&PrngResult) & 0xFFFF);
break;
case 4: /* Word - Read */
(void) XBram_In32(Addr);
break;
}
*ActualData = InjectedData ^ RandomData;
*ActualEcc = InjectedEcc ^ CalculateEcc(RandomData);
}
/*****************************************************************************/
/**
* Run a self-test on the driver/device. Unless fault injection is implemented
* in hardware, this function only does a minimal test in which available
* registers (if any) are written and read.
*
* With fault injection, all possible single-bit and double-bit errors are
* injected, and checked to the extent possible, given the implemented hardware.
*
* @param InstancePtr is a pointer to the XBram instance.
* @param IntMask is the interrupt mask to use. When testing
* with interrupts, this should be set to allow interrupt
* generation, otherwise it should be 0.
*
* @return
* - XST_SUCCESS if fault injection/detection is working properly OR
* if ECC is Not Enabled in the HW.
* - XST_FAILURE if the injected fault is not correctly detected or
* the Control Base Address is Zero when ECC is enabled.
* .
*
* If the BRAM device is not present in the
* hardware a bus error could be generated. Other indicators of a
* bus error, such as registers in bridges or buses, may be
* necessary to determine if this function caused a bus error.
*
* @note None.
*
******************************************************************************/
int XBram_SelfTest(XBram *InstancePtr, u8 IntMask)
{
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
if (InstancePtr->Config.EccPresent == 0) {
return (XST_SUCCESS);
}
if (InstancePtr->Config.CtrlBaseAddress == 0) {
return (XST_SUCCESS);
}
/*
* Only 32-bit data width is supported as of yet. 64-bit and 128-bit
* widths will be supported in future.
*/
if (InstancePtr->Config.DataWidth != 32)
return (XST_SUCCESS);
/*
* Read from the implemented readable registers in the hardware device.
*/
if (InstancePtr->Config.CorrectableFailingRegisters) {
(void) RD(CE_FFA_0_OFFSET);
}
if (InstancePtr->Config.CorrectableFailingDataRegs) {
(void) RD(CE_FFD_0_OFFSET);
(void) RD(CE_FFE_0_OFFSET);
}
if (InstancePtr->Config.UncorrectableFailingRegisters) {
(void) RD(UE_FFA_0_OFFSET);
}
if (InstancePtr->Config.UncorrectableFailingDataRegs) {
(void) RD(UE_FFD_0_OFFSET);
(void) RD(UE_FFE_0_OFFSET);
}
/*
* Write and read the implemented read/write registers in the hardware
* device.
*/
if (InstancePtr->Config.EccStatusInterruptPresent) {
WR(ECC_EN_IRQ_OFFSET, 0);
if (RD(ECC_EN_IRQ_OFFSET) != 0) {
return (XST_FAILURE);
}
}
if (InstancePtr->Config.CorrectableCounterBits > 0) {
u32 Value;
/* Calculate counter max value */
if (InstancePtr->Config.CorrectableCounterBits == 32) {
Value = 0xFFFFFFFF;
} else {
Value = (1 <<
InstancePtr->Config.CorrectableCounterBits) - 1;
}
WR(CE_CNT_OFFSET, Value);
if (RD(CE_CNT_OFFSET) != Value) {
return (XST_FAILURE);
}
WR(CE_CNT_OFFSET, 0);
if (RD(CE_CNT_OFFSET) != 0) {
return (XST_FAILURE);
}
}
/*
* If fault injection is implemented, inject all possible single-bit
* and double-bit errors, and check all observable effects.
*/
if (InstancePtr->Config.FaultInjectionPresent &&
InstancePtr->Config.WriteAccess != 0) {
const u32 Addr[2] = {InstancePtr->Config.MemBaseAddress &
0xfffffffc,
InstancePtr->Config.MemHighAddress &
0xfffffffc};
u32 SavedWords[2];
u32 ActualData;
u32 ActualEcc;
u32 CounterValue = 0;
u32 CounterMax;
int WordIndex = 0;
int Result = XST_SUCCESS;
int Index1;
int Index2;
int Width;
PrngResult = 42; /* Random seed */
/* Save two words in BRAM used for test */
SavedWords[0] = XBram_In32(Addr[0]);
SavedWords[1] = XBram_In32(Addr[1]);
for (Width = 1; Width <= 4; Width <<= 1) {
/* Calculate counter max value */
if (InstancePtr->Config.CorrectableCounterBits == 32) {
CounterMax = 0xFFFFFFFF;
} else {
CounterMax =(1 <<
InstancePtr->Config.CorrectableCounterBits) - 1;
}
/* Inject and check all single bit errors */
for (Index1 = 0; Index1 < TOTAL_BITS; Index1++) {
/* Save counter value */
if (InstancePtr->Config.CorrectableCounterBits > 0) {
CounterValue = RD(CE_CNT_OFFSET);
}
/* Inject single bit error */
InjectErrors(InstancePtr, Addr[WordIndex], Index1,
Index1, Width, &ActualData, &ActualEcc);
/* Check that CE is set */
if (InstancePtr->Config.EccStatusInterruptPresent) {
CHECK(ECC_STATUS_OFFSET,
XBRAM_IR_CE_MASK, Result);
}
/* Check that address, data, ECC are correct */
if (InstancePtr->Config.CorrectableFailingRegisters) {
CHECK(CE_FFA_0_OFFSET, Addr[WordIndex], Result);
}
/* Checks are only for LMB BRAM */
if (InstancePtr->Config.CorrectableFailingDataRegs) {
CHECK(CE_FFD_0_OFFSET, ActualData, Result);
CHECK(CE_FFE_0_OFFSET, ActualEcc, Result);
}
/* Check that counter has incremented */
if (InstancePtr->Config.CorrectableCounterBits > 0 &&
CounterValue < CounterMax) {
CHECK(CE_CNT_OFFSET,
CounterValue + 1, Result);
}
/* Restore correct data in the used word */
XBram_Out32(Addr[WordIndex], SavedWords[WordIndex]);
/* Allow interrupts to occur */
/* Clear status register */
if (InstancePtr->Config.EccStatusInterruptPresent) {
WR(ECC_EN_IRQ_OFFSET, IntMask);
WR(ECC_STATUS_OFFSET, XBRAM_IR_ALL_MASK);
WR(ECC_EN_IRQ_OFFSET, 0);
}
/* Switch to the other word */
WordIndex = WordIndex ^ 1;
if (Result != XST_SUCCESS) break;
}
if (Result != XST_SUCCESS) {
return XST_FAILURE;
}
for (Index1 = 0; Index1 < TOTAL_BITS; Index1++) {
for (Index2 = 0; Index2 < TOTAL_BITS; Index2++) {
if (Index1 != Index2) {
/* Inject double bit error */
InjectErrors(InstancePtr,
Addr[WordIndex],
Index1, Index2, Width,
&ActualData,
&ActualEcc);
/* Check that UE is set */
if (InstancePtr->Config.
EccStatusInterruptPresent) {
CHECK(ECC_STATUS_OFFSET,
XBRAM_IR_UE_MASK,
Result);
}
/* Check that address, data, ECC are correct */
if (InstancePtr->Config.
UncorrectableFailingRegisters) {
CHECK(UE_FFA_0_OFFSET, Addr[WordIndex],
Result);
CHECK(UE_FFD_0_OFFSET,
ActualData, Result);
CHECK(UE_FFE_0_OFFSET, ActualEcc,
Result);
}
/* Restore correct data in the used word */
XBram_Out32(Addr[WordIndex],
SavedWords[WordIndex]);
/* Allow interrupts to occur */
/* Clear status register */
if (InstancePtr->Config.
EccStatusInterruptPresent) {
WR(ECC_EN_IRQ_OFFSET, IntMask);
WR(ECC_STATUS_OFFSET,
XBRAM_IR_ALL_MASK);
WR(ECC_EN_IRQ_OFFSET, 0);
}
/* Switch to the other word */
WordIndex = WordIndex ^ 1;
}
if (Result != XST_SUCCESS) break;
}
if (Result != XST_SUCCESS) break;
}
/* Check saturation of correctable error counter */
if (InstancePtr->Config.CorrectableCounterBits > 0 &&
Result == XST_SUCCESS) {
WR(CE_CNT_OFFSET, CounterMax);
InjectErrors(InstancePtr, Addr[WordIndex], 0, 0,
4, &ActualData, &ActualEcc);
CHECK(CE_CNT_OFFSET, CounterMax, Result);
}
/* Restore the two words used for test */
XBram_Out32(Addr[0], SavedWords[0]);
XBram_Out32(Addr[1], SavedWords[1]);
/* Clear the Status Register. */
if (InstancePtr->Config.EccStatusInterruptPresent) {
WR(ECC_STATUS_OFFSET, XBRAM_IR_ALL_MASK);
}
/* Set Correctable Counter to zero */
if (InstancePtr->Config.CorrectableCounterBits > 0) {
WR(CE_CNT_OFFSET, 0);
}
if (Result != XST_SUCCESS) break;
} /* Width loop */
return (Result);
}
return (XST_SUCCESS);
}
/** @} */