Files
kernels/hw/rtl/libs/VX_scope_tap.sv
Blaise Tine d47cccc157 Vortex 2.0 changes:
+ Microarchitecture optimizations
+ 64-bit support
+ Xilinx FPGA support
+ LLVM-16 support
+ Refactoring and quality control fixes
2023-10-19 20:51:22 -07:00

314 lines
12 KiB
Systemverilog

// 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