Module: HDLRuby::Low::Low2VHDL

Defined in:
lib/HDLRuby/hruby_low2vhd.rb

Overview

Provides tools for converting HDLRuby::Low objects to VHDL.

Constant Summary collapse

@@vhdl08 =

Indicates if VHDL'08 can be generated. Default: true

NOTE: when possible, it is better to be left true since the identifier does not require any mangling in VHDL'08

true
@@alliance =

Indicates if target toolchain is Alliance: requires a slightly different VHDL syntax.

NOTE: this syntax is not lint-compatible and should be avoided unless using specifically Alliance.

false

Class Method Summary collapse

Class Method Details

.allianceObject

Tells if Allicance toolchain is targeted.



44
45
46
# File 'lib/HDLRuby/hruby_low2vhd.rb', line 44

def self.alliance
    return @@alliance
end

.alliance=(mode) ⇒ Object

Sets/unsets the Allicance toolchain targeting.



49
50
51
# File 'lib/HDLRuby/hruby_low2vhd.rb', line 49

def self.alliance=(mode)
    @@alliance = mode ? true : false
end

.architecture_name(name) ⇒ Object

Converts a +name+ to a VHDL architecture name.

NOTE: assume names have been converted to VHDL-compatible ones.



113
114
115
# File 'lib/HDLRuby/hruby_low2vhd.rb', line 113

def self.architecture_name(name)
    return self.vhdl_name(name.to_s + "_a")
end

.arith?(type) ⇒ Boolean

Tells if a +type+ is arithmetic-compatible.

Returns:

  • (Boolean)


118
119
120
121
# File 'lib/HDLRuby/hruby_low2vhd.rb', line 118

def self.arith?(type)
    return type.is_a?(TypeVector) && 
        [:signed,:unsigned,:float].include?(type.base.name)
end

.entity_name(name) ⇒ Object

Converts a +name+ to a VHDL entity name.

NOTE: assume names have been converted to VHDL-compatible ones.



106
107
108
# File 'lib/HDLRuby/hruby_low2vhd.rb', line 106

def self.entity_name(name)
    return self.vhdl_name(name.to_s + "_e")
end

.mux_function(type, num, spaces) ⇒ Object

Generates the VHDL code for the mux function for type string +tstr+ with +num+ choices. +spaces+ is the ident for the resulting code.



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/HDLRuby/hruby_low2vhd.rb', line 288

def self.mux_function(type,num,spaces)
    # Create the strin of the type.
    tstr = type.to_vhdl
    # Create the name of the function from the type.
    name = mux_name(tstr,num)
    # Create the condition.
    if num == 2 then
        cond = "cond : boolean"
    else
        # First compute the width of the condition.
        width = (num-1).width
        # Now generate the condition.
        cond = "val : std_logic_vector(#{width-1} downto 0)"
    end
    # Generate the arguments.
    args = num.times.map {|i| "arg#{i} : #{tstr}" }.join("; ")
    # Generate the body.
    if num == 2 then
        body = "#{spaces}   if(cond) then\n" +
               "#{spaces}      return arg0;\n" +
               "#{spaces}   else\n" +
               "#{spaces}      return arg1;\n" +
               "#{spaces}   end if;\n"
    else
        # First compute the type of the choices.
        vtype = TypeVector.new(:"",Bit,width-1..0)
        # Now generate the body.
        body = "#{spaces}   case(val) is\n" +
            num.times.map do |i|
               pos = Value.new(vtype,i).to_vhdl
               "#{spaces}   when #{pos} => return arg#{i};\n"
            end.join + 
               "#{spaces}   end case;\n"
    end
    # Generate the choices.
    # Generates the function
    return "#{spaces}function #{name}" + 
               "(#{cond}; #{args})\n" +
           "#{spaces}return #{tstr} is\n" +
           "#{spaces}begin\n" + body +
           "#{spaces}end #{mux_name(tstr,num)};\n\n"
end

.mux_name(tstr, num) ⇒ Object

Generates the name of a mux function by type string +tstr+ and number of arguments +num+.



281
282
283
# File 'lib/HDLRuby/hruby_low2vhd.rb', line 281

def self.mux_name(tstr,num)
    return "mux#{tstr.gsub(/[^a-zA-Z0-9_]/,"_")}#{num}"
end

.packages(spaces) ⇒ Object

Generates the pakage requirement for an entity. +spaces+ are the spaces to put before each line.



55
56
57
58
59
# File 'lib/HDLRuby/hruby_low2vhd.rb', line 55

def self.packages(spaces)
    return "#{spaces}library ieee;\n" +
           "#{spaces}use ieee.std_logic_1164.all;\n" +
           "#{spaces}use ieee.numeric_std.all;\n\n"
end

.to_arith(expr) ⇒ Object

Generates expression +expr+ while casting it to arithmetic-compatible type if required.



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/HDLRuby/hruby_low2vhd.rb', line 125

def self.to_arith(expr)
    if arith?(expr.type) then
        # The expression is arithmetic-compatible, just generate it.
        if expr.is_a?(Value) then
            return expr.to_arith
        else
            return expr.to_vhdl
        end
    else
        # The expression is to convert, by default convert to unsigned
        # (this is the standard interpretation of HDLRuby).
        if expr.type.to_vhdl == "std_logic" then
            # std_logic case: must convert to vector first.
            if alliance then
                # Alliance toolchain case.
                return "unsigned('0' & " + expr.to_vhdl + ")"
            else
                # General case.
                return "unsigned(\"\" & " + expr.to_vhdl + ")"
            end
        else
            # Other case, ue the expression direction.
            return "unsigned(" + expr.to_vhdl + ")"
        end
    end
end

.to_boolean(expr) ⇒ Object

