Bit Masking in a Cache - c

In C I am attempting to separate an integer address value into the tag bit and the set index bit in a direct mapped cache with 4 sets. I do this so I can compare the correct tags at the line in the correct set of my cache. Here for example the address parameters:
number of set index bits s=2
number of offset bits b=3
total number of address bits m=8
And in my example the address integer is 40 -- int address = 40.
40 in binary is 00101000
The set index should be 01 = 1
The tag should be 001 = 1
Instead for the tag I am getting 2 and the set index is 8 which have to be wrong because I only have 4 sets in my cache.
Here is how I am doing it, I am bit-masking the address to get the tag, which are the bits to the left of the set index up to the m=8 bit. And the set index is between the tag and offset bits which is the 01 in the middle of the 8 bit sequence.
int tag = ((address & ~0 << (s + b)) >> 4)
int set = (address & (~(~0 << s) << b))
I know I must be wrong but the more I try to write a mask the more I am getting confused and I must be forgetting something. I at least thought I was getting the tag right because the left bits should be easier than getting middle bits. Any advice or help would be greatly appreciated thanks a lot!

These equations will work for you:
tag = (address >> 5) & 0x7;
set = (address >> 3) & 0x3;
If you want to use the variables s, b, and m:
tag = (address >> (m-b)) & ((1u << b)-1);
set = (address >> (m-b-s)) & ((1u << s)-1);
In general if you want to extract N bits starting at bit i:
bits = (value >> i) & ((1u << N)-1);

Related

Does someone can explain me this line of C Code (Pointer Arithmetic, bit shift)?

Let *c be 32bit in Memory and xmc[] array of 32bit in memory (abstract: Network packet)
xmc[0] = (*c >> 4) & 0x7;
xmc[1] = (*c >> 1) & 0x7;
xmc[2] = (*c++ & 0x1) << 2;
xmc[2] |= (*c >> 6) & 0x3;
xmc[3] = (*c >> 3) & 0x7;
What do the lines xmc[2] of code do to the Value (thought in binary)?
I tried to look up the arithmetic, but I failed understanding the part beginning from *c++.
EDIT: Added more context for clarification
Dereferencing and increment:
First, you are taking the value stored at the address pointed by the c pointer and incrementing the address.
Bitwise AND with a mask: A bitwise AND (&) is done with a mask of value 0x1 (decimal 1), which means that only the least significant bit is taken out of the value stored at the address c.
Think about it like that: You can have a variable on 4 bits, called a, with a decimal value of 3 (binary 0011) and you are doing a bitwise AND between a and a mask of decimal value 2 (binary 10), also on 4 bits (so 0010):
a = 0011
b = 0010
Bitwise AND (a & b or a & (0x10)) will compute an AND between each two bits from a and b. First bit in a is 1, first bit in b is 0 => least significant bit in the result is 1 & 0 = 0, go on with the second bits of each variable, leading to the second least significant bit in the result being 1, and so on...
AND with such a mask is typically used to take a certain bit (or a group of bits) from a value stored in a variable. In your case, your code takes the least significant bit stored in a.
Left shift: The left shift << takes the least significant bit two positions to the left (e.g. from 0001 to 0100), adding 2 bits on 0 to the right.
Let's assume that we operating on a unsigned 32 bit value. Then code
xmc[2] = (*c++ & 0x1) << 2;
is equivalent to
uint32_t tmp1 = *c; // Read the value that c points to and
c = c + 1; // increment the pointer c
// These two lines is the *c++ part
uint32_t tmp2 = tmp1 & 0x1; // Make tmp2 equal to the least significant bit of tmp1
// i.e. tmp2 will be 1 if tmp1 is odd and
// tmp2 will be 0 if tmp1 is even
uint32_t tmp3 = tmp2 << 2; // Make tmp3 equal to tmp2 shifted 2 bits to the left
// This is the same as: tmp3 = tmp2 * 4
xmc[2] = tmp3; // Save the result in xmc[2]
In pseudo code this means:
If the value pointed to be c is odd, set xmc[2] to 4
If the value pointed to be c is even, set xmc[2] to 0
Increment the pointer c
Today's date could be said to be 20230215.
If you have that as a number, you could extract the components as follows:
n = 20230215;
y = n / 10000 % 10000;
m = n / 100 % 100;
d = n / 1 % 100;
The code in question does the same thing. It's extracting four values (a, b, c and d) spread over two bytes.
c[0] c[1]
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+
| | a | a | a | b | b | b | c | | c | c | d | d | d | | | |
+---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+
Since we want to extract bits instead of digits, we need to use powers of two instead of using powers of ten. When dealing with powers of two, >> can be used in lieu of division, and & can be used in lieu of %.
To extract a, b, c and d, we could use the following:
n = ( c[0] << 8 ) | c[1];
xmc[0] = ( n >> 12 ) & 0x7;
xmc[1] = ( n >> 9 ) & 0x7;
xmc[2] = ( n >> 6 ) & 0x7;
xmc[3] = ( n >> 3 ) & 0x7;
The posted code takes an approach that avoids calculating n, but it does the same thing.
++ is a post-increment operator in C language, and it would be the last operation before assigning a value to the left of the =.
xmc[2] = (*c++ & 0x1) << 2;
This statement could be considered to:
de-reference : *c
bit-wise AND : *c & 0x1
left-shift : (*c & 0x1) << 2
post-increment : c++
assign to left of = : xmc[2] = the result of the 3rd step.
But, the compiler would optimize these operations by its design and might increase c before the bit-wise AND operation by utilizing the registers of CPU and stacks in the memory. i.e. differences might be found in the final assembly code.
The posted code could be more easily read if it were, equivalently:
xmc[0] = (c[0] >> 4) & 0x7;
xmc[1] = (c[0] >> 1) & 0x7;
xmc[2] = (c[0] & 0x1) << 2;
xmc[2] |= (c[1] >> 6) & 0x3;
xmc[3] = (c[1] >> 3) & 0x7;
(And then, if later code depended on c being advanced, this could be followed by a standalone c++.)
It looks like xmc[0] is taken from three particular bits of c[0], and xmc[1] is taken from three other bits of c[0]. Then the next field straddles a word boundary, in that xmc[2] is composed by putting together one bit from c[0], and two bits from c[1]. Finally xmc[3] is taken from three other bits of c[1].
#ikegami's answer has more of the details.

