Coping without Signaltap – Part 2 – 2022-04-26
By the end of part 1 I was able to communicate over JTAG with a design running on the IceSugarPro, remotely control the LEDs on the board and read the contents of a wide register through a FIFO queue.
That’s the barebones of a useful debugging subsystem, but to be truly useful we need the ability to set a trigger condition.
Since we have two user registers available with the ECP5 JTAGG component, I’m going to use the first one as a command register, and the second one as a data register. I will define commands to start a capture, to set trigger conditions and to write to an output register.
I will define the trigger condition in terms of “Mask”, “Invert” and “Edge”, as follows:
- A zero bit in the Mask is considered a “don’t care” bit, while a one bit indicates a bit which must match the condition.
- Bits selected by the Mask register will be considered to match if they are zero, unless the corresponding bit is set in the Invert register. In other words, active bits in the incoming signal must match the contents of the Invert register.
- If the corresponding bit is set in the Edge register, then it becomes edge-sensitive rather than level-sensitive; falling-edge if the corresponding invert bit is zero, rising edge otherwise. For the moment I’m not going to worry about implementing “either-edge” sensitivity.
The logic required to provide these facilities is as follows:
- Let the incoming data be V(n).
- Let V'(n) = V(n) xor Invert
- Let P'(n) = V'(n-1), i.e. the previous value of V’
From the variables V’ and P’, along with Mask and Edge , we want the following truth table (where the condition is considered matched when the output is zero):
|0||0||0||1||X (don’t care)|
(For now I consider the case where E is set and mask isn’t set to be an illegal combination, hence the “don’t care” bits in the table.) The table can be reduced to the boolean expression ((not P’) and M and E) or (V’ and M).
So we simply wait for a Capture command, then wait for the trigger condition to be satisfied and fill the FIFO with data, yes? Easy!
Well… no. Firstly, it will take a couple of cycles to match the trigger condition and start recording, so the actual event we’re using as a trigger won’t be recorded. It’s also useful to be able to see some context in the run-up to the trigger event – so it would also be useful to have some “lead-in”.
The way I’ve implemented this is to allow the FIFO to freewheel while waiting for the trigger condition, so it’s constantly filling with data. As the lead-in ends, the FIFO’s read pointer is set to the write pointer – an offset, which can be a quarter, a half or three quarters of the FIFO’s depth. Captured data will fill the remainder of the FIFO, until the write pointer catches up with the newly set read pointer.During capture the FIFO fills when it reaches the read pointer.
The only difficulty with this was in the clock-domain crossing: the read side of the FIFO is clocked by the JTAG interface, and thus may very well be completely static during the entire capture process. For this reason the write side needs to maintain its own multiplexed “full” pointer which caches the lead-in read pointer until it can be confident that the read side has latched and adopted the new pointer.
The project, along with an example tcl script, can be found in the jcapture directory of the IceSugarPro_Tests repo on github.
So now maybe I can use it to find out why EightThirtyTwo’s interrupts don’t work under Yosys and friends…