diff options
Diffstat (limited to 'fpga/usrp2/vrt')
-rw-r--r-- | fpga/usrp2/vrt/.gitignore | 4 | ||||
-rw-r--r-- | fpga/usrp2/vrt/Makefile.srcs | 17 | ||||
-rw-r--r-- | fpga/usrp2/vrt/gen_context_pkt.v | 91 | ||||
-rw-r--r-- | fpga/usrp2/vrt/trigger_context_pkt.v | 52 | ||||
-rwxr-xr-x | fpga/usrp2/vrt/vita_rx.build | 1 | ||||
-rw-r--r-- | fpga/usrp2/vrt/vita_rx_chain.v | 42 | ||||
-rw-r--r-- | fpga/usrp2/vrt/vita_rx_control.v | 199 | ||||
-rw-r--r-- | fpga/usrp2/vrt/vita_rx_framer.v | 209 | ||||
-rw-r--r-- | fpga/usrp2/vrt/vita_rx_tb.v | 229 | ||||
-rwxr-xr-x | fpga/usrp2/vrt/vita_tx.build | 1 | ||||
-rw-r--r-- | fpga/usrp2/vrt/vita_tx_chain.v | 101 | ||||
-rw-r--r-- | fpga/usrp2/vrt/vita_tx_control.v | 214 | ||||
-rw-r--r-- | fpga/usrp2/vrt/vita_tx_deframer.v | 232 | ||||
-rw-r--r-- | fpga/usrp2/vrt/vita_tx_tb.v | 176 |
14 files changed, 1568 insertions, 0 deletions
diff --git a/fpga/usrp2/vrt/.gitignore b/fpga/usrp2/vrt/.gitignore new file mode 100644 index 000000000..446b2daae --- /dev/null +++ b/fpga/usrp2/vrt/.gitignore @@ -0,0 +1,4 @@ +vita_rx_tb +vita_tx_tb +*.vcd +*.sav diff --git a/fpga/usrp2/vrt/Makefile.srcs b/fpga/usrp2/vrt/Makefile.srcs new file mode 100644 index 000000000..4851bc924 --- /dev/null +++ b/fpga/usrp2/vrt/Makefile.srcs @@ -0,0 +1,17 @@ +# +# Copyright 2010 Ettus Research LLC +# + +################################################## +# VRT Sources +################################################## +VRT_SRCS = $(abspath $(addprefix $(BASE_DIR)/../vrt/, \ +vita_rx_control.v \ +vita_rx_framer.v \ +vita_rx_chain.v \ +vita_tx_control.v \ +vita_tx_deframer.v \ +vita_tx_chain.v \ +gen_context_pkt.v \ +trigger_context_pkt.v \ +)) diff --git a/fpga/usrp2/vrt/gen_context_pkt.v b/fpga/usrp2/vrt/gen_context_pkt.v new file mode 100644 index 000000000..cc34cceed --- /dev/null +++ b/fpga/usrp2/vrt/gen_context_pkt.v @@ -0,0 +1,91 @@ + + +module gen_context_pkt + #(parameter PROT_ENG_FLAGS=1, + parameter DSP_NUMBER=0) + (input clk, input reset, input clear, + input trigger, output sent, + input [31:0] streamid, + input [63:0] vita_time, + input [31:0] message, + input [31:0] seqnum, + output [35:0] data_o, output src_rdy_o, input dst_rdy_i); + + localparam CTXT_IDLE = 0; + localparam CTXT_PROT_ENG = 1; + localparam CTXT_HEADER = 2; + localparam CTXT_STREAMID = 3; + localparam CTXT_SECS = 4; + localparam CTXT_TICS = 5; + localparam CTXT_TICS2 = 6; + localparam CTXT_MESSAGE = 7; + localparam CTXT_FLOWCTRL = 8; + localparam CTXT_DONE = 9; + + reg [33:0] data_int; + wire src_rdy_int, dst_rdy_int; + reg [3:0] seqno; + reg [3:0] ctxt_state; + reg [63:0] err_time; + reg [31:0] stored_message; + + always @(posedge clk) + if(reset | clear) + stored_message <= 0; + else + if(trigger) + stored_message <= message; + else if(ctxt_state == CTXT_DONE) + stored_message <= 0; + + // Don't want to clear most of this to avoid getting stuck with a half packet in the pipe + always @(posedge clk) + if(reset) + begin + ctxt_state <= CTXT_IDLE; + seqno <= 0; + end + else + case(ctxt_state) + CTXT_IDLE : + if(trigger) + begin + err_time <= vita_time; + if(PROT_ENG_FLAGS) + ctxt_state <= CTXT_PROT_ENG; + else + ctxt_state <= CTXT_HEADER; + end + + CTXT_DONE : + begin + ctxt_state <= CTXT_IDLE; + seqno <= seqno + 4'd1; + end + default : + if(dst_rdy_int) + ctxt_state <= ctxt_state + 1; + endcase // case (ctxt_state) + + assign src_rdy_int = ~( (ctxt_state == CTXT_IDLE) | (ctxt_state == CTXT_DONE) ); + + always @* + case(ctxt_state) + CTXT_PROT_ENG : data_int <= { 2'b01, 13'b0, DSP_NUMBER[0], 1'b1, 1'b1, 16'd28 }; // UDP port 1 or 3 + CTXT_HEADER : data_int <= { 1'b0, (PROT_ENG_FLAGS ? 1'b0 : 1'b1), 12'b010100001101, seqno, 16'd7 }; + CTXT_STREAMID : data_int <= { 2'b00, streamid }; + CTXT_SECS : data_int <= { 2'b00, err_time[63:32] }; + CTXT_TICS : data_int <= { 2'b00, 32'd0 }; + CTXT_TICS2 : data_int <= { 2'b00, err_time[31:0] }; + CTXT_MESSAGE : data_int <= { 2'b00, message }; + CTXT_FLOWCTRL : data_int <= { 2'b10, seqnum }; + default : data_int <= {2'b00, 32'b00}; + endcase // case (ctxt_state) + + fifo_short #(.WIDTH(34)) ctxt_fifo + (.clk(clk), .reset(reset), .clear(0), + .datain(data_int), .src_rdy_i(src_rdy_int), .dst_rdy_o(dst_rdy_int), + .dataout(data_o[33:0]), .src_rdy_o(src_rdy_o), .dst_rdy_i(dst_rdy_i)); + assign data_o[35:34] = 2'b00; + +endmodule // gen_context_pkt diff --git a/fpga/usrp2/vrt/trigger_context_pkt.v b/fpga/usrp2/vrt/trigger_context_pkt.v new file mode 100644 index 000000000..226ec45f2 --- /dev/null +++ b/fpga/usrp2/vrt/trigger_context_pkt.v @@ -0,0 +1,52 @@ + + +module trigger_context_pkt + #(parameter BASE=0) + (input clk, input reset, input clear, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + input packet_consumed, output reg trigger); + + wire [23:0] cycles; + wire [15:0] packets; + wire [6:0] dummy1; + wire [14:0] dummy2; + wire enable_timed, enable_consumed; + reg [30:0] cycle_count, packet_count; + + + setting_reg #(.my_addr(BASE+4), .at_reset(0)) sr_cycles + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out({enable_cycle,dummy1,cycles}),.changed()); + + setting_reg #(.my_addr(BASE+5), .at_reset(0)) sr_packets + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out({enable_consumed,dummy2,packets}),.changed()); + + always @(posedge clk) + if(reset | clear) + cycle_count <= 0; + else + if(trigger) + cycle_count <= 0; + else if(enable_cycle) + cycle_count <= cycle_count + 1; + + always @(posedge clk) + if(reset | clear) + packet_count <= 0; + else + if(trigger) + packet_count <= 0; + else if(packet_consumed & enable_consumed) + packet_count <= packet_count + 1; + + always @(posedge clk) + if(reset | clear) + trigger <= 0; + else + if((cycle_count > cycles)|(packet_count > packets)) + trigger <= 1; + else + trigger <= 0; + +endmodule // trigger_context_pkt diff --git a/fpga/usrp2/vrt/vita_rx.build b/fpga/usrp2/vrt/vita_rx.build new file mode 100755 index 000000000..010d1be6e --- /dev/null +++ b/fpga/usrp2/vrt/vita_rx.build @@ -0,0 +1 @@ +iverilog -Wimplict -Wportbind -y ../models -y . -y ../control_lib/ -y ../fifo -y ../coregen -y /opt/Xilinx/10.1/ISE/verilog/src/XilinxCoreLib -y /opt/Xilinx/10.1/ISE/verilog/src/unisims/ -y ../timing -o vita_rx_tb vita_rx_tb.v diff --git a/fpga/usrp2/vrt/vita_rx_chain.v b/fpga/usrp2/vrt/vita_rx_chain.v new file mode 100644 index 000000000..d7498286d --- /dev/null +++ b/fpga/usrp2/vrt/vita_rx_chain.v @@ -0,0 +1,42 @@ + +module vita_rx_chain + #(parameter BASE=0, + parameter UNIT=0, + parameter FIFOSIZE=10) + (input clk, input reset, input clear, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + input [63:0] vita_time, output overrun, + input [31:0] sample, output run, input strobe, + output [35:0] rx_data_o, output rx_src_rdy_o, input rx_dst_rdy_i, + output [31:0] debug ); + + wire [100:0] sample_data; + wire sample_dst_rdy, sample_src_rdy; + wire [31:0] vrc_debug, vrf_debug; + + wire [35:0] rx_data_int; + wire rx_src_rdy_int, rx_dst_rdy_in; + + vita_rx_control #(.BASE(BASE), .WIDTH(32)) vita_rx_control + (.clk(clk), .reset(reset), .clear(clear), + .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + .vita_time(vita_time), .overrun(overrun), + .sample(sample), .run(run), .strobe(strobe), + .sample_fifo_o(sample_data), .sample_fifo_dst_rdy_i(sample_dst_rdy), .sample_fifo_src_rdy_o(sample_src_rdy), + .debug_rx(vrc_debug)); + + vita_rx_framer #(.BASE(BASE), .MAXCHAN(1)) vita_rx_framer + (.clk(clk), .reset(reset), .clear(clear), + .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + .sample_fifo_i(sample_data), .sample_fifo_dst_rdy_o(sample_dst_rdy), .sample_fifo_src_rdy_i(sample_src_rdy), + .data_o(rx_data_int), .src_rdy_o(rx_src_rdy_int), .dst_rdy_i(rx_dst_rdy_int), + .debug_rx(vrf_debug) ); + + dsp_framer36 #(.BUF_SIZE(FIFOSIZE), .PORT_SEL(UNIT)) dsp0_framer36 + (.clk(clk), .reset(reset), .clear(clear), + .data_i(rx_data_int), .src_rdy_i(rx_src_rdy_int), .dst_rdy_o(rx_dst_rdy_int), + .data_o(rx_data_o), .src_rdy_o(rx_src_rdy_o), .dst_rdy_i(rx_dst_rdy_i) ); + + assign debug = vrc_debug; // | vrf_debug; + +endmodule // vita_rx_chain diff --git a/fpga/usrp2/vrt/vita_rx_control.v b/fpga/usrp2/vrt/vita_rx_control.v new file mode 100644 index 000000000..4c0cef50d --- /dev/null +++ b/fpga/usrp2/vrt/vita_rx_control.v @@ -0,0 +1,199 @@ + +module vita_rx_control + #(parameter BASE=0, + parameter WIDTH=32) + (input clk, input reset, input clear, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + input [63:0] vita_time, + output overrun, + + // To vita_rx_framer + output [5+64+WIDTH-1:0] sample_fifo_o, + output sample_fifo_src_rdy_o, + input sample_fifo_dst_rdy_i, + + // From DSP Core + input [WIDTH-1:0] sample, + output run, + input strobe, + + output [31:0] debug_rx + ); + + // FIXME add TX Interruption (halt, pause, continue) functionality + + wire [63:0] new_time; + wire [31:0] new_command; + wire sc_pre1; + + wire [63:0] rcvtime_pre; + reg [63:0] rcvtime; + wire [28:0] numlines_pre; + wire send_imm_pre, chain_pre, reload_pre; + reg send_imm, chain, reload; + wire read_ctrl, not_empty_ctrl, write_ctrl; + reg sc_pre2; + wire [33:0] fifo_line; + reg [28:0] lines_left, lines_total; + reg [2:0] ibs_state; + wire now, early, late; + wire sample_fifo_in_rdy; + + setting_reg #(.my_addr(BASE)) sr_cmd + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(new_command),.changed()); + + setting_reg #(.my_addr(BASE+1)) sr_time_h + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(new_time[63:32]),.changed()); + + setting_reg #(.my_addr(BASE+2)) sr_time_l + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(new_time[31:0]),.changed(sc_pre1)); + + // FIFO to store commands sent from the settings bus + always @(posedge clk) + if(reset | clear) + sc_pre2 <= 0; + else + sc_pre2 <= sc_pre1; + + assign write_ctrl = sc_pre1 & ~sc_pre2; + + wire [4:0] command_queue_len; + + fifo_short #(.WIDTH(96)) commandfifo + (.clk(clk),.reset(reset),.clear(clear), + .datain({new_command,new_time}), .src_rdy_i(write_ctrl), .dst_rdy_o(), + .dataout({send_imm_pre,chain_pre,reload_pre,numlines_pre,rcvtime_pre}), + .src_rdy_o(not_empty_ctrl), .dst_rdy_i(read_ctrl), + .occupied(command_queue_len), .space() ); + + reg [33:0] pkt_fifo_line; + + localparam IBS_IDLE = 0; + localparam IBS_WAITING = 1; + localparam IBS_RUNNING = 2; + localparam IBS_OVERRUN = 4; + localparam IBS_BROKENCHAIN = 5; + localparam IBS_LATECMD = 6; + localparam IBS_ZEROLEN = 7; + + wire signal_cmd_done = (lines_left == 1) & (~chain | (not_empty_ctrl & (numlines_pre==0))); + wire signal_overrun = (ibs_state == IBS_OVERRUN); + wire signal_brokenchain = (ibs_state == IBS_BROKENCHAIN); + wire signal_latecmd = (ibs_state == IBS_LATECMD); + wire signal_zerolen = (ibs_state == IBS_ZEROLEN); + + // Buffer of samples for while we're writing the packet headers + wire [4:0] flags = {signal_zerolen,signal_overrun,signal_brokenchain,signal_latecmd,signal_cmd_done}; + + wire attempt_sample_write = ((run & strobe) | (ibs_state==IBS_OVERRUN) | + (ibs_state==IBS_BROKENCHAIN) | (ibs_state==IBS_LATECMD) | + (ibs_state==IBS_ZEROLEN)); + + fifo_short #(.WIDTH(5+64+WIDTH)) rx_sample_fifo + (.clk(clk),.reset(reset),.clear(clear), + .datain({flags,vita_time,sample}), .src_rdy_i(attempt_sample_write), .dst_rdy_o(sample_fifo_in_rdy), + .dataout(sample_fifo_o), + .src_rdy_o(sample_fifo_src_rdy_o), .dst_rdy_i(sample_fifo_dst_rdy_i), + .space(), .occupied() ); + + // Inband Signalling State Machine + time_compare + time_compare (.time_now(vita_time), .trigger_time(rcvtime), .now(now), .early(early), .late(late)); + + wire too_late = late & ~send_imm; + wire go_now = now | send_imm; + wire full = ~sample_fifo_in_rdy; + + always @(posedge clk) + if(reset | clear) + begin + ibs_state <= IBS_IDLE; + lines_left <= 0; + lines_total <= 0; + rcvtime <= 0; + send_imm <= 0; + chain <= 0; + reload <= 0; + end + else + case(ibs_state) + IBS_IDLE : + if(not_empty_ctrl) + begin + lines_left <= numlines_pre; + lines_total <= numlines_pre; + rcvtime <= rcvtime_pre; + if(numlines_pre == 0) + ibs_state <= IBS_ZEROLEN; + else + ibs_state <= IBS_WAITING; + send_imm <= send_imm_pre; + chain <= chain_pre; + reload <= reload_pre; + end + IBS_WAITING : + if(go_now) + ibs_state <= IBS_RUNNING; + else if(too_late) + ibs_state <= IBS_LATECMD; + IBS_RUNNING : + if(strobe) + if(full) + ibs_state <= IBS_OVERRUN; + else + begin + lines_left <= lines_left - 1; + if(lines_left == 1) + if(~chain) + ibs_state <= IBS_IDLE; + else if(~not_empty_ctrl & reload) + begin + ibs_state <= IBS_RUNNING; + lines_left <= lines_total; + end + else if(~not_empty_ctrl) + ibs_state <= IBS_BROKENCHAIN; + else + begin + lines_left <= numlines_pre; + lines_total <= numlines_pre; + rcvtime <= rcvtime_pre; + send_imm <= send_imm_pre; + chain <= chain_pre; + reload <= reload_pre; + if(numlines_pre == 0) // If we are told to stop here + ibs_state <= IBS_IDLE; + else + ibs_state <= IBS_RUNNING; + end + end // else: !if(full) + IBS_OVERRUN : + if(sample_fifo_in_rdy) + ibs_state <= IBS_IDLE; + IBS_LATECMD : + if(sample_fifo_in_rdy) + ibs_state <= IBS_IDLE; + IBS_BROKENCHAIN : + if(sample_fifo_in_rdy) + ibs_state <= IBS_IDLE; + IBS_ZEROLEN : + if(sample_fifo_in_rdy) + ibs_state <= IBS_IDLE; + endcase // case(ibs_state) + + assign overrun = (ibs_state == IBS_OVERRUN); + assign run = (ibs_state == IBS_RUNNING); + + assign read_ctrl = ( (ibs_state == IBS_IDLE) | ((ibs_state == IBS_RUNNING) & strobe & ~full & (lines_left==1) & chain) ) + & not_empty_ctrl; + + assign debug_rx = { { ibs_state[2:0], command_queue_len }, + { 8'd0 }, + { go_now, too_late, run, strobe, read_ctrl, write_ctrl, 1'b0, ~not_empty_ctrl }, + { 2'b0, overrun, chain_pre, sample_fifo_in_rdy, attempt_sample_write, sample_fifo_src_rdy_o,sample_fifo_dst_rdy_i} }; + +endmodule // vita_rx_control diff --git a/fpga/usrp2/vrt/vita_rx_framer.v b/fpga/usrp2/vrt/vita_rx_framer.v new file mode 100644 index 000000000..04b636778 --- /dev/null +++ b/fpga/usrp2/vrt/vita_rx_framer.v @@ -0,0 +1,209 @@ + +module vita_rx_framer + #(parameter BASE=0, + parameter MAXCHAN=1) + (input clk, input reset, input clear, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + // To FIFO interface of Buffer Pool + output [35:0] data_o, + input dst_rdy_i, + output src_rdy_o, + + // From vita_rx_control + input [5+64+(32*MAXCHAN)-1:0] sample_fifo_i, + input sample_fifo_src_rdy_i, + output sample_fifo_dst_rdy_o, + + output [31:0] debug_rx + ); + + localparam SAMP_WIDTH = 5+64+(32*MAXCHAN); + reg [3:0] sample_phase; + wire [3:0] numchan; + wire [4:0] flags_fifo_o = sample_fifo_i[SAMP_WIDTH-1:SAMP_WIDTH-5]; + wire [63:0] vita_time_fifo_o = sample_fifo_i[SAMP_WIDTH-6:SAMP_WIDTH-69]; + + reg [31:0] data_fifo_o; + + // The tools won't synthesize properly without this kludge because of the variable + // parameter length + + wire [127:0] FIXED_WIDTH_KLUDGE = sample_fifo_i; + always @* + case(sample_phase) + 4'd0 : data_fifo_o = FIXED_WIDTH_KLUDGE[31:0]; + 4'd1 : data_fifo_o = FIXED_WIDTH_KLUDGE[63:32]; + 4'd2 : data_fifo_o = FIXED_WIDTH_KLUDGE[95:64]; + 4'd3 : data_fifo_o = FIXED_WIDTH_KLUDGE[127:96]; + default : data_fifo_o = 32'hDEADBEEF; + endcase // case (sample_phase) + + wire clear_pkt_count, pkt_fifo_rdy, sample_fifo_in_rdy; + + wire [31:0] vita_header, vita_streamid, vita_trailer; + wire [15:0] samples_per_packet; + + reg [33:0] pkt_fifo_line; + reg [3:0] vita_state; + reg [15:0] sample_ctr; + reg [3:0] pkt_count; + + wire [15:0] vita_pkt_len = samples_per_packet + 6; + //wire [4:0] flags = {signal_zerolen,signal_overrun,signal_brokenchain,signal_latecmd,signal_cmd_done}; + + setting_reg #(.my_addr(BASE+4)) sr_header + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(vita_header),.changed()); + + setting_reg #(.my_addr(BASE+5)) sr_streamid + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(vita_streamid),.changed(clear_pkt_count)); + + setting_reg #(.my_addr(BASE+6)) sr_trailer + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(vita_trailer),.changed()); + + setting_reg #(.my_addr(BASE+7),.width(16)) sr_samples_per_pkt + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(samples_per_packet),.changed()); + + setting_reg #(.my_addr(BASE+8),.width(4), .at_reset(1)) sr_numchan + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(numchan),.changed()); + + // Output FIFO for packetized data + localparam VITA_IDLE = 0; + localparam VITA_HEADER = 1; + localparam VITA_STREAMID = 2; + localparam VITA_SECS = 3; + localparam VITA_TICS = 4; + localparam VITA_TICS2 = 5; + localparam VITA_PAYLOAD = 6; + localparam VITA_TRAILER = 7; + localparam VITA_ERR_HEADER = 9; // All ERR at 4'b1000 or'ed with base + localparam VITA_ERR_STREAMID = 10; + localparam VITA_ERR_SECS = 11; + localparam VITA_ERR_TICS = 12; + localparam VITA_ERR_TICS2 = 13; + localparam VITA_ERR_PAYLOAD = 14; + localparam VITA_ERR_TRAILER = 15; // Extension context packets have no trailer + + always @(posedge clk) + if(reset | clear | clear_pkt_count) + pkt_count <= 0; + else if((vita_state == VITA_TRAILER) & pkt_fifo_rdy) + pkt_count <= pkt_count + 1; + + wire has_streamid = vita_header[28]; + wire has_trailer = vita_header[26]; + reg trl_eob; + + always @* + case(vita_state) + // Data packets are IF Data packets with or w/o streamid, no classid, with trailer + VITA_HEADER : pkt_fifo_line <= {2'b01,3'b000,vita_header[28],2'b01,vita_header[25:24], + vita_header[23:20],pkt_count[3:0],vita_pkt_len[15:0]}; + VITA_STREAMID : pkt_fifo_line <= {2'b00,vita_streamid}; + VITA_SECS : pkt_fifo_line <= {2'b00,vita_time_fifo_o[63:32]}; + VITA_TICS : pkt_fifo_line <= {2'b00,32'd0}; + VITA_TICS2 : pkt_fifo_line <= {2'b00,vita_time_fifo_o[31:0]}; + VITA_PAYLOAD : pkt_fifo_line <= {2'b00,data_fifo_o}; + VITA_TRAILER : pkt_fifo_line <= {2'b10,vita_trailer[31:21],1'b1,vita_trailer[19:9],trl_eob,8'd0}; + + // Error packets are Extension Context packets, which have no trailer + VITA_ERR_HEADER : pkt_fifo_line <= {2'b01,4'b0101,4'b0000,vita_header[23:20],pkt_count,16'd6}; + VITA_ERR_STREAMID : pkt_fifo_line <= {2'b00,vita_streamid}; + VITA_ERR_SECS : pkt_fifo_line <= {2'b00,vita_time_fifo_o[63:32]}; + VITA_ERR_TICS : pkt_fifo_line <= {2'b00,32'd0}; + VITA_ERR_TICS2 : pkt_fifo_line <= {2'b00,vita_time_fifo_o[31:0]}; + VITA_ERR_PAYLOAD : pkt_fifo_line <= {2'b10,27'd0,flags_fifo_o}; + //VITA_ERR_TRAILER : pkt_fifo_line <= {2'b11,vita_trailer}; + + default : pkt_fifo_line <= 34'h0_FFFF_FFFF; + endcase // case (vita_state) + + always @(posedge clk) + if(reset | clear) + begin + vita_state <= VITA_IDLE; + sample_ctr <= 0; + sample_phase <= 0; + end + else + if(vita_state==VITA_IDLE) + begin + sample_ctr <= 1; + sample_phase <= 0; + if(sample_fifo_src_rdy_i) + if(|flags_fifo_o[4:1]) + vita_state <= VITA_ERR_HEADER; + else + vita_state <= VITA_HEADER; + end + else if(pkt_fifo_rdy) + case(vita_state) + VITA_HEADER : + if(has_streamid) + vita_state <= VITA_STREAMID; + else + vita_state <= VITA_SECS; + VITA_PAYLOAD : + if(sample_fifo_src_rdy_i) + begin + if(sample_phase == (numchan-4'd1)) + begin + sample_phase <= 0; + sample_ctr <= sample_ctr + 1; + trl_eob <= flags_fifo_o[0]; + if(sample_ctr == samples_per_packet) + vita_state <= VITA_TRAILER; + if(|flags_fifo_o) // end early if any flag is set + vita_state <= VITA_TRAILER; + end + else + sample_phase <= sample_phase + 1; + end // if (sample_fifo_src_rdy_i) + VITA_ERR_PAYLOAD : + vita_state <= VITA_IDLE; + VITA_TRAILER : + vita_state <= VITA_IDLE; + default : + vita_state <= vita_state + 1; + endcase // case (vita_state) + + reg req_write_pkt_fifo; + always @* + case(vita_state) + VITA_IDLE : + req_write_pkt_fifo <= 0; + VITA_HEADER, VITA_STREAMID, VITA_SECS, VITA_TICS, VITA_TICS2, VITA_TRAILER : + req_write_pkt_fifo <= 1; + VITA_PAYLOAD : + // Write if sample ready and no error flags + req_write_pkt_fifo <= (sample_fifo_src_rdy_i & ~|flags_fifo_o[4:1]); + VITA_ERR_HEADER, VITA_ERR_STREAMID, VITA_ERR_SECS, VITA_ERR_TICS, VITA_ERR_TICS2, VITA_ERR_PAYLOAD : + req_write_pkt_fifo <= 1; + default : + req_write_pkt_fifo <= 0; + endcase // case (vita_state) + + //wire req_write_pkt_fifo = (vita_state != VITA_IDLE) & (sample_fifo_src_rdy_i | (vita_state != VITA_PAYLOAD)); + + // Short FIFO to buffer between us and the FIFOs outside + fifo_short #(.WIDTH(34)) rx_pkt_fifo + (.clk(clk), .reset(reset), .clear(clear), + .datain(pkt_fifo_line), .src_rdy_i(req_write_pkt_fifo), .dst_rdy_o(pkt_fifo_rdy), + .dataout(data_o[33:0]), .src_rdy_o(src_rdy_o), .dst_rdy_i(dst_rdy_i), + .space(),.occupied() ); + + assign data_o[35:34] = 2'b00; // Always write full lines + assign sample_fifo_dst_rdy_o = pkt_fifo_rdy & + ( ((vita_state==VITA_PAYLOAD) & + (sample_phase == (numchan-4'd1)) & + ~|flags_fifo_o[4:1]) | + (vita_state==VITA_ERR_PAYLOAD)); + + assign debug_rx = vita_state; + +endmodule // vita_rx_framer diff --git a/fpga/usrp2/vrt/vita_rx_tb.v b/fpga/usrp2/vrt/vita_rx_tb.v new file mode 100644 index 000000000..023934f39 --- /dev/null +++ b/fpga/usrp2/vrt/vita_rx_tb.v @@ -0,0 +1,229 @@ + + +module vita_rx_tb; + + localparam DECIM = 8'd4; + localparam MAXCHAN=1; + localparam NUMCHAN=1; + + reg clk = 0; + reg reset = 1; + + initial #1000 reset = 0; + always #50 clk = ~clk; + + initial $dumpfile("vita_rx_tb.vcd"); + initial $dumpvars(0,vita_rx_tb); + + wire [(MAXCHAN*32)-1:0] sample; + wire strobe, run; + wire [35:0] data_o; + wire src_rdy; + reg dst_rdy = 1; + wire [63:0] vita_time; + + reg set_stb = 0; + reg [7:0] set_addr; + reg [31:0] set_data; + wire set_stb_dsp; + wire [7:0] set_addr_dsp; + wire [31:0] set_data_dsp; + + /* + settings_bus_crossclock settings_bus_xclk_dsp + (.clk_i(clk), .rst_i(reset), .set_stb_i(set_stb), .set_addr_i(set_addr), .set_data_i(set_data), + .clk_o(clk), .rst_o(reset), .set_stb_o(set_stb_dsp), .set_addr_o(set_addr_dsp), .set_data_o(set_data_dsp)); + */ + + wire sample_dst_rdy, sample_src_rdy; + //wire [99:0] sample_data_o; + wire [64+5+(MAXCHAN*32)-1:0] sample_data_o; + + vita_rx_control #(.BASE(0), .WIDTH(32*MAXCHAN)) vita_rx_control + (.clk(clk), .reset(reset), .clear(0), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .vita_time(vita_time), .overrun(overrun), + .sample_fifo_o(sample_data_o), .sample_fifo_dst_rdy_i(sample_dst_rdy), .sample_fifo_src_rdy_o(sample_src_rdy), + .sample(sample), .run(run), .strobe(strobe)); + + vita_rx_framer #(.BASE(0), .MAXCHAN(MAXCHAN)) vita_rx_framer + (.clk(clk), .reset(reset), .clear(0), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .data_o(data_o), .dst_rdy_i(dst_rdy), .src_rdy_o(src_rdy), + .sample_fifo_i(sample_data_o), .sample_fifo_dst_rdy_o(sample_dst_rdy), .sample_fifo_src_rdy_i(sample_src_rdy), + .fifo_occupied(), .fifo_full(), .fifo_empty() ); + + rx_dsp_model rx_dsp_model + (.clk(clk), .reset(reset), .run(run), .decim(DECIM), .strobe(strobe), .sample(sample[31:0])); + + generate + if(MAXCHAN>1) + assign sample[(MAXCHAN*32)-1:32] = 0; + endgenerate + + time_64bit #(.TICKS_PER_SEC(120000000), .BASE(0)) time_64bit + (.clk(clk), .rst(reset), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .pps(0), .vita_time(vita_time)); + + always @(posedge clk) + if(src_rdy & dst_rdy) + begin + if(data_o[32] & ~data_o[33]) + begin + $display("RX-PKT-START %d",$time); + $display(" RX-PKT-DAT %x",data_o[31:0]); + end + else if(data_o[32] & data_o[33]) + begin + $display(" RX-PKT-DAT %x -- With ERR",data_o[31:0]); + $display("RX-PKT-ERR %d",$time); + end + else if(~data_o[32] & data_o[33]) + begin + $display(" RX-PKT-DAT %x",data_o[31:0]); + $display("RX-PKT-END %d",$time); + end + else + $display(" RX-PKT DAT %x",data_o[31:0]); + end + + initial + begin + @(negedge reset); + @(posedge clk); + write_setting(4,32'h15F00000); // VITA header + write_setting(5,32'hF00D1234); // VITA streamid + write_setting(6,32'hE0000000); // VITA trailer + write_setting(7,8); // Samples per VITA packet + write_setting(8,NUMCHAN); // Vector length + + queue_rx_cmd(1,1,0,10,32'h0,32'h0); // send imm, single packet + #10000; + + queue_rx_cmd(1,0,0,0,32'h0,32'h0); // send imm, single packet + //queue_rx_cmd(1,1,0,0,32'h0,32'h0); // send imm, single packet + + //queue_rx_cmd(1,0,0,0,32'h0,32'h0); // send imm, single packet + + /* + queue_rx_cmd(1,0,0,8,32'h0,32'h0); // send imm, single packet + queue_rx_cmd(1,0,0,16,32'h0,32'h0); // send imm, 2 packets worth + queue_rx_cmd(1,0,0,7,32'h0,32'h0); // send imm, 1 short packet worth + queue_rx_cmd(1,0,0,9,32'h0,32'h0); // send imm, just longer than 1 packet + + queue_rx_cmd(1,1,0,16,32'h0,32'h0); // chained + queue_rx_cmd(0,0,0,8,32'h0,32'h0); // 2nd in chain + + queue_rx_cmd(1,1,0,17,32'h0,32'h0); // chained, odd length + queue_rx_cmd(0,0,0,9,32'h0,32'h0); // 2nd in chain, also odd length + + queue_rx_cmd(0,0,0,8,32'h0,32'h340); // send at, on time + queue_rx_cmd(0,0,0,8,32'h0,32'h100); // send at, but late + + #100000; + $display("\nChained, break chain\n"); + queue_rx_cmd(1,1,0,8,32'h0,32'h0); // chained, but break chain + #100000; + $display("\nSingle packet\n"); + queue_rx_cmd(1,0,0,8,32'h0,32'h0); // send imm, single packet + #100000; + $display("\nEnd chain with zero samples, shouldn't error\n"); + queue_rx_cmd(1,1,0,8,32'h0,32'h0); // chained + queue_rx_cmd(0,0,0,0,32'h0,32'h0); // end chain with zero samples, should keep us out of error + #100000; + + $display("\nEnd chain with zero samples on odd-length, shouldn't error\n"); + queue_rx_cmd(1,1,0,14,32'h0,32'h0); // chained + queue_rx_cmd(0,0,0,0,32'h0,32'h0); // end chain with zero samples, should keep us out of error + #100000; + $display("Should have gotten 14 samples and EOF by now\n"); + + queue_rx_cmd(1,1,0,9,32'h0,32'h0); // chained, but break chain, odd length + #100000; + dst_rdy <= 0; // stop pulling out of fifo so we can get an overrun + queue_rx_cmd(1,0,0,100,32'h0,32'h0); // long enough to fill the fifos + queue_rx_cmd(1,0,0,5,32'h0,32'h0); // this command waits until the previous error packet is sent + #100000; + dst_rdy <= 1; // restart the reads so we can see what we got + #100000; + dst_rdy <= 0; // stop pulling out of fifo so we can get an overrun + queue_rx_cmd(1,1,0,100,32'h0,32'h0); // long enough to fill the fifos + //queue_rx_cmd(1,0,0,5,32'h0,32'h0); // this command waits until the previous error packet is sent + #100000; + @(posedge clk); + dst_rdy <= 1; + */ + #100000 $finish; + end + + task write_setting; + input [7:0] addr; + input [31:0] data; + begin + set_stb <= 0; + @(posedge clk); + set_addr <= addr; + set_data <= data; + set_stb <= 1; + @(posedge clk); + set_stb <= 0; + end + endtask // write_setting + + task queue_rx_cmd; + input send_imm; + input chain; + input reload; + input [28:0] lines; + input [31:0] secs; + input [31:0] tics; + begin + write_setting(0,{send_imm,chain,reload,lines}); + write_setting(1,secs); + write_setting(2,tics); + end + endtask // queue_rx_cmd + +endmodule // rx_control_tb + +module rx_dsp_model + (input clk, input reset, + input run, + input [7:0] decim, + output strobe, + output [31:0] sample); + + reg [15:0] pktnum = 0; + reg [15:0] counter = 0; + + reg run_d1; + always @(posedge clk) run_d1 <= run; + + always @(posedge clk) + if(run & ~run_d1) + begin + counter <= 0; + pktnum <= pktnum + 1; + end + else if(run & strobe) + counter <= counter + 1; + + assign sample = {pktnum,counter}; + + reg [7:0] stb_ctr = 0; + + always @(posedge clk) + if(reset) + stb_ctr <= 0; + else if(run & ~run_d1) + stb_ctr <= 1; + else if(run) + if(stb_ctr == decim-1) + stb_ctr <= 0; + else + stb_ctr <= stb_ctr + 1; + + assign strobe = stb_ctr == decim-1; + +endmodule // rx_dsp_model diff --git a/fpga/usrp2/vrt/vita_tx.build b/fpga/usrp2/vrt/vita_tx.build new file mode 100755 index 000000000..e7106aa10 --- /dev/null +++ b/fpga/usrp2/vrt/vita_tx.build @@ -0,0 +1 @@ +iverilog -Wimplict -Wportbind -y ../sdr_lib -y ../models -y . -y ../control_lib/ -y ../fifo -y ../coregen -y /opt/Xilinx/10.1/ISE/verilog/src/XilinxCoreLib -y /opt/Xilinx/10.1/ISE/verilog/src/unisims/ -y ../timing -o vita_tx_tb vita_tx_tb.v diff --git a/fpga/usrp2/vrt/vita_tx_chain.v b/fpga/usrp2/vrt/vita_tx_chain.v new file mode 100644 index 000000000..6f567668d --- /dev/null +++ b/fpga/usrp2/vrt/vita_tx_chain.v @@ -0,0 +1,101 @@ + +module vita_tx_chain + #(parameter BASE_CTRL=0, + parameter BASE_DSP=0, + parameter REPORT_ERROR=0, + parameter DO_FLOW_CONTROL=0, + parameter PROT_ENG_FLAGS=0, + parameter USE_TRANS_HEADER=0, + parameter DSP_NUMBER=0) + (input clk, input reset, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + input [63:0] vita_time, + input [35:0] tx_data_i, input tx_src_rdy_i, output tx_dst_rdy_o, + output [35:0] err_data_o, output err_src_rdy_o, input err_dst_rdy_i, + output [15:0] dac_a, output [15:0] dac_b, + output underrun, output run, + output [31:0] debug); + + localparam MAXCHAN = 1; + localparam FIFOWIDTH = 5+64+16+(32*MAXCHAN); + + wire [FIFOWIDTH-1:0] tx1_data; + wire tx1_src_rdy, tx1_dst_rdy; + wire clear_vita; + wire [31:0] sample_tx; + wire [31:0] streamid, message; + wire trigger, sent; + wire [31:0] debug_vtc, debug_vtd, debug_tx_dsp; + + wire error, packet_consumed; + wire [31:0] error_code; + wire clear_seqnum; + wire [31:0] current_seqnum; + + assign underrun = error; + assign message = error_code; + + setting_reg #(.my_addr(BASE_CTRL+1)) sr + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(),.changed(clear_vita)); + + setting_reg #(.my_addr(BASE_CTRL+2), .at_reset(0)) sr_streamid + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(streamid),.changed(clear_seqnum)); + + vita_tx_deframer #(.BASE(BASE_CTRL), + .MAXCHAN(MAXCHAN), + .USE_TRANS_HEADER(USE_TRANS_HEADER)) + vita_tx_deframer + (.clk(clk), .reset(reset), .clear(clear_vita), .clear_seqnum(clear_seqnum), + .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + .data_i(tx_data_i), .src_rdy_i(tx_src_rdy_i), .dst_rdy_o(tx_dst_rdy_o), + .sample_fifo_o(tx1_data), .sample_fifo_src_rdy_o(tx1_src_rdy), .sample_fifo_dst_rdy_i(tx1_dst_rdy), + .current_seqnum(current_seqnum), + .debug(debug_vtd) ); + + vita_tx_control #(.BASE(BASE_CTRL), .WIDTH(32*MAXCHAN)) vita_tx_control + (.clk(clk), .reset(reset), .clear(clear_vita), + .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + .vita_time(vita_time), .error(error), .ack(ack), .error_code(error_code), + .sample_fifo_i(tx1_data), .sample_fifo_src_rdy_i(tx1_src_rdy), .sample_fifo_dst_rdy_o(tx1_dst_rdy), + .sample(sample_tx), .run(run), .strobe(strobe_tx), .packet_consumed(packet_consumed), + .debug(debug_vtc) ); + + dsp_core_tx #(.BASE(BASE_DSP)) dsp_core_tx + (.clk(clk),.rst(reset), + .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + .sample(sample_tx), .run(run), .strobe(strobe_tx), + .dac_a(dac_a),.dac_b(dac_b), + .debug(debug_tx_dsp) ); + + wire [35:0] flow_data, err_data_int; + wire flow_src_rdy, flow_dst_rdy, err_src_rdy_int, err_dst_rdy_int; + + gen_context_pkt #(.PROT_ENG_FLAGS(PROT_ENG_FLAGS),.DSP_NUMBER(DSP_NUMBER)) gen_flow_pkt + (.clk(clk), .reset(reset), .clear(clear_vita), + .trigger(trigger & (DO_FLOW_CONTROL==1)), .sent(), + .streamid(streamid), .vita_time(vita_time), .message(32'd0), + .seqnum(current_seqnum), + .data_o(flow_data), .src_rdy_o(flow_src_rdy), .dst_rdy_i(flow_dst_rdy)); + trigger_context_pkt #(.BASE(BASE_CTRL)) trigger_context_pkt + (.clk(clk), .reset(reset), .clear(clear_vita), + .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + .packet_consumed(packet_consumed), .trigger(trigger)); + + gen_context_pkt #(.PROT_ENG_FLAGS(PROT_ENG_FLAGS),.DSP_NUMBER(DSP_NUMBER)) gen_tx_err_pkt + (.clk(clk), .reset(reset), .clear(clear_vita), + .trigger((error|ack) & (REPORT_ERROR==1)), .sent(), + .streamid(streamid), .vita_time(vita_time), .message(message), + .seqnum(current_seqnum), + .data_o(err_data_int), .src_rdy_o(err_src_rdy_int), .dst_rdy_i(err_dst_rdy_int)); + + assign debug = debug_vtc | debug_vtd; + + fifo36_mux #(.prio(1)) mux_err_and_flow // Priority to err messages + (.clk(clk), .reset(reset), .clear(0), // Don't clear this or it could get clogged + .data0_i(err_data_int), .src0_rdy_i(err_src_rdy_int), .dst0_rdy_o(err_dst_rdy_int), + .data1_i(flow_data), .src1_rdy_i(flow_src_rdy), .dst1_rdy_o(flow_dst_rdy), + .data_o(err_data_o), .src_rdy_o(err_src_rdy_o), .dst_rdy_i(err_dst_rdy_i)); + +endmodule // vita_tx_chain diff --git a/fpga/usrp2/vrt/vita_tx_control.v b/fpga/usrp2/vrt/vita_tx_control.v new file mode 100644 index 000000000..e966d987c --- /dev/null +++ b/fpga/usrp2/vrt/vita_tx_control.v @@ -0,0 +1,214 @@ + +module vita_tx_control + #(parameter BASE=0, + parameter WIDTH=32) + (input clk, input reset, input clear, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + input [63:0] vita_time, + output error, output ack, + output reg [31:0] error_code, + output reg packet_consumed, + + // From vita_tx_deframer + input [5+64+16+WIDTH-1:0] sample_fifo_i, + input sample_fifo_src_rdy_i, + output sample_fifo_dst_rdy_o, + + // To DSP Core + output [WIDTH-1:0] sample, + output reg run, + input strobe, + + output [31:0] debug + ); + + wire [63:0] send_time = sample_fifo_i[63:0]; + wire [15:0] seqnum = sample_fifo_i[79:64]; + wire eop = sample_fifo_i[80]; + wire eob = sample_fifo_i[81]; + wire sob = sample_fifo_i[82]; + wire send_at = sample_fifo_i[83]; + wire seqnum_err = sample_fifo_i[84]; + + wire now, early, late, too_early; + + // FIXME ignore too_early for now for timing reasons + assign too_early = 0; + time_compare + time_compare (.time_now(vita_time), .trigger_time(send_time), + .now(now), .early(early), .late(late), .too_early()); + + reg late_qual, late_del; + + always @(posedge clk) + if(reset | clear) + late_del <= 0; + else + late_del <= late; + + always @(posedge clk) + if(reset | clear) + late_qual <= 0; + else + late_qual <= (sample_fifo_src_rdy_i & ~sample_fifo_dst_rdy_o); + + localparam IBS_IDLE = 0; + localparam IBS_RUN = 1; // FIXME do we need this? + localparam IBS_CONT_BURST = 2; + localparam IBS_ERROR = 3; + localparam IBS_ERROR_DONE = 4; + localparam IBS_ERROR_WAIT = 5; + + wire [31:0] CODE_EOB_ACK = {seqnum,16'd1}; + wire [31:0] CODE_UNDERRUN = {seqnum,16'd2}; + wire [31:0] CODE_SEQ_ERROR = {seqnum,16'd4}; + wire [31:0] CODE_TIME_ERROR = {seqnum,16'd8}; + wire [31:0] CODE_UNDERRUN_MIDPKT = {seqnum,16'd16}; + wire [31:0] CODE_SEQ_ERROR_MIDBURST = {seqnum,16'd32}; + + reg [2:0] ibs_state; + + wire [31:0] error_policy; + setting_reg #(.my_addr(BASE+3)) sr_error_policy + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(error_policy),.changed()); + + wire policy_wait = error_policy[0]; + wire policy_next_packet = error_policy[1]; + wire policy_next_burst = error_policy[2]; + reg send_error, send_ack; + + always @(posedge clk) + if(reset | clear) + begin + ibs_state <= IBS_IDLE; + send_error <= 0; + send_ack <= 0; + error_code <= 0; + end + else + case(ibs_state) + IBS_IDLE : + if(sample_fifo_src_rdy_i) + if(seqnum_err) + begin + ibs_state <= IBS_ERROR; + error_code <= CODE_SEQ_ERROR; + send_error <= 1; + end + else if(~send_at | now) + ibs_state <= IBS_RUN; + else if((late_qual & late_del) | too_early) + begin + ibs_state <= IBS_ERROR; + error_code <= CODE_TIME_ERROR; + send_error <= 1; + end + + IBS_RUN : + if(strobe) + if(~sample_fifo_src_rdy_i) + begin + ibs_state <= IBS_ERROR; + error_code <= CODE_UNDERRUN_MIDPKT; + send_error <= 1; + end + else if(eop) + if(eob) + begin + ibs_state <= IBS_ERROR_DONE; // Not really an error + error_code <= CODE_EOB_ACK; + send_ack <= 1; + end + else + ibs_state <= IBS_CONT_BURST; + + IBS_CONT_BURST : + if(strobe) + begin + if(policy_next_packet) + ibs_state <= IBS_ERROR_DONE; + else if(policy_wait) + ibs_state <= IBS_ERROR_WAIT; + else + ibs_state <= IBS_ERROR; + error_code <= CODE_UNDERRUN; + send_error <= 1; + end + else if(sample_fifo_src_rdy_i) + if(seqnum_err) + begin + ibs_state <= IBS_ERROR; + error_code <= CODE_SEQ_ERROR_MIDBURST; + send_error <= 1; + end + else + ibs_state <= IBS_RUN; + + IBS_ERROR : + begin + send_error <= 0; + if(sample_fifo_src_rdy_i & eop) + if(policy_next_packet | (policy_next_burst & eob)) + ibs_state <= IBS_IDLE; + else if(policy_wait) + ibs_state <= IBS_ERROR_WAIT; + end + + IBS_ERROR_DONE : + begin + send_error <= 0; + send_ack <= 0; + ibs_state <= IBS_IDLE; + end + + IBS_ERROR_WAIT : + send_error <= 0; + endcase // case (ibs_state) + + + assign sample_fifo_dst_rdy_o = (ibs_state == IBS_ERROR) | (strobe & (ibs_state == IBS_RUN)); // FIXME also cleanout + + assign sample = (ibs_state == IBS_RUN) ? sample_fifo_i[5+64+16+WIDTH-1:5+64+16] : {WIDTH{1'b0}}; + //assign run = (ibs_state == IBS_RUN) | (ibs_state == IBS_CONT_BURST); + assign error = send_error; + assign ack = send_ack; + + localparam MAX_IDLE = 1000000; + // approx 10 ms timeout with a 100 MHz clock, but burning samples will slow that down + reg [19:0] countdown; + + always @(posedge clk) + if(reset | clear) + begin + run <= 0; + countdown <= 0; + end + else + if (ibs_state == IBS_RUN) + if(eob & eop & strobe & sample_fifo_src_rdy_i) + run <= 0; + else + begin + run <= 1; + countdown <= MAX_IDLE; + end + else + if (countdown == 0) + run <= 0; + else + countdown <= countdown - 1; + + always @(posedge clk) + if(reset | clear) + packet_consumed <= 0; + else + packet_consumed <= eop & sample_fifo_src_rdy_i & sample_fifo_dst_rdy_o; + + assign debug = { { now,late_qual,late_del,ack,eop,eob,sob,send_at }, + { sample_fifo_src_rdy_i, sample_fifo_dst_rdy_o, strobe, run, error, ibs_state[2:0] }, + { 8'b0 }, + { 8'b0 } }; + +endmodule // vita_tx_control diff --git a/fpga/usrp2/vrt/vita_tx_deframer.v b/fpga/usrp2/vrt/vita_tx_deframer.v new file mode 100644 index 000000000..eb39feaec --- /dev/null +++ b/fpga/usrp2/vrt/vita_tx_deframer.v @@ -0,0 +1,232 @@ + +module vita_tx_deframer + #(parameter BASE=0, + parameter MAXCHAN=1, + parameter USE_TRANS_HEADER=0) + (input clk, input reset, input clear, input clear_seqnum, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + // To FIFO interface of Buffer Pool + input [35:0] data_i, + input src_rdy_i, + output dst_rdy_o, + + output [5+64+16+(32*MAXCHAN)-1:0] sample_fifo_o, + output sample_fifo_src_rdy_o, + input sample_fifo_dst_rdy_i, + + output [31:0] current_seqnum, + + // FIFO Levels + output [15:0] fifo_occupied, + output fifo_full, + output fifo_empty, + output [31:0] debug + ); + + localparam FIFOWIDTH = 5+64+16+(32*MAXCHAN); + + wire [1:0] numchan; + setting_reg #(.my_addr(BASE), .at_reset(0), .width(2)) sr_numchan + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(numchan),.changed()); + + reg [3:0] vita_state; + wire has_streamid, has_classid, has_secs, has_tics, has_trailer; + assign has_streamid = (data_i[31:28]==4'b0001); + assign has_classid = data_i[27]; + assign has_secs = ~(data_i[23:22]==2'b00); + assign has_tics = ~(data_i[21:20]==2'b00); + assign has_trailer = data_i[26]; + assign is_sob = data_i[25]; + assign is_eob = data_i[24]; + wire eof = data_i[33]; + reg has_streamid_reg, has_classid_reg, has_secs_reg, has_tics_reg; + reg has_trailer_reg, is_sob_reg, is_eob_reg; + + reg [15:0] pkt_len; + reg [1:0] vector_phase; + wire line_done; + + wire [31:0] seqnum = data_i; + reg [31:0] seqnum_reg; + wire [31:0] next_seqnum = seqnum_reg + 32'd1; + wire [3:0] vita_seqnum = data_i[19:16]; + reg [3:0] vita_seqnum_reg; + wire [3:0] next_vita_seqnum = vita_seqnum_reg[3:0] + 4'd1; + reg seqnum_err; + + assign current_seqnum = seqnum_reg; + + // Output FIFO for packetized data + localparam VITA_TRANS_HEADER = 0; + localparam VITA_HEADER = 1; + localparam VITA_STREAMID = 2; + localparam VITA_CLASSID = 3; + localparam VITA_CLASSID2 = 4; + localparam VITA_SECS = 5; + localparam VITA_TICS = 6; + localparam VITA_TICS2 = 7; + localparam VITA_PAYLOAD = 8; + localparam VITA_STORE = 9; + localparam VITA_TRAILER = 10; + localparam VITA_DUMP = 11; + + wire [15:0] hdr_len = 2 + has_streamid_reg + has_classid_reg + has_classid_reg + has_secs_reg + + has_tics_reg + has_tics_reg + has_trailer_reg; + + wire vita_eof = (pkt_len==hdr_len); + wire eop = eof | vita_eof; // FIXME would ignoring eof allow larger VITA packets? + wire fifo_space; + + always @(posedge clk) + if(reset | clear | clear_seqnum) + begin + seqnum_reg <= 32'hFFFF_FFFF; + vita_seqnum_reg <= 4'hF; + end + else + begin + if((vita_state==VITA_TRANS_HEADER) & src_rdy_i) + seqnum_reg <= seqnum; + if((vita_state==VITA_HEADER) & src_rdy_i) + vita_seqnum_reg <= vita_seqnum; + end // else: !if(reset | clear_seqnum) + + always @(posedge clk) + if(reset | clear) + begin + vita_state <= (USE_TRANS_HEADER==1) ? VITA_TRANS_HEADER : VITA_HEADER; + {has_streamid_reg, has_classid_reg, has_secs_reg, has_tics_reg, has_trailer_reg, is_sob_reg, is_eob_reg} + <= 0; + seqnum_err <= 0; + end + else + if((vita_state == VITA_STORE) & fifo_space) + if(vita_eof) + if(eof) + vita_state <= (USE_TRANS_HEADER==1) ? VITA_TRANS_HEADER : VITA_HEADER; + else if(has_trailer_reg) + vita_state <= VITA_TRAILER; + else + vita_state <= VITA_DUMP; + else + begin + vita_state <= VITA_PAYLOAD; + pkt_len <= pkt_len - 1; + end + else if(src_rdy_i) + case(vita_state) + VITA_TRANS_HEADER : + begin + seqnum_err <= ~(seqnum == next_seqnum); + vita_state <= VITA_HEADER; + end + VITA_HEADER : + begin + {has_streamid_reg, has_classid_reg, has_secs_reg, has_tics_reg, has_trailer_reg, is_sob_reg, is_eob_reg} + <= {has_streamid, has_classid, has_secs, has_tics, has_trailer, is_sob, is_eob}; + pkt_len <= data_i[15:0]; + vector_phase <= 0; + if(has_streamid) + vita_state <= VITA_STREAMID; + else if(has_classid) + vita_state <= VITA_CLASSID; + else if(has_secs) + vita_state <= VITA_SECS; + else if(has_tics) + vita_state <= VITA_TICS; + else + vita_state <= VITA_PAYLOAD; + seqnum_err <= seqnum_err | ~(vita_seqnum == next_vita_seqnum); + end // case: VITA_HEADER + VITA_STREAMID : + if(has_classid_reg) + vita_state <= VITA_CLASSID; + else if(has_secs_reg) + vita_state <= VITA_SECS; + else if(has_tics_reg) + vita_state <= VITA_TICS; + else + vita_state <= VITA_PAYLOAD; + VITA_CLASSID : + vita_state <= VITA_CLASSID2; + VITA_CLASSID2 : + if(has_secs_reg) + vita_state <= VITA_SECS; + else if(has_tics_reg) + vita_state <= VITA_TICS; + else + vita_state <= VITA_PAYLOAD; + VITA_SECS : + if(has_tics_reg) + vita_state <= VITA_TICS; + else + vita_state <= VITA_PAYLOAD; + VITA_TICS : + vita_state <= VITA_TICS2; + VITA_TICS2 : + vita_state <= VITA_PAYLOAD; + VITA_PAYLOAD : + if(line_done) + begin + vector_phase <= 0; + vita_state <= VITA_STORE; + end + else + vector_phase <= vector_phase + 1; + VITA_TRAILER : + if(eof) + vita_state <= (USE_TRANS_HEADER==1) ? VITA_TRANS_HEADER : VITA_HEADER; + else + vita_state <= VITA_DUMP; + VITA_DUMP : + if(eof) + vita_state <= (USE_TRANS_HEADER==1) ? VITA_TRANS_HEADER : VITA_HEADER; + VITA_STORE : + ; + default : + vita_state <= (USE_TRANS_HEADER==1) ? VITA_TRANS_HEADER : VITA_HEADER; + endcase // case (vita_state) + + assign line_done = (vector_phase == numchan); + + wire [FIFOWIDTH-1:0] fifo_i; + reg [63:0] send_time; + reg [31:0] sample_a, sample_b, sample_c, sample_d; + + always @(posedge clk) + case(vita_state) + VITA_SECS : + send_time[63:32] <= data_i[31:0]; + VITA_TICS2 : + send_time[31:0] <= data_i[31:0]; + endcase // case (vita_state) + + always @(posedge clk) + if(vita_state == VITA_PAYLOAD) + case(vector_phase) + 0: sample_a <= data_i[31:0]; + 1: sample_b <= data_i[31:0]; + 2: sample_c <= data_i[31:0]; + 3: sample_d <= data_i[31:0]; + endcase // case (vector_phase) + + wire store = (vita_state == VITA_STORE); + fifo_short #(.WIDTH(FIFOWIDTH)) short_tx_q + (.clk(clk), .reset(reset), .clear(clear), + .datain(fifo_i), .src_rdy_i(store), .dst_rdy_o(fifo_space), + .dataout(sample_fifo_o), .src_rdy_o(sample_fifo_src_rdy_o), .dst_rdy_i(sample_fifo_dst_rdy_i) ); + + // sob, eob, has_secs (send_at) ignored on all lines except first + assign fifo_i = {sample_d,sample_c,sample_b,sample_a,seqnum_err,has_secs_reg,is_sob_reg,is_eob_reg,eop, + 12'd0,seqnum_reg[3:0],send_time}; + + assign dst_rdy_o = ~(vita_state == VITA_PAYLOAD) & ~((vita_state==VITA_STORE)& ~fifo_space) ; + + assign debug = { { 8'b0 }, + { 8'b0 }, + { eof, line_done, store, fifo_space, src_rdy_i, dst_rdy_o, vector_phase[1:0] }, + { has_secs_reg, is_sob_reg, is_eob_reg, eop, vita_state[3:0] } }; + +endmodule // vita_tx_deframer diff --git a/fpga/usrp2/vrt/vita_tx_tb.v b/fpga/usrp2/vrt/vita_tx_tb.v new file mode 100644 index 000000000..a118ffd4e --- /dev/null +++ b/fpga/usrp2/vrt/vita_tx_tb.v @@ -0,0 +1,176 @@ + + +module vita_tx_tb; + + localparam DECIM = 8'd4; + localparam INTERP = 8'd4; + + localparam MAXCHAN=1; + localparam NUMCHAN=1; + + reg clk = 0; + reg reset = 1; + + initial #1000 reset = 0; + always #50 clk = ~clk; + + initial $dumpfile("vita_tx_tb.vcd"); + initial $dumpvars(0,vita_tx_tb); + + wire [(MAXCHAN*32)-1:0] sample, sample_tx; + wire strobe, run; + reg [35:0] data_o = 36'h0; + reg src_rdy = 0; + wire dst_rdy; + + wire [63:0] vita_time; + + reg set_stb = 0; + reg [7:0] set_addr; + reg [31:0] set_data; + wire set_stb_dsp; + wire [7:0] set_addr_dsp; + wire [31:0] set_data_dsp; + + wire sample_dst_rdy, sample_src_rdy; + wire [5+64+16+(MAXCHAN*32)-1:0] sample_data_o, sample_data_tx; + + time_64bit #(.TICKS_PER_SEC(100000000), .BASE(0)) time_64bit + (.clk(clk), .rst(reset), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .pps(0), .vita_time(vita_time)); + + wire [35:0] data_tx; + wire src_rdy_tx, dst_rdy_tx; + wire sample_dst_rdy_tx, sample_src_rdy_tx; + + fifo_long #(.WIDTH(36)) fifo_short + (.clk(clk), .reset(reset), .clear(0), + .datain(data_o), .src_rdy_i(src_rdy), .dst_rdy_o(dst_rdy), + .dataout(data_tx), .src_rdy_o(src_rdy_tx), .dst_rdy_i(dst_rdy_tx)); + + vita_tx_deframer #(.BASE(16), .MAXCHAN(MAXCHAN), .USE_TRANS_HEADER(0)) vita_tx_deframer + (.clk(clk), .reset(reset), .clear(0), .clear_seqnum(0), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .data_i(data_tx), .dst_rdy_o(dst_rdy_tx), .src_rdy_i(src_rdy_tx), + .sample_fifo_o(sample_data_tx), + .sample_fifo_dst_rdy_i(sample_dst_rdy_tx), .sample_fifo_src_rdy_o(sample_src_rdy_tx), + .fifo_occupied(), .fifo_full(), .fifo_empty() ); + + vita_tx_control #(.BASE(16), .WIDTH(MAXCHAN*32)) vita_tx_control + (.clk(clk), .reset(reset), .clear(0), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .vita_time(vita_time), .error(underrun), .error_code(), + .sample_fifo_i(sample_data_tx), + .sample_fifo_dst_rdy_o(sample_dst_rdy_tx), .sample_fifo_src_rdy_i(sample_src_rdy_tx), + .sample(sample_tx), .run(run_tx), .strobe(strobe_tx)); + + tx_dsp_model tx_dsp_model + (.clk(clk), .reset(reset), .run(run_tx), .interp(INTERP), .strobe(strobe_tx), .sample(sample_tx[31:0] )); + + task write_setting; + input [7:0] addr; + input [31:0] data; + begin + set_stb <= 0; + @(posedge clk); + set_addr <= addr; + set_data <= data; + set_stb <= 1; + @(posedge clk); + set_stb <= 0; + end + endtask // write_setting + + initial + begin + @(negedge reset); + @(posedge clk); + write_setting(4,32'h14900008); // VITA header + write_setting(5,32'hF00D1234); // VITA streamid + write_setting(6,32'h98765432); // VITA trailer + write_setting(7,8); // Samples per VITA packet + write_setting(8,NUMCHAN); // Samples per VITA packet + #10000; + queue_vita_packets(0, 32'h300, 5, 32'h0000_1000, 32'h0, 4'h0, 1, 0, 1); + queue_vita_packets(0, 32'h0, 5, 32'h0000_2000, 32'h0, 4'h1, 0, 0, 0); + queue_vita_packets(0, 32'h0, 5, 32'h0000_3000, 32'h0, 4'h2, 0, 0, 0); + + queue_vita_packets(0, 32'h400, 3, 32'h0000_4000, 32'h0, 4'h3, 1, 0, 1); + queue_vita_packets(0, 32'h0, 3, 32'h0000_5000, 32'h0, 4'h4, 0, 0, 0); + queue_vita_packets(0, 32'h0, 3, 32'h0000_6000, 32'h0, 4'h5, 0, 1, 0); + + #300000 $finish; + end + + task queue_vita_packets; + input [31:0] send_secs; + input [31:0] sendtime; + input [15:0] samples; + input [15:0] word; + input [31:0] trailer; + input [3:0] seqnum; + input sob; + input eob; + input sendat; + + reg [15:0] i; + + begin + src_rdy <= 0; + @(posedge clk); + src_rdy <= 1; + data_o <= {4'b0001,4'h0,1'b0,|trailer,sob,eob,{2{sendat}},1'b0,sendat,seqnum,(16'd1+samples+|trailer+sendat+sendat+sendat)}; // header + @(posedge clk); + //data_o <= {4'b0000,32'h0}; // streamid + //@(posedge clk); + if(sendat) + begin + data_o <= {4'b0000,send_secs}; // SECS + @(posedge clk); + data_o <= {4'b0000,32'h0}; // TICS + @(posedge clk); + data_o <= {4'b0000,sendtime}; // TICS + @(posedge clk); + end + for(i=0;i<samples-1;i=i+1) + begin + data_o <= {4'b0000,i,word}; // Payload + @(posedge clk); + end + if(trailer==0) + begin + data_o <= {4'b0010,i,16'hBEEF}; // Last Payload + @(posedge clk); + end + else + begin + data_o <= {4'b0000,i,16'hBEEF}; // Last Payload + @(posedge clk); + data_o <= {4'b0010,trailer}; // Last Payload + @(posedge clk); + end + src_rdy <= 0; + @(posedge clk); + + end + endtask // queue_vita_packets + +endmodule // vita_tx_tb + + +module tx_dsp_model + (input clk, input reset, + input run, + input [7:0] interp, + output strobe, + input [31:0] sample); + + cic_strober strober(.clock(clk), .reset(reset), .enable(run), .rate(interp), .strobe_fast(1), .strobe_slow(strobe)); + + always @(posedge clk) + if(strobe) + $display("Time %d, Sent Sample %x",$time,sample); + + +endmodule // tx_dsp_model |