I've been working on extracting field information for DID0 and 1 and quickly realized there was something missing in the TivaWare macros. Although the fields in the registers had masks defined they did not have position (shift) information defined which makes reporting in a friendly readable fashion difficult. I first added shifts manually but that's going to be a maintenance headache and irked me since the needed information is actually embedded in the mask. You could use a function to calculate the position with relative shifts but that seemed an inelegant waste. So I did a little searching and noodling. The core of this (the position calc) comes from others but I've not seen quite this form, they were interested in the reverse problem.
First the macros to implement the solution. The macro that would be used is GET_SUB_FIELD which returns the value of the field given an integer and a mask.
#ifdef __cplusplus
extern "C" {
#endif
/* Macros used to extract a subfield given only the mask. Needed since
TivaWare defines the mask but not the offset, so the offset must be
calculated.
SUB_FIELD_DIVISOR calulates a divisor to shift the field right. This
works by overlapping the mask for 1 bit by sifting the inverse of the
mask left by 1. This will give a power of two divide.
IMPORTANT NOTE: This only works if the mask is contiguous, gaps
in the mask will cause errors
IMPORTANT NOTE: Argument is evaluated multiple times
GET_SUB_FIELD returns a sub field of an integer type field given a
mask for that field. The mask must consist of a sequential set of
bits set that indicate the bits containing the sub field. This field
is shifted right to align its rightmost bit on bit 0. Since the
divisor will be a power of 2, when a constant for themask is used
then the compiler is free to replace the divide by a shift.
IMPORTANT NOTE: Argument mask is evaluated multiple times */
#define SUB_FIELD_DIVISOR(mask)((mask) & ~((mask) << 1))
#define GET_SUB_FIELD(val,mask) (((val) & (mask))/SUB_FIELD_DIVISOR((mask)))
#ifdef __cplusplus
}
#endif
Next the tests
TEST(subfield_test, mask_offset_0) {
RESET_FFF();
// Check no mask case. This is actually an error but it's not detected
// by the code. We will rely on lint to catch.
EXPECT_EQ(0u,SUB_FIELD_DIVISOR(0u));
}
TEST(subfield_test, mask_offset_1) {
RESET_FFF();
// Check simple 1 bit field with no offset. Should produce a divider of 1
EXPECT_EQ(1u,SUB_FIELD_DIVISOR(1u));
}
TEST(subfield_test, mask_offset_1x) {
RESET_FFF();
// Check simple multiple bit field with no offset. Should produce a divider of 1
EXPECT_EQ(1u,SUB_FIELD_DIVISOR(3u));
EXPECT_EQ(1u,SUB_FIELD_DIVISOR(0xFu));
EXPECT_EQ(1u,SUB_FIELD_DIVISOR(0xFFu));
}
TEST(subfield_test, mask_offset_3) {
RESET_FFF();
// Check simple 1 bit field with a 3 bit offset. Should produce a divider of 8
EXPECT_EQ(8u,SUB_FIELD_DIVISOR(0x8u));
}
TEST(subfield_test, mask_offset_3x) {
RESET_FFF();
// Check simple multiple bit field 3 bit offset. Should produce a divider of 8
EXPECT_EQ(8u,SUB_FIELD_DIVISOR(0x18u));
EXPECT_EQ(8u,SUB_FIELD_DIVISOR(0xF8u));
EXPECT_EQ(8u,SUB_FIELD_DIVISOR(0xFF8u));
}
TEST(subfield_test, mask_offset_8) {
RESET_FFF();
// Check simple 1 bit field with a 8 bit offset. Should produce a divider of 256
EXPECT_EQ(256u,SUB_FIELD_DIVISOR(0x100u));
}
TEST(subfield_test, mask_offset_8x) {
RESET_FFF();
// Check simple multiple bit field with a 8 bit offset. Should produce a divider of 256
EXPECT_EQ(256u,SUB_FIELD_DIVISOR(0x300u));
EXPECT_EQ(256u,SUB_FIELD_DIVISOR(0xF00u));
EXPECT_EQ(256u,SUB_FIELD_DIVISOR(0xFF00u));
}
TEST(subfield_test, get_subfield_1) {
RESET_FFF();
// extract a simple one bit field at different offsets with no other
// bits set
EXPECT_EQ(1u,GET_SUB_FIELD(0x1u, 0x1u));
EXPECT_EQ(0u,GET_SUB_FIELD(0x0u, 0x1u));
EXPECT_EQ(1u,GET_SUB_FIELD(0x10u, 0x10u));
EXPECT_EQ(0u,GET_SUB_FIELD(0x0u, 0x10u));
EXPECT_EQ(1u,GET_SUB_FIELD(0x100u, 0x100u));
EXPECT_EQ(0u,GET_SUB_FIELD(0x0u, 0x100u));
}
TEST(subfield_test, get_subfield_1x) {
RESET_FFF();
// extract a simple one bit field with other bits set.
EXPECT_EQ(1u,GET_SUB_FIELD(0x1Fu, 0x10u));
EXPECT_EQ(0u,GET_SUB_FIELD(0x0Fu, 0x10u));
EXPECT_EQ(1u,GET_SUB_FIELD(0xF0u, 0x10u));
EXPECT_EQ(0u,GET_SUB_FIELD(0xE0u, 0x10u));
EXPECT_EQ(1u,GET_SUB_FIELD(0xFFu, 0x10u));
EXPECT_EQ(0u,GET_SUB_FIELD(0xEFu, 0x10u));
}
TEST(subfield_test, get_subfield_4x) {
RESET_FFF();
// extract a multiple bit field.
EXPECT_EQ(1u,GET_SUB_FIELD(0x1Fu, 0xF0u));
EXPECT_EQ(0u,GET_SUB_FIELD(0x0Fu, 0xF0u));
EXPECT_EQ(1u,GET_SUB_FIELD(0xF10u, 0xF0u));
EXPECT_EQ(0xFu,GET_SUB_FIELD(0xFF0u, 0xF0u));
EXPECT_EQ(0x2u,GET_SUB_FIELD(0x2Fu, 0xF0u));
EXPECT_EQ(0x5u,GET_SUB_FIELD(0x5Fu, 0xF0u));
EXPECT_EQ(0x3u,GET_SUB_FIELD(0xF30u, 0xF0u));
EXPECT_EQ(0x7u,GET_SUB_FIELD(0xF70u, 0xF0u));
EXPECT_EQ(0xAu,GET_SUB_FIELD(0xFAFu, 0xF0u));
EXPECT_EQ(0xCu,GET_SUB_FIELD(0xFCCu, 0xF0u));
}
I use google test but I haven't included any of the framework needed (includes etc..) The tests already proved valuable, detecting simple oversights.
Robert