//=========================================================================
// RTL Model of GCD Unit
//
// Author : Christoher Batten
// Date   : February 5, 2005
//
// This module implements a simple GCD datapath using a RTL Verilog 
// style. The test harness tests one set of inputs possibly taken from
// the command line and is designed to work with the vmodel-tester.pl 
// perl script.
//

//-------------------------------------------------------------------------
// GCD Datapath

module gcd_dpath #( parameter width = 16 )
                  ( input  clock,
                    input  A_en, B_en, A_mux_sel, B_mux_sel, out_mux_sel,
                    input  [width-1:0] A_in, B_in,
                    output B_zero, A_lt_B,
                    output [width-1:0] Y );

  reg [width-1:0] A, B;

  // Output is A

  assign Y = A;
  
  // Datapath logic

  wire [width-1:0] out    = ( out_mux_sel ) ? B   : A - B;
  wire [width-1:0] A_next = ( A_mux_sel )   ? out : A_in;
  wire [width-1:0] B_next = ( B_mux_sel )   ? A   : B_in;
  
  // Generate output control signals

  wire B_zero = ( B == 0 );
  wire A_lt_B = ( A < B );

  // Edge-triggered flip-flops

  always @( posedge clock )
  begin
   if ( A_en ) 
     A <= A_next;
   if ( B_en ) 
     B <= B_next;
  end

endmodule

//-------------------------------------------------------------------------
// GCD Control Unit

module gcd_ctrl ( input  clock, reset, go, 
                  input  B_zero, A_lt_B,
                  output A_en, B_en, A_mux_sel, B_mux_sel, out_mux_sel,
                  output done );

  // The running bit indicates whether or not we are currently
  // calculating a GCD. It is zero until go goes high at which time it
  // becomes one. It remains one until done goes high.

  reg running = 0;  
  always @( posedge clock )
  begin
    if ( go )
      running <= 1;
    else if ( done )
      running <= 0;    
  end
  
  // Combinational control logic - we group all the control signals
  // onto one bus to make the verilog more concise

  reg [5:0] ctrl_sig;
  assign { A_en, B_en, A_mux_sel, B_mux_sel, out_mux_sel, done } = ctrl_sig;

  always @(*)
  begin

    if ( !running )      ctrl_sig = 6'b11_00x_0; // Latch in A and B values
    else if ( A_lt_B )   ctrl_sig = 6'b11_111_0; // A <= B and B <= A
    else if ( !B_zero )  ctrl_sig = 6'b10_1x0_0; // A <= A - B and B <= B
    else                 ctrl_sig = 6'b00_xxx_1; // Done

  end

endmodule

//-------------------------------------------------------------------------
// GCD Toplevel Module

module gcd_rtl #( parameter width = 16 ) 
                ( input              clock,
                  input              reset,
                  input              go,
                  input [width-1:0]  A_in, B_in,
                  output             done,
                  output [width-1:0] Y );


  // Control signals             
  wire B_zero, A_lt_B, A_en, B_en, A_mux_sel, B_mux_sel, out_mux_sel;

  // Instantiate control and datapath units
  gcd_dpath #( .width(width) ) 
             the_gcd_dpath( .clock(clock), .A_en(A_en), .B_en(B_en), 
                            .A_mux_sel(A_mux_sel), .B_mux_sel(B_mux_sel),
                            .out_mux_sel(out_mux_sel),
                            .A_in(A_in), .B_in(B_in), .B_zero(B_zero), 
                            .A_lt_B(A_lt_B), .Y(Y) );

  gcd_ctrl the_gcd_ctrl( .clock(clock), .reset(reset), .go(go),
                         .B_zero(B_zero), .A_lt_B(A_lt_B),
                         .A_en(A_en), .B_en(B_en), 
                         .A_mux_sel(A_mux_sel), .B_mux_sel(B_mux_sel),
                         .out_mux_sel(out_mux_sel),
                         .done(done) );
  
endmodule

//-------------------------------------------------------------------------
// GCD Test Harness

module gcd_test;
  parameter width = 32;

  reg clock = 0;
  reg reset = 0;
  reg go    = 0;

  reg [width-1:0] A_in, B_in;

  wire done;
  wire [width-1:0] Y;

  //-------------------------------------------------
  // Instantiate the gcd unit and make a clock gen

  always #10 clock = ~clock;

  gcd_rtl #(.width(width))
          gcd_unit( .clock(clock), .reset(reset), .go(go),
                    .A_in(A_in), .B_in(B_in), .done(done), .Y(Y) );
  
  initial
  begin

    // This turns on VCD (plus) output

    $vcdpluson(0);
    
    // Default inputs if cmdline args are not provided
    
    A_in = 27;
    B_in = 15;

    // Read in cmdline args
    
    $value$plusargs("a-in=%d",A_in);
    $value$plusargs("b-in=%d",B_in);

    // Strobe reset signal

    #5  reset = 1;
    #20 reset = 0;
                
    // Strobe go signal

    #20 go = 1;
    #20 go = 0;
    
  end


  always @( done )
  begin

    // If done output results
    
    if ( done )
    begin
     
      #15;
      $display(" a-in    = %d", A_in );
      $display(" b-in    = %d", B_in );
      $display(" gcd-out = %d", Y    );
      $finish;

    end
    
  end

endmodule