//
// 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 words,
    parameter bits,
    parameter reverse_w = 0,
    parameter reverse_b = 0,
    parameter reg_d     = 0,
    parameter reg_q     = 0,
    parameter transpose = 1)
   (
    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-w-1 : w;
	       bb = reverse_b ? bits -b-1 : 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.
//
module reverse
  #(parameter bits,
    parameter reg_d   = 0,
    parameter reg_q   = 0,
    parameter reverse = 1)
   (
    input	      clk,
    input [bits-1:0]  d,
    output [bits-1:0] q
    );

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

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

   always @(*)
     begin
	integer i;
	for (i = 0; i < bits; i = i + 1)
	  begin
	     integer ii;
	     ii = reverse ? bits-i-1 : i;
	     out[i] = in[ii];
	  end
     end
endmodule // parameter