Turn 0 bits to 1 bits if the bit is between low and high

Full disclosure, this is a homework problem and I do not need exact code. I am tasked with reproducing the following code while only using ~ & + <<.
int result = 0;
int i;
for(i = lowbit; i <= highbit; i++)
result |= 1 << i;
return result;
Where lowbit and highbit are parameters between 0 and 31 inclusive. If lowbit is a larger number than highbit, return 0.
What I have tried so for is the following code
int result = 0;
int negone = ~0x0;
int first = 1 << (lowbit + negone); //the first 1 bit is at the lowbit th location
int last = 1 << (highbit + negone); //the last 1 bit is at the highbit th location
int tick = ~(first + last); //attempting to get all bits in the range of low and highbit.
result = ~(~first & ~tick); //bitwise | without using |
result = ~(~last & ~result);
return result + 1; //the first bit should always be on.
So is there something fundamental I am missing here? In addition to what I have not working this also goes over my limit of 12 operators that I am allowed to use, but I'd like to try and get it working before I even begin to limit the operators.
When I run the test script on this I get errors on most of the tests it is put against including lowbit and highbit being equal to each other. Cases where highbit is the max size and lowbit is the least size seem to work though.
Any help would be much appreciated.
negone should be initialized this way:
uint32_t negone = ~0UL;
You are adding the bit number with a bit pattern in:
int first = 1 << (lowbit + negone); //the first 1 bit is at the lowbit th location
int last = 1 << (highbit + negone);
You should instead compute the 32 bit masks
uint32_t first = negone << lowbit; // all bits below lowbit are 0, others are 1
uint32_t last = negone << highbit << 1; // all bits above highbit are 1, other are 0
The result is obtained by masking the complement of first with last:
uint32_t result = ~first & last;
Combining the above steps gives is a direct solution with 7 operators (12 including the parentheses and the assignment), no addition, and no subtraction:
uint32_t result = ~(~0UL << highbit << 1) & (~0UL << lowbit);
I use 0UL because type unsigned long is guaranteed to have at least 32 bits, whereas type unsigned int might have just 16 bits.
1) Create a mask with the bits low to high set:
uint32_t mask = ~(~0ul << highbit << 1) & (~0ul << lowbit)
Example: lowbit = 4, highbit = 12 (9 bits)
mask = ~(0xffffffff << 12 << 1) & (0xffffffff << 4)
= ~(0xffff7000) & 0xfffffff0
= 0x00001fff & 0xfffffff0
= 0x00001ff0
2) Apply the mask to the value to be modified, this most simply an | operation, but that is not a valid operator in this exercise, so must be transformed using De Morgan's forum:
A|B -> ~(~A & ~B) :
result = ~(~result & ~mask) ;
It is of course possible to combining the two steps, but perhaps clarity would not then be served.
The original code generates a block of 1 from lowbit on until highbit (inclusive).
This can be achieved without a loop as follows:
int nrOfBits = highbit + ~lowbit + 2; // highbit - lowbit + 1
int shift = (nrOfBits & 0x1f + 1);
int result = ~(~(1 << shift)+1) << lowbit;
The idea is that, for example a range of 8 bits filled up with 1 means a number of 255, whereas 2^8 is 256. So - as operator - is not allowed, we use 2-complement to get -256, add 1 to get -255, and turn it back to +255 using 2-complement operator ~. Then, we just have to shift the block lowbits left.
The problem could be that tick = ~(first+last) does not flip the bit from the lowbit to the highbit.
Maybe we can do something like this:
/* supposed that lowbit = 1, highbit = 2 */
uint32_t negone = ~(0u); /* negone = all 1s */
uint32_t first = negone << lowbit; /* first = ...111110 */
uint32_t last = (1 << (highbit + 1)) + negone; /* last = ...0000111 */
uint32_t tick = last & first; /* tick = ...000110 */
result = ~(~result&~tick); /* Bitwise Or without | as you mentioned. */
It takes 11 bit operations to do this.
p.s. I am wondering why the first bit should be always on.
Edit: In order to avoid undefined operation, we should use unsigned type, like uint32_t.

