//
// Conditionally register a data word; useful for parameterizing modules.
//
module condreg
  #(parameter bits,
    parameter register)
   (
    input	      clk,
    input [bits-1:0]  d,
    output [bits-1:0] q
    );

   reg [bits-1:0]  qr;
   wire		   clock = register ? clk : 1'b0;
   assign          q = register ? qr : d;

   always @(posedge clock)
     qr <= d;
endmodule // condreg

//
// To transpose the dimensions of a packed array containing
// two-dimentional data, optionally reversing the word/bits order.
// The terms "words" and "bits" are as defined to the input data
// stream; i.e. the with words = 3, bits = 2 and the d[] array
// containing b2 b1 b0 a2 a1 a0, the q[] array will contain:
// b2 a2 b1 a1 b0 a0 with no reversal,
// a2 b2 a1 b1 a0 b0 with word reversal,
// b0 a0 b1 a1 b2 a2 with bit reversal, and
// a0 b0 a1 b1 a2 b2 with word and bit reversals.
//
// If the parameter "transpose" is 0, then no actual transpose is done;
// this may be useful to parameterize other modules.
//
module transpose
  #(parameter integer words,
    parameter integer bits,
    parameter [0:0] reverse_w = 1'b0,
    parameter [0:0] reverse_b = 1'b0,
    parameter [0:0] reg_d     = 1'b0,
    parameter [0:0] reg_q     = 1'b0,
    parameter [0:0] transpose = 1'b1)
   (
    input		    clk,
    input [words*bits-1:0]  d,
    output [words*bits-1:0] q
   );

   wire [words*bits-1:0]    in;
   reg [words*bits-1:0]     out;

   condreg #(.bits(words*bits), .register(reg_d))
   dreg (.clk (clk), .d (d), .q(in));
   condreg #(.bits(words*bits), .register(reg_q))
   qreg (.clk (clk), .d (out), .q(q));

   always @(*)
     begin
	integer w, b;
	for (w = 0; w < words; w = w + 1)
	  for (b = 0; b < bits; b = b + 1)
	    begin
	       integer ww, bb, ii, oo;
	       ww = reverse_w ? (words-1)-w : w;
	       bb = reverse_b ? (bits -1)-b : b;
	       ii = ww*bits+bb;
	       oo = transpose ? b*words+w  : w*bits+b;
	       out[oo] = in[ii];
	    end
     end // always @ (*)
endmodule // parameter

//
// Bit-reverse a packed array
//
// If the parameter "reverse" is 0, then just pass through
// the input; this may be useful to parameterize other modules.
// This is just a special case of the transpose module.
//
module reverse
  #(parameter integer bits,
    parameter [0:0] reg_d   = 1'b0,
    parameter [0:0] reg_q   = 1'b0,
    parameter [0:0] reverse = 1'b1)
   (
    input	      clk,
    input [bits-1:0]  d,
    output [bits-1:0] q
    );

   transpose #(
	       .words     ( 1 ),
	       .bits      ( bits ),
	       .reverse_w ( 1'b0 ),
	       .reverse_b ( reverse ),
	       .reg_d     ( reg_d ),
	       .reg_q     ( reg_q ),
	       .transpose ( 1'b0 )
	       )
   rev ( .clk (clk), .d (d), .q (q) );
endmodule // parameter