Sitemap || RHDL / RHDL

RHDL

UsersGuide


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.


Inputs and Outputs


Input and Output ports for a model are defined in the inputs and outputs section of the model.

Generics

Generics in RHDL are analogous to generics in VHDL.  They are defined at the beginning of a model declaration block, for example:

  Foo = model {
generics width => 8, length => 8
    inputs a, b
    outputs c
    define_behavior {
       #behavioral stuff
       #width and length can be used in this scope
    }
  }
Note the use of '=>'not '=' in the generic declaration.  The generic declaration in this case is specifying two generics width and length  both with default values of 8.

Behavioral elements

init block

init blocks are optional.  They are used to make declarations which are 'one-time-only' - ie. the block passed to init is executed only once.  In  a purely structural design where you are only instantiating models and connecting them together with signals you might only have an init block without a define_behavior block.  In a behavior model, you might have both an init block and a define_behavior block - variables and signals which are internal to the model would be declared in the scope of the init block and then the define_behavior block would also be defined within the init block.

Examples:
 
  1. Structural:
    NandGate = model {
      inputs  aa, bb
      outputs a_nand_b
      init {
         sig_type = aa.type
         a_and_b = Signal(sig_type.new) #more generic
         andg = AndGate.new(:a=>aa,:b=>bb,:out=>a_and_b,:my_generic=>22)
         Inverter.new(:a=>a_and_b, :not_a=>a_nand_b)
      }
    }
      In this example we've built a nand gate out of an Inverter and an AndGate. [Note the trick used to create the a_and_b Signal; it causes the Signal to be of the same type as the aa input thus making the model more generic (see the design reuse section below for more ways to improve reusabilty).]

    2. Behavioral:
  include RHDL
  Latch = model {
    inputs  g,rst,d
    outputs q
init {
reset_val = 0
    define_behavior {
      process(g,rst,d){
        behavior {
          if rst == '1'
            q <= reset_val
          elsif g == '1'
            q <= d
          end
        }
      }
    }
  }
}

   In this latch example notice that we have both an init block and a define_behavior block.  The reset_val variable is set to 0 in the scope of the init block, it's then assigned to q during a reset (in the define_behavior block below) [NOTE: there are other ways of doing this sort of thing, you could declare a RESET_VAL constant early on in the model description, for example, or you could use a generic. The point being illustrated here is that variables defined in the init block don't change over time.]
  

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, but it must have an init block).

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 as it is in VHDL.

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.

Models

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

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

  
include RHDL MyOr = model {
inputs a, b
outputs a_or_b
define_behavior { a_or_b <= (a | b) } }

The name of this model (or class) is My_Or.   The first line 'include RHDL' simply makes the various RHDL elements available to your design. The next two lines are where the inputs and outputs of the model are declared. It is called whenever you instantiate a My_Or object (such as: My_Or.new(aa,bb,output) )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 = MyOr.new(:a=>aa,:b=>bb,:a_or_b=>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 by named association.

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 = MyOr.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