-
Notifications
You must be signed in to change notification settings - Fork 11
Get: (a_size = 1)
This page deals with the configuration when a_size = 1. This means that the Host wants to read 2 bytes of data since 21 = 2 bytes.
For this overview we will assume the following memory organisation

Since we are reading two bytes (a_size = 1) the a_address will only have even addressing as shown in the figure above. The a_mask selects the two byte lanes to read from. a_mask must have two contiguous bits set meaning 'b0011 and 'b1100 are the only options when a_size = 1. Which means if we want to read from the zeroth and first byte lanes then a_mask = 'b0011. One HIGH bit of
a_mask indicates the active byte lane.
| a_address[31:0] | a_mask[3:0] |
|---|---|
| 'd0 | 'b0011 |
| 'd2 | 'b1100 |
| 'd4 | 'b0011 |
| 'd6 | 'b1100 |
| 'd8 | 'b0011 |
| . | . |
| . | . |
Note: The 'd in above table indicates decimal representation and the 'b indicates binary representation.
If we want to read two bytes 2F FF from memory the even address to access it will be a_address = 'd6 and mask should be a_mask = 'b1100. This reads 0x2FFF from the memory as shown above in the diagram as well.
If we want to read two bytes E0 AB from memory the even address to access it will be a_address = 'd8 and mask should be a_mask = 'b0011. This read 0xE0AB from the memory.
Following are the points needed to be considered when dealing with reading two bytes (Get: a_size = 1)
-
a_addressshould only be even (0, 2, 4, 6, 8, 10, 12, 14, 16 ...) -
a_maskmust have only two bits active and contiguous at any given time. -
a_maskmust be aligned witha_addressat any given time according to the table given above.
// Our own mask created using the a_address
// If data bus width (DBW) is 32-bits then 32/8 = 4 bytes so a_mask uses 4 bits to represent the active byte lane.
val mask = Wire(UInt((DBW/8).W))
mask := (1 << a_address(1,0))
// Creating two wires of Chisel.Bool type in order to set them `true.B` if the check is passed or `false.B` if the check is failed.
val addr_chk = Wire(Bool())
val mask_chk = Wire(Bool())
when(a_size === 0.U) { // 1 Byte covered in Get: (a_size = 0)
addr_chk := true.B
mask_chk := ~((a_mask & ~mask).orR)
} .elsewhen(a_size === 1.U) { // 2 Bytes
addr_chk := ~a_address(0)
mask_chk := Mux(a_address(1), ~((a_mask & "b0011".U).orR), ~((a_mask & "b1100".U).orR))
}Note: The code above is taken from the previous case where a_size = 0 and was modified to deal with a_size = 1 as well.
This time we need to check the a_address as well since it must be even. So we extract the zeroth bit of a_address with a_address(0). If this bit is set then the address would be odd and we would invert the result to 'b0 in order to make the addr_chk := false.B.
The logic for handling mask_chk ensures that any given time only two bits are HIGH due to a_size = 1 and they must be contiguous and must only be either 'b0011 or 'b1100 according to the address provided.
We will go through some test cases to explain the reasoning for this logic and ensure the logic works well i.e passes the check when inputs are correct and fails them if the inputs are wrong.
| a_address[31:0] | a_size[1:0] | a_mask[3:0] |
|---|---|---|
| 'h6 | 'h1 | 'b1100 |
With these inputs incoming let's analyse what is happening in the wires we declared addr_chk, mask_chk.
Note: We won't be using the mask wire in this condition.
Let's visualise the a_address bits in a concise way:
| a_address[31] | a_address[30] | ... | a_address[2] | a_address[1] | a_address[0] |
|---|---|---|---|---|---|
| 0 | 0 | ... | 1 | 1 | 0 |
The addr_chk is wired with the logic which ensures that the zeroth bit must not be set in order to ensure even addressing.
Since we extract the zeroth bit and invert it ~a_address(0) so we get,
addr_chk := true.Bbecause a_address(0) gives 'b0 in this case and inverting it results in 'b1 or true.B
We know that a_mask in this condition must always have 2 bits HIGH, they must be contiguous and they must be select either the LSB byte lanes 'b0011 or the MSB byte lanes 'b1100.
In this condition we have a_mask = 'b1100 as input coming in. We use a Mux() construct of Chisel to control the wiring of mask_chk. The condition for the Mux is the first bit of a_address. If this bit is set it means the address we are getting is not word aligned and can be the following (2, 6, 10, 14, 18, 22 ... ) and we use this logic ~((a_mask & "b0011".U).orR) to set the mask_chk. Otherwise, if the first bit of a_address is not set, we use the next logic ~((a_mask & "b1100".U).orR) to set the mask_chk.
In our case (considering the input we receive), the first bit of a_address is set as can be seen above where we visualise the a_address bits which means the address we receive would be even but not word aligned. So the first condition of Mux() would be used to wire the mask_chk.
Let's see what is happening in the logic ~((a_mask & "b0011".U).orR). This logic needs to ensure that the received a_mask must be set as 'b1100.
We AND the a_mask with "b0011".U which results in 1100 & 0011 = 0000.
Then we use the .orR OR reduction to check if any bits are set. Here 'b0000.orR returns false.B since no bits are set.
Finally, we invert the result ~false.B which results in:
mask_chk := true.BThis means that since Input 1 parameters were correct our logic correctly ensures they are right and passes the check. ✅
Let's repeat the same test case with wrong a_address and then with wrong a_mask.
| a_address[31:0] | a_size[1:0] | a_mask[3:0] |
|---|---|---|
| 'h7 | 'h1 | 'b1100 |
We just changed the a_address and kept the a_size and a_mask same.
With these inputs incoming let's analyse what is happening in the wires we declared addr_chk, mask_chk.
Let's visualise the a_address bits:
| a_address[31] | a_address[30] | ... | a_address[2] | a_address[1] | a_address[0] |
|---|---|---|---|---|---|
| 0 | 0 | ... | 1 | 1 | 1 |
Since we extract the zeroth bit and invert it ~a_address(0) so we get,
addr_chk := false.Bbecause a_address(0) gives 'b1 in this case and inverting it results in 'b0 or false.B
This means that our address check fails and even if a_mask is set correctly and mask_chk passes, we have a wrong address and this will generate an error and our test case with these inputs, fails. ❌
Now let's try with the correct address but wrong alignment of a_mask.
| a_address[31:0] | a_size[1:0] | a_mask[3:0] |
|---|---|---|
| 'h6 | 'h1 | 'b0011 |
Here the a_address is correct but since the address is not word aligned a_mask cannot select LSB byte lanes 'b0011. Let's see if you verification logic correctly catches the error and fails the mask_chk.
Let's visualise the a_address bits:
| a_address[31] | a_address[30] | ... | a_address[2] | a_address[1] | a_address[0] |
|---|---|---|---|---|---|
| 0 | 0 | ... | 1 | 1 | 0 |
In this condition we have a_mask = 'b0011 as input coming in. Since the first bit of a_address is set as shown above, the Mux() would wire the mask_chk with this logic ~((a_mask & "b0011".U).orR).
We AND the a_mask with "b0011".U which results in 0011 & 0011 = 0011.
Then we use the .orR OR reduction to check if any bits are set. Here 'b0011.orR returns true.B since some bits are set.
Finally, we invert the result ~true.B which results in:
mask_chk := false.BAnd this correctly ensures that our check fails the input case where the a_mask was incorrectly configured. ❌
Since this overview got lengthy as well, we will keep it till here and not cover the second example 😄 . But hopefully, the explanation would help self-understand the second example as well.