Skip to content

Get: (a_size = 1)

Muhammad Hadir Khan edited this page Aug 11, 2020 · 6 revisions

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

Memory

a_address and a_mask Possibilities

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.

Example 1: Reading two bytes 2F FF from Memory

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.

Example 2: Reading two bytes E0 AB from Memory

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.

Functional verification design

Following are the points needed to be considered when dealing with reading two bytes (Get: a_size = 1)

  1. a_address should only be even (0, 2, 4, 6, 8, 10, 12, 14, 16 ...)
  2. a_mask must have only two bits active and contiguous at any given time.
  3. a_mask must be aligned with a_address at 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.

Test Case: Example 1 (Reading two bytes 2FFF from Memory)

Input 1:
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
addr_chk

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.B

because a_address(0) gives 'b0 in this case and inverting it results in 'b1 or true.B

mask_chk

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.B

This 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.

Input 2:
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
addr_chk

Since we extract the zeroth bit and invert it ~a_address(0) so we get,

addr_chk := false.B

because 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.

Input 3:
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
mask_chk

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.B

And this correctly ensures that our check fails the input case where the a_mask was incorrectly configured. ❌

Clone this wiki locally