From 7e2221507aeb1db27b2879ec74d2ad19061b37ad Mon Sep 17 00:00:00 2001 From: Kip Walker Date: Thu, 18 Apr 2024 10:12:41 -0700 Subject: [PATCH] [dv/dpi] jtagdpi lookahead for 'R' on TCK posedge Clients performing a shift operation with data readback will be sending a sequence of commands that toggle TCK, providing the data to shift in, interleaved with read commands to sample the data shifted out. In the current implementation where a single command is processed on each jtagdpi tick, this leads to an uneven TCK duty cycle (i.e. 3 jtagdpi ticks per TCK cycle) whenever data read commands are present. The JTAG spec dictates that TDO values change on the falling edge of TCK and so should be sampled around the rising edge. The command processing is changed to support a "lookahead" to attempt to pull a read command back to the same jtagdpi tick where a TCK rising edge command is handled. With this lookahead, a client that does (TCK falling edge, TCK rising edge, read TDO) will complete its TCK cycle in just 2 jtagdpi ticks. In theory a client that generates a sequence like (TCK falling edge, read TDO, TCK rising edge) could also lookahead from the read command for a TCK rising edge. However, such a client is likely to read the data before sending the rising edge command - so a successful lookahead would incur an additional network round-trip to the client. On the other hand, the client-side change to take advantage of this optimization is trivial. Signed-off-by: Kip Walker --- hw/dv/dpi/jtagdpi/jtagdpi.c | 39 ++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/hw/dv/dpi/jtagdpi/jtagdpi.c b/hw/dv/dpi/jtagdpi/jtagdpi.c index 7b4614a42b9b2..4b90a1e31cf80 100644 --- a/hw/dv/dpi/jtagdpi/jtagdpi.c +++ b/hw/dv/dpi/jtagdpi/jtagdpi.c @@ -22,8 +22,37 @@ struct jtagdpi_ctx { uint8_t tdo; uint8_t trst_n; uint8_t srst_n; + // Lookahead buffer - non-zero if valid + char cmd; }; +static bool lookahead(struct jtagdpi_ctx *ctx) { + // Look at the next command if available. Return true if it's an + // 'R', otherwise buffer it to return via get_cmd(). + char cmd; + if (!tcp_server_read(ctx->sock, &cmd)) { + return false; + } + if (cmd == 'R') { + return true; + } else { + ctx->cmd = cmd; + return false; + } +} + +static bool get_cmd(struct jtagdpi_ctx *ctx, char *cmd) { + // Return a buffered command if available, or try to pull one from + // the socket. + if (ctx->cmd) { + *cmd = ctx->cmd; + ctx->cmd = 0; + return true; + } else { + return tcp_server_read(ctx->sock, cmd); + } +} + /** * Reset the JTAG signals to a "dongle unplugged" state */ @@ -56,7 +85,7 @@ static void update_jtag_signals(struct jtagdpi_ctx *ctx) { // read a command byte char cmd; - if (!tcp_server_read(ctx->sock, &cmd)) { + if (!get_cmd(ctx, &cmd)) { return; } @@ -66,10 +95,18 @@ static void update_jtag_signals(struct jtagdpi_ctx *ctx) { // parse received command byte if (cmd >= '0' && cmd <= '7') { // JTAG write + uint8_t tck = ctx->tck; char cmd_bit = cmd - '0'; ctx->tdi = (cmd_bit >> 0) & 0x1; ctx->tms = (cmd_bit >> 1) & 0x1; ctx->tck = (cmd_bit >> 2) & 0x1; + // On a rising edge of TCK, we can process a following 'R' command + // to sense the current TDO without waiting for the next DPI + // callback. Since TDO changes on the falling edge of TCK, it is + // already stable and valid. + if (!tck && ctx->tck && lookahead(ctx)) { + act_send_resp = true; + } } else if (cmd >= 'r' && cmd <= 'u') { // JTAG reset (active high from OpenOCD) char cmd_bit = cmd - 'r';