Extracting 4 bits from N to N+4 in unsigned long

Consider the following integer:
uint32_t p = 0xdeadbeef;
I want to get:
0..3 bits so I did:
p & ((1 << 4) - 1); and that went good.
however, for 4..7 what I tried did not go as expected:
(p >> 16) & 0xFFFF0000
Why would it not extract the bits I want? Am I not moving p 16 positions to the right and then taking out 4 bits?
Would really appreciate an answer with explanation, thanks!
If you want to get bits from 4..7
(p>>4) & 0xf
If you want to get bits from N to (N+4-1)
(p>>N) & 0xf
And N should be <32 (if your system is 32 bits system). otherwise you will get undefined behaviour
No, you're actually removing bits 0 to 15 from p, so it will hold 0xdead and afterwards you perform the bitwise and so this will yield 0.
If you want to extract the upper 16 bits you will first have to the & operation and shift afterwards:
p = (p & 0xffff0000) >> 16;
To extracts the bits 4 to 7 you will want to do:
p = p & 0xf0;
or if you want them shifted down
p = (p & 0xf0) >> 4;
Btw. Could it be that mean the term nibble 4 to 7 instead of bit 4..7? Nibbles are 4 bits and represented by one hex digit, this would correlate with what you are trying to in the code

constructing 32 bit unsigned int in c

I am trying to construct a 32 bit binary value using an unsigned int. This is how
the 32 bit is split into parts
part 1 = 4 bit
part 2 = 2 bit
part 3 = 12 bit
part 4 = 2 bit
part 5 = 12 bit
How do i use the parts to construct 32 bit binary value (unsigned int) using
bit shifting can someone please help
(cant just shift bits... what if i want to update certain part?)
This is typical bit-shifting/bit-masking stuff. Here's how you set the initial value:
uint32_t value = ((part1 & 0xF) << 28)
| ((part2 & 0x3) << 26)
| ((part3 & 0xFFF) << 14)
| ((part4 & 0x3) << 12)
| (part5 & 0xFFF);
Each line uses a bitwise-AND (&) to clear the upper bits of each part so it doesn't overflow its allocated bit-width within the final value. For example, if part4 was 0xFF and you forgot the & 0x3 then the upper 6 bits of part4 (0xFC) would spill into the region for part3. Then the part is shifted (<<) to its final location and bitwise-OR'd (|) with the rest of the parts.
Some developers accomplish the same thing via bitfields but I don't recommend that approach due to potential portability issues.
Most (all?) of the other answers here so far have forgotten the bitwise-AND portion of the solution. Their answers will result in bugs if the part values ever exceed the specified bit width.
If you want to update a particular portion of the value, you'll need some more bit-masking via bitwise-AND and bitwise-OR. For example, to update part4 you'd do this:
value &= ~(0x3 << 12); /* Clear the part4 region */
value |= (part4 & 0x3) << 12; /* Set the part4 region to the new value */
That first line is a little tricky if you're new to bitwork in C. It says take 0x3 and shift it by 12 (result = 0x00003000), perform a bitwise-complement (result = 0xFFFFCFFF), and set value equal to itself bitwise-AND'd with that result. That's how you clear the part4 region of the value... because you're bitwise-AND'ing that region with zero the result is that region is now zero.
The second line sets the zeroed part4 region to the new value, just like we did above when setting the initial value.
You just use shifting, as you suggested:
uint32_t x = (part1 << 28)
| (part2 << 26)
| (part3 << 14)
| (part4 << 12)
| (part5);
Looks like you want this value:
part5 + (part4 << 12) + (part3 << 14) + (part2 << 26) + (part1 << 28)
(Make sure all five variables are of type uint32_t or some such.)

Am I extracting these fields correctly using bitwise shift? (tag, index, offset)

I am building a CPU cache emulator in C. I was hoping you could tell me if I am extracting these fields correctly:
The 32-bit address should be broken up as follows:
+---------------------------------------------------+
| tag (20 bits) | index (10 bits) | offset (2 bits) |
+---------------------------------------------------+
Here is my code to obtain the values for each:
void extract_fields(unsigned int address){
unsigned int tag, index, offset;
// Extract tag
tag = address >> 12;
// Extract index
index = address << 20;
index = index >> 22;
// Extract offset
offset = address << 30;
offset = offset >> 30;
}
Any advice is much appreciated!
It looks like your solution works, but it is often done a different way that is probably a bit more clear.
For your example:
// Shift off the lowest 12 bits, and mask off the higher ones
tag = (address >> 12) & 0xFFFFF;
// Shift off the lowest 2 bits, and mask off the higher ones
index = (address >> 2) & 0x3FF;
// Shift off the lowest 0 bits, and mask off the higher ones
offset = (address >> 0) & 0x3;

Resources