Motivated by previous posts on working with AXI streams, particularly the bus width conversion, I wondered if there was a concise but multi-purpose component that could be factored out such that an AXI stream of data could be manipulated by a state machine to perform functions like bus width conversion and protocol editing. Well it turns out there is, but as with all things AXI, when interfacing directly with hand crafted VHDL, testing is key. So for this component I have also swapped my minimal pin wiggler's toolkit for a more grown up test frame work, OSVVM, to make sure I bring the full power of constrained random testing to bear.
Logic
An early decision was how to specify the control logic. It seemed sensible that the actions pass, pause, drop and insert were combinations, or the cross product, of consume (or read) and emit (or write). The first three would be relatively easy to provide since solutions have already been studied for pause, and dropping a word creates a space, whereas inserting a word requires a space to be made. That means pausing the input whilst writing to the output. Finally, the swap action is trivial to implement, and shares a small problem with insert in that an interface needs to be defined to specify the alternative data. It works well to have this alternative data interface also be AXI-Streaming (AXI-S) in order to apply back pressure before accepting a word when processed data is still waiting to be read out.
Actions | Read | Write | Use Alternative Data |
---|---|---|---|
Pause | 0 | 0 | 0 |
Insert | 0 | 1 | 1 |
Drop | 1 | 0 | 0 |
Swap | 1 | 1 | 1 |
Pass | 1 | 1 | 0 |
NB. Below the "manager" is attached to the "subordinate interface" with the s_* signals, and the "subordinate" is attached to the "manager interface" with the m_* signals.
It is worth noting that when presenting data to be inserted, it might be the case that nothing is being presented on the standard input stream concurrently. This means the conditions for processing an insert action will be different to what you might expect for all the other actions. It also means that the test bench setup needs to be given consideration as the two bits specifying the action to take on data must be presented in time with data that could be on either or both AXI-S buses. The code below should look sufficiently familiar and justified, where the handshake of data switches between the sub-ordinate input and the alternative input depending on the action.
Testing
For testing I wanted to verify the following AXI scenarios, as they are bound to cause problems left untested:
Scenario | Delay Range (clock cycles) |
---|---|
Delay between transactions | 0-2 |
'TReady' a variable delay after 'TValid' | 0-2 |
'TValid' a variable delay after 'TReady' | 0-2 |
I also felt the need for some credible testing given that the AXI-S handshakes are more troublesome than many might give credit. So I enlisted the help of OSVVM, which comes with AXI Verification Components (VCs). This also gave me the excuse to test out TCover() functionality to verify that all 25 pairs of transitions had been tested between each of the actions pass, swap, drop, insert and pause.
The test bench source code is quite long and hence not included here (not least because b2evolution struggled when I put it in Prism codeblocks and submission caused HTTP 403 errors). Therefore this is best viewed directly on GitHub.
You will see that I setup a profile for the constrained random testing, for 100s of each action and a transition coverage model both of which must complete. The transition model uses smaller numbers as there are 25 different pairings and the main driver is the former coverage from which the random selections are made. The instruction for which action to take is presented by both the Tx and Alt AXI VCs via their TUser bits because neither presents these values all the time. A multiplexer is used to select which VC gets through to the device under test, where the priority is given the Alt VC. When neither is driving, the default values are used which neatly returns the pass action. Now we can present the read/write inputs along with the Tx and optionally the Alt data. As you might expect, a scoreboard (FIFO) is used to line up the expected data in the receiver for checking off. Finally a generic can supply a non-default seed for the random number generator in order to explore alternative sequences. The data itself is not randomised for the sake of ease of debugging. Execution is not dependent on the values in the data, so this was considered unimportant.
For those using a free version of ModelSim you will find that OSVVM takes the design over a limit on statements resulting in poorer performance. Scroll the text box below down past all the libraries loaded to find the warning message. In spite of this the test bench still runs just fine. I do wonder if all those packages are really required and if a more customised package inclusion might reduce the number of statements in the calculated design size. For instance I am not aware that I using osvvm.memory* nor osvvm_common.interrupt* packages. The use of VHDL contexts does routinely lend itself to including way more than you actually for the job for the sake of some convenience.
Another observation is that out of the box this test bench creates 111 warnings over 222 lines of transcript text of the following format:
Hence the test bench includes code to silence these messages as a bit of an eye sore. OSVVM provides a function ReleaseTransactionRecord() to provide the missing driver, but this must be applied via an 'external signal' for each transaction record. I applied this by driving the three StreamRecTypes from the top level of the test bench, hence I resorted to 'external signals'. It is worth noting the equivalent call exists for OSVVM_Common.AddressBusTransactionPkg.AddressBusRecType too.
Conclusions
With this toolkit I would expect it to be easier to implement the AXI bus width conversion function as a sequence of swap and insert actions with intermittent pause, with a simple counter for an FSM driving the control logic. Another application I've mentioned is protocol conversion, an example here might be conversion between VITA-49 and OpenCPI's various protocols, where fields need to be added and at times pairs of words need to be reversed.