diff --git a/fifo_combiner.sv b/fifo_combiner.sv new file mode 100644 index 0000000..33ba5fb --- /dev/null +++ b/fifo_combiner.sv @@ -0,0 +1,174 @@ +//------------------------------------------------------------------------------ +// fifo_combiner.sv +// published as part of https://github.com/pConst/basic_verilog +// Konstantin Pavlov, pavlovconst@gmail.com +//------------------------------------------------------------------------------ + +// INFO ------------------------------------------------------------------------ +// Combines / accumulates data words from multiple FIFOs to a single output FIFO. +// Features three different element enumeration strategies. +// Reads if ANY input FIFO has data. +// +// See also fifo_operator.sv +// + +/* --- INSTANTIATION TEMPLATE BEGIN --- + +fifo_combiner #( + .WIDTH( 2 ), + .ENCODER_MODE( "ROUND_ROBIN" ), + .FWFT_MODE( "TRUE" ), + .DATA_W( 32 ) +) FC1 ( + .clk( ), + .nrst( ), + + .r_empty( ), + .r_req( ), + .r_data( ), + + .w_full( ), // connect to "almost_full" if FWFT_MODE="FALSE" + .w_req( ), + .w_data( ) +); + +--- INSTANTIATION TEMPLATE END ---*/ + +module fifo_combiner #( parameter + + WIDTH = 2, // number of input fifo ports to combine + WIDTH_W = clogb2(WIDTH), // input port index width + ENCODER_MODE = "ROUND_ROBIN", // "ROUND_ROBIN", "ROUND_ROBIN_PERFORMANCE" or "PRIORITY" + FWFT_MODE = "TRUE", // "TRUE" - first word fall-trrough" mode + // "FALSE" - normal fifo mode + DATA_W = 32 // data field width +)( + + input clk, // clock + input nrst, // inverted reset + + // input ports + input [WIDTH-1:0] r_empty, + output [WIDTH-1:0] r_req, + input [WIDTH-1:0][DATA_W-1:0] r_data, + + // output port + input w_full, + output logic w_req, + output logic [DATA_W-1:0] w_data +); + + + logic enc_valid; + logic [WIDTH-1:0] enc_filt; + logic [WIDTH_W-1:0] enc_bin; + + logic [WIDTH-1:0] r_empty_rev; + reverse_vector #( + .WIDTH( WIDTH ) // WIDTH must be >=2 + ) empty_rev ( + .in( r_empty[WIDTH-1:0] ), + .out( r_empty_rev[WIDTH-1:0] ) + ); + + generate + if( ENCODER_MODE == "ROUND_ROBIN" ) begin + round_robin_enc #( + .WIDTH( WIDTH ) + ) rr_enc ( + .clk( clk ), + .nrst( nrst ), + .id( ~r_empty[WIDTH-1:0] ), + .od_valid( enc_valid ), + .od_filt( enc_filt[WIDTH-1:0] ), + .od_bin( enc_bin[WIDTH_W-1:0] ) + ); + end else if( ENCODER_MODE == "ROUND_ROBIN_PERFORMANCE" ) begin + round_robin_performance_enc #( + .WIDTH( WIDTH ) + ) rr_perf_enc ( + .clk( clk ), // !! + .nrst( nrst ), + .id(~r_empty[WIDTH-1:0] ), + .od_valid( enc_valid ), + .od_filt( enc_filt[WIDTH-1:0] ), + .od_bin( enc_bin[WIDTH_W-1:0] ) + ); + end else if( ENCODER_MODE == "PRIORITY" ) begin + priority_enc #( + .WIDTH( WIDTH ) // WIDTH must be >=2 + ) pri_enc ( + .id( ~r_empty[WIDTH-1:0] ), + .od_valid( enc_valid ), + .od_filt( enc_filt[WIDTH-1:0] ), + .od_bin( enc_bin[WIDTH_W-1:0] ) + ); + end // ENCODER_MODE + endgenerate + + + logic r_valid; + assign r_valid = enc_valid && ~w_full; + + assign r_req[WIDTH-1:0] = {WIDTH{r_valid}} && + enc_filt[WIDTH-1:0]; + + // buffering read data + logic r_valid_d1 = 1'b0; + logic [WIDTH_W-1:0] enc_bin_d1; + logic [WIDTH-1:0][DATA_W-1:0] r_data_d1 = '0; + always_ff @(posedge clk) begin + if ( ~nrst ) begin + r_valid_d1 <= 1'b0; + enc_bin_d1[WIDTH_W-1:0] <= '0; + r_data_d1[WIDTH-1:0] <= '0; + end else begin + r_valid_d1 <= r_valid; + enc_bin_d1[WIDTH_W-1:0] <= enc_bin[WIDTH_W-1:0]; + r_data_d1[WIDTH-1:0] <= r_data[WIDTH-1:0]; + end + end + + // routing data to write port + generate + if( FWFT_MODE == "TRUE" ) begin + + always_comb begin + if ( ~nrst ) begin + w_req = 1'b0; + w_data[DATA_W-1:0] = '0; + end else begin + if( r_valid ) begin + w_req = 1'b1; + w_data[DATA_W-1:0] = r_data[enc_bin[WIDTH_W-1:0]][DATA_W-1:0]; + end else begin + w_req = 1'b0; + w_data[DATA_W-1:0] = '0; + end + end + end + + end else if( FWFT_MODE == "FALSE" ) begin + + always_comb begin + if ( ~nrst ) begin + w_req = 1'b0; + w_data[DATA_W-1:0] = '0; + end else begin + if( r_valid_d1 ) begin + w_req = 1'b1; + w_data[DATA_W-1:0] = r_data_d1[enc_bin_d1[WIDTH_W-1:0]][DATA_W-1:0]; + end else begin + w_req = 1'b0; + w_data[DATA_W-1:0] = '0; + end + end + end + + end // FWFT_MODE + endgenerate + + `include "clogb2.svh" + +endmodule + diff --git a/fifo_operator.sv b/fifo_operator.sv new file mode 100644 index 0000000..95ff129 --- /dev/null +++ b/fifo_operator.sv @@ -0,0 +1,132 @@ +//------------------------------------------------------------------------------ +// fifo_operator.sv +// published as part of https://github.com/pConst/basic_verilog +// Konstantin Pavlov, pavlovconst@gmail.com +//------------------------------------------------------------------------------ + +// INFO ------------------------------------------------------------------------ +// Performs custom operation on data words from multiple FIFOs and stores +// result to a single output FIFO. +// Reads only if ALL input FIFOs have data. +// Source code could be easily adapted to apply any operator on input data. +// +// See also fifo_combiner.sv +// + +/* --- INSTANTIATION TEMPLATE BEGIN --- + +fifo_operator #( + .WIDTH( 2 ), + .FWFT_MODE( "TRUE" ), + .DATA_W( 32 ) +) FO1 ( + .clk( ), + .nrst( ), + + .r_empty( ), + .r_req( ), + .r_data( ), + + .w_full( ), // connect to "almost_full" if FWFT_MODE="FALSE" + .w_req( ), + .w_data( ) +); + +--- INSTANTIATION TEMPLATE END ---*/ + +module fifo_operator #( parameter + + WIDTH = 2, // number of input fifo ports to opeate on + WIDTH_W = clogb2(WIDTH), // input port index width + FWFT_MODE = "TRUE", // "TRUE" - first word fall-trrough" mode + // "FALSE" - normal fifo mode + DATA_W = 32 // data field width +)( + + input clk, // clock + input nrst, // inverted reset + + // input ports + input [WIDTH-1:0] r_empty, + output [WIDTH-1:0] r_req, + input [WIDTH-1:0][DATA_W-1:0] r_data, + + // output port + input w_full, + output logic w_req, + output logic [DATA_W-1:0] w_data +); + + + logic r_valid; + assign r_valid = ~|r_empty && ~w_full; + + assign r_req[WIDTH-1:0] = {WIDTH{r_valid}}; + + // buffering read data + logic r_valid_d1 = 1'b0; + logic [WIDTH-1:0][DATA_W-1:0] r_data_d1 = '0; + always_ff @(posedge clk) begin + if ( ~nrst ) begin + r_valid_d1 <= 1'b0; + r_data_d1[WIDTH-1:0] <= '0; + end else begin + r_valid_d1 <= r_valid; + r_data_d1[WIDTH-1:0] <= r_data[WIDTH-1:0]; + end + end + + // routing data to write port + generate + if( FWFT_MODE == "TRUE" ) begin + + always_comb begin + if ( ~nrst ) begin + w_req = 1'b0; + w_data[DATA_W-1:0] = '0; + end else begin + if( r_valid ) begin + w_req = 1'b1; + w_data[DATA_W-1:0] = operator(r_data); + end else begin + w_req = 1'b0; + w_data[DATA_W-1:0] = '0; + end + end + end + + end else if( FWFT_MODE == "FALSE" ) begin + + always_comb begin + if ( ~nrst ) begin + w_req = 1'b0; + w_data[DATA_W-1:0] = '0; + end else begin + if( r_valid_d1 ) begin + w_req = 1'b1; + w_data[DATA_W-1:0] = operator(r_data_d1); + end else begin + w_req = 1'b0; + w_data[DATA_W-1:0] = '0; + end + end + end + + end // FWFT_MODE + endgenerate + + // bitwize OR operator, as an example + function [DATA_W-1:0] operator ( + input [WIDTH-1:0][DATA_W-1:0] data + ); + integer i; + operator[DATA_W-1:0] = '0; + for( i=0; i=2 -) reverse_b ( - .in( id[WIDTH-1:0] ), - .out( id_r[WIDTH-1:0] ) -); + // reversed id[] data + // conventional operation of priority encoder is when MSB bits have a priority + logic [WIDTH-1:0] id_r; + reverse_vector #( + .WIDTH( WIDTH ) // WIDTH must be >=2 + ) reverse_b ( + .in( id[WIDTH-1:0] ), + .out( id_r[WIDTH-1:0] ) + ); -leave_one_hot #( - .WIDTH( WIDTH ) -) one_hot_b ( - .in( id_r[WIDTH-1:0] ), - .out( od_filt[WIDTH-1:0] ) -); + leave_one_hot #( + .WIDTH( WIDTH ) + ) one_hot_b ( + .in( id_r[WIDTH-1:0] ), + .out( od_filt[WIDTH-1:0] ) + ); -logic err_no_hot; -assign od_valid = ~err_no_hot; + logic err_no_hot; + assign od_valid = ~err_no_hot; -pos2bin #( - .BIN_WIDTH( WIDTH_W ) -) pos2bin_b ( - .pos( od_filt[WIDTH-1:0] ), - .bin( od_bin[WIDTH_W-1:0] ), + pos2bin #( + .BIN_WIDTH( WIDTH_W ) + ) pos2bin_b ( + .pos( od_filt[WIDTH-1:0] ), + .bin( od_bin[WIDTH_W-1:0] ), - .err_no_hot( err_no_hot ), - .err_multi_hot( ) -); + .err_no_hot( err_no_hot ), + .err_multi_hot( ) + ); + + `include "clogb2.svh" endmodule + diff --git a/round_robin_enc.sv b/round_robin_enc.sv index b102193..4f5cbd8 100755 --- a/round_robin_enc.sv +++ b/round_robin_enc.sv @@ -35,7 +35,7 @@ round_robin_enc #( module round_robin_enc #( parameter WIDTH = 32, - WIDTH_W = $clog2(WIDTH) + WIDTH_W = $clogb2(WIDTH) )( input clk, // clock input nrst, // inversed reset, synchronous @@ -47,30 +47,33 @@ module round_robin_enc #( parameter ); -// current bit selector -logic [WIDTH_W-1:0] priority_bit = '0; -always_ff @(posedge clk) begin - if( ~nrst ) begin - priority_bit[WIDTH_W-1:0] <= '0; - end else begin - if( priority_bit[WIDTH_W-1:0] == WIDTH-1 ) begin + // current bit selector + logic [WIDTH_W-1:0] priority_bit = '0; + always_ff @(posedge clk) begin + if( ~nrst ) begin priority_bit[WIDTH_W-1:0] <= '0; end else begin - priority_bit[WIDTH_W-1:0] <= priority_bit[WIDTH_W-1:0] + 1'b1; - end // if - end // if nrst -end - -always_comb begin - if( id[priority_bit[WIDTH_W-1:0]] ) begin - od_valid = id[priority_bit[WIDTH_W-1:0]]; - od_filt[WIDTH-1:0] = 1'b1 << priority_bit[WIDTH_W-1:0]; - od_bin[WIDTH_W-1:0] = priority_bit[WIDTH_W-1:0]; - end else begin - od_valid = 1'b0; - od_filt[WIDTH-1:0] = '0; - od_bin[WIDTH_W-1:0] = '0; + if( priority_bit[WIDTH_W-1:0] == WIDTH-1 ) begin + priority_bit[WIDTH_W-1:0] <= '0; + end else begin + priority_bit[WIDTH_W-1:0] <= priority_bit[WIDTH_W-1:0] + 1'b1; + end // if + end // if nrst end -end + + always_comb begin + if( id[priority_bit[WIDTH_W-1:0]] ) begin + od_valid = id[priority_bit[WIDTH_W-1:0]]; + od_filt[WIDTH-1:0] = 1'b1 << priority_bit[WIDTH_W-1:0]; + od_bin[WIDTH_W-1:0] = priority_bit[WIDTH_W-1:0]; + end else begin + od_valid = 1'b0; + od_filt[WIDTH-1:0] = '0; + od_bin[WIDTH_W-1:0] = '0; + end + end + + `include "clogb2.svh" endmodule + diff --git a/round_robin_performance_enc.sv b/round_robin_performance_enc.sv index 93549e6..72eda07 100755 --- a/round_robin_performance_enc.sv +++ b/round_robin_performance_enc.sv @@ -34,7 +34,7 @@ round_robin_performance_enc #( module round_robin_performance_enc #( parameter WIDTH = 32, - WIDTH_W = $clog2(WIDTH) + WIDTH_W = $clogb2(WIDTH) )( input clk, // clock input nrst, // inversed reset, synchronous @@ -46,68 +46,71 @@ module round_robin_performance_enc #( parameter ); -// current bit selector -logic [WIDTH_W-1:0] priority_bit = '0; + // current bit selector + logic [WIDTH_W-1:0] priority_bit = '0; -// prepare double width buffer with LSB bits masked out -logic [2*WIDTH-1:0] mask; -logic [2*WIDTH-1:0] id_buf; -always_comb begin - integer i; - for ( i=0; i<2*WIDTH; i++ ) begin - if( i>priority_bit[WIDTH_W-1:0] ) begin - mask[i] = 1'b1; + // prepare double width buffer with LSB bits masked out + logic [2*WIDTH-1:0] mask; + logic [2*WIDTH-1:0] id_buf; + always_comb begin + integer i; + for ( i=0; i<2*WIDTH; i++ ) begin + if( i>priority_bit[WIDTH_W-1:0] ) begin + mask[i] = 1'b1; + end else begin + mask[i] = 1'b0; + end + end + id_buf[2*WIDTH-1:0] = {2{id[WIDTH-1:0]}} & mask[2*WIDTH-1:0]; + end + + logic [2*WIDTH-1:0] id_buf_filt; + leave_one_hot #( + .WIDTH( 2*WIDTH ) + ) one_hot_b ( + .in( id_buf[2*WIDTH-1:0] ), + .out( id_buf_filt[2*WIDTH-1:0] ) + ); + + logic [(WIDTH_W+1)-1:0] id_buf_bin; // one more bit to decode double width input + + logic err_no_hot; + assign od_valid = ~err_no_hot; + + pos2bin #( + .BIN_WIDTH( (WIDTH_W+1) ) + ) pos2bin_b ( + .pos( id_buf_filt[2*WIDTH-1:0] ), + .bin( id_buf_bin[(WIDTH_W+1)-1:0] ), + + .err_no_hot( err_no_hot ), + .err_multi_hot( ) + ); + + always_comb begin + if( od_valid ) begin + od_bin[WIDTH_W-1:0] = id_buf_bin[(WIDTH_W+1)-1:0] % WIDTH; + od_filt[WIDTH-1:0] = 1'b1 << od_bin[WIDTH_W-1:0]; end else begin - mask[i] = 1'b0; + od_bin[WIDTH_W-1:0] = '0; + od_filt[WIDTH-1:0] = '0; end end - id_buf[2*WIDTH-1:0] = {2{id[WIDTH-1:0]}} & mask[2*WIDTH-1:0]; -end -logic [2*WIDTH-1:0] id_buf_filt; -leave_one_hot #( - .WIDTH( 2*WIDTH ) -) one_hot_b ( - .in( id_buf[2*WIDTH-1:0] ), - .out( id_buf_filt[2*WIDTH-1:0] ) -); - -logic [(WIDTH_W+1)-1:0] id_buf_bin; // one more bit to decode double width input - -logic err_no_hot; -assign od_valid = ~err_no_hot; - -pos2bin #( - .BIN_WIDTH( (WIDTH_W+1) ) -) pos2bin_b ( - .pos( id_buf_filt[2*WIDTH-1:0] ), - .bin( id_buf_bin[(WIDTH_W+1)-1:0] ), - - .err_no_hot( err_no_hot ), - .err_multi_hot( ) -); - -always_comb begin - if( od_valid ) begin - od_bin[WIDTH_W-1:0] = id_buf_bin[(WIDTH_W+1)-1:0] % WIDTH; - od_filt[WIDTH-1:0] = 1'b1 << od_bin[WIDTH_W-1:0]; - end else begin - od_bin[WIDTH_W-1:0] = '0; - od_filt[WIDTH-1:0] = '0; - end -end - -// latching current -always_ff @(posedge clk) begin - if( ~nrst ) begin - priority_bit[WIDTH_W-1:0] <= '0; - end else begin - if( od_valid ) begin - priority_bit[WIDTH_W-1:0] <= od_bin[WIDTH_W-1:0]; + // latching current + always_ff @(posedge clk) begin + if( ~nrst ) begin + priority_bit[WIDTH_W-1:0] <= '0; end else begin - // nop, - end // if - end // if nrst -end + if( od_valid ) begin + priority_bit[WIDTH_W-1:0] <= od_bin[WIDTH_W-1:0]; + end else begin + // nop, + end // if + end // if nrst + end + + `include "clogb2.svh" endmodule +