PCIe DMA #1
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
We need to generate DMA requests based on register reads and writes. The structure of a DMA request is as follows:
For read requests:
req_src_add: Tthe address that will be in the TLP. The bottom 2 bits, aka Address Type, is hardcoded to 0 for untranslated.
req_src_sel: ngl not sure what this does
req_dst_addr: the address of the dma ram where the data is written. This address gets shifted over by RAM_ADDR_W-RAM_SEG_ADDR_W. This simplifies to $clog2(RAM_SEGS*RAM_SEG_BE_W); RAM_SEGS is 2, and RAM_SEG_BE_W is 8, so $clog2(16) = 4, so the address is shifted over by 4 (divided by 16). Really this just means the ram is 2 segments, and each segment is 64 bits. 128 bits (16 bytes) per address, so divide the address by 16.
req_dst_sel: this ends up coming out on the dma interface as cmd_sel when it writes to the memory? Does not look like sel is used at all.
req_imm/req_imm_en: Not sure what these are used for
req_len: self explanatory
req_tag: As far as I can tell, this is just so the requester can match status responses with requests. It doesn't go into the pcie core at all.
req_id: Does not look to be used
req_dest: Not used in pcie core
req_user: not used in pcie core
req_valid: self explanatory
req_ready: self explanatory
I think the id/dest/user fields are used to handle the requests, and are not necessarily a part of the request itself. It also looks like if we send a request for 0 bytes, the response will say that 0 bytes are valid which I guess makes sense. For write requests its pretty similar I believe its just the the addresses are switched.
So the only fields we really need to do are the source and destination addresses, and the length. We can autogenerate the tag based on just a counter of packets. When we write to a certain register, it will then send out that descriptor. When we get a response, it will latch those values to another set of registers with a clear on read bit for valid.
For simplicity, read and write will have their own sections.
We added this and it seems to be working, except that if we try to run it multiple times in a row, it doesn't end up overwriting the data. We may need to test this in sim and or get a trace on it with an ILA.
Here are the original read request and the write request, right after a reboot. You can see that the read request is ack'd, then the status is valid. So is the write request.
However, when we try the second read we do not see a status valid for the read.
Maybe we need to look at the RQ and RC interfaces? It might be that we are not getting a response from the cpu again? Maybe the CPU needs to see a second tag or something?
Hmm supposedly the core is still incrementing the tag when we send the request. I think we will need to look at the actual axi streams. they are like 256 bits wide though so it will be a pretty big ILA I guess.
so for the RQ and RC streams, they are both 256 bit with 8 bits of keep only, RQ is 62 bits and RC has 75 bits of user.
So we can do a 75 bit tuser,
data: 256
keep: 8
user: 75
last: 1
valid: 1
ready: 1
Huh, It looks like even though we read the second data, the data that we are writing back is still the old data. So its coming from the FPGA, not from the PC.
So the problem is that we are not writing to the memory again. We are seeing wr_cmd_valid for the first write, but not the second one. But read is still happening, so we are reading the nonsense data.
Here are the RC and RQ busses for the FIRST transfer, this is the one that is successful.
ok the only thing of note here that I can think of is that the sequence number is the same: 0. Do we need to change it so that we send different sequence numbers?
Interesting, in the example they enable client tag, should we try that?
Yes, that fixes it.