1
0
mirror of https://github.com/corundum/corundum.git synced 2025-01-16 08:12:53 +08:00
corundum/fpga/common/rtl/tdma_ber_ch.v
Alex Forencich 448fa8eb4c Use SPDX
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-06-26 11:44:57 -07:00

575 lines
20 KiB
Verilog

// SPDX-License-Identifier: BSD-2-Clause-Views
/*
* Copyright (c) 2019-2023 The Regents of the University of California
*/
// Language: Verilog 2001
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* TDMA BER module
*/
module tdma_ber_ch #
(
// Timeslot index width
parameter INDEX_WIDTH = 6,
// Slice index width
parameter SLICE_WIDTH = 5,
// Width of AXI lite data bus in bits
parameter AXIL_DATA_WIDTH = 32,
// Width of AXI lite address bus in bits
parameter AXIL_ADDR_WIDTH = INDEX_WIDTH+4,
// Width of AXI lite wstrb (width of data bus in words)
parameter AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8),
// pipeline stages on PHY interface ports
parameter PHY_PIPELINE = 0
)
(
input wire clk,
input wire rst,
/*
* PHY connections
*/
input wire phy_tx_clk,
input wire phy_rx_clk,
input wire [6:0] phy_rx_error_count,
output wire phy_tx_prbs31_enable,
output wire phy_rx_prbs31_enable,
/*
* AXI-Lite slave interface
*/
input wire [AXIL_ADDR_WIDTH-1:0] s_axil_awaddr,
input wire [2:0] s_axil_awprot,
input wire s_axil_awvalid,
output wire s_axil_awready,
input wire [AXIL_DATA_WIDTH-1:0] s_axil_wdata,
input wire [AXIL_STRB_WIDTH-1:0] s_axil_wstrb,
input wire s_axil_wvalid,
output wire s_axil_wready,
output wire [1:0] s_axil_bresp,
output wire s_axil_bvalid,
input wire s_axil_bready,
input wire [AXIL_ADDR_WIDTH-1:0] s_axil_araddr,
input wire [2:0] s_axil_arprot,
input wire s_axil_arvalid,
output wire s_axil_arready,
output wire [AXIL_DATA_WIDTH-1:0] s_axil_rdata,
output wire [1:0] s_axil_rresp,
output wire s_axil_rvalid,
input wire s_axil_rready,
/*
* TDMA schedule
*/
input wire [INDEX_WIDTH-1:0] tdma_timeslot_index,
input wire tdma_timeslot_start,
input wire tdma_timeslot_active
);
parameter VALID_ADDR_WIDTH = AXIL_ADDR_WIDTH - $clog2(AXIL_STRB_WIDTH);
parameter WORD_WIDTH = AXIL_STRB_WIDTH;
parameter WORD_SIZE = AXIL_DATA_WIDTH/WORD_WIDTH;
// check configuration
initial begin
if (AXIL_ADDR_WIDTH < INDEX_WIDTH+4) begin
$error("Error: AXI address width too narrow (instance %m)");
$finish;
end
if (AXIL_DATA_WIDTH != 32) begin
$error("Error: AXI data width must be 32 (instance %m)");
$finish;
end
if (AXIL_STRB_WIDTH * 8 != AXIL_DATA_WIDTH) begin
$error("Error: Interface requires byte (8-bit) granularity (instance %m)");
$finish;
end
end
reg tx_prbs31_enable_reg = 1'b0, tx_prbs31_enable_next;
reg rx_prbs31_enable_reg = 1'b0, rx_prbs31_enable_next;
// PHY TX BER interface
reg phy_tx_prbs31_enable_reg = 1'b0;
reg [PHY_PIPELINE-1:0] phy_tx_prbs31_enable_pipe_reg = 0;
always @(posedge phy_tx_clk) begin
phy_tx_prbs31_enable_reg <= tx_prbs31_enable_reg;
phy_tx_prbs31_enable_pipe_reg <= {phy_tx_prbs31_enable_pipe_reg, phy_tx_prbs31_enable_reg};
end
assign phy_tx_prbs31_enable = PHY_PIPELINE ? phy_tx_prbs31_enable_pipe_reg[PHY_PIPELINE-1] : phy_tx_prbs31_enable_reg;
// PHY RX BER interface
reg phy_rx_prbs31_enable_reg = 1'b0;
reg [PHY_PIPELINE-1:0] phy_rx_prbs31_enable_pipe_reg = 0;
reg [PHY_PIPELINE*7-1:0] phy_rx_error_count_pipe_reg = 0;
// accumulate errors, dump every 16 cycles
reg [10:0] phy_rx_error_count_reg = 0;
reg [10:0] phy_rx_error_count_acc_reg = 0;
reg [3:0] phy_rx_count_reg = 4'd0;
reg phy_rx_flag_reg = 1'b0;
always @(posedge phy_rx_clk) begin
phy_rx_prbs31_enable_reg <= rx_prbs31_enable_reg;
phy_rx_prbs31_enable_pipe_reg <= {phy_rx_prbs31_enable_pipe_reg, phy_rx_prbs31_enable_reg};
phy_rx_error_count_pipe_reg <= {phy_rx_error_count_pipe_reg, phy_rx_error_count};
phy_rx_count_reg <= phy_rx_count_reg + 1;
if (phy_rx_count_reg == 0) begin
phy_rx_error_count_reg <= phy_rx_error_count_acc_reg;
phy_rx_error_count_acc_reg <= PHY_PIPELINE ? phy_rx_error_count_pipe_reg[(PHY_PIPELINE-1)*7 +: 7] : phy_rx_error_count;
phy_rx_flag_reg <= !phy_rx_flag_reg;
end else begin
phy_rx_error_count_acc_reg <= phy_rx_error_count_acc_reg + (PHY_PIPELINE ? phy_rx_error_count_pipe_reg[(PHY_PIPELINE-1)*7 +: 7] : phy_rx_error_count);
end
end
assign phy_rx_prbs31_enable = PHY_PIPELINE ? phy_rx_prbs31_enable_pipe_reg[PHY_PIPELINE-1] : phy_rx_prbs31_enable_reg;
// synchronize dumped counts to control clock domain
reg rx_flag_sync_reg_1 = 1'b0;
reg rx_flag_sync_reg_2 = 1'b0;
reg rx_flag_sync_reg_3 = 1'b0;
always @(posedge clk) begin
rx_flag_sync_reg_1 <= phy_rx_flag_reg;
rx_flag_sync_reg_2 <= rx_flag_sync_reg_1;
rx_flag_sync_reg_3 <= rx_flag_sync_reg_2;
end
reg [31:0] cycle_count_reg = 32'd0, cycle_count_next;
reg [31:0] update_count_reg = 32'd0, update_count_next;
reg [31:0] rx_error_count_reg = 32'd0, rx_error_count_next;
reg [31:0] atomic_cycle_count_reg = 32'd0, atomic_cycle_count_next;
reg [31:0] atomic_update_count_reg = 32'd0, atomic_update_count_next;
reg [31:0] atomic_rx_error_count_reg = 32'd0, atomic_rx_error_count_next;
reg accumulate_enable_reg = 1'b0, accumulate_enable_next;
reg slice_enable_reg = 1'b0, slice_enable_next;
reg [31:0] slice_time_reg = 0, slice_time_next;
reg [31:0] slice_offset_reg = 0, slice_offset_next;
reg [SLICE_WIDTH-1:0] slice_select_reg = 0, slice_select_next;
reg error_count_mem_read_reg = 0;
reg [31:0] error_count_mem_read_data_reg = 0;
reg update_count_mem_read_reg = 0;
reg [31:0] update_count_mem_read_data_reg = 0;
reg [31:0] error_count_mem[(2**(INDEX_WIDTH+SLICE_WIDTH))-1:0];
reg [31:0] update_count_mem[(2**(INDEX_WIDTH+SLICE_WIDTH))-1:0];
integer i;
initial begin
for (i = 0; i < 2**(INDEX_WIDTH+SLICE_WIDTH); i = i + 1) begin
error_count_mem[i] = 0;
update_count_mem[i] = 0;
end
end
reg [10:0] phy_rx_error_count_sync_reg = 0;
reg phy_rx_error_count_sync_valid_reg = 1'b0;
always @(posedge clk) begin
phy_rx_error_count_sync_valid_reg <= 1'b0;
if (rx_flag_sync_reg_2 ^ rx_flag_sync_reg_3) begin
phy_rx_error_count_sync_reg <= phy_rx_error_count_reg;
phy_rx_error_count_sync_valid_reg <= 1'b1;
end
end
reg slice_running_reg = 1'b0;
reg slice_active_reg = 1'b0;
reg [31:0] slice_count_reg = 0;
reg [SLICE_WIDTH-1:0] cur_slice_reg = 0;
reg [1:0] accumulate_state_reg = 0;
reg [31:0] rx_ts_update_count_read_reg = 0;
reg [31:0] rx_ts_error_count_read_reg = 0;
reg [31:0] rx_ts_update_count_reg = 0;
reg [31:0] rx_ts_error_count_reg = 0;
reg [INDEX_WIDTH+SLICE_WIDTH-1:0] index_reg = 0;
always @(posedge clk) begin
if (tdma_timeslot_start) begin
slice_running_reg <= 1'b1;
if (slice_offset_reg) begin
slice_active_reg <= 1'b0;
slice_count_reg <= slice_offset_reg;
end else begin
slice_active_reg <= 1'b1;
slice_count_reg <= slice_time_reg;
end
cur_slice_reg <= 0;
end else if (slice_count_reg > 0) begin
slice_count_reg <= slice_count_reg - 1;
end else begin
slice_count_reg <= slice_time_reg;
slice_active_reg <= slice_running_reg;
if (slice_active_reg && slice_running_reg) begin
cur_slice_reg <= cur_slice_reg + 1;
slice_running_reg <= cur_slice_reg < {SLICE_WIDTH{1'b1}};
slice_active_reg <= cur_slice_reg < {SLICE_WIDTH{1'b1}};
end
end
case (accumulate_state_reg)
2'd0: begin
if (accumulate_enable_reg && tdma_timeslot_active && phy_rx_error_count_sync_valid_reg) begin
if (slice_enable_reg) begin
index_reg <= (tdma_timeslot_index << SLICE_WIDTH) + cur_slice_reg;
if (slice_active_reg) begin
accumulate_state_reg <= 2'd1;
end
end else begin
index_reg <= (tdma_timeslot_index << SLICE_WIDTH);
accumulate_state_reg <= 2'd1;
end
end
end
2'd1: begin
rx_ts_update_count_read_reg <= update_count_mem[index_reg];
rx_ts_error_count_read_reg <= error_count_mem[index_reg];
accumulate_state_reg <= 2'd2;
end
2'd2: begin
rx_ts_error_count_reg <= rx_ts_error_count_read_reg + phy_rx_error_count_sync_reg;
rx_ts_update_count_reg <= rx_ts_update_count_read_reg + 1;
// if ((rx_ts_error_count_reg + inc_reg) >> 32) begin
// rx_ts_error_count_reg <= 32'hffffffff;
// end else begin
// rx_ts_error_count_reg <= rx_ts_error_count_reg + inc_reg;
// end
// if ((rx_ts_update_count_reg + 1) >> 32) begin
// rx_ts_update_count_reg <= 32'hffffffff;
// end else begin
// rx_ts_update_count_reg <= rx_ts_update_count_reg + 1;
// end
accumulate_state_reg <= 2'd3;
end
2'd3: begin
update_count_mem[index_reg] <= rx_ts_update_count_reg;
error_count_mem[index_reg] <= rx_ts_error_count_reg;
accumulate_state_reg <= 2'd0;
end
default: begin
accumulate_state_reg <= 2'd0;
end
endcase
end
// control registers
reg read_eligible;
reg write_eligible;
reg mem_wr_en;
reg mem_rd_en;
reg last_read_reg = 1'b0, last_read_next;
reg s_axil_awready_reg = 1'b0, s_axil_awready_next;
reg s_axil_wready_reg = 1'b0, s_axil_wready_next;
reg s_axil_bvalid_reg = 1'b0, s_axil_bvalid_next;
reg s_axil_arready_reg = 1'b0, s_axil_arready_next;
reg [AXIL_DATA_WIDTH-1:0] s_axil_rdata_reg = {AXIL_DATA_WIDTH{1'b0}}, s_axil_rdata_next;
reg s_axil_rvalid_reg = 1'b0, s_axil_rvalid_next;
assign s_axil_awready = s_axil_awready_reg;
assign s_axil_wready = s_axil_wready_reg;
assign s_axil_bresp = 2'b00;
assign s_axil_bvalid = s_axil_bvalid_reg;
assign s_axil_arready = s_axil_arready_reg;
assign s_axil_rdata = error_count_mem_read_reg ? error_count_mem_read_data_reg : (update_count_mem_read_reg ? update_count_mem_read_data_reg : s_axil_rdata_reg);
assign s_axil_rresp = 2'b00;
assign s_axil_rvalid = s_axil_rvalid_reg;
wire [INDEX_WIDTH+SLICE_WIDTH-1:0] axil_ram_addr = ((mem_rd_en ? s_axil_araddr[INDEX_WIDTH+1+2-1:1+2] : s_axil_awaddr[INDEX_WIDTH+1+2-1:1+2]) << SLICE_WIDTH) | slice_select_reg;
always @* begin
mem_wr_en = 1'b0;
mem_rd_en = 1'b0;
last_read_next = last_read_reg;
s_axil_awready_next = 1'b0;
s_axil_wready_next = 1'b0;
s_axil_bvalid_next = s_axil_bvalid_reg && !s_axil_bready;
s_axil_arready_next = 1'b0;
s_axil_rdata_next = s_axil_rdata_reg;
s_axil_rvalid_next = s_axil_rvalid_reg && !s_axil_rready;
cycle_count_next = cycle_count_reg + 1;
update_count_next = update_count_reg;
rx_error_count_next = rx_error_count_reg;
if (phy_rx_error_count_sync_valid_reg) begin
update_count_next = update_count_reg + 1;
rx_error_count_next = phy_rx_error_count_sync_reg + rx_error_count_reg;
end
atomic_cycle_count_next = atomic_cycle_count_reg + 1;
if (atomic_cycle_count_reg[31] && ~atomic_cycle_count_next[31]) begin
atomic_cycle_count_next = 32'hffffffff;
end
atomic_update_count_next = atomic_update_count_reg;
atomic_rx_error_count_next = atomic_rx_error_count_reg;
if (phy_rx_error_count_sync_valid_reg) begin
atomic_update_count_next = atomic_update_count_reg + 1;
atomic_rx_error_count_next = phy_rx_error_count_sync_reg + atomic_rx_error_count_reg;
// saturate
if (atomic_update_count_reg[31] && ~atomic_update_count_next[31]) begin
atomic_update_count_next = 32'hffffffff;
end
if (atomic_rx_error_count_reg[31] && ~atomic_rx_error_count_next[31]) begin
atomic_rx_error_count_next = 32'hffffffff;
end
end
accumulate_enable_next = accumulate_enable_reg;
slice_enable_next = slice_enable_reg;
slice_time_next = slice_time_reg;
slice_offset_next = slice_offset_reg;
slice_select_next = slice_select_reg;
tx_prbs31_enable_next = tx_prbs31_enable_reg;
rx_prbs31_enable_next = rx_prbs31_enable_reg;
write_eligible = s_axil_awvalid && s_axil_wvalid && (!s_axil_bvalid || s_axil_bready) && (!s_axil_awready && !s_axil_wready);
read_eligible = s_axil_arvalid && (!s_axil_rvalid || s_axil_rready) && (!s_axil_arready);
if (write_eligible && (!read_eligible || last_read_reg)) begin
last_read_next = 1'b0;
s_axil_awready_next = 1'b1;
s_axil_wready_next = 1'b1;
s_axil_bvalid_next = 1'b1;
if (s_axil_awaddr[INDEX_WIDTH+2+1+1-1]) begin
mem_wr_en = 1'b1;
end else begin
case (s_axil_awaddr & ({AXIL_ADDR_WIDTH{1'b1}} << 2))
16'h0000: begin
// control
tx_prbs31_enable_next = s_axil_wdata[0];
rx_prbs31_enable_next = s_axil_wdata[1];
end
16'h0004: begin
// cycle count
cycle_count_next = 1;
end
16'h0008: begin
// update count
update_count_next = 0;
end
16'h000C: begin
// error count
rx_error_count_next = 0;
end
16'h0014: begin
// cycle count
atomic_cycle_count_next = 1;
end
16'h0018: begin
// update count
atomic_update_count_next = 0;
end
16'h001C: begin
// error count
atomic_rx_error_count_next = 0;
end
16'h0020: begin
// control
accumulate_enable_next = s_axil_wdata[0];
slice_enable_next = s_axil_wdata[1];
end
16'h0024: begin
// slice time
slice_time_next = s_axil_wdata;
end
16'h0028: begin
// slice offset
slice_offset_next = s_axil_wdata;
end
16'h0030: begin
// slice select
slice_select_next = s_axil_wdata;
end
endcase
end
end else if (read_eligible) begin
last_read_next = 1'b1;
s_axil_arready_next = 1'b1;
s_axil_rvalid_next = 1'b1;
s_axil_rdata_next = {AXIL_DATA_WIDTH{1'b0}};
if (s_axil_araddr[INDEX_WIDTH+2+1+1-1]) begin
mem_rd_en = 1'b1;
end else begin
case (s_axil_araddr & ({AXIL_ADDR_WIDTH{1'b1}} << 2))
16'h0000: begin
// control
s_axil_rdata_next[0] = tx_prbs31_enable_reg;
s_axil_rdata_next[1] = rx_prbs31_enable_reg;
end
16'h0004: begin
// cycle count
s_axil_rdata_next = cycle_count_reg;
end
16'h0008: begin
// update count
s_axil_rdata_next = update_count_reg;
end
16'h000C: begin
// error count
s_axil_rdata_next = rx_error_count_reg;
end
16'h0014: begin
// cycle count
s_axil_rdata_next = atomic_cycle_count_reg;
atomic_cycle_count_next = 1;
end
16'h0018: begin
// update count
s_axil_rdata_next = atomic_update_count_reg;
atomic_update_count_next = 0;
end
16'h001C: begin
// error count
s_axil_rdata_next = atomic_rx_error_count_reg;
atomic_rx_error_count_next = 0;
end
16'h0020: begin
// control
s_axil_rdata_next[0] = accumulate_enable_reg;
s_axil_rdata_next[1] = slice_enable_reg;
end
16'h0024: begin
// slice time
s_axil_rdata_next = slice_time_reg;
end
16'h0028: begin
// slice offset
s_axil_rdata_next = slice_offset_reg;
end
16'h0030: begin
// slice select
s_axil_rdata_next = slice_select_reg;
end
endcase
end
end
end
always @(posedge clk) begin
if (rst) begin
last_read_reg <= 1'b0;
s_axil_awready_reg <= 1'b0;
s_axil_wready_reg <= 1'b0;
s_axil_bvalid_reg <= 1'b0;
s_axil_arready_reg <= 1'b0;
s_axil_rvalid_reg <= 1'b0;
cycle_count_reg <= 32'd0;
update_count_reg <= 32'd0;
rx_error_count_reg <= 32'd0;
atomic_cycle_count_reg <= 32'd0;
atomic_update_count_reg <= 32'd0;
atomic_rx_error_count_reg <= 32'd0;
accumulate_enable_reg <= 1'b0;
slice_enable_reg <= 1'b0;
slice_time_reg <= 32'd0;
slice_offset_reg <= 32'd0;
slice_select_reg <= 0;
tx_prbs31_enable_reg <= 1'b0;
rx_prbs31_enable_reg <= 1'b0;
end else begin
last_read_reg <= last_read_next;
s_axil_awready_reg <= s_axil_awready_next;
s_axil_wready_reg <= s_axil_wready_next;
s_axil_bvalid_reg <= s_axil_bvalid_next;
s_axil_arready_reg <= s_axil_arready_next;
s_axil_rvalid_reg <= s_axil_rvalid_next;
cycle_count_reg <= cycle_count_next;
update_count_reg <= update_count_next;
rx_error_count_reg <= rx_error_count_next;
atomic_cycle_count_reg <= atomic_cycle_count_next;
atomic_update_count_reg <= atomic_update_count_next;
atomic_rx_error_count_reg <= atomic_rx_error_count_next;
accumulate_enable_reg <= accumulate_enable_next;
slice_enable_reg <= slice_enable_next;
slice_time_reg <= slice_time_next;
slice_offset_reg <= slice_offset_next;
slice_select_reg <= slice_select_next;
tx_prbs31_enable_reg <= tx_prbs31_enable_next;
rx_prbs31_enable_reg <= rx_prbs31_enable_next;
end
s_axil_rdata_reg <= s_axil_rdata_next;
error_count_mem_read_reg <= 1'b0;
update_count_mem_read_reg <= 1'b0;
if (mem_rd_en) begin
if (s_axil_araddr[2]) begin
error_count_mem_read_data_reg <= error_count_mem[axil_ram_addr];
error_count_mem_read_reg <= 1'b1;
end else begin
update_count_mem_read_data_reg <= update_count_mem[axil_ram_addr];
update_count_mem_read_reg <= 1'b1;
end
end else begin
for (i = 0; i < WORD_WIDTH; i = i + 1) begin
if (mem_wr_en && s_axil_wstrb[i]) begin
if (s_axil_awaddr[2]) begin
error_count_mem[axil_ram_addr][WORD_SIZE*i +: WORD_SIZE] <= s_axil_wdata[WORD_SIZE*i +: WORD_SIZE];
end else begin
update_count_mem[axil_ram_addr][WORD_SIZE*i +: WORD_SIZE] <= s_axil_wdata[WORD_SIZE*i +: WORD_SIZE];
end
end
end
end
end
endmodule
`resetall