diff options
Diffstat (limited to 'fpga/usrp3/lib/vita/chdr_16sc_to_12sc.v')
-rw-r--r-- | fpga/usrp3/lib/vita/chdr_16sc_to_12sc.v | 431 |
1 files changed, 235 insertions, 196 deletions
diff --git a/fpga/usrp3/lib/vita/chdr_16sc_to_12sc.v b/fpga/usrp3/lib/vita/chdr_16sc_to_12sc.v index c91d9cbd1..39ee2ccc9 100644 --- a/fpga/usrp3/lib/vita/chdr_16sc_to_12sc.v +++ b/fpga/usrp3/lib/vita/chdr_16sc_to_12sc.v @@ -1,221 +1,260 @@ // // Copyright 2013 Ettus Research LLC // - +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// module chdr_16sc_to_12sc #(parameter BASE=0) - ( input set_stb, input [7:0] set_addr, input [31:0] set_data, - //left side of device - input clk, input reset, + ( + // Clocks and resets + input clk, + input reset, + // Settings bus + input set_stb, + input [7:0] set_addr, + input [31:0] set_data, + // Input CHDR bus input [63:0] i_tdata, input i_tlast, input i_tvalid, output i_tready, - //right side of device - output reg [63:0] o_tdata = 0, + // Output CHDR bus + output reg [63:0] o_tdata, output o_tlast, output o_tvalid, input o_tready, - + // Debug output [31:0] debug - ); - - wire chdr_has_hdr = 1'b1; - wire chdr_has_time = i_tdata[61]; - wire chdr_has_tlr = 1'b0; - - wire [11:0] imag0; - wire [11:0] real0; - wire [11:0] imag1; - wire [11:0] real1; - wire [11:0] imag2; - wire [11:0] real2; - - - - wire [16:0] round_i0; - wire [16:0] round_r0; - wire [16:0] round_i1; - wire [16:0] round_r1; - wire [16:0] round_i2; - wire [16:0] round_r2; - -//pipiline registers - - reg [11:0] imag0_out; - reg [11:0] real0_out; - reg [11:0] imag1_out; - reg [11:0] real1_out; - - reg [15:0] len_data; - - //chdr length calculations - wire [15:0] chdr_header_lines = chdr_has_time? 16 : 8; - wire [15:0] in_samples = i_tdata[47:32] - chdr_header_lines; - wire [15:0] samples = (in_samples*3) >> 2; - wire [15:0] chdr_payload_lines = samples + chdr_header_lines; - - - reg needs_exline = 0; - reg in_exline = 0; - - - wire set_sid; - wire [15:0] my_newhome; - + ); + + wire chdr_has_time = i_tdata[61]; + + wire [11:0] q0; + wire [11:0] i0; + wire [11:0] q1; + wire [11:0] i1; + wire [11:0] q2; + wire [11:0] i2; + + wire [16:0] round_q0; + wire [16:0] round_i0; + wire [16:0] round_q1; + wire [16:0] round_i1; + wire [16:0] round_q2; + wire [16:0] round_i2; + + // Pipeline registers + reg [11:0] q0_out; + reg [11:0] i0_out; + reg [11:0] q1_out; + reg [11:0] i1_out; + + // CHDR has either 8 bytes of header or 16 if VITA time is included. + wire [15:0] chdr_header_lines = chdr_has_time? 16 : 8; + // Calculate size of samples input in bytes by taking CHDR size filed and subtracting header length. + wire [15:0] sample_byte_count_in = i_tdata[47:32] - chdr_header_lines; + // Calculate size of samples to be output by taking input size and scaling by 3/4 + wire [15:0] sample_byte_count_out = (sample_byte_count_in*3) >> 2; + // Calculate size of output CHDR packet by adding back header size to new payload size. + wire [15:0] output_chdr_pkt_size = sample_byte_count_out + chdr_header_lines; + + reg needs_extra_line; + reg in_extra_line; + + wire set_sid; + wire [15:0] new_sid_dst; + setting_reg #(.my_addr(BASE), .width(17)) new_destination (.clk(clk), .rst(reset), .strobe(set_stb), .addr(set_addr), .in(set_data), - .out({set_sid, my_newhome[15:0]})); - - //state machine - - localparam HEADER = 3'd0; // IDLE - localparam TIME = 3'd1; - localparam LINE_ODD_ZERO = 3'd2; - localparam LINE_EVEN_ONE = 3'd3; - localparam LINE_ODD_TWO = 3'd4; - localparam REG_STATE = 3'd5; - - reg [2:0] state; - - - - always @(posedge clk) begin - if (reset) begin - state <= HEADER; - needs_exline <= 0; - in_exline <= 0; - end - - - else if ((o_tvalid && o_tready) || (i_tready && i_tvalid)) case(state) - - - HEADER: begin - - needs_exline <= (in_samples[4:2] == 3 || in_samples[4:2] == 4 || in_samples[4:2] == 6); - state <= (i_tdata[61])? TIME: REG_STATE; - - end - - TIME: begin - state <= (i_tlast) ? HEADER: REG_STATE; - end - - REG_STATE: begin - if (i_tlast & !needs_exline || in_exline) begin - state <= HEADER; - in_exline <= 0; - end - else if (i_tlast & needs_exline) begin - state <= LINE_EVEN_ONE; - in_exline <= 1; - - end - else - state <= LINE_EVEN_ONE; - end - - - LINE_EVEN_ONE: begin - if (i_tlast & !needs_exline || in_exline) begin - state <= HEADER; - in_exline <= 0; - end - else if (i_tlast & needs_exline) begin - state <= LINE_ODD_TWO; - in_exline <= 1; - - end - else - state <= LINE_ODD_TWO; - end - - - - - LINE_ODD_TWO: begin - if (i_tlast & !needs_exline || in_exline) begin - state <= HEADER; - in_exline <= 0; - end - else if (i_tlast & needs_exline) begin - state <= LINE_ODD_ZERO; - in_exline <= 1; - - end - else - state <= LINE_ODD_ZERO; - end - - LINE_ODD_ZERO: begin - if (i_tlast & !needs_exline || in_exline) begin - state <= HEADER; - in_exline <= 0; - end - else if (i_tlast & needs_exline) begin - state <= REG_STATE; - in_exline <= 1; - end - else - state <= REG_STATE; - end - - default: state <= HEADER; - - endcase - end - - assign round_i0 = ({i_tdata[63],i_tdata[63:48]} + 'h0008); - assign round_r0 = ({i_tdata[47],i_tdata[47:32]} + 'h0008); - - assign imag0 = (round_i0[16] == 0 && round_i0[15] == 1)?(12'h7FF):(round_i0[16] == 1 && round_i0[15] == 0)? (12'h800):(round_i0[15:4]); - - assign real0 = (round_r0[16] == 0 && round_r0[15] == 1)?(12'h7FF):(round_r0[16] == 1 && round_r0[15] == 0)? (12'h800):(round_r0[15:4]); - - assign round_i1 = ({i_tdata[31],i_tdata[31:16]} + 'h0008); - assign round_r1 = ({i_tdata[15],i_tdata[15:0]} + 'h0008); - - assign imag1 = (round_i1[16] == 0 && round_i1[15] == 1)?(12'h7FF):(round_i1[16] == 1 && round_i1[15] == 0)? (12'h800):(round_i1[15:4]); - - assign real1 = (round_r1[16] == 0 && round_r1[15] == 1)?(12'h7FF):(round_r1[16] == 1 && round_r1[15] == 0)? (12'h800):(round_r1[15:4]); - - - - + .out({set_sid, new_sid_dst[15:0]})); + // state machine + + localparam HEADER = 3'd0; + localparam TIME = 3'd1; + localparam SAMPLE1 = 3'd2; + localparam SAMPLE2 = 3'd3; + localparam SAMPLE3 = 3'd4; + localparam SAMPLE4 = 3'd5; + + reg [2:0] state; + + always @(posedge clk) + if (reset) begin + state <= HEADER; + needs_extra_line <= 0; + in_extra_line <= 0; + end else begin + case(state) + // + // Process Header line of input packet or idle in this state waiting for new packet. + // If the the input packet has SAMPLE_COUNT MODULO 8 == 3 or 4 or 6 then when tlast is asserted we will need one more + // output cycle to finish outputing processed samples. + // + HEADER: begin + if (o_tready && i_tvalid) + begin + needs_extra_line <= (sample_byte_count_in[4:2] == 3 || sample_byte_count_in[4:2] == 4 || sample_byte_count_in[4:2] == 6); + // If the input packet had time, then add time to output packet + state <= (i_tdata[61])? TIME: SAMPLE1; + end + end + // + // Process time field of input packet + // + TIME: begin + if (o_tready && i_tvalid) + begin + // If we get a premature end of line go back to searching for start of new packet. + state <= (i_tlast) ? HEADER: SAMPLE1; + end + end + // + // Process line of sample data from input packet. + // Not yet enough data to prepare first of three repeating output lines + // unless this the last line of a packet when the lats output line + // is composed of data from one or both samples in this input packet. + // + SAMPLE1: begin + if ((i_tlast && !needs_extra_line && o_tready && i_tvalid) || (in_extra_line && o_tready)) begin + // We can finish this packet immediately. + state <= HEADER; + in_extra_line <= 0; + end else if (i_tlast && needs_extra_line && o_tready && i_tvalid) begin + // We still need one more output line to drain all samples into this packet. + // (SHOULD NOT BE POSSIBLE TO GET HERE!) + state <= SAMPLE2; + in_extra_line <= 1; + end else if (o_tready && i_tvalid) + state <= SAMPLE2; + end + // + // First of three repeating output line patterns + // + SAMPLE2: begin + if ((i_tlast && !needs_extra_line && o_tready && i_tvalid) || (in_extra_line && o_tready)) begin + // We can finish this packet immediately. + state <= HEADER; + in_extra_line <= 0; + end else if (i_tlast && needs_extra_line && o_tready && i_tvalid) begin + // We still need one more output line to drain all samples into this packet. + state <= SAMPLE3; + in_extra_line <= 1; + end else if (o_tready && i_tvalid) + state <= SAMPLE3; + end + // + // Second of three repeating output line patterns + // + SAMPLE3: begin + if ((i_tlast && !needs_extra_line && o_tready && i_tvalid) || (in_extra_line && o_tready)) begin + // We can finish this packet immediately. + state <= HEADER; + in_extra_line <= 0; + end else if (i_tlast && needs_extra_line && o_tready && i_tvalid) begin + // We still need one more output line to drain all samples into this packet. + state <= SAMPLE4; + in_extra_line <= 1; + end else if (o_tready && i_tvalid) + state <= SAMPLE4; + end + // + // Third of three repeating output line patterns + // + SAMPLE4: begin + if ((i_tlast && !needs_extra_line && o_tready && i_tvalid) || (in_extra_line && o_tready)) begin + // We can finish this packet immediately. + state <= HEADER; + in_extra_line <= 0; + end else if (i_tlast && needs_extra_line && o_tready && i_tvalid) begin + // We still need one more output line to drain all samples into this packet. + // (SHOULD NOT BE POSSIBLE TO GET HERE!!) + state <= SAMPLE1; + in_extra_line <= 1; + end else if (o_tready && i_tvalid) + state <= SAMPLE1; + end + // + // Should never get here. + // + default: state <= HEADER; + + endcase + end + + + // Add rounding value into 16bit samples before trunctaion + assign round_q0 = ({i_tdata[63],i_tdata[63:48]} + 'h0008); + assign round_i0 = ({i_tdata[47],i_tdata[47:32]} + 'h0008); + // Truncate with saturation to 12bits precision. + assign q0 = (round_q0[16:15] == 2'b01) ? 12'h7FF : ((round_q0[16:15] == 2'b10) ? 12'h800 : round_q0[15:4]); + assign i0 = (round_i0[16:15] == 2'b01) ? 12'h7FF : ((round_i0[16:15] == 2'b10) ? 12'h800 : round_i0[15:4]); + // Add rounding value into 16bit samples before trunctaion + assign round_q1 = ({i_tdata[31],i_tdata[31:16]} + 'h0008); + assign round_i1 = ({i_tdata[15],i_tdata[15:0]} + 'h0008); + // Truncate with saturation to 12bits precision. + assign q1 = (round_q1[16:15] == 2'b01) ? 12'h7FF : ((round_q1[16:15] == 2'b10) ? 12'h800 : round_q1[15:4]); + assign i1 = (round_i1[16:15] == 2'b01) ? 12'h7FF : ((round_i1[16:15] == 2'b10) ? 12'h800 : round_i1[15:4]); + + // + // Keep values from current input line to populate fields in next cycles output line + // always @(posedge clk) if (i_tvalid && o_tready) begin - imag0_out <= imag0; - real0_out <= real0; - imag1_out <= imag1; - real1_out <= real1; + q0_out <= q0; + i0_out <= i0; + q1_out <= q1; + i1_out <= i1; end - - + // + // Mux Output data + // always @(*) case(state) - - HEADER: o_tdata <= {i_tdata[63:48], chdr_payload_lines, - set_sid ? {i_tdata[15:0], my_newhome[15:0]}:i_tdata[31:0]}; - TIME: o_tdata <= i_tdata; - - - REG_STATE: o_tdata <= {imag0,real0,imag1, real1, 16'b0}; - - LINE_EVEN_ONE: o_tdata <= {imag0_out, real0_out, imag1_out, real1_out, imag0, real0[11:8]}; - LINE_ODD_TWO: o_tdata <= {real0_out[7:0], imag1_out, real1_out, imag0, real0,imag1[11:4]}; - LINE_ODD_ZERO: o_tdata <= {imag1_out[3:0], real1_out, imag0, real0, imag1, real1}; - - default : o_tdata <= i_tdata; - endcase - - assign o_tvalid =((in_exline) || (state != REG_STATE & i_tvalid) || (i_tlast & i_tvalid & !needs_exline)); - assign i_tready = (o_tready & !in_exline)||(state == REG_STATE && !i_tlast); - assign o_tlast = (needs_exline)? in_exline: i_tlast; - - + // Populate header with CHDR fields + HEADER: o_tdata = {i_tdata[63:48], output_chdr_pkt_size, + set_sid ? {i_tdata[15:0], new_sid_dst[15:0]}:i_tdata[31:0]}; + // Add 64bit VITA time to packet + TIME: o_tdata = i_tdata; + // Special ending corner case for input packets with SAMPLE_COUNT MODULO 8 == (1 | 2) + SAMPLE1: o_tdata = {q0,i0,q1, i1, 16'b0}; + // Line one of repeating 12bit packed data pattern + SAMPLE2: o_tdata = {q0_out, i0_out, q1_out, i1_out, q0, i0[11:8]}; + // Line two of repeating 12bit packed data pattern + SAMPLE3: o_tdata = {i0_out[7:0], q1_out, i1_out, q0, i0,q1[11:4]}; + // Line three of repeating 12bit packed data pattern + SAMPLE4: o_tdata = {q1_out[3:0], i1_out, q0, i0, q1, i1}; + default : o_tdata = i_tdata; + endcase // case(state) + + + assign o_tvalid = + // We are outputing the (extra) last line of a packet + in_extra_line || + // When not in the SAMPLE1 state and there's new input data (Unless its the last line....) + (state != SAMPLE1 & i_tvalid) || + // Last line of input packet and we can finish this cycle. (Includes when state is SAMPLE1) + (i_tlast & i_tvalid & !needs_extra_line); + + assign i_tready = + // Downstream is ready and we are not currently outputing last (extra) line of packet. + (o_tready && !in_extra_line) || + // We don't create output data in SAMPLE1 unless its last line so don't need downstream ready to proceed. + ((state == SAMPLE1) && !i_tlast); + + assign o_tlast = (needs_extra_line) ? in_extra_line : i_tlast; endmodule |