Generates a expression converted to the boolean type.



183
184
185
186
187
188
189
190
191
192
# File 'lib/HDLRuby/hruby_low2vhd.rb', line 183

def self.to_boolean(expr)
    # if boolean?(expr) then
    if expr.boolean? then
        # Comparison, no conversion required.
        return expr.to_vhdl
    else
        # Conversion to boolean required.
        return "(" + expr.to_vhdl + " = '1')"
    end
end

.to_type(type, expr) ⇒ Object

Generates epression +expr+ while casting it to match +type+ if required.



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/HDLRuby/hruby_low2vhd.rb', line 196

def self.to_type(type,expr)
    # puts "expr=#{expr.to_vhdl}" unless expr.is_a?(Concat)
    # puts "type.width=#{type.width}, expr.type.width=#{expr.type.width}"
    if type.to_vhdl == "std_logic" then
        # Conversion to std_logic required.
        if expr.is_a?(Value) then
            # Values can simply be rewritten.
            if expr.content.to_s.to_i(2) == 0 then
                return "'0'"
            else
                return "'1'"
            end
        elsif expr.type.to_vhdl != "std_logic"
            # Otherwise a cast is required.
            # if expr.type.base.name == :signed then
            #     return "unsigned(#{expr.to_vhdl})(0)"
            # else
            #    # return "unsigned(#{expr.to_vhdl}(0))"
            #    return "unsigned(#{expr.to_vhdl})(0)"
            # end
            if alliance then
                # Specific syntax for casting to std_logic with Alliance
                if expr.type.width == 1 then
                    # No cast required with alliance if bitwidth is 1.
                    return expr.to_vhdl
                else
                    # Multi-bit, need to select a bit and possibly
                    # cast to unsigned.
                    if expr.type.signed? then
                        return "unsigned(#{expr.to_vhdl}(0))"
                    # elsif expr.is_a?(RefRange) then
                    #     # Range reference case.
                    #     return "#{expr.ref.to_vhdl}(#{expr.range.first.to_vhdl})"
                    else
                        # Other cases.
                        return "#{expr.to_vhdl}(0)"
                    end
                end
            else
                # Lint-compatible casting to std_logic
                if expr.type.signed? then
                    # Signed, cast to unsigned.
                    return "unsigned(#{expr.to_vhdl})(0)"
                # elsif expr.is_a?(RefRange) then
                #     # Range reference case.
                #     return "#{expr.ref.to_vhdl}(#{expr.range.first.to_vhdl})"
                else
                    # Other cases: for std_logic generation.
                    return expr.to_vhdl(0,true)
                end
            end
        else
            # Both are std_logic, nothing to to.
            return expr.to_vhdl
        end
    elsif expr.is_a?(Value) then
        # puts "type=#{type}, type.range=#{type.range}"
        # Value width must be adjusted.
        return expr.to_vhdl(0,false,type.width)
    elsif expr.is_a?(Concat) then
        return expr.to_vhdl(type)
    elsif expr.type.width < type.width then
        # Need to extend the type.
        return '"' + "0" * (type.width - expr.type.width) + '" & ' +
               expr.to_vhdl
    else 
        # No conversion required.
        return expr.to_vhdl
    end
end

.unarith_cast(type) ⇒ Object

Cast a +type+ to undo arithmetic conversion if necessary.



268
269
270
271
272
273
274
275
276
277
# File 'lib/HDLRuby/hruby_low2vhd.rb', line 268

def self.unarith_cast(type)
    # Is the type arithmetic?
    if arith?(type) then
        # Yes, no undo required.
        return ""
    else
        # No, undo required.
        return "std_logic_vector"
    end
end

.vhdl08Object

Tells if VHDL'08 is supported or not.



27
28
29
# File 'lib/HDLRuby/hruby_low2vhd.rb', line 27

def self.vhdl08
    return @@vhdl08
end

.vhdl08=(mode) ⇒ Object

Sets/unsets the support of VHDL'08.



32
33
34
# File 'lib/HDLRuby/hruby_low2vhd.rb', line 32

def self.vhdl08=(mode)
    @@vhdl08 = mode ? true : false
end

.vhdl_name(name) ⇒ Object

Converts a +name+ to a VHDL-compatible name.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/HDLRuby/hruby_low2vhd.rb', line 75

def self.vhdl_name(name)
    if vhdl08 then
        # VHDL'08, nothing to do if the name is VHDL-compatible.
        return name.to_s if self.vhdl_name?(name)
        # Otherwise put the name between //
        return "\\#{name}\\".to_s
    else
        # Not VHDL'08, need to mangle the name.
        # For safety also force downcase.
        name = name.to_s
        # Other letters: convert special characters.
        name = name.each_char.map do |c|
            if c=~ /[a-uw-z0-9]/ then
                c
            elsif c == "v" then
                "vv"
            else
                "v" + c.ord.to_s
            end
        end.join
        # First character: only letter is possible.
        unless name[0] =~ /[a-z]/ then
            name = "v" + name
        end
        return name
    end
end

.vhdl_name?(name) ⇒ Boolean

Tells if a +name+ is VHDL-compatible. To ensure compatibile, assume all the character must have the same case.

Returns:

  • (Boolean)


64
65
66
67
68
69
70
71
72
# File 'lib/HDLRuby/hruby_low2vhd.rb', line 64

def self.vhdl_name?(name)
    name = name.to_s
    # First: character check.
    return false unless name =~ /^[a-zA-Z]|([a-zA-Z][a-zA-Z_0-9]*[a-zA-Z0-9])$/
    # Then character sequence check.
    return false if name.include?("__")
    # Then case check.
    return (name == name.upcase || name == name.downcase)
end