+ Microarchitecture optimizations + 64-bit support + Xilinx FPGA support + LLVM-16 support + Refactoring and quality control fixes
314 lines
12 KiB
Systemverilog
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
|