Sitemap || RHDL / RHDL

RHDL

UsersGuide


Contents:

Introduction:

RHDL (Ruby Hardware Description Language) is an HDL based on the Ruby programming language. My idea in developing RHDL was to build an HDL on an object oriented programming language to allow HDL features ( concurrent processes, signals, parallelism etc.) in addition to features which come with a modern, object oriented, agile programming language like Ruby (www.ruby-lang.org). The intent is to allow more than just simulation, but also verification and testbench creation features. Ultimately, I would like to be able to translate RHDL to VHDL and/or Verilog. The fact that RHDL is based on Ruby allows modeling at a higher level of abstraction than is possible with VHDL or Verilog.

RHDL users don't need to know much Ruby - this is intentional, I didn't want potential users to have to know Ruby in order to benefit from RHDL. However, RHDL becomes a more powerful tool if the user takes a little time to learn some Ruby (an excellent tutorial and reference book on Ruby is: "Programming Ruby: The Pragmatic Programmer's Guide" by Thomas and Hunt.)

RHDL is not stricly speaking a new language. It is a set of modules (code libraries) that allow Ruby to look like an HDL. Ruby has a concept called code blocks; these blocks are made into lexically scoped closures in order to define a domain specific language like RHDL without having to write a seperate parser.

RHDL syntax

Comments

Comments start with a '#'. The rest of the line following is a comment.

string literals

RHDL Types

Bit

Bit Operators: (given a = Bit('1'), b = Bit('0') )

BitVector

EnumType

Other Ruby Types

RHDL elements

Signals

Signal assignment:

It is essential to understand that '=' is not to be used for signal assignment. The reason being that Ruby uses '=' for assigning object references to variables. Use either 'assign' or '<<' to assign new values to signals.

Examples [given the declaration: bv = Signal(BitVector('1001',4)) ]

Delayed signal assignment

You can specify that values be assigned to signals after a specified amount of ttime by usign the assign_at method:

    bv.assign_at(5) { bv + 1 }


which means that the value bv+1 will be assigned to bv five timesteps after the current one.

Ports

TODO: define Ports

Note: the role of Ports in RHDL is 'evolving'. At this point, I don't believe they are necessary except for two uses:

Behavioral elements

define_behavior

define_behavior takes a block of code (surrounded by '{}') in which all behavioral code for the design is placed. (NOTE: a completely structural design doesn't need to have a define_behavior declaration).

It is essential that the '{' be on the same line as 'define_behavior'. The reason for this is that define_behavior is a method and the block of code surrounded by '{}' is an argument to the define_behavior method, which means the following would be illegal:

    define_behavior
    {
       #this will result in an error!
    }

process

Very similar to the 'process' statement in VHDL or the 'always' statement in Verilog. The process statement can take a list of sensitive signals. The block following the process declaration is executed when any of the signals in the sensitivity list changes. Example:

    process(clk){
       puts "clk changed: clk"
    } 

will print "clk changed: 1|0" whenever the clk signal changes. NOTE: process statements should only be used within define_behavior blocks.

Again, as with the define_behavior declaration, the '{' must appear on the same line as the 'process'.

wait

Used to suspend a process until some condition is met. Example: 
    process() {
        wait { $simTime == 1000 }
        #other stuff
    }

In this example the process will be initiated, but it will be suspended until $simTime is equal to 1000. NOTE: $simTime is a globally available variable that contains the current simulation time (or the number of steps that have been run so far).

NOTE: You can use wait statements in a process with a sensitivity list, but the results may not be what you expect. It is probably best to use wait statements only in a process without a sensitivity list but this is not enforced by RHDL.

wait_for

Used to suspend a process for a time. Example:

      
   clock=Signal(Bit(0))
   define_behavior {
     process() {
          wait_for 4
          clock << clock.inv
          wait_for 3
          clock << clock.inv
      }
   }

This example creates a repeating clock signal which starts out low and remains low for four time steps and then goes high and remains high for three time steps and repeats.

Designs

A design in RHDL is similar to an entity/architecture pair in VHDL. An RHDL design contains either a structural or behavioral design description. RHDL designs are classes in Ruby – in other words they define a template for defining objects. RHDL designs can be included in other RHDL designs thus implementing hierarchy.

Here's an example of a simple RHDL design that defines a simple OR gate:

  require 'RHDL' 
  class My_Or < RHDL::Design
    include RHDL
    def initialize(a,b,a_or_b)
      super()
      define_behavior {
        a_or_b << (a | b)
      }
    end
  end

The name of this design class is My_Or. The the '< RHDL::Design' indicates that this class inherits from a Ruby module called RHDL::Design; this module is actually where statements like define_behavior, process and wait are actually defined. The next line 'include RHDL' simply makes those methods available to your design. The next line which starts 'def initialize' is the constructor for My_Or objects. It is called whenever you instantiate a My_Or object (such as: My_Or.new(aa,bb,output) ). The constructor takes signal arguments - in this case: a,b,a_or_b ; a and b are inputs to the design, while a_or_b is an output. The next line 'super()' is required so that RHDL designs can be correctly constructed; you can think of this as some wizardry that is peeking out from behind the curtain (it's a Ruby requirement that couldn't quite be hidden from view, just remember that you always need to make a call to super in your design's constructor because you really want your design to be super!). After this the behavior of the design is described in a define_behavior block; in this case a is Or'ed with b.

Instantiating the design

Now to use the My_Or class/design that you've defined:

  include RHDL
  aa = Signal(Bit('0'))
  bb = Signal(Bit('0'))
  output = Signal(Bit()) #initialized to 'X'
  myOr = My_Or.new(aa,bb,output)

The include RHDL tells Ruby to allow calls to functions (methods) defined in the RHDL module to be called in the current scope. We then set up two input signals (aa and bb) and an output signal ( output), instantiate a My_Or object and pass in the signals.

Simulating the design

Simulation in RHDL is done by including the Simulator module and calling the step method which is defined in that module (NOTE: this is subject to change, I'll probably be changing the name of this to the Simulator module). Here's how the design is simulated:

  include Simulator
  
  step { puts "aa=#{aa}, bb=#{bb}, output=#{output}"}
  aa << '1'
  step
  bb << '1'
  step
  aa << '0'
  step

The output from running this is:

  step #0: aa=0, bb=0, output=X
  step #1: aa=0, bb=0, output=0
  step #2: aa=1, bb=0, output=1
  step #3: aa=1, bb=1, output=1
  step #4: aa=0, bb=1, output=1

The step method in the Simulator module is used to step the simulation to the next time interval. Notice that step can take an optional block of code which is executed at each time step. You do not need to specify this block of code for each step as it will be 'remembered' until a new block is given. In this example the first step takes a block which prints out the values of aa, bb and output. The step method takes care of printing the 'step #x:' in front of each line of output (NOTE: the line which starts with 'step #0:' is the output at $simTime==0 prior to any simulation steps, it represents the initial values of the signals specified.)

A note about design reuse

By now you've noticed that there is no type specification on the signals passed into/out of the My_Or design. This is primarily because Ruby is what is known as a dynamically typed language as opposed to a statically typed language like C or Java (or VHDL or Verilog) where the types of all variables must be declared prior to their use in the program. The main advantage in this case is that you can reuse your My_Or design with different types of signals, for example, if you want to OR four-bit BitVectors:

  include RHDL
  aa = Signal(BitVector('0000',4))
  bb = Signal(Bit('0000',4))
  output = Signal(BitVector('XXXX',4)) 
  myOr = My_Or.new(aa,bb,output)

As long as the underlying types of the signals being passed in support an OR operator ( | ) it just works. No need to rewrite your My_Or design to accommodate different types. This greatly improves the chances that you'll be able to reuse your design code.

...More to come... for now do check out the examples.


Site map || RHDL / RHDL