// Copyright © 2019-2023 // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. `include "VX_platform.vh" `TRACING_OFF module VX_scope_tap #( parameter SCOPE_ID = 0, // scope identifier parameter SCOPE_IDW = 8, // scope identifier width parameter TRIGGERW = 0, // trigger signals width parameter PROBEW = 0, // probe signal width parameter SIZE = 256, // trace buffer size parameter IDLE_CTRW = 16 // idle time between triggers counter width ) ( input wire clk, input wire reset, input wire start, input wire stop, input wire [TRIGGERW-1:0] triggers, input wire [PROBEW-1:0] probes, input wire bus_in, output wire bus_out ); localparam TX_DATAW = 64; localparam TX_DATA_BITS = `LOG2UP(TX_DATAW); localparam DATAW = PROBEW + TRIGGERW; localparam DATA_BITS = `LOG2UP(DATAW); localparam ADDRW = `CLOG2(SIZE); localparam TRIGGER_ENABLE = (TRIGGERW != 0); localparam MAX_IDLE_CTR = (2 ** IDLE_CTRW) - 1; localparam CTRL_STATE_IDLE = 2'd0; localparam CTRL_STATE_RECV = 2'd1; localparam CTRL_STATE_CMD = 2'd2; localparam CTRL_STATE_SEND = 2'd3; localparam CTRL_STATE_BITS = 2; localparam TAP_STATE_IDLE = 2'd0; localparam TAP_STATE_WAIT = 2'd1; localparam TAP_STATE_RUN = 2'd2; localparam TAP_STATE_BITS = 2; localparam CMD_GET_WIDTH = 3'd0; localparam CMD_GET_COUNT = 3'd1; localparam CMD_GET_START = 3'd2; localparam CMD_GET_DATA = 3'd3; localparam CMD_SET_START = 3'd4; localparam CMD_SET_STOP = 3'd5; localparam CMD_TYPE_BITS = 3; localparam GET_TYPE_WIDTH = 2'd0; localparam GET_TYPE_COUNT = 2'd1; localparam GET_TYPE_START = 2'd2; localparam GET_TYPE_DATA = 2'd3; localparam GET_TYPE_BITS = 2; `NO_RW_RAM_CHECK reg [DATAW-1:0] data_store [SIZE-1:0]; `NO_RW_RAM_CHECK reg [IDLE_CTRW-1:0] delta_store [SIZE-1:0]; reg [TRIGGERW-1:0] prev_triggers; reg [IDLE_CTRW-1:0] delta; reg [63:0] timestamp, start_time; reg [ADDRW-1:0] waddr, waddr_end; reg cmd_start, delta_flush; reg [63:0] start_delay, delay_cntr; reg [TAP_STATE_BITS-1:0] tap_state; reg [CTRL_STATE_BITS-1:0] ctrl_state; reg [GET_TYPE_BITS-1:0] get_type; reg [TX_DATA_BITS-1:0] ser_tx_ctr; reg [DATA_BITS-1:0] read_offset; reg [ADDRW-1:0] raddr; reg read_data; // // trace capture // wire [ADDRW-1:0] raddr_n = raddr + 1; wire [ADDRW:0] count = (ADDRW+1)'(waddr) + 1; always @(posedge clk) begin if (reset) begin tap_state <= TAP_STATE_IDLE; raddr <= '0; waddr <= '0; delta <= '0; prev_triggers <= '0; read_offset <= '0; read_data <= 0; timestamp <= '0; end else begin timestamp <= timestamp + 1; case (tap_state) TAP_STATE_IDLE: begin if (start || cmd_start) begin delta <= '0; delta_flush <= 1; if (0 == start_delay) begin tap_state <= TAP_STATE_RUN; start_time <= timestamp; `ifdef DBG_TRACE_SCOPE `TRACE(2, ("%d: *** scope #%0d: recording start - time=%0d\n", $time, SCOPE_ID, timestamp)); `endif end else begin tap_state <= TAP_STATE_WAIT; delay_cntr <= start_delay; `ifdef DBG_TRACE_SCOPE `TRACE(2, ("%d: *** scope #%0d: delayed start - time=%0d\n", $time, SCOPE_ID, start_delay)); `endif end end end TAP_STATE_WAIT: begin delay_cntr <= delay_cntr - 1; if (1 == delay_cntr) begin tap_state <= TAP_STATE_RUN; start_time <= timestamp; `ifdef DBG_TRACE_SCOPE `TRACE(2, ("%d: *** scope #%0d: recording start - time=%0d\n", $time, SCOPE_ID, timestamp)); `endif end end TAP_STATE_RUN: begin if (TRIGGER_ENABLE != 0) begin if (delta_flush || (triggers != prev_triggers)) begin data_store[waddr] <= {probes, triggers}; delta_store[waddr] <= delta; waddr <= waddr + 1; delta <= '0; delta_flush <= 0; end else begin delta <= delta + 1; delta_flush <= (delta == (MAX_IDLE_CTR-1)); end prev_triggers <= triggers; end else begin data_store[waddr] <= {probes, triggers}; delta_store[waddr] <= '0; waddr <= waddr + 1; end if (stop || (waddr >= waddr_end)) begin waddr <= waddr; `ifdef DBG_TRACE_SCOPE `TRACE(2, ("%d: *** scope #%0d: recording stop - waddr=(%0d, %0d)\n", $time, SCOPE_ID, waddr, waddr_end)); `endif tap_state <= TAP_STATE_IDLE; end end default:; endcase if (ctrl_state == CTRL_STATE_SEND && get_type == GET_TYPE_DATA && ser_tx_ctr == 0) begin if (~read_data) begin read_data <= 1; end else begin if (DATAW > TX_DATAW) begin `IGNORE_WARNINGS_BEGIN if (read_offset < DATA_BITS'(DATAW-TX_DATAW)) begin read_offset <= read_offset + DATA_BITS'(TX_DATAW); end else begin raddr <= raddr_n; read_data <= 0; read_offset <= '0; end `IGNORE_WARNINGS_END end else begin raddr <= raddr_n; read_data <= 0; end if (raddr_n == waddr) begin raddr <= 0; end end end end end // // command controller // reg bus_out_r; reg [TX_DATAW-1:0] ser_buf_in; wire [TX_DATAW-1:0] ser_buf_in_n = {ser_buf_in[TX_DATAW-2:0], bus_in}; `UNUSED_VAR (ser_buf_in) wire [CMD_TYPE_BITS-1:0] cmd_type = ser_buf_in[CMD_TYPE_BITS-1:0]; wire [SCOPE_IDW-1:0] cmd_scope_id = ser_buf_in_n[CMD_TYPE_BITS +: SCOPE_IDW]; wire [TX_DATAW-CMD_TYPE_BITS-SCOPE_IDW-1:0] cmd_data = ser_buf_in[TX_DATAW-1:CMD_TYPE_BITS+SCOPE_IDW]; wire [TX_DATAW-1:0] data_chunk = TX_DATAW'(DATAW'(data_store[raddr] >> read_offset)); wire [TX_DATAW-1:0] get_data = read_data ? data_chunk : TX_DATAW'(delta_store[raddr]); always @(posedge clk) begin if (reset) begin ctrl_state <= CTRL_STATE_IDLE; cmd_start <= 0; start_delay <= '0; waddr_end <= ADDRW'(SIZE-1); bus_out_r <= 0; end else begin bus_out_r <= 0; cmd_start <= 0; case (ctrl_state) CTRL_STATE_IDLE: begin if (bus_in) begin ctrl_state <= CTRL_STATE_RECV; end ser_tx_ctr <= TX_DATA_BITS'(TX_DATAW-1); end CTRL_STATE_RECV: begin ser_tx_ctr <= ser_tx_ctr - 1; ser_buf_in <= ser_buf_in_n; if (ser_tx_ctr == 0) begin ctrl_state <= (cmd_scope_id == SCOPE_ID) ? CTRL_STATE_CMD : CTRL_STATE_IDLE; end end CTRL_STATE_CMD: begin ctrl_state <= CTRL_STATE_IDLE; case (cmd_type) CMD_SET_START: begin start_delay <= 64'(cmd_data); cmd_start <= 1; end CMD_SET_STOP: begin waddr_end <= ADDRW'(cmd_data); end CMD_GET_WIDTH, CMD_GET_START, CMD_GET_COUNT, CMD_GET_DATA: begin ctrl_state <= CTRL_STATE_SEND; get_type <= GET_TYPE_BITS'(cmd_type); ser_tx_ctr <= TX_DATA_BITS'(TX_DATAW-1); bus_out_r <= 1; end default:; endcase `ifdef DBG_TRACE_SCOPE `TRACE(2, ("%d: *** scope #%0d: CMD: type=%0d\n", $time, SCOPE_ID, cmd_type)); `endif end CTRL_STATE_SEND: begin ser_tx_ctr <= ser_tx_ctr - 1; case (get_type) GET_TYPE_WIDTH: begin bus_out_r <= 1'(DATAW >> ser_tx_ctr); `ifdef DBG_TRACE_SCOPE if (ser_tx_ctr == 0) begin `TRACE(2, ("%d: *** scope #%0d: SEND width=%0d\n", $time, SCOPE_ID, DATAW)); end `endif end GET_TYPE_COUNT: begin bus_out_r <= 1'(count >> ser_tx_ctr); `ifdef DBG_TRACE_SCOPE if (ser_tx_ctr == 0) begin `TRACE(2, ("%d: *** scope #%0d: SEND count=%0d\n", $time, SCOPE_ID, count)); end `endif end GET_TYPE_START: begin bus_out_r <= 1'(start_time >> ser_tx_ctr); `ifdef DBG_TRACE_SCOPE if (ser_tx_ctr == 0) begin `TRACE(2, ("%d: *** scope #%0d: SEND start=%0d\n", $time, SCOPE_ID, start_time)); end `endif end GET_TYPE_DATA: begin bus_out_r <= 1'(get_data >> ser_tx_ctr); `ifdef DBG_TRACE_SCOPE if (ser_tx_ctr == 0) begin `TRACE(2, ("%d: *** scope #%0d: SEND data=%0d\n", $time, SCOPE_ID, get_data)); end `endif end default:; endcase if (ser_tx_ctr == 0) begin ctrl_state <= CTRL_STATE_IDLE; end end default:; endcase end end assign bus_out = bus_out_r; endmodule `TRACING_ON