Squashed B200 FPGA Source. Code from Josh Blum, Ian Buckley, and Matt Ettus.
Original-commit: 0df4b801a34697f2058b4a7b95e08d2a0576c9db
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
#
|
||||
# Copyright 2013 Ettus Research LLC
|
||||
#
|
||||
|
||||
##################################################
|
||||
# Control Lib Sources
|
||||
##################################################
|
||||
CONTROL_LIB_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/control/, \
|
||||
reset_sync.v \
|
||||
por_gen.v \
|
||||
gpio_atr.v \
|
||||
simple_spi_core.v \
|
||||
simple_i2c_core.v \
|
||||
setting_reg.v \
|
||||
settings_bus_crossclock.v \
|
||||
radio_ctrl_proc.v \
|
||||
ram_2port.v \
|
||||
axi_crossbar.v \
|
||||
axi_slave_mux.v \
|
||||
axi_fifo_header.v \
|
||||
arb_qualify_master.v \
|
||||
axi_forwarding_cam.v \
|
||||
axi_test_vfifo.v \
|
||||
dram_2port.v \
|
||||
cvita_uart.v \
|
||||
))
|
||||
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// This module forms the qualification engine for a single master as
|
||||
// part of a larger arbitration engine for a slave. It would typically
|
||||
// be instantiated from arb_select_master.v to form a complete arbitor solution.
|
||||
//
|
||||
|
||||
module arb_qualify_master
|
||||
#(
|
||||
parameter WIDTH=16 // Bit width of destination field.
|
||||
)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
// Header signals
|
||||
input [WIDTH-1:0] header,
|
||||
input header_valid,
|
||||
// Slave Confg Signals
|
||||
input [WIDTH-1:0] slave_addr,
|
||||
input [WIDTH-1:0] slave_mask,
|
||||
input slave_valid,
|
||||
// Arbitration flags
|
||||
output reg master_valid,
|
||||
input master_ack
|
||||
);
|
||||
|
||||
localparam WAIT_HEADER_VALID = 0;
|
||||
localparam MATCH = 1;
|
||||
localparam WAIT_HEADER_NOT_VALID = 2;
|
||||
|
||||
|
||||
reg [1:0] state, next_state;
|
||||
|
||||
|
||||
// Does masked slave address match header field for dest from master?
|
||||
assign header_match = ((header & slave_mask) == (slave_addr & slave_mask)) && slave_valid;
|
||||
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
state <= WAIT_HEADER_VALID;
|
||||
master_valid <= 0;
|
||||
end else
|
||||
begin
|
||||
case(state)
|
||||
//
|
||||
// Wait here until Masters FIFO presents a valid header word.
|
||||
//
|
||||
WAIT_HEADER_VALID: begin
|
||||
if (header_valid)
|
||||
if (header_match) begin
|
||||
state <= MATCH;
|
||||
master_valid <= 1;
|
||||
end else
|
||||
next_state <= WAIT_HEADER_NOT_VALID;
|
||||
end
|
||||
//
|
||||
// There should only ever be one match across various arbitors
|
||||
// if they are configured correctly and since the backing FIFO in the
|
||||
// master should not start to drain until the arbitration is won
|
||||
// by that master, master_ack should always preceed de-assertion of
|
||||
// header_valid so we don't check for the other order of deassertion.
|
||||
//
|
||||
MATCH: begin
|
||||
if (master_ack) begin
|
||||
master_valid <= 0;
|
||||
state <= WAIT_HEADER_NOT_VALID;
|
||||
end
|
||||
end
|
||||
//
|
||||
// Wait here until this master starts to drain this packet from his FIFO.
|
||||
//
|
||||
WAIT_HEADER_NOT_VALID: begin
|
||||
if (!header_valid) begin
|
||||
state <= WAIT_HEADER_VALID;
|
||||
end
|
||||
end
|
||||
endcase // case(state)
|
||||
end // else: !if(reset | clear)
|
||||
|
||||
endmodule // arb_qualify_master
|
||||
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
`define LOG2(N) (\
|
||||
N < 2 ? 0 : \
|
||||
N < 4 ? 1 : \
|
||||
N < 8 ? 2 : \
|
||||
N < 16 ? 3 : \
|
||||
N < 32 ? 4 : \
|
||||
N < 64 ? 5 : \
|
||||
N < 128 ? 6 : \
|
||||
N < 256 ? 7 : \
|
||||
N < 512 ? 8 : \
|
||||
N < 1024 ? 9 : \
|
||||
10)
|
||||
|
||||
module axi_crossbar
|
||||
#(
|
||||
parameter FIFO_WIDTH = 64, // AXI4-STREAM data bus width
|
||||
parameter DST_WIDTH = 16, // Width of DST field we are routing on.
|
||||
parameter NUM_INPUTS = 2, // number of input AXI4-STREAM buses
|
||||
parameter NUM_OUTPUTS = 2 // number of output AXI4-STREAM buses
|
||||
)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
input [7:0] local_addr,
|
||||
// Inputs
|
||||
input [(FIFO_WIDTH*NUM_INPUTS)-1:0] i_tdata,
|
||||
input [NUM_INPUTS-1:0] i_tvalid,
|
||||
input [NUM_INPUTS-1:0] i_tlast,
|
||||
output [NUM_INPUTS-1:0] i_tready,
|
||||
input [NUM_INPUTS-1:0] pkt_present,
|
||||
// Setting Bus
|
||||
input set_stb,
|
||||
input [15:0] set_addr,
|
||||
input [31:0] set_data,
|
||||
// Output
|
||||
output [(FIFO_WIDTH*NUM_OUTPUTS)-1:0] o_tdata,
|
||||
output [NUM_OUTPUTS-1:0] o_tvalid,
|
||||
output [NUM_OUTPUTS-1:0] o_tlast,
|
||||
input [NUM_OUTPUTS-1:0] o_tready,
|
||||
// readback bus
|
||||
input rb_rd_stb,
|
||||
input [`LOG2(NUM_OUTPUTS)+`LOG2(NUM_INPUTS)-1:0] rb_addr,
|
||||
output [31:0] rb_data
|
||||
);
|
||||
|
||||
genvar m,n;
|
||||
|
||||
wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_valid_in;
|
||||
wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_ack_in;
|
||||
wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_valid_out;
|
||||
wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_ack_out;
|
||||
|
||||
wire [NUM_INPUTS-1:0] i_tready_slave [0:NUM_OUTPUTS-1];
|
||||
|
||||
//
|
||||
// Instantiate an axi_slave_mux for every slave/output of the Crossbar switch.
|
||||
// Each axi_slave_mux contains logic to maux and resolve arbitration
|
||||
// for this particular slave/output.
|
||||
//
|
||||
|
||||
generate
|
||||
for (m = 0; m < NUM_OUTPUTS; m = m + 1) begin: instantiate_slave_mux
|
||||
|
||||
wire [NUM_INPUTS-1:0] i_tready_tmp;
|
||||
|
||||
axi_slave_mux
|
||||
#(
|
||||
.FIFO_WIDTH(FIFO_WIDTH), // AXI4-STREAM data bus width
|
||||
.DST_WIDTH(DST_WIDTH), // Width of DST field we are routing on.
|
||||
.NUM_INPUTS(NUM_INPUTS) // number of input AXI buses
|
||||
) axi_slave_mux_i
|
||||
(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
// Inputs
|
||||
.i_tdata(i_tdata),
|
||||
.i_tvalid(i_tvalid),
|
||||
.i_tlast(i_tlast),
|
||||
.i_tready(i_tready_tmp),
|
||||
// Forwarding flags (One from each Input/Master)
|
||||
.forward_valid(forward_valid_in[(m+1)*NUM_INPUTS-1:m*NUM_INPUTS]),
|
||||
.forward_ack(forward_ack_out[(m+1)*NUM_INPUTS-1:m*NUM_INPUTS]),
|
||||
// Output
|
||||
.o_tdata(o_tdata[(m*FIFO_WIDTH)+FIFO_WIDTH-1:m*FIFO_WIDTH]),
|
||||
.o_tvalid(o_tvalid[m]),
|
||||
.o_tlast(o_tlast[m]),
|
||||
.o_tready(o_tready[m])
|
||||
);
|
||||
|
||||
if (m==0)
|
||||
assign i_tready_slave[0] = i_tready_tmp;
|
||||
else
|
||||
assign i_tready_slave[m] = i_tready_tmp | i_tready_slave[m-1] ;
|
||||
|
||||
end // block: instantiate_slave_mux
|
||||
endgenerate
|
||||
|
||||
assign i_tready = i_tready_slave[NUM_OUTPUTS-1];
|
||||
|
||||
//
|
||||
// Permute the forwarding flag buses
|
||||
//
|
||||
|
||||
generate
|
||||
for (m = 0; m < NUM_OUTPUTS; m = m + 1) begin: permute_outer
|
||||
for (n = 0; n < NUM_INPUTS; n = n + 1) begin: permute_inner
|
||||
assign forward_valid_in[n*NUM_OUTPUTS+m] = forward_valid_out[n+m*NUM_INPUTS];
|
||||
assign forward_ack_in[n+m*NUM_INPUTS] = forward_ack_out[n*NUM_OUTPUTS+m];
|
||||
end
|
||||
end
|
||||
|
||||
endgenerate
|
||||
|
||||
|
||||
//
|
||||
// Instantiate an axi_forwarding_cam for every Input/Master of the Crossbar switch.
|
||||
// Each contains a TCAM like lookup that allocates an egress port.
|
||||
//
|
||||
|
||||
wire [31:0] rb_data_mux[0:NUM_INPUTS-1];
|
||||
|
||||
generate
|
||||
for (m = 0; m < NUM_INPUTS; m = m + 1) begin: instantiate_cam
|
||||
axi_forwarding_cam
|
||||
#(
|
||||
.BASE(0),
|
||||
.WIDTH(FIFO_WIDTH), // Bit width of FIFO word.
|
||||
.NUM_OUTPUTS(NUM_OUTPUTS)
|
||||
) axi_forwarding_cam_i
|
||||
(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
// Monitored FIFO signals
|
||||
.o_tdata(i_tdata[(m*FIFO_WIDTH)+FIFO_WIDTH-1:m*FIFO_WIDTH]),
|
||||
.o_tvalid(i_tvalid[m]),
|
||||
.o_tready(i_tready[m]),
|
||||
.o_tlast(i_tlast[m]),
|
||||
.pkt_present(pkt_present[m]),
|
||||
// Configuration
|
||||
.local_addr(local_addr),
|
||||
// Setting Bus
|
||||
.set_stb(set_stb),
|
||||
.set_addr(set_addr),
|
||||
.set_data(set_data),
|
||||
// Header signals
|
||||
.forward_valid(forward_valid_out[(m+1)*NUM_OUTPUTS-1:m*NUM_OUTPUTS]),
|
||||
.forward_ack(forward_ack_in[(m+1)*NUM_OUTPUTS-1:m*NUM_OUTPUTS]),
|
||||
// Readback bus
|
||||
.rb_rd_stb(rb_rd_strobe && (rb_addr[`LOG2(NUM_OUTPUTS)+`LOG2(NUM_INPUTS)-1:`LOG2(NUM_OUTPUTS)] == m)),
|
||||
.rb_addr(rb_addr[`LOG2(NUM_OUTPUTS)-1:0]),
|
||||
.rb_data(rb_data_mux[m])
|
||||
);
|
||||
end // block: instantiate_fifo_header
|
||||
endgenerate
|
||||
|
||||
assign rb_data = rb_data_mux[rb_addr[`LOG2(NUM_OUTPUTS)+`LOG2(NUM_INPUTS)-1:`LOG2(NUM_OUTPUTS)]];
|
||||
|
||||
|
||||
endmodule // axi_crossbar
|
||||
@@ -0,0 +1,214 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
//
|
||||
|
||||
`timescale 1 ps / 1 ps
|
||||
|
||||
module axi_crossbar_tb;
|
||||
|
||||
|
||||
localparam STREAM_WIDTH = 64;
|
||||
|
||||
// Currently support simulations upto 8x8 configurations
|
||||
localparam MAX_NUM_INPUTS = 8;
|
||||
localparam MAX_NUM_OUTPUTS = 8;
|
||||
|
||||
wire [(MAX_NUM_INPUTS*STREAM_WIDTH)-1:0] i_tdata;
|
||||
wire [STREAM_WIDTH-1:0] i_tdata_array [0:MAX_NUM_INPUTS-1];
|
||||
wire [MAX_NUM_INPUTS-1:0] i_tvalid;
|
||||
wire [MAX_NUM_INPUTS-1:0] i_tready;
|
||||
wire [MAX_NUM_INPUTS-1:0] i_tlast;
|
||||
wire [MAX_NUM_INPUTS-1:0] pkt_present;
|
||||
|
||||
reg [STREAM_WIDTH-1:0] data_in [0:MAX_NUM_INPUTS-1];
|
||||
reg [MAX_NUM_INPUTS-1:0] valid_in;
|
||||
wire [MAX_NUM_INPUTS-1:0] ready_in;
|
||||
reg [MAX_NUM_INPUTS-1:0] last_in;
|
||||
|
||||
wire [(MAX_NUM_OUTPUTS*STREAM_WIDTH)-1:0] o_tdata;
|
||||
wire [STREAM_WIDTH-1:0] o_tdata_array [0:MAX_NUM_OUTPUTS-1];
|
||||
wire [MAX_NUM_OUTPUTS-1:0] o_tvalid;
|
||||
wire [MAX_NUM_OUTPUTS-1:0] o_tready;
|
||||
wire [MAX_NUM_OUTPUTS-1:0] o_tlast;
|
||||
|
||||
|
||||
wire [STREAM_WIDTH-1:0] data_out [0:MAX_NUM_OUTPUTS-1];
|
||||
wire [MAX_NUM_OUTPUTS-1:0] valid_out;
|
||||
reg [MAX_NUM_OUTPUTS-1:0] ready_out;
|
||||
wire [MAX_NUM_OUTPUTS-1:0] last_out;
|
||||
|
||||
|
||||
genvar m;
|
||||
|
||||
reg clk;
|
||||
reg reset;
|
||||
reg clear;
|
||||
reg set_stb;
|
||||
reg [15:0] set_addr;
|
||||
reg [31:0] set_data;
|
||||
|
||||
// reg reset;
|
||||
|
||||
//
|
||||
// Simulation specific testbench is included here
|
||||
//
|
||||
`include "task_library.v"
|
||||
`include "simulation_script.v"
|
||||
|
||||
|
||||
//
|
||||
// Define Clocks
|
||||
//
|
||||
initial begin
|
||||
clk = 1'b1;
|
||||
end
|
||||
|
||||
// 125MHz clock
|
||||
always #4000 clk = ~clk;
|
||||
|
||||
//
|
||||
// Good starting state
|
||||
//
|
||||
initial begin
|
||||
reset <= 0;
|
||||
clear <= 0;
|
||||
set_stb <= 0;
|
||||
set_addr <= 0;
|
||||
set_data <= 0;
|
||||
/* -----\/----- EXCLUDED -----\/-----
|
||||
data_in[0] <= 0;
|
||||
valid_in[0] <= 0;
|
||||
last_in[0] <= 0;
|
||||
|
||||
data_in[1] <= 0;
|
||||
valid_in[1] <= 0;
|
||||
last_in[1] <= 0;
|
||||
-----/\----- EXCLUDED -----/\----- */
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
//
|
||||
// AXI Crossbar instance
|
||||
//
|
||||
localparam SR_AWIDTH = 16;
|
||||
localparam SR_XB_LOCAL = 512;
|
||||
|
||||
wire [7:0] local_addr;
|
||||
|
||||
setting_reg #(.my_addr(SR_XB_LOCAL), .awidth(SR_AWIDTH), .width(8)) sr_local_addr
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out(local_addr),.changed());
|
||||
|
||||
axi_crossbar
|
||||
#(
|
||||
.FIFO_WIDTH(STREAM_WIDTH), // AXI4-STREAM data bus width
|
||||
.DST_WIDTH(16), // Width of DST field we are routing on.
|
||||
.NUM_INPUTS(NUM_INPUTS), // number of input AXI4-STREAM buses
|
||||
.NUM_OUTPUTS(NUM_OUTPUTS) // number of output AXI4-STREAM buses
|
||||
) axi_crossbar_i
|
||||
(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.local_addr(local_addr),
|
||||
// Inputs
|
||||
.i_tdata(i_tdata[(NUM_INPUTS*STREAM_WIDTH)-1:0]),
|
||||
.i_tvalid(i_tvalid[NUM_INPUTS-1:0]),
|
||||
.i_tlast(i_tlast[NUM_INPUTS-1:0]),
|
||||
.i_tready(i_tready[NUM_INPUTS-1:0]),
|
||||
.pkt_present(pkt_present[NUM_INPUTS-1:0]),
|
||||
// Settings bus
|
||||
.set_stb(set_stb),
|
||||
.set_addr(set_addr),
|
||||
.set_data(set_data),
|
||||
// Output
|
||||
.o_tdata(o_tdata[(NUM_OUTPUTS*STREAM_WIDTH)-1:0]),
|
||||
.o_tvalid(o_tvalid[NUM_OUTPUTS-1:0]),
|
||||
.o_tlast(o_tlast[NUM_OUTPUTS-1:0]),
|
||||
.o_tready(o_tready[NUM_OUTPUTS-1:0]),
|
||||
// Readback Bus
|
||||
.rb_rd_stb(1'b0),
|
||||
.rb_addr(0),
|
||||
.rb_data()
|
||||
);
|
||||
|
||||
//
|
||||
// Input FIFOs
|
||||
//
|
||||
generate
|
||||
for (m=0;m<NUM_INPUTS;m=m+1)
|
||||
begin: input_fifos
|
||||
|
||||
assign i_tdata[(STREAM_WIDTH*m)+STREAM_WIDTH-1:STREAM_WIDTH*m] = i_tdata_array[m];
|
||||
|
||||
axi_fifo_short
|
||||
#(.WIDTH(STREAM_WIDTH+1)) axi_fifo_short_in
|
||||
(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.o_tdata({i_tlast[m],i_tdata_array[m]}),
|
||||
.o_tvalid(i_tvalid[m]),
|
||||
.o_tready(i_tready[m]),
|
||||
.i_tdata({last_in[m],data_in[m]}),
|
||||
.i_tvalid(valid_in[m]),
|
||||
.i_tready(ready_in[m]),
|
||||
.space(),
|
||||
.occupied()
|
||||
);
|
||||
|
||||
monitor_axi_fifo
|
||||
#(
|
||||
.COUNT_BITS(8)
|
||||
) monitor_axi_fifo_in
|
||||
(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
// Monitored FIFO signals
|
||||
.i_tvalid(valid_in[m]),
|
||||
.i_tready(ready_in[m]),
|
||||
.i_tlast(last_in[m]),
|
||||
.o_tvalid(i_tvalid[m]),
|
||||
.o_tready(i_tready[m]),
|
||||
.o_tlast(i_tlast[m]),
|
||||
// FIFO status output
|
||||
.pkt_present(pkt_present[m]), // Flags any whole packets present
|
||||
.pkt_count()
|
||||
);
|
||||
|
||||
end
|
||||
endgenerate
|
||||
|
||||
|
||||
//
|
||||
// Output FIFO's
|
||||
//
|
||||
generate
|
||||
for (m=0;m<NUM_OUTPUTS;m=m+1)
|
||||
begin: output_fifos
|
||||
|
||||
assign o_tdata_array[m] = o_tdata[(STREAM_WIDTH*m)+STREAM_WIDTH-1:STREAM_WIDTH*m];
|
||||
|
||||
axi_fifo_short
|
||||
#(.WIDTH(STREAM_WIDTH+1)) axi_fifo_short_out
|
||||
(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.i_tdata({o_tlast[m],o_tdata_array[m]}),
|
||||
.i_tvalid(o_tvalid[m]),
|
||||
.i_tready(o_tready[m]),
|
||||
.o_tdata({last_out[m],data_out[m]}),
|
||||
.o_tvalid(valid_out[m]),
|
||||
.o_tready(ready_out[m]),
|
||||
.space(),
|
||||
.occupied()
|
||||
);
|
||||
end
|
||||
endgenerate // block: output_fifos
|
||||
|
||||
endmodule // axi_crossbar_tb
|
||||
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// This module is connected to the output port of an AXI4-STREAM FIFO that is used to move packetized data.
|
||||
// It extracts and indicates the header (first word) of a packet in the FIFO. The header and flag are pipelined
|
||||
// for timing closure.
|
||||
//
|
||||
|
||||
module axi_fifo_header
|
||||
#(
|
||||
parameter WIDTH=64 // Bit width of FIFO word.
|
||||
)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
// Monitored FIFO signals
|
||||
input [WIDTH-1:0] o_tdata,
|
||||
input o_tvalid,
|
||||
input o_tready,
|
||||
input o_tlast,
|
||||
input pkt_present,
|
||||
// Header signals
|
||||
output reg [WIDTH-1:0] header,
|
||||
output reg header_valid
|
||||
);
|
||||
|
||||
localparam WAIT_SOF = 0;
|
||||
localparam WAIT_EOF = 1;
|
||||
|
||||
reg out_state;
|
||||
|
||||
|
||||
//
|
||||
// Monitor packets leaving FIFO
|
||||
//
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
out_state <= WAIT_SOF;
|
||||
end else
|
||||
case(out_state)
|
||||
//
|
||||
// After RESET or the EOF of previous packet, the first cycle with
|
||||
// output valid asserted is the SOF and presents the Header word.
|
||||
// The cycle following the concurrent presentation of asserted output
|
||||
// valid and output ready presents the word following the header.
|
||||
//
|
||||
WAIT_SOF:
|
||||
if (o_tvalid && o_tready) begin
|
||||
out_state <= WAIT_EOF;
|
||||
end else begin
|
||||
out_state <= WAIT_SOF;
|
||||
end
|
||||
//
|
||||
// EOF is signalled by o_tlast asserted whilst output valid and ready asserted.
|
||||
//
|
||||
WAIT_EOF:
|
||||
if (o_tlast && o_tvalid && o_tready) begin
|
||||
out_state <= WAIT_SOF;
|
||||
end else begin
|
||||
out_state <= WAIT_EOF;
|
||||
end
|
||||
endcase // case(in_state)
|
||||
|
||||
//
|
||||
// Pipeline Header signals
|
||||
//
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
header <= 0;
|
||||
header_valid <= 0;
|
||||
end else if (o_tvalid && (out_state == WAIT_SOF) && pkt_present) begin
|
||||
// Header will remian valid until o_tready is asserted as this will cause a state transition.
|
||||
header <= o_tdata;
|
||||
header_valid <= 1;
|
||||
end else begin
|
||||
header_valid <= 0;
|
||||
end
|
||||
|
||||
|
||||
endmodule // axi_fifo_header
|
||||
@@ -0,0 +1,232 @@
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// This module implements a highly customized TCAM that enbales forwarding
|
||||
// decisions to be made on a 16bit field from a VITA SID field.
|
||||
// The 16bits are allocated by convention as 8 bits of Network address
|
||||
// (Addresses USRP's etc) and 8 bits of Host address (adresses endpoints in
|
||||
// a USRP). By definition if the DEST field in the SID addresses a different
|
||||
// USRP than this one then we don't care about the Host field, only the Network Field.
|
||||
// We only look at the Host Field when the Network field addresses us.
|
||||
// Thus Need TCAM of 256+256 entries with Log2(N) bits, where N is the number of
|
||||
// slave(output) ports on the crossbar switch.
|
||||
//
|
||||
//
|
||||
//
|
||||
// SID format:
|
||||
//
|
||||
// |--------|---------|--------|---------|
|
||||
// | SOURCE | DEST | DEST |
|
||||
// | ADDRESS | NETWORK| HOST |
|
||||
// |--------|---------|--------|---------|
|
||||
// 8 8 8 8
|
||||
//
|
||||
|
||||
|
||||
`define LOG2(N) (\
|
||||
N < 2 ? 0 : \
|
||||
N < 4 ? 1 : \
|
||||
N < 8 ? 2 : \
|
||||
N < 16 ? 3 : \
|
||||
N < 32 ? 4 : \
|
||||
N < 64 ? 5 : \
|
||||
N < 128 ? 6 : \
|
||||
N < 256 ? 7 : \
|
||||
N < 512 ? 8 : \
|
||||
N < 1024 ? 9 : \
|
||||
10)
|
||||
|
||||
module axi_forwarding_cam
|
||||
#(
|
||||
parameter BASE = 0, // BASE address for setting registers in this block. (512 addrs used)
|
||||
parameter WIDTH=64, // Bit width of FIFO word.
|
||||
parameter NUM_OUTPUTS=2 // Number of outputs (destinations) in crossbar.
|
||||
)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
// Monitored FIFO signals
|
||||
input [WIDTH-1:0] o_tdata,
|
||||
input o_tvalid,
|
||||
input o_tready,
|
||||
input o_tlast,
|
||||
input pkt_present,
|
||||
// Configuration
|
||||
input [7:0] local_addr,
|
||||
// Setting Bus
|
||||
input set_stb,
|
||||
input [15:0] set_addr,
|
||||
input [31:0] set_data,
|
||||
// Forwarding Flags
|
||||
output reg [NUM_OUTPUTS-1:0] forward_valid,
|
||||
input [NUM_OUTPUTS-1:0] forward_ack,
|
||||
// readback bus
|
||||
input rb_rd_stb,
|
||||
input [`LOG2(NUM_OUTPUTS)-1:0] rb_addr,
|
||||
output [31:0] rb_data
|
||||
);
|
||||
|
||||
|
||||
localparam WAIT_SOF = 0;
|
||||
localparam WAIT_EOF = 1;
|
||||
reg state;
|
||||
|
||||
localparam IDLE = 0;
|
||||
localparam FORWARD = 1;
|
||||
localparam WAIT = 2;
|
||||
|
||||
reg [1:0] demux_state;
|
||||
|
||||
reg [15:0] dst;
|
||||
reg dst_valid, dst_valid_reg;
|
||||
wire local_dst;
|
||||
wire [8:0] read_addr;
|
||||
|
||||
//
|
||||
// Monitor packets leaving FIFO
|
||||
//
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
state <= WAIT_SOF;
|
||||
end else
|
||||
case(state)
|
||||
//
|
||||
// After RESET or the EOF of previous packet, the first cycle with
|
||||
// output valid asserted is the SOF and presents the Header word.
|
||||
// The cycle following the concurrent presentation of asserted output
|
||||
// valid and output ready presents the word following the header.
|
||||
//
|
||||
WAIT_SOF:
|
||||
if (o_tvalid && o_tready) begin
|
||||
state <= WAIT_EOF;
|
||||
end else begin
|
||||
state <= WAIT_SOF;
|
||||
end
|
||||
//
|
||||
// EOF is signalled by o_tlast asserted whilst output valid and ready asserted.
|
||||
//
|
||||
WAIT_EOF:
|
||||
if (o_tlast && o_tvalid && o_tready) begin
|
||||
state <= WAIT_SOF;
|
||||
end else begin
|
||||
state <= WAIT_EOF;
|
||||
end
|
||||
endcase // case(in_state)
|
||||
|
||||
//
|
||||
// Extract Destination fields(s) from SID
|
||||
//
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
dst <= 0;
|
||||
dst_valid <= 0;
|
||||
dst_valid_reg <= 0;
|
||||
end else if (o_tvalid && (state == WAIT_SOF) && pkt_present) begin
|
||||
// SID will remain valid until o_tready is asserted as this will cause a state transition.
|
||||
dst <= o_tdata[15:0];
|
||||
dst_valid <= 1;
|
||||
dst_valid_reg <= dst_valid;
|
||||
end else begin
|
||||
dst_valid <= 0;
|
||||
dst_valid_reg <= dst_valid;
|
||||
end
|
||||
|
||||
//
|
||||
// Is Network field in DST our local address?
|
||||
//
|
||||
assign local_dst = (dst[15:8] == local_addr) && dst_valid;
|
||||
|
||||
|
||||
//
|
||||
// Mux address to RAM so that it searches CAM for Network field or Host field.
|
||||
// Network addresses are stored in the lower 256 locations, host addresses the upper 256.
|
||||
//
|
||||
assign read_addr = {local_dst,(local_dst ? dst[7:0] : dst[15:8])};
|
||||
|
||||
//
|
||||
// Imply a block RAM here, 512xCeil(Log2(NUM_OUTPUTS))
|
||||
//
|
||||
//synthesis attribute ram_style of mem is block
|
||||
reg [(`LOG2(NUM_OUTPUTS))-1 : 0] mem [0:511];
|
||||
reg [8:0] read_addr_reg;
|
||||
wire write;
|
||||
wire [`LOG2(NUM_OUTPUTS)-1:0] read_data;
|
||||
|
||||
assign write = (set_addr[15:9] == (BASE >>9)) && set_stb; // Addr decode.
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
read_addr_reg <= read_addr;
|
||||
|
||||
if (write) begin
|
||||
mem[set_addr[8:0]] <= set_data[`LOG2(NUM_OUTPUTS)-1:0];
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
assign read_data = mem[read_addr_reg];
|
||||
|
||||
|
||||
//
|
||||
// State machine to manage forwarding flags.
|
||||
//
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
demux_state <= IDLE;
|
||||
end else
|
||||
case(demux_state)
|
||||
|
||||
// Wait for Valid DST which indicates a new packet lookup in the CAM.
|
||||
IDLE: begin
|
||||
if (dst_valid_reg == 1) begin
|
||||
forward_valid <= 1 << read_data;
|
||||
demux_state <= FORWARD;
|
||||
end
|
||||
end
|
||||
// When Slave/Output thats forwarding ACK's the forward flag, clear request and wait for packet to be transfered
|
||||
FORWARD: begin
|
||||
if ((forward_ack & forward_valid) != 0) begin
|
||||
forward_valid <= 0;
|
||||
demux_state <= WAIT;
|
||||
end
|
||||
end
|
||||
// When packet transfered go back to idle.
|
||||
WAIT: begin
|
||||
if (forward_ack == 0)
|
||||
demux_state <= IDLE;
|
||||
end
|
||||
|
||||
endcase // case (demux_state)
|
||||
|
||||
//
|
||||
// Compile forwarding statistics
|
||||
// (This uses a lot of registers!)
|
||||
//
|
||||
genvar m;
|
||||
reg [31:0] statistics [0:NUM_OUTPUTS-1];
|
||||
|
||||
generate
|
||||
for (m = 0; m < NUM_OUTPUTS; m = m + 1) begin: generate_stats
|
||||
always @(posedge clk)
|
||||
if (reset | clear)
|
||||
statistics[m] <= 0;
|
||||
else if ((rb_addr == m) && rb_rd_stb)
|
||||
statistics[m] <= 0;
|
||||
else if (forward_ack[m] & forward_valid[m])
|
||||
statistics[m] <= statistics[m] + 1;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
assign rb_data = statistics[rb_addr];
|
||||
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
`define LOG2(N) (\
|
||||
N < 2 ? 0 : \
|
||||
N < 4 ? 1 : \
|
||||
N < 8 ? 2 : \
|
||||
N < 16 ? 3 : \
|
||||
N < 32 ? 4 : \
|
||||
N < 64 ? 5 : \
|
||||
N < 128 ? 6 : \
|
||||
N < 256 ? 7 : \
|
||||
N < 512 ? 8 : \
|
||||
N < 1024 ? 9 : \
|
||||
10)
|
||||
|
||||
|
||||
module axi_slave_mux
|
||||
#(
|
||||
parameter FIFO_WIDTH = 64, // AXI4-STREAM data bus width
|
||||
parameter DST_WIDTH = 16, // Width of DST field we are routing on.
|
||||
parameter NUM_INPUTS = 2 // number of input AXI buses
|
||||
)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
// Inputs
|
||||
input [(FIFO_WIDTH*NUM_INPUTS)-1:0] i_tdata,
|
||||
input [NUM_INPUTS-1:0] i_tvalid,
|
||||
input [NUM_INPUTS-1:0] i_tlast,
|
||||
output [NUM_INPUTS-1:0] i_tready,
|
||||
// Forwarding Flags
|
||||
input [NUM_INPUTS-1:0] forward_valid,
|
||||
output reg [NUM_INPUTS-1:0] forward_ack,
|
||||
// Output
|
||||
output [FIFO_WIDTH-1:0] o_tdata,
|
||||
output o_tvalid,
|
||||
output o_tlast,
|
||||
input o_tready
|
||||
);
|
||||
|
||||
wire [FIFO_WIDTH-1:0] i_tdata_array [0:NUM_INPUTS-1];
|
||||
|
||||
reg [`LOG2(NUM_INPUTS):0] select;
|
||||
reg enable;
|
||||
|
||||
|
||||
reg state;
|
||||
|
||||
localparam CHECK_THIS_INPUT = 0;
|
||||
localparam WAIT_LAST = 1;
|
||||
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
state <= CHECK_THIS_INPUT;
|
||||
select <= 0;
|
||||
enable <= 0;
|
||||
forward_ack <= 0;
|
||||
end else begin
|
||||
case(state)
|
||||
// Is the currently selected input addressing this slave with a ready packet?
|
||||
CHECK_THIS_INPUT: begin
|
||||
if (forward_valid[select]) begin
|
||||
enable <= 1;
|
||||
forward_ack[select] <= 1;
|
||||
state <= WAIT_LAST;
|
||||
end else if (select == NUM_INPUTS - 1 ) begin
|
||||
select <= 0;
|
||||
end else begin
|
||||
select <= select + 1;
|
||||
end
|
||||
end
|
||||
// Assert ACK immediately to forwarding logic and then wait for end of packet.
|
||||
WAIT_LAST: begin
|
||||
|
||||
if (i_tlast[select] && i_tvalid[select] && o_tready) begin
|
||||
if (select == NUM_INPUTS - 1 ) begin
|
||||
select <= 0;
|
||||
end else begin
|
||||
select <= select + 1;
|
||||
end
|
||||
state <= CHECK_THIS_INPUT;
|
||||
forward_ack <= 0;
|
||||
enable <= 0;
|
||||
end else begin
|
||||
forward_ack[select] <= 1;
|
||||
enable <= 1;
|
||||
end
|
||||
end
|
||||
endcase // case(state)
|
||||
end
|
||||
|
||||
//
|
||||
// Combinatorial mux
|
||||
//
|
||||
genvar m;
|
||||
|
||||
generate
|
||||
for (m = 0; m < NUM_INPUTS; m = m + 1) begin: form_buses
|
||||
assign i_tdata_array[m] = i_tdata[(m*FIFO_WIDTH)+FIFO_WIDTH-1:m*FIFO_WIDTH];
|
||||
end
|
||||
endgenerate
|
||||
|
||||
assign o_tdata = i_tdata_array[select];
|
||||
assign o_tvalid = enable && i_tvalid[select];
|
||||
assign o_tlast = enable && i_tlast[select];
|
||||
// assign i_tready = {NUM_INPUTS{o_tready}} & (enable << select);
|
||||
|
||||
generate
|
||||
for (m = 0; m < NUM_INPUTS; m = m + 1) begin: form_ready
|
||||
assign i_tready[m] = o_tready && enable && (select == m);
|
||||
end
|
||||
endgenerate
|
||||
|
||||
|
||||
endmodule // axi_slave_mux
|
||||
@@ -0,0 +1,139 @@
|
||||
//
|
||||
// Test Virtual FIFO's by streaming modulo 2^32 counter (replicated in upper
|
||||
// and lower 32bits). Test result by tracking count on receive and using
|
||||
// sticky flag for error indication.
|
||||
// Also provide signal from MSB of 32bit count to blink LED.
|
||||
//
|
||||
|
||||
module axi_test_vfifo
|
||||
#(parameter PACKET_SIZE = 128)
|
||||
(
|
||||
input aclk,
|
||||
input aresetn,
|
||||
input enable,
|
||||
// AXI Stream Out
|
||||
output reg out_axis_tvalid,
|
||||
input out_axis_tready,
|
||||
output [63 : 0] out_axis_tdata,
|
||||
output reg [7 : 0] out_axis_tstrb,
|
||||
output reg [7 : 0] out_axis_tkeep,
|
||||
output reg out_axis_tlast,
|
||||
output reg [0 : 0] out_axis_tid,
|
||||
output reg [0 : 0] out_axis_tdest,
|
||||
input vfifo_full,
|
||||
// AXI Stream In
|
||||
input in_axis_tvalid,
|
||||
output reg in_axis_tready,
|
||||
input [63 : 0] in_axis_tdata,
|
||||
input [7 : 0] in_axis_tstrb,
|
||||
input [7 : 0] in_axis_tkeep,
|
||||
input in_axis_tlast,
|
||||
input [0 : 0] in_axis_tid,
|
||||
input [0 : 0] in_axis_tdest,
|
||||
// Flags
|
||||
output reg flag_error,
|
||||
output heartbeat_in,
|
||||
output heartbeat_out,
|
||||
output [31:0] expected_count
|
||||
);
|
||||
|
||||
|
||||
reg [31:0] out_count;
|
||||
reg [31:0] in_count;
|
||||
reg [63:0] in_axis_tdata_reg;
|
||||
reg in_data_valid;
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Output
|
||||
//
|
||||
always @(posedge aclk)
|
||||
if (!aresetn) begin
|
||||
out_count <= 0;
|
||||
out_axis_tvalid <= 0;
|
||||
out_axis_tid <= 0; // Don't care.
|
||||
out_axis_tdest <= 0; // Only use port 0 of VFIFO.
|
||||
out_axis_tstrb <= 0; // Unused in VFIFO
|
||||
out_axis_tkeep <= 8'hFF; // Always use every byte of data
|
||||
out_axis_tlast <= 1'b0;
|
||||
end else if (enable) begin
|
||||
if (~vfifo_full) begin
|
||||
// Always ready to output new count value.
|
||||
out_axis_tvalid <= 1;
|
||||
if (out_axis_tready)
|
||||
out_count <= out_count + 1;
|
||||
// Assert TLAST every PACKET_SIZE beats.
|
||||
if (out_count[15:0] == PACKET_SIZE)
|
||||
out_axis_tlast <= 1'b1;
|
||||
else
|
||||
out_axis_tlast <= 1'b0;
|
||||
end else begin
|
||||
out_axis_tvalid <= 0;
|
||||
end
|
||||
end else begin
|
||||
out_axis_tlast <= 1'b0;
|
||||
out_axis_tvalid <= 0;
|
||||
end
|
||||
|
||||
assign out_axis_tdata = {out_count,out_count};
|
||||
|
||||
assign heartbeat_out = out_count[28];
|
||||
|
||||
|
||||
//
|
||||
// Input (Ignore TLAST signal)
|
||||
//
|
||||
always @(posedge aclk)
|
||||
if (!aresetn) begin
|
||||
in_axis_tready <= 0;
|
||||
in_axis_tdata_reg <= 0;
|
||||
in_data_valid <= 0;
|
||||
|
||||
end else if (enable) begin
|
||||
in_axis_tready <= 1;
|
||||
in_axis_tdata_reg <= in_axis_tdata;
|
||||
if (in_axis_tvalid)
|
||||
in_data_valid <= 1;
|
||||
else
|
||||
in_data_valid <= 0;
|
||||
end else begin
|
||||
in_data_valid <= 0;
|
||||
in_axis_tready <= 0;
|
||||
end // else: !if(enable)
|
||||
|
||||
|
||||
assign heartbeat_in = in_count[28];
|
||||
|
||||
//
|
||||
// Input Checker
|
||||
//
|
||||
always @(posedge aclk)
|
||||
if (!aresetn) begin
|
||||
in_count <= 0;
|
||||
flag_error <= 0;
|
||||
end else if (enable) begin
|
||||
if (in_data_valid) begin
|
||||
|
||||
if ((in_axis_tdata_reg[63:32] != in_count) || (in_axis_tdata_reg[31:0] != in_count))
|
||||
begin
|
||||
flag_error <= 1;
|
||||
in_count <= in_axis_tdata_reg[63:32] + 1;
|
||||
end
|
||||
else
|
||||
begin
|
||||
flag_error <= 0;
|
||||
in_count <= in_count + 1;
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
assign expected_count = in_count;
|
||||
|
||||
|
||||
endmodule // axi_test_vfifo
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
//create a compressed vita based uart data interface
|
||||
|
||||
module cvita_uart
|
||||
#(
|
||||
parameter SIZE = 0
|
||||
)
|
||||
(
|
||||
//clocking interface
|
||||
input clk, input rst,
|
||||
|
||||
//uart interface
|
||||
input rxd, output txd,
|
||||
|
||||
//chdr fifo input
|
||||
input [63:0] i_tdata,
|
||||
input i_tlast,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
|
||||
//chdr fifo output
|
||||
output [63:0] o_tdata,
|
||||
output o_tlast,
|
||||
output o_tvalid,
|
||||
input o_tready
|
||||
);
|
||||
|
||||
reg [31:0] sid;
|
||||
|
||||
//baud clock divider
|
||||
reg [15:0] clkdiv;
|
||||
|
||||
//hold rx in disable until a tx event
|
||||
reg rxd_enable;
|
||||
|
||||
//==================================================================
|
||||
//== RXD capture and packet generation interface
|
||||
//==================================================================
|
||||
wire [7:0] rx_char;
|
||||
wire fifo_empty;
|
||||
wire fifo_read;
|
||||
reg [11:0] seqnum;
|
||||
wire pgen_trigger;
|
||||
wire pgen_done;
|
||||
|
||||
//rx uart capture
|
||||
simple_uart_rx #(.SIZE(SIZE)) simple_uart_rx
|
||||
(
|
||||
.clk(clk), .rst(rst),
|
||||
.fifo_out(rx_char), .fifo_read(fifo_read), .fifo_level(), .fifo_empty(fifo_empty),
|
||||
.clkdiv(clkdiv), .rx(rxd)
|
||||
);
|
||||
|
||||
//packet generation - holds rx character
|
||||
context_packet_gen context_packet_gen
|
||||
(
|
||||
.clk(clk), .reset(rst), .clear(1'b0),
|
||||
.trigger(pgen_trigger),
|
||||
.seqnum(seqnum),
|
||||
.sid({sid[15:0], sid[31:16]}),
|
||||
.body({56'b0, rx_char}),
|
||||
.vita_time(64'b0),
|
||||
|
||||
.done(pgen_done),
|
||||
.o_tdata(o_tdata), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready)
|
||||
);
|
||||
|
||||
//state machine to manage pgen and rx uart
|
||||
reg [1:0] rxd_state;
|
||||
localparam RXD_STATE_RECV_CHAR = 0;
|
||||
localparam RXD_STATE_PGEN_TRIG = 1;
|
||||
localparam RXD_STATE_WAIT_DONE = 2;
|
||||
localparam RXD_STATE_READ_FIFO = 3;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
seqnum <= 12'b0;
|
||||
rxd_state <= RXD_STATE_RECV_CHAR;
|
||||
end
|
||||
else case (rxd_state)
|
||||
|
||||
RXD_STATE_RECV_CHAR: begin
|
||||
if (!fifo_empty && rxd_enable) rxd_state <= RXD_STATE_PGEN_TRIG;
|
||||
end
|
||||
|
||||
RXD_STATE_PGEN_TRIG: begin
|
||||
rxd_state <= RXD_STATE_WAIT_DONE;
|
||||
end
|
||||
|
||||
RXD_STATE_WAIT_DONE: begin
|
||||
if (pgen_done) rxd_state <= RXD_STATE_READ_FIFO;
|
||||
end
|
||||
|
||||
RXD_STATE_READ_FIFO: begin
|
||||
rxd_state <= RXD_STATE_RECV_CHAR;
|
||||
seqnum <= seqnum + 1'b1;
|
||||
end
|
||||
|
||||
endcase //rxd_state
|
||||
end
|
||||
|
||||
assign fifo_read = (rxd_state == RXD_STATE_READ_FIFO) || (!rxd_enable);
|
||||
assign pgen_trigger = (rxd_state == RXD_STATE_PGEN_TRIG);
|
||||
|
||||
//==================================================================
|
||||
//== TXD generation and packet control interface
|
||||
//==================================================================
|
||||
wire [7:0] tx_char;
|
||||
wire fifo_write;
|
||||
wire fifo_full;
|
||||
|
||||
simple_uart_tx #(.SIZE(SIZE)) simple_uart_tx
|
||||
(
|
||||
.clk(clk), .rst(rst),
|
||||
.fifo_in(tx_char), .fifo_write(fifo_write), .fifo_level(), .fifo_full(fifo_full),
|
||||
.clkdiv(clkdiv), .baudclk(), .tx(txd)
|
||||
);
|
||||
|
||||
//state machine to manage control and tx uart
|
||||
reg [1:0] txd_state;
|
||||
localparam TXD_STATE_RECV_CHDR = 0;
|
||||
localparam TXD_STATE_RECV_TIME = 1;
|
||||
localparam TXD_STATE_RECV_BODY = 2;
|
||||
localparam TXD_STATE_DROP_FIFO = 3;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin;
|
||||
txd_state <= TXD_STATE_RECV_CHDR;
|
||||
rxd_enable <= 1'b0;
|
||||
end
|
||||
if (i_tvalid && i_tready) case (txd_state)
|
||||
|
||||
TXD_STATE_RECV_CHDR: begin
|
||||
txd_state <= (i_tdata[61])? TXD_STATE_RECV_TIME : TXD_STATE_RECV_BODY;
|
||||
sid <= i_tdata[31:0];
|
||||
end
|
||||
|
||||
TXD_STATE_RECV_TIME: begin
|
||||
txd_state <= TXD_STATE_RECV_BODY;
|
||||
end
|
||||
|
||||
TXD_STATE_RECV_BODY: begin
|
||||
txd_state <= (i_tlast)? TXD_STATE_RECV_CHDR : TXD_STATE_DROP_FIFO;
|
||||
clkdiv <= i_tdata[47:32];
|
||||
rxd_enable <= 1'b1;
|
||||
end
|
||||
|
||||
TXD_STATE_DROP_FIFO: begin
|
||||
if (i_tlast) txd_state <= TXD_STATE_RECV_CHDR;
|
||||
end
|
||||
|
||||
endcase //txd_state
|
||||
end
|
||||
|
||||
assign tx_char = i_tdata[7:0];
|
||||
assign fifo_write = (txd_state == TXD_STATE_RECV_BODY) && i_tvalid && i_tready;
|
||||
assign i_tready = !fifo_full;
|
||||
|
||||
endmodule // cvita_uart
|
||||
@@ -0,0 +1,27 @@
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Copyright Ettus Research LLC
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
module dram_2port
|
||||
#(parameter DWIDTH=32,
|
||||
parameter AWIDTH=9)
|
||||
(input clk,
|
||||
input write,
|
||||
input [AWIDTH-1:0] raddr,
|
||||
input [AWIDTH-1:0] waddr,
|
||||
input [DWIDTH-1:0] wdata,
|
||||
output [DWIDTH-1:0] rdata);
|
||||
|
||||
reg [DWIDTH-1:0] ram [(1<<AWIDTH)-1:0];
|
||||
integer i;
|
||||
initial
|
||||
for(i=0;i<(1<<AWIDTH);i=i+1)
|
||||
ram[i] <= {DWIDTH{1'b0}};
|
||||
|
||||
assign rdata = ram[raddr];
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (write) ram[waddr] <= wdata;
|
||||
end
|
||||
|
||||
endmodule //dram_2port
|
||||
@@ -0,0 +1,64 @@
|
||||
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
|
||||
module gpio_atr
|
||||
#(parameter BASE = 0,
|
||||
parameter WIDTH = 32)
|
||||
(input clk, input reset,
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data,
|
||||
input rx, input tx,
|
||||
inout [WIDTH-1:0] gpio,
|
||||
output reg [31:0] gpio_readback
|
||||
);
|
||||
|
||||
wire [WIDTH-1:0] ddr, in_idle, in_tx, in_rx, in_fdx;
|
||||
reg [WIDTH-1:0] rgpio, igpio;
|
||||
reg [WIDTH-1:0] gpio_pipe;
|
||||
|
||||
|
||||
setting_reg #(.my_addr(BASE+0), .width(WIDTH)) reg_idle
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
|
||||
.out(in_idle),.changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+1), .width(WIDTH)) reg_rx
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
|
||||
.out(in_rx),.changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+2), .width(WIDTH)) reg_tx
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
|
||||
.out(in_tx),.changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+3), .width(WIDTH)) reg_fdx
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
|
||||
.out(in_fdx),.changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+4), .width(WIDTH)) reg_ddr
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
|
||||
.out(ddr),.changed());
|
||||
|
||||
always @(posedge clk)
|
||||
case({tx,rx})
|
||||
2'b00: rgpio <= in_idle;
|
||||
2'b01: rgpio <= in_rx;
|
||||
2'b10: rgpio <= in_tx;
|
||||
2'b11: rgpio <= in_fdx;
|
||||
endcase // case ({tx,rx})
|
||||
|
||||
integer n;
|
||||
always @*
|
||||
for(n=0;n<WIDTH;n=n+1)
|
||||
igpio[n] <= ddr[n] ? rgpio[n] : 1'bz;
|
||||
|
||||
assign gpio = igpio;
|
||||
|
||||
// Double pipeline stage for timing, first flop is in IOB, second in core logic.
|
||||
always @(posedge clk) begin
|
||||
gpio_pipe <= gpio;
|
||||
gpio_readback <= gpio_pipe;
|
||||
end
|
||||
|
||||
endmodule // gpio_atr
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
|
||||
module por_gen
|
||||
(input clk,
|
||||
output reset_out);
|
||||
|
||||
reg por_rst;
|
||||
reg [7:0] por_counter = 8'h0;
|
||||
|
||||
always @(posedge clk)
|
||||
if (por_counter != 8'h55)
|
||||
begin
|
||||
por_counter <= por_counter + 8'h1;
|
||||
por_rst <= 1'b1;
|
||||
end
|
||||
else
|
||||
por_rst <= 1'b0;
|
||||
|
||||
assign reset_out = por_rst;
|
||||
|
||||
endmodule // por_gen
|
||||
@@ -0,0 +1,143 @@
|
||||
|
||||
|
||||
// Radio Control Processor
|
||||
// Accepts compressed vita extension context packets of the following form:
|
||||
// { VITA Compressed Header, Stream ID }
|
||||
// { Optional 64 bit time }
|
||||
// { 16'h0, setting bus address [15:0], setting [31:0] }
|
||||
//
|
||||
// If there is a timestamp, packet is held until that time comes.
|
||||
// Goes immediately if there is no timestamp or if time has passed.
|
||||
// Sends out setting to setting bus, and then generates a response packet
|
||||
// with the same sequence number, the src/dest swapped streamid, and the actual time
|
||||
// the setting was sent.
|
||||
//
|
||||
// Note -- if t0 is the requested time, the actual send time on the setting bus is t0 + 1 cycle.
|
||||
// Note 2 -- if t1 is the actual time the setting bus, t1+2 is the reported time.
|
||||
|
||||
module radio_ctrl_proc
|
||||
(input clk, input reset, input clear,
|
||||
|
||||
input [63:0] ctrl_tdata, input ctrl_tlast, input ctrl_tvalid, output reg ctrl_tready,
|
||||
output reg [63:0] resp_tdata, output reg resp_tlast, output resp_tvalid, input resp_tready,
|
||||
|
||||
input [63:0] vita_time,
|
||||
|
||||
output set_stb, output [7:0] set_addr, output [31:0] set_data,
|
||||
input ready,
|
||||
|
||||
input [63:0] readback,
|
||||
|
||||
output [31:0] debug);
|
||||
|
||||
localparam RC_HEAD = 4'd0;
|
||||
localparam RC_TIME = 4'd1;
|
||||
localparam RC_DATA = 4'd2;
|
||||
localparam RC_DUMP = 4'd3;
|
||||
localparam RC_RESP_HEAD = 4'd4;
|
||||
localparam RC_RESP_TIME = 4'd5;
|
||||
localparam RC_RESP_DATA = 4'd6;
|
||||
|
||||
wire IS_EC = ctrl_tdata[63];
|
||||
wire HAS_TIME = ctrl_tdata[61];
|
||||
reg HAS_TIME_reg;
|
||||
|
||||
reg [3:0] rc_state;
|
||||
reg [63:0] cmd_time;
|
||||
|
||||
wire now, late, go;
|
||||
reg [11:0] seqnum;
|
||||
reg [31:0] sid;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset)
|
||||
begin
|
||||
rc_state <= RC_HEAD;
|
||||
HAS_TIME_reg <= 1'b0;
|
||||
sid <= 32'd0;
|
||||
seqnum <= 12'd0;
|
||||
end
|
||||
else
|
||||
case(rc_state)
|
||||
RC_HEAD :
|
||||
if(ctrl_tvalid)
|
||||
begin
|
||||
sid <= ctrl_tdata[31:0];
|
||||
seqnum <= ctrl_tdata[59:48];
|
||||
HAS_TIME_reg <= HAS_TIME;
|
||||
if(IS_EC)
|
||||
if(HAS_TIME)
|
||||
rc_state <= RC_TIME;
|
||||
else
|
||||
rc_state <= RC_DATA;
|
||||
else
|
||||
if(~ctrl_tlast)
|
||||
rc_state <= RC_DUMP;
|
||||
end
|
||||
|
||||
RC_TIME :
|
||||
if(ctrl_tvalid)
|
||||
if(ctrl_tlast)
|
||||
rc_state <= RC_RESP_HEAD;
|
||||
else if(go)
|
||||
rc_state <= RC_DATA;
|
||||
|
||||
RC_DATA :
|
||||
if(ctrl_tvalid)
|
||||
if(ready)
|
||||
if(ctrl_tlast)
|
||||
rc_state <= RC_RESP_HEAD;
|
||||
else
|
||||
rc_state <= RC_DUMP;
|
||||
|
||||
RC_DUMP :
|
||||
if(ctrl_tvalid)
|
||||
if(ctrl_tlast)
|
||||
rc_state <= RC_RESP_HEAD;
|
||||
|
||||
RC_RESP_HEAD :
|
||||
if(resp_tready)
|
||||
rc_state <= RC_RESP_TIME;
|
||||
|
||||
RC_RESP_TIME :
|
||||
if(resp_tready)
|
||||
rc_state <= RC_RESP_DATA;
|
||||
|
||||
RC_RESP_DATA:
|
||||
if(resp_tready)
|
||||
rc_state <= RC_HEAD;
|
||||
|
||||
default :
|
||||
rc_state <= RC_HEAD;
|
||||
endcase // case (rc_state)
|
||||
|
||||
always @*
|
||||
case (rc_state)
|
||||
RC_HEAD : ctrl_tready <= 1'b1;
|
||||
RC_TIME : ctrl_tready <= ctrl_tlast | go;
|
||||
RC_DATA : ctrl_tready <= ready;
|
||||
RC_DUMP : ctrl_tready <= 1'b1;
|
||||
default : ctrl_tready <= 1'b0;
|
||||
endcase // case (rc_state)
|
||||
|
||||
time_compare time_compare
|
||||
(.clk(clk), .reset(reset), .time_now(vita_time), .trigger_time(ctrl_tdata), .now(now), .early(), .late(late), .too_early());
|
||||
|
||||
assign go = now | late;
|
||||
|
||||
assign set_stb = (rc_state == RC_DATA) & ready & ctrl_tvalid;
|
||||
assign set_addr = ctrl_tdata[39:32];
|
||||
assign set_data = ctrl_tdata[31:0];
|
||||
|
||||
always @*
|
||||
case (rc_state)
|
||||
RC_RESP_HEAD : { resp_tlast, resp_tdata } <= {1'b0, 4'hA, seqnum, 16'd24, sid[15:0], sid[31:16] };
|
||||
RC_RESP_TIME : { resp_tlast, resp_tdata } <= {1'b0, vita_time};
|
||||
RC_RESP_DATA : { resp_tlast, resp_tdata } <= {1'b1, readback};
|
||||
default : { resp_tlast, resp_tdata } <= 65'h0;
|
||||
endcase // case (rc_state)
|
||||
|
||||
assign resp_tvalid = (rc_state == RC_RESP_HEAD) | (rc_state == RC_RESP_TIME) | (rc_state == RC_RESP_DATA);
|
||||
|
||||
endmodule // radio_ctrl_proc
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module radio_ctrl_proc_tb();
|
||||
|
||||
reg clk = 0;
|
||||
reg reset = 1;
|
||||
|
||||
always #10 clk = ~clk;
|
||||
|
||||
initial $dumpfile("radio_ctrl_proc_tb.vcd");
|
||||
initial $dumpvars(0,radio_ctrl_proc_tb);
|
||||
|
||||
initial
|
||||
begin
|
||||
#1000 reset = 0;
|
||||
#20000;
|
||||
$finish;
|
||||
end
|
||||
|
||||
reg [63:0] vita_time = 64'd0;
|
||||
always @(posedge clk)
|
||||
if(reset) vita_time <= 64'd0;
|
||||
else vita_time <= vita_time + 64'd1;
|
||||
|
||||
reg [63:0] tdata;
|
||||
wire [63:0] tdata_int;
|
||||
reg tlast;
|
||||
wire tlast_int;
|
||||
reg tvalid = 1'b0;
|
||||
wire tvalid_int;
|
||||
wire tready, tready_int;
|
||||
|
||||
wire [7:0] set_addr;
|
||||
wire [31:0] set_data;
|
||||
wire set_stb;
|
||||
wire ready = 1'b1;
|
||||
|
||||
task send_packet;
|
||||
input ec;
|
||||
input timed;
|
||||
input [11:0] seqnum;
|
||||
input [31:0] sid;
|
||||
input [63:0] vtime;
|
||||
input [15:0] addr;
|
||||
input [31:0] data;
|
||||
|
||||
begin
|
||||
// Send a packet
|
||||
@(posedge clk);
|
||||
tlast <= 1'b0;
|
||||
tdata <= { ec, 1'b0, timed, 1'b0, seqnum, timed ? 16'd6 : 16'd4, sid };
|
||||
tvalid <= 1;
|
||||
@(posedge clk);
|
||||
if(timed)
|
||||
begin
|
||||
tdata <= vtime;
|
||||
@(posedge clk);
|
||||
end
|
||||
tlast <= 1'b1;
|
||||
tdata <= { 16'h0, addr, data };
|
||||
@(posedge clk);
|
||||
tvalid <= 0;
|
||||
@(posedge clk);
|
||||
end
|
||||
endtask // send_packet
|
||||
|
||||
initial
|
||||
begin
|
||||
tvalid <= 1'b0;
|
||||
while(reset)
|
||||
@(posedge clk);
|
||||
send_packet(1'b1,1'b0,12'h5,32'hDEAD_BEEF,64'h0,16'hB,32'hF00D_1234);
|
||||
send_packet(1'b1,1'b1,12'h6,32'hDEAD_6789,64'h20,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h7,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
//send_packet(.ec(1), .timed(0), .seqnum(5), .sid(32'hDEAD_BEEF), .vtime(0), .addr(16'hB), .data(32'hF00D_1234));
|
||||
end
|
||||
|
||||
axi_fifo_short #(.WIDTH(65)) axi_fifo_short
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({tlast,tdata}), .i_tvalid(tvalid), .i_tready(tready),
|
||||
.o_tdata({tlast_int,tdata_int}), .o_tvalid(tvalid_int), .o_tready(tready_int));
|
||||
|
||||
wire [63:0] resp_tdata;
|
||||
wire resp_tlast, resp_tvalid, resp_tready;
|
||||
|
||||
radio_ctrl_proc radio_ctrl_proc
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.ctrl_tdata(tdata_int), .ctrl_tlast(tlast_int), .ctrl_tvalid(tvalid_int), .ctrl_tready(tready_int),
|
||||
.resp_tdata(resp_tdata), .resp_tlast(resp_tlast), .resp_tvalid(resp_tvalid), .resp_tready(resp_tready),
|
||||
.vita_time(vita_time), .ready(ready),
|
||||
.set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
|
||||
.debug()
|
||||
);
|
||||
|
||||
assign resp_tready = 1'b1;
|
||||
|
||||
always @(posedge clk)
|
||||
if(resp_tvalid & resp_tready)
|
||||
begin
|
||||
$display("%x",resp_tdata);
|
||||
if(resp_tlast)
|
||||
$display("TLAST");
|
||||
end
|
||||
endmodule // radio_ctrl_proc_tb
|
||||
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
module ram_2port
|
||||
#(parameter DWIDTH=32,
|
||||
parameter AWIDTH=9)
|
||||
(input clka,
|
||||
input ena,
|
||||
input wea,
|
||||
input [AWIDTH-1:0] addra,
|
||||
input [DWIDTH-1:0] dia,
|
||||
output reg [DWIDTH-1:0] doa,
|
||||
|
||||
input clkb,
|
||||
input enb,
|
||||
input web,
|
||||
input [AWIDTH-1:0] addrb,
|
||||
input [DWIDTH-1:0] dib,
|
||||
output reg [DWIDTH-1:0] dob);
|
||||
|
||||
reg [DWIDTH-1:0] ram [(1<<AWIDTH)-1:0];
|
||||
/*
|
||||
integer i;
|
||||
initial
|
||||
for(i=0;i<(1<<AWIDTH);i=i+1)
|
||||
ram[i] <= {DWIDTH{1'b0}};
|
||||
*/
|
||||
|
||||
always @(posedge clka) begin
|
||||
if (ena)
|
||||
begin
|
||||
if (wea)
|
||||
ram[addra] <= dia;
|
||||
doa <= ram[addra];
|
||||
end
|
||||
end
|
||||
always @(posedge clkb) begin
|
||||
if (enb)
|
||||
begin
|
||||
if (web)
|
||||
ram[addrb] <= dib;
|
||||
dob <= ram[addrb];
|
||||
end
|
||||
end
|
||||
endmodule // ram_2port
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
module reset_sync
|
||||
(input clk,
|
||||
input reset_in,
|
||||
output reset_out);
|
||||
|
||||
reg reset_int;
|
||||
|
||||
reg reset_out_tmp;
|
||||
|
||||
//synthesis attribute async_reg of reset_out_tmp is "true";
|
||||
//synthesis attribute async_reg of reset_int is "true";
|
||||
always @(posedge clk or posedge reset_in)
|
||||
if(reset_in)
|
||||
{reset_out_tmp,reset_int} <= 2'b11;
|
||||
else
|
||||
{reset_out_tmp,reset_int} <= {reset_int,1'b0};
|
||||
|
||||
assign reset_out = reset_out_tmp;
|
||||
|
||||
|
||||
endmodule // reset_sync
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Copyright 2011-2012 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
//-- A settings register is a peripheral for the settings register bus.
|
||||
//-- When the settings register sees strobe abd a matching address,
|
||||
//-- the outputs will be become registered to the given input bus.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
module setting_reg
|
||||
#(parameter my_addr = 0,
|
||||
parameter awidth = 8,
|
||||
parameter width = 32,
|
||||
parameter at_reset=0)
|
||||
(input clk, input rst, input strobe, input wire [awidth-1:0] addr,
|
||||
input wire [31:0] in, output reg [width-1:0] out, output reg changed);
|
||||
|
||||
always @(posedge clk)
|
||||
if(rst)
|
||||
begin
|
||||
out <= at_reset;
|
||||
changed <= 1'b0;
|
||||
end
|
||||
else
|
||||
if(strobe & (my_addr==addr))
|
||||
begin
|
||||
out <= in[width-1:0];
|
||||
changed <= 1'b1;
|
||||
end
|
||||
else
|
||||
changed <= 1'b0;
|
||||
|
||||
endmodule // setting_reg
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// Copyright 2011-2012 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
// This module takes the settings bus on one clock domain and crosses it over to another domain
|
||||
// Typically it will be used with the input settings bus on the wishbone clock, and either
|
||||
// the system or dsp clock on the output side
|
||||
|
||||
module settings_bus_crossclock
|
||||
#(parameter FLOW_CTRL=0, parameter AWIDTH=8, parameter DWIDTH=32)
|
||||
(input clk_i, input rst_i, input set_stb_i, input [AWIDTH-1:0] set_addr_i, input [DWIDTH-1:0] set_data_i,
|
||||
input clk_o, input rst_o, output set_stb_o, output [AWIDTH-1:0] set_addr_o, output [DWIDTH-1:0] set_data_o, input blocked);
|
||||
|
||||
wire nfull, nempty;
|
||||
|
||||
axi_fifo_2clk #(.WIDTH(AWIDTH + DWIDTH), .SIZE(0)) settings_fifo
|
||||
(.reset(rst_i),
|
||||
.i_aclk(clk_i), .i_tdata({set_addr_i,set_data_i}), .i_tvalid(set_stb_i), .i_tready(nfull),
|
||||
.o_aclk(clk_o), .o_tdata({set_addr_o,set_data_o}), .o_tready(set_stb_o), .o_tvalid(nempty));
|
||||
|
||||
assign set_stb_o = nempty & (~blocked | ~FLOW_CTRL);
|
||||
|
||||
endmodule // settings_bus_crossclock
|
||||
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
// Simple I2C core
|
||||
|
||||
// Settings reg map:
|
||||
//
|
||||
// BASE+0 control register
|
||||
// byte0 - control bits, data byte, or command bits, prescaler
|
||||
// byte1 - what to do? (documented in cpp file)
|
||||
// write prescaler lo
|
||||
// write prescaler hi
|
||||
// write control
|
||||
// write data
|
||||
// write command
|
||||
// read data
|
||||
// read status
|
||||
//
|
||||
|
||||
// Readback:
|
||||
//
|
||||
// byte0 has readback value based on the last read command
|
||||
//
|
||||
|
||||
module simple_i2c_core
|
||||
#(
|
||||
//settings register base address
|
||||
parameter BASE = 0,
|
||||
|
||||
//i2c line level at reset
|
||||
parameter ARST_LVL = 1
|
||||
)
|
||||
(
|
||||
//clock and synchronous reset
|
||||
input clock, input reset,
|
||||
|
||||
//32-bit settings bus inputs
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data,
|
||||
|
||||
//32-bit data readback
|
||||
output reg [31:0] readback,
|
||||
|
||||
//read is high when i2c core can begin another transaction
|
||||
output reg ready,
|
||||
|
||||
// I2C signals
|
||||
// i2c clock line
|
||||
input scl_pad_i, // SCL-line input
|
||||
output scl_pad_o, // SCL-line output (always 1'b0)
|
||||
output scl_padoen_o, // SCL-line output enable (active low)
|
||||
|
||||
// i2c data line
|
||||
input sda_pad_i, // SDA-line input
|
||||
output sda_pad_o, // SDA-line output (always 1'b0)
|
||||
output sda_padoen_o, // SDA-line output enable (active low)
|
||||
|
||||
//optional debug output
|
||||
output [31:0] debug
|
||||
);
|
||||
|
||||
//declare command settings register
|
||||
wire [7:0] sr_what, sr_data;
|
||||
wire sr_changed;
|
||||
setting_reg #(.my_addr(BASE+0),.width(16)) i2c_cmd_sr(
|
||||
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
|
||||
.out({sr_what, sr_data}),.changed(sr_changed));
|
||||
|
||||
//declare wb interface signals
|
||||
wire [2:0] wb_addr;
|
||||
wire [7:0] wb_data_mosi;
|
||||
wire [7:0] wb_data_miso;
|
||||
wire wb_we, wb_stb, wb_cyc;
|
||||
wire wb_ack;
|
||||
|
||||
//create wishbone-based i2c core
|
||||
i2c_master_top #(.ARST_LVL(ARST_LVL)) i2c
|
||||
(.wb_clk_i(clock),.wb_rst_i(reset),.arst_i(1'b0),
|
||||
.wb_adr_i(wb_addr),.wb_dat_i(wb_data_mosi),.wb_dat_o(wb_data_miso),
|
||||
.wb_we_i(wb_we),.wb_stb_i(wb_stb),.wb_cyc_i(wb_cyc),
|
||||
.wb_ack_o(wb_ack),.wb_inta_o(),
|
||||
.scl_pad_i(scl_pad_i),.scl_pad_o(scl_pad_o),.scl_padoen_o(scl_padoen_o),
|
||||
.sda_pad_i(sda_pad_i),.sda_pad_o(sda_pad_o),.sda_padoen_o(sda_padoen_o) );
|
||||
|
||||
//not ready between setting register and wishbone ack
|
||||
always @(posedge clock) begin
|
||||
if (reset || wb_ack) ready <= 1;
|
||||
else if (sr_changed) ready <= 0;
|
||||
end
|
||||
|
||||
//register wishbone data on every ack
|
||||
always @(posedge clock) begin
|
||||
if (wb_ack) readback <= {24'b0, wb_data_miso};
|
||||
end
|
||||
|
||||
//assign wishbone signals
|
||||
assign wb_addr = sr_what[2:0];
|
||||
assign wb_stb = sr_changed;
|
||||
assign wb_we = wb_stb && sr_what[3];
|
||||
assign wb_cyc = wb_stb;
|
||||
assign wb_data_mosi = sr_data;
|
||||
|
||||
endmodule //simple_i2c_core
|
||||
@@ -0,0 +1,207 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
// Simple SPI core, the simplest, yet complete spi core I can think of
|
||||
|
||||
// Settings register controlled.
|
||||
// 2 settings regs, control and data
|
||||
// 1 32-bit readback and status signal
|
||||
|
||||
// Settings reg map:
|
||||
//
|
||||
// BASE+0 divider setting
|
||||
// bits [15:0] spi clock divider
|
||||
//
|
||||
// BASE+1 configuration input
|
||||
// bits [23:0] slave select, bit0 = slave0 enabled
|
||||
// bits [29:24] num bits (1 through 32)
|
||||
// bit [30] data input edge = in data bit latched on rising edge of clock
|
||||
// bit [31] data output edge = out data bit latched on rising edge of clock
|
||||
//
|
||||
// BASE+2 input data
|
||||
// Writing this register begins a spi transaction.
|
||||
// Bits are latched out from bit 0.
|
||||
// Therefore, load this register in reverse.
|
||||
//
|
||||
// Readback
|
||||
// Bits are latched into bit 0.
|
||||
// Therefore, data will be in-order.
|
||||
|
||||
module simple_spi_core
|
||||
#(
|
||||
//settings register base address
|
||||
parameter BASE = 0,
|
||||
|
||||
//width of serial enables (up to 24 is possible)
|
||||
parameter WIDTH = 8,
|
||||
|
||||
//idle state of the spi clock
|
||||
parameter CLK_IDLE = 0,
|
||||
|
||||
//idle state of the serial enables
|
||||
parameter SEN_IDLE = 24'hffffff
|
||||
)
|
||||
(
|
||||
//clock and synchronous reset
|
||||
input clock, input reset,
|
||||
|
||||
//32-bit settings bus inputs
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data,
|
||||
|
||||
//32-bit data readback
|
||||
output [31:0] readback,
|
||||
|
||||
//read is high when spi core can begin another transaction
|
||||
output ready,
|
||||
|
||||
//spi interface, slave selects, clock, data in, data out
|
||||
output [WIDTH-1:0] sen,
|
||||
output sclk,
|
||||
output mosi,
|
||||
input miso,
|
||||
|
||||
//optional debug output
|
||||
output [31:0] debug
|
||||
);
|
||||
|
||||
wire [15:0] sclk_divider;
|
||||
setting_reg #(.my_addr(BASE+0),.width(16)) divider_sr(
|
||||
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
|
||||
.out(sclk_divider),.changed());
|
||||
|
||||
wire [23:0] slave_select;
|
||||
wire [5:0] num_bits;
|
||||
wire datain_edge, dataout_edge;
|
||||
setting_reg #(.my_addr(BASE+1),.width(32)) config_sr(
|
||||
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
|
||||
.out({dataout_edge, datain_edge, num_bits, slave_select}),.changed());
|
||||
|
||||
wire [31:0] mosi_data;
|
||||
wire trigger_spi;
|
||||
setting_reg #(.my_addr(BASE+2),.width(32)) data_sr(
|
||||
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
|
||||
.out(mosi_data),.changed(trigger_spi));
|
||||
|
||||
localparam WAIT_TRIG = 0;
|
||||
localparam PRE_IDLE = 1;
|
||||
localparam CLK_REG = 2;
|
||||
localparam CLK_INV = 3;
|
||||
localparam POST_IDLE = 4;
|
||||
localparam IDLE_SEN = 5;
|
||||
|
||||
reg [2:0] state;
|
||||
|
||||
reg ready_reg;
|
||||
assign ready = ready_reg && ~trigger_spi;
|
||||
|
||||
//serial clock either idles or is in one of two clock states
|
||||
reg sclk_reg;
|
||||
assign sclk = sclk_reg;
|
||||
|
||||
//serial enables either idle or enabled based on state
|
||||
wire sen_is_idle = (state == WAIT_TRIG) || (state == IDLE_SEN);
|
||||
wire [23:0] sen24 = (sen_is_idle)? SEN_IDLE : (SEN_IDLE ^ slave_select);
|
||||
reg [WIDTH-1:0] sen_reg;
|
||||
always @(posedge clock) sen_reg <= sen24[WIDTH-1:0];
|
||||
assign sen = sen_reg;
|
||||
|
||||
//data output shift register
|
||||
reg [31:0] dataout_reg;
|
||||
wire [31:0] dataout_next = {dataout_reg[30:0], 1'b0};
|
||||
assign mosi = dataout_reg[31];
|
||||
|
||||
//data input shift register
|
||||
// IJB. One pipeline stage to break critical path from register in I/O pads.
|
||||
reg miso_pipe;
|
||||
always @(posedge clock)
|
||||
miso_pipe = miso;
|
||||
|
||||
reg [31:0] datain_reg;
|
||||
wire [31:0] datain_next = {datain_reg[30:0], miso_pipe};
|
||||
assign readback = datain_reg;
|
||||
|
||||
//counter for spi clock
|
||||
reg [15:0] sclk_counter;
|
||||
wire sclk_counter_done = (sclk_counter == sclk_divider);
|
||||
wire [15:0] sclk_counter_next = (sclk_counter_done)? 0 : sclk_counter + 1;
|
||||
|
||||
//counter for latching bits miso/mosi
|
||||
reg [6:0] bit_counter;
|
||||
wire [6:0] bit_counter_next = bit_counter + 1;
|
||||
wire bit_counter_done = (bit_counter_next == num_bits);
|
||||
|
||||
always @(posedge clock) begin
|
||||
if (reset) begin
|
||||
state <= WAIT_TRIG;
|
||||
sclk_reg <= CLK_IDLE;
|
||||
ready_reg <= 0;
|
||||
end
|
||||
else begin
|
||||
case (state)
|
||||
|
||||
WAIT_TRIG: begin
|
||||
if (trigger_spi) state <= PRE_IDLE;
|
||||
ready_reg <= ~trigger_spi;
|
||||
dataout_reg <= mosi_data;
|
||||
sclk_counter <= 0;
|
||||
bit_counter <= 0;
|
||||
sclk_reg <= CLK_IDLE;
|
||||
end
|
||||
|
||||
PRE_IDLE: begin
|
||||
if (sclk_counter_done) state <= CLK_REG;
|
||||
sclk_counter <= sclk_counter_next;
|
||||
sclk_reg <= CLK_IDLE;
|
||||
end
|
||||
|
||||
CLK_REG: begin
|
||||
if (sclk_counter_done) begin
|
||||
state <= CLK_INV;
|
||||
if (datain_edge != CLK_IDLE) datain_reg <= datain_next;
|
||||
if (dataout_edge != CLK_IDLE && bit_counter != 0) dataout_reg <= dataout_next;
|
||||
sclk_reg <= ~CLK_IDLE; //transition to rising when CLK_IDLE == 0
|
||||
end
|
||||
sclk_counter <= sclk_counter_next;
|
||||
end
|
||||
|
||||
CLK_INV: begin
|
||||
if (sclk_counter_done) begin
|
||||
state <= (bit_counter_done)? POST_IDLE : CLK_REG;
|
||||
bit_counter <= bit_counter_next;
|
||||
if (datain_edge == CLK_IDLE) datain_reg <= datain_next;
|
||||
if (dataout_edge == CLK_IDLE && ~bit_counter_done) dataout_reg <= dataout_next;
|
||||
sclk_reg <= CLK_IDLE; //transition to falling when CLK_IDLE == 0
|
||||
end
|
||||
sclk_counter <= sclk_counter_next;
|
||||
end
|
||||
|
||||
POST_IDLE: begin
|
||||
if (sclk_counter_done) state <= IDLE_SEN;
|
||||
sclk_counter <= sclk_counter_next;
|
||||
sclk_reg <= CLK_IDLE;
|
||||
end
|
||||
|
||||
IDLE_SEN: begin
|
||||
if (sclk_counter_done) state <= WAIT_TRIG;
|
||||
sclk_counter <= sclk_counter_next;
|
||||
sclk_reg <= CLK_IDLE;
|
||||
end
|
||||
|
||||
default: state <= WAIT_TRIG;
|
||||
|
||||
endcase //state
|
||||
end
|
||||
end
|
||||
|
||||
assign debug = {
|
||||
trigger_spi, state, //4
|
||||
sclk, mosi, miso, ready, //4
|
||||
//sen[7:0], //8
|
||||
1'b0, bit_counter[6:0], //8
|
||||
sclk_counter_done, bit_counter_done, //2
|
||||
sclk_counter[5:0] //6
|
||||
};
|
||||
|
||||
endmodule //simple_spi_core
|
||||
@@ -0,0 +1,38 @@
|
||||
#
|
||||
# Copyright 2013 Ettus Research LLC
|
||||
#
|
||||
|
||||
##################################################
|
||||
# DSP Sources
|
||||
##################################################
|
||||
DSP_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/dsp/, \
|
||||
ddc_chain.v \
|
||||
duc_chain.v \
|
||||
sign_extend.v \
|
||||
cordic_z24.v \
|
||||
clip_reg.v \
|
||||
cordic_stage.v \
|
||||
clip.v \
|
||||
cic_strober.v \
|
||||
cic_decim.v \
|
||||
cic_interp.v \
|
||||
cic_dec_shifter.v \
|
||||
cic_int_shifter.v \
|
||||
small_hb_dec.v \
|
||||
small_hb_int.v \
|
||||
hb_dec.v \
|
||||
hb_interp.v \
|
||||
round_sd.v \
|
||||
add2_and_clip_reg.v \
|
||||
add2_and_clip.v \
|
||||
add2.v \
|
||||
add2_reg.v \
|
||||
add2_and_round_reg.v \
|
||||
add2_and_round.v \
|
||||
round_reg.v \
|
||||
round.v \
|
||||
srl.v \
|
||||
acc.v \
|
||||
rx_frontend.v \
|
||||
rx_dcoffset.v \
|
||||
))
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
|
||||
module acc
|
||||
#(parameter IWIDTH=16, OWIDTH=30)
|
||||
(input clk,
|
||||
input clear,
|
||||
input acc,
|
||||
input [IWIDTH-1:0] in,
|
||||
output reg [OWIDTH-1:0] out);
|
||||
|
||||
wire [OWIDTH-1:0] in_signext;
|
||||
sign_extend #(.bits_in(IWIDTH),.bits_out(OWIDTH))
|
||||
acc_signext (.in(in),.out(in_signext));
|
||||
|
||||
// CLEAR & ~ACC --> clears the accumulator
|
||||
// CLEAR & ACC --> loads the accumulator
|
||||
// ~CLEAR & ACC --> accumulates
|
||||
// ~CLEAR & ~ACC --> hold
|
||||
|
||||
wire [OWIDTH-1:0] addend1 = clear ? 0 : out;
|
||||
wire [OWIDTH-1:0] addend2 = ~acc ? 0 : in_signext;
|
||||
wire [OWIDTH-1:0] sum_int = addend1 + addend2;
|
||||
|
||||
always @(posedge clk)
|
||||
out <= sum_int;
|
||||
|
||||
endmodule // acc
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
|
||||
module add2
|
||||
#(parameter WIDTH=16)
|
||||
(input [WIDTH-1:0] in1,
|
||||
input [WIDTH-1:0] in2,
|
||||
output [WIDTH-1:0] sum);
|
||||
|
||||
wire [WIDTH:0] sum_int = {in1[WIDTH-1],in1} + {in2[WIDTH-1],in2};
|
||||
assign sum = sum_int[WIDTH:1]; // Note -- will have some bias
|
||||
|
||||
endmodule // add2
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
module add2_and_clip
|
||||
#(parameter WIDTH=16)
|
||||
(input [WIDTH-1:0] in1,
|
||||
input [WIDTH-1:0] in2,
|
||||
output [WIDTH-1:0] sum);
|
||||
|
||||
wire [WIDTH:0] sum_int = {in1[WIDTH-1],in1} + {in2[WIDTH-1],in2};
|
||||
clip #(.bits_in(WIDTH+1),.bits_out(WIDTH)) clip
|
||||
(.in(sum_int),.out(sum));
|
||||
|
||||
endmodule // add2_and_clip
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
module add2_and_clip_reg
|
||||
#(parameter WIDTH=16)
|
||||
(input clk,
|
||||
input rst,
|
||||
input [WIDTH-1:0] in1,
|
||||
input [WIDTH-1:0] in2,
|
||||
input strobe_in,
|
||||
output reg [WIDTH-1:0] sum,
|
||||
output reg strobe_out);
|
||||
|
||||
wire [WIDTH-1:0] sum_int;
|
||||
|
||||
add2_and_clip #(.WIDTH(WIDTH)) add2_and_clip (.in1(in1),.in2(in2),.sum(sum_int));
|
||||
|
||||
always @(posedge clk)
|
||||
if(rst)
|
||||
sum <= 0;
|
||||
else if(strobe_in)
|
||||
sum <= sum_int;
|
||||
|
||||
always @(posedge clk)
|
||||
strobe_out <= strobe_in;
|
||||
|
||||
endmodule // add2_and_clip_reg
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
|
||||
module add2_and_round
|
||||
#(parameter WIDTH=16)
|
||||
(input [WIDTH-1:0] in1,
|
||||
input [WIDTH-1:0] in2,
|
||||
output [WIDTH-1:0] sum);
|
||||
|
||||
wire [WIDTH:0] sum_int = {in1[WIDTH-1],in1} + {in2[WIDTH-1],in2};
|
||||
assign sum = sum_int[WIDTH:1] + (sum_int[WIDTH] & sum_int[0]);
|
||||
|
||||
endmodule // add2_and_round
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
|
||||
module add2_and_round_reg
|
||||
#(parameter WIDTH=16)
|
||||
(input clk,
|
||||
input [WIDTH-1:0] in1,
|
||||
input [WIDTH-1:0] in2,
|
||||
output reg [WIDTH-1:0] sum);
|
||||
|
||||
wire [WIDTH-1:0] sum_int;
|
||||
|
||||
add2_and_round #(.WIDTH(WIDTH)) add2_n_rnd (.in1(in1),.in2(in2),.sum(sum_int));
|
||||
|
||||
always @(posedge clk)
|
||||
sum <= sum_int;
|
||||
|
||||
endmodule // add2_and_round_reg
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
|
||||
module add2_reg
|
||||
#(parameter WIDTH=16)
|
||||
(input clk,
|
||||
input [WIDTH-1:0] in1,
|
||||
input [WIDTH-1:0] in2,
|
||||
output reg [WIDTH-1:0] sum);
|
||||
|
||||
wire [WIDTH-1:0] sum_int;
|
||||
|
||||
add2 #(.WIDTH(WIDTH)) add2 (.in1(in1),.in2(in2),.sum(sum_int));
|
||||
|
||||
always @(posedge clk)
|
||||
sum <= sum_int;
|
||||
|
||||
endmodule // add2_reg
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
// -*- verilog -*-
|
||||
//
|
||||
// USRP - Universal Software Radio Peripheral
|
||||
//
|
||||
// Copyright (C) 2003 Matt Ettus
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
|
||||
// NOTE This only works for N=4, max decim rate of 128
|
||||
// NOTE signal "rate" is EQUAL TO the actual rate, no more -1 BS
|
||||
|
||||
module cic_dec_shifter(rate,signal_in,signal_out);
|
||||
parameter bw = 16;
|
||||
parameter maxbitgain = 28;
|
||||
|
||||
input [7:0] rate;
|
||||
input wire [bw+maxbitgain-1:0] signal_in;
|
||||
output reg [bw-1:0] signal_out;
|
||||
|
||||
function [4:0] bitgain;
|
||||
input [7:0] rate;
|
||||
case(rate)
|
||||
// Exact Cases -- N*log2(rate)
|
||||
8'd1 : bitgain = 0;
|
||||
8'd2 : bitgain = 4;
|
||||
8'd4 : bitgain = 8;
|
||||
8'd8 : bitgain = 12;
|
||||
8'd16 : bitgain = 16;
|
||||
8'd32 : bitgain = 20;
|
||||
8'd64 : bitgain = 24;
|
||||
8'd128 : bitgain = 28;
|
||||
|
||||
// Nearest without overflow -- ceil(N*log2(rate))
|
||||
8'd3 : bitgain = 7;
|
||||
8'd5 : bitgain = 10;
|
||||
8'd6 : bitgain = 11;
|
||||
8'd7 : bitgain = 12;
|
||||
8'd9 : bitgain = 13;
|
||||
8'd10,8'd11 : bitgain = 14;
|
||||
8'd12,8'd13 : bitgain = 15;
|
||||
8'd14,8'd15 : bitgain = 16;
|
||||
8'd17,8'd18,8'd19 : bitgain = 17;
|
||||
8'd20,8'd21,8'd22 : bitgain = 18;
|
||||
8'd23,8'd24,8'd25,8'd26 : bitgain = 19;
|
||||
8'd27,8'd28,8'd29,8'd30,8'd31 : bitgain = 20;
|
||||
8'd33,8'd34,8'd35,8'd36,8'd37,8'd38 : bitgain = 21;
|
||||
8'd39,8'd40,8'd41,8'd42,8'd43,8'd44,8'd45 : bitgain = 22;
|
||||
8'd46,8'd47,8'd48,8'd49,8'd50,8'd51,8'd52,8'd53 : bitgain = 23;
|
||||
8'd54,8'd55,8'd56,8'd57,8'd58,8'd59,8'd60,8'd61,8'd62,8'd63 : bitgain = 24;
|
||||
8'd65,8'd66,8'd67,8'd68,8'd69,8'd70,8'd71,8'd72,8'd73,8'd74,8'd75,8'd76 : bitgain = 25;
|
||||
8'd77,8'd78,8'd79,8'd80,8'd81,8'd82,8'd83,8'd84,8'd85,8'd86,8'd87,8'd88,8'd89,8'd90 : bitgain = 26;
|
||||
8'd91,8'd92,8'd93,8'd94,8'd95,8'd96,8'd97,8'd98,8'd99,8'd100,8'd101,8'd102,8'd103,8'd104,8'd105,8'd106,8'd107 : bitgain = 27;
|
||||
default : bitgain = 28;
|
||||
endcase // case(rate)
|
||||
endfunction // bitgain
|
||||
|
||||
wire [4:0] shift = bitgain(rate);
|
||||
|
||||
// We should be able to do this, but can't ....
|
||||
// assign signal_out = signal_in[shift+bw-1:shift];
|
||||
|
||||
always @*
|
||||
case(shift)
|
||||
5'd0 : signal_out = signal_in[0+bw-1:0];
|
||||
5'd4 : signal_out = signal_in[4+bw-1:4];
|
||||
5'd7 : signal_out = signal_in[7+bw-1:7];
|
||||
5'd8 : signal_out = signal_in[8+bw-1:8];
|
||||
5'd10 : signal_out = signal_in[10+bw-1:10];
|
||||
5'd11 : signal_out = signal_in[11+bw-1:11];
|
||||
5'd12 : signal_out = signal_in[12+bw-1:12];
|
||||
5'd13 : signal_out = signal_in[13+bw-1:13];
|
||||
5'd14 : signal_out = signal_in[14+bw-1:14];
|
||||
5'd15 : signal_out = signal_in[15+bw-1:15];
|
||||
5'd16 : signal_out = signal_in[16+bw-1:16];
|
||||
5'd17 : signal_out = signal_in[17+bw-1:17];
|
||||
5'd18 : signal_out = signal_in[18+bw-1:18];
|
||||
5'd19 : signal_out = signal_in[19+bw-1:19];
|
||||
5'd20 : signal_out = signal_in[20+bw-1:20];
|
||||
5'd21 : signal_out = signal_in[21+bw-1:21];
|
||||
5'd22 : signal_out = signal_in[22+bw-1:22];
|
||||
5'd23 : signal_out = signal_in[23+bw-1:23];
|
||||
5'd24 : signal_out = signal_in[24+bw-1:24];
|
||||
5'd25 : signal_out = signal_in[25+bw-1:25];
|
||||
5'd26 : signal_out = signal_in[26+bw-1:26];
|
||||
5'd27 : signal_out = signal_in[27+bw-1:27];
|
||||
5'd28 : signal_out = signal_in[28+bw-1:28];
|
||||
|
||||
default : signal_out = signal_in[28+bw-1:28];
|
||||
endcase // case(shift)
|
||||
|
||||
endmodule // cic_dec_shifter
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
// -*- verilog -*-
|
||||
//
|
||||
// USRP - Universal Software Radio Peripheral
|
||||
//
|
||||
// Copyright (C) 2003 Matt Ettus
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
|
||||
module cic_decim
|
||||
#(parameter bw = 16, parameter N = 4, parameter log2_of_max_rate = 7)
|
||||
(input clock,
|
||||
input reset,
|
||||
input enable,
|
||||
input [7:0] rate,
|
||||
input strobe_in,
|
||||
input strobe_out,
|
||||
input [bw-1:0] signal_in,
|
||||
output reg [bw-1:0] signal_out);
|
||||
|
||||
localparam maxbitgain = N * log2_of_max_rate;
|
||||
|
||||
wire [bw+maxbitgain-1:0] signal_in_ext;
|
||||
reg [bw+maxbitgain-1:0] integrator [0:N-1];
|
||||
reg [bw+maxbitgain-1:0] differentiator [0:N-1];
|
||||
reg [bw+maxbitgain-1:0] pipeline [0:N-1];
|
||||
reg [bw+maxbitgain-1:0] sampler;
|
||||
|
||||
integer i;
|
||||
|
||||
sign_extend #(bw,bw+maxbitgain)
|
||||
ext_input (.in(signal_in),.out(signal_in_ext));
|
||||
|
||||
always @(posedge clock)
|
||||
if(~enable)
|
||||
for(i=0;i<N;i=i+1)
|
||||
integrator[i] <= 0;
|
||||
else if (strobe_in)
|
||||
begin
|
||||
integrator[0] <= integrator[0] + signal_in_ext;
|
||||
for(i=1;i<N;i=i+1)
|
||||
integrator[i] <= integrator[i] + integrator[i-1];
|
||||
end
|
||||
|
||||
always @(posedge clock)
|
||||
if(~enable)
|
||||
begin
|
||||
sampler <= 0;
|
||||
for(i=0;i<N;i=i+1)
|
||||
begin
|
||||
pipeline[i] <= 0;
|
||||
differentiator[i] <= 0;
|
||||
end
|
||||
end
|
||||
else if (strobe_out)
|
||||
begin
|
||||
sampler <= integrator[N-1];
|
||||
differentiator[0] <= sampler;
|
||||
pipeline[0] <= sampler - differentiator[0];
|
||||
for(i=1;i<N;i=i+1)
|
||||
begin
|
||||
differentiator[i] <= pipeline[i-1];
|
||||
pipeline[i] <= pipeline[i-1] - differentiator[i];
|
||||
end
|
||||
end // if (enable && strobe_out)
|
||||
|
||||
wire [bw-1:0] signal_out_unreg;
|
||||
|
||||
cic_dec_shifter #(bw)
|
||||
cic_dec_shifter(rate,pipeline[N-1],signal_out_unreg);
|
||||
|
||||
always @(posedge clock)
|
||||
signal_out <= signal_out_unreg;
|
||||
|
||||
endmodule // cic_decim
|
||||
@@ -0,0 +1,88 @@
|
||||
// -*- verilog -*-
|
||||
//
|
||||
// USRP - Universal Software Radio Peripheral
|
||||
//
|
||||
// Copyright (C) 2003 Matt Ettus
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
|
||||
// NOTE This only works for N=4, max interp rate of 128
|
||||
// NOTE signal "rate" is EQUAL TO the actual rate (no more -1 BS)
|
||||
|
||||
module cic_int_shifter(rate,signal_in,signal_out);
|
||||
parameter bw = 16;
|
||||
parameter maxbitgain = 21;
|
||||
|
||||
input [7:0] rate;
|
||||
input wire [bw+maxbitgain-1:0] signal_in;
|
||||
output reg [bw-1:0] signal_out;
|
||||
|
||||
function [4:0] bitgain;
|
||||
input [7:0] rate;
|
||||
case(rate)
|
||||
// Exact Cases
|
||||
8'd1 : bitgain = 0;
|
||||
8'd2 : bitgain = 3;
|
||||
8'd4 : bitgain = 6;
|
||||
8'd8 : bitgain = 9;
|
||||
8'd16 : bitgain = 12;
|
||||
8'd32 : bitgain = 15;
|
||||
8'd64 : bitgain = 18;
|
||||
8'd128 : bitgain = 21;
|
||||
|
||||
// Nearest without overflow
|
||||
8'd3 : bitgain = 5;
|
||||
8'd5 : bitgain = 7;
|
||||
8'd6 : bitgain = 8;
|
||||
8'd7 : bitgain = 9;
|
||||
8'd9,8'd10 : bitgain = 10;
|
||||
8'd11,8'd12 : bitgain = 11;
|
||||
8'd13,8'd14,8'd15 : bitgain = 12;
|
||||
8'd17,8'd18,8'd19,8'd20 : bitgain = 13;
|
||||
8'd21,8'd22,8'd23,8'd24,8'd25 : bitgain = 14;
|
||||
8'd26,8'd27,8'd28,8'd29,8'd30,8'd31 : bitgain = 15;
|
||||
8'd33,8'd34,8'd35,8'd36,8'd37,8'd38,8'd39,8'd40 : bitgain = 16;
|
||||
8'd41,8'd42,8'd43,8'd44,8'd45,8'd46,8'd47,8'd48,8'd49,8'd50 : bitgain = 17;
|
||||
8'd51,8'd52,8'd53,8'd54,8'd55,8'd56,8'd57,8'd58,8'd59,8'd60,8'd61,8'd62,8'd63 : bitgain = 18;
|
||||
8'd65,8'd66,8'd67,8'd68,8'd69,8'd70,8'd71,8'd72,8'd73,8'd74,8'd75,8'd76,8'd77,8'd78,8'd79,8'd80 : bitgain = 19;
|
||||
8'd81,8'd82,8'd83,8'd84,8'd85,8'd86,8'd87,8'd88,8'd89,8'd90,8'd91,8'd92,8'd93,8'd94,8'd95,8'd96,8'd97,8'd98,8'd99,8'd100,8'd101 : bitgain = 20;
|
||||
|
||||
default : bitgain = 21;
|
||||
endcase // case(rate)
|
||||
endfunction // bitgain
|
||||
|
||||
wire [4:0] shift = bitgain(rate);
|
||||
|
||||
// We should be able to do this, but can't ....
|
||||
// assign signal_out = signal_in[shift+bw-1:shift];
|
||||
|
||||
always @*
|
||||
case(shift)
|
||||
5'd0 : signal_out = signal_in[0+bw-1:0];
|
||||
5'd3 : signal_out = signal_in[3+bw-1:3];
|
||||
5'd6 : signal_out = signal_in[6+bw-1:6];
|
||||
5'd9 : signal_out = signal_in[9+bw-1:9];
|
||||
5'd12 : signal_out = signal_in[12+bw-1:12];
|
||||
5'd15 : signal_out = signal_in[15+bw-1:15];
|
||||
5'd18 : signal_out = signal_in[18+bw-1:18];
|
||||
5'd21 : signal_out = signal_in[21+bw-1:21];
|
||||
|
||||
5'd5 : signal_out = signal_in[5+bw-1:5];
|
||||
5'd7 : signal_out = signal_in[7+bw-1:7];
|
||||
5'd8 : signal_out = signal_in[8+bw-1:8];
|
||||
5'd10 : signal_out = signal_in[10+bw-1:10];
|
||||
5'd11 : signal_out = signal_in[11+bw-1:11];
|
||||
5'd13 : signal_out = signal_in[13+bw-1:13];
|
||||
5'd14 : signal_out = signal_in[14+bw-1:14];
|
||||
5'd16 : signal_out = signal_in[16+bw-1:16];
|
||||
5'd17 : signal_out = signal_in[17+bw-1:17];
|
||||
5'd19 : signal_out = signal_in[19+bw-1:19];
|
||||
5'd20 : signal_out = signal_in[20+bw-1:20];
|
||||
|
||||
default : signal_out = signal_in[21+bw-1:21];
|
||||
endcase // case(shift)
|
||||
|
||||
endmodule // cic_int_shifter
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
// -*- verilog -*-
|
||||
//
|
||||
// USRP - Universal Software Radio Peripheral
|
||||
//
|
||||
// Copyright (C) 2003 Matt Ettus
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
|
||||
module cic_interp
|
||||
#(parameter bw = 16, parameter N = 4, parameter log2_of_max_rate = 7)
|
||||
(input clock,
|
||||
input reset,
|
||||
input enable,
|
||||
input [7:0] rate,
|
||||
input strobe_in,
|
||||
input strobe_out,
|
||||
input [bw-1:0] signal_in,
|
||||
output reg [bw-1:0] signal_out);
|
||||
|
||||
integer i;
|
||||
localparam maxbitgain = (N-1)*log2_of_max_rate;
|
||||
|
||||
wire [bw+maxbitgain-1:0] signal_in_ext;
|
||||
reg [bw+maxbitgain-1:0] integrator [0:N-1];
|
||||
reg [bw+maxbitgain-1:0] differentiator [0:N-1];
|
||||
reg [bw+maxbitgain-1:0] pipeline [0:N-1];
|
||||
|
||||
sign_extend #(bw,bw+maxbitgain)
|
||||
ext_input (.in(signal_in),.out(signal_in_ext));
|
||||
|
||||
//FIXME Note that this section has pipe and diff reversed
|
||||
// It still works, but is confusing
|
||||
always @(posedge clock)
|
||||
if(reset | ~enable)
|
||||
for(i=0;i<N;i=i+1)
|
||||
integrator[i] <= 0;
|
||||
else if (enable & strobe_out)
|
||||
begin
|
||||
if(strobe_in)
|
||||
integrator[0] <= integrator[0] + pipeline[N-1];
|
||||
for(i=1;i<N;i=i+1)
|
||||
integrator[i] <= integrator[i] + integrator[i-1];
|
||||
end
|
||||
|
||||
always @(posedge clock)
|
||||
if(reset | ~enable)
|
||||
begin
|
||||
for(i=0;i<N;i=i+1)
|
||||
begin
|
||||
differentiator[i] <= 0;
|
||||
pipeline[i] <= 0;
|
||||
end
|
||||
end
|
||||
else if (enable && strobe_in)
|
||||
begin
|
||||
differentiator[0] <= signal_in_ext;
|
||||
pipeline[0] <= signal_in_ext - differentiator[0];
|
||||
for(i=1;i<N;i=i+1)
|
||||
begin
|
||||
differentiator[i] <= pipeline[i-1];
|
||||
pipeline[i] <= pipeline[i-1] - differentiator[i];
|
||||
end
|
||||
end
|
||||
|
||||
wire [bw-1:0] signal_out_unreg;
|
||||
cic_int_shifter #(bw)
|
||||
cic_int_shifter(rate,integrator[N-1],signal_out_unreg);
|
||||
|
||||
always @(posedge clock)
|
||||
signal_out <= signal_out_unreg;
|
||||
|
||||
endmodule // cic_interp
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// USRP2 - Universal Software Radio Peripheral Mk II
|
||||
//
|
||||
// Copyright (C) 2008 Matt Ettus
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
module cic_strober
|
||||
#(parameter WIDTH=8)
|
||||
( input clock,
|
||||
input reset,
|
||||
input enable,
|
||||
input [WIDTH-1:0] rate, // Rate should EQUAL to your desired divide ratio, no more -1 BS
|
||||
input strobe_fast,
|
||||
output wire strobe_slow );
|
||||
|
||||
reg [WIDTH-1:0] counter;
|
||||
wire now = (counter==1);
|
||||
assign strobe_slow = now && enable && strobe_fast;
|
||||
|
||||
always @(posedge clock)
|
||||
if(reset)
|
||||
counter <= 0;
|
||||
else if (~enable)
|
||||
counter <= rate;
|
||||
else if(strobe_fast)
|
||||
if(now)
|
||||
counter <= rate;
|
||||
else
|
||||
counter <= counter - 1;
|
||||
|
||||
endmodule // cic_strober
|
||||
@@ -0,0 +1,24 @@
|
||||
// -*- verilog -*-
|
||||
//
|
||||
// USRP - Universal Software Radio Peripheral
|
||||
//
|
||||
// Copyright (C) 2008 Matt Ettus
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
// Clipping "macro", keeps the bottom bits
|
||||
|
||||
module clip
|
||||
#(parameter bits_in=0,
|
||||
parameter bits_out=0)
|
||||
(input [bits_in-1:0] in,
|
||||
output [bits_out-1:0] out);
|
||||
|
||||
wire overflow = |in[bits_in-1:bits_out-1] & ~(&in[bits_in-1:bits_out-1]);
|
||||
assign out = overflow ?
|
||||
(in[bits_in-1] ? {1'b1,{(bits_out-1){1'b0}}} : {1'b0,{(bits_out-1){1'b1}}}) :
|
||||
in[bits_out-1:0];
|
||||
|
||||
endmodule // clip
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// -*- verilog -*-
|
||||
//
|
||||
// USRP - Universal Software Radio Peripheral
|
||||
//
|
||||
// Copyright (C) 2008 Matt Ettus
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
// Clipping "macro", keeps the bottom bits
|
||||
|
||||
module clip_reg
|
||||
#(parameter bits_in=0,
|
||||
parameter bits_out=0,
|
||||
parameter STROBED=1'b0)
|
||||
(input clk,
|
||||
input [bits_in-1:0] in,
|
||||
output reg [bits_out-1:0] out,
|
||||
input strobe_in,
|
||||
output reg strobe_out);
|
||||
|
||||
wire [bits_out-1:0] temp;
|
||||
|
||||
clip #(.bits_in(bits_in),.bits_out(bits_out)) clip (.in(in),.out(temp));
|
||||
|
||||
always @(posedge clk)
|
||||
strobe_out <= strobe_in;
|
||||
|
||||
always @(posedge clk)
|
||||
if(strobe_in | ~STROBED)
|
||||
out <= temp;
|
||||
|
||||
endmodule // clip_reg
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// -*- verilog -*-
|
||||
//
|
||||
// USRP - Universal Software Radio Peripheral
|
||||
//
|
||||
// Copyright (C) 2003 Matt Ettus
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
module cordic_stage( clock, reset, enable, xi,yi,zi,constant,xo,yo,zo);
|
||||
parameter bitwidth = 16;
|
||||
parameter zwidth = 16;
|
||||
parameter shift = 1;
|
||||
|
||||
input clock;
|
||||
input reset;
|
||||
input enable;
|
||||
input [bitwidth-1:0] xi,yi;
|
||||
input [zwidth-1:0] zi;
|
||||
input [zwidth-1:0] constant;
|
||||
output [bitwidth-1:0] xo,yo;
|
||||
output [zwidth-1:0] zo;
|
||||
|
||||
wire z_is_pos = ~zi[zwidth-1];
|
||||
|
||||
reg [bitwidth-1:0] xo,yo;
|
||||
reg [zwidth-1:0] zo;
|
||||
|
||||
always @(posedge clock)
|
||||
if(reset)
|
||||
begin
|
||||
xo <= 0;
|
||||
yo <= 0;
|
||||
zo <= 0;
|
||||
end
|
||||
else //if(enable)
|
||||
begin
|
||||
xo <= z_is_pos ?
|
||||
xi - {{shift+1{yi[bitwidth-1]}},yi[bitwidth-2:shift]} :
|
||||
xi + {{shift+1{yi[bitwidth-1]}},yi[bitwidth-2:shift]};
|
||||
yo <= z_is_pos ?
|
||||
yi + {{shift+1{xi[bitwidth-1]}},xi[bitwidth-2:shift]} :
|
||||
yi - {{shift+1{xi[bitwidth-1]}},xi[bitwidth-2:shift]};
|
||||
zo <= z_is_pos ?
|
||||
zi - constant :
|
||||
zi + constant;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,112 @@
|
||||
// -*- verilog -*-
|
||||
//
|
||||
// USRP - Universal Software Radio Peripheral
|
||||
//
|
||||
// Copyright (C) 2003, 2007 Matt Ettus
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
module cordic_z24(clock, reset, enable, xi, yi, zi, xo, yo, zo );
|
||||
parameter bitwidth = 16;
|
||||
parameter stages = 19;
|
||||
localparam zwidth = 24;
|
||||
|
||||
input clock;
|
||||
input reset;
|
||||
input enable;
|
||||
input [bitwidth-1:0] xi, yi;
|
||||
output [bitwidth-1:0] xo, yo;
|
||||
input [zwidth-1:0] zi;
|
||||
output [zwidth-1:0] zo;
|
||||
|
||||
reg [bitwidth+1:0] x0,y0;
|
||||
reg [zwidth-2:0] z0;
|
||||
wire [bitwidth+1:0] x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20;
|
||||
wire [bitwidth+1:0] y1,y2,y3,y4,y5,y6,y7,y8,y9,y10,y11,y12,y13,y14,y15,y16,y17,y18,y19,y20;
|
||||
wire [zwidth-2:0] z1,z2,z3,z4,z5,z6,z7,z8,z9,z10,z11,z12,z13,z14,z15,z16,z17,z18,z19,z20;
|
||||
|
||||
wire [bitwidth+1:0] xi_ext = {{2{xi[bitwidth-1]}},xi};
|
||||
wire [bitwidth+1:0] yi_ext = {{2{yi[bitwidth-1]}},yi};
|
||||
|
||||
// Compute consts. Would be easier if vlog had atan...
|
||||
// see gen_cordic_consts.py
|
||||
|
||||
// constants for 24 bit wide phase
|
||||
localparam c00 = 23'd2097152;
|
||||
localparam c01 = 23'd1238021;
|
||||
localparam c02 = 23'd654136;
|
||||
localparam c03 = 23'd332050;
|
||||
localparam c04 = 23'd166669;
|
||||
localparam c05 = 23'd83416;
|
||||
localparam c06 = 23'd41718;
|
||||
localparam c07 = 23'd20860;
|
||||
localparam c08 = 23'd10430;
|
||||
localparam c09 = 23'd5215;
|
||||
localparam c10 = 23'd2608;
|
||||
localparam c11 = 23'd1304;
|
||||
localparam c12 = 23'd652;
|
||||
localparam c13 = 23'd326;
|
||||
localparam c14 = 23'd163;
|
||||
localparam c15 = 23'd81;
|
||||
localparam c16 = 23'd41;
|
||||
localparam c17 = 23'd20;
|
||||
localparam c18 = 23'd10;
|
||||
localparam c19 = 23'd5;
|
||||
localparam c20 = 23'd3;
|
||||
localparam c21 = 23'd1;
|
||||
localparam c22 = 23'd1;
|
||||
localparam c23 = 23'd0;
|
||||
|
||||
always @(posedge clock)
|
||||
if(reset)
|
||||
begin
|
||||
x0 <= 0; y0 <= 0; z0 <= 0;
|
||||
end
|
||||
else// if(enable)
|
||||
begin
|
||||
z0 <= zi[zwidth-2:0];
|
||||
case (zi[zwidth-1:zwidth-2])
|
||||
2'b00, 2'b11 :
|
||||
begin
|
||||
x0 <= xi_ext;
|
||||
y0 <= yi_ext;
|
||||
end
|
||||
2'b01, 2'b10 :
|
||||
begin
|
||||
x0 <= -xi_ext;
|
||||
y0 <= -yi_ext;
|
||||
end
|
||||
endcase // case(zi[zwidth-1:zwidth-2])
|
||||
end // else: !if(reset)
|
||||
|
||||
// FIXME need to handle variable number of stages
|
||||
// This would be easier if arrays worked better in vlog...
|
||||
|
||||
cordic_stage #(bitwidth+2,zwidth-1,0) cordic_stage0 (clock,reset,enable,x0,y0,z0,c00,x1,y1,z1);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,1) cordic_stage1 (clock,reset,enable,x1,y1,z1,c01,x2,y2,z2);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,2) cordic_stage2 (clock,reset,enable,x2,y2,z2,c02,x3,y3,z3);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,3) cordic_stage3 (clock,reset,enable,x3,y3,z3,c03,x4,y4,z4);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,4) cordic_stage4 (clock,reset,enable,x4,y4,z4,c04,x5,y5,z5);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,5) cordic_stage5 (clock,reset,enable,x5,y5,z5,c05,x6,y6,z6);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,6) cordic_stage6 (clock,reset,enable,x6,y6,z6,c06,x7,y7,z7);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,7) cordic_stage7 (clock,reset,enable,x7,y7,z7,c07,x8,y8,z8);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,8) cordic_stage8 (clock,reset,enable,x8,y8,z8,c08,x9,y9,z9);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,9) cordic_stage9 (clock,reset,enable,x9,y9,z9,c09,x10,y10,z10);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,10) cordic_stage10 (clock,reset,enable,x10,y10,z10,c10,x11,y11,z11);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,11) cordic_stage11 (clock,reset,enable,x11,y11,z11,c11,x12,y12,z12);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,12) cordic_stage12 (clock,reset,enable,x12,y12,z12,c12,x13,y13,z13);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,13) cordic_stage13 (clock,reset,enable,x13,y13,z13,c13,x14,y14,z14);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,14) cordic_stage14 (clock,reset,enable,x14,y14,z14,c14,x15,y15,z15);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,15) cordic_stage15 (clock,reset,enable,x15,y15,z15,c15,x16,y16,z16);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,16) cordic_stage16 (clock,reset,enable,x16,y16,z16,c16,x17,y17,z17);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,17) cordic_stage17 (clock,reset,enable,x17,y17,z17,c17,x18,y18,z18);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,18) cordic_stage18 (clock,reset,enable,x18,y18,z18,c18,x19,y19,z19);
|
||||
cordic_stage #(bitwidth+2,zwidth-1,19) cordic_stage19 (clock,reset,enable,x19,y19,z19,c19,x20,y20,z20);
|
||||
|
||||
assign xo = x20[bitwidth:1];
|
||||
assign yo = y20[bitwidth:1];
|
||||
assign zo = z20;
|
||||
|
||||
endmodule // cordic
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
//
|
||||
// Copyright 2011-2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
//! The USRP digital down-conversion chain
|
||||
|
||||
module ddc_chain
|
||||
#(
|
||||
parameter BASE = 0,
|
||||
parameter DSPNO = 0,
|
||||
parameter WIDTH = 24
|
||||
)
|
||||
(input clk, input rst, input clr,
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data,
|
||||
|
||||
// From RX frontend
|
||||
input [WIDTH-1:0] rx_fe_i,
|
||||
input [WIDTH-1:0] rx_fe_q,
|
||||
|
||||
// To RX control
|
||||
output [31:0] sample,
|
||||
input run,
|
||||
output strobe,
|
||||
output [31:0] debug
|
||||
);
|
||||
|
||||
localparam cwidth = 25;
|
||||
localparam zwidth = 24;
|
||||
|
||||
wire [31:0] phase_inc;
|
||||
reg [31:0] phase;
|
||||
|
||||
wire [17:0] scale_factor;
|
||||
wire [cwidth-1:0] i_cordic, q_cordic;
|
||||
wire [WIDTH-1:0] i_cordic_clip, q_cordic_clip;
|
||||
wire [WIDTH-1:0] i_cic, q_cic;
|
||||
wire [WIDTH-1:0] i_hb1, q_hb1;
|
||||
wire [WIDTH-1:0] i_hb2, q_hb2;
|
||||
|
||||
wire strobe_cic, strobe_hb1, strobe_hb2;
|
||||
wire enable_hb1, enable_hb2;
|
||||
wire [7:0] cic_decim_rate;
|
||||
|
||||
reg [WIDTH-1:0] rx_fe_i_mux, rx_fe_q_mux;
|
||||
wire realmode;
|
||||
wire swap_iq;
|
||||
|
||||
setting_reg #(.my_addr(BASE+0)) sr_0
|
||||
(.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out(phase_inc),.changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+1), .width(18)) sr_1
|
||||
(.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out(scale_factor),.changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+2), .width(10)) sr_2
|
||||
(.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out({enable_hb1, enable_hb2, cic_decim_rate}),.changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+3), .width(2)) sr_3
|
||||
(.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out({realmode,swap_iq}),.changed());
|
||||
|
||||
// MUX so we can do realmode signals on either input
|
||||
|
||||
always @(posedge clk)
|
||||
if(swap_iq)
|
||||
begin
|
||||
rx_fe_i_mux <= rx_fe_q;
|
||||
rx_fe_q_mux <= realmode ? 0 : rx_fe_i;
|
||||
end
|
||||
else
|
||||
begin
|
||||
rx_fe_i_mux <= rx_fe_i;
|
||||
rx_fe_q_mux <= realmode ? 0 : rx_fe_q;
|
||||
end
|
||||
|
||||
// NCO
|
||||
always @(posedge clk)
|
||||
if(rst)
|
||||
phase <= 0;
|
||||
else if(~run)
|
||||
phase <= 0;
|
||||
else
|
||||
phase <= phase + phase_inc;
|
||||
|
||||
//sign extension of cordic input
|
||||
wire [cwidth-1:0] to_cordic_i, to_cordic_q;
|
||||
sign_extend #(.bits_in(WIDTH), .bits_out(cwidth)) sign_extend_cordic_i (.in(rx_fe_i_mux), .out(to_cordic_i));
|
||||
sign_extend #(.bits_in(WIDTH), .bits_out(cwidth)) sign_extend_cordic_q (.in(rx_fe_q_mux), .out(to_cordic_q));
|
||||
|
||||
// CORDIC 24-bit I/O
|
||||
cordic_z24 #(.bitwidth(cwidth))
|
||||
cordic(.clock(clk), .reset(rst), .enable(run),
|
||||
.xi(to_cordic_i),. yi(to_cordic_q), .zi(phase[31:32-zwidth]),
|
||||
.xo(i_cordic),.yo(q_cordic),.zo() );
|
||||
|
||||
clip_reg #(.bits_in(cwidth), .bits_out(WIDTH)) clip_i
|
||||
(.clk(clk), .in(i_cordic), .strobe_in(1'b1), .out(i_cordic_clip));
|
||||
clip_reg #(.bits_in(cwidth), .bits_out(WIDTH)) clip_q
|
||||
(.clk(clk), .in(q_cordic), .strobe_in(1'b1), .out(q_cordic_clip));
|
||||
|
||||
// CIC decimator 24 bit I/O
|
||||
cic_strober cic_strober(.clock(clk),.reset(rst),.enable(run),.rate(cic_decim_rate),
|
||||
.strobe_fast(1),.strobe_slow(strobe_cic) );
|
||||
|
||||
cic_decim #(.bw(WIDTH))
|
||||
decim_i (.clock(clk),.reset(rst),.enable(run),
|
||||
.rate(cic_decim_rate),.strobe_in(1'b1),.strobe_out(strobe_cic),
|
||||
.signal_in(i_cordic_clip),.signal_out(i_cic));
|
||||
|
||||
cic_decim #(.bw(WIDTH))
|
||||
decim_q (.clock(clk),.reset(rst),.enable(run),
|
||||
.rate(cic_decim_rate),.strobe_in(1'b1),.strobe_out(strobe_cic),
|
||||
.signal_in(q_cordic_clip),.signal_out(q_cic));
|
||||
|
||||
// First (small) halfband 24 bit I/O
|
||||
small_hb_dec #(.WIDTH(WIDTH)) small_hb_i
|
||||
(.clk(clk),.rst(rst),.bypass(~enable_hb1),.run(run),
|
||||
.stb_in(strobe_cic),.data_in(i_cic),.stb_out(strobe_hb1),.data_out(i_hb1));
|
||||
|
||||
small_hb_dec #(.WIDTH(WIDTH)) small_hb_q
|
||||
(.clk(clk),.rst(rst),.bypass(~enable_hb1),.run(run),
|
||||
.stb_in(strobe_cic),.data_in(q_cic),.stb_out(),.data_out(q_hb1));
|
||||
|
||||
// Second (large) halfband 24 bit I/O
|
||||
wire [8:0] cpi_hb = enable_hb1 ? {cic_decim_rate,1'b0} : {1'b0,cic_decim_rate};
|
||||
hb_dec #(.WIDTH(WIDTH)) hb_i
|
||||
(.clk(clk),.rst(rst),.bypass(~enable_hb2),.run(run),.cpi(cpi_hb),
|
||||
.stb_in(strobe_hb1),.data_in(i_hb1),.stb_out(strobe_hb2),.data_out(i_hb2));
|
||||
|
||||
hb_dec #(.WIDTH(WIDTH)) hb_q
|
||||
(.clk(clk),.rst(rst),.bypass(~enable_hb2),.run(run),.cpi(cpi_hb),
|
||||
.stb_in(strobe_hb1),.data_in(q_hb1),.stb_out(),.data_out(q_hb2));
|
||||
|
||||
//scalar operation (gain of 6 bits)
|
||||
wire [35:0] prod_i, prod_q;
|
||||
|
||||
MULT18X18S mult_i
|
||||
(.P(prod_i), .A(i_hb2[WIDTH-1:WIDTH-18]), .B(scale_factor), .C(clk), .CE(strobe_hb2), .R(rst) );
|
||||
MULT18X18S mult_q
|
||||
(.P(prod_q), .A(q_hb2[WIDTH-1:WIDTH-18]), .B(scale_factor), .C(clk), .CE(strobe_hb2), .R(rst) );
|
||||
|
||||
//pipeline for the multiplier (gain of 10 bits)
|
||||
reg [WIDTH-1:0] prod_reg_i, prod_reg_q;
|
||||
reg strobe_mult;
|
||||
|
||||
always @(posedge clk) begin
|
||||
strobe_mult <= strobe_hb2;
|
||||
prod_reg_i <= prod_i[33:34-WIDTH];
|
||||
prod_reg_q <= prod_q[33:34-WIDTH];
|
||||
end
|
||||
|
||||
// Round final answer to 16 bits
|
||||
round_sd #(.WIDTH_IN(WIDTH),.WIDTH_OUT(16)) round_i
|
||||
(.clk(clk),.reset(rst), .in(prod_reg_i),.strobe_in(strobe_mult), .out(sample[31:16]), .strobe_out(strobe));
|
||||
|
||||
round_sd #(.WIDTH_IN(WIDTH),.WIDTH_OUT(16)) round_q
|
||||
(.clk(clk),.reset(rst), .in(prod_reg_q),.strobe_in(strobe_mult), .out(sample[15:0]), .strobe_out());
|
||||
|
||||
assign debug = {enable_hb1, enable_hb2, run, strobe, strobe_cic, strobe_hb1, strobe_hb2};
|
||||
|
||||
endmodule // ddc_chain
|
||||
@@ -0,0 +1,146 @@
|
||||
//
|
||||
// Copyright 2011-2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
//! The USRP digital up-conversion chain
|
||||
|
||||
module duc_chain
|
||||
#(
|
||||
parameter BASE = 0,
|
||||
parameter DSPNO = 0,
|
||||
parameter WIDTH = 24
|
||||
)
|
||||
(input clk, input rst, input clr,
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data,
|
||||
|
||||
// To TX frontend
|
||||
output [WIDTH-1:0] tx_fe_i,
|
||||
output [WIDTH-1:0] tx_fe_q,
|
||||
|
||||
// From TX control
|
||||
input [31:0] sample,
|
||||
input run,
|
||||
output strobe,
|
||||
output [31:0] debug
|
||||
);
|
||||
|
||||
wire [17:0] scale_factor;
|
||||
wire [31:0] phase_inc;
|
||||
reg [31:0] phase;
|
||||
wire [7:0] interp_rate;
|
||||
wire [3:0] tx_femux_a, tx_femux_b;
|
||||
wire enable_hb1, enable_hb2;
|
||||
wire rate_change;
|
||||
|
||||
setting_reg #(.my_addr(BASE+0)) sr_0
|
||||
(.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out(phase_inc),.changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+1), .width(18)) sr_1
|
||||
(.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out(scale_factor),.changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+2), .width(10)) sr_2
|
||||
(.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out({enable_hb1, enable_hb2, interp_rate}),.changed(rate_change));
|
||||
|
||||
// Strobes are all now delayed by 1 cycle for timing reasons
|
||||
wire strobe_cic_pre, strobe_hb1_pre, strobe_hb2_pre;
|
||||
reg strobe_cic = 1;
|
||||
reg strobe_hb1 = 1;
|
||||
reg strobe_hb2 = 1;
|
||||
|
||||
assign strobe = strobe_hb1;
|
||||
|
||||
cic_strober #(.WIDTH(8))
|
||||
cic_strober(.clock(clk),.reset(rst),.enable(run & ~rate_change),.rate(interp_rate),
|
||||
.strobe_fast(1),.strobe_slow(strobe_cic_pre) );
|
||||
cic_strober #(.WIDTH(2))
|
||||
hb2_strober(.clock(clk),.reset(rst),.enable(run & ~rate_change),.rate(enable_hb2 ? 2 : 1),
|
||||
.strobe_fast(strobe_cic_pre),.strobe_slow(strobe_hb2_pre) );
|
||||
cic_strober #(.WIDTH(2))
|
||||
hb1_strober(.clock(clk),.reset(rst),.enable(run & ~rate_change),.rate(enable_hb1 ? 2 : 1),
|
||||
.strobe_fast(strobe_hb2_pre),.strobe_slow(strobe_hb1_pre) );
|
||||
|
||||
always @(posedge clk) strobe_hb1 <= strobe_hb1_pre;
|
||||
always @(posedge clk) strobe_hb2 <= strobe_hb2_pre;
|
||||
always @(posedge clk) strobe_cic <= strobe_cic_pre;
|
||||
|
||||
// NCO
|
||||
always @(posedge clk)
|
||||
if(rst)
|
||||
phase <= 0;
|
||||
else if(~run)
|
||||
phase <= 0;
|
||||
else
|
||||
phase <= phase + phase_inc;
|
||||
|
||||
wire signed [17:0] da, db;
|
||||
wire signed [35:0] prod_i, prod_q;
|
||||
|
||||
assign tx_fe_i = prod_i[33:34-WIDTH];
|
||||
assign tx_fe_q = prod_q[33:34-WIDTH];
|
||||
|
||||
wire [17:0] i_interp, q_interp;
|
||||
|
||||
wire [17:0] hb1_i, hb1_q, hb2_i, hb2_q;
|
||||
|
||||
wire [7:0] cpo = enable_hb2 ? ({interp_rate,1'b0}) : interp_rate;
|
||||
// Note that max CIC rate is 128, which would give an overflow on cpo if enable_hb2 is true,
|
||||
// but the default case inside hb_interp handles this
|
||||
|
||||
hb_interp #(.IWIDTH(18),.OWIDTH(18),.ACCWIDTH(WIDTH)) hb_interp_i
|
||||
(.clk(clk),.rst(rst),.bypass(~enable_hb1),.cpo(cpo),.stb_in(strobe_hb1),.data_in({sample[31:16], 2'b0}),.stb_out(strobe_hb2),.data_out(hb1_i));
|
||||
hb_interp #(.IWIDTH(18),.OWIDTH(18),.ACCWIDTH(WIDTH)) hb_interp_q
|
||||
(.clk(clk),.rst(rst),.bypass(~enable_hb1),.cpo(cpo),.stb_in(strobe_hb1),.data_in({sample[15:0], 2'b0}),.stb_out(strobe_hb2),.data_out(hb1_q));
|
||||
|
||||
small_hb_int #(.WIDTH(18)) small_hb_interp_i
|
||||
(.clk(clk),.rst(rst),.bypass(~enable_hb2),.stb_in(strobe_hb2),.data_in(hb1_i),
|
||||
.output_rate(interp_rate),.stb_out(strobe_cic),.data_out(hb2_i));
|
||||
small_hb_int #(.WIDTH(18)) small_hb_interp_q
|
||||
(.clk(clk),.rst(rst),.bypass(~enable_hb2),.stb_in(strobe_hb2),.data_in(hb1_q),
|
||||
.output_rate(interp_rate),.stb_out(strobe_cic),.data_out(hb2_q));
|
||||
|
||||
cic_interp #(.bw(18),.N(4),.log2_of_max_rate(7))
|
||||
cic_interp_i(.clock(clk),.reset(rst),.enable(run & ~rate_change),.rate(interp_rate),
|
||||
.strobe_in(strobe_cic),.strobe_out(1),
|
||||
.signal_in(hb2_i),.signal_out(i_interp));
|
||||
|
||||
cic_interp #(.bw(18),.N(4),.log2_of_max_rate(7))
|
||||
cic_interp_q(.clock(clk),.reset(rst),.enable(run & ~rate_change),.rate(interp_rate),
|
||||
.strobe_in(strobe_cic),.strobe_out(1),
|
||||
.signal_in(hb2_q),.signal_out(q_interp));
|
||||
|
||||
localparam cwidth = WIDTH; // was 18
|
||||
localparam zwidth = 24; // was 16
|
||||
|
||||
wire [cwidth-1:0] da_c, db_c;
|
||||
|
||||
cordic_z24 #(.bitwidth(cwidth))
|
||||
cordic(.clock(clk), .reset(rst), .enable(run),
|
||||
.xi({i_interp,{(cwidth-18){1'b0}}}),.yi({q_interp,{(cwidth-18){1'b0}}}),
|
||||
.zi(phase[31:32-zwidth]),
|
||||
.xo(da_c),.yo(db_c),.zo() );
|
||||
|
||||
MULT18X18S MULT18X18S_inst
|
||||
(.P(prod_i), // 36-bit multiplier output
|
||||
.A(da_c[cwidth-1:cwidth-18]), // 18-bit multiplier input
|
||||
.B(scale_factor), // 18-bit multiplier input
|
||||
.C(clk), // Clock input
|
||||
.CE(1), // Clock enable input
|
||||
.R(rst) // Synchronous reset input
|
||||
);
|
||||
|
||||
MULT18X18S MULT18X18S_inst_2
|
||||
(.P(prod_q), // 36-bit multiplier output
|
||||
.A(db_c[cwidth-1:cwidth-18]), // 18-bit multiplier input
|
||||
.B(scale_factor), // 18-bit multiplier input
|
||||
.C(clk), // Clock input
|
||||
.CE(1), // Clock enable input
|
||||
.R(rst) // Synchronous reset input
|
||||
);
|
||||
|
||||
assign debug = {strobe_cic, strobe_hb1, strobe_hb2,run};
|
||||
|
||||
endmodule // duc_chain
|
||||
@@ -0,0 +1,177 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
// Final halfband decimator
|
||||
// Implements impulse responses of the form [A 0 B 0 C .. 0 H 0.5 H 0 .. C 0 B 0 A]
|
||||
// Strobe in cannot come faster than every 2nd clock cycle
|
||||
// These taps designed by halfgen4 from ldoolittle
|
||||
// myfilt = round(2^18 * halfgen4(.7/4,8))
|
||||
|
||||
module hb_dec
|
||||
#(parameter WIDTH=24)
|
||||
(input clk,
|
||||
input rst,
|
||||
input bypass,
|
||||
input run,
|
||||
input [8:0] cpi, // Clocks per input -- equal to the decimation ratio ahead of this block
|
||||
input stb_in,
|
||||
input [WIDTH-1:0] data_in,
|
||||
output reg stb_out,
|
||||
output reg [WIDTH-1:0] data_out);
|
||||
|
||||
localparam INTWIDTH = 17;
|
||||
localparam ACCWIDTH = WIDTH + 3;
|
||||
|
||||
// Round off inputs to 17 bits because of 18 bit multipliers
|
||||
wire [INTWIDTH-1:0] data_rnd;
|
||||
wire stb_rnd;
|
||||
|
||||
round_sd #(.WIDTH_IN(WIDTH),.WIDTH_OUT(INTWIDTH)) round_in
|
||||
(.clk(clk),.reset(rst),.in(data_in),.strobe_in(stb_in),.out(data_rnd),.strobe_out(stb_rnd));
|
||||
|
||||
// Control
|
||||
reg [3:0] addr_odd_a, addr_odd_b, addr_odd_c, addr_odd_d;
|
||||
wire write_odd, write_even, do_mult;
|
||||
reg odd;
|
||||
reg [2:0] phase, phase_d1;
|
||||
reg stb_out_int;
|
||||
wire clear, do_acc;
|
||||
assign do_mult = 1;
|
||||
|
||||
always @(posedge clk)
|
||||
if(rst | ~run)
|
||||
odd <= 0;
|
||||
else if(stb_rnd)
|
||||
odd <= ~odd;
|
||||
|
||||
assign write_odd = stb_rnd & odd;
|
||||
assign write_even = stb_rnd & ~odd;
|
||||
|
||||
always @(posedge clk)
|
||||
if(rst | ~run)
|
||||
phase <= 0;
|
||||
else if(stb_rnd & odd)
|
||||
phase <= 1;
|
||||
else if(phase == 4)
|
||||
phase <= 0;
|
||||
else if(phase != 0)
|
||||
phase <= phase + 1;
|
||||
|
||||
always @(posedge clk)
|
||||
phase_d1 <= phase;
|
||||
|
||||
reg [15:0] stb_out_pre;
|
||||
always @(posedge clk)
|
||||
if(rst)
|
||||
stb_out_pre <= 0;
|
||||
else
|
||||
stb_out_pre <= {stb_out_pre[14:0],(stb_rnd & odd)};
|
||||
|
||||
always @*
|
||||
case(phase)
|
||||
1 : begin addr_odd_a = 0; addr_odd_b = 15; end
|
||||
2 : begin addr_odd_a = 1; addr_odd_b = 14; end
|
||||
3 : begin addr_odd_a = 2; addr_odd_b = 13; end
|
||||
4 : begin addr_odd_a = 3; addr_odd_b = 12; end
|
||||
default : begin addr_odd_a = 0; addr_odd_b = 15; end
|
||||
endcase // case(phase)
|
||||
|
||||
always @*
|
||||
case(phase)
|
||||
1 : begin addr_odd_c = 4; addr_odd_d = 11; end
|
||||
2 : begin addr_odd_c = 5; addr_odd_d = 10; end
|
||||
3 : begin addr_odd_c = 6; addr_odd_d = 9; end
|
||||
4 : begin addr_odd_c = 7; addr_odd_d = 8; end
|
||||
default : begin addr_odd_c = 4; addr_odd_d = 11; end
|
||||
endcase // case(phase)
|
||||
|
||||
assign do_acc = |stb_out_pre[6:3];
|
||||
assign clear = stb_out_pre[3];
|
||||
|
||||
// Data
|
||||
wire [INTWIDTH-1:0] data_odd_a, data_odd_b, data_odd_c, data_odd_d;
|
||||
reg [INTWIDTH:0] sum1, sum2; // these are 18-bit inputs to mult
|
||||
reg [WIDTH:0] final_sum;
|
||||
wire [WIDTH-1:0] final_sum_clip;
|
||||
reg [17:0] coeff1, coeff2;
|
||||
wire [35:0] prod1, prod2;
|
||||
|
||||
always @* // Outer coeffs
|
||||
case(phase_d1)
|
||||
1 : coeff1 = -107;
|
||||
2 : coeff1 = 445;
|
||||
3 : coeff1 = -1271;
|
||||
4 : coeff1 = 2959;
|
||||
default : coeff1 = -107;
|
||||
endcase // case(phase)
|
||||
|
||||
always @* // Inner coeffs
|
||||
case(phase_d1)
|
||||
1 : coeff2 = -6107;
|
||||
2 : coeff2 = 11953;
|
||||
3 : coeff2 = -24706;
|
||||
4 : coeff2 = 82359;
|
||||
default : coeff2 = -6107;
|
||||
endcase // case(phase)
|
||||
|
||||
srl #(.WIDTH(INTWIDTH)) srl_odd_a
|
||||
(.clk(clk),.write(write_odd),.in(data_rnd),.addr(addr_odd_a),.out(data_odd_a));
|
||||
srl #(.WIDTH(INTWIDTH)) srl_odd_b
|
||||
(.clk(clk),.write(write_odd),.in(data_rnd),.addr(addr_odd_b),.out(data_odd_b));
|
||||
srl #(.WIDTH(INTWIDTH)) srl_odd_c
|
||||
(.clk(clk),.write(write_odd),.in(data_rnd),.addr(addr_odd_c),.out(data_odd_c));
|
||||
srl #(.WIDTH(INTWIDTH)) srl_odd_d
|
||||
(.clk(clk),.write(write_odd),.in(data_rnd),.addr(addr_odd_d),.out(data_odd_d));
|
||||
|
||||
always @(posedge clk) sum1 <= {data_odd_a[INTWIDTH-1],data_odd_a} + {data_odd_b[INTWIDTH-1],data_odd_b};
|
||||
always @(posedge clk) sum2 <= {data_odd_c[INTWIDTH-1],data_odd_c} + {data_odd_d[INTWIDTH-1],data_odd_d};
|
||||
|
||||
wire [INTWIDTH-1:0] data_even;
|
||||
reg [3:0] addr_even;
|
||||
|
||||
always @(posedge clk)
|
||||
case(cpi)
|
||||
// 1 is an error
|
||||
2 : addr_even <= 9; // Maximum speed (overall decim by 4)
|
||||
3, 4, 5, 6, 7 : addr_even <= 8;
|
||||
default : addr_even <= 7;
|
||||
endcase // case(cpi)
|
||||
|
||||
srl #(.WIDTH(INTWIDTH)) srl_even
|
||||
(.clk(clk),.write(write_even),.in(data_rnd),.addr(addr_even),.out(data_even));
|
||||
|
||||
MULT18X18S mult1(.C(clk), .CE(do_mult), .R(rst), .P(prod1), .A(coeff1), .B(sum1) );
|
||||
MULT18X18S mult2(.C(clk), .CE(do_mult), .R(rst), .P(prod2), .A(coeff2), .B(sum2) );
|
||||
|
||||
reg [35:0] sum_of_prod;
|
||||
always @(posedge clk) sum_of_prod <= prod1 + prod2; // Can't overflow
|
||||
|
||||
wire [ACCWIDTH-1:0] acc_out;
|
||||
acc #(.IWIDTH(ACCWIDTH-2),.OWIDTH(ACCWIDTH))
|
||||
acc (.clk(clk),.clear(clear),.acc(do_acc),.in(sum_of_prod[35:38-ACCWIDTH]),.out(acc_out));
|
||||
|
||||
wire [ACCWIDTH-1:0] data_even_signext;
|
||||
|
||||
localparam SHIFT_FACTOR = 6;
|
||||
|
||||
sign_extend #(.bits_in(INTWIDTH),.bits_out(ACCWIDTH-SHIFT_FACTOR)) signext_data_even
|
||||
(.in(data_even),.out(data_even_signext[ACCWIDTH-1:SHIFT_FACTOR]));
|
||||
assign data_even_signext[SHIFT_FACTOR-1:0] = 0;
|
||||
|
||||
always @(posedge clk) final_sum <= acc_out + data_even_signext;
|
||||
|
||||
clip #(.bits_in(WIDTH+1), .bits_out(WIDTH)) clip (.in(final_sum), .out(final_sum_clip));
|
||||
|
||||
// Output MUX to allow for bypass
|
||||
wire selected_stb = bypass ? stb_in : stb_out_pre[8];
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
stb_out <= selected_stb;
|
||||
if(selected_stb)
|
||||
data_out <= bypass ? data_in : final_sum_clip;
|
||||
end
|
||||
|
||||
endmodule // hb_dec
|
||||
@@ -0,0 +1,165 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
// First halfband iterpolator
|
||||
// Implements impulse responses of the form [A 0 B 0 C .. 0 H 0.5 H 0 .. C 0 B 0 A]
|
||||
// Strobe in cannot come faster than every 4th clock cycle,
|
||||
// Strobe out cannot come faster than every 2nd clock cycle
|
||||
|
||||
// These taps designed by halfgen4 from ldoolittle
|
||||
// myfilt = round(2^18 * halfgen4(.7/4,8))
|
||||
|
||||
module hb_interp
|
||||
#(parameter IWIDTH=18, OWIDTH=18, ACCWIDTH=24)
|
||||
(input clk,
|
||||
input rst,
|
||||
input bypass,
|
||||
input [7:0] cpo, // Clocks per output, must be at least 2
|
||||
input stb_in,
|
||||
input [IWIDTH-1:0] data_in,
|
||||
input stb_out,
|
||||
output reg [OWIDTH-1:0] data_out);
|
||||
|
||||
localparam MWIDTH = ACCWIDTH-2;
|
||||
localparam CWIDTH = 18;
|
||||
|
||||
reg [CWIDTH-1:0] coeff1, coeff2;
|
||||
reg [3:0] addr_a, addr_b, addr_c, addr_d, addr_e;
|
||||
wire [IWIDTH-1:0] data_a, data_b, data_c, data_d, data_e, sum1, sum2;
|
||||
wire [35:0] prod1, prod2;
|
||||
|
||||
reg [2:0] phase, phase_d1, phase_d2, phase_d3, phase_d4, phase_d5;
|
||||
|
||||
always @(posedge clk)
|
||||
if(rst)
|
||||
phase <= 0;
|
||||
else
|
||||
if(stb_in)
|
||||
phase <= 1;
|
||||
else if(phase==4)
|
||||
phase <= 0;
|
||||
else if(phase!=0)
|
||||
phase <= phase + 1;
|
||||
always @(posedge clk) phase_d1 <= phase;
|
||||
always @(posedge clk) phase_d2 <= phase_d1;
|
||||
always @(posedge clk) phase_d3 <= phase_d2;
|
||||
always @(posedge clk) phase_d4 <= phase_d3;
|
||||
always @(posedge clk) phase_d5 <= phase_d4;
|
||||
|
||||
srl #(.WIDTH(IWIDTH)) srl_a
|
||||
(.clk(clk),.rst(rst),.write(stb_in),.in(data_in),.addr(addr_a),.out(data_a));
|
||||
srl #(.WIDTH(IWIDTH)) srl_b
|
||||
(.clk(clk),.rst(rst),.write(stb_in),.in(data_in),.addr(addr_b),.out(data_b));
|
||||
srl #(.WIDTH(IWIDTH)) srl_c
|
||||
(.clk(clk),.rst(rst),.write(stb_in),.in(data_in),.addr(addr_c),.out(data_c));
|
||||
srl #(.WIDTH(IWIDTH)) srl_d
|
||||
(.clk(clk),.rst(rst),.write(stb_in),.in(data_in),.addr(addr_d),.out(data_d));
|
||||
srl #(.WIDTH(IWIDTH)) srl_e
|
||||
(.clk(clk),.rst(rst),.write(stb_in),.in(data_in),.addr(addr_e),.out(data_e));
|
||||
|
||||
always @*
|
||||
case(phase)
|
||||
1 : begin addr_a = 0; addr_b = 15; end
|
||||
2 : begin addr_a = 1; addr_b = 14; end
|
||||
3 : begin addr_a = 2; addr_b = 13; end
|
||||
4 : begin addr_a = 3; addr_b = 12; end
|
||||
default : begin addr_a = 0; addr_b = 15; end
|
||||
endcase // case(phase)
|
||||
|
||||
always @*
|
||||
case(phase)
|
||||
1 : begin addr_c = 4; addr_d = 11; end
|
||||
2 : begin addr_c = 5; addr_d = 10; end
|
||||
3 : begin addr_c = 6; addr_d = 9; end
|
||||
4 : begin addr_c = 7; addr_d = 8; end
|
||||
default : begin addr_c = 4; addr_d = 11; end
|
||||
endcase // case(phase)
|
||||
|
||||
always @*
|
||||
case(cpo)
|
||||
2 : addr_e <= 9;
|
||||
3,4,5,6,7,8 : addr_e <= 8;
|
||||
default : addr_e <= 7; // This case works for 256, which = 0 due to overflow outside this block
|
||||
endcase // case(cpo)
|
||||
|
||||
always @* // Outer coeffs
|
||||
case(phase_d1)
|
||||
1 : coeff1 = -107;
|
||||
2 : coeff1 = 445;
|
||||
3 : coeff1 = -1271;
|
||||
4 : coeff1 = 2959;
|
||||
default : coeff1 = -107;
|
||||
endcase // case(phase)
|
||||
|
||||
always @* // Inner coeffs
|
||||
case(phase_d1)
|
||||
1 : coeff2 = -6107;
|
||||
2 : coeff2 = 11953;
|
||||
3 : coeff2 = -24706;
|
||||
4 : coeff2 = 82359;
|
||||
default : coeff2 = -6107;
|
||||
endcase // case(phase)
|
||||
|
||||
add2_reg /*_and_round_reg*/ #(.WIDTH(IWIDTH)) add1 (.clk(clk),.in1(data_a),.in2(data_b),.sum(sum1));
|
||||
add2_reg /*_and_round_reg*/ #(.WIDTH(IWIDTH)) add2 (.clk(clk),.in1(data_c),.in2(data_d),.sum(sum2));
|
||||
// sum1, sum2 available on phase_d1
|
||||
|
||||
wire do_mult = 1;
|
||||
MULT18X18S mult1(.C(clk), .CE(do_mult), .R(rst), .P(prod1), .A(coeff1), .B(sum1) );
|
||||
MULT18X18S mult2(.C(clk), .CE(do_mult), .R(rst), .P(prod2), .A(coeff2), .B(sum2) );
|
||||
// prod1, prod2 available on phase_d2
|
||||
|
||||
wire [MWIDTH-1:0] sum_of_prod;
|
||||
|
||||
add2_and_round_reg #(.WIDTH(MWIDTH))
|
||||
add3 (.clk(clk),.in1(prod1[35:36-MWIDTH]),.in2(prod2[35:36-MWIDTH]),.sum(sum_of_prod));
|
||||
// sum_of_prod available on phase_d3
|
||||
|
||||
wire [ACCWIDTH-1:0] acc_out;
|
||||
|
||||
wire clear = (phase_d3 == 1);
|
||||
wire do_acc = (phase_d3 != 0);
|
||||
|
||||
acc #(.IWIDTH(MWIDTH),.OWIDTH(ACCWIDTH)) //IJB rst
|
||||
acc (.clk(clk),.clear(rst|clear),.acc(do_acc),.in(sum_of_prod),.out(acc_out));
|
||||
// acc_out available on phase_d4
|
||||
|
||||
wire [ACCWIDTH-6:0] clipped_acc;
|
||||
clip #(.bits_in(ACCWIDTH),.bits_out(ACCWIDTH-5)) final_clip(.in(acc_out),.out(clipped_acc));
|
||||
|
||||
reg [ACCWIDTH-6:0] clipped_reg;
|
||||
always @(posedge clk)
|
||||
if (rst)
|
||||
clipped_reg <= 0;
|
||||
else if(phase_d4 == 4)
|
||||
clipped_reg <= clipped_acc;
|
||||
// clipped_reg available on phase_d5
|
||||
|
||||
wire [OWIDTH-1:0] data_out_round;
|
||||
round #(.bits_in(ACCWIDTH-5),.bits_out(OWIDTH)) final_round (.in(clipped_reg),.out(data_out_round));
|
||||
|
||||
reg odd;
|
||||
always @(posedge clk)
|
||||
if(rst)
|
||||
odd <= 0;
|
||||
else if(stb_in)
|
||||
odd <= 0;
|
||||
else if(stb_out)
|
||||
odd <= 1;
|
||||
|
||||
always @(posedge clk)
|
||||
if (rst)
|
||||
data_out <= 0;
|
||||
else if(bypass)
|
||||
data_out <= data_in;
|
||||
else if(stb_out)
|
||||
if(odd)
|
||||
data_out <= data_e;
|
||||
else
|
||||
data_out <= data_out_round;
|
||||
|
||||
// data_out available on phase_d6
|
||||
|
||||
endmodule // hb_interp
|
||||
@@ -0,0 +1,47 @@
|
||||
// -*- verilog -*-
|
||||
//
|
||||
// USRP - Universal Software Radio Peripheral
|
||||
//
|
||||
// Copyright (C) 2011 Matt Ettus
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
// Rounding "macro"
|
||||
// Keeps the topmost bits, does proper 2s comp round to zero (unbiased truncation)
|
||||
|
||||
module round
|
||||
#(parameter bits_in=0,
|
||||
parameter bits_out=0,
|
||||
parameter round_to_zero=0, // original behavior
|
||||
parameter round_to_nearest=1, // lowest noise
|
||||
parameter trunc=0) // round to negative infinity
|
||||
(input [bits_in-1:0] in,
|
||||
output [bits_out-1:0] out,
|
||||
output [bits_in-bits_out:0] err);
|
||||
|
||||
wire round_corr,round_corr_trunc,round_corr_rtz,round_corr_nearest,round_corr_nearest_safe;
|
||||
|
||||
assign round_corr_trunc = 0;
|
||||
assign round_corr_rtz = (in[bits_in-1] & |in[bits_in-bits_out-1:0]);
|
||||
assign round_corr_nearest = in[bits_in-bits_out-1];
|
||||
|
||||
generate
|
||||
if(bits_in-bits_out > 1)
|
||||
assign round_corr_nearest_safe = (~in[bits_in-1] & (&in[bits_in-2:bits_out])) ? 0 :
|
||||
round_corr_nearest;
|
||||
else
|
||||
assign round_corr_nearest_safe = round_corr_nearest;
|
||||
endgenerate
|
||||
|
||||
|
||||
assign round_corr = round_to_nearest ? round_corr_nearest_safe :
|
||||
trunc ? round_corr_trunc :
|
||||
round_to_zero ? round_corr_rtz :
|
||||
0; // default to trunc
|
||||
|
||||
assign out = in[bits_in-1:bits_in-bits_out] + round_corr;
|
||||
|
||||
assign err = in - {out,{(bits_in-bits_out){1'b0}}};
|
||||
|
||||
endmodule // round
|
||||
@@ -0,0 +1,32 @@
|
||||
// -*- verilog -*-
|
||||
//
|
||||
// USRP - Universal Software Radio Peripheral
|
||||
//
|
||||
// Copyright (C) 2008 Matt Ettus
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
// Rounding "macro"
|
||||
// Keeps the topmost bits, does proper 2s comp rounding (round-to-zero)
|
||||
|
||||
module round_reg
|
||||
#(parameter bits_in=0,
|
||||
parameter bits_out=0)
|
||||
(input clk,
|
||||
input [bits_in-1:0] in,
|
||||
output reg [bits_out-1:0] out,
|
||||
output reg [bits_in-bits_out:0] err);
|
||||
|
||||
wire [bits_out-1:0] temp;
|
||||
wire [bits_in-bits_out:0] err_temp;
|
||||
|
||||
round #(.bits_in(bits_in),.bits_out(bits_out)) round (.in(in),.out(temp), .err(err_temp));
|
||||
|
||||
always @(posedge clk)
|
||||
out <= temp;
|
||||
|
||||
always @(posedge clk)
|
||||
err <= err_temp;
|
||||
|
||||
endmodule // round_reg
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
|
||||
module round_sd
|
||||
#(parameter WIDTH_IN=18,
|
||||
parameter WIDTH_OUT=16,
|
||||
parameter DISABLE_SD=0)
|
||||
(input clk, input reset,
|
||||
input [WIDTH_IN-1:0] in, input strobe_in,
|
||||
output [WIDTH_OUT-1:0] out, output strobe_out);
|
||||
|
||||
localparam ERR_WIDTH = WIDTH_IN - WIDTH_OUT + 1;
|
||||
|
||||
wire [ERR_WIDTH-1:0] err;
|
||||
wire [WIDTH_IN-1:0] err_ext, sum;
|
||||
|
||||
sign_extend #(.bits_in(ERR_WIDTH),.bits_out(WIDTH_IN)) ext_err (.in(err), .out(err_ext));
|
||||
|
||||
add2_and_clip_reg #(.WIDTH(WIDTH_IN)) add2_and_clip_reg
|
||||
(.clk(clk), .rst(reset), .in1(in), .in2((DISABLE_SD == 0) ? err_ext : 0), .strobe_in(strobe_in), .sum(sum), .strobe_out(strobe_out));
|
||||
|
||||
round #(.bits_in(WIDTH_IN),.bits_out(WIDTH_OUT)) round_sum (.in(sum), .out(out), .err(err));
|
||||
|
||||
endmodule // round_sd
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
module rx_dcoffset
|
||||
#(parameter WIDTH=16,
|
||||
parameter ADDR=8'd0,
|
||||
parameter alpha_shift=20)
|
||||
(input clk, input rst,
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data,
|
||||
input [WIDTH-1:0] in, output [WIDTH-1:0] out);
|
||||
|
||||
wire set_now = set_stb & (ADDR == set_addr);
|
||||
|
||||
reg fixed; // uses fixed offset
|
||||
wire [WIDTH-1:0] fixed_dco;
|
||||
|
||||
localparam int_width = WIDTH + alpha_shift;
|
||||
reg [int_width-1:0] integrator;
|
||||
wire [WIDTH-1:0] quantized;
|
||||
|
||||
always @(posedge clk)
|
||||
if(rst)
|
||||
begin
|
||||
fixed <= 0;
|
||||
integrator <= {int_width{1'b0}};
|
||||
end
|
||||
else if(set_now)
|
||||
begin
|
||||
fixed <= set_data[31];
|
||||
if(set_data[30])
|
||||
integrator <= {set_data[29:0],{(int_width-30){1'b0}}};
|
||||
end
|
||||
else if(~fixed)
|
||||
integrator <= integrator + {{(alpha_shift){out[WIDTH-1]}},out};
|
||||
|
||||
round_sd #(.WIDTH_IN(int_width),.WIDTH_OUT(WIDTH)) round_sd
|
||||
(.clk(clk), .reset(rst), .in(integrator), .strobe_in(1'b1), .out(quantized), .strobe_out());
|
||||
|
||||
add2_and_clip_reg #(.WIDTH(WIDTH)) add2_and_clip_reg
|
||||
(.clk(clk), .rst(rst), .in1(in), .in2(-quantized), .strobe_in(1'b1), .sum(out), .strobe_out());
|
||||
|
||||
endmodule // rx_dcoffset
|
||||
@@ -0,0 +1,78 @@
|
||||
|
||||
module rx_frontend
|
||||
#(parameter BASE = 0,
|
||||
parameter IQCOMP_EN = 1)
|
||||
(input clk, input rst,
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data,
|
||||
|
||||
input [15:0] adc_a, input adc_ovf_a,
|
||||
input [15:0] adc_b, input adc_ovf_b,
|
||||
|
||||
output [23:0] i_out, output [23:0] q_out,
|
||||
input run,
|
||||
output [31:0] debug
|
||||
);
|
||||
|
||||
reg [15:0] adc_i, adc_q;
|
||||
wire [17:0] adc_i_ofs, adc_q_ofs;
|
||||
wire [35:0] corr_i, corr_q; wire [17:0] mag_corr,phase_corr;
|
||||
wire swap_iq;
|
||||
|
||||
setting_reg #(.my_addr(BASE), .width(1)) sr_8
|
||||
(.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out(swap_iq),.changed());
|
||||
|
||||
always @(posedge clk)
|
||||
if(swap_iq) // Swap
|
||||
{adc_i,adc_q} <= {adc_b,adc_a};
|
||||
else
|
||||
{adc_i,adc_q} <= {adc_a,adc_b};
|
||||
|
||||
setting_reg #(.my_addr(BASE+1),.width(18)) sr_1
|
||||
(.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out(mag_corr),.changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+2),.width(18)) sr_2
|
||||
(.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out(phase_corr),.changed());
|
||||
|
||||
generate
|
||||
if(IQCOMP_EN == 1)
|
||||
begin
|
||||
rx_dcoffset #(.WIDTH(18),.ADDR(BASE+3)) rx_dcoffset_i
|
||||
(.clk(clk),.rst(rst),.set_stb(set_stb),.set_addr(set_addr),.set_data(set_data),
|
||||
.in({adc_i,2'b00}),.out(adc_i_ofs));
|
||||
|
||||
rx_dcoffset #(.WIDTH(18),.ADDR(BASE+4)) rx_dcoffset_q
|
||||
(.clk(clk),.rst(rst),.set_stb(set_stb),.set_addr(set_addr),.set_data(set_data),
|
||||
.in({adc_q,2'b00}),.out(adc_q_ofs));
|
||||
|
||||
MULT18X18S mult_mag_corr
|
||||
(.P(corr_i), .A(adc_i_ofs), .B(mag_corr), .C(clk), .CE(1), .R(rst) );
|
||||
|
||||
MULT18X18S mult_phase_corr
|
||||
(.P(corr_q), .A(adc_i_ofs), .B(phase_corr), .C(clk), .CE(1), .R(rst) );
|
||||
|
||||
add2_and_clip_reg #(.WIDTH(24)) add_clip_i
|
||||
(.clk(clk), .rst(rst),
|
||||
.in1({adc_i_ofs,6'd0}), .in2(corr_i[35:12]), .strobe_in(1'b1),
|
||||
.sum(i_out), .strobe_out());
|
||||
|
||||
add2_and_clip_reg #(.WIDTH(24)) add_clip_q
|
||||
(.clk(clk), .rst(rst),
|
||||
.in1({adc_q_ofs,6'd0}), .in2(corr_q[35:12]), .strobe_in(1'b1),
|
||||
.sum(q_out), .strobe_out());
|
||||
end // if (IQCOMP_EN == 1)
|
||||
else
|
||||
begin
|
||||
rx_dcoffset #(.WIDTH(24),.ADDR(BASE+3)) rx_dcoffset_i
|
||||
(.clk(clk),.rst(rst),.set_stb(set_stb),.set_addr(set_addr),.set_data(set_data),
|
||||
.in({adc_i,8'b00}),.out(i_out));
|
||||
|
||||
rx_dcoffset #(.WIDTH(24),.ADDR(BASE+4)) rx_dcoffset_q
|
||||
(.clk(clk),.rst(rst),.set_stb(set_stb),.set_addr(set_addr),.set_data(set_data),
|
||||
.in({adc_q,8'b00}),.out(q_out));
|
||||
end // else: !if(IQCOMP_EN == 1)
|
||||
endgenerate
|
||||
|
||||
endmodule // rx_frontend
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
`timescale 1ns/1ns
|
||||
module rx_frontend_tb();
|
||||
|
||||
reg clk, rst;
|
||||
|
||||
initial rst = 1;
|
||||
initial #1000 rst = 0;
|
||||
initial clk = 0;
|
||||
always #5 clk = ~clk;
|
||||
|
||||
initial $dumpfile("rx_frontend_tb.vcd");
|
||||
initial $dumpvars(0,rx_frontend_tb);
|
||||
|
||||
reg [15:0] adc_in;
|
||||
wire [17:0] adc_out;
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(adc_in[13])
|
||||
$write("-%d,",-adc_in);
|
||||
else
|
||||
$write("%d,",adc_in);
|
||||
if(adc_out[13])
|
||||
$write("-%d\n",-adc_out);
|
||||
else
|
||||
$write("%d\n",adc_out);
|
||||
end
|
||||
|
||||
rx_frontend #(.BASE(0)) rx_frontend
|
||||
(.clk(clk),.rst(rst),
|
||||
.set_stb(0),.set_addr(0),.set_data(0),
|
||||
.adc_a(adc_in), .adc_ovf_a(0),
|
||||
.adc_b(0), .adc_ovf_b(0),
|
||||
.i_out(adc_out),.q_out(),
|
||||
.run(), .debug());
|
||||
|
||||
always @(posedge clk)
|
||||
if(rst)
|
||||
adc_in <= 0;
|
||||
else
|
||||
adc_in <= adc_in + 4;
|
||||
//adc_in <= (($random % 473) + 23)/4;
|
||||
|
||||
endmodule // rx_frontend_tb
|
||||
@@ -0,0 +1,23 @@
|
||||
// -*- verilog -*-
|
||||
//
|
||||
// USRP - Universal Software Radio Peripheral
|
||||
//
|
||||
// Copyright (C) 2003 Matt Ettus
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
|
||||
// Sign extension "macro"
|
||||
// bits_out should be greater than bits_in
|
||||
|
||||
module sign_extend (in,out);
|
||||
parameter bits_in=0; // FIXME Quartus insists on a default
|
||||
parameter bits_out=0;
|
||||
|
||||
input [bits_in-1:0] in;
|
||||
output [bits_out-1:0] out;
|
||||
|
||||
assign out = {{(bits_out-bits_in){in[bits_in-1]}},in};
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,124 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
// Short halfband decimator (intended to be followed by another stage)
|
||||
// Implements impulse responses of the form [A 0 B 0.5 B 0 A]
|
||||
//
|
||||
// These taps designed by halfgen4 from ldoolittle:
|
||||
// 2 * 131072 * halfgen4(.75/8,2)
|
||||
module small_hb_dec
|
||||
#(parameter WIDTH=18)
|
||||
(input clk,
|
||||
input rst,
|
||||
input bypass,
|
||||
input run,
|
||||
input stb_in,
|
||||
input [WIDTH-1:0] data_in,
|
||||
output reg stb_out,
|
||||
output reg [WIDTH-1:0] data_out);
|
||||
|
||||
// Round off inputs to 17 bits because of 18 bit multipliers
|
||||
localparam INTWIDTH = 17;
|
||||
wire [INTWIDTH-1:0] data_rnd;
|
||||
wire stb_rnd;
|
||||
|
||||
round_sd #(.WIDTH_IN(WIDTH),.WIDTH_OUT(INTWIDTH)) round_in
|
||||
(.clk(clk),.reset(rst),.in(data_in),.strobe_in(stb_in),.out(data_rnd),.strobe_out(stb_rnd));
|
||||
|
||||
|
||||
reg stb_rnd_d1;
|
||||
reg [INTWIDTH-1:0] data_rnd_d1;
|
||||
always @(posedge clk) stb_rnd_d1 <= stb_rnd;
|
||||
always @(posedge clk) data_rnd_d1 <= data_rnd;
|
||||
|
||||
wire go;
|
||||
reg phase, go_d1, go_d2, go_d3, go_d4;
|
||||
always @(posedge clk)
|
||||
if(rst | ~run)
|
||||
phase <= 0;
|
||||
else if(stb_rnd_d1)
|
||||
phase <= ~phase;
|
||||
assign go = stb_rnd_d1 & phase;
|
||||
always @(posedge clk)
|
||||
if(rst | ~run)
|
||||
begin
|
||||
go_d1 <= 0;
|
||||
go_d2 <= 0;
|
||||
go_d3 <= 0;
|
||||
go_d4 <= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
go_d1 <= go;
|
||||
go_d2 <= go_d1;
|
||||
go_d3 <= go_d2;
|
||||
go_d4 <= go_d3;
|
||||
end
|
||||
|
||||
wire [17:0] coeff_a = -10690;
|
||||
wire [17:0] coeff_b = 75809;
|
||||
|
||||
reg [INTWIDTH-1:0] d1, d2, d3, d4 , d5, d6;
|
||||
always @(posedge clk)
|
||||
if(stb_rnd_d1 | rst)
|
||||
begin
|
||||
d1 <= data_rnd_d1;
|
||||
d2 <= d1;
|
||||
d3 <= d2;
|
||||
d4 <= d3;
|
||||
d5 <= d4;
|
||||
d6 <= d5;
|
||||
end
|
||||
|
||||
reg [17:0] sum_a, sum_b, middle, middle_d1;
|
||||
|
||||
always @(posedge clk)
|
||||
if(go)
|
||||
begin
|
||||
sum_a <= {data_rnd_d1[INTWIDTH-1],data_rnd_d1} + {d6[INTWIDTH-1],d6};
|
||||
sum_b <= {d2[INTWIDTH-1],d2} + {d4[INTWIDTH-1],d4};
|
||||
//middle <= {d3[INTWIDTH-1],d3};
|
||||
middle <= {d3,1'b0};
|
||||
end
|
||||
|
||||
always @(posedge clk)
|
||||
if(go_d1)
|
||||
middle_d1 <= middle;
|
||||
|
||||
wire [17:0] sum = go_d1 ? sum_b : sum_a;
|
||||
wire [17:0] coeff = go_d1 ? coeff_b : coeff_a;
|
||||
wire [35:0] prod;
|
||||
MULT18X18S mult(.C(clk), .CE(go_d1 | go_d2), .R(rst), .P(prod), .A(coeff), .B(sum) );
|
||||
|
||||
localparam ACCWIDTH = 30;
|
||||
reg [ACCWIDTH-1:0] accum;
|
||||
|
||||
always @(posedge clk)
|
||||
if(rst)
|
||||
accum <= 0;
|
||||
else if(go_d2)
|
||||
accum <= {middle_d1[17],middle_d1[17],middle_d1,{(16+ACCWIDTH-36){1'b0}}} + {prod[35:36-ACCWIDTH]};
|
||||
else if(go_d3)
|
||||
accum <= accum + {prod[35:36-ACCWIDTH]};
|
||||
|
||||
wire [WIDTH:0] accum_rnd;
|
||||
wire [WIDTH-1:0] accum_rnd_clip;
|
||||
|
||||
wire stb_round;
|
||||
|
||||
round_sd #(.WIDTH_IN(ACCWIDTH),.WIDTH_OUT(WIDTH+1)) round_acc
|
||||
(.clk(clk), .reset(rst), .in(accum), .strobe_in(go_d4), .out(accum_rnd), .strobe_out(stb_round));
|
||||
|
||||
clip #(.bits_in(WIDTH+1),.bits_out(WIDTH)) clip (.in(accum_rnd), .out(accum_rnd_clip));
|
||||
|
||||
// Output
|
||||
always @(posedge clk)
|
||||
begin
|
||||
stb_out <= bypass ? stb_in : stb_round;
|
||||
data_out <= bypass ? data_in : accum_rnd_clip;
|
||||
end
|
||||
|
||||
|
||||
endmodule // small_hb_dec
|
||||
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
// Short halfband decimator (intended to be followed by another stage)
|
||||
// Implements impulse responses of the form [A 0 B 0.5 B 0 A]
|
||||
//
|
||||
// These taps designed by halfgen4 from ldoolittle:
|
||||
// 2 * 131072 * halfgen4(.75/8,2)
|
||||
|
||||
module small_hb_int
|
||||
#(parameter WIDTH=18)
|
||||
(input clk,
|
||||
input rst,
|
||||
input bypass,
|
||||
input stb_in,
|
||||
input [WIDTH-1:0] data_in,
|
||||
input [7:0] output_rate,
|
||||
input stb_out,
|
||||
output reg [WIDTH-1:0] data_out);
|
||||
|
||||
|
||||
reg [WIDTH-1:0] d1, d2, d3, d4, d5, d6;
|
||||
|
||||
localparam MWIDTH = 36;
|
||||
wire [MWIDTH-1:0] prod;
|
||||
|
||||
reg [6:0] stbin_d;
|
||||
|
||||
always @(posedge clk)
|
||||
stbin_d <= {stbin_d[5:0],stb_in};
|
||||
|
||||
always @(posedge clk)
|
||||
if (rst)
|
||||
begin
|
||||
d1 <= 0;
|
||||
d2 <= 0;
|
||||
d3 <= 0;
|
||||
d4 <= 0;
|
||||
d5 <= 0;
|
||||
d6 <= 0;
|
||||
end
|
||||
else if(stb_in)
|
||||
begin
|
||||
d1 <= data_in;
|
||||
d2 <= d1;
|
||||
d3 <= d2;
|
||||
d4 <= d3;
|
||||
d5 <= d4;
|
||||
d6 <= d5;
|
||||
end
|
||||
|
||||
wire [WIDTH-1:0] sum_outer, sum_inner;
|
||||
add2_and_round_reg #(.WIDTH(WIDTH)) add_outer (.clk(clk),.in1(d1),.in2(d4),.sum(sum_outer));
|
||||
add2_and_round_reg #(.WIDTH(WIDTH)) add_inner (.clk(clk),.in1(d2),.in2(d3),.sum(sum_inner));
|
||||
|
||||
wire [17:0] coeff_outer = -10690;
|
||||
wire [17:0] coeff_inner = 75809;
|
||||
|
||||
MULT18X18S mult(.C(clk), .CE(1), .R(rst), .P(prod), .A(stbin_d[1] ? coeff_outer : coeff_inner),
|
||||
.B(stbin_d[1] ? sum_outer : sum_inner) );
|
||||
|
||||
wire [MWIDTH:0] accum;
|
||||
acc #(.IWIDTH(MWIDTH),.OWIDTH(MWIDTH+1))
|
||||
acc (.clk(clk),.clear(stbin_d[2]),.acc(|stbin_d[3:2]),.in(prod),.out(accum));
|
||||
|
||||
wire [WIDTH+2:0] accum_rnd;
|
||||
round_reg #(.bits_in(MWIDTH+1),.bits_out(WIDTH+3))
|
||||
final_round (.clk(clk),.in(accum),.out(accum_rnd));
|
||||
|
||||
wire [WIDTH-1:0] clipped;
|
||||
clip_reg #(.bits_in(WIDTH+3),.bits_out(WIDTH)) final_clip
|
||||
(.clk(clk),.in(accum_rnd),.strobe_in(1'b1), .out(clipped));
|
||||
|
||||
reg [WIDTH-1:0] saved, saved_d3;
|
||||
always @(posedge clk)
|
||||
if(stbin_d[6])
|
||||
saved <= clipped;
|
||||
|
||||
always @(posedge clk)
|
||||
if(stbin_d[3])
|
||||
saved_d3 <= d3;
|
||||
|
||||
always @(posedge clk)
|
||||
if(bypass)
|
||||
data_out <= data_in;
|
||||
else if(stb_in & stb_out)
|
||||
case(output_rate)
|
||||
1 : data_out <= d6;
|
||||
2 : data_out <= d4;
|
||||
3, 4, 5, 6, 7 : data_out <= d3;
|
||||
default : data_out <= d2;
|
||||
endcase // case(output_rate)
|
||||
else if(stb_out)
|
||||
data_out <= saved;
|
||||
|
||||
endmodule // small_hb_int
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
|
||||
module srl
|
||||
#(parameter WIDTH=18)
|
||||
(input clk,
|
||||
input rst,
|
||||
input write,
|
||||
input [WIDTH-1:0] in,
|
||||
input [3:0] addr,
|
||||
output [WIDTH-1:0] out);
|
||||
|
||||
genvar i;
|
||||
generate
|
||||
for (i=0;i<WIDTH;i=i+1)
|
||||
begin : gen_srl
|
||||
SRL16E
|
||||
srl16e(.Q(out[i]),
|
||||
.A0(addr[0]),.A1(addr[1]),.A2(addr[2]),.A3(addr[3]),
|
||||
.CE(write|rst),.CLK(clk),.D(in[i]));
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule // srl
|
||||
@@ -0,0 +1,20 @@
|
||||
#
|
||||
# Copyright 2012-2013 Ettus Research LLC
|
||||
#
|
||||
|
||||
##################################################
|
||||
# FIFO Sources
|
||||
##################################################
|
||||
FIFO_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/fifo/, \
|
||||
axi_mux4.v \
|
||||
axi_mux8.v \
|
||||
axi_demux4.v \
|
||||
axi_demux8.v \
|
||||
axi_fifo_short.v \
|
||||
axi_packet_gate.v \
|
||||
axi_fifo.v \
|
||||
axi_fifo64_to_fifo32.v \
|
||||
axi_fifo32_to_fifo64.v \
|
||||
axi_fifo_2clk.v \
|
||||
axi_loopback.v \
|
||||
))
|
||||
@@ -0,0 +1,77 @@
|
||||
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
// axi_demux -- takes one AXI stream, sends to one of 4 output channels
|
||||
// Choice of output channel is by external logic based on first line of packet ("header" port)
|
||||
// If compressed vita data, this line contains vita header and streamid.
|
||||
|
||||
module axi_demux4
|
||||
#(parameter ACTIVE_CHAN = 4'b1111, // ACTIVE_CHAN is a map of connected outputs
|
||||
parameter WIDTH = 64,
|
||||
parameter BUFFER=0)
|
||||
(input clk, input reset, input clear,
|
||||
output [WIDTH-1:0] header, input [1:0] dest,
|
||||
input [WIDTH-1:0] i_tdata, input i_tlast, input i_tvalid, output i_tready,
|
||||
output [WIDTH-1:0] o0_tdata, output o0_tlast, output o0_tvalid, input o0_tready,
|
||||
output [WIDTH-1:0] o1_tdata, output o1_tlast, output o1_tvalid, input o1_tready,
|
||||
output [WIDTH-1:0] o2_tdata, output o2_tlast, output o2_tvalid, input o2_tready,
|
||||
output [WIDTH-1:0] o3_tdata, output o3_tlast, output o3_tvalid, input o3_tready);
|
||||
|
||||
wire [WIDTH-1:0] i_tdata_int;
|
||||
wire i_tlast_int, i_tvalid_int, i_tready_int;
|
||||
|
||||
generate
|
||||
if(BUFFER == 0)
|
||||
begin
|
||||
assign i_tdata_int = i_tdata;
|
||||
assign i_tlast_int = i_tlast;
|
||||
assign i_tvalid_int = i_tvalid;
|
||||
assign i_tready = i_tready_int;
|
||||
end
|
||||
else
|
||||
axi_fifo_short #(.WIDTH(WIDTH+1)) axi_fifo_short
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({i_tlast,i_tdata}), .i_tvalid(i_tvalid), .i_tready(i_tready),
|
||||
.o_tdata({i_tlast_int,i_tdata_int}), .o_tvalid(i_tvalid_int), .o_tready(i_tready_int),
|
||||
.space(), .occupied());
|
||||
endgenerate
|
||||
|
||||
reg [3:0] dm_state;
|
||||
localparam DM_IDLE = 4'b0000;
|
||||
localparam DM_0 = 4'b0001;
|
||||
localparam DM_1 = 4'b0010;
|
||||
localparam DM_2 = 4'b0100;
|
||||
localparam DM_3 = 4'b1000;
|
||||
|
||||
assign header = i_tdata_int;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
dm_state <= DM_IDLE;
|
||||
else
|
||||
case (dm_state)
|
||||
DM_IDLE :
|
||||
if(i_tvalid_int)
|
||||
case(dest)
|
||||
2'b00 : dm_state <= DM_0;
|
||||
2'b01 : dm_state <= DM_1;
|
||||
2'b10 : dm_state <= DM_2;
|
||||
2'b11 : dm_state <= DM_3;
|
||||
endcase // case (i_tdata[1:0])
|
||||
|
||||
DM_0, DM_1, DM_2, DM_3 :
|
||||
if(i_tvalid_int & i_tready_int & i_tlast_int)
|
||||
dm_state <= DM_IDLE;
|
||||
|
||||
default :
|
||||
dm_state <= DM_IDLE;
|
||||
endcase // case (dm_state)
|
||||
|
||||
assign {o3_tvalid, o2_tvalid, o1_tvalid, o0_tvalid} = dm_state & {4{i_tvalid_int}};
|
||||
assign i_tready_int = |(dm_state & ({o3_tready, o2_tready, o1_tready, o0_tready} | ~ACTIVE_CHAN));
|
||||
|
||||
assign {o0_tlast, o0_tdata} = {i_tlast_int, i_tdata_int};
|
||||
assign {o1_tlast, o1_tdata} = {i_tlast_int, i_tdata_int};
|
||||
assign {o2_tlast, o2_tdata} = {i_tlast_int, i_tdata_int};
|
||||
assign {o3_tlast, o3_tdata} = {i_tlast_int, i_tdata_int};
|
||||
|
||||
endmodule // axi_demux4
|
||||
@@ -0,0 +1,60 @@
|
||||
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
// axi_demux -- takes one AXI stream, sends to one of 8 output channels
|
||||
// Choice of output channel is by external logic based on first line of packet ("header" port)
|
||||
// If compressed vita data, this line contains vita header and streamid.
|
||||
|
||||
module axi_demux8 #(
|
||||
parameter ACTIVE_CHAN = 8'b11111111, // ACTIVE_CHAN is a map of connected outputs
|
||||
parameter WIDTH = 64,
|
||||
parameter BUFFER=0
|
||||
) (
|
||||
input clk, input reset, input clear,
|
||||
output [WIDTH-1:0] header, input [2:0] dest,
|
||||
input [WIDTH-1:0] i_tdata, input i_tlast, input i_tvalid, output i_tready,
|
||||
output [WIDTH-1:0] o0_tdata, output o0_tlast, output o0_tvalid, input o0_tready,
|
||||
output [WIDTH-1:0] o1_tdata, output o1_tlast, output o1_tvalid, input o1_tready,
|
||||
output [WIDTH-1:0] o2_tdata, output o2_tlast, output o2_tvalid, input o2_tready,
|
||||
output [WIDTH-1:0] o3_tdata, output o3_tlast, output o3_tvalid, input o3_tready,
|
||||
output [WIDTH-1:0] o4_tdata, output o4_tlast, output o4_tvalid, input o4_tready,
|
||||
output [WIDTH-1:0] o5_tdata, output o5_tlast, output o5_tvalid, input o5_tready,
|
||||
output [WIDTH-1:0] o6_tdata, output o6_tlast, output o6_tvalid, input o6_tready,
|
||||
output [WIDTH-1:0] o7_tdata, output o7_tlast, output o7_tvalid, input o7_tready
|
||||
);
|
||||
|
||||
wire [WIDTH-1:0] i_tdata_int0, i_tdata_int1;
|
||||
wire i_tlast_int0, i_tlast_int1;
|
||||
wire i_tvalid_int0, i_tvalid_int1;
|
||||
wire i_tready_int0, i_tready_int1;
|
||||
|
||||
axi_demux4 #(.ACTIVE_CHAN({2'b00, (|(ACTIVE_CHAN[7:4])), (|(ACTIVE_CHAN[3:0]))}), .WIDTH(WIDTH), .BUFFER(BUFFER)) demux2 (
|
||||
.clk(clk), .reset(reset), .clear(clear),
|
||||
.header(header), .dest({1'b0, dest[2]}),
|
||||
.i_tdata(i_tdata), .i_tlast(i_tlast), .i_tvalid(i_tvalid), .i_tready(i_tready),
|
||||
.o0_tdata(i_tdata_int0), .o0_tlast(i_tlast_int0), .o0_tvalid(i_tvalid_int0), .o0_tready(i_tready_int0),
|
||||
.o1_tdata(i_tdata_int1), .o1_tlast(i_tlast_int1), .o1_tvalid(i_tvalid_int1), .o1_tready(i_tready_int1),
|
||||
.o2_tdata(), .o2_tlast(), .o2_tvalid(), .o2_tready(1'b0),
|
||||
.o3_tdata(), .o3_tlast(), .o3_tvalid(), .o3_tready(1'b0)
|
||||
);
|
||||
|
||||
axi_demux4 #(.ACTIVE_CHAN(ACTIVE_CHAN[3:0]), .WIDTH(WIDTH), .BUFFER(0)) demux4_int0 (
|
||||
.clk(clk), .reset(reset), .clear(clear),
|
||||
.header(), .dest(dest[1:0]),
|
||||
.i_tdata(i_tdata_int0), .i_tlast(i_tlast_int0), .i_tvalid(i_tvalid_int0), .i_tready(i_tready_int0),
|
||||
.o0_tdata(o0_tdata), .o0_tlast(o0_tlast), .o0_tvalid(o0_tvalid), .o0_tready(o0_tready),
|
||||
.o1_tdata(o1_tdata), .o1_tlast(o1_tlast), .o1_tvalid(o1_tvalid), .o1_tready(o1_tready),
|
||||
.o2_tdata(o2_tdata), .o2_tlast(o2_tlast), .o2_tvalid(o2_tvalid), .o2_tready(o2_tready),
|
||||
.o3_tdata(o3_tdata), .o3_tlast(o3_tlast), .o3_tvalid(o3_tvalid), .o3_tready(o3_tready)
|
||||
);
|
||||
|
||||
axi_demux4 #(.ACTIVE_CHAN(ACTIVE_CHAN[7:4]), .WIDTH(WIDTH), .BUFFER(0)) demux4_int1 (
|
||||
.clk(clk), .reset(reset), .clear(clear),
|
||||
.header(), .dest(dest[1:0]),
|
||||
.i_tdata(i_tdata_int1), .i_tlast(i_tlast_int1), .i_tvalid(i_tvalid_int1), .i_tready(i_tready_int1),
|
||||
.o0_tdata(o4_tdata), .o0_tlast(o4_tlast), .o0_tvalid(o4_tvalid), .o0_tready(o4_tready),
|
||||
.o1_tdata(o5_tdata), .o1_tlast(o5_tlast), .o1_tvalid(o5_tvalid), .o1_tready(o5_tready),
|
||||
.o2_tdata(o6_tdata), .o2_tlast(o6_tlast), .o2_tvalid(o6_tvalid), .o2_tready(o6_tready),
|
||||
.o3_tdata(o7_tdata), .o3_tlast(o7_tlast), .o3_tvalid(o7_tvalid), .o3_tready(o7_tready)
|
||||
);
|
||||
|
||||
endmodule // axi_demux4
|
||||
@@ -0,0 +1,169 @@
|
||||
//
|
||||
// Copyright 2012-2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
|
||||
// Block RAM AXI fifo
|
||||
|
||||
// Special case SIZE <= 5 uses a short fifo
|
||||
|
||||
module axi_fifo
|
||||
#(parameter WIDTH=32, SIZE=9)
|
||||
(input clk, input reset, input clear,
|
||||
input [WIDTH-1:0] i_tdata,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
output [WIDTH-1:0] o_tdata,
|
||||
output o_tvalid,
|
||||
input o_tready,
|
||||
|
||||
output reg [15:0] space,
|
||||
output reg [15:0] occupied);
|
||||
|
||||
generate
|
||||
if(SIZE<=5) begin
|
||||
wire [5:0] space_short, occupied_short;
|
||||
axi_fifo_short #(.WIDTH(WIDTH)) fifo_short
|
||||
(
|
||||
.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata(i_tdata), .i_tvalid(i_tvalid), .i_tready(i_tready),
|
||||
.o_tdata(o_tdata), .o_tvalid(o_tvalid), .o_tready(o_tready),
|
||||
.space(space_short), .occupied(occupied_short)
|
||||
);
|
||||
always @* space <= {10'b0, space_short};
|
||||
always @* occupied <= {10'b0, occupied_short};
|
||||
end
|
||||
else begin
|
||||
|
||||
wire write = i_tvalid & i_tready;
|
||||
wire read = o_tvalid & o_tready;
|
||||
wire full, empty;
|
||||
|
||||
assign i_tready = ~full;
|
||||
assign o_tvalid = ~empty;
|
||||
|
||||
// Read side states
|
||||
localparam EMPTY = 0;
|
||||
localparam PRE_READ = 1;
|
||||
localparam READING = 2;
|
||||
|
||||
reg [SIZE-1:0] wr_addr, rd_addr;
|
||||
reg [1:0] read_state;
|
||||
|
||||
reg empty_reg, full_reg;
|
||||
always @(posedge clk)
|
||||
if(reset)
|
||||
wr_addr <= 0;
|
||||
else if(clear)
|
||||
wr_addr <= 0;
|
||||
else if(write)
|
||||
wr_addr <= wr_addr + 1;
|
||||
|
||||
ram_2port #(.DWIDTH(WIDTH),.AWIDTH(SIZE))
|
||||
ram (.clka(clk),
|
||||
.ena(1'b1),
|
||||
.wea(write),
|
||||
.addra(wr_addr),
|
||||
.dia(i_tdata),
|
||||
.doa(),
|
||||
|
||||
.clkb(clk),
|
||||
.enb((read_state==PRE_READ)|read),
|
||||
.web(1'b0),
|
||||
.addrb(rd_addr),
|
||||
.dib({WIDTH{1'b1}}),
|
||||
.dob(o_tdata));
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset)
|
||||
begin
|
||||
read_state <= EMPTY;
|
||||
rd_addr <= 0;
|
||||
empty_reg <= 1;
|
||||
end
|
||||
else
|
||||
if(clear)
|
||||
begin
|
||||
read_state <= EMPTY;
|
||||
rd_addr <= 0;
|
||||
empty_reg <= 1;
|
||||
end
|
||||
else
|
||||
case(read_state)
|
||||
EMPTY :
|
||||
if(write)
|
||||
begin
|
||||
//rd_addr <= wr_addr;
|
||||
read_state <= PRE_READ;
|
||||
end
|
||||
PRE_READ :
|
||||
begin
|
||||
read_state <= READING;
|
||||
empty_reg <= 0;
|
||||
rd_addr <= rd_addr + 1;
|
||||
end
|
||||
|
||||
READING :
|
||||
if(read)
|
||||
if(rd_addr == wr_addr)
|
||||
begin
|
||||
empty_reg <= 1;
|
||||
if(write)
|
||||
read_state <= PRE_READ;
|
||||
else
|
||||
read_state <= EMPTY;
|
||||
end
|
||||
else
|
||||
rd_addr <= rd_addr + 1;
|
||||
endcase // case(read_state)
|
||||
|
||||
wire [SIZE-1:0] dont_write_past_me = rd_addr - 2;
|
||||
wire becoming_full = wr_addr == dont_write_past_me;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset)
|
||||
full_reg <= 0;
|
||||
else if(clear)
|
||||
full_reg <= 0;
|
||||
else if(read & ~write)
|
||||
full_reg <= 0;
|
||||
//else if(write & ~read & (wr_addr == (rd_addr-3)))
|
||||
else if(write & ~read & becoming_full)
|
||||
full_reg <= 1;
|
||||
|
||||
//assign empty = (read_state != READING);
|
||||
assign empty = empty_reg;
|
||||
|
||||
// assign full = ((rd_addr - 1) == wr_addr);
|
||||
assign full = full_reg;
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// space and occupied are for diagnostics only
|
||||
// not guaranteed exact
|
||||
|
||||
localparam NUMLINES = (1<<SIZE);
|
||||
always @(posedge clk)
|
||||
if(reset)
|
||||
space <= NUMLINES;
|
||||
else if(clear)
|
||||
space <= NUMLINES;
|
||||
else if(read & ~write)
|
||||
space <= space + 16'b1;
|
||||
else if(write & ~read)
|
||||
space <= space - 16'b1;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset)
|
||||
occupied <= 16'b0;
|
||||
else if(clear)
|
||||
occupied <= 16'b0;
|
||||
else if(read & ~write)
|
||||
occupied <= occupied - 16'b1;
|
||||
else if(write & ~read)
|
||||
occupied <= occupied + 16'b1;
|
||||
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule // fifo_long
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
module axi_fifo32_to_fifo64
|
||||
(input clk, input reset, input clear,
|
||||
input [31:0] i_tdata, input [1:0] i_tuser, input i_tlast, input i_tvalid, output i_tready,
|
||||
output [63:0] o_tdata, output [2:0] o_tuser, output o_tlast, output o_tvalid, input o_tready
|
||||
);
|
||||
|
||||
reg [31:0] holding;
|
||||
|
||||
reg state;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
state <= 0;
|
||||
else
|
||||
if(i_tvalid & i_tready)
|
||||
case(state)
|
||||
0 : if(~i_tlast) state <= 1'b1;
|
||||
1 : state <= 1'b0;
|
||||
default : state <= 1'b0;
|
||||
endcase // case (state)
|
||||
|
||||
always @(posedge clk)
|
||||
if(i_tvalid & i_tready)
|
||||
holding <= i_tdata;
|
||||
|
||||
assign i_tready = (state == 0 && !i_tlast)? 1'b1 : o_tready;
|
||||
assign o_tvalid = (state == 0 && !i_tlast)? 1'b0 : i_tvalid;
|
||||
|
||||
assign o_tdata = (state == 0) ? {i_tdata, 32'h0} : { holding, i_tdata };
|
||||
assign o_tlast = i_tlast;
|
||||
|
||||
wire [2:0] occ_in = (i_tuser == 0) ? 3'd4 : {1'b0, i_tuser};
|
||||
wire [2:0] occ_out = (state == 0) ? occ_in : (occ_in + 3'd4);
|
||||
|
||||
assign o_tuser = ~o_tlast ? 3'd0 : occ_out;
|
||||
|
||||
endmodule // axi_fifo32_to_fifo64
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
module axi_fifo64_to_fifo32
|
||||
(input clk, input reset, input clear,
|
||||
input [63:0] i_tdata, input [2:0] i_tuser, input i_tlast, input i_tvalid, output i_tready,
|
||||
output [31:0] o_tdata, output [1:0] o_tuser, output o_tlast, output o_tvalid, input o_tready
|
||||
);
|
||||
|
||||
wire short_last = i_tlast & ((i_tuser == 3'd1) | (i_tuser == 3'd2) | (i_tuser == 3'd3) | (i_tuser == 3'd4));
|
||||
|
||||
reg state;
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
state <= 1'b0;
|
||||
else
|
||||
if(i_tvalid & o_tready)
|
||||
case(state)
|
||||
1'b0 :
|
||||
if(~short_last)
|
||||
state <= 1'b1;
|
||||
1'b1 :
|
||||
state <= 1'b0;
|
||||
endcase // case (state)
|
||||
|
||||
assign o_tdata = (state == 0) ? i_tdata[63:32] : i_tdata[31:0];
|
||||
assign o_tuser = o_tlast ? i_tuser[1:0] : 2'd0;
|
||||
assign o_tlast = i_tlast & ((state == 1'b1) | short_last);
|
||||
|
||||
assign o_tvalid = i_tvalid;
|
||||
assign i_tready = o_tready & ((state == 1'b1) | short_last);
|
||||
|
||||
endmodule // axi_fifo64_to_fifo32
|
||||
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
// Special case SIZE <= 5 uses a short fifo
|
||||
|
||||
module axi_fifo_2clk
|
||||
#(parameter WIDTH=69, SIZE=9)
|
||||
(input reset,
|
||||
input i_aclk,
|
||||
input [WIDTH-1:0] i_tdata,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
input o_aclk,
|
||||
output [WIDTH-1:0] o_tdata,
|
||||
output o_tvalid,
|
||||
input o_tready);
|
||||
|
||||
wire write, read, empty, full;
|
||||
assign i_tready = ~full;
|
||||
assign write = i_tvalid & i_tready;
|
||||
|
||||
wire [71:0] tdata_int;
|
||||
wire tvalid_int, tready_int;
|
||||
assign tvalid_int = ~empty;
|
||||
assign read = tvalid_int & tready_int;
|
||||
|
||||
wire [71:0] wr_data;
|
||||
assign wr_data[WIDTH-1:0] = i_tdata;
|
||||
wire [71:0] rd_data;
|
||||
assign tdata_int = rd_data[WIDTH-1:0];
|
||||
|
||||
generate
|
||||
if(WIDTH<72) begin
|
||||
assign wr_data[71:WIDTH] = 0;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
generate
|
||||
if(SIZE<=5)
|
||||
fifo_short_2clk fifo_short_2clk
|
||||
(.rst(reset),
|
||||
.wr_clk(i_aclk),
|
||||
.din(wr_data), // input [71 : 0] din
|
||||
.wr_en(write), // input wr_en
|
||||
.full(full), // output full
|
||||
.wr_data_count(), // output [9 : 0] wr_data_count
|
||||
|
||||
.rd_clk(o_aclk), // input rd_clk
|
||||
.dout(rd_data), // output [71 : 0] dout
|
||||
.rd_en(read), // input rd_en
|
||||
.empty(empty), // output empty
|
||||
.rd_data_count() // output [9 : 0] rd_data_count
|
||||
);
|
||||
else
|
||||
fifo_4k_2clk fifo_4k_2clk
|
||||
(.rst(reset),
|
||||
.wr_clk(i_aclk),
|
||||
.din(wr_data), // input [71 : 0] din
|
||||
.wr_en(write), // input wr_en
|
||||
.full(full), // output full
|
||||
.wr_data_count(), // output [9 : 0] wr_data_count
|
||||
|
||||
.rd_clk(o_aclk), // input rd_clk
|
||||
.dout(rd_data), // output [71 : 0] dout
|
||||
.rd_en(read), // input rd_en
|
||||
.empty(empty), // output empty
|
||||
.rd_data_count() // output [9 : 0] rd_data_count
|
||||
);
|
||||
endgenerate
|
||||
|
||||
generate
|
||||
if(SIZE>9)
|
||||
axi_fifo #(.WIDTH(WIDTH), .SIZE(SIZE)) fifo_1clk
|
||||
(.clk(o_aclk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata(tdata_int), .i_tvalid(tvalid_int), .i_tready(tready_int),
|
||||
.o_tdata(o_tdata), .o_tvalid(o_tvalid), .o_tready(o_tready),
|
||||
.space(), .occupied());
|
||||
else
|
||||
begin
|
||||
assign o_tdata = tdata_int;
|
||||
assign o_tvalid = tvalid_int;
|
||||
assign tready_int = o_tready;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule // axi_fifo_2clk
|
||||
@@ -0,0 +1,114 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module axi_fifo_32_64_tb();
|
||||
|
||||
reg clk = 0;
|
||||
reg reset = 1;
|
||||
|
||||
always #10 clk = ~clk;
|
||||
|
||||
initial $dumpfile("axi_fifo_32_64_tb.vcd");
|
||||
initial $dumpvars(0,axi_fifo_32_64_tb);
|
||||
|
||||
task send_packet;
|
||||
input [63:0] data_start;
|
||||
input [2:0] user;
|
||||
input [31:0] len;
|
||||
|
||||
begin
|
||||
@(posedge clk);
|
||||
{i_tuser, i_tlast, i_tdata} <= { 3'd0, 1'b0, data_start };
|
||||
repeat(len-1)
|
||||
begin
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
i_tdata <= i_tdata + 64'h0000_0002_0000_0002;
|
||||
end
|
||||
i_tuser <= user;
|
||||
i_tlast <= 1;
|
||||
@(posedge clk);
|
||||
i_tvalid <= 1'b0;
|
||||
@(posedge clk);
|
||||
end
|
||||
endtask // send_packet
|
||||
|
||||
initial
|
||||
begin
|
||||
#1000 reset = 0;
|
||||
#200000;
|
||||
$finish;
|
||||
end
|
||||
|
||||
reg [63:0] i_tdata;
|
||||
reg [2:0] i_tuser;
|
||||
reg i_tlast;
|
||||
reg i_tvalid;
|
||||
wire i_tready;
|
||||
|
||||
wire [63:0] i_tdata_int;
|
||||
wire [2:0] i_tuser_int;
|
||||
wire i_tlast_int, i_tvalid_int, i_tready_int;
|
||||
|
||||
wire [63:0] o_tdata;
|
||||
wire [31:0] o_tdata_int, o_tdata_int2;
|
||||
wire [2:0] o_tuser;
|
||||
wire [1:0] o_tuser_int, o_tuser_int2;
|
||||
wire o_tlast, o_tlast_int, o_tvalid, o_tvalid_int, o_tready, o_tready_int;
|
||||
wire o_tlast_int2, o_tvalid_int2, o_tready_int2;
|
||||
|
||||
localparam RPT_COUNT = 16;
|
||||
|
||||
initial
|
||||
begin
|
||||
i_tvalid <= 0;
|
||||
|
||||
while(reset)
|
||||
@(posedge clk);
|
||||
@(posedge clk);
|
||||
|
||||
send_packet(64'hA0000000_A0000001, 3'd7, 4);
|
||||
@(posedge clk);
|
||||
end // initial begin
|
||||
|
||||
axi_fifo #(.WIDTH(68), .SIZE(10)) fifo
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({i_tlast,i_tuser,i_tdata}), .i_tvalid(i_tvalid), .i_tready(i_tready),
|
||||
.o_tdata({i_tlast_int,i_tuser_int,i_tdata_int}), .o_tvalid(i_tvalid_int), .o_tready(i_tready_int));
|
||||
|
||||
axi_fifo64_to_fifo32 dut
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata(i_tdata_int), .i_tuser(i_tuser_int), .i_tlast(i_tlast_int), .i_tvalid(i_tvalid_int), .i_tready(i_tready_int),
|
||||
.o_tdata(o_tdata_int), .o_tuser(o_tuser_int), .o_tlast(o_tlast_int), .o_tvalid(o_tvalid_int), .o_tready(o_tready_int));
|
||||
|
||||
/*
|
||||
axi_fifo #(.WIDTH(35), .SIZE(10)) fifo_middle
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({o_tlast_int,o_tuser_int,o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int),
|
||||
.o_tdata({o_tlast_int2,o_tuser_int2,o_tdata_int2}), .o_tvalid(o_tvalid_int2), .o_tready(o_tready_int2));
|
||||
*/
|
||||
assign o_tdata_int2 = o_tdata_int;
|
||||
assign o_tlast_int2 = o_tlast_int;
|
||||
assign o_tuser_int2 = o_tuser_int;
|
||||
assign o_tvalid_int2 = o_tvalid_int;
|
||||
assign o_tready_int = o_tready_int2;
|
||||
|
||||
axi_fifo32_to_fifo64 dut2
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata(o_tdata_int2), .i_tuser(o_tuser_int2), .i_tlast(o_tlast_int2), .i_tvalid(o_tvalid_int2), .i_tready(o_tready_int2),
|
||||
.o_tdata(o_tdata), .o_tuser(o_tuser), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready));
|
||||
|
||||
assign o_tready = 1'b1;
|
||||
|
||||
always @(posedge clk)
|
||||
if(i_tvalid & i_tready)
|
||||
$display("IN: TUSER %x\tTLAST %x\tTDATA %x", i_tuser, i_tlast, i_tdata);
|
||||
|
||||
always @(posedge clk)
|
||||
if(o_tvalid_int & o_tready_int)
|
||||
$display("\t\t\t\t\t\tMIDDLE: TUSER %x\tTLAST %x\tTDATA %x", o_tuser_int, o_tlast_int, o_tdata_int);
|
||||
|
||||
always @(posedge clk)
|
||||
if(o_tvalid & o_tready)
|
||||
$display("\t\t\t\t\t\t\t\t\t\t\tOUT: TUSER %x\tTLAST %x\tTDATA %x", o_tuser, o_tlast, o_tdata);
|
||||
|
||||
endmodule // axi_fifo_32_64_tb
|
||||
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// 32 word FIFO with AXI4-STREAM interface.
|
||||
//
|
||||
// NOTE: This module uses the SRLC32E primitive explicitly and as such
|
||||
// can only be used with Xilinx technology of the VIRTEX-6/SPARTAN-6/SIERIES-7 or newer.
|
||||
//
|
||||
|
||||
module axi_fifo_short
|
||||
#(parameter WIDTH=32)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
input [WIDTH-1:0] i_tdata,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
output [WIDTH-1:0] o_tdata,
|
||||
output o_tvalid,
|
||||
input o_tready,
|
||||
|
||||
output reg [5:0] space,
|
||||
output reg [5:0] occupied
|
||||
);
|
||||
|
||||
reg full, empty;
|
||||
wire write = i_tvalid & i_tready;
|
||||
wire read = o_tready & o_tvalid;
|
||||
|
||||
assign i_tready = ~full;
|
||||
assign o_tvalid = ~empty;
|
||||
|
||||
reg [4:0] a;
|
||||
genvar i;
|
||||
|
||||
generate
|
||||
for (i=0;i<WIDTH;i=i+1)
|
||||
begin : gen_srlc32e
|
||||
SRLC32E
|
||||
srlc32e(.Q(o_tdata[i]), .Q31(),
|
||||
.A(a), //.A0(a[0]),.A1(a[1]),.A2(a[2]),.A3(a[3]),.A4(a[4]),
|
||||
.CE(write),.CLK(clk),.D(i_tdata[i]));
|
||||
end
|
||||
endgenerate
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset)
|
||||
begin
|
||||
a <= 0;
|
||||
empty <= 1;
|
||||
full <= 0;
|
||||
end
|
||||
else if(clear)
|
||||
begin
|
||||
a <= 0;
|
||||
empty <= 1;
|
||||
full<= 0;
|
||||
end
|
||||
else if(read & ~write)
|
||||
begin
|
||||
full <= 0;
|
||||
if(a==0)
|
||||
empty <= 1;
|
||||
else
|
||||
a <= a - 1;
|
||||
end
|
||||
else if(write & ~read)
|
||||
begin
|
||||
empty <= 0;
|
||||
if(~empty)
|
||||
a <= a + 1;
|
||||
if(a == 30)
|
||||
full <= 1;
|
||||
end
|
||||
|
||||
// NOTE will fail if you write into a full fifo or read from an empty one
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// space and occupied are used for diagnostics, not
|
||||
// guaranteed correct
|
||||
|
||||
//assign space = full ? 0 : empty ? 16 : 15-a;
|
||||
//assign occupied = empty ? 0 : full ? 16 : a+1;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset)
|
||||
space <= 6'd32;
|
||||
else if(clear)
|
||||
space <= 6'd32;
|
||||
else if(read & ~write)
|
||||
space <= space + 6'd1;
|
||||
else if(write & ~read)
|
||||
space <= space - 6'd1;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset)
|
||||
occupied <= 6'd0;
|
||||
else if(clear)
|
||||
occupied <= 6'd0;
|
||||
else if(read & ~write)
|
||||
occupied <= occupied - 6'd1;
|
||||
else if(write & ~read)
|
||||
occupied <= occupied + 6'd1;
|
||||
|
||||
endmodule // axi_fifo_short
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
//
|
||||
// Copyright 2012-2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
module axi_fifo_tb();
|
||||
|
||||
reg clk, reset;
|
||||
reg read_flag, write_flag;
|
||||
|
||||
reg error;
|
||||
reg [7:0] i_tdata, o_tdata_ref;
|
||||
wire [7:0] o_tdata;
|
||||
reg i_tvalid, o_tready;
|
||||
wire o_tvalid, i_tready;
|
||||
wire [15:0] space, occupied;
|
||||
|
||||
always
|
||||
#100 clk = ~clk;
|
||||
|
||||
initial clk = 0;
|
||||
|
||||
|
||||
axi_fifo
|
||||
#(
|
||||
.WIDTH(8),
|
||||
.SIZE(8)
|
||||
)
|
||||
dut
|
||||
(.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(1'b0),
|
||||
.i_tdata(i_tdata),
|
||||
.i_tvalid(i_tvalid),
|
||||
.i_tready(i_tready),
|
||||
.o_tdata(o_tdata),
|
||||
.o_tvalid(o_tvalid),
|
||||
.o_tready(o_tready),
|
||||
.space(space),
|
||||
.occupied(occupied)
|
||||
);
|
||||
|
||||
|
||||
task write;
|
||||
begin
|
||||
write_flag <= 1;
|
||||
i_tvalid <= 1'b1;
|
||||
#1;
|
||||
while (i_tready != 1'b1)
|
||||
@(posedge clk);
|
||||
#1;
|
||||
@(posedge clk);
|
||||
write_flag <= 0;
|
||||
i_tvalid <= 1'b0;
|
||||
i_tdata <= i_tdata + 8'h1;
|
||||
end
|
||||
endtask // write
|
||||
|
||||
task read;
|
||||
begin
|
||||
read_flag <= 1;
|
||||
o_tready <= 1'b1;
|
||||
#1;
|
||||
while (o_tvalid != 1'b1)
|
||||
@(posedge clk);
|
||||
#1;
|
||||
@(posedge clk);
|
||||
read_flag <= 0;
|
||||
o_tready <= 1'b0;
|
||||
if (o_tdata_ref != o_tdata) begin
|
||||
$display("ERROR: Expected %d, got %d, at time %d",o_tdata_ref,o_tdata,$time);
|
||||
error <= 1'b1;
|
||||
end else
|
||||
error <= 1'b0;
|
||||
o_tdata_ref = o_tdata_ref + 8'h1;
|
||||
end
|
||||
endtask // read
|
||||
|
||||
initial
|
||||
begin
|
||||
reset <= 1'b0;
|
||||
error <= 1'b0;
|
||||
i_tdata <= 8'b00;
|
||||
o_tdata_ref <= 8'b00;
|
||||
i_tvalid <= 1'b0;
|
||||
o_tready <= 1'b0;
|
||||
read_flag <= 0;
|
||||
write_flag <= 0;
|
||||
|
||||
repeat(10) @(posedge clk);
|
||||
reset <= 1'b1;
|
||||
repeat(10) @(posedge clk);
|
||||
reset <= 1'b0;
|
||||
@(posedge clk);
|
||||
@(negedge clk);
|
||||
|
||||
// FIFO Should be empty now, check avail space
|
||||
if (space != 16'd256)
|
||||
begin $display("ERROR: FIFO is empty, space should read 256 not %d at time %d",space,$time); error <= 1; end
|
||||
if (occupied != 16'd0)
|
||||
begin $display("ERROR: FIFO is empty, occupied should read 0 not %d at time %d",occupied,$time); error <= 1; end
|
||||
if (o_tvalid == 1'b1)
|
||||
begin $display("ERROR: FIFO is empty, o_tvalid should be 0 at time %d",$time); error <= 1; end
|
||||
@(posedge clk);
|
||||
// Push 1 item onto FIFO, check fullness updates accordingly
|
||||
write();
|
||||
@(posedge clk);
|
||||
@(negedge clk);
|
||||
if (space != 16'd255)
|
||||
begin $display("ERROR: FIFO space should read 255 not %d at time %d",space,$time); error <= 1; end
|
||||
if (occupied != 16'd1)
|
||||
begin $display("ERROR: FIFO occupied should read 1 not %d at time %d",occupied,$time); error <= 1; end
|
||||
if (o_tvalid == 1'b0)
|
||||
begin $display("ERROR: FIFO is not empty, o_tvalid should be 1 at time %d",$time); error <= 1; end
|
||||
// Pop FIFO once, check it goes back empty OK.
|
||||
@(posedge clk);
|
||||
read();
|
||||
@(posedge clk);
|
||||
@(negedge clk);
|
||||
if (space != 16'd256)
|
||||
begin $display("ERROR: FIFO is empty, space should read 256 not %d at time %d",space,$time); error <= 1; end
|
||||
if (occupied != 16'd0)
|
||||
begin $display("ERROR: FIFO is empty, occupied should read 0 not %d at time %d",occupied,$time); error <= 1; end
|
||||
if (o_tvalid == 1'b1)
|
||||
begin $display("ERROR: FIFO is empty, o_tvalid should be 0 at time %d",$time); error <= 1; end
|
||||
// Push FIFO 255 times and see if it goes full incorrectly
|
||||
repeat(255) begin
|
||||
@(posedge clk);
|
||||
write();
|
||||
end
|
||||
@(posedge clk);
|
||||
@(negedge clk);
|
||||
if (space != 16'd1)
|
||||
begin $display("ERROR: FIFO is nearly full, space should read 1 not %d at time %d",space,$time); error <= 1; end
|
||||
if (occupied != 16'd255)
|
||||
begin $display("ERROR: FIFO is nearly full, occupied should read 255 not %d at time %d",occupied,$time); error <= 1; end
|
||||
if (o_tvalid == 1'b0)
|
||||
begin $display("ERROR: FIFO is nearly full, o_tvalid should be 1 at time %d",$time); error <= 1; end
|
||||
if (i_tready == 1'b0)
|
||||
begin $display("ERROR: FIFO is nearly full, i_tready should be 1 at time %d",$time); error <= 1; end
|
||||
// Push FIFO one more time, now it should be full
|
||||
@(posedge clk);
|
||||
write();
|
||||
@(posedge clk);
|
||||
@(negedge clk);
|
||||
if (space != 16'd0)
|
||||
begin $display("ERROR: FIFO is full, space should read 0 not %d at time %d",space,$time); error <= 1; end
|
||||
if (occupied != 16'd256)
|
||||
begin $display("ERROR: FIFO is full, occupied should read 256 not %d at time %d",occupied,$time); error <= 1; end
|
||||
if (o_tvalid == 1'b0)
|
||||
begin $display("ERROR: FIFO is full, o_tvalid should be 1 at time %d",$time); error <= 1; end
|
||||
if (i_tready == 1'b1)
|
||||
begin $display("ERROR: FIFO is full, i_tready should be 0 at time %d",$time); error <= 1; end
|
||||
// POP FIFO once, check it went nonfull.
|
||||
@(posedge clk);
|
||||
read();
|
||||
@(posedge clk);
|
||||
@(negedge clk);
|
||||
if (space != 16'd1)
|
||||
begin $display("ERROR: FIFO is nearly full, space should read 1 not %d at time %d",space,$time); error <= 1; end
|
||||
if (occupied != 16'd255)
|
||||
begin $display("ERROR: FIFO is nearly full, occupied should read 255 not %d at time %d",occupied,$time); error <= 1; end
|
||||
if (o_tvalid == 1'b0)
|
||||
begin $display("ERROR: FIFO is nearly full, o_tvalid should be 1 at time %d",$time); error <= 1; end
|
||||
if (i_tready == 1'b0)
|
||||
begin $display("ERROR: FIFO is nearly full, i_tready should be 1 at time %d",$time); error <= 1; end
|
||||
// Take FIFO to empty state
|
||||
repeat(255) begin
|
||||
@(posedge clk);
|
||||
read();
|
||||
end
|
||||
@(posedge clk);
|
||||
@(negedge clk);
|
||||
if (space != 16'd256)
|
||||
begin $display("ERROR: FIFO is empty, space should read 256 not %d at time %d",space,$time); error <= 1; end
|
||||
if (occupied != 16'd0)
|
||||
begin $display("ERROR: FIFO is empty, occupied should read 0 not %d at time %d",occupied,$time); error <= 1; end
|
||||
if (o_tvalid == 1'b1)
|
||||
begin $display("ERROR: FIFO is empty, o_tvalid should be 0 at time %d",$time); error <= 1; end
|
||||
// Push 1 item onto FIFO
|
||||
@(posedge clk);
|
||||
write();
|
||||
@(posedge clk);
|
||||
// Now write twice as fast as we read, and write 256 times, which should leave, 129 elements in FIFO.
|
||||
fork
|
||||
repeat(256) begin
|
||||
write();
|
||||
@(posedge clk);
|
||||
end
|
||||
repeat(128) begin
|
||||
read();
|
||||
@(posedge clk);
|
||||
@(posedge clk);
|
||||
end
|
||||
join
|
||||
@(posedge clk);
|
||||
if (space != 16'd127)
|
||||
begin $display("ERROR: FIFO space should read 127 not %d at time %d",space,$time); error <= 1; end
|
||||
if (occupied != 16'd129)
|
||||
begin $display("ERROR: FIFO occupied should read 129 not %d at time %d",occupied,$time); error <= 1; end
|
||||
|
||||
|
||||
|
||||
//
|
||||
// END
|
||||
//
|
||||
repeat(10) @(posedge clk);
|
||||
$finish;
|
||||
end // initial begin
|
||||
|
||||
endmodule // axi_fifo_tb
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
|
||||
//
|
||||
// axi_loopback.v
|
||||
//
|
||||
// Loopback all data assuming it's in CHDR format, and swap SRC/DST in the SID in the process
|
||||
// thus reflecting it back to it's origin...in theory!
|
||||
//
|
||||
|
||||
module axi_loopback
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
// Input AXIS
|
||||
input [WIDTH-1:0] i_tdata,
|
||||
input i_tlast,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
// Output AXIS
|
||||
output [WIDTH-1:0] o_tdata,
|
||||
output o_tlast,
|
||||
output o_tvalid,
|
||||
input o_tready
|
||||
);
|
||||
|
||||
|
||||
localparam WIDTH=64;
|
||||
|
||||
wire [WIDTH-1:0] fifoin_tdata,fifoout_tdata,dmux_tdata;
|
||||
wire fifoin_tlast,dmux_tlast;
|
||||
wire fifoin_tvalid,dmux_tvalid;
|
||||
wire fifoin_tready,dmux_tready;
|
||||
|
||||
// Since most real endpoints go via Demux4 place one in here to look for bugs.
|
||||
axi_demux4 #(.ACTIVE_CHAN(4'b0001), .WIDTH(WIDTH)) demux
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.header(), .dest(2'b00),
|
||||
.i_tdata(i_tdata), .i_tlast(i_tlast), .i_tvalid(i_tvalid), .i_tready(i_tready),
|
||||
.o0_tdata(dmux_tdata), .o0_tlast(dmux_tlast), .o0_tvalid(dmux_tvalid), .o0_tready(dmux_tready),
|
||||
.o1_tdata(), .o1_tlast(), .o1_tvalid(), .o1_tready(1'b1),
|
||||
.o2_tdata(), .o2_tlast(), .o2_tvalid(), .o2_tready(1'b1),
|
||||
.o3_tdata(), .o3_tlast(), .o3_tvalid(), .o3_tready(1'b1));
|
||||
|
||||
axi_fifo_short #(.WIDTH(WIDTH+1)) axi_fifo_short1
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({dmux_tlast,dmux_tdata}), .i_tvalid(dmux_tvalid), .i_tready(dmux_tready),
|
||||
.o_tdata({fifoin_tlast,fifoin_tdata}), .o_tvalid(fifoin_tvalid), .o_tready(fifoin_tready),
|
||||
.space(), .occupied());
|
||||
|
||||
reg header;
|
||||
always @(posedge clk) begin
|
||||
if(reset) begin
|
||||
header <= 1'b1;
|
||||
end else if (header) begin
|
||||
if(fifoin_tvalid & fifoin_tready & ~fifoin_tlast) header <= 1'b0;
|
||||
end else begin
|
||||
if(fifoin_tvalid & fifoin_tready & fifoin_tlast) header <= 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
assign fifoout_tdata = header ?
|
||||
{fifoin_tdata[63:32] ,fifoin_tdata[15:0],fifoin_tdata[31:16]} :
|
||||
fifoin_tdata;
|
||||
|
||||
axi_fifo_short #(.WIDTH(WIDTH+1)) axi_fifo_short2
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({fifoin_tlast,fifoout_tdata}), .i_tvalid(fifoin_tvalid), .i_tready(fifoin_tready),
|
||||
.o_tdata({o_tlast,o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
|
||||
.space(), .occupied());
|
||||
|
||||
endmodule // axi_loopback
|
||||
@@ -0,0 +1,112 @@
|
||||
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
// axi_mux -- takes 4 64-bit AXI stream, merges them to 1 output channel
|
||||
// Round-robin if PRIO=0, priority if PRIO=1 (lower number ports get priority)
|
||||
// Bubble cycles are inserted after each packet in PRIO mode, or on wraparound in Round Robin mode
|
||||
|
||||
module axi_mux4
|
||||
#(parameter PRIO=0,
|
||||
parameter WIDTH=64,
|
||||
parameter BUFFER=0)
|
||||
(input clk, input reset, input clear,
|
||||
input [WIDTH-1:0] i0_tdata, input i0_tlast, input i0_tvalid, output i0_tready,
|
||||
input [WIDTH-1:0] i1_tdata, input i1_tlast, input i1_tvalid, output i1_tready,
|
||||
input [WIDTH-1:0] i2_tdata, input i2_tlast, input i2_tvalid, output i2_tready,
|
||||
input [WIDTH-1:0] i3_tdata, input i3_tlast, input i3_tvalid, output i3_tready,
|
||||
output [WIDTH-1:0] o_tdata, output o_tlast, output o_tvalid, input o_tready);
|
||||
|
||||
wire [WIDTH-1:0] o_tdata_int;
|
||||
wire o_tlast_int, o_tvalid_int, o_tready_int;
|
||||
|
||||
reg [3:0] mx_state;
|
||||
localparam MX_IDLE = 4'b0000;
|
||||
localparam MX_0 = 4'b0001;
|
||||
localparam MX_1 = 4'b0010;
|
||||
localparam MX_2 = 4'b0100;
|
||||
localparam MX_3 = 4'b1000;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
mx_state <= MX_IDLE;
|
||||
else
|
||||
case (mx_state)
|
||||
MX_IDLE :
|
||||
if(i0_tvalid)
|
||||
mx_state <= MX_0;
|
||||
else if(i1_tvalid)
|
||||
mx_state <= MX_1;
|
||||
else if(i2_tvalid)
|
||||
mx_state <= MX_2;
|
||||
else if(i3_tvalid)
|
||||
mx_state <= MX_3;
|
||||
|
||||
MX_0 :
|
||||
if(o_tready_int & o_tvalid_int & o_tlast_int)
|
||||
if(PRIO)
|
||||
mx_state <= MX_IDLE;
|
||||
else if(i1_tvalid)
|
||||
mx_state <= MX_1;
|
||||
else if(i2_tvalid)
|
||||
mx_state <= MX_2;
|
||||
else if(i3_tvalid)
|
||||
mx_state <= MX_3;
|
||||
else
|
||||
mx_state <= MX_IDLE;
|
||||
|
||||
MX_1 :
|
||||
if(o_tready_int & o_tvalid_int & o_tlast_int)
|
||||
if(PRIO)
|
||||
mx_state <= MX_IDLE;
|
||||
else if(i2_tvalid)
|
||||
mx_state <= MX_2;
|
||||
else if(i3_tvalid)
|
||||
mx_state <= MX_3;
|
||||
else
|
||||
mx_state <= MX_IDLE;
|
||||
|
||||
MX_2 :
|
||||
if(o_tready_int & o_tvalid_int & o_tlast_int)
|
||||
if(PRIO)
|
||||
mx_state <= MX_IDLE;
|
||||
else if(i3_tvalid)
|
||||
mx_state <= MX_3;
|
||||
else
|
||||
mx_state <= MX_IDLE;
|
||||
|
||||
MX_3 :
|
||||
if(o_tready_int & o_tvalid_int & o_tlast_int)
|
||||
if(PRIO)
|
||||
mx_state <= MX_IDLE;
|
||||
else
|
||||
mx_state <= MX_IDLE;
|
||||
|
||||
default :
|
||||
mx_state <= MX_IDLE;
|
||||
endcase // case (mx_state)
|
||||
|
||||
assign {i3_tready, i2_tready, i1_tready, i0_tready} = mx_state & {4{o_tready_int}};
|
||||
|
||||
assign o_tvalid_int = |(mx_state & ({i3_tvalid, i2_tvalid, i1_tvalid, i0_tvalid}));
|
||||
|
||||
assign {o_tlast_int, o_tdata_int} = mx_state[3] ? {i3_tlast, i3_tdata} :
|
||||
mx_state[2] ? {i2_tlast, i2_tdata} :
|
||||
mx_state[1] ? {i1_tlast, i1_tdata} :
|
||||
{i0_tlast, i0_tdata};
|
||||
|
||||
generate
|
||||
if(BUFFER == 0)
|
||||
begin
|
||||
assign o_tdata = o_tdata_int;
|
||||
assign o_tlast = o_tlast_int;
|
||||
assign o_tvalid = o_tvalid_int;
|
||||
assign o_tready_int = o_tready;
|
||||
end
|
||||
else
|
||||
axi_fifo_short #(.WIDTH(WIDTH+1)) axi_fifo_short
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({o_tlast_int,o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int),
|
||||
.o_tdata({o_tlast,o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
|
||||
.space(), .occupied());
|
||||
endgenerate
|
||||
|
||||
endmodule // axi__mux4
|
||||
@@ -0,0 +1,56 @@
|
||||
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
// axi_mux -- takes 8 64-bit AXI stream, merges them to 1 output channel
|
||||
// Round-robin if PRIO=0, priority if PRIO=1 (lower number ports get priority)
|
||||
// Bubble cycles are inserted after each packet in PRIO mode, or on wraparound in Round Robin mode
|
||||
|
||||
module axi_mux8 #(
|
||||
parameter PRIO=0,
|
||||
parameter WIDTH=64,
|
||||
parameter BUFFER=0
|
||||
) (
|
||||
input clk, input reset, input clear,
|
||||
input [WIDTH-1:0] i0_tdata, input i0_tlast, input i0_tvalid, output i0_tready,
|
||||
input [WIDTH-1:0] i1_tdata, input i1_tlast, input i1_tvalid, output i1_tready,
|
||||
input [WIDTH-1:0] i2_tdata, input i2_tlast, input i2_tvalid, output i2_tready,
|
||||
input [WIDTH-1:0] i3_tdata, input i3_tlast, input i3_tvalid, output i3_tready,
|
||||
input [WIDTH-1:0] i4_tdata, input i4_tlast, input i4_tvalid, output i4_tready,
|
||||
input [WIDTH-1:0] i5_tdata, input i5_tlast, input i5_tvalid, output i5_tready,
|
||||
input [WIDTH-1:0] i6_tdata, input i6_tlast, input i6_tvalid, output i6_tready,
|
||||
input [WIDTH-1:0] i7_tdata, input i7_tlast, input i7_tvalid, output i7_tready,
|
||||
output [WIDTH-1:0] o_tdata, output o_tlast, output o_tvalid, input o_tready
|
||||
);
|
||||
|
||||
wire [WIDTH-1:0] o_tdata_int0, o_tdata_int1;
|
||||
wire o_tlast_int0, o_tlast_int1;
|
||||
wire o_tvalid_int0, o_tvalid_int1;
|
||||
wire o_tready_int0, o_tready_int1;
|
||||
|
||||
axi_mux4 #(.PRIO(PRIO), .WIDTH(WIDTH), .BUFFER(0)) mux4_int0 (
|
||||
.clk(clk), .reset(reset), .clear(clear),
|
||||
.i0_tdata(i0_tdata), .i0_tlast(i0_tlast), .i0_tvalid(i0_tvalid), .i0_tready(i0_tready),
|
||||
.i1_tdata(i1_tdata), .i1_tlast(i1_tlast), .i1_tvalid(i1_tvalid), .i1_tready(i1_tready),
|
||||
.i2_tdata(i2_tdata), .i2_tlast(i2_tlast), .i2_tvalid(i2_tvalid), .i2_tready(i2_tready),
|
||||
.i3_tdata(i3_tdata), .i3_tlast(i3_tlast), .i3_tvalid(i3_tvalid), .i3_tready(i3_tready),
|
||||
.o_tdata(o_tdata_int0), .o_tlast(o_tlast_int0), .o_tvalid(o_tvalid_int0), .o_tready(o_tready_int0)
|
||||
);
|
||||
|
||||
axi_mux4 #(.PRIO(PRIO), .WIDTH(WIDTH), .BUFFER(0)) mux4_int1 (
|
||||
.clk(clk), .reset(reset), .clear(clear),
|
||||
.i0_tdata(i4_tdata), .i0_tlast(i4_tlast), .i0_tvalid(i4_tvalid), .i0_tready(i4_tready),
|
||||
.i1_tdata(i5_tdata), .i1_tlast(i5_tlast), .i1_tvalid(i5_tvalid), .i1_tready(i5_tready),
|
||||
.i2_tdata(i6_tdata), .i2_tlast(i6_tlast), .i2_tvalid(i6_tvalid), .i2_tready(i6_tready),
|
||||
.i3_tdata(i7_tdata), .i3_tlast(i7_tlast), .i3_tvalid(i7_tvalid), .i3_tready(i7_tready),
|
||||
.o_tdata(o_tdata_int1), .o_tlast(o_tlast_int1), .o_tvalid(o_tvalid_int1), .o_tready(o_tready_int1)
|
||||
);
|
||||
|
||||
axi_mux4 #(.PRIO(PRIO), .WIDTH(WIDTH), .BUFFER(BUFFER)) mux2 (
|
||||
.clk(clk), .reset(reset), .clear(clear),
|
||||
.i0_tdata(o_tdata_int0), .i0_tlast(o_tlast_int0), .i0_tvalid(o_tvalid_int0), .i0_tready(o_tready_int0),
|
||||
.i1_tdata(o_tdata_int1), .i1_tlast(o_tlast_int1), .i1_tvalid(o_tvalid_int1), .i1_tready(o_tready_int1),
|
||||
.i2_tdata(0), .i2_tlast(1'b0), .i2_tvalid(1'b0), .i2_tready(),
|
||||
.i3_tdata(0), .i3_tlast(1'b0), .i3_tvalid(1'b0), .i3_tready(),
|
||||
.o_tdata(o_tdata), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready)
|
||||
);
|
||||
|
||||
endmodule // axi_mux8
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
// Hold packets in fifo until they are complete. This prevents slowly-built packets
|
||||
// from clogging up the downstream. This block will hold up to 255 packets.
|
||||
// Will permanently block if a single packet is bigger than the fifo.
|
||||
// Will also drop any packet with an error signalled on the last line.
|
||||
// This is useful after an ethernet interface to drop packets with bad CRCs.
|
||||
|
||||
module axi_packet_gate
|
||||
#(parameter WIDTH=68,
|
||||
parameter SIZE=10)
|
||||
(input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
input [WIDTH-1:0] i_tdata,
|
||||
input i_tlast,
|
||||
input i_terror,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
output [WIDTH-1:0] o_tdata,
|
||||
output o_tlast,
|
||||
output o_tvalid,
|
||||
input o_tready
|
||||
);
|
||||
|
||||
reg [7:0] num_packets;
|
||||
reg dump;
|
||||
|
||||
wire o_tvalid_int, o_tready_int, i_tvalid_int, i_tready_int;
|
||||
|
||||
assign i_tvalid_int = (~dump & (num_packets != 8'hFF)) ? i_tvalid : 1'b0;
|
||||
assign i_tready = (~dump & (num_packets != 8'hFF)) ? i_tready_int : 1'b0;
|
||||
|
||||
assign o_tvalid = (num_packets != 8'h0) ? o_tvalid_int : 1'b0;
|
||||
assign o_tready_int = (num_packets != 8'h0) ? o_tready : 1'b0;
|
||||
|
||||
wire last_in = i_tvalid_int & i_tready_int & i_tlast;
|
||||
wire last_out = o_tvalid_int & o_tready_int & o_tlast;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
begin
|
||||
num_packets <= 8'd0;
|
||||
dump <= 1'b0;
|
||||
end
|
||||
else
|
||||
if(dump)
|
||||
if(num_packets != 8'd0)
|
||||
if(last_out)
|
||||
num_packets <= num_packets - 8'd1;
|
||||
else
|
||||
;
|
||||
else
|
||||
dump <= 1'b0;
|
||||
else
|
||||
if(last_in)
|
||||
if(i_terror)
|
||||
begin
|
||||
dump <= 1'b1;
|
||||
if(last_out)
|
||||
num_packets <= num_packets - 8'd1;
|
||||
end
|
||||
else if(~last_out)
|
||||
num_packets <= num_packets + 8'd1;
|
||||
else
|
||||
;
|
||||
else if(last_out)
|
||||
num_packets <= num_packets - 8'd1;
|
||||
|
||||
axi_fifo #(.SIZE(SIZE), .WIDTH(WIDTH+1)) axi_fifo
|
||||
(.clk(clk), .reset(reset), .clear(clear | (dump & (num_packets == 8'd0))),
|
||||
.i_tdata({i_tlast,i_tdata}), .i_tvalid(i_tvalid_int), .i_tready(i_tready_int),
|
||||
.o_tdata({o_tlast,o_tdata}), .o_tvalid(o_tvalid_int), .o_tready(o_tready_int));
|
||||
|
||||
endmodule // axi_packet_gate
|
||||
@@ -0,0 +1,106 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module axi_packet_gate_tb();
|
||||
|
||||
reg clk = 0;
|
||||
reg reset = 1;
|
||||
|
||||
always #10 clk = ~clk;
|
||||
|
||||
initial $dumpfile("axi_packet_gate_tb.vcd");
|
||||
initial $dumpvars(0,axi_packet_gate_tb);
|
||||
|
||||
task send_packet;
|
||||
input [63:0] data_start;
|
||||
input [2:0] user;
|
||||
input [31:0] len;
|
||||
input error;
|
||||
|
||||
begin
|
||||
// Send a packet
|
||||
@(posedge clk);
|
||||
{i_terror, i_tuser, i_tlast, i_tdata} <= { 1'b0, user, 1'b0, data_start };
|
||||
repeat(len-1)
|
||||
begin
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
i_tdata <= i_tdata + 1;
|
||||
end
|
||||
i_tlast <= 1;
|
||||
i_terror <= error;
|
||||
i_tdata <= i_tdata + 1;
|
||||
@(posedge clk);
|
||||
i_tvalid <= 1'b0;
|
||||
|
||||
@(posedge clk);
|
||||
end
|
||||
endtask // send_packet
|
||||
|
||||
|
||||
initial
|
||||
begin
|
||||
#1000 reset = 0;
|
||||
#200000;
|
||||
$finish;
|
||||
end
|
||||
|
||||
wire [63:0] o_tdata;
|
||||
reg [63:0] i_tdata;
|
||||
wire [2:0] o_tuser;
|
||||
reg [2:0] i_tuser;
|
||||
reg i_tlast;
|
||||
wire o_tlast;
|
||||
wire o_tvalid, i_tready;
|
||||
reg i_tvalid, o_tready;
|
||||
reg i_terror;
|
||||
|
||||
localparam RPT_COUNT = 16;
|
||||
|
||||
initial
|
||||
begin
|
||||
i_tvalid <= 0;
|
||||
o_tready <= 0;
|
||||
|
||||
while(reset)
|
||||
@(posedge clk);
|
||||
@(posedge clk);
|
||||
|
||||
send_packet(64'hA0,3'd0, 16, 0);
|
||||
send_packet(64'hB0,3'd0, 16, 0);
|
||||
o_tready <= 1;
|
||||
send_packet(64'hC0,3'd0, 16, 1);
|
||||
send_packet(64'hD0,3'd0, 16, 0);
|
||||
send_packet(64'hE0,3'd0, 16, 0);
|
||||
send_packet(64'hF0,3'd0, 16, 0);
|
||||
|
||||
@(posedge clk);
|
||||
|
||||
end // initial begin
|
||||
|
||||
wire i_terror_int, i_tlast_int, i_tready_int, i_tvalid_int;
|
||||
wire [2:0] i_tuser_int;
|
||||
wire [63:0] i_tdata_int;
|
||||
wire o_tlast_int, o_tready_int, o_tvalid_int;
|
||||
wire [2:0] o_tuser_int;
|
||||
wire [63:0] o_tdata_int;
|
||||
|
||||
axi_fifo #(.WIDTH(69), .SIZE(10)) fifo
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({i_terror,i_tlast,i_tuser,i_tdata}), .i_tvalid(i_tvalid), .i_tready(i_tready),
|
||||
.o_tdata({i_terror_int,i_tlast_int,i_tuser_int,i_tdata_int}), .o_tvalid(i_tvalid_int), .o_tready(i_tready_int));
|
||||
|
||||
axi_packet_gate #(.WIDTH(67), .SIZE(10)) dut
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({i_tuser_int,i_tdata_int}), .i_terror(i_terror_int), .i_tlast(i_tlast_int), .i_tvalid(i_tvalid_int), .i_tready(i_tready_int),
|
||||
.o_tdata({o_tuser_int,o_tdata_int}), .o_tlast(o_tlast_int), .o_tvalid(o_tvalid_int), .o_tready(o_tready_int));
|
||||
|
||||
axi_fifo #(.WIDTH(68), .SIZE(10)) fifo_out
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({o_tlast_int,o_tuser_int,o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int),
|
||||
.o_tdata({o_tlast,o_tuser,o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready));
|
||||
|
||||
always @(posedge clk)
|
||||
if(o_tvalid & o_tready)
|
||||
$display("TUSER %x\tTLAST %x\tTDATA %x",o_tuser,o_tlast, o_tdata);
|
||||
|
||||
endmodule // axi_packet_gate_tb
|
||||
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// This module is instantiated in parallel with a FIFO with AXI4-STREAM interfaces.
|
||||
// It tracks how many complete packets are contained within the FIFO, and also indicates
|
||||
// when the first word of a packet is presented on the FIFO outputs.
|
||||
//
|
||||
|
||||
|
||||
module monitor_axi_fifo
|
||||
#(
|
||||
parameter COUNT_BITS=8
|
||||
)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
// Monitored FIFO signals
|
||||
input i_tvalid,
|
||||
input i_tready,
|
||||
input i_tlast,
|
||||
input o_tvalid,
|
||||
input o_tready,
|
||||
input o_tlast,
|
||||
// FIFO status outputs
|
||||
output reg [COUNT_BITS-1:0] pkt_count, // Exact whole packet count
|
||||
output pkt_present // Flags any whole packets present
|
||||
|
||||
);
|
||||
|
||||
localparam WAIT_SOF = 0;
|
||||
localparam WAIT_EOF = 1;
|
||||
|
||||
|
||||
reg in_state, out_state;
|
||||
reg pause_tx;
|
||||
|
||||
//
|
||||
// Count packets arriving into large FIFO
|
||||
//
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
in_state <= WAIT_SOF;
|
||||
end else
|
||||
case(in_state)
|
||||
//
|
||||
// After RESET or the EOF of previous packet, the first cycle with
|
||||
// input valid and input ready asserted is the SOF.
|
||||
//
|
||||
WAIT_SOF:
|
||||
if (i_tvalid && i_tready) begin
|
||||
in_state <= WAIT_EOF;
|
||||
end else begin
|
||||
in_state <= WAIT_SOF;
|
||||
end
|
||||
//
|
||||
// EOF is signalled by the assertion i_tlast whilst input valid and ready are asserted.
|
||||
//
|
||||
WAIT_EOF:
|
||||
if (i_tlast && i_tvalid && i_tready) begin
|
||||
in_state <= WAIT_SOF;
|
||||
end else begin
|
||||
in_state <= WAIT_EOF;
|
||||
end
|
||||
endcase // case(in_state)
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Count packets leaving large FIFO
|
||||
//
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
out_state <= WAIT_SOF;
|
||||
end else
|
||||
case(out_state)
|
||||
//
|
||||
// After RESET or the EOF of previous packet, the first cycle with
|
||||
// output valid and output ready asserted is the SOF.
|
||||
//
|
||||
WAIT_SOF:
|
||||
if (o_tvalid && o_tready) begin
|
||||
out_state <= WAIT_EOF;
|
||||
end else begin
|
||||
out_state <= WAIT_SOF;
|
||||
end
|
||||
//
|
||||
// EOF is signalled by o_tlast asserted whilst output valid and ready asserted.
|
||||
//
|
||||
WAIT_EOF:
|
||||
if (o_tlast && o_tvalid && o_tready) begin
|
||||
out_state <= WAIT_SOF;
|
||||
end else begin
|
||||
out_state <= WAIT_EOF;
|
||||
end
|
||||
endcase // case(in_state)
|
||||
|
||||
|
||||
//
|
||||
// Count packets in FIFO.
|
||||
// No protection on counter wrap,
|
||||
// unclear how to gracefully deal with it.
|
||||
// Perhaps generate Error IRQ so that S/W could clean up?
|
||||
// Configure so that the pkt_count is ample for the application.
|
||||
//
|
||||
always @(posedge clk)
|
||||
if (reset | clear)
|
||||
pkt_count <= 0;
|
||||
else if (((out_state==WAIT_EOF) && o_tlast && o_tvalid && o_tready ) &&
|
||||
((in_state==WAIT_EOF) && i_tlast && i_tvalid && i_tready))
|
||||
pkt_count <= pkt_count;
|
||||
else if ((out_state==WAIT_EOF) && o_tlast && o_tvalid && o_tready)
|
||||
pkt_count <= pkt_count - 1;
|
||||
else if ((in_state==WAIT_EOF) && i_tlast && i_tvalid && i_tready)
|
||||
pkt_count <= pkt_count + 1;
|
||||
|
||||
// Non-zero packet count indicates packet(s) present.
|
||||
assign pkt_present = |pkt_count;
|
||||
|
||||
endmodule // count_tx_packets
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
|
||||
module shortfifo
|
||||
#(parameter WIDTH=32)
|
||||
(input clk, input rst,
|
||||
input [WIDTH-1:0] datain,
|
||||
output [WIDTH-1:0] dataout,
|
||||
input read,
|
||||
input write,
|
||||
input clear,
|
||||
output reg full,
|
||||
output reg empty,
|
||||
output reg [4:0] space,
|
||||
output reg [4:0] occupied);
|
||||
|
||||
reg [3:0] a;
|
||||
genvar i;
|
||||
|
||||
generate
|
||||
for (i=0;i<WIDTH;i=i+1)
|
||||
begin : gen_srl16
|
||||
SRL16E
|
||||
srl16e(.Q(dataout[i]),
|
||||
.A0(a[0]),.A1(a[1]),.A2(a[2]),.A3(a[3]),
|
||||
.CE(write),.CLK(clk),.D(datain[i]));
|
||||
end
|
||||
endgenerate
|
||||
|
||||
always @(posedge clk)
|
||||
if(rst)
|
||||
begin
|
||||
a <= 0;
|
||||
empty <= 1;
|
||||
full <= 0;
|
||||
end
|
||||
else if(clear)
|
||||
begin
|
||||
a <= 0;
|
||||
empty <= 1;
|
||||
full<= 0;
|
||||
end
|
||||
else if(read & ~write)
|
||||
begin
|
||||
full <= 0;
|
||||
if(a==0)
|
||||
empty <= 1;
|
||||
else
|
||||
a <= a - 1;
|
||||
end
|
||||
else if(write & ~read)
|
||||
begin
|
||||
empty <= 0;
|
||||
if(~empty)
|
||||
a <= a + 1;
|
||||
if(a == 14)
|
||||
full <= 1;
|
||||
end
|
||||
|
||||
// NOTE will fail if you write into a full fifo or read from an empty one
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// space and occupied are used for diagnostics, not
|
||||
// guaranteed correct
|
||||
|
||||
//assign space = full ? 0 : empty ? 16 : 15-a;
|
||||
//assign occupied = empty ? 0 : full ? 16 : a+1;
|
||||
|
||||
always @(posedge clk)
|
||||
if(rst)
|
||||
space <= 16;
|
||||
else if(clear)
|
||||
space <= 16;
|
||||
else if(read & ~write)
|
||||
space <= space + 1;
|
||||
else if(write & ~read)
|
||||
space <= space - 1;
|
||||
|
||||
always @(posedge clk)
|
||||
if(rst)
|
||||
occupied <= 0;
|
||||
else if(clear)
|
||||
occupied <= 0;
|
||||
else if(read & ~write)
|
||||
occupied <= occupied - 1;
|
||||
else if(write & ~read)
|
||||
occupied <= occupied + 1;
|
||||
|
||||
endmodule // shortfifo
|
||||
@@ -0,0 +1,13 @@
|
||||
#
|
||||
# Copyright 2010-2013 Ettus Research LLC
|
||||
#
|
||||
|
||||
##################################################
|
||||
# SERDES Sources
|
||||
##################################################
|
||||
GPIF2_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/gpif2/, \
|
||||
gpif2_slave_fifo32.v \
|
||||
gpif2_to_fifo64.v \
|
||||
fifo64_to_gpif2.v \
|
||||
gpif2_error_checker.v \
|
||||
))
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// Copyright 2012-2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
module fifo64_to_gpif2
|
||||
#(
|
||||
parameter FIFO_SIZE = 9
|
||||
)
|
||||
(
|
||||
//input fifo interface
|
||||
input fifo_clk, input fifo_rst,
|
||||
input [63:0] i_tdata,
|
||||
input i_tlast,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
|
||||
//output interface
|
||||
input gpif_clk, input gpif_rst,
|
||||
output [31:0] o_tdata,
|
||||
output o_tlast,
|
||||
output o_tvalid,
|
||||
input o_tready
|
||||
);
|
||||
|
||||
wire [31:0] i32_tdata;
|
||||
wire i32_tlast;
|
||||
wire i32_tvalid, i32_tready;
|
||||
|
||||
axi_fifo64_to_fifo32 fifo64_to_fifo32
|
||||
(
|
||||
.clk(fifo_clk), .reset(fifo_rst), .clear(1'b0),
|
||||
.i_tdata(i_tdata), .i_tuser(3'b0/*done care*/), .i_tlast(i_tlast), .i_tvalid(i_tvalid), .i_tready(i_tready),
|
||||
.o_tdata(i32_tdata), .o_tuser(/*ignored cuz vita has len*/), .o_tlast(i32_tlast), .o_tvalid(i32_tvalid), .o_tready(i32_tready)
|
||||
);
|
||||
|
||||
wire [31:0] gate_tdata;
|
||||
wire gate_tlast;
|
||||
wire gate_tvalid, gate_tready;
|
||||
|
||||
axi_fifo_2clk #(.WIDTH(33), .SIZE(0/*SRL*/)) cross_clock_fifo
|
||||
(
|
||||
.reset(fifo_rst | gpif_rst),
|
||||
.i_aclk(fifo_clk), .i_tdata({i32_tlast, i32_tdata}), .i_tvalid(i32_tvalid), .i_tready(i32_tready),
|
||||
.o_aclk(gpif_clk), .o_tdata({gate_tlast, gate_tdata}), .o_tvalid(gate_tvalid), .o_tready(gate_tready)
|
||||
);
|
||||
|
||||
wire [31:0] int0_tdata; wire int0_tlast, int0_tvalid, int0_tready;
|
||||
|
||||
axi_packet_gate #(.WIDTH(32), .SIZE(FIFO_SIZE)) buffer_whole_pkt
|
||||
(
|
||||
.clk(gpif_clk), .reset(gpif_rst), .clear(1'b0),
|
||||
.i_tdata(gate_tdata), .i_tlast(gate_tlast), .i_terror(1'b0), .i_tvalid(gate_tvalid), .i_tready(gate_tready),
|
||||
.o_tdata(int0_tdata), .o_tlast(int0_tlast), .o_tvalid(int0_tvalid), .o_tready(int0_tready)
|
||||
);
|
||||
|
||||
axi_fifo #(.WIDTH(33), .SIZE(0)) outgress_timing_fifo
|
||||
(
|
||||
.clk(gpif_clk), .reset(gpif_rst), .clear(1'b0),
|
||||
.i_tdata({int0_tlast, int0_tdata}), .i_tvalid(int0_tvalid), .i_tready(int0_tready), .space(),
|
||||
.o_tdata({o_tlast, o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready), .occupied()
|
||||
);
|
||||
|
||||
endmodule //fifo_to_gpmc16
|
||||
@@ -0,0 +1,126 @@
|
||||
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
|
||||
// inspect the input for invalid conditions
|
||||
// when bad - drain input, flag error, and insert error msg packet
|
||||
//
|
||||
// Packets alignment errors are searched for in two different ways:
|
||||
// 1) Blatently illegal values in what is assumed to be the PACKET_LENGTH field
|
||||
// in the CHDR header
|
||||
// (We could probably improve this by looking at other fields of the header that
|
||||
// have a limited range of values)
|
||||
// 2) Packet length indicating an EOF word that doesn't have TLAST set in the FIFO.
|
||||
// (Upstream can howvever legally insert TLAST in the FIFO for words that are not EOF)
|
||||
//
|
||||
// Packet allignment recovery strategy is to wait for TLAST asserted and then decode the
|
||||
// following data assuming it is the start of new CHDR headers.
|
||||
//
|
||||
//TODO - insert bad packet
|
||||
|
||||
module gpif2_error_checker
|
||||
#(parameter SIZE = 9)
|
||||
(input clk, input reset, input clear,
|
||||
input [31:0] i_tdata, input i_tlast, input i_tvalid, output i_tready,
|
||||
output [31:0] o_tdata, output o_tlast, output o_tvalid, input o_tready,
|
||||
output bus_error, output [63:0] debug);
|
||||
|
||||
wire [31:0] gate_tdata;
|
||||
wire gate_tlast, gate_terror;
|
||||
wire gate_tvalid, gate_tready;
|
||||
|
||||
localparam STATE_HDR = 0;
|
||||
localparam STATE_FWD = 1;
|
||||
localparam STATE_EOF = 2;
|
||||
localparam STATE_WAIT = 3;
|
||||
reg [1:0] state;
|
||||
|
||||
reg [15:0] lines32;
|
||||
reg [11:0] seq_id_ref;
|
||||
reg seq_id_bad;
|
||||
reg seq_id_wayoff;
|
||||
|
||||
|
||||
wire [15:0] hdr_bytes = i_tdata[15:0] + 3; //round up to multiple of 4
|
||||
wire [15:0] hdr_lines32 = {2'b0, hdr_bytes[15:2]}; //convert to lines32 count
|
||||
wire [11:0] seq_id_actual = i_tdata[27:16];
|
||||
|
||||
|
||||
wire obviously_bad_hdr = (hdr_lines32 == 16'h0) || (hdr_lines32 > (1 << SIZE));
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (reset | clear) begin
|
||||
state <= STATE_HDR;
|
||||
lines32 <= 16'b0;
|
||||
seq_id_ref <= 12'h0;
|
||||
seq_id_bad <= 0;
|
||||
seq_id_wayoff <= 0;
|
||||
|
||||
|
||||
end
|
||||
else case (state)
|
||||
|
||||
STATE_HDR: begin //forward header and grab vita length
|
||||
if (i_tvalid && i_tready) begin
|
||||
if (obviously_bad_hdr) state <= STATE_WAIT;
|
||||
else if (hdr_lines32 == 16'h1) state <= STATE_HDR;
|
||||
else if (hdr_lines32 == 16'h2) state <= STATE_EOF;
|
||||
else state <= STATE_FWD;
|
||||
seq_id_bad <= (seq_id_actual != seq_id_ref);
|
||||
seq_id_wayoff <= (seq_id_actual != seq_id_ref) |
|
||||
(seq_id_actual != seq_id_ref+1) |
|
||||
(seq_id_actual != seq_id_ref+2) |
|
||||
(seq_id_actual != seq_id_ref+3);
|
||||
if (seq_id_actual != seq_id_ref)
|
||||
seq_id_ref <= seq_id_actual + 1;
|
||||
else
|
||||
seq_id_ref <= seq_id_ref + 1;
|
||||
end
|
||||
lines32 <= hdr_lines32;
|
||||
|
||||
end
|
||||
|
||||
STATE_FWD: begin //forward the rest of vita packet
|
||||
if (i_tvalid && i_tready) begin
|
||||
if (lines32 == 16'h3) state <= STATE_EOF;
|
||||
lines32 <= lines32 - 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
STATE_EOF: begin //do last line of vita frame + eof
|
||||
if (i_tvalid && i_tready)
|
||||
if (gate_tlast) state <= STATE_HDR;
|
||||
else state <= STATE_WAIT; // Try somehow to get synchronized again.
|
||||
end
|
||||
|
||||
STATE_WAIT: begin //drop until idle
|
||||
if (i_tvalid && i_tready && i_tlast) state <= STATE_HDR;
|
||||
end
|
||||
|
||||
endcase //state
|
||||
end
|
||||
|
||||
assign bus_error = (gate_terror && gate_tvalid && gate_tready) || ((state == STATE_HDR) && i_tvalid && i_tready && obviously_bad_hdr);
|
||||
assign gate_tlast = (state == STATE_HDR)? (hdr_lines32 == 16'h1) : (state == STATE_EOF);
|
||||
assign gate_tdata = i_tdata;
|
||||
assign gate_tvalid = i_tvalid && ((state == STATE_HDR)? !obviously_bad_hdr : (state != STATE_WAIT));
|
||||
assign i_tready = gate_tready;
|
||||
|
||||
axi_packet_gate #(.WIDTH(32), .SIZE(SIZE)) gate_xfer
|
||||
(
|
||||
.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata(gate_tdata), .i_tlast(gate_tlast), .i_terror(1'b0), .i_tvalid(gate_tvalid), .i_tready(gate_tready),
|
||||
.o_tdata(o_tdata), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready)
|
||||
);
|
||||
assign debug = {13'b0,
|
||||
seq_id_wayoff, //[50] [114]
|
||||
gate_terror, // [49] [113]
|
||||
obviously_bad_hdr, // [48] [112]
|
||||
seq_id_bad, // [47] [111]
|
||||
seq_id_ref, // [46:35] [110:99]
|
||||
i_tlast, // [34] [98]
|
||||
i_tready, // [33] [97]
|
||||
i_tvalid, // [32] [96]
|
||||
i_tdata}; // [31:0] [95:64]
|
||||
|
||||
|
||||
endmodule // cvita_insert_tlast
|
||||
@@ -0,0 +1,375 @@
|
||||
//
|
||||
// Copyright 2011-2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//this is a FIFO master interface for the FX3 in "slave fifo" mode.
|
||||
|
||||
module gpif2_slave_fifo32
|
||||
#(
|
||||
//sizes for fifo64 2 clock cascade fifos
|
||||
parameter DATA_RX_FIFO_SIZE = 12, //max vita pkt size
|
||||
parameter DATA_TX_FIFO_SIZE = 12, //max vita pkt size
|
||||
parameter CTRL_RX_FIFO_SIZE = 5, //small resp packets
|
||||
parameter CTRL_TX_FIFO_SIZE = 5, //small ctrl packets
|
||||
|
||||
//address constants for the endpoints
|
||||
parameter ADDR_DATA_TX = 2'b00,
|
||||
parameter ADDR_DATA_RX = 2'b01,
|
||||
parameter ADDR_CTRL_TX = 2'b10,
|
||||
parameter ADDR_CTRL_RX = 2'b11,
|
||||
|
||||
parameter END_WITH_COMMA = 0
|
||||
)
|
||||
(// GPIF signals
|
||||
input gpif_clk, input gpif_rst, input gpif_enb,
|
||||
inout [31:0] gpif_d,
|
||||
input [3:0] gpif_ctl,
|
||||
output reg sloe,
|
||||
output reg slrd,
|
||||
output reg slwr,
|
||||
output slcs,
|
||||
output reg pktend,
|
||||
output reg [1:0] fifoadr,
|
||||
|
||||
// FIFO interfaces
|
||||
input fifo_clk, input fifo_rst,
|
||||
|
||||
output [63:0] tx_tdata, output tx_tlast, output tx_tvalid, input tx_tready,
|
||||
input [63:0] rx_tdata, input rx_tlast, input rx_tvalid, output rx_tready,
|
||||
output [63:0] ctrl_tdata, output ctrl_tlast, output ctrl_tvalid, input ctrl_tready,
|
||||
input [63:0] resp_tdata, input resp_tlast, input resp_tvalid, output resp_tready,
|
||||
|
||||
output [31:0] debug
|
||||
);
|
||||
|
||||
reg fifo_nearly_full;
|
||||
wire ctrl_tx_fifo_nearly_full, data_tx_fifo_nearly_full;
|
||||
wire ctrl_tx_fifo_has_space, data_tx_fifo_has_space;
|
||||
|
||||
|
||||
wire [159:0] debug_tx_data, debug_tx_ctrl;
|
||||
|
||||
assign slcs = 1'b0;
|
||||
|
||||
//DMA FIFO ready and watermark flags
|
||||
reg EP_READY, EP_READY1, EP_WMARK, EP_WMARK1;
|
||||
always @(posedge gpif_clk) EP_READY <= gpif_ctl[0];
|
||||
always @(posedge gpif_clk) EP_WMARK <= gpif_ctl[1];
|
||||
always @(posedge gpif_clk) EP_READY1 <= EP_READY;
|
||||
always @(posedge gpif_clk) EP_WMARK1 <= EP_WMARK;
|
||||
|
||||
// GPIF output data lines, tristate
|
||||
reg [31:0] gpif_data_in, gpif_data_out;
|
||||
always @(posedge gpif_clk) gpif_data_in <= gpif_d;
|
||||
assign gpif_d = sloe ? gpif_data_out[31:0] : 32'bz;
|
||||
|
||||
// ////////////////////////////////////////////////////////////////////
|
||||
// GPIF bus master state machine
|
||||
|
||||
wire wr_fifo_xfer, wr_fifo_eof;
|
||||
wire [31:0] wr_fifo_data;
|
||||
reg read_ready_go, write_ready_go;
|
||||
reg wr_one, rd_one;
|
||||
|
||||
reg [3:0] state; //state machine current state
|
||||
localparam STATE_IDLE = 0;
|
||||
localparam STATE_THINK = 1;
|
||||
localparam STATE_READ = 2;
|
||||
localparam STATE_WRITE = 3;
|
||||
localparam STATE_WAIT = 4;
|
||||
|
||||
reg [2:0] idle_cycles;
|
||||
reg [1:0] last_addr, next_addr;
|
||||
wire local_fifo_ready;
|
||||
|
||||
reg slrd1, slrd2, slrd3;
|
||||
|
||||
always @(posedge gpif_clk)
|
||||
if (gpif_rst) begin
|
||||
slrd1 <= 1;
|
||||
slrd2 <= 1;
|
||||
slrd3 <= 1;
|
||||
end else begin
|
||||
slrd1 <= slrd;
|
||||
slrd2 <= slrd1;
|
||||
slrd3 <= slrd2;
|
||||
end
|
||||
|
||||
wire RD_VALID = ~slrd3;
|
||||
wire RD_LAST = slrd2;
|
||||
wire WR_VALID = (EP_WMARK1 || !wr_one);
|
||||
|
||||
// //////////////////////////////////////////////////////////////
|
||||
// FX2 slave FIFO bus master state machine
|
||||
//
|
||||
always @(posedge gpif_clk)
|
||||
if(gpif_rst) begin
|
||||
state <= STATE_IDLE;
|
||||
sloe <= 0;
|
||||
slrd <= 1;
|
||||
slwr <= 1;
|
||||
pktend <= 1;
|
||||
gpif_data_out <= 32'b0;
|
||||
idle_cycles <= 0;
|
||||
fifoadr <= 0;
|
||||
wr_one <= 1'b0;
|
||||
rd_one <= 1'b0;
|
||||
last_addr <= 2'b0;
|
||||
end
|
||||
else if (gpif_enb) begin
|
||||
case (state)
|
||||
|
||||
//
|
||||
// Increment fifoadr to point at next thread, set all strobes to idle,
|
||||
//
|
||||
STATE_IDLE: begin
|
||||
sloe <= 0;
|
||||
slrd <= 1;
|
||||
slwr <= 1;
|
||||
pktend <= 1;
|
||||
gpif_data_out <= 32'b0;
|
||||
fifoadr <= next_addr;
|
||||
state <= STATE_WAIT;
|
||||
idle_cycles <= 0;
|
||||
end
|
||||
|
||||
//
|
||||
// now wait here for 8 clock cycles before transitioning to STATE_THINK.
|
||||
// We stay in this state if no local FIFO's can proceed at this point.
|
||||
//
|
||||
STATE_WAIT: begin
|
||||
if (local_fifo_ready) begin
|
||||
idle_cycles <= idle_cycles + 1'b1;
|
||||
if (idle_cycles == 3'b111) state <= STATE_THINK;
|
||||
end
|
||||
else begin
|
||||
idle_cycles <= 3'b0;
|
||||
fifoadr <= fifoadr + 2'b1;
|
||||
end
|
||||
end
|
||||
|
||||
//
|
||||
// If there is a read to start, assert SLRD and SLOE and transition to STATE_READ.
|
||||
// If there is a write to perform, set flags that says there is the possibility to do at least
|
||||
// one write (wr_one) and transition to STATE_WRITE
|
||||
//
|
||||
STATE_THINK: begin
|
||||
|
||||
if (EP_READY1 && read_ready_go) begin
|
||||
state <= STATE_READ;
|
||||
slrd <= 0;
|
||||
rd_one <= 0;
|
||||
end
|
||||
else if (EP_READY1 && write_ready_go) begin
|
||||
state <= STATE_WRITE;
|
||||
sloe <= 1;
|
||||
wr_one <= 1'b0;
|
||||
end
|
||||
else begin
|
||||
state <= STATE_IDLE;
|
||||
end
|
||||
|
||||
idle_cycles <= 0;
|
||||
last_addr <= fifoadr;
|
||||
end
|
||||
|
||||
// If flag rd_one is set (armed 5 cycles after slrd goes initialy assrted) and RD_VALID has gone deasserted
|
||||
// (meaning that the watermark deasserted 5 clock cycles ago) transition to STATE_IDLE.
|
||||
// If watermark deasserted 2 cycles ago de-assert slrd ...read data is still traveling in the pipeline.
|
||||
// Whilst RD_VALID stays asserted keep the rd_one flag armed.
|
||||
STATE_READ: begin
|
||||
if (rd_one && ~RD_VALID) state <= STATE_IDLE;
|
||||
if (~EP_WMARK1 | fifo_nearly_full) slrd <= 1;
|
||||
if (RD_VALID) rd_one <= 1'b1;
|
||||
end
|
||||
|
||||
// If local FIFO goes empty or tlast is set then transition to STATE_IDLE
|
||||
// Push local FIFO data out onto GPIF data bus.
|
||||
// if local FIFO has valid data then assert slwr
|
||||
// if local FIFO assertes tlast then assert pktend
|
||||
// If WR_VALID asserted (because wr_one already asserted in the first cycle in this state)
|
||||
// now clear wr_one (watermark will keep WR_VALID asserted from now on if this is a burst).
|
||||
//
|
||||
STATE_WRITE: begin
|
||||
if (~wr_fifo_xfer || wr_fifo_eof) state <= STATE_IDLE;
|
||||
gpif_data_out <= wr_fifo_data;
|
||||
slwr <= ~wr_fifo_xfer;
|
||||
pktend <= ~wr_fifo_eof;
|
||||
if (WR_VALID) wr_one <= 1'b1;
|
||||
end
|
||||
|
||||
default: state <= STATE_IDLE;
|
||||
endcase
|
||||
end
|
||||
|
||||
// ///////////////////////////////////////////////////////////////////
|
||||
// fifo signal assignments and enables
|
||||
|
||||
//output from fifos - ready to xfer
|
||||
wire data_tx_tready, ctrl_tx_tready;
|
||||
wire ctrl_rx_tvalid, data_rx_tvalid;
|
||||
|
||||
//Priority encoding for the the next address to service:
|
||||
//The next address to service is based on the readiness
|
||||
//of the internal fifos and last serviced fairness metric.
|
||||
always @(posedge gpif_clk) next_addr <=
|
||||
((ctrl_rx_tvalid && last_addr != ADDR_CTRL_RX)? ADDR_CTRL_RX :
|
||||
((ctrl_tx_fifo_has_space && last_addr != ADDR_CTRL_TX)? ADDR_CTRL_TX :
|
||||
((data_rx_tvalid && last_addr != ADDR_DATA_RX)? ADDR_DATA_RX :
|
||||
((data_tx_fifo_has_space && last_addr != ADDR_DATA_TX)? ADDR_DATA_TX :
|
||||
(fifoadr + 2'b1)
|
||||
))));
|
||||
|
||||
//Help the FPGA search to only look for addrs that the FPGA is ready for
|
||||
assign local_fifo_ready =
|
||||
(ctrl_rx_tvalid && (fifoadr == ADDR_CTRL_RX)) ||
|
||||
(ctrl_tx_fifo_has_space && (fifoadr == ADDR_CTRL_TX)) ||
|
||||
(data_rx_tvalid && (fifoadr == ADDR_DATA_RX)) ||
|
||||
(data_tx_fifo_has_space && (fifoadr == ADDR_DATA_TX));
|
||||
|
||||
always @(posedge gpif_clk) fifo_nearly_full <=
|
||||
(ctrl_tx_fifo_nearly_full && (fifoadr == ADDR_CTRL_TX)) ||
|
||||
(data_tx_fifo_nearly_full && (fifoadr == ADDR_DATA_TX));
|
||||
|
||||
always @(posedge gpif_clk) read_ready_go <=
|
||||
(ctrl_tx_fifo_has_space && (fifoadr == ADDR_CTRL_TX)) ||
|
||||
(data_tx_fifo_has_space && (fifoadr == ADDR_DATA_TX));
|
||||
|
||||
always @(posedge gpif_clk) write_ready_go <=
|
||||
(ctrl_rx_tvalid && (fifoadr == ADDR_CTRL_RX)) ||
|
||||
(data_rx_tvalid && (fifoadr == ADDR_DATA_RX));
|
||||
|
||||
//fifo xfer enable
|
||||
wire data_rx_tready = (state == STATE_WRITE) && (fifoadr == ADDR_DATA_RX) && WR_VALID;
|
||||
wire ctrl_rx_tready = (state == STATE_WRITE) && (fifoadr == ADDR_CTRL_RX) && WR_VALID;
|
||||
wire data_tx_tvalid = (state == STATE_READ) && (fifoadr == ADDR_DATA_TX) && RD_VALID;
|
||||
wire ctrl_tx_tvalid = (state == STATE_READ) && (fifoadr == ADDR_CTRL_TX) && RD_VALID;
|
||||
|
||||
//outputs from rx fifo paths
|
||||
wire ctrl_rx_tlast, data_rx_tlast;
|
||||
wire [31:0] ctrl_rx_tdata, data_rx_tdata;
|
||||
|
||||
//mux rx outputs for gpif state machine
|
||||
assign wr_fifo_xfer = (fifoadr == ADDR_CTRL_RX)? (ctrl_rx_tvalid && ctrl_rx_tready) : (data_rx_tvalid && data_rx_tready);
|
||||
assign wr_fifo_eof = wr_fifo_xfer && ((fifoadr == ADDR_CTRL_RX)? ctrl_rx_tlast : data_rx_tlast);
|
||||
assign wr_fifo_data = (fifoadr == ADDR_CTRL_RX)? ctrl_rx_tdata : data_rx_tdata;
|
||||
|
||||
wire ctrl_bus_error, tx_bus_error;
|
||||
|
||||
// ////////////////////////////////////////////////////////////////////
|
||||
// TX Data Path
|
||||
|
||||
gpif2_to_fifo64 #(.FIFO_SIZE(DATA_TX_FIFO_SIZE)) gpif2_to_fifo64_tx(
|
||||
.gpif_clk(gpif_clk), .gpif_rst(gpif_rst),
|
||||
.i_tdata(gpif_data_in), .i_tlast(RD_LAST), .i_tvalid(data_tx_tvalid), .i_tready(data_tx_tready),
|
||||
.fifo_clk(fifo_clk), .fifo_rst(fifo_rst),
|
||||
.fifo_nearly_full(data_tx_fifo_nearly_full), .fifo_has_space(data_tx_fifo_has_space),
|
||||
.o_tdata(tx_tdata), .o_tlast(tx_tlast), .o_tvalid(tx_tvalid), .o_tready(tx_tready),
|
||||
.bus_error(tx_bus_error), .debug(debug_tx_data)
|
||||
);
|
||||
|
||||
// ////////////////////////////////////////////
|
||||
// RX Data Path
|
||||
|
||||
fifo64_to_gpif2 #(.FIFO_SIZE(DATA_RX_FIFO_SIZE)) fifo64_to_gpif2_rx(
|
||||
.fifo_clk(fifo_clk), .fifo_rst(fifo_rst),
|
||||
.i_tdata(rx_tdata), .i_tlast(rx_tlast), .i_tvalid(rx_tvalid), .i_tready(rx_tready),
|
||||
.gpif_clk(gpif_clk), .gpif_rst(gpif_rst),
|
||||
.o_tdata(data_rx_tdata), .o_tlast(data_rx_tlast), .o_tvalid(data_rx_tvalid), .o_tready(data_rx_tready)
|
||||
);
|
||||
|
||||
// ////////////////////////////////////////////////////////////////////
|
||||
// CTRL path
|
||||
|
||||
gpif2_to_fifo64 #(.FIFO_SIZE(CTRL_TX_FIFO_SIZE)) gpif2_to_fifo64_ctrl(
|
||||
.gpif_clk(gpif_clk), .gpif_rst(gpif_rst),
|
||||
.i_tdata(gpif_data_in), .i_tlast(RD_LAST), .i_tvalid(ctrl_tx_tvalid), .i_tready(ctrl_tx_tready),
|
||||
.fifo_clk(fifo_clk), .fifo_rst(fifo_rst),
|
||||
.fifo_nearly_full(ctrl_tx_fifo_nearly_full), .fifo_has_space(ctrl_tx_fifo_has_space),
|
||||
.o_tdata(ctrl_tdata), .o_tlast(ctrl_tlast), .o_tvalid(ctrl_tvalid), .o_tready(ctrl_tready),
|
||||
.bus_error(ctrl_bus_error), .debug(debug_tx_ctrl)
|
||||
);
|
||||
|
||||
// ////////////////////////////////////////////////////////////////////
|
||||
// RESP path
|
||||
|
||||
fifo64_to_gpif2 #(.FIFO_SIZE(CTRL_RX_FIFO_SIZE)) fifo64_to_gpif2_resp(
|
||||
.fifo_clk(fifo_clk), .fifo_rst(fifo_rst),
|
||||
.i_tdata(resp_tdata), .i_tlast(resp_tlast), .i_tvalid(resp_tvalid), .i_tready(resp_tready),
|
||||
.gpif_clk(gpif_clk), .gpif_rst(gpif_rst),
|
||||
.o_tdata(ctrl_rx_tdata), .o_tlast(ctrl_rx_tlast), .o_tvalid(ctrl_rx_tvalid), .o_tready(ctrl_rx_tready)
|
||||
);
|
||||
|
||||
// ////////////////////////////////////////////
|
||||
// DEBUG
|
||||
|
||||
wire [31:0] debug0 = {
|
||||
sloe, slrd, slwr, pktend, fifoadr, EP_READY, EP_WMARK, //8
|
||||
state, //4
|
||||
data_tx_tvalid, data_tx_tready, data_rx_tvalid, data_rx_tready, //4
|
||||
gpif_d[15:0] //16
|
||||
};
|
||||
|
||||
reg [31:0] debug_reg0;
|
||||
reg [31:0] debug_reg1;
|
||||
reg [31:0] debug_reg2;
|
||||
always @(posedge gpif_clk) debug_reg0 <= debug0;
|
||||
always @(posedge gpif_clk) debug_reg1 <= debug_reg0;
|
||||
always @(posedge gpif_clk) debug_reg2 <= debug_reg1;
|
||||
assign debug = debug_reg2;
|
||||
|
||||
wire [37:0] debug_resp = {
|
||||
resp_tlast, // 37
|
||||
resp_tready, // 36
|
||||
resp_tvalid, // 35
|
||||
ctrl_rx_tlast, // 34
|
||||
ctrl_rx_tready, // 33
|
||||
ctrl_rx_tvalid, // 32
|
||||
ctrl_rx_tdata // 31:0
|
||||
};
|
||||
|
||||
|
||||
reg [255:0] debug1,debug2;
|
||||
|
||||
always @(posedge gpif_clk) debug1 <= {debug_resp,debug_tx_ctrl,debug0};
|
||||
always @(posedge gpif_clk) debug2 <= debug1;
|
||||
|
||||
|
||||
|
||||
wire [35:0] CONTROL0,CONTROL1;
|
||||
/*
|
||||
chipscope_ila_32 chipscope_ila_32(
|
||||
.CONTROL(CONTROL0), // INOUT BUS [35:0]
|
||||
.CLK(gpif_clk), // IN
|
||||
.TRIG0(debug2) // IN BUS [31:0]
|
||||
);
|
||||
|
||||
chipscope_ila_128 chipscope_ila_128(
|
||||
.CONTROL(CONTROL1), // INOUT BUS [35:0]
|
||||
.CLK(fifo_clk), // IN
|
||||
.TRIG0({debug4,debug6}) // IN BUS [31:0]
|
||||
);
|
||||
|
||||
|
||||
chipscope_ila_256 chipscope_ila_256(
|
||||
.CONTROL(CONTROL0), // INOUT BUS [35:0]
|
||||
.CLK(gpif_clk), // IN
|
||||
.TRIG0(debug2) // IN BUS [31:0]
|
||||
);
|
||||
|
||||
chipscope_ila_32 chipscope_ila_32_2(
|
||||
.CONTROL(CONTROL1), // INOUT BUS [35:0]
|
||||
.CLK(gpif_clk), // IN
|
||||
.TRIG0(32'd0) // IN BUS [31:0]
|
||||
);
|
||||
|
||||
chipscope_icon chipscope_icon(
|
||||
.CONTROL0(CONTROL0), // INOUT BUS [35:0]
|
||||
.CONTROL1(CONTROL1) // INOUT BUS [35:0]
|
||||
);
|
||||
|
||||
*/
|
||||
endmodule // gpif2_slave_fifo32
|
||||
@@ -0,0 +1,118 @@
|
||||
//
|
||||
// Copyright 2012-2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
module gpif2_to_fifo64
|
||||
#(
|
||||
parameter FIFO_SIZE = 9
|
||||
)
|
||||
(
|
||||
//input interface
|
||||
input gpif_clk, input gpif_rst,
|
||||
input [31:0] i_tdata,
|
||||
input i_tlast,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
output fifo_has_space,
|
||||
output fifo_nearly_full,
|
||||
|
||||
//output fifo interface
|
||||
input fifo_clk, input fifo_rst,
|
||||
output [63:0] o_tdata,
|
||||
output o_tlast,
|
||||
output o_tvalid,
|
||||
input o_tready,
|
||||
|
||||
output bus_error,
|
||||
output [159:0] debug
|
||||
);
|
||||
|
||||
wire [31:0] int_tdata;
|
||||
wire int_tlast;
|
||||
wire int_tvalid, int_tready;
|
||||
|
||||
wire [31:0] int0_tdata; wire int0_tlast, int0_tvalid, int0_tready;
|
||||
|
||||
//this fifo provides a space signal so we know a burst is possible
|
||||
localparam BURST_SIZE = (FIFO_SIZE < 8)? FIFO_SIZE : 8;
|
||||
wire [15:0] space;
|
||||
|
||||
|
||||
assign fifo_has_space = space >= (1 << BURST_SIZE);
|
||||
assign fifo_nearly_full = (space < 6); // 5 spaces left.
|
||||
|
||||
axi_fifo #(.WIDTH(33), .SIZE(0)) ingress_timing_fifo
|
||||
(
|
||||
.clk(gpif_clk), .reset(gpif_rst), .clear(1'b0),
|
||||
.i_tdata({i_tlast, i_tdata}), .i_tvalid(i_tvalid), .i_tready(i_tready), .space(),
|
||||
.o_tdata({int0_tlast, int0_tdata}), .o_tvalid(int0_tvalid), .o_tready(int0_tready), .occupied()
|
||||
);
|
||||
axi_fifo #(.WIDTH(33), .SIZE(BURST_SIZE)) min_read_buff
|
||||
(
|
||||
.clk(gpif_clk), .reset(gpif_rst), .clear(1'b0),
|
||||
.i_tdata({int0_tlast, int0_tdata}), .i_tvalid(int0_tvalid), .i_tready(int0_tready), .space(space),
|
||||
.o_tdata({int_tlast, int_tdata}), .o_tvalid(int_tvalid), .o_tready(int_tready), .occupied()
|
||||
);
|
||||
|
||||
reg input_write_error;
|
||||
|
||||
always @(posedge gpif_clk) input_write_error <= i_tvalid & ~i_tready;
|
||||
|
||||
|
||||
wire [31:0] chk_tdata;
|
||||
wire chk_tlast;
|
||||
wire chk_tvalid, chk_tready;
|
||||
|
||||
axi_fifo_2clk #(.WIDTH(33), .SIZE(0/*SRL*/)) cross_clock_fifo
|
||||
(
|
||||
.reset(fifo_rst | gpif_rst),
|
||||
.i_aclk(gpif_clk), .i_tdata({int_tlast, int_tdata}), .i_tvalid(int_tvalid), .i_tready(int_tready),
|
||||
.o_aclk(fifo_clk), .o_tdata({chk_tlast, chk_tdata}), .o_tvalid(chk_tvalid), .o_tready(chk_tready)
|
||||
);
|
||||
|
||||
wire [31:0] o32_tdata;
|
||||
wire o32_tlast;
|
||||
wire o32_tvalid, o32_tready;
|
||||
|
||||
//reframes a tlast from the vita header - and drops bad packets
|
||||
//*
|
||||
gpif2_error_checker #(.SIZE(FIFO_SIZE)) checker
|
||||
(
|
||||
.clk(fifo_clk), .reset(fifo_rst), .clear(1'b0),
|
||||
.i_tdata(chk_tdata), .i_tlast(chk_tlast), .i_tvalid(chk_tvalid), .i_tready(chk_tready),
|
||||
.o_tdata(o32_tdata), .o_tlast(o32_tlast), .o_tvalid(o32_tvalid), .o_tready(o32_tready),
|
||||
.bus_error(bus_error), .debug(debug[63:0])
|
||||
);
|
||||
//*/
|
||||
//assign o32_tdata = chk_tdata;
|
||||
//assign o32_tlast = chk_tlast;
|
||||
//assign o32_tvalid = chk_tvalid;
|
||||
//assign chk_tready = o32_tready;
|
||||
|
||||
axi_fifo32_to_fifo64 fifo32_to_fifo64
|
||||
(
|
||||
.clk(fifo_clk), .reset(fifo_rst), .clear(1'b0),
|
||||
.i_tdata(o32_tdata), .i_tuser(2'b0/*always 32 bits*/), .i_tlast(o32_tlast), .i_tvalid(o32_tvalid), .i_tready(o32_tready),
|
||||
.o_tdata(o_tdata), .o_tuser(/*ignored cuz vita has len*/), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready)
|
||||
);
|
||||
|
||||
assign debug[159:64] = {
|
||||
fifo_nearly_full, // 146
|
||||
space[9:0], // 145:136
|
||||
input_write_error, // 135
|
||||
int_tlast, // 134
|
||||
int_tready, // 133
|
||||
int_tvalid, // 132
|
||||
i_tlast, // 131
|
||||
i_tready, // 130
|
||||
fifo_has_space, // 129
|
||||
i_tvalid, // 128
|
||||
int_tdata[31:0], // 127:96
|
||||
i_tdata[31:0] // 95:64
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
endmodule //fifo_to_gpif2
|
||||
@@ -0,0 +1,3 @@
|
||||
vita.txt
|
||||
xo.txt
|
||||
zpu.txt
|
||||
@@ -0,0 +1,21 @@
|
||||
#
|
||||
# Copyright 2013 Ettus Research LLC
|
||||
#
|
||||
|
||||
##################################################
|
||||
# Packet Processing Sources
|
||||
##################################################
|
||||
PACKET_PROC_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/packet_proc/, \
|
||||
eth_dispatch.v \
|
||||
ip_hdr_checksum.v \
|
||||
vrlp_eth_framer.v \
|
||||
chdr_eth_framer.v \
|
||||
eth_interface.v \
|
||||
vrlp_to_compressed_vita.v \
|
||||
compressed_vita_to_vrlp.v \
|
||||
source_flow_control.v \
|
||||
cvita_insert_tlast.v \
|
||||
cvita_dest_lookup.v \
|
||||
cvita_chunker.v \
|
||||
cvita_dechunker.v \
|
||||
))
|
||||
@@ -0,0 +1,136 @@
|
||||
|
||||
// chdr_eth_framer
|
||||
// Takes a CHDR stream in and adds udp, ip, and ethernet framing
|
||||
// Uses 8 setting reg addresses. First 4 are simple registers:
|
||||
// BASE+0 : Upper 16 bits of ethernet src mac
|
||||
// BASE+1 : Lower 32 bits of ethernet src mac
|
||||
// BASE+2 : IP src address
|
||||
// BASE+3 : UDP src port
|
||||
//
|
||||
// Next 4 control write ports on a RAM indexed by destination field of stream ID
|
||||
// BASE+4 : Dest SID for next 3 regs
|
||||
// BASE+5 : Dest IP
|
||||
// BASE+6 : Dest UDP port, upper 16 bits of dest mac
|
||||
// BASE+7 : Lower 32 bits of dest mac
|
||||
//
|
||||
|
||||
module chdr_eth_framer
|
||||
#(parameter BASE=0)
|
||||
(input clk, input reset, input clear,
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data,
|
||||
input [63:0] in_tdata, input in_tlast, input in_tvalid, output in_tready,
|
||||
output [63:0] out_tdata, output [3:0] out_tuser, output out_tlast, output out_tvalid, input out_tready,
|
||||
output [31:0] debug );
|
||||
|
||||
localparam SR_AWIDTH = 8;
|
||||
|
||||
reg [7:0] sid;
|
||||
reg [15:0] chdr_len;
|
||||
|
||||
reg [2:0] vef_state;
|
||||
localparam VEF_IDLE = 3'd0;
|
||||
localparam VEF_PAYLOAD = 3'd7;
|
||||
|
||||
reg [63:0] tdata;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
begin
|
||||
vef_state <= VEF_IDLE;
|
||||
sid <= 8'd0;
|
||||
chdr_len <= 16'd0;
|
||||
end
|
||||
else
|
||||
case(vef_state)
|
||||
VEF_IDLE :
|
||||
if(in_tvalid)
|
||||
begin
|
||||
vef_state <= 1;
|
||||
sid <= in_tdata[7:0];
|
||||
chdr_len <= in_tdata[47:32];
|
||||
end
|
||||
VEF_PAYLOAD :
|
||||
if(in_tvalid & out_tready)
|
||||
if(in_tlast)
|
||||
vef_state <= VEF_IDLE;
|
||||
default :
|
||||
if(out_tready)
|
||||
vef_state <= vef_state + 3'd1;
|
||||
endcase // case (vef_state)
|
||||
|
||||
assign in_tready = (vef_state == VEF_PAYLOAD) ? out_tready : 1'b0;
|
||||
assign out_tvalid = (vef_state == VEF_PAYLOAD) ? in_tvalid : (vef_state == VEF_IDLE) ? 1'b0 : 1'b1;
|
||||
assign out_tlast = (vef_state == VEF_PAYLOAD) ? in_tlast : 1'b0;
|
||||
assign out_tuser = ((vef_state == VEF_PAYLOAD) & in_tlast) ? {1'b0,chdr_len[2:0]} : 4'b0000;
|
||||
assign out_tdata = tdata;
|
||||
|
||||
wire [47:0] pad = 48'h0;
|
||||
wire [47:0] mac_src, mac_dst;
|
||||
wire [15:0] eth_type = 16'h0800;
|
||||
wire [15:0] misc_ip = { 4'd4 /* IPv4 */, 4'd5 /* IP HDR Len */, 8'h00 /* DSCP and ECN */};
|
||||
wire [15:0] ip_len = (16'd28 + chdr_len); // 20 for IP, 8 for UDP
|
||||
wire [15:0] ident = 16'h0;
|
||||
wire [15:0] flag_frag = { 3'b010 /* don't fragment */, 13'h0 };
|
||||
wire [15:0] ttl_prot = { 8'h10 /* TTL */, 8'h11 /* UDP */ };
|
||||
wire [15:0] iphdr_checksum;
|
||||
wire [31:0] ip_src, ip_dst;
|
||||
wire [15:0] udp_src, udp_dst;
|
||||
wire [15:0] udp_len = (16'd8 + chdr_len);
|
||||
wire [15:0] udp_checksum = 16'h0;
|
||||
|
||||
setting_reg #(.my_addr(BASE), .awidth(SR_AWIDTH), .width(16)) set_mac_upper
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(mac_src[47:32]), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+1), .awidth(SR_AWIDTH), .width(32)) set_mac_lower
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(mac_src[31:0]), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+2), .awidth(SR_AWIDTH), .width(32)) set_ip
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(ip_src), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+3), .awidth(SR_AWIDTH), .width(16)) set_udp
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(udp_src), .changed());
|
||||
|
||||
// Tables of MAC/IP/UDP addresses
|
||||
wire [7:0] ram_addr; // FIXME we could skip this part if we had wider SR addresses
|
||||
|
||||
setting_reg #(.my_addr(BASE+4), .awidth(SR_AWIDTH), .width(8)) set_ram_addr
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(ram_addr), .changed());
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(8)) ram_ip
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+5)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[7:0]), .dib(32'hFFFF_FFFF), .dob(ip_dst));
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(8)) ram_udpmac
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+6)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[7:0]), .dib(32'hFFFF_FFFF), .dob({udp_dst,mac_dst[47:32]}));
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(8)) ram_maclower
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+7)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[7:0]), .dib(32'hFFFF_FFFF), .dob(mac_dst[31:0]));
|
||||
|
||||
ip_hdr_checksum ip_hdr_checksum
|
||||
(.clk(clk), .in({misc_ip,ip_len,ident,flag_frag,ttl_prot,16'd0,ip_src,ip_dst}),
|
||||
.out(iphdr_checksum));
|
||||
|
||||
always @*
|
||||
case(vef_state)
|
||||
1 : tdata <= { pad[47:0], mac_dst[47:32]};
|
||||
2 : tdata <= { mac_dst[31:0], mac_src[47:16]};
|
||||
3 : tdata <= { mac_src[15:0], eth_type[15:0], misc_ip[15:0], ip_len[15:0] };
|
||||
4 : tdata <= { ident[15:0], flag_frag[15:0], ttl_prot[15:0], iphdr_checksum[15:0]};
|
||||
5 : tdata <= { ip_src, ip_dst};
|
||||
6 : tdata <= { udp_src, udp_dst, udp_len, udp_checksum};
|
||||
default : tdata <= in_tdata;
|
||||
endcase // case (vef_state)
|
||||
|
||||
endmodule // chdr_eth_framer
|
||||
@@ -0,0 +1,78 @@
|
||||
|
||||
module compressed_vita_to_vrlp
|
||||
(input clk, input reset, input clear,
|
||||
input [63:0] i_tdata, input i_tlast, input i_tvalid, output i_tready,
|
||||
output [63:0] o_tdata, output [15:0] o_tuser, output o_tlast, output o_tvalid, input o_tready
|
||||
);
|
||||
|
||||
wire [19:0] vrlp_size = 20'd3 + {4'b0000,i_tdata[47:32]};
|
||||
reg odd_len;
|
||||
reg [2:0] cv2v_state;
|
||||
|
||||
reg [63:0] o_tdata_int;
|
||||
wire o_tlast_int, o_tvalid_int, o_tready_int;
|
||||
|
||||
localparam CV2V_VRLP = 3'd0; // VRLP header
|
||||
localparam CV2V_VRT_ECH = 3'd1; // Extension context header
|
||||
localparam CV2V_VRT_IFH = 3'd2; // IF Data header
|
||||
localparam CV2V_BODY = 3'd3;
|
||||
localparam CV2V_VEND_ODD = 3'd4;
|
||||
localparam CV2V_VEND_EVEN = 3'd4;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
begin
|
||||
cv2v_state <= CV2V_VRLP;
|
||||
odd_len <= 1'b0;
|
||||
end
|
||||
else
|
||||
case(cv2v_state)
|
||||
CV2V_VRLP :
|
||||
if(i_tvalid & o_tready_int)
|
||||
begin
|
||||
odd_len <= i_tdata[32];
|
||||
if(i_tdata[63])
|
||||
cv2v_state <= CV2V_VRT_ECH;
|
||||
else
|
||||
cv2v_state <= CV2V_VRT_IFH;
|
||||
end
|
||||
|
||||
CV2V_VRT_ECH, CV2V_VRT_IFH :
|
||||
if(i_tvalid & o_tready_int)
|
||||
cv2v_state <= CV2V_BODY;
|
||||
|
||||
CV2V_BODY :
|
||||
if(i_tlast & i_tvalid & o_tready_int)
|
||||
if(odd_len)
|
||||
cv2v_state <= CV2V_VRLP;
|
||||
else
|
||||
cv2v_state <= CV2V_VEND_EVEN;
|
||||
|
||||
CV2V_VEND_EVEN :
|
||||
if(o_tready_int)
|
||||
cv2v_state <= CV2V_VRLP;
|
||||
endcase // case (cv2v_state)
|
||||
|
||||
assign i_tready = o_tready_int & (cv2v_state != CV2V_VRLP) & (cv2v_state != CV2V_VEND_EVEN);
|
||||
assign o_tvalid_int = i_tvalid | (cv2v_state == CV2V_VEND_EVEN);
|
||||
|
||||
always @*
|
||||
case(cv2v_state)
|
||||
CV2V_VRLP : o_tdata_int <= { 32'h5652_4c50 /*VRLP*/, i_tdata[59:48] /*seqnum*/, vrlp_size[19:0] };
|
||||
CV2V_VRT_ECH : o_tdata_int <= { 4'h5 /*type*/, 4'h0, 3'b00, i_tdata[61] /*time*/, i_tdata[51:48] /*seqnum*/, i_tdata[47:32] /*len*/, i_tdata[31:0] /*sid*/ };
|
||||
CV2V_VRT_IFH : o_tdata_int <= { 4'h1 /*type*/, 1'b0, i_tdata[62] /*TRL*/, 1'b0, i_tdata[60] /*eob*/, 3'b00, i_tdata[61] /*time*/, i_tdata[51:48] /*seqnum*/, i_tdata[47:32] /*len*/, i_tdata[31:0] /*sid*/ };
|
||||
CV2V_BODY : o_tdata_int <= (i_tlast & odd_len) ? { i_tdata[63:32], 32'h5645_4E44 /*VEND*/ } : i_tdata;
|
||||
CV2V_VEND_EVEN : o_tdata_int <= { 32'h5645_4E44 /*VEND*/, 32'h0};
|
||||
default : o_tdata_int <= i_tdata;
|
||||
endcase // case (cv2v_state)
|
||||
|
||||
assign o_tlast_int = (cv2v_state == CV2V_VEND_EVEN) | ((cv2v_state == CV2V_BODY) & i_tlast & odd_len);
|
||||
|
||||
// Short FIFO before output
|
||||
axi_fifo_short #(.WIDTH(81)) axi_fifo_short
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({o_tlast_int, i_tdata[15:0], o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int),
|
||||
.o_tdata({o_tlast, o_tuser, o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
|
||||
.space(), .occupied());
|
||||
|
||||
endmodule // compressed_vita_to_vrlp
|
||||
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
// Quantize cvita packets to a configurable quantum value. o_tlast and
|
||||
// i_tready will be held off until the entire quantized packet is xferred.
|
||||
// If quantum is changed, it is the responsibility of the client to clear
|
||||
// this module. error is asserted if a packet is larger than the quantum
|
||||
// error can be reset by asserting reset or clear.
|
||||
|
||||
module cvita_chunker # (
|
||||
parameter PAD_VALUE = 64'hFFFFFFFF_FFFFFFFF
|
||||
) (
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
input [15:0] frame_size,
|
||||
|
||||
input [63:0] i_tdata,
|
||||
input i_tlast,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
|
||||
output [63:0] o_tdata,
|
||||
output o_tlast,
|
||||
output o_tvalid,
|
||||
input o_tready,
|
||||
|
||||
output error
|
||||
);
|
||||
|
||||
localparam ST_HEADER = 2'd0;
|
||||
localparam ST_DATA = 2'd1;
|
||||
localparam ST_PADDING = 2'd2;
|
||||
localparam ST_ERROR = 2'd3;
|
||||
|
||||
reg [1:0] state;
|
||||
reg [15:0] frame_rem;
|
||||
|
||||
wire [15:0] cvita_len_ceil = i_tdata[47:32] + 7;
|
||||
wire [15:0] axi_len = {3'b000, cvita_len_ceil[15:3]};
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (reset | clear) begin
|
||||
state <= ST_HEADER;
|
||||
frame_rem <= 16'd0;
|
||||
end else if (o_tvalid & o_tready) begin
|
||||
case (state)
|
||||
ST_HEADER: begin
|
||||
if (axi_len > frame_size)
|
||||
state <= ST_ERROR;
|
||||
else if (i_tlast)
|
||||
state <= ST_PADDING;
|
||||
else
|
||||
state <= ST_DATA;
|
||||
|
||||
frame_rem <= frame_size - 16'd1;
|
||||
end
|
||||
|
||||
ST_DATA: begin
|
||||
if (i_tlast) begin
|
||||
state <= o_tlast ? ST_HEADER : ST_PADDING;
|
||||
frame_rem <= o_tlast ? 16'd0 : (frame_rem - 16'd1);
|
||||
end else begin
|
||||
state <= ST_DATA;
|
||||
frame_rem <= frame_rem - 16'd1;
|
||||
end
|
||||
end
|
||||
|
||||
ST_PADDING: begin
|
||||
if (o_tlast) begin
|
||||
state <= ST_HEADER;
|
||||
frame_rem <= 16'd0;
|
||||
end else begin
|
||||
state <= ST_PADDING;
|
||||
frame_rem <= frame_rem - 16'd1;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
assign i_tready = o_tready & (state != ST_PADDING);
|
||||
|
||||
assign o_tvalid = i_tvalid | (state == ST_PADDING);
|
||||
assign o_tlast = (frame_rem != 0) ? (frame_rem == 16'd1) : (axi_len == 16'd1);
|
||||
assign o_tdata = (state == ST_PADDING) ? PAD_VALUE : i_tdata;
|
||||
|
||||
assign error = (state == ST_ERROR);
|
||||
|
||||
endmodule // cvita_chunker
|
||||
@@ -0,0 +1,187 @@
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
`timescale 500ps/1ps
|
||||
|
||||
module cvita_chunker_tb();
|
||||
|
||||
// TB stimulus
|
||||
reg clk = 0;
|
||||
reg reset = 1;
|
||||
reg clear = 0;
|
||||
reg [15:0] quantum;
|
||||
|
||||
// Check vars
|
||||
reg [31:0] o_xfer_count = 0, i_xfer_count = 0;
|
||||
reg [63:0] o_last_tdata = 0;
|
||||
|
||||
|
||||
always #10 clk = ~clk;
|
||||
|
||||
initial $dumpfile("cvita_chunker_tb.vcd");
|
||||
initial $dumpvars(0,cvita_chunker_tb);
|
||||
|
||||
function check_result;
|
||||
input [31:0] o_xfer_count_arg;
|
||||
input [31:0] i_xfer_count_arg;
|
||||
input [63:0] o_last_tdata_arg;
|
||||
input error_arg;
|
||||
begin
|
||||
//Check vars
|
||||
check_result = 1;
|
||||
check_result = check_result & ((o_xfer_count_arg == o_xfer_count) !== 0);
|
||||
check_result = check_result & ((i_xfer_count_arg == i_xfer_count) !== 0);
|
||||
check_result = check_result & ((o_last_tdata_arg == o_last_tdata) !== 0);
|
||||
check_result = check_result & ((error_arg == error) != 0);
|
||||
|
||||
if (check_result) begin
|
||||
$display ("... Passed");
|
||||
end else begin
|
||||
$display ("... FAILED!!!");
|
||||
$display ("o_xfer_count = %d (Expected %d)",o_xfer_count,o_xfer_count_arg);
|
||||
$display ("i_xfer_count = %d (Expected %d)",i_xfer_count,i_xfer_count_arg);
|
||||
$display ("o_last_tdata = %h (Expected %h)",o_last_tdata,o_last_tdata_arg);
|
||||
$display ("error = %d (Expected %d)",error,error_arg);
|
||||
end
|
||||
|
||||
//Reset vars
|
||||
o_xfer_count = 0;
|
||||
i_xfer_count = 0;
|
||||
o_last_tdata = 64'h0;
|
||||
end
|
||||
endfunction
|
||||
|
||||
task send_packet;
|
||||
input [63:0] data_start;
|
||||
input [31:0] len;
|
||||
|
||||
begin
|
||||
if(len < 9) begin
|
||||
{i_tlast, i_tdata} <= { 1'b1, data_start[63:48],len[15:0], data_start[31:0] };
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
i_tvalid <= 0;
|
||||
end else begin
|
||||
{i_tlast, i_tdata} <= { 1'b0, data_start[63:48],len[15:0], data_start[31:0] };
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
repeat(((len-1)/8)-1) begin
|
||||
i_tdata <= i_tdata + 64'h0000_0002_0000_0002;
|
||||
@(posedge clk);
|
||||
end
|
||||
i_tdata <= i_tdata + 64'h0000_0002_0000_0002;
|
||||
i_tlast <= 1;
|
||||
@(posedge clk);
|
||||
i_tvalid <= 0;
|
||||
end // else: !if(len < 3)
|
||||
end
|
||||
endtask // send_packet
|
||||
|
||||
task reset_quantum_atomic;
|
||||
input [15:0] quant;
|
||||
begin
|
||||
quantum <= quant;
|
||||
clear <= 1;
|
||||
@(posedge clk);
|
||||
clear <= 0;
|
||||
@(posedge clk);
|
||||
end
|
||||
endtask // reset_quantum_atomic
|
||||
|
||||
|
||||
initial begin
|
||||
#100 reset = 0;
|
||||
#200000;
|
||||
$finish;
|
||||
end
|
||||
|
||||
reg [63:0] i_tdata;
|
||||
reg i_tlast;
|
||||
reg i_tvalid;
|
||||
wire i_tready;
|
||||
|
||||
wire [63:0] o_tdata;
|
||||
wire o_tlast, o_tvalid, o_tready;
|
||||
wire error;
|
||||
|
||||
initial begin
|
||||
quantum <= 256;
|
||||
i_tvalid <= 0;
|
||||
while(reset) @(posedge clk);
|
||||
|
||||
$write ("Running test case: First packet after reset");
|
||||
send_packet(64'h00000001_00000000, 128);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
check_result(256,16,64'hFFFFFFFF_FFFFFFFF,0);
|
||||
|
||||
reset_quantum_atomic(8);
|
||||
|
||||
$write ("Running test case: sizeof(packet) < quantum");
|
||||
send_packet(64'h00000001_00000000, 40);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
check_result(8,5,64'hFFFFFFFF_FFFFFFFF,0);
|
||||
|
||||
reset_quantum_atomic(5);
|
||||
|
||||
$write ("Running test case: sizeof(packet) == quantum");
|
||||
send_packet(64'h00000001_00000000, 40);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
check_result(5,5,64'h00000030_00000008,0);
|
||||
|
||||
$write ("Running test case: sizeof(packet) == quantum - 64bits");
|
||||
send_packet(64'h00000001_00000000, 32);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
check_result(5,4,64'hFFFFFFFF_FFFFFFFF,0);
|
||||
|
||||
$write ("Running test case: sizeof(packet) == quantum + 64bits");
|
||||
send_packet(64'h00000001_00000000, 48);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
check_result(32'hxxxxxxxx,32'hxxxxxxxx,64'hxxxxxxxx_xxxxxxxx,1);
|
||||
|
||||
$write ("Running test case: Error reset");
|
||||
reset_quantum_atomic(8);
|
||||
check_result(32'hxxxxxxxx,32'hxxxxxxxx,64'hxxxxxxxx_xxxxxxxx,0);
|
||||
|
||||
$write ("Running test case: sizeof(packet) > quantum");
|
||||
send_packet(64'h00000001_00000000, 80);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
check_result(32'hxxxxxxxx,32'hxxxxxxxx,64'hxxxxxxxx_xxxxxxxx,1);
|
||||
|
||||
reset_quantum_atomic(8);
|
||||
|
||||
$write ("Running test case: sizeof(packet) == 2");
|
||||
send_packet(64'h00000001_00000000, 8);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
check_result(8,1,64'hFFFFFFFF_FFFFFFFF,0);
|
||||
|
||||
$write ("Running test case: Multiple packets back-to-back");
|
||||
send_packet(64'h00000001_00000000, 40);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
send_packet(64'h00000001_00000000, 16);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
send_packet(64'h00000001_00000000, 64);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
check_result(24,15,64'h0000004e0000000e,0);
|
||||
|
||||
end // initial begin
|
||||
|
||||
|
||||
cvita_chunker dut (
|
||||
.clk(clk), .reset(reset), .clear(clear), .frame_size(quantum),
|
||||
.i_tdata(i_tdata), .i_tlast(i_tlast), .i_tvalid(i_tvalid), .i_tready(i_tready),
|
||||
.o_tdata(o_tdata), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready),
|
||||
.error(error));
|
||||
|
||||
assign o_tready = 1;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (o_tvalid & o_tready) begin
|
||||
o_xfer_count <= o_xfer_count + 1;
|
||||
o_last_tdata <= o_tdata;
|
||||
end
|
||||
if (i_tvalid & i_tready) i_xfer_count <= i_xfer_count + 1;
|
||||
end
|
||||
|
||||
endmodule // cvita_chunker_tb
|
||||
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
module cvita_dechunker # (
|
||||
parameter PAD_VALUE = 64'hFFFFFFFF_FFFFFFFF
|
||||
) (
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
input [15:0] frame_size,
|
||||
|
||||
input [63:0] i_tdata,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
|
||||
output [63:0] o_tdata,
|
||||
output o_tlast,
|
||||
output o_tvalid,
|
||||
input o_tready,
|
||||
|
||||
output error
|
||||
);
|
||||
|
||||
localparam ST_HEADER = 2'd0;
|
||||
localparam ST_DATA = 2'd1;
|
||||
localparam ST_PADDING = 2'd2;
|
||||
localparam ST_ERROR = 2'd3;
|
||||
|
||||
reg [1:0] state;
|
||||
reg [15:0] frame_rem, pkt_rem;
|
||||
wire i_tlast;
|
||||
|
||||
wire [15:0] cvita_len_ceil = i_tdata[47:32] + 7;
|
||||
wire [15:0] axi_len = {3'b000, cvita_len_ceil[15:3]};
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (reset | clear) begin
|
||||
state <= ST_HEADER;
|
||||
frame_rem <= 16'd0;
|
||||
pkt_rem <= 16'd0;
|
||||
end else if (i_tvalid & i_tready) begin
|
||||
case (state)
|
||||
ST_HEADER: begin
|
||||
if (axi_len > frame_size)
|
||||
state <= ST_ERROR;
|
||||
else if (~o_tlast)
|
||||
state <= ST_DATA;
|
||||
else
|
||||
state <= ST_PADDING;
|
||||
|
||||
frame_rem <= frame_size - 16'd1;
|
||||
pkt_rem <= axi_len - 16'd1;
|
||||
end
|
||||
|
||||
ST_DATA: begin
|
||||
if (o_tlast) begin
|
||||
state <= i_tlast ? ST_HEADER : ST_PADDING;
|
||||
pkt_rem <= 16'd0;
|
||||
end else begin
|
||||
state <= ST_DATA;
|
||||
pkt_rem <= pkt_rem - 16'd1;
|
||||
end
|
||||
frame_rem <= frame_rem - 16'd1;
|
||||
end
|
||||
|
||||
ST_PADDING: begin
|
||||
if (i_tlast) begin
|
||||
state <= ST_HEADER;
|
||||
frame_rem <= 16'd0;
|
||||
end else begin
|
||||
state <= ST_PADDING;
|
||||
frame_rem <= frame_rem - 16'd1;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
assign i_tready = o_tready | (state == ST_PADDING);
|
||||
assign i_tlast = (frame_rem == 16'd1); //Temp signal
|
||||
|
||||
assign o_tvalid = i_tvalid & (state != ST_PADDING);
|
||||
assign o_tlast = (pkt_rem != 0) ? (pkt_rem == 16'd1) : (axi_len == 16'd1);
|
||||
assign o_tdata = i_tdata;
|
||||
|
||||
assign error = (state == ST_ERROR);
|
||||
|
||||
endmodule // cvita_dechunker
|
||||
@@ -0,0 +1,183 @@
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
`timescale 500ps/1ps
|
||||
|
||||
module cvita_dechunker_tb();
|
||||
|
||||
// TB stimulus
|
||||
reg clk = 0;
|
||||
reg reset = 1;
|
||||
reg clear = 0;
|
||||
reg [15:0] quantum;
|
||||
wire error;
|
||||
|
||||
// Check vars
|
||||
reg [31:0] o_xfer_count = 0, i_xfer_count = 0;
|
||||
reg [63:0] o_last_tdata = 0;
|
||||
|
||||
|
||||
always #10 clk = ~clk;
|
||||
|
||||
initial $dumpfile("cvita_dechunker_tb.vcd");
|
||||
initial $dumpvars(0,cvita_dechunker_tb);
|
||||
|
||||
function check_result;
|
||||
input [31:0] o_xfer_count_arg;
|
||||
input [31:0] i_xfer_count_arg;
|
||||
input [63:0] o_last_tdata_arg;
|
||||
input error_arg;
|
||||
begin
|
||||
//Check vars
|
||||
check_result = 1;
|
||||
check_result = check_result & ((o_xfer_count_arg == o_xfer_count) !== 0);
|
||||
check_result = check_result & ((i_xfer_count_arg == i_xfer_count) !== 0);
|
||||
check_result = check_result & ((o_last_tdata_arg == o_last_tdata) !== 0);
|
||||
check_result = check_result & ((error_arg == error) != 0);
|
||||
|
||||
if (check_result) begin
|
||||
$display ("... Passed");
|
||||
end else begin
|
||||
$display ("... FAILED!!!");
|
||||
$display ("o_xfer_count = %d (Expected %d)",o_xfer_count,o_xfer_count_arg);
|
||||
$display ("i_xfer_count = %d (Expected %d)",i_xfer_count,i_xfer_count_arg);
|
||||
$display ("o_last_tdata = %h (Expected %h)",o_last_tdata,o_last_tdata_arg);
|
||||
$display ("error = %d (Expected %d)",error,error_arg);
|
||||
end
|
||||
|
||||
//Reset vars
|
||||
o_xfer_count = 0;
|
||||
i_xfer_count = 0;
|
||||
o_last_tdata = 64'h0;
|
||||
end
|
||||
endfunction
|
||||
|
||||
task send_packet;
|
||||
input [63:0] data_start;
|
||||
input [31:0] len;
|
||||
input [31:0] quant;
|
||||
|
||||
begin
|
||||
if(quant < 2) begin
|
||||
{i_tlast, i_tdata} <= { 1'b1, data_start[63:48],len[15:0], data_start[31:0] };
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
i_tvalid <= 0;
|
||||
end else begin
|
||||
{i_tlast, i_tdata} <= { 1'b0, data_start[63:48],len[15:0], data_start[31:0] };
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
repeat(quant - 2) begin
|
||||
i_tdata <= i_tdata + 64'h0000_0002_0000_0002;
|
||||
@(posedge clk);
|
||||
end
|
||||
i_tdata <= i_tdata + 64'h0000_0002_0000_0002;
|
||||
i_tlast <= 1;
|
||||
@(posedge clk);
|
||||
i_tvalid <= 1'b0;
|
||||
end // else: !if(len < 3)
|
||||
end
|
||||
endtask // send_packet
|
||||
|
||||
task reset_quantum_atomic;
|
||||
input [15:0] quant;
|
||||
begin
|
||||
quantum <= quant;
|
||||
clear <= 1;
|
||||
@(posedge clk);
|
||||
clear <= 0;
|
||||
@(posedge clk);
|
||||
end
|
||||
endtask // reset_quantum_atomic
|
||||
|
||||
|
||||
initial begin
|
||||
#100 reset = 0;
|
||||
#200000;
|
||||
$finish;
|
||||
end
|
||||
|
||||
reg [63:0] i_tdata;
|
||||
reg i_tlast;
|
||||
reg i_tvalid;
|
||||
wire i_tready;
|
||||
|
||||
wire [63:0] o_tdata;
|
||||
wire o_tlast, o_tvalid, o_tready;
|
||||
|
||||
initial begin
|
||||
quantum <= 8;
|
||||
i_tvalid <= 0;
|
||||
while(reset) @(posedge clk);
|
||||
|
||||
$write ("Running test case: First packet after reset");
|
||||
send_packet(64'h00000001_00000000, 32, 8);
|
||||
@(posedge clk);
|
||||
check_result(4,8,64'hxxxxxxxx_xxxxxx06, 0);
|
||||
|
||||
reset_quantum_atomic(10);
|
||||
|
||||
$write ("Running test case: sizeof(packet) < quantum");
|
||||
send_packet(64'h00000001_00000000, 64, 10);
|
||||
@(posedge clk);
|
||||
check_result(8,10,64'hxxxxxxxx_xxxxxx0e, 0);
|
||||
|
||||
$write ("Running test case: sizeof(packet) == quantum");
|
||||
send_packet(64'h00000001_00000000, 80, 10);
|
||||
@(posedge clk);
|
||||
check_result(10,10,64'hxxxxxxxx_xxxxxx12, 0);
|
||||
|
||||
$write ("Running test case: sizeof(packet) == quantum - 64bits");
|
||||
send_packet(64'h00000001_00000000, 72, 10);
|
||||
@(posedge clk);
|
||||
check_result(9,10,64'hxxxxxxxx_xxxxxx10, 0);
|
||||
|
||||
$write ("Running test case: sizeof(packet) == quantum + 64bits");
|
||||
send_packet(64'h00000001_00000000, 88, 10);
|
||||
@(posedge clk);
|
||||
check_result(32'hxxxxxxxx,10,64'hxxxxxxxx_xxxxxxxx, 1);
|
||||
|
||||
reset_quantum_atomic(10);
|
||||
|
||||
$write ("Running test case: sizeof(packet) > quantum");
|
||||
send_packet(64'h00000001_00000000, 88, 10);
|
||||
@(posedge clk);
|
||||
check_result(32'hxxxxxxxx,10,64'hxxxxxxxx_xxxxxxxx, 1);
|
||||
|
||||
reset_quantum_atomic(8);
|
||||
|
||||
$write ("Running test case: sizeof(packet) == 2");
|
||||
send_packet(64'h00000001_00000000, 8, 8);
|
||||
@(posedge clk);
|
||||
check_result(1,8,64'hxxxxxxxx_xxxxxx00, 0);
|
||||
|
||||
$write ("Running test case: Multiple packets");
|
||||
send_packet(64'h00000001_00000000, 8, 8);
|
||||
send_packet(64'h00000001_00000000, 16, 8);
|
||||
send_packet(64'h00000001_00000000, 24, 8);
|
||||
send_packet(64'h00000001_00000000, 32, 8);
|
||||
@(posedge clk);
|
||||
check_result(10,32,64'hxxxxxxxx_xxxxxx06, 0);
|
||||
|
||||
end // initial begin
|
||||
|
||||
|
||||
cvita_dechunker dut (
|
||||
.clk(clk), .reset(reset), .clear(clear), .frame_size(quantum),
|
||||
.i_tdata(i_tdata), .i_tvalid(i_tvalid), .i_tready(i_tready),
|
||||
.o_tdata(o_tdata), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready),
|
||||
.error(error));
|
||||
|
||||
assign o_tready = 1;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (o_tvalid & o_tready) begin
|
||||
o_xfer_count <= o_xfer_count + 1;
|
||||
o_last_tdata <= o_tdata;
|
||||
end
|
||||
if (i_tvalid & i_tready) i_xfer_count <= i_xfer_count + 1;
|
||||
end
|
||||
|
||||
endmodule // cvita_dechunker_tb
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
// Map the endpoint dest part of the SID in the CVITA header to a destination
|
||||
// This destination (o_tdest) signal will be valid with o_tdata
|
||||
// This only works with VALID CVITA frames
|
||||
|
||||
module cvita_dest_lookup
|
||||
#(
|
||||
parameter DEST_WIDTH = 4
|
||||
)
|
||||
(
|
||||
input clk, input rst,
|
||||
input set_stb, input [7:0] set_addr, input [DEST_WIDTH-1:0] set_data,
|
||||
input [63:0] i_tdata, input i_tlast, input i_tvalid, output i_tready,
|
||||
output [63:0] o_tdata, output o_tlast, output o_tvalid, input o_tready,
|
||||
output [DEST_WIDTH-1:0] o_tdest
|
||||
);
|
||||
|
||||
reg [7:0] endpoint;
|
||||
ram_2port #(.DWIDTH(DEST_WIDTH), .AWIDTH(8)) dest_lut
|
||||
(
|
||||
.clka(clk), .ena(1'b1), .wea(set_stb), .addra(set_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(endpoint), .dib(8'hff), .dob(o_tdest)
|
||||
);
|
||||
|
||||
reg forward;
|
||||
reg [1:0] count;
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
forward <= 1'b0;
|
||||
count <= 2'b0;
|
||||
end
|
||||
else if (forward == 1'b0 && i_tvalid) begin
|
||||
if (count == 2'b11) forward <= 1'b1;
|
||||
endpoint <= i_tdata[23:16];
|
||||
count <= count + 1'b1;
|
||||
end
|
||||
else if (forward == 1'b1 && i_tvalid && i_tready && i_tlast) begin
|
||||
forward <= 1'b0;
|
||||
count <= 2'b0;
|
||||
end
|
||||
end
|
||||
|
||||
assign o_tdata = i_tdata;
|
||||
assign o_tlast = i_tlast;
|
||||
assign o_tvalid = i_tvalid && forward;
|
||||
assign i_tready = o_tready && forward;
|
||||
|
||||
endmodule // cvita_dest_lookup
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
// Insert tlast bit for fifos that don't support it. This only works with VALID CVITA frames
|
||||
// A single partial or invalid frame will make this wrong FOREVER
|
||||
|
||||
module cvita_insert_tlast
|
||||
(input clk, input reset, input clear,
|
||||
input [63:0] i_tdata, input i_tvalid, output i_tready,
|
||||
output [63:0] o_tdata, output o_tlast, output o_tvalid, input o_tready);
|
||||
|
||||
assign o_tdata = i_tdata;
|
||||
assign o_tvalid = i_tvalid;
|
||||
assign i_tready = o_tready;
|
||||
|
||||
wire [15:0] cvita_len_ceil = i_tdata[47:32] + 7;
|
||||
wire [15:0] axi_len = {3'b000, cvita_len_ceil[15:3]};
|
||||
|
||||
reg [15:0] count;
|
||||
|
||||
assign o_tlast = (count != 0) ? (count == 16'd1) : (axi_len == 16'd1);
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
begin
|
||||
count <= 16'd0;
|
||||
end
|
||||
else
|
||||
if(i_tready & i_tvalid)
|
||||
if(count != 16'd0)
|
||||
count <= count - 16'd1;
|
||||
else
|
||||
count <= axi_len - 16'd1;
|
||||
|
||||
endmodule // cvita_insert_tlast
|
||||
@@ -0,0 +1,92 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module cvita_insert_tlast_tb();
|
||||
|
||||
reg clk = 0;
|
||||
reg reset = 1;
|
||||
|
||||
always #10 clk = ~clk;
|
||||
|
||||
initial $dumpfile("cvita_insert_tlast_tb.vcd");
|
||||
initial $dumpvars(0,cvita_insert_tlast_tb);
|
||||
|
||||
task send_packet;
|
||||
input [63:0] data_start;
|
||||
input [31:0] len;
|
||||
|
||||
begin
|
||||
if(len < 9)
|
||||
begin
|
||||
{i_tlast, i_tdata} <= { 1'b1, data_start[63:48],len[15:0], data_start[31:0] };
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
i_tvalid <= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
{i_tlast, i_tdata} <= { 1'b0, data_start[63:48],len[15:0], data_start[31:0] };
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
repeat(((len-2)/2)-1+len[0])
|
||||
begin
|
||||
i_tdata <= i_tdata + 64'h0000_0002_0000_0002;
|
||||
@(posedge clk);
|
||||
end
|
||||
i_tdata <= i_tdata + 64'h0000_0002_0000_0002;
|
||||
i_tlast <= 1;
|
||||
@(posedge clk);
|
||||
i_tvalid <= 1'b0;
|
||||
end // else: !if(len < 3)
|
||||
end
|
||||
endtask // send_packet
|
||||
|
||||
initial
|
||||
begin
|
||||
#1000 reset = 0;
|
||||
#200000;
|
||||
$finish;
|
||||
end
|
||||
|
||||
reg [63:0] i_tdata;
|
||||
reg i_tlast;
|
||||
reg i_tvalid;
|
||||
wire i_tready;
|
||||
|
||||
wire [63:0] o_tdata;
|
||||
wire o_tlast, o_tvalid, o_tready, o_tlast_regen;
|
||||
|
||||
initial
|
||||
begin
|
||||
i_tvalid <= 0;
|
||||
|
||||
while(reset)
|
||||
@(posedge clk);
|
||||
@(posedge clk);
|
||||
|
||||
send_packet(64'hA0000000_A0000001, 24);
|
||||
send_packet(64'hA0000000_A0000001, 20);
|
||||
send_packet(64'hA0000000_A0000001, 16);
|
||||
send_packet(64'hA0000000_A0000001, 12);
|
||||
send_packet(64'hA0000000_A0000001, 8);
|
||||
send_packet(64'hA0000000_A0000001, 4);
|
||||
send_packet(64'hA0000000_A0000001, 4);
|
||||
send_packet(64'hA0000000_A0000001, 8);
|
||||
send_packet(64'hA0000000_A0000001, 12);
|
||||
end // initial begin
|
||||
|
||||
cvita_insert_tlast dut
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata(i_tdata), .i_tvalid(i_tvalid), .i_tready(i_tready),
|
||||
.o_tdata(o_tdata), .o_tlast(o_tlast_regen), .o_tvalid(o_tvalid), .o_tready(o_tready));
|
||||
|
||||
assign o_tready = 1;
|
||||
|
||||
|
||||
always @(posedge clk)
|
||||
if(o_tvalid & o_tready)
|
||||
begin
|
||||
$display ("TLAST %x\t TLAST_REGEN %x",i_tlast, o_tlast_regen);
|
||||
if(i_tlast != o_tlast_regen)
|
||||
$display("ERROR!!!!!!");
|
||||
end
|
||||
endmodule // cvita_insert_tlast_tb
|
||||
@@ -0,0 +1,573 @@
|
||||
|
||||
// Ethernet dispatcher
|
||||
// Incoming ethernet packets are examined and sent to the correct destination
|
||||
// There are 3 destinations, ZPU, other ethernet port (out), and vita router
|
||||
// Packets going to the vita router will have the ethernet/ip/udp headers stripped off.
|
||||
//
|
||||
// To make things simpler, we start out by sending all packets to zpu and out port.
|
||||
// By the end of the eth/ip/udp headers, we can determine where the correct destination is.
|
||||
// If the correct destination is vita, we send an error indication on the zpu and out ports,
|
||||
// which will cause the axi_packet_gate to drop those packets, and send the vita frame to
|
||||
// the vita port.
|
||||
//
|
||||
// If at the end of the headers we determine the packet should go to zpu, then we send an
|
||||
// error indication on the out port, the rest of the packet to zpu and nothing on vita.
|
||||
// If it should go to out, we send the error indication to zpu, the rest of the packet to out,
|
||||
// and nothing on vita.
|
||||
//
|
||||
// Downstream we should have adequate fifo space, otherwise we could get backed up here.
|
||||
//
|
||||
// No tuser bits sent to vita, as vita assumes there are no errors and that occupancy is
|
||||
// indicated by the length field of the vita header.
|
||||
|
||||
//
|
||||
// Rules for forwarding:
|
||||
//
|
||||
// Ethernet Broadcast (Dst MAC = ff:ff:ff:ff:ff:ff). Forward to both ZPU and XO MAC.
|
||||
// ? Ethernet Multicast (Dst MAC = USRP_NEXT_HOP). Forward only to ZPU.
|
||||
// ? Ethernet Multicast (Dst MAC = Unknown). Forward only to XO.
|
||||
// Ethernet Unicast (Dst MAC = Unknown). Forward only to XO.
|
||||
// Ethernet Unicast (Dst MAC = local). Look deeper......
|
||||
// IP Broadcast. Forward to both ZPU and XO MAC. (Should be coverd by Eth broadcast)
|
||||
// IP Multicast. ? Unknow Action.
|
||||
// IP Unicast (Dst IP = local). Look deeper....
|
||||
// UDP (Port = Listed) and its a VRLP packet. Forward only to VITA Radio Core.
|
||||
// UDP (Port = Unknown). Forward only to ZPU.
|
||||
//
|
||||
//
|
||||
|
||||
module eth_dispatch
|
||||
#(parameter BASE=0)
|
||||
(
|
||||
// Clocking and reset interface
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
// Setting register interface
|
||||
input set_stb,
|
||||
input [15:0] set_addr,
|
||||
input [31:0] set_data,
|
||||
// Input 68bit AXI-Stream interface (from MAC)
|
||||
input [63:0] in_tdata,
|
||||
input [3:0] in_tuser,
|
||||
input in_tlast,
|
||||
input in_tvalid,
|
||||
output in_tready,
|
||||
// Output AXI-STream interface to VITA Radio Core
|
||||
output [63:0] vita_tdata,
|
||||
output [3:0] vita_tuser,
|
||||
output vita_tlast,
|
||||
output vita_tvalid,
|
||||
input vita_tready,
|
||||
// Output AXI-Stream interface to ZPU
|
||||
output [63:0] zpu_tdata,
|
||||
output [3:0] zpu_tuser,
|
||||
output zpu_tlast,
|
||||
output zpu_tvalid,
|
||||
input zpu_tready,
|
||||
// Output AXI-Stream interface to cross-over MAC
|
||||
output [63:0] xo_tdata,
|
||||
output [3:0] xo_tuser,
|
||||
output xo_tlast,
|
||||
output xo_tvalid,
|
||||
input xo_tready,
|
||||
// Debug
|
||||
output [31:0] debug
|
||||
);
|
||||
|
||||
//
|
||||
// State machine declarations
|
||||
//
|
||||
reg [2:0] state;
|
||||
|
||||
localparam WAIT_PACKET = 0;
|
||||
localparam READ_HEADER = 1;
|
||||
localparam FORWARD_ZPU = 2;
|
||||
localparam FORWARD_ZPU_AND_XO = 3;
|
||||
localparam FORWARD_XO = 4;
|
||||
localparam FORWARD_RADIO_CORE = 5;
|
||||
localparam DROP_PACKET = 6;
|
||||
localparam CLASSIFY_PACKET = 7;
|
||||
|
||||
|
||||
//
|
||||
// Small RAM stores packet header during parsing.
|
||||
//
|
||||
localparam HEADER_RAM_SIZE = 9;
|
||||
(*ram_style="distributed"*)
|
||||
reg [68:0] header_ram [HEADER_RAM_SIZE-1:0];
|
||||
reg [3:0] header_ram_addr;
|
||||
reg drop_this_packet;
|
||||
|
||||
wire header_done = (header_ram_addr == HEADER_RAM_SIZE-1);
|
||||
reg fwd_input;
|
||||
|
||||
//
|
||||
reg [63:0] in_tdata_reg;
|
||||
|
||||
//
|
||||
wire out_tvalid;
|
||||
wire out_tready;
|
||||
wire out_tlast;
|
||||
wire [3:0] out_tuser;
|
||||
wire [63:0] out_tdata;
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Output AXI-STream interface to VITA Radio Core
|
||||
wire [63:0] vita_pre_tdata;
|
||||
wire [3:0] vita_pre_tuser;
|
||||
wire vita_pre_tlast;
|
||||
wire vita_pre_tvalid;
|
||||
wire vita_pre_tready;
|
||||
// Output AXI-Stream interface to ZPU
|
||||
wire [63:0] zpu_pre_tdata;
|
||||
wire [3:0] zpu_pre_tuser;
|
||||
wire zpu_pre_tlast;
|
||||
wire zpu_pre_tvalid;
|
||||
wire zpu_pre_tready;
|
||||
// Output AXI-Stream interface to cross-over MAC
|
||||
wire [63:0] xo_pre_tdata;
|
||||
wire [3:0] xo_pre_tuser;
|
||||
wire xo_pre_tlast;
|
||||
wire xo_pre_tvalid;
|
||||
wire xo_pre_tready;
|
||||
|
||||
//
|
||||
// Packet Parse Flags
|
||||
//
|
||||
reg is_eth_dst_addr;
|
||||
reg is_eth_broadcast;
|
||||
reg is_eth_type_ipv4;
|
||||
reg is_ipv4_dst_addr;
|
||||
reg is_ipv4_proto_udp;
|
||||
reg is_ipv4_proto_icmp;
|
||||
reg [1:0] is_udp_dst_ports;
|
||||
reg is_icmp_no_fwd;
|
||||
reg is_chdr;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Settings regs
|
||||
//
|
||||
|
||||
wire [47:0] my_mac;
|
||||
|
||||
setting_reg #(.my_addr(BASE), .awidth(16), .width(32)) sr_my_mac_lsb
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out(my_mac[31:0]),.changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+1), .awidth(16), .width(16)) sr_my_mac_msb
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out(my_mac[47:32]),.changed());
|
||||
|
||||
wire [31:0] my_ip;
|
||||
|
||||
setting_reg #(.my_addr(BASE+2), .awidth(16), .width(32)) sr_my_ip
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out(my_ip[31:0]),.changed());
|
||||
|
||||
wire [15:0] my_port0, my_port1;
|
||||
|
||||
setting_reg #(.my_addr(BASE+3), .awidth(16), .width(32)) sr_udp_port
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out({my_port1[15:0],my_port0[15:0]}),.changed());
|
||||
|
||||
wire forward_ndest, forward_bcast;
|
||||
setting_reg #(.my_addr(BASE+4), .awidth(16), .width(2)) sr_forward_ctrl
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out({forward_ndest, forward_bcast}),.changed());
|
||||
|
||||
wire [7:0] my_icmp_type, my_icmp_code;
|
||||
setting_reg #(.my_addr(BASE+5), .awidth(16), .width(16)) sr_icmp_ctrl
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out({my_icmp_type, my_icmp_code}),.changed());
|
||||
|
||||
assign debug =
|
||||
{
|
||||
1'b0, state, //4
|
||||
1'b0, in_tvalid, in_tready, in_tlast, //4
|
||||
1'b0, is_eth_dst_addr, is_eth_broadcast, is_eth_type_ipv4,
|
||||
is_ipv4_dst_addr, is_ipv4_proto_udp, is_udp_dst_ports, //8
|
||||
header_ram_addr[3:0], //4
|
||||
4'b0, 8'b0
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Packet Forwarding State machine.
|
||||
//
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset || clear) begin
|
||||
state <= WAIT_PACKET;
|
||||
header_ram_addr <= 0;
|
||||
drop_this_packet <= 0;
|
||||
fwd_input <= 0;
|
||||
end else begin
|
||||
// Defaults.
|
||||
drop_this_packet <= 0;
|
||||
|
||||
case(state)
|
||||
//
|
||||
// Wait for start of a packet
|
||||
// IJB: Add protection for a premature EOF here
|
||||
//
|
||||
WAIT_PACKET: begin
|
||||
if (in_tvalid && in_tready) begin
|
||||
header_ram[header_ram_addr] <= {in_tlast,in_tuser,in_tdata};
|
||||
header_ram_addr <= header_ram_addr + 1;
|
||||
state <= READ_HEADER;
|
||||
end
|
||||
fwd_input <= 0;
|
||||
end
|
||||
//
|
||||
// Continue to read full packet header into RAM.
|
||||
//
|
||||
READ_HEADER: begin
|
||||
if (in_tvalid && in_tready) begin
|
||||
header_ram[header_ram_addr] <= {in_tlast,in_tuser,in_tdata};
|
||||
// Have we reached end of fields we parse in header or got a short packet?
|
||||
if (header_done || in_tlast) begin
|
||||
// Make decision about where this packet is forwarded to.
|
||||
state <= CLASSIFY_PACKET;
|
||||
end // if (header_done || in_tlast)
|
||||
else begin
|
||||
header_ram_addr <= header_ram_addr + 1;
|
||||
state <= READ_HEADER;
|
||||
end // else: !if(header_done || in_tlast)
|
||||
end // if (in_tvalid && in_tready)
|
||||
end // case: READ_HEADER
|
||||
|
||||
//
|
||||
// Classify Packet
|
||||
//
|
||||
CLASSIFY_PACKET: begin
|
||||
// Make decision about where this packet is forwarded to.
|
||||
if (is_eth_type_ipv4 && is_ipv4_proto_icmp && is_icmp_no_fwd) begin
|
||||
header_ram_addr <= 0;
|
||||
state <= FORWARD_ZPU;
|
||||
end else if (is_eth_broadcast) begin
|
||||
header_ram_addr <= 0;
|
||||
state <= forward_bcast? FORWARD_ZPU_AND_XO : FORWARD_ZPU;
|
||||
end else if (!is_eth_dst_addr) begin
|
||||
header_ram_addr <= 0;
|
||||
state <= forward_ndest? FORWARD_XO : DROP_PACKET;
|
||||
end else if ((is_udp_dst_ports != 0) && is_chdr) begin
|
||||
header_ram_addr <= 6; // Jump to CHDR
|
||||
state <= FORWARD_RADIO_CORE;
|
||||
end else if (drop_this_packet) begin
|
||||
header_ram_addr <= HEADER_RAM_SIZE-1;
|
||||
state <= DROP_PACKET;
|
||||
end else begin
|
||||
header_ram_addr <= 0;
|
||||
state <= FORWARD_ZPU;
|
||||
end
|
||||
end // case: CLASSIFY_PACKET
|
||||
|
||||
//
|
||||
// Forward this packet only to local ZPU
|
||||
//
|
||||
FORWARD_ZPU: begin
|
||||
if (out_tvalid && out_tready) begin
|
||||
if (out_tlast) begin
|
||||
state <= WAIT_PACKET;
|
||||
end
|
||||
if (header_done) fwd_input <= 1;
|
||||
header_ram_addr <= out_tlast? 4'b0 : header_ram_addr + 1;
|
||||
end
|
||||
end
|
||||
//
|
||||
// Forward this packet to both local ZPU and XO
|
||||
//
|
||||
FORWARD_ZPU_AND_XO: begin
|
||||
if (out_tvalid && out_tready) begin
|
||||
if (out_tlast) begin
|
||||
state <= WAIT_PACKET;
|
||||
end
|
||||
if (header_done) fwd_input <= 1;
|
||||
header_ram_addr <= out_tlast? 4'b0 : header_ram_addr + 1;
|
||||
end
|
||||
end
|
||||
//
|
||||
// Forward this packet to XO only
|
||||
//
|
||||
FORWARD_XO: begin
|
||||
if (out_tvalid && out_tready) begin
|
||||
if (out_tlast) begin
|
||||
state <= WAIT_PACKET;
|
||||
end
|
||||
if (header_done) fwd_input <= 1;
|
||||
header_ram_addr <= out_tlast? 4'b0 : header_ram_addr + 1;
|
||||
end
|
||||
end
|
||||
//
|
||||
// Forward this packet to the Radio Core only
|
||||
//
|
||||
FORWARD_RADIO_CORE: begin
|
||||
if (out_tvalid && out_tready) begin
|
||||
if (out_tlast) begin
|
||||
state <= WAIT_PACKET;
|
||||
end
|
||||
if (header_done) fwd_input <= 1;
|
||||
header_ram_addr <= out_tlast? 4'b0 : header_ram_addr + 1;
|
||||
end
|
||||
end
|
||||
//
|
||||
// Drop this packet on the ground
|
||||
//
|
||||
DROP_PACKET: begin
|
||||
if (out_tvalid && out_tready) begin
|
||||
if (out_tlast) begin
|
||||
state <= WAIT_PACKET;
|
||||
end
|
||||
if (header_done) fwd_input <= 1;
|
||||
header_ram_addr <= out_tlast? 4'b0 : header_ram_addr + 1;
|
||||
end
|
||||
end
|
||||
endcase // case (state)
|
||||
end // else: !if(reset || clear)
|
||||
|
||||
//
|
||||
// Classifier State machine.
|
||||
// Deep packet inspection during header ingress.
|
||||
//
|
||||
always @(posedge clk)
|
||||
if (reset || clear) begin
|
||||
is_eth_dst_addr <= 1'b0;
|
||||
is_eth_broadcast <= 1'b0;
|
||||
is_eth_type_ipv4 <= 1'b0;
|
||||
is_ipv4_dst_addr <= 1'b0;
|
||||
is_ipv4_proto_udp <= 1'b0;
|
||||
is_ipv4_proto_icmp <= 1'b0;
|
||||
is_udp_dst_ports <= 0;
|
||||
is_icmp_no_fwd <= 0;
|
||||
is_chdr <= 1'b0;
|
||||
|
||||
// space_in_fifo <= 0;
|
||||
// is_there_fifo_space <= 1;
|
||||
// packet_length <= 0;
|
||||
end else if (in_tvalid && in_tready) begin // if (reset || clear)
|
||||
in_tdata_reg <= in_tdata;
|
||||
|
||||
case (header_ram_addr)
|
||||
// Pipelined, so nothing to look at first cycle.
|
||||
// Reset all the flags here.
|
||||
0: begin
|
||||
is_eth_dst_addr <= 1'b0;
|
||||
is_eth_broadcast <= 1'b0;
|
||||
is_eth_type_ipv4 <= 1'b0;
|
||||
is_ipv4_dst_addr <= 1'b0;
|
||||
is_ipv4_proto_udp <= 1'b0;
|
||||
is_ipv4_proto_icmp <= 1'b0;
|
||||
is_udp_dst_ports <= 0;
|
||||
is_icmp_no_fwd <= 0;
|
||||
is_chdr <= 1'b0;
|
||||
end
|
||||
1: begin
|
||||
// Look at upper 16bits of MAC Dst Addr.
|
||||
if (in_tdata_reg[15:0] == 16'hFFFF)
|
||||
is_eth_broadcast <= 1'b1;
|
||||
if (in_tdata_reg[15:0] == my_mac[47:32])
|
||||
is_eth_dst_addr <= 1'b1;
|
||||
end
|
||||
2: begin
|
||||
// Look at lower 32bits of MAC Dst Addr.
|
||||
if (is_eth_broadcast && (in_tdata_reg[63:32] == 32'hFFFFFFFF))
|
||||
is_eth_broadcast <= 1'b1;
|
||||
else
|
||||
is_eth_broadcast <= 1'b0;
|
||||
if (is_eth_dst_addr && (in_tdata_reg[63:32] == my_mac[31:0]))
|
||||
is_eth_dst_addr <= 1'b1;
|
||||
else
|
||||
is_eth_dst_addr <= 1'b0;
|
||||
end // case: 2
|
||||
3: begin
|
||||
// Look at Ethertype
|
||||
if (in_tdata_reg[47:32] == 16'h0800)
|
||||
is_eth_type_ipv4 <= 1'b1;
|
||||
// Extract Packet Length
|
||||
// ADD THIS HERE.
|
||||
end
|
||||
4: begin
|
||||
// Look at protocol enapsulated by IPv4
|
||||
if ((in_tdata_reg[23:16] == 8'h11) && is_eth_type_ipv4)
|
||||
is_ipv4_proto_udp <= 1'b1;
|
||||
if ((in_tdata_reg[23:16] == 8'h01) && is_eth_type_ipv4)
|
||||
is_ipv4_proto_icmp <= 1'b1;
|
||||
end
|
||||
5: begin
|
||||
// Look at IP DST Address.
|
||||
if ((in_tdata_reg[31:0] == my_ip[31:0]) && is_eth_type_ipv4)
|
||||
is_ipv4_dst_addr <= 1'b1;
|
||||
end
|
||||
6: begin
|
||||
// Look at UDP dest port
|
||||
if ((in_tdata_reg[47:32] == my_port0[15:0]) && is_ipv4_proto_udp)
|
||||
is_udp_dst_ports[0] <= 1'b1;
|
||||
if ((in_tdata_reg[47:32] == my_port1[15:0]) && is_ipv4_proto_udp)
|
||||
is_udp_dst_ports[1] <= 1'b1;
|
||||
// Look at ICMP type and code
|
||||
if (in_tdata_reg[63:48] == {my_icmp_type, my_icmp_code} && is_ipv4_proto_icmp)
|
||||
is_icmp_no_fwd <= 1'b1;
|
||||
end
|
||||
7: begin
|
||||
// Look for a possible CHDR header string
|
||||
if (in_tdata_reg[63:32] != 32'h0)
|
||||
is_chdr <= 1'b1;
|
||||
end
|
||||
8: begin
|
||||
// Check VRT Stream ID
|
||||
// ADD THIS HERE.
|
||||
end
|
||||
endcase // case (header_ram_addr)
|
||||
end // if (in_tvalid && in_tready)
|
||||
|
||||
|
||||
//
|
||||
// Output (Egress) Interface muxing
|
||||
//
|
||||
assign out_tready =
|
||||
(state == DROP_PACKET) ||
|
||||
((state == FORWARD_RADIO_CORE) && vita_pre_tready) ||
|
||||
((state == FORWARD_XO) && xo_pre_tready) ||
|
||||
((state == FORWARD_ZPU) && zpu_pre_tready) ||
|
||||
((state == FORWARD_ZPU_AND_XO) && zpu_pre_tready && xo_pre_tready);
|
||||
|
||||
assign out_tvalid = ((state == FORWARD_RADIO_CORE) ||
|
||||
(state == FORWARD_XO) ||
|
||||
(state == FORWARD_ZPU) ||
|
||||
(state == FORWARD_ZPU_AND_XO) ||
|
||||
(state == DROP_PACKET)) && (!fwd_input || in_tvalid);
|
||||
|
||||
assign {out_tlast,out_tuser,out_tdata} = fwd_input ? {in_tlast,in_tuser,in_tdata} : header_ram[header_ram_addr];
|
||||
|
||||
assign in_tready = (state == WAIT_PACKET) ||
|
||||
(state == READ_HEADER) ||
|
||||
(state == DROP_PACKET) ||
|
||||
(out_tready && fwd_input);
|
||||
|
||||
|
||||
// Because we can forward to both the ZPU and XO FIFO's concurrently
|
||||
// we have to make sure both can accept data in the same cycle.
|
||||
// This makes it possible for either destination to block the other.
|
||||
assign xo_pre_tvalid = out_tvalid &&
|
||||
((state == FORWARD_XO) ||
|
||||
((state == FORWARD_ZPU_AND_XO) && zpu_pre_tready));
|
||||
assign zpu_pre_tvalid = out_tvalid &&
|
||||
((state == FORWARD_ZPU) ||
|
||||
((state == FORWARD_ZPU_AND_XO) && xo_pre_tready));
|
||||
assign vita_pre_tvalid = out_tvalid && (state == FORWARD_RADIO_CORE);
|
||||
|
||||
assign {zpu_pre_tuser,zpu_pre_tdata} = ((state == FORWARD_ZPU_AND_XO) || (state == FORWARD_ZPU)) ?
|
||||
{out_tuser,out_tdata} : 0;
|
||||
|
||||
assign {xo_pre_tuser,xo_pre_tdata} = ((state == FORWARD_ZPU_AND_XO) || (state == FORWARD_XO)) ?
|
||||
{out_tuser,out_tdata} : 0;
|
||||
|
||||
assign {vita_pre_tuser,vita_pre_tdata} = (state == FORWARD_RADIO_CORE) ? {out_tuser,out_tdata} : 0;
|
||||
|
||||
assign zpu_pre_tlast = out_tlast && ((state == FORWARD_ZPU) || (state == FORWARD_ZPU_AND_XO));
|
||||
|
||||
assign xo_pre_tlast = out_tlast && ((state == FORWARD_XO) || (state == FORWARD_ZPU_AND_XO));
|
||||
|
||||
assign vita_pre_tlast = out_tlast && (state == FORWARD_RADIO_CORE);
|
||||
|
||||
//
|
||||
// Egress FIFO's (Large)
|
||||
//
|
||||
axi_fifo #(.WIDTH(69),.SIZE(10))
|
||||
axi_fifo_zpu (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.i_tdata({zpu_pre_tlast,zpu_pre_tuser,zpu_pre_tdata}),
|
||||
.i_tvalid(zpu_pre_tvalid),
|
||||
.i_tready(zpu_pre_tready),
|
||||
.o_tdata({zpu_tlast,zpu_tuser,zpu_tdata}),
|
||||
.o_tvalid(zpu_tvalid),
|
||||
.o_tready(zpu_tready),
|
||||
.space(),
|
||||
.occupied()
|
||||
);
|
||||
|
||||
axi_fifo #(.WIDTH(69),.SIZE(10))
|
||||
axi_fifo_xo (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.i_tdata({xo_pre_tlast,xo_pre_tuser,xo_pre_tdata}),
|
||||
.i_tvalid(xo_pre_tvalid),
|
||||
.i_tready(xo_pre_tready),
|
||||
.o_tdata({xo_tlast,xo_tuser,xo_tdata}),
|
||||
.o_tvalid(xo_tvalid),
|
||||
.o_tready(xo_tready),
|
||||
.space(),
|
||||
.occupied()
|
||||
);
|
||||
|
||||
axi_fifo #(.WIDTH(69),.SIZE(10))
|
||||
axi_fifo_vita (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.i_tdata({vita_pre_tlast,vita_pre_tuser,vita_pre_tdata}),
|
||||
.i_tvalid(vita_pre_tvalid),
|
||||
.i_tready(vita_pre_tready),
|
||||
.o_tdata({vita_tlast,vita_tuser,vita_tdata}),
|
||||
.o_tvalid(vita_tvalid),
|
||||
.o_tready(vita_tready),
|
||||
.space(),
|
||||
.occupied()
|
||||
);
|
||||
|
||||
|
||||
/* -----\/----- EXCLUDED -----\/-----
|
||||
|
||||
wire vready, zready, oready;
|
||||
wire vvalid, zvalid, ovalid;
|
||||
|
||||
reg [2:0] ed_state;
|
||||
localparam ED_IDLE = 3'd0;
|
||||
localparam ED_IN_HDR = 3'd1;
|
||||
localparam ED_VITA = 3'd2;
|
||||
localparam ED_ZPU = 3'd3;
|
||||
localparam ED_OUT = 3'd4;
|
||||
localparam ED_DROP = 3'd5;
|
||||
-----/\----- EXCLUDED -----/\----- */
|
||||
|
||||
// for now, send everything to zpu
|
||||
/*
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
ed_state <= ED_IDLE;
|
||||
else
|
||||
case(ed_state)
|
||||
ED_IDLE:
|
||||
if(vready & zready & oready & in_tvalid)
|
||||
;
|
||||
endcase // case (ed_state)
|
||||
*/
|
||||
|
||||
/* -----\/----- EXCLUDED -----\/-----
|
||||
axi_packet_gate #(.WIDTH(64), .SIZE(10)) vita_gate
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata(in_tdata), .i_tlast(), .i_terror(), .i_tvalid(1'b0), .i_tready(vready),
|
||||
.o_tdata(vita_tdata), .o_tlast(vita_tlast), .o_tvalid(vita_tvalid), .o_tready(vita_tready));
|
||||
|
||||
axi_packet_gate #(.WIDTH(68), .SIZE(10)) zpu_gate
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({in_tuser,in_tdata}), .i_tlast(in_tlast), .i_terror(in_tuser[3]), .i_tvalid(in_tvalid), .i_tready(in_tready),
|
||||
.o_tdata({zpu_tuser,zpu_tdata}), .o_tlast(zpu_tlast), .o_tvalid(zpu_tvalid), .o_tready(zpu_tready));
|
||||
|
||||
axi_packet_gate #(.WIDTH(68), .SIZE(10)) out_gate
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({in_tuser,in_tdata}), .i_tlast(), .i_terror(), .i_tvalid(1'b0), .i_tready(oready),
|
||||
.o_tdata({out_tuser,out_tdata}), .o_tlast(out_tlast), .o_tvalid(out_tvalid), .o_tready(out_tready));
|
||||
-----/\----- EXCLUDED -----/\----- */
|
||||
|
||||
endmodule // eth_dispatch
|
||||
@@ -0,0 +1,311 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
//
|
||||
|
||||
`timescale 1 ps / 1 ps
|
||||
|
||||
module eth_dispatch_tb();
|
||||
|
||||
|
||||
// Clocking and reset interface
|
||||
reg clk;
|
||||
reg reset;
|
||||
reg clear;
|
||||
// Setting register interface
|
||||
reg set_stb;
|
||||
reg [15:0] set_addr;
|
||||
reg [31:0] set_data;
|
||||
// Input 68bit AXI-Stream interface (from MAC)
|
||||
wire [63:0] in_tdata;
|
||||
wire [3:0] in_tuser;
|
||||
wire in_tlast;
|
||||
wire in_tvalid;
|
||||
wire in_tready;
|
||||
// Output AXI-Stream interface to VITA Radio Core
|
||||
wire [63:0] vita_tdata;
|
||||
wire [3:0] vita_tuser;
|
||||
wire vita_tlast;
|
||||
wire vita_tvalid;
|
||||
wire vita_tready;
|
||||
// Output AXI-Stream interface to ZPU
|
||||
wire [63:0] zpu_tdata;
|
||||
wire [3:0] zpu_tuser;
|
||||
wire zpu_tlast;
|
||||
wire zpu_tvalid;
|
||||
wire zpu_tready;
|
||||
// Output AXI-Stream interface to cross-over MAC
|
||||
wire [63:0] xo_tdata;
|
||||
wire [3:0] xo_tuser;
|
||||
wire xo_tlast;
|
||||
wire xo_tvalid;
|
||||
wire xo_tready;
|
||||
|
||||
reg [63:0] data_in;
|
||||
reg [3:0] user_in;
|
||||
reg valid_in;
|
||||
wire ready_in;
|
||||
reg last_in;
|
||||
|
||||
eth_dispatch
|
||||
#(.BASE(0))
|
||||
eth_dispatch_i
|
||||
(
|
||||
// Clocking and reset interface
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
// Setting register interface
|
||||
.set_stb(set_stb),
|
||||
.set_addr(set_addr),
|
||||
.set_data(set_data),
|
||||
// Input 68bit AXI-Stream interface (from MAC)
|
||||
.in_tdata(in_tdata),
|
||||
.in_tuser(in_tuser),
|
||||
.in_tlast(in_tlast),
|
||||
.in_tvalid(in_tvalid),
|
||||
.in_tready(in_tready),
|
||||
// Output AXI-STream interface to VITA Radio Core
|
||||
.vita_tdata(vita_tdata),
|
||||
.vita_tuser(vita_tuser),
|
||||
.vita_tlast(vita_tlast),
|
||||
.vita_tvalid(vita_tvalid),
|
||||
.vita_tready(vita_tready),
|
||||
// Output AXI-Stream interface to ZPU
|
||||
.zpu_tdata(zpu_tdata),
|
||||
.zpu_tuser(zpu_tuser),
|
||||
.zpu_tlast(zpu_tlast),
|
||||
.zpu_tvalid(zpu_tvalid),
|
||||
.zpu_tready(zpu_tready),
|
||||
// Output AXI-Stream interface to cross-over MAC
|
||||
.xo_tdata(xo_tdata),
|
||||
.xo_tuser(xo_tuser),
|
||||
.xo_tlast(xo_tlast),
|
||||
.xo_tvalid(xo_tvalid),
|
||||
.xo_tready(xo_tready)
|
||||
);
|
||||
|
||||
//
|
||||
// Define Clocks
|
||||
//
|
||||
initial begin
|
||||
clk = 1'b1;
|
||||
end
|
||||
|
||||
// 125MHz clock
|
||||
always #4000 clk = ~clk;
|
||||
|
||||
//
|
||||
// Good starting state
|
||||
//
|
||||
initial begin
|
||||
reset <= 0;
|
||||
clear <= 0;
|
||||
set_stb <= 0;
|
||||
set_addr <= 0;
|
||||
set_data <= 0;
|
||||
data_in <= 0;
|
||||
user_in <= 0;
|
||||
valid_in <= 0;
|
||||
last_in <= 0;
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Task Libaray
|
||||
//
|
||||
task write_setting_bus;
|
||||
input [15:0] address;
|
||||
input [31:0] data;
|
||||
|
||||
begin
|
||||
|
||||
@(negedge clk);
|
||||
set_stb = 1'b0;
|
||||
set_addr = 16'h0;
|
||||
set_data = 32'h0;
|
||||
@(negedge clk);
|
||||
set_stb = 1'b1;
|
||||
set_addr = address;
|
||||
set_data = data;
|
||||
@(negedge clk);
|
||||
set_stb = 1'b0;
|
||||
set_addr = 16'h0;
|
||||
set_data = 32'h0;
|
||||
|
||||
end
|
||||
endtask // write_setting_bus
|
||||
|
||||
|
||||
task enqueue_line;
|
||||
input last;
|
||||
input [2:0] keep;
|
||||
input [63:0] data;
|
||||
begin
|
||||
data_in <= {keep, data};
|
||||
last_in <= last;
|
||||
valid_in <= 1;
|
||||
while (~ready_in) begin
|
||||
@(negedge clk);
|
||||
end
|
||||
@(negedge clk);
|
||||
data_in <= 0;
|
||||
last_in <= 0;
|
||||
valid_in <= 0;
|
||||
end
|
||||
endtask // enqueue_line
|
||||
|
||||
task enqueue_arp_req;
|
||||
input [47:0] src_mac;
|
||||
input [31:0] src_ip;
|
||||
input [47:0] dst_mac;
|
||||
input [31:0] dst_ip;
|
||||
|
||||
begin
|
||||
@(negedge clk);
|
||||
// Line 0
|
||||
enqueue_line( 0, 3'b0, {48'h0,16'hffff});
|
||||
// Line 1 - Eth
|
||||
enqueue_line( 0, 3'b0, {32'hffffffff,src_mac[47:16]});
|
||||
// Line 2 - Eth+ARP (HTYPE = 1, PTYPE = 0x0800)
|
||||
enqueue_line( 0, 3'b0, {src_mac[15:0],16'h0806,16'h0001,16'h0800});
|
||||
// Line 3 - HLEN=6, PLEN=4 OPER=1
|
||||
enqueue_line( 0, 3'b0, {8'h06,8'h04,16'h0001,src_mac[47:16]});
|
||||
// Line 4 - ARP
|
||||
enqueue_line( 0, 3'b0, {src_mac[15:0],src_ip[31:0],dst_mac[47:32]});
|
||||
// Line 5 - ARP
|
||||
enqueue_line( 1, 3'b0, {dst_mac[31:0],dst_ip[31:0]});
|
||||
end
|
||||
endtask // enqueue_arp_req
|
||||
|
||||
|
||||
|
||||
reg [11:0] frame_count =12'h0;
|
||||
|
||||
task enqueue_vita_pkt;
|
||||
// We assume that we always have SID and TSF fields.
|
||||
input [47:0] mac;
|
||||
input [31:0] ip;
|
||||
input [15:0] udp;
|
||||
input [15:0] vita_size;
|
||||
input [63:0] vita_tsf;
|
||||
input [31:0] vita_sid;
|
||||
|
||||
|
||||
integer i;
|
||||
reg [15:0] j;
|
||||
reg [19:0] vrl_size;
|
||||
reg [15:0] udp_size;
|
||||
reg [15:0] ip_size;
|
||||
|
||||
|
||||
begin
|
||||
vrl_size = vita_size + 3;
|
||||
udp_size = vrl_size*4 + 8;
|
||||
ip_size = udp_size + 20;
|
||||
@(negedge clk);
|
||||
// Line 0
|
||||
enqueue_line( 0, 3'b0, {48'h0,mac[47:32]});
|
||||
// Line 1 - Eth
|
||||
enqueue_line( 0, 3'b0, {mac[31:0],32'h11223344});
|
||||
// Line 2 - Eth+IP
|
||||
enqueue_line( 0, 3'b0, {16'h5566,16'h0800,16'h0000,ip_size});
|
||||
// Line 3 - IP
|
||||
enqueue_line( 0, 3'b0, 'h11<<16);
|
||||
// Line 4 - IP
|
||||
enqueue_line( 0, 3'b0, {32'h09080706, ip});
|
||||
// Line 5 - UDP
|
||||
enqueue_line( 0, 3'b0, {16'h1234, udp, udp_size, 16'h0});
|
||||
// Line 6 - VRL
|
||||
enqueue_line( 0, 3'b0, {"VRLP",frame_count,vrl_size});
|
||||
// Line 7 - VRT
|
||||
enqueue_line( 0, 3'b0, {16'b0001000000010000, vita_size,vita_sid}); //vita hdr + SID
|
||||
enqueue_line( 0, 3'b0, vita_tsf);
|
||||
j = 0;
|
||||
|
||||
for (i = 6; i < vita_size; i = i + 2) begin
|
||||
enqueue_line( 0 , 3'b0, {j,j+16'h1,j+16'h2,j+16'h3});
|
||||
j = j + 4;
|
||||
end
|
||||
|
||||
if (i-vita_size==0) // 2x32words to finish VITA packet.
|
||||
enqueue_line( 1, 3'b0, {j,j+16'h1,j+16'h2,j+16'h3});
|
||||
else // 1x32bit word to finish VITA packet
|
||||
enqueue_line( 1, 3'h4, {j,j+16'h1,j+16'h2,j+16'h3});
|
||||
end
|
||||
|
||||
endtask // enqueue_packet
|
||||
|
||||
|
||||
//
|
||||
// Simulation specific testbench is included here
|
||||
//
|
||||
`include "simulation_script.v"
|
||||
|
||||
|
||||
//
|
||||
// Input FIFO
|
||||
//
|
||||
axi_fifo_short
|
||||
#(.WIDTH(69)) axi_fifo_short_in
|
||||
(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.o_tdata({in_tlast,in_tuser,in_tdata}),
|
||||
.o_tvalid(in_tvalid),
|
||||
.o_tready(in_tready),
|
||||
.i_tdata({last_in,user_in,data_in}),
|
||||
.i_tvalid(valid_in),
|
||||
.i_tready(ready_in),
|
||||
.space(),
|
||||
.occupied()
|
||||
);
|
||||
|
||||
//
|
||||
// Output Sinks
|
||||
//
|
||||
axi_probe_tb
|
||||
#(.FILENAME("zpu.txt"),.VITA_PORT0(60000),.VITA_PORT1(60001)) axi_probe_tb_zpu
|
||||
(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.tdata(zpu_tdata),
|
||||
.tvalid(zpu_tvalid),
|
||||
.tready(zpu_tready),
|
||||
.tlast(zpu_tlast)
|
||||
);
|
||||
|
||||
assign zpu_tready = 1'b1;
|
||||
|
||||
axi_probe_tb
|
||||
#(.FILENAME("xo.txt"),.VITA_PORT0(60000),.VITA_PORT1(60001)) axi_probe_tb_xo
|
||||
(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.tdata(xo_tdata),
|
||||
.tvalid(xo_tvalid),
|
||||
.tready(xo_tready),
|
||||
.tlast(xo_tlast)
|
||||
);
|
||||
|
||||
assign xo_tready = 1'b1;
|
||||
|
||||
axi_probe_tb
|
||||
#(.FILENAME("vita.txt"),.VITA_PORT0(60000),.VITA_PORT1(60001),.START_AT_VRL(1)) axi_probe_tb_vita
|
||||
(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.tdata(vita_tdata),
|
||||
.tvalid(vita_tvalid),
|
||||
.tready(vita_tready),
|
||||
.tlast(vita_tlast)
|
||||
);
|
||||
|
||||
assign vita_tready = 1'b1;
|
||||
|
||||
endmodule // eth_dispatch_tb
|
||||
@@ -0,0 +1,113 @@
|
||||
|
||||
// Adapts from internal VITA to ethernet packets. Also handles ZPU and ethernet crossover interfaces.
|
||||
|
||||
module eth_interface
|
||||
#(parameter BASE=0,
|
||||
parameter XO_FIFOSIZE=10,
|
||||
parameter ZPU_FIFOSIZE=10,
|
||||
parameter VITA_FIFOSIZE=10,
|
||||
parameter ETHOUT_FIFOSIZE=10)
|
||||
(input clk, input reset, input clear,
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data,
|
||||
// Eth ports
|
||||
output [63:0] eth_tx_tdata, output [3:0] eth_tx_tuser, output eth_tx_tlast, output eth_tx_tvalid, input eth_tx_tready,
|
||||
input [63:0] eth_rx_tdata, input [3:0] eth_rx_tuser, input eth_rx_tlast, input eth_rx_tvalid, output eth_rx_tready,
|
||||
// Vita router interface
|
||||
output [63:0] e2v_tdata, output e2v_tlast, output e2v_tvalid, input e2v_tready,
|
||||
input [63:0] v2e_tdata, input v2e_tlast, input v2e_tvalid, output v2e_tready,
|
||||
// Ethernet crossover
|
||||
output [63:0] xo_tdata, output [3:0] xo_tuser, output xo_tlast, output xo_tvalid, input xo_tready,
|
||||
input [63:0] xi_tdata, input [3:0] xi_tuser, input xi_tlast, input xi_tvalid, output xi_tready,
|
||||
// ZPU
|
||||
output [63:0] e2z_tdata, output [3:0] e2z_tuser, output e2z_tlast, output e2z_tvalid, input e2z_tready,
|
||||
input [63:0] z2e_tdata, input [3:0] z2e_tuser, input z2e_tlast, input z2e_tvalid, output z2e_tready,
|
||||
|
||||
output [31:0] debug
|
||||
);
|
||||
|
||||
wire [63:0] v2ef_tdata;
|
||||
wire [3:0] v2ef_tuser;
|
||||
wire v2ef_tlast, v2ef_tvalid, v2ef_tready;
|
||||
|
||||
// //////////////////////////////////////////////////////////////
|
||||
// Incoming Ethernet path
|
||||
// Includes FIFO on the output going to ZPU
|
||||
|
||||
wire [63:0] epg_tdata_int;
|
||||
wire [3:0] epg_tuser_int;
|
||||
wire epg_tlast_int, epg_tvalid_int, epg_tready_int;
|
||||
|
||||
axi_packet_gate #(.WIDTH(68), .SIZE(10)) packet_gater //holds 8K pkts
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
|
||||
.i_tdata({eth_rx_tuser, eth_rx_tdata}), .i_tlast(eth_rx_tlast),
|
||||
.i_terror(eth_rx_tuser[3]), //top bit of user bus is error
|
||||
.i_tvalid(eth_rx_tvalid), .i_tready(eth_rx_tready),
|
||||
|
||||
.o_tdata({epg_tuser_int, epg_tdata_int}), .o_tlast(epg_tlast_int),
|
||||
.o_tvalid(epg_tvalid_int), .o_tready(epg_tready_int));
|
||||
|
||||
wire [63:0] e2z_tdata_int;
|
||||
wire [3:0] e2z_tuser_int;
|
||||
wire e2z_tlast_int, e2z_tvalid_int, e2z_tready_int;
|
||||
|
||||
eth_dispatch #(.BASE(BASE+8)) eth_dispatch
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.set_stb(set_stb), .set_addr(set_addr) , .set_data(set_data),
|
||||
.in_tdata(epg_tdata_int), .in_tuser(epg_tuser_int), .in_tlast(epg_tlast_int), .in_tvalid(epg_tvalid_int), .in_tready(epg_tready_int),
|
||||
.vita_tdata(e2v_tdata), .vita_tlast(e2v_tlast), .vita_tvalid(e2v_tvalid), .vita_tready(e2v_tready),
|
||||
.zpu_tdata(e2z_tdata_int), .zpu_tuser(e2z_tuser_int), .zpu_tlast(e2z_tlast_int), .zpu_tvalid(e2z_tvalid_int), .zpu_tready(e2z_tready_int),
|
||||
.xo_tdata(xo_tdata), .xo_tuser(xo_tuser), .xo_tlast(xo_tlast), .xo_tvalid(xo_tvalid), .xo_tready(xo_tready), // to other eth port
|
||||
.debug(debug));
|
||||
|
||||
axi_fifo #(.WIDTH(69),.SIZE(ZPU_FIFOSIZE)) zpu_fifo
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({e2z_tlast_int,e2z_tuser_int,e2z_tdata_int}), .i_tvalid(e2z_tvalid_int), .i_tready(e2z_tready_int),
|
||||
.o_tdata({e2z_tlast,e2z_tuser,e2z_tdata}), .o_tvalid(e2z_tvalid), .o_tready(e2z_tready));
|
||||
|
||||
// //////////////////////////////////////////////////////////////
|
||||
// Outgoing Ethernet path
|
||||
// Includes FIFOs on path from VITA router, from ethernet crossover, and on the overall output
|
||||
|
||||
wire [63:0] eth_tx_tdata_int;
|
||||
wire [3:0] eth_tx_tuser_int;
|
||||
wire eth_tx_tlast_int, eth_tx_tvalid_int, eth_tx_tready_int;
|
||||
|
||||
wire [63:0] xi_tdata_int;
|
||||
wire [3:0] xi_tuser_int;
|
||||
wire xi_tlast_int, xi_tvalid_int, xi_tready_int;
|
||||
|
||||
wire [63:0] v2e_tdata_int;
|
||||
wire v2e_tlast_int, v2e_tvalid_int, v2e_tready_int;
|
||||
|
||||
axi_fifo #(.WIDTH(65),.SIZE(VITA_FIFOSIZE)) vitaout_fifo
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({v2e_tlast,v2e_tdata}), .i_tvalid(v2e_tvalid), .i_tready(v2e_tready),
|
||||
.o_tdata({v2e_tlast_int,v2e_tdata_int}), .o_tvalid(v2e_tvalid_int), .o_tready(v2e_tready_int));
|
||||
|
||||
chdr_eth_framer #(.BASE(BASE)) my_eth_framer
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.set_stb(set_stb), .set_addr(set_addr) , .set_data(set_data),
|
||||
.in_tdata(v2e_tdata_int), .in_tlast(v2e_tlast_int), .in_tvalid(v2e_tvalid_int), .in_tready(v2e_tready_int),
|
||||
.out_tdata(v2ef_tdata), .out_tuser(v2ef_tuser), .out_tlast(v2ef_tlast), .out_tvalid(v2ef_tvalid), .out_tready(v2ef_tready),
|
||||
.debug());
|
||||
|
||||
axi_fifo #(.WIDTH(69),.SIZE(XO_FIFOSIZE)) xo_fifo
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({xi_tlast,xi_tuser,xi_tdata}), .i_tvalid(xi_tvalid), .i_tready(xi_tready),
|
||||
.o_tdata({xi_tlast_int,xi_tuser_int,xi_tdata_int}), .o_tvalid(xi_tvalid_int), .o_tready(xi_tready_int));
|
||||
|
||||
axi_mux4 #(.PRIO(0), .WIDTH(68)) eth_mux
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i0_tdata({z2e_tuser,z2e_tdata}), .i0_tlast(z2e_tlast), .i0_tvalid(z2e_tvalid), .i0_tready(z2e_tready),
|
||||
.i1_tdata({v2ef_tuser,v2ef_tdata}), .i1_tlast(v2ef_tlast), .i1_tvalid(v2ef_tvalid), .i1_tready(v2ef_tready),
|
||||
.i2_tdata({xi_tuser_int,xi_tdata_int}), .i2_tlast(xi_tlast_int), .i2_tvalid(xi_tvalid_int), .i2_tready(xi_tready_int),
|
||||
.i3_tdata(), .i3_tlast(), .i3_tvalid(1'b0), .i3_tready(),
|
||||
.o_tdata({eth_tx_tuser_int,eth_tx_tdata_int}), .o_tlast(eth_tx_tlast_int), .o_tvalid(eth_tx_tvalid_int), .o_tready(eth_tx_tready_int));
|
||||
|
||||
axi_fifo #(.WIDTH(69),.SIZE(ETHOUT_FIFOSIZE)) ethout_fifo
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({eth_tx_tlast_int,eth_tx_tuser_int,eth_tx_tdata_int}), .i_tvalid(eth_tx_tvalid_int), .i_tready(eth_tx_tready_int),
|
||||
.o_tdata({eth_tx_tlast,eth_tx_tuser,eth_tx_tdata}), .o_tvalid(eth_tx_tvalid), .o_tready(eth_tx_tready));
|
||||
|
||||
endmodule // eth_interface
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
// Compute IP header checksum. 2 cycles of latency.
|
||||
module ip_hdr_checksum
|
||||
(input clk, input [159:0] in, output reg [15:0] out);
|
||||
|
||||
wire [18:0] padded [0:9];
|
||||
reg [18:0] sum_a, sum_b;
|
||||
|
||||
genvar i;
|
||||
generate
|
||||
for(i=0 ; i<10 ; i=i+1)
|
||||
assign padded[i] = {3'b000,in[i*16+15:i*16]};
|
||||
endgenerate
|
||||
|
||||
always @(posedge clk) sum_a = padded[0] + padded[1] + padded[2] + padded[3] + padded[4];
|
||||
always @(posedge clk) sum_b = padded[5] + padded[6] + padded[7] + padded[8] + padded[9];
|
||||
|
||||
wire [18:0] sum = sum_a + sum_b;
|
||||
|
||||
always @(posedge clk)
|
||||
out <= ~(sum[15:0] + {13'd0,sum[18:16]});
|
||||
|
||||
|
||||
endmodule // ip_hdr_checksum
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
module ip_hdr_checksum_tb();
|
||||
|
||||
initial $dumpfile("ip_hdr_checksum_tb.vcd");
|
||||
initial $dumpvars(0,ip_hdr_checksum_tb);
|
||||
|
||||
reg clk;
|
||||
|
||||
wire [159:0] in = {
|
||||
16'h4500,
|
||||
16'h0030,
|
||||
16'h4422,
|
||||
16'h4000,
|
||||
16'h8006,
|
||||
16'h0000,
|
||||
16'h8c7c,
|
||||
16'h19ac,
|
||||
16'hae24,
|
||||
16'h1e2b
|
||||
};
|
||||
|
||||
wire [15:0] out;
|
||||
ip_hdr_checksum ip_hdr_checksum
|
||||
(.clk(clk),
|
||||
.in(in),
|
||||
.out(out));
|
||||
|
||||
initial
|
||||
begin
|
||||
clk <= 0;
|
||||
#100 clk <= 1;
|
||||
#100 clk <= 0;
|
||||
#100 clk <= 1;
|
||||
#100 $display("Computed 0x%x, should be 0x442e", out);
|
||||
#100 $finish;
|
||||
end
|
||||
|
||||
endmodule // ip_hdr_checksum_tb
|
||||
@@ -0,0 +1,127 @@
|
||||
|
||||
// source_flow_control.v
|
||||
//
|
||||
// This block passes the in_* AXI port to the out_* AXI port only when it has
|
||||
// enough flow control credits. Data is held when there are not enough credits.
|
||||
// Credits are replenished with extension context packets which update the
|
||||
// last_consumed packet register. Max credits are controlled by settings regs.
|
||||
// The 2nd line of the packet contains the sequence number in the low 12 bits.
|
||||
// These packets should not have a time value, but if they do it will be ignored.
|
||||
|
||||
module source_flow_control
|
||||
#(parameter BASE=0)
|
||||
(input clk, input reset, input clear,
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data,
|
||||
input [63:0] fc_tdata, input fc_tlast, input fc_tvalid, output fc_tready,
|
||||
input [63:0] in_tdata, input in_tlast, input in_tvalid, output in_tready,
|
||||
output [63:0] out_tdata, output out_tlast, output out_tvalid, input out_tready);
|
||||
|
||||
reg [31:0] last_seqnum_consumed;
|
||||
wire [31:0] window_size;
|
||||
wire [31:0] go_until_seqnum = last_seqnum_consumed + window_size + 1;
|
||||
reg [31:0] current_seqnum;
|
||||
wire window_reset;
|
||||
wire window_enable;
|
||||
wire setting_changed;
|
||||
|
||||
setting_reg #(.my_addr(BASE)) sr_window_size
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
|
||||
.out(window_size),.changed(setting_changed));
|
||||
|
||||
setting_reg #(.my_addr(BASE+1), .width(1)) sr_window_enable
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
|
||||
.out(window_enable),.changed(window_reset));
|
||||
|
||||
reg go;
|
||||
reg [1:0] sfc_state;
|
||||
|
||||
|
||||
localparam SFC_HEAD = 2'd0;
|
||||
localparam SFC_TIME = 2'd1;
|
||||
localparam SFC_BODY = 2'd2;
|
||||
localparam SFC_DUMP = 2'd3;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear | window_reset)
|
||||
begin
|
||||
last_seqnum_consumed <= 32'hFFFFFFFF;
|
||||
sfc_state <= SFC_HEAD;
|
||||
end
|
||||
else
|
||||
if(fc_tvalid & fc_tready)
|
||||
case(sfc_state)
|
||||
SFC_HEAD :
|
||||
if(fc_tlast)
|
||||
sfc_state <= SFC_HEAD; // Error. CHDR packet with only a header is an error.
|
||||
else if(~fc_tdata[63]) // Is this NOT an extension context packet?
|
||||
sfc_state <= SFC_DUMP; // Error. Only extension context packets should come in on this interface.
|
||||
else if(fc_tdata[61]) // Does this packet have time?
|
||||
sfc_state <= SFC_TIME;
|
||||
else
|
||||
sfc_state <= SFC_BODY;
|
||||
|
||||
SFC_TIME :
|
||||
if(fc_tlast)
|
||||
sfc_state <= SFC_HEAD; // Error, CHDR packet with only header and time is an error.
|
||||
else
|
||||
sfc_state <= SFC_BODY;
|
||||
|
||||
SFC_BODY :
|
||||
begin
|
||||
last_seqnum_consumed <= fc_tdata[31:0]; // Sequence number is in lower 32bits.
|
||||
if(fc_tlast)
|
||||
sfc_state <= SFC_HEAD;
|
||||
else
|
||||
sfc_state <= SFC_DUMP; // Error. Not expecting any more data in a CHDR packet.
|
||||
end
|
||||
|
||||
SFC_DUMP : // shouldn't ever need to be here, this is an error condition
|
||||
if(fc_tlast)
|
||||
sfc_state <= SFC_HEAD;
|
||||
endcase // case (sfc_state)
|
||||
|
||||
assign fc_tready = 1'b1; // Always consume FC -- FIXME Even if we are getting reset?
|
||||
assign out_tdata = in_tdata; // CHDR data flows through combinatorially.
|
||||
assign out_tlast = in_tlast;
|
||||
assign in_tready = go ? out_tready : 1'b0;
|
||||
assign out_tvalid = go ? in_tvalid : 1'b0;
|
||||
|
||||
//
|
||||
// Each time we recieve the end of an IF data packet increment the current_seqnum.
|
||||
// We bravely assume that no packets go missing...or at least that they will be detected elsewhere
|
||||
// and then handled appropriately.
|
||||
// The SEQNUM needs to be initialized every time we start a new stream. In new_rx_framer this is done
|
||||
// as a side effect of writing a new SID value to the setting reg.
|
||||
//
|
||||
// By incrementing current_seqnum on the last signal we get the nice effect that packet flow is
|
||||
// always suspended between packets rather than within a packet.
|
||||
//
|
||||
always @(posedge clk)
|
||||
if(reset | clear | window_reset)
|
||||
current_seqnum <= 0;
|
||||
else if (in_tvalid && in_tready && in_tlast)
|
||||
current_seqnum <= current_seqnum + 1;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
go <= 1'b0;
|
||||
else
|
||||
if(~window_enable)
|
||||
go <= 1'b1;
|
||||
else
|
||||
case(go)
|
||||
1'b0 :
|
||||
// This test assumes the host is well behaved in sending good numbers for packets consumed
|
||||
// and that current_seqnum increments always by 1 only.
|
||||
// This way wraps are dealt with without a large logic penalty.
|
||||
if (in_tvalid & (go_until_seqnum - current_seqnum != 0))
|
||||
// if(in_tvalid & (go_until_seqnum > current_seqnum)) // FIXME will need to handle wrap of 32-bit seqnum
|
||||
go <= 1'b1;
|
||||
|
||||
1'b1 :
|
||||
if(in_tvalid & in_tready & in_tlast)
|
||||
go <= 1'b0;
|
||||
endcase // case (go)
|
||||
|
||||
|
||||
endmodule // source_flow_control
|
||||
@@ -0,0 +1,249 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module source_flow_control_tb();
|
||||
|
||||
reg clk = 0;
|
||||
reg reset = 1;
|
||||
|
||||
always #10 clk = ~clk;
|
||||
|
||||
initial $dumpfile("source_flow_control_tb.vcd");
|
||||
initial $dumpvars(0,source_flow_control_tb);
|
||||
|
||||
initial
|
||||
begin
|
||||
#1000 reset = 0;
|
||||
#20000;
|
||||
$finish;
|
||||
end
|
||||
|
||||
reg [63:0] tdata;
|
||||
wire [63:0] tdata_int;
|
||||
reg tlast;
|
||||
wire tlast_int;
|
||||
reg tvalid = 1'b0;
|
||||
wire tvalid_int;
|
||||
wire tready, tready_int;
|
||||
|
||||
reg [63:0] fc_tdata;
|
||||
reg fc_tlast, fc_tvalid;
|
||||
wire fc_tready;
|
||||
|
||||
wire [63:0] out_tdata;
|
||||
wire out_tlast, out_tready, out_tvalid;
|
||||
|
||||
wire [15:0] occ_in, occ_out;
|
||||
reg set_stb = 0;
|
||||
reg [7:0] set_addr;
|
||||
reg [31:0] set_data;
|
||||
|
||||
|
||||
task send_fc_packet;
|
||||
input [31:0] seqnum;
|
||||
input [31:0] sid;
|
||||
input always_go;
|
||||
|
||||
begin
|
||||
@(posedge clk);
|
||||
fc_tlast <= 1'b0;
|
||||
fc_tdata <= { 1'b1, 1'b0, 1'b0, 1'b0, 12'hABC, 16'd4, sid };
|
||||
fc_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
fc_tlast <= 1'b1;
|
||||
//fc_tdata <= { 52'h0,seqnum };
|
||||
fc_tdata <= { 31'h0,always_go, seqnum };
|
||||
@(posedge clk);
|
||||
fc_tvalid <= 0;
|
||||
@(posedge clk);
|
||||
end
|
||||
endtask // send_packet
|
||||
|
||||
task send_packet;
|
||||
input ec;
|
||||
input timed;
|
||||
input [11:0] seqnum;
|
||||
input [31:0] sid;
|
||||
input [63:0] vtime;
|
||||
input [15:0] addr;
|
||||
input [31:0] data;
|
||||
|
||||
begin
|
||||
// Send a packet
|
||||
@(posedge clk);
|
||||
tlast <= 1'b0;
|
||||
tdata <= { ec, 1'b0, timed, 1'b0, seqnum, timed ? 16'd6 : 16'd4, sid };
|
||||
tvalid <= 1;
|
||||
@(posedge clk);
|
||||
if(timed)
|
||||
begin
|
||||
tdata <= vtime;
|
||||
@(posedge clk);
|
||||
end
|
||||
tlast <= 1'b1;
|
||||
tdata <= { 16'h0, addr, data };
|
||||
@(posedge clk);
|
||||
tlast <= 1'b0;
|
||||
tvalid <= 0;
|
||||
@(posedge clk);
|
||||
end
|
||||
endtask // send_packet
|
||||
|
||||
initial
|
||||
begin
|
||||
tvalid <= 1'b0;
|
||||
while(reset)
|
||||
@(posedge clk);
|
||||
@(posedge clk);
|
||||
// Set flow control window to be 2
|
||||
set_stb <= 1;
|
||||
set_addr <= 0;
|
||||
set_data <= 2;
|
||||
@(posedge clk);
|
||||
set_stb <= 0;
|
||||
// ExtContext. Time. Seq=0, SID=DEAD_6789, Time=10
|
||||
send_packet(1'b1,1'b1,12'h0,32'hDEAD_6789,64'h10,16'hB,32'hF00D_1234);
|
||||
send_packet(1'b1,1'b1,12'h1,32'hDEAD_6789,64'h20,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h2,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h3,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h4,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h5,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h6,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h7,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h8,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
#500;
|
||||
// Consumed 2 packets
|
||||
send_fc_packet(32'd1,32'h3,1'b0);
|
||||
#300;
|
||||
// Consumed 1 packet
|
||||
send_fc_packet(32'd2,32'h3,1'b0);
|
||||
#500;
|
||||
// Consumed 2 packets
|
||||
send_fc_packet(32'd4,32'h3,1'b0);
|
||||
#400;
|
||||
// Send same SEQ ID again to test it causes no changes.
|
||||
send_fc_packet(32'd4,32'h3,1'b0);
|
||||
#300;
|
||||
// Consumed 1 packet
|
||||
send_fc_packet(32'd5,32'h3,1'b0);
|
||||
#500;
|
||||
// Consumed 2 packets
|
||||
send_fc_packet(32'd7,32'h3,1'b0);
|
||||
#500;
|
||||
send_packet(1'b1,1'b1,12'h9,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'hA,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
#300;
|
||||
// Consumed 1 packet
|
||||
send_fc_packet(32'd8,32'h3,1'b0);
|
||||
//
|
||||
// Now force internal sequence count to close to wrap value to test corner case
|
||||
//
|
||||
#100;
|
||||
source_flow_control.current_seqnum <= 32'hFFFF_FFFC;
|
||||
#100;
|
||||
send_fc_packet(32'hFFFF_FFFA,32'h3,1'b0);
|
||||
#100;
|
||||
send_packet(1'b1,1'b1,12'hFFC,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
#200;
|
||||
send_packet(1'b1,1'b1,12'hFFD,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'hFFE,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'hFFF,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h000,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h001,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h002,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
#200;
|
||||
// Consumed 2 packets
|
||||
send_fc_packet(32'hFFFF_FFFC,32'h3,1'b0);
|
||||
#200;
|
||||
// Consumed 2 packets
|
||||
send_fc_packet(32'hFFFF_FFFE,32'h3,1'b0);
|
||||
send_packet(1'b1,1'b1,12'h003,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h004,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
#200;
|
||||
// Consumed 2 packets
|
||||
send_fc_packet(32'h0,32'h3,1'b0);
|
||||
#200;
|
||||
// Consumed 2 packets
|
||||
send_fc_packet(32'h2,32'h3,1'b0);
|
||||
#500;
|
||||
//
|
||||
// Again force internal sequence count to close to wrap value to test new corner case
|
||||
//
|
||||
#100;
|
||||
source_flow_control.current_seqnum <= 32'hFFFF_FFFC;
|
||||
#100;
|
||||
send_fc_packet(32'hFFFF_FFFA,32'h3,1'b0);
|
||||
#100;
|
||||
send_packet(1'b1,1'b1,12'hFFC,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
#200;
|
||||
send_packet(1'b1,1'b1,12'hFFD,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'hFFE,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'hFFF,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h000,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h001,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h002,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
#200;
|
||||
// Consumed 1 packets
|
||||
send_fc_packet(32'hFFFF_FFFB,32'h3,1'b0);
|
||||
#200;
|
||||
// Consumed 1 packets
|
||||
send_fc_packet(32'hFFFF_FFFC,32'h3,1'b0);
|
||||
send_packet(1'b1,1'b1,12'h003,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h004,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
#200;
|
||||
// Consumed 1 packets
|
||||
send_fc_packet(32'hFFFF_FFFD,32'h3,1'b0);
|
||||
#200;
|
||||
// Consumed 1 packets
|
||||
send_fc_packet(32'hFFFF_FFFE,32'h3,1'b0);
|
||||
#200;
|
||||
// Consumed 1 packets
|
||||
send_fc_packet(32'hFFFF_FFFF,32'h3,1'b0);
|
||||
#200;
|
||||
// Consumed 1 packets
|
||||
send_fc_packet(32'h0,32'h3,1'b0);
|
||||
#200;
|
||||
// Consumed 1 packets
|
||||
send_fc_packet(32'h1,32'h3,1'b0);
|
||||
#200;
|
||||
// Consumed 1 packets
|
||||
send_fc_packet(32'h2,32'h3,1'b0);
|
||||
#500;
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
axi_fifo #(.WIDTH(65), .SIZE(10)) fifo_in
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({tlast,tdata}), .i_tvalid(tvalid), .i_tready(tready),
|
||||
.o_tdata({tlast_int,tdata_int}), .o_tvalid(tvalid_int), .o_tready(tready_int),
|
||||
.occupied(occ_in));
|
||||
|
||||
source_flow_control source_flow_control
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
|
||||
.fc_tdata(fc_tdata), .fc_tlast(fc_tlast), .fc_tvalid(fc_tvalid), .fc_tready(fc_tready),
|
||||
.in_tdata(tdata_int), .in_tlast(tlast_int), .in_tvalid(tvalid_int), .in_tready(tready_int),
|
||||
.out_tdata(out_tdata), .out_tlast(out_tlast), .out_tvalid(out_tvalid), .out_tready(out_tready)
|
||||
);
|
||||
|
||||
wire [63:0] dump_tdata;
|
||||
wire dump_tlast, dump_tvalid, dump_tready;
|
||||
|
||||
axi_fifo #(.WIDTH(65), .SIZE(10)) fifo_out
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({out_tlast,out_tdata}), .i_tvalid(out_tvalid), .i_tready(out_tready),
|
||||
.o_tdata({dump_tlast,dump_tdata}), .o_tvalid(dump_tvalid), .o_tready(dump_tready),
|
||||
.occupied(occ_out));
|
||||
|
||||
assign dump_tready = 0;
|
||||
|
||||
always @(posedge clk)
|
||||
if(out_tvalid & out_tready)
|
||||
begin
|
||||
$display("%x",out_tdata);
|
||||
if(out_tlast)
|
||||
$display("TLAST");
|
||||
end
|
||||
endmodule // source_flow_control_tb
|
||||
@@ -0,0 +1,137 @@
|
||||
|
||||
// vita_eth_framer
|
||||
// Takes a vita stream in and adds udp, ip, and ethernet framing
|
||||
// Uses 8 setting reg addresses. First 4 are simple registers:
|
||||
// BASE+0 : Upper 16 bits of ethernet src mac
|
||||
// BASE+1 : Lower 32 bits of ethernet src mac
|
||||
// BASE+2 : IP src address
|
||||
// BASE+3 : UDP src port
|
||||
//
|
||||
// Next 4 control write ports on a RAM indexed by destination field of stream ID
|
||||
// BASE+4 : Dest SID for next 3 regs
|
||||
// BASE+5 : Dest IP
|
||||
// BASE+6 : Dest UDP port, upper 16 bits of dest mac
|
||||
// BASE+7 : Lower 32 bits of dest mac
|
||||
|
||||
module vita_eth_framer
|
||||
#(parameter BASE=0)
|
||||
(input clk, input reset, input clear,
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data,
|
||||
input [63:0] in_tdata, input in_tlast, input in_tvalid, output in_tready,
|
||||
output [63:0] out_tdata, output [3:0] out_tuser, output out_tlast, output out_tvalid, input out_tready,
|
||||
output [31:0] debug );
|
||||
|
||||
localparam SR_AWIDTH = 8;
|
||||
|
||||
reg [31:0] sid;
|
||||
reg [15:0] vita_len;
|
||||
|
||||
reg [2:0] vef_state;
|
||||
localparam VEF_IDLE = 3'd0;
|
||||
localparam VEF_PAYLOAD = 3'd7;
|
||||
|
||||
reg [63:0] tdata;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
begin
|
||||
vef_state <= VEF_IDLE;
|
||||
sid <= 32'd0;
|
||||
vita_len <= 16'd0;
|
||||
end
|
||||
else
|
||||
case(vef_state)
|
||||
VEF_IDLE :
|
||||
if(in_tvalid)
|
||||
begin
|
||||
vef_state <= 1;
|
||||
sid <= in_tdata[31:0];
|
||||
vita_len <= in_tdata[47:32];
|
||||
end
|
||||
VEF_PAYLOAD :
|
||||
if(in_tvalid & out_tready)
|
||||
if(in_tlast)
|
||||
vef_state <= VEF_IDLE;
|
||||
default :
|
||||
if(out_tready)
|
||||
vef_state <= vef_state + 3'd1;
|
||||
endcase // case (vef_state)
|
||||
|
||||
assign in_tready = (vef_state == VEF_PAYLOAD) ? out_tready : 1'b0;
|
||||
assign out_tvalid = (vef_state == VEF_PAYLOAD) ? in_tvalid : (vef_state == VEF_IDLE) ? 1'b0 : 1'b1;
|
||||
assign out_tlast = (vef_state == VEF_PAYLOAD) ? in_tlast : 1'b0;
|
||||
assign out_tuser = ((vef_state == VEF_PAYLOAD) & in_tlast) ? {1'b0,vita_len[0],2'b00} : 4'b0000;
|
||||
assign out_tdata = tdata;
|
||||
|
||||
wire [15:0] vita_len_in_bytes = {vita_len[13:0],2'b00}; // Vita length is in 32-bit words
|
||||
|
||||
wire [47:0] pad = 48'h0;
|
||||
wire [47:0] mac_src, mac_dst;
|
||||
wire [15:0] eth_type = 16'h0800;
|
||||
wire [15:0] misc_ip = { 4'd4 /* IPv4 */, 4'd5 /* IP HDR Len */, 8'h00 /* DSCP and ECN */};
|
||||
wire [15:0] ip_len = (16'd28 + vita_len_in_bytes); // 20 for IP, 8 for UDP
|
||||
wire [15:0] ident = 16'h0;
|
||||
wire [15:0] flag_frag = { 3'b010 /* don't fragment */, 13'h0 };
|
||||
wire [15:0] ttl_prot = { 8'h10 /* TTL */, 8'h11 /* UDP */ };
|
||||
wire [15:0] iphdr_checksum;
|
||||
wire [31:0] ip_src, ip_dst;
|
||||
wire [15:0] udp_src, udp_dst;
|
||||
wire [15:0] udp_len = (16'd8 + vita_len_in_bytes);
|
||||
wire [15:0] udp_checksum = 16'h0;
|
||||
|
||||
setting_reg #(.my_addr(BASE), .awidth(SR_AWIDTH), .width(16)) set_mac_upper
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(mac_src[47:32]), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+1), .awidth(SR_AWIDTH), .width(32)) set_mac_lower
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(mac_src[31:0]), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+2), .awidth(SR_AWIDTH), .width(32)) set_ip
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(ip_src), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+3), .awidth(SR_AWIDTH), .width(16)) set_udp
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(udp_src), .changed());
|
||||
|
||||
// Tables of MAC/IP/UDP addresses
|
||||
wire [8:0] ram_addr; // FIXME we could skip this part if we had wider SR addresses
|
||||
|
||||
setting_reg #(.my_addr(BASE+4), .awidth(SR_AWIDTH), .width(9)) set_ram_addr
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(ram_addr), .changed());
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_ip
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+5)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob(ip_dst));
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_udpmac
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+6)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob({udp_dst,mac_dst[47:32]}));
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_maclower
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+7)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob(mac_dst[31:0]));
|
||||
|
||||
ip_hdr_checksum ip_hdr_checksum
|
||||
(.clk(clk), .in({misc_ip,ip_len,ident,flag_frag,ttl_prot,16'd0,ip_src,ip_dst}),
|
||||
.out(iphdr_checksum));
|
||||
|
||||
always @*
|
||||
case(vef_state)
|
||||
1 : tdata <= { pad[47:0], mac_dst[47:32]};
|
||||
2 : tdata <= { mac_dst[31:0], mac_src[47:16]};
|
||||
3 : tdata <= { mac_src[15:0], eth_type[15:0], misc_ip[15:0], ip_len[15:0] };
|
||||
4 : tdata <= { ident[15:0], flag_frag[15:0], ttl_prot[15:0], iphdr_checksum[15:0]};
|
||||
5 : tdata <= { ip_src, ip_dst};
|
||||
6 : tdata <= { udp_src, udp_dst, udp_len, udp_checksum};
|
||||
default : tdata <= in_tdata;
|
||||
endcase // case (vef_state)
|
||||
|
||||
endmodule // vita_eth_framer
|
||||
@@ -0,0 +1,139 @@
|
||||
|
||||
// vrlp_eth_framer
|
||||
// Takes a vrlp stream in and adds udp, ip, and ethernet framing
|
||||
// Uses 8 setting reg addresses. First 4 are simple registers:
|
||||
// BASE+0 : Upper 16 bits of ethernet src mac
|
||||
// BASE+1 : Lower 32 bits of ethernet src mac
|
||||
// BASE+2 : IP src address
|
||||
// BASE+3 : UDP src port
|
||||
//
|
||||
// Next 4 control write ports on a RAM indexed by destination field of stream ID
|
||||
// BASE+4 : Dest SID for next 3 regs
|
||||
// BASE+5 : Dest IP
|
||||
// BASE+6 : Dest UDP port, upper 16 bits of dest mac
|
||||
// BASE+7 : Lower 32 bits of dest mac
|
||||
//
|
||||
// in_tuser holds streamid
|
||||
|
||||
module vrlp_eth_framer
|
||||
#(parameter BASE=0)
|
||||
(input clk, input reset, input clear,
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data,
|
||||
input [63:0] in_tdata, input [15:0] in_tuser, input in_tlast, input in_tvalid, output in_tready,
|
||||
output [63:0] out_tdata, output [3:0] out_tuser, output out_tlast, output out_tvalid, input out_tready,
|
||||
output [31:0] debug );
|
||||
|
||||
localparam SR_AWIDTH = 8;
|
||||
|
||||
reg [15:0] sid;
|
||||
reg [15:0] vrlp_len;
|
||||
|
||||
reg [2:0] vef_state;
|
||||
localparam VEF_IDLE = 3'd0;
|
||||
localparam VEF_PAYLOAD = 3'd7;
|
||||
|
||||
reg [63:0] tdata;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
begin
|
||||
vef_state <= VEF_IDLE;
|
||||
sid <= 16'd0;
|
||||
vrlp_len <= 16'd0;
|
||||
end
|
||||
else
|
||||
case(vef_state)
|
||||
VEF_IDLE :
|
||||
if(in_tvalid)
|
||||
begin
|
||||
vef_state <= 1;
|
||||
sid <= in_tuser[15:0];
|
||||
vrlp_len <= in_tdata[15:0]; // modified for VRLP header
|
||||
end
|
||||
VEF_PAYLOAD :
|
||||
if(in_tvalid & out_tready)
|
||||
if(in_tlast)
|
||||
vef_state <= VEF_IDLE;
|
||||
default :
|
||||
if(out_tready)
|
||||
vef_state <= vef_state + 3'd1;
|
||||
endcase // case (vef_state)
|
||||
|
||||
wire [15:0] vrlp_len_in_bytes = {vrlp_len[13:0],2'b00}; // Vrlp length is in 32-bit words
|
||||
|
||||
assign in_tready = (vef_state == VEF_PAYLOAD) ? out_tready : 1'b0;
|
||||
assign out_tvalid = (vef_state == VEF_PAYLOAD) ? in_tvalid : (vef_state == VEF_IDLE) ? 1'b0 : 1'b1;
|
||||
assign out_tlast = (vef_state == VEF_PAYLOAD) ? in_tlast : 1'b0;
|
||||
assign out_tuser = ((vef_state == VEF_PAYLOAD) & in_tlast) ? {1'b0,vrlp_len_in_bytes[2:0]} : 4'b0000;
|
||||
assign out_tdata = tdata;
|
||||
|
||||
wire [47:0] pad = 48'h0;
|
||||
wire [47:0] mac_src, mac_dst;
|
||||
wire [15:0] eth_type = 16'h0800;
|
||||
wire [15:0] misc_ip = { 4'd4 /* IPv4 */, 4'd5 /* IP HDR Len */, 8'h00 /* DSCP and ECN */};
|
||||
wire [15:0] ip_len = (16'd28 + vrlp_len_in_bytes); // 20 for IP, 8 for UDP
|
||||
wire [15:0] ident = 16'h0;
|
||||
wire [15:0] flag_frag = { 3'b010 /* don't fragment */, 13'h0 };
|
||||
wire [15:0] ttl_prot = { 8'h10 /* TTL */, 8'h11 /* UDP */ };
|
||||
wire [15:0] iphdr_checksum;
|
||||
wire [31:0] ip_src, ip_dst;
|
||||
wire [15:0] udp_src, udp_dst;
|
||||
wire [15:0] udp_len = (16'd8 + vrlp_len_in_bytes);
|
||||
wire [15:0] udp_checksum = 16'h0;
|
||||
|
||||
setting_reg #(.my_addr(BASE), .awidth(SR_AWIDTH), .width(16)) set_mac_upper
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(mac_src[47:32]), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+1), .awidth(SR_AWIDTH), .width(32)) set_mac_lower
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(mac_src[31:0]), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+2), .awidth(SR_AWIDTH), .width(32)) set_ip
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(ip_src), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+3), .awidth(SR_AWIDTH), .width(16)) set_udp
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(udp_src), .changed());
|
||||
|
||||
// Tables of MAC/IP/UDP addresses
|
||||
wire [8:0] ram_addr; // FIXME we could skip this part if we had wider SR addresses
|
||||
|
||||
setting_reg #(.my_addr(BASE+4), .awidth(SR_AWIDTH), .width(9)) set_ram_addr
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(ram_addr), .changed());
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_ip
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+5)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob(ip_dst));
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_udpmac
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+6)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob({udp_dst,mac_dst[47:32]}));
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_maclower
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+7)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob(mac_dst[31:0]));
|
||||
|
||||
ip_hdr_checksum ip_hdr_checksum
|
||||
(.clk(clk), .in({misc_ip,ip_len,ident,flag_frag,ttl_prot,16'd0,ip_src,ip_dst}),
|
||||
.out(iphdr_checksum));
|
||||
|
||||
always @*
|
||||
case(vef_state)
|
||||
1 : tdata <= { pad[47:0], mac_dst[47:32]};
|
||||
2 : tdata <= { mac_dst[31:0], mac_src[47:16]};
|
||||
3 : tdata <= { mac_src[15:0], eth_type[15:0], misc_ip[15:0], ip_len[15:0] };
|
||||
4 : tdata <= { ident[15:0], flag_frag[15:0], ttl_prot[15:0], iphdr_checksum[15:0]};
|
||||
5 : tdata <= { ip_src, ip_dst};
|
||||
6 : tdata <= { udp_src, udp_dst, udp_len, udp_checksum};
|
||||
default : tdata <= in_tdata;
|
||||
endcase // case (vef_state)
|
||||
|
||||
endmodule // vrlp_eth_framer
|
||||
@@ -0,0 +1,90 @@
|
||||
|
||||
module vrlp_to_compressed_vita
|
||||
(input clk, input reset, input clear,
|
||||
input [63:0] i_tdata, input i_tlast, input i_tvalid, output i_tready,
|
||||
output [63:0] o_tdata, output o_tlast, output o_tvalid, input o_tready);
|
||||
|
||||
wire [63:0] o_tdata_int;
|
||||
wire o_tlast_int, o_tvalid_int, o_tready_int;
|
||||
|
||||
reg [1:0] v2cv_state;
|
||||
reg [11:0] seqnum;
|
||||
reg trim_line;
|
||||
|
||||
localparam V2CV_VRLP = 2'd0;
|
||||
localparam V2CV_VRTH = 2'd1;
|
||||
localparam V2CV_BODY = 2'd2;
|
||||
localparam V2CV_DUMP = 2'd3;
|
||||
|
||||
wire is_ec = i_tdata[63:60] == 4'h5;
|
||||
wire has_trailer = i_tdata[58] & ~is_ec;
|
||||
wire has_time = |i_tdata[53:52];
|
||||
wire eob = i_tdata[56] & ~is_ec;
|
||||
wire [15:0] len = i_tdata[47:32];
|
||||
wire [31:0] sid = i_tdata[31:0];
|
||||
|
||||
wire [63:0] compressed_hdr = { is_ec, has_trailer, has_time, eob, seqnum, len, sid };
|
||||
wire bad_vita = |i_tdata[55:54] /* has secs */ | i_tdata[59] /* has class */ | ( {i_tdata[63],i_tdata[61:60]} != 3'b001 );
|
||||
reg [15:0] len_reg;
|
||||
wire [16:0] vita_words32 = i_tdata[16:0]-17'd4;
|
||||
assign trim_now = 0;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
begin
|
||||
v2cv_state <= V2CV_VRLP;
|
||||
seqnum <= 12'd0;
|
||||
trim_line <= 1'b0;
|
||||
len_reg <= 16'd0;
|
||||
end
|
||||
else
|
||||
case(v2cv_state)
|
||||
V2CV_VRLP :
|
||||
if(i_tvalid)
|
||||
begin
|
||||
seqnum <= i_tdata[31:20];
|
||||
trim_line <= i_tdata[0];
|
||||
len_reg <= vita_words32[16:1];
|
||||
if(~i_tlast)
|
||||
v2cv_state <= V2CV_VRTH;
|
||||
end
|
||||
|
||||
V2CV_VRTH :
|
||||
if(i_tvalid & o_tready_int)
|
||||
begin
|
||||
len_reg <= len_reg - 16'd1;
|
||||
if(i_tlast)
|
||||
v2cv_state <= V2CV_VRLP;
|
||||
else if(bad_vita)
|
||||
v2cv_state <= V2CV_DUMP;
|
||||
else
|
||||
v2cv_state <= V2CV_BODY;
|
||||
end
|
||||
|
||||
V2CV_BODY :
|
||||
if(i_tvalid & o_tready_int)
|
||||
begin
|
||||
len_reg <= len_reg - 16'd1;
|
||||
if(i_tlast)
|
||||
v2cv_state <= V2CV_VRLP;
|
||||
else if(len_reg == 16'd0)
|
||||
v2cv_state <= V2CV_DUMP;
|
||||
end
|
||||
V2CV_DUMP :
|
||||
if(i_tvalid)
|
||||
if(i_tlast)
|
||||
v2cv_state <= V2CV_VRLP;
|
||||
endcase // case (v2cv_state)
|
||||
|
||||
assign o_tdata_int = (v2cv_state == V2CV_VRTH) ? compressed_hdr : i_tdata;
|
||||
assign o_tlast_int = i_tlast | (len_reg == 16'd0);
|
||||
assign o_tvalid_int = i_tvalid && (((v2cv_state == V2CV_VRTH) && !bad_vita) || (v2cv_state == V2CV_BODY));
|
||||
assign i_tready = o_tready_int | (v2cv_state == V2CV_VRLP) | (v2cv_state == V2CV_DUMP);
|
||||
|
||||
axi_fifo_short #(.WIDTH(65)) short_fifo
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({o_tlast_int,o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int),
|
||||
.o_tdata({o_tlast,o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
|
||||
.space(), .occupied());
|
||||
|
||||
endmodule // vrlp_to_compressed_vita
|
||||
@@ -0,0 +1,120 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module vrlp_to_compressed_vita_tb();
|
||||
|
||||
reg clk = 0;
|
||||
reg reset = 1;
|
||||
|
||||
always #10 clk = ~clk;
|
||||
|
||||
initial $dumpfile("vrlp_to_compressed_vita_tb.vcd");
|
||||
initial $dumpvars(0,vrlp_to_compressed_vita_tb);
|
||||
|
||||
task send_packet;
|
||||
input [63:0] data_start;
|
||||
input [31:0] len;
|
||||
|
||||
begin
|
||||
// Send a packet
|
||||
@(posedge clk);
|
||||
{i_tlast, i_tdata} <= { 1'b0, data_start };
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
i_tdata <= 64'hAAAA_BBBB_CCCC_0000;
|
||||
|
||||
repeat(len-2)
|
||||
begin
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
i_tdata <= i_tdata + 1;
|
||||
end
|
||||
i_tlast <= 1;
|
||||
i_tdata <= i_tdata + 1;
|
||||
@(posedge clk);
|
||||
i_tvalid <= 1'b0;
|
||||
|
||||
@(posedge clk);
|
||||
end
|
||||
endtask // send_packet
|
||||
|
||||
|
||||
initial
|
||||
begin
|
||||
#1000 reset = 0;
|
||||
#200000;
|
||||
$finish;
|
||||
end
|
||||
|
||||
wire [63:0] o_tdata;
|
||||
reg [63:0] i_tdata;
|
||||
wire [2:0] o_tuser;
|
||||
reg [2:0] i_tuser;
|
||||
reg i_tlast;
|
||||
wire o_tlast;
|
||||
wire o_tvalid, i_tready;
|
||||
reg i_tvalid, o_tready;
|
||||
reg i_terror;
|
||||
|
||||
localparam RPT_COUNT = 16;
|
||||
|
||||
initial
|
||||
begin
|
||||
i_tvalid <= 0;
|
||||
o_tready <= 0;
|
||||
|
||||
while(reset)
|
||||
@(posedge clk);
|
||||
@(posedge clk);
|
||||
|
||||
//send_packet(64'hA0,3'd0, 16, 0);
|
||||
send_packet(64'hAABC_0008_DEAD_BEEF, 4);
|
||||
send_packet(64'h7DEF_0008_8765_4321, 4);
|
||||
send_packet(64'hAABC_0007_F00D_1234, 4);
|
||||
send_packet(64'h7DEF_0007_ABCD_4321, 4);
|
||||
o_tready <= 1;
|
||||
//send_packet(64'hC0,3'd0, 16, 1);
|
||||
//send_packet(64'hD0,3'd0, 16, 0);
|
||||
//send_packet(64'hE0,3'd0, 16, 0);
|
||||
//send_packet(64'hF0,3'd0, 16, 0);
|
||||
|
||||
@(posedge clk);
|
||||
|
||||
end // initial begin
|
||||
|
||||
wire i_terror_int, i_tlast_int, i_tready_int, i_tvalid_int;
|
||||
wire [2:0] i_tuser_int;
|
||||
wire [63:0] i_tdata_int;
|
||||
wire o_tlast_int, o_tready_int, o_tvalid_int;
|
||||
wire [2:0] o_tuser_int;
|
||||
wire [63:0] o_tdata_int;
|
||||
wire [63:0] vrlp_tdata;
|
||||
wire vrlp_tlast, vrlp_tvalid, vrlp_tready;
|
||||
|
||||
axi_fifo #(.WIDTH(65), .SIZE(10)) fifo
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({i_tlast,i_tdata}), .i_tvalid(i_tvalid), .i_tready(i_tready),
|
||||
.o_tdata({i_tlast_int,i_tdata_int}), .o_tvalid(i_tvalid_int), .o_tready(i_tready_int));
|
||||
|
||||
compressed_vita_to_vrlp dut0
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata(i_tdata_int), .i_tlast(i_tlast_int), .i_tvalid(i_tvalid_int), .i_tready(i_tready_int),
|
||||
.o_tdata(vrlp_tdata), .o_tlast(vrlp_tlast), .o_tvalid(vrlp_tvalid), .o_tready(vrlp_tready));
|
||||
|
||||
vrlp_to_compressed_vita dut1
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata(vrlp_tdata), .i_tlast(vrlp_tlast), .i_tvalid(vrlp_tvalid), .i_tready(vrlp_tready),
|
||||
.o_tdata(o_tdata_int), .o_tlast(o_tlast_int), .o_tvalid(o_tvalid_int), .o_tready(o_tready_int));
|
||||
|
||||
axi_fifo #(.WIDTH(65), .SIZE(10)) fifo_out
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({o_tlast_int,o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int),
|
||||
.o_tdata({o_tlast,o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready));
|
||||
|
||||
always @(posedge clk)
|
||||
if(o_tvalid & o_tready)
|
||||
begin
|
||||
$display("%x",o_tdata);
|
||||
if(o_tlast)
|
||||
$display("======EOF========");
|
||||
end
|
||||
endmodule // vrlp_to_compressed_vita_tb
|
||||
@@ -0,0 +1,11 @@
|
||||
#
|
||||
# Copyright 2013 Ettus Research LLC
|
||||
#
|
||||
|
||||
##################################################
|
||||
# Timing Sources
|
||||
##################################################
|
||||
TIMING_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/timing/, \
|
||||
time_compare.v \
|
||||
timekeeper.v \
|
||||
))
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// Copyright 2011-2012 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
|
||||
// 64 bits worth of ticks
|
||||
//
|
||||
// Not concerned with clock wrapping, human race will likely have extermintated it's self by this time.
|
||||
//
|
||||
|
||||
module time_compare
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input [63:0] time_now,
|
||||
input [63:0] trigger_time,
|
||||
output now,
|
||||
output early,
|
||||
output late,
|
||||
output too_early);
|
||||
|
||||
/*
|
||||
reg [63:0] time_diff;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (reset) begin
|
||||
time_diff <= 64'b0;
|
||||
now <= 1'b0;
|
||||
late <= 1'b0;
|
||||
early <= 1'b0;
|
||||
end
|
||||
else begin
|
||||
time_diff <= trigger_time - time_now;
|
||||
now <= ~(|time_diff);
|
||||
late <= time_diff[63];
|
||||
early <= ~now & ~late;
|
||||
end
|
||||
end
|
||||
//assign now = ~(|time_diff);
|
||||
//assign late = time_diff[63];
|
||||
//assign early = ~now & ~late;
|
||||
assign too_early = 0; //not implemented
|
||||
*/
|
||||
|
||||
assign now = time_now == trigger_time;
|
||||
assign late = time_now > trigger_time;
|
||||
assign early = ~now & ~late;
|
||||
assign too_early = 0; //not implemented
|
||||
|
||||
endmodule // time_compare
|
||||
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module time_transfer_tb();
|
||||
|
||||
reg clk = 0, rst = 1;
|
||||
always #5 clk = ~clk;
|
||||
|
||||
initial
|
||||
begin
|
||||
@(negedge clk);
|
||||
@(negedge clk);
|
||||
rst <= 0;
|
||||
end
|
||||
|
||||
initial $dumpfile("time_transfer_tb.vcd");
|
||||
initial $dumpvars(0,time_transfer_tb);
|
||||
|
||||
initial #100000000 $finish;
|
||||
|
||||
wire exp_time, pps, pps_rcv;
|
||||
wire [63:0] vita_time_rcv;
|
||||
reg [63:0] vita_time = 0;
|
||||
reg [63:0] counter = 0;
|
||||
|
||||
localparam PPS_PERIOD = 439; // PPS_PERIOD % 10 must = 9
|
||||
always @(posedge clk)
|
||||
if(counter == PPS_PERIOD)
|
||||
counter <= 0;
|
||||
else
|
||||
counter <= counter + 1;
|
||||
assign pps = (counter == (PPS_PERIOD-1));
|
||||
|
||||
always @(posedge clk)
|
||||
vita_time <= vita_time + 1;
|
||||
|
||||
time_sender time_sender
|
||||
(.clk(clk),.rst(rst),
|
||||
.vita_time(vita_time),
|
||||
.send_sync(pps),
|
||||
.exp_time_out(exp_time) );
|
||||
|
||||
time_receiver time_receiver
|
||||
(.clk(clk),.rst(rst),
|
||||
.vita_time(vita_time_rcv),
|
||||
.sync_rcvd(pps_rcv),
|
||||
.exp_time_in(exp_time) );
|
||||
|
||||
wire [31:0] delta = vita_time - vita_time_rcv;
|
||||
endmodule // time_transfer_tb
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user