-
Notifications
You must be signed in to change notification settings - Fork 10
PutFullData
This page deals with the elaboration of PutFullData
operation. This operation is initiated by the host on channel A and expects a response of AccessAck
from the device on channel D.
The main difference between PutFullData
and PutPartialData
is that the former operation must write all the data indicated by a_size
while the latter can write all the data indicated by a_size
or it can write less than it depending upon the configuration of a_mask
.
The other difference that the specification indicates is that in PutFullData
the a_mask
must have contiguous bits set for example in a 2 byte data write, a_mask
can only be 'b0011
or 'b1100
not 'b0101
or any other configuration like this, however there is no such contiguous bits restriction for the a_mask
in the PutPartialData
operation.
Note: For our implementation we make sure that PutPartialData
also has contiguous bits of a_mask
set.
For this overview we will be using this figure of memory for convenience:
This means that the host wants to write 2a_size = 20 = 1 byte of data. The address can be word aligned as well as subword aligned which means we do not need to check for address, what matters is a_mask
. This is exactly the same as we discussed in the Get:(a_size = 0) operation earlier. Read the Get operation first before reading further as that section elaborates in detail what is happening. Following are the possibilities of a_address
and a_mask
as covered previously:
a_address[31:0] | a_mask[3:0] |
---|---|
'd0 | 'b0001 |
'd1 | 'b0010 |
'd2 | 'b0100 |
'd3 | 'b1000 |
'd4 | 'b0001 |
'd5 | 'b0010 |
'd6 | 'b0100 |
'd7 | 'b1000 |
. | . |
. | . |
Note: The 'd in above table indicates decimal representation and the 'b indicates binary representation.
For this, we will set the a_mask = 'b0001
and a_address = 'h0
. This will write one byte of data to the memory at address 0 on the byte lane set by a_mask
.
Here is a little animation to demonstrate what is happening:
For PutFullData
we just create one additional check fulldata_chk
which is only used when the opcode is of PutFullData
.
// 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())
val fulldata_chk = Wire(Bool()) // This is the new line added
when(a_size === 0.U) {
addr_chk := true.B
mask_chk := ~((a_mask & ~mask).orR)
fulldata_chk := (a_mask & mask).orR // This is the new line added
}
Note: This is the code that is elaborated in the Get(a_size = 0) with additional code lines marked with the comments.
The logic (a_mask & mask).orR
is the new one added for the PutFullData
. This ensures that a_size
matches with a_mask
.
a_address[31:0] | a_size[1:0] | a_mask[3:0] |
---|---|---|
'h0 | 'h0 | 'b0001 |
With these inputs incoming let's analyse what is happening in the wires mask
and fulldata_chk
.
mask = (1 << a_address(1,0))
a_address bits are as follows:
a_address[31] | a_address[30] | ... | a_address[1] | a_address[0] |
---|---|---|---|---|
0 | 0 | ... | 0 | 0 |
Since we extract the first two bits of a_address
we get 'b00
So 'b0001 << 'b00
results in the same 'b0001
or 'd1
Which means,
mask := 1.U
We are using the mask
and a_mask
to set this wire. Currently we have mask := 'b0001
and a_mask := 'b0001
.
In the first step we do a bitwise AND operation between a_mask
and mask
as:
a_mask & mask
This results in
'b0001 & 'b0001 = 'b0001
The .orR
is a Chisel function for OR Reduction. Which returns true if any bit is set. Here it checks if any bit is set or not. As seen above there is a bit set so,
'b0001.orR
returns true. Which makes the fulldata_chk := true.B
and passes the check. ✅
a_address[31:0] | a_size[1:0] | a_mask[3:0] |
---|---|---|
'h0 | 'h0 | 'b0100 |
With these inputs incoming let's analyse what is happening in the wires mask
and fulldata_chk
.
Since the a_address = 'h0
same as the above Input 1 the mask
would be set as:
mask := 1.U
We are using the mask
and a_mask
to set this wire. Currently we have mask := 'b0001
and a_mask := 'b0100
.
In the first step we do a bitwise AND operation between a_mask
and mask
as:
a_mask & mask
This results in
'b0001 & 'b0100 = 'b0000
The .orR
is a Chisel function for OR Reduction. Which returns true if any bit is set. Here it checks if any bit is set or not. As seen above there is no bit set so,
'b0000.orR
returns false. Which makes the fulldata_chk := false.B
and fails the check. ❌
This check rightly fails since the a_address
and a_mask
are not aligned. If a_address = 'h0
then a_mask
must only be 'b0001
to write the first byte of data on to the first address location. According to the tilelink specification a_address
, a_size
and a_mask
must correspond with one another. (See page 56)
This means that the host wants to write 2a_size = 21 = 2 bytes of data. The a_address
will only be even this time. It can have subword addressing but must be even like (0, 2, 4, 6, 8, 10, 12 ...). Correspondingly, the a_mask
will have contiguous bits in the form of 'b0011
or 'b1100
. Complete elaboration and details can be found in the Get: (a_size = 1) page where we go in detail. For PutFullData
the address_chk
and mask_chk
will be performed the same way as discussed previously in the Get: (a_size = 1) page. The additional thing here will be the fulldata_chk
.
// 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())
val fulldata_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)
fulldata_chk := (a_mask & mask).orR
} .elsewhen(a_size === 1.U) { // 2 Bytes covered in Get: (a_size = 1)
addr_chk := ~a_address(0)
mask_chk := Mux(a_address(1), ~((a_mask & "b0011".U).orR), ~((a_mask & "b1100".U).orR))
fulldata_chk := Mux(a_address(1), a_mask(3,2).andR, a_mask(1,0).andR) // New line added here
}
Note: This is the code that is elaborated in the Get(a_size = 1) with additional code lines marked with the comments.
Here we simply check if a_address
first bit is set it means the address will be even and subword aligned (2, 6, 10, 14, 18 ...) therefore the a_mask
must have two MSB bits set to access the byte lanes 'b1100
. This is what we are checking with the logic a_mask(3,2).andR
. The .andR
is AND reduction in Chisel which returns true.B
if all bits are set otherwise returns false.B
. Consequently, if the first bit is not set it means the addressing is word aligned (0, 4, 8, 12 ...) therefore a_mask
must have LSB byte lanes active there it's value must be 'b0011
and this is what we check with the logic a_mask(1,0).andR
. You must already know why a_mask
must have two MSB bits or LSB bits set depending upon the a_address
if you read the Get
operation overview first. We will not be going into the details here.
This means that the host wants to write 2a_size = 22 = 4 bytes of data. The a_address
is only allowed to be word aligned in this configuration i.e (0, 4, 8, 12, 16, 20 ...). The a_mask
must have all bits active 'b1111
to select all active byte lanes to write.
// 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())
val fulldata_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 covered in Get: (a_size = 1)
addr_chk := ~a_address(0)
mask_chk := Mux(a_address(1), ~((a_mask & "b0011".U).orR), ~((a_mask & "b1100".U).orR))
fulldata_chk := Mux(a_address(1), a_mask(3,2).andR, a_mask(1,0).andR)
} .elsewhen(a_size === 2.U) { // 4 Bytes
addr_chk := ~(a_address(2,0).orR)
mask_chk := true.B
fulldata_chk := a_mask(3,0).andR // New line added here
}
Note: This is the code that is elaborated in the Get(a_size = 2) with additional code lines marked with the comments.
This is as simple as it gets. The fulldata_chk
is wired with this logic a_mask(3,0).andR
which just ensures that all bits of a_mask
are set in this configuration otherwise fulldata_chk := false.B
and the check fails which will generate an error.