I suspect that the set that contains Ruby Programmers AND HDL coders is
pretty small (maybe it only contains me at this point), so this may not
make much sense to the vast majority of Rubyists out there.
I'm probably jumping the gun a bit by announcing this, but It's just
amazing how much progress I've made on this in only a couple of days - it
just goes to show the power of Ruby.
Anyway, I've had an idea for a while now for creating a Hardware
Description Language using Ruby. It's been done with Java (JHDL - see:
http://www.jhdl.org/ ) and also in Perl (see the Hardware::Simulator
module on CPAN) so I wanted to try it in Ruby.
For those not familiar with HDLs they are languages used to describe
hardware designs, kind of similar to textual schematics, but much more
powerful because they are programming languages. HDL's tend to look very
different from regular programming languages, however. A lot of stuff has
to appear to happen in parallel in an HDL.
I tried to make RHDL look very similar to VHDL, a widely used HDL in
industry (the other being Verilog). Here's an example of a design that
has a couple of inputs and produces outputs based on OR'ing, AND'ing and
inverting inputs. It also contains a process which is sensitive to a clk
signal and increments a counter whenever 'clk' changes and is equal to
'1':
#################<<<RHDL>>>######################
class My_Design < RHDL::Design
include RHDL
def initialize()
inputs = { 'A' => Port.new(),
'B' => Port.new() }
outputs = { 'NOT_A' => Port.new(),
'A_OR_B' => Port.new(),
'CLK' => Port.new(),
'A_AND_B' => Port.new()
}
super(inputs,outputs)
#define internal signals
a = Signal.new(Bit.new('1')); inputs['A'].connect(a)
b = Signal.new(Bit.new('0')); inputs['B'].connect(b)
o = Signal.new(Bit.new()); outputs['NOT_A'].connect(o)
o2= Signal.new(Bit.new()); outputs['A_AND_B'].connect(o2)
o3= Signal.new(Bit.new()); outputs['A_OR_B'].connect(o3)
clk= Signal.new(Bit.new('0')); outputs['CLK'].connect(clk)
counter = 0
define_behavior {
o2<= proc{a * b}
o3<= proc{a + b}
o<= proc{ a.inv }
#create a clock that changes every 2 cycles:
clk.assign_at(proc{ clk.inv },2 )
process(clk) {
if clk == '1' && clk.event
counter += 1
puts "counter = #{counter}"
end
}
}
end
end
bit_values = ['0','1','Z','X']
#now instantiate the design and step through it while assigning all
#possible values to A and B inputs:
md = My_Design.new()
bit_values.each { |v|
bit_values.each { |v2|
md.step({'A' => v,'B' => v2 })
}
}
################>>>RHDL<<<###################
And here's the output from running this design:
Inputs: A = 0 B = 0 Outputs: CLK = 0 NOT_A = 1 A_OR_B = 0 A_AND_B = 0
Inputs: A = 0 B = 1 Outputs: CLK = 1 NOT_A = 1 A_OR_B = 1 A_AND_B = 0
counter = 1
Inputs: A = 0 B = Z Outputs: CLK = 1 NOT_A = 1 A_OR_B = X A_AND_B = X
Inputs: A = 0 B = X Outputs: CLK = 0 NOT_A = 1 A_OR_B = X A_AND_B = X
Inputs: A = 1 B = 0 Outputs: CLK = 0 NOT_A = 0 A_OR_B = 1 A_AND_B = 0
Inputs: A = 1 B = 1 Outputs: CLK = 1 NOT_A = 0 A_OR_B = 1 A_AND_B = 1
counter = 2
Inputs: A = 1 B = Z Outputs: CLK = 1 NOT_A = 0 A_OR_B = X A_AND_B = X
Inputs: A = 1 B = X Outputs: CLK = 0 NOT_A = 0 A_OR_B = X A_AND_B = X
Inputs: A = Z B = 0 Outputs: CLK = 0 NOT_A = X A_OR_B = X A_AND_B = X
Inputs: A = Z B = 1 Outputs: CLK = 1 NOT_A = X A_OR_B = X A_AND_B = X
counter = 3
Inputs: A = Z B = Z Outputs: CLK = 1 NOT_A = X A_OR_B = X A_AND_B = X
Inputs: A = Z B = X Outputs: CLK = 0 NOT_A = X A_OR_B = X A_AND_B = X
Inputs: A = X B = 0 Outputs: CLK = 0 NOT_A = X A_OR_B = X A_AND_B = X
Inputs: A = X B = 1 Outputs: CLK = 1 NOT_A = X A_OR_B = X A_AND_B = X
counter = 4
Inputs: A = X B = Z Outputs: CLK = 1 NOT_A = X A_OR_B = X A_AND_B = X
Inputs: A = X B = X Outputs: CLK = 0 NOT_A = X A_OR_B = X A_AND_B = X
The amazing thing is that the module RHDL is only 300 lines of Ruby!
And, I think it has some advantages over JHDL which seems to only allow a
structural style at this point (ie. in JHDL you have to instanitate
primitive gates, it doesn't seem to allow equations).
This is mostly a proof of concept, so for now if you're interested in
getting the RHDL source code send me an email.
TODO:
* code cleanup - Like I said, I did this in a couple of days and the code
tends to show it ;-)
* get rid of the need for 'proc's for equation assignments (I think it's
doable, so they'll look like: o <= {a+b} but I also want to allow:
o <= a [the case where there is no equation] )
* define a Bit_Vector class so signals can have width (as in busses).
* I'll probably change the operators to AND, OR, XOR so that *,+,^ can
have their normal uses (and it's consistent with VHDL).
* Way in the future: look at a way to generate VHDL from RHDL.
Phil