###########################################################################
# TestTb.rb - test TestBench and hierarchy and structural style
###########################################################################
require 'RHDL'

################################################
# not gate
################################################
class My_Not < RHDL::Design
  include RHDL
  def initialize(a,not_a)
    inputs  = { 'A'     => Port(a) }
    outputs = { 'NOT_A' => Port(not_a) }
    super(inputs,outputs)
    define_behavior {
      not_a.<< { a.inv  }
    }
  end
end

################################################
# Or gate
################################################
class My_Or < RHDL::Design
  include RHDL
  def initialize(a,b,a_and_b)
    inputs  = { 'A' => Port(a),
                'B' => Port(b)
              }
    outputs = {
               'A_AND_B' => Port(a_and_b)
              }
    super(inputs,outputs)
    define_behavior {
      a_and_b.<< { a | b }
    }
  end
end

##############################################################################
# My_And is an 'and' gate made in a structural style with an OR gate and three
# NOT gates (to test hierarchy and structural design in RHDL)
##############################################################################
class My_And < RHDL::Design
  include RHDL
  def initialize(a,b,a_and_b)
    inputs  = { 'A' => Port(a),
                'B' => Port(b)
              }
    outputs = {
                'A_AND_B' => Port(a_and_b)
              }
    super(inputs,outputs)
    a_not = Signal(Bit())
    b_not = Signal(Bit())
    a_not_OR_b_not = Signal(Bit())
    notGateA = My_Not.new(a,a_not)
    notGateB = My_Not.new(b,b_not)
    orGate = My_Or.new(a_not,b_not,a_not_OR_b_not)
    notGateOut = My_Not.new(a_not_OR_b_not,a_and_b)
  end
end


class My_Counter < RHDL::Design
  include RHDL #mixin RHDL methods
  def initialize(rst,clk,counter)
    inputs  = { 'RST' => Port(rst),
                'CLK' => Port(clk)}
    outputs = {
                'COUNTER' => Port(counter)
              }
    super(inputs,outputs)

    clk_events = 0 #an integer variable
    define_behavior {
          process(counter){
            puts "-->Start of First Process"
            wait { clk_events == 3 }
            puts "counter = #{counter}"
            puts "<--End of First Process"
          }
          process(clk) {
            puts "--->Start of Second Process"
            #synchronous reset
            if clk == '1' && clk.event
              if rst == '1'
                counter.assign 0
              else
                counter.assign (counter + 1)
              end
              clk_events += 1
            end
            puts "clk_events = #{clk_events}"
            puts "<---End of Second Process"
          }
    }
  end
end