Class: Origen::Registers::Container

Inherits:
Object
  • Object
show all
Defined in:
lib/origen/registers/container.rb

Overview

A container can be used to easily interface register operations to an IPS-style interface where the container will take care of data alignment and byte enable calculations. A container looks and behaves like a register and drivers should be able to accept a container in place of a regular register.

Here are some examples:

include Origen::Registers

#       Name  Address  Size  Bits
add_reg :r0,   4,       8,    data => {:bits => 8}
add_reg :r1,   5,       8,    data => {:bits => 8}
add_reg :r2,   6,       8,    data => {:bits => 8}
add_reg :r3,   7,       8,    data => {:bits => 8}

reg(:r0).write(0xB0)
reg(:r1).write(0xB1)
reg(:r2).write(0xB2)
reg(:r3).write(0xB3)

big    = Container.new
little = Container.new(:endian => :little)

big.add(reg(:r0)).data              # => 0x0000_00B0
little.add(reg(:r0)).data           # => 0xB000_0000
big.byte_enable                     # => 0b0001
little.byte_enable                  # => 0b1000

big.empty
big.data                            # => 0x0000_0000
big.address                         # => nil
big.add(reg(:r2))
big.address                         # => 4 (longword aligned)
big.add(reg(:r3)).add(reg(:r1)
big.add.data                        # => 0xB3B2_B100
big.byte_enable                     # => 0b1110

# Treat it like it's a register in drivers:
big.shift_out_left do |bit|
  pin(:tdi).drive!(bit.data)
end

# The address can be overridden
big.empty
big.add(reg(:r2), :address => 10)
big.address                         # => 8 (longword aligned)

# Containers can accomodate other containers
big.empty
lower_word = Container.new
lower_word.add(:r0).add(:r1)
big.add(:r3)
lower_word.data                     # => 0x0000_B1B0
big.data                            # => 0xB300_0000
big.add(lower_word)
big.data                            # => 0xB300_B1B0
lower_word.data                     # => 0x0000_B1B0

# Contained registers are the same register objects
reg(:r0).write(0x55)
big.data                            # => 0xB300_B155
lower_word.data                     # => 0x0000_B155

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Container

Returns a new instance of Container.

Parameters:

  • options (Hash) (defaults to: {})

    Options to customize the container

Options Hash (options):

  • :size (Integer) — default: 32

    The size of the container in bits

  • :endian (Symbol) — default: :big

    The endianness of the container, :big or :little For example big endian means that 4 a 32-bit container the bytes are arranged

    3,2,1,0

    whereas a little endian container would be [0,1,2,3].

  • :bits_per_address (Integer) — default: 8

    The number of bits that will be represented by an address increment of the given register's addresses



89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/origen/registers/container.rb', line 89

def initialize(options = {})
  options = {
    size:             32,
    endian:           :big,
    bits_per_address: 8
  }.merge(options)
  @size = options[:size]
  @endian = options[:endian]
  @owned_by = options[:owned_by]
  @bits_per_address = options[:bits_per_address]
  @regs = []
  @addresses = {}
end

Instance Attribute Details

#bits_per_addressObject (readonly)

The number of bits represented by an address increment of the contained registers. For example if the contained registers have a byte address this will return 8.



72
73
74
# File 'lib/origen/registers/container.rb', line 72

def bits_per_address
  @bits_per_address
end

#owned_byObject

Set this to a string or an array of strings that represents the name of the object that owns the container. If present any owned_by? requests made to the container will be evaluated against this string. If not then the request will be sent to the first contained register (if present).



80
81
82
# File 'lib/origen/registers/container.rb', line 80

def owned_by
  @owned_by
end

#regsObject (readonly) Also known as: registers

Returns the currently held registers



74
75
76
# File 'lib/origen/registers/container.rb', line 74

def regs
  @regs
end

#sizeObject (readonly)

The size of the container in bits



68
69
70
# File 'lib/origen/registers/container.rb', line 68

def size
  @size
end

Instance Method Details

#[](i) ⇒ Object

Returns the bit at the given bit position if it exists, otherwise returns an un-writable bit



273
274
275
# File 'lib/origen/registers/container.rb', line 273

def [](i)
  bit_at_position(i)
end

#add(reg, options = {}) ⇒ Object

Add the given register to the container, currently there is no error checking performed to ensure that it doesn't overlap with any existing contained registers.



110
111
112
113
114
115
116
# File 'lib/origen/registers/container.rb', line 110

def add(reg, options = {})
  @regs << reg
  addr = options[:address] || options[:addr]
  @addresses[reg] = addr if addr
  @regs.sort_by! { |reg| address_of_reg(reg) }
  self
end

#addressObject Also known as: addr

Returns the aligned address of the container based on the address of the currently contained registers



173
174
175
176
177
178
179
# File 'lib/origen/registers/container.rb', line 173

def address
  unless @regs.empty?
    addr = address_of_reg(@regs.first)
    shift = Math.log(size / bits_per_address, 2)
    (addr >> shift) << shift
  end
end

#address_of_reg(reg) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



119
120
121
# File 'lib/origen/registers/container.rb', line 119

def address_of_reg(reg)
  @addresses[reg] || reg.address
end

#big_endian?Boolean

Returns:

  • (Boolean)


211
212
213
# File 'lib/origen/registers/container.rb', line 211

def big_endian?
  @endian == :big
end

#bit_at_position(i) ⇒ Object

Returns the bit at the given bit position if it exists, otherwise returns an un-writable bit



255
256
257
258
259
260
261
262
# File 'lib/origen/registers/container.rb', line 255

def bit_at_position(i)
  reg = regs.find { |reg| reg_contains_position?(reg, i) }
  if reg
    reg[i - bit_shift_for_reg(reg)]
  else
    dummy_bit
  end
end

#bit_shift_for_reg(reg) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



207
208
209
# File 'lib/origen/registers/container.rb', line 207

def bit_shift_for_reg(reg)
  shift_for_reg(reg) * bits_per_address
end

#byte_enableObject

Returns the byte enable required to update the contained registers.



183
184
185
186
187
188
189
190
# File 'lib/origen/registers/container.rb', line 183

def byte_enable
  enable = 0
  regs.each do |reg|
    enable_bits = 0.ones_comp(reg.size / bits_per_address)
    enable += (enable_bits << shift_for_reg(reg))
  end
  enable
end

#clear_flagsObject

Call the clear_flags on all contained registers



283
284
285
# File 'lib/origen/registers/container.rb', line 283

def clear_flags
  @regs.each(&:clear_flags)
end

#contains_bits?Boolean

Returns:

  • (Boolean)


103
104
105
# File 'lib/origen/registers/container.rb', line 103

def contains_bits?
  true
end

#dataObject Also known as: val, value

Returns the data held by the contained registers where the data from each register is shifted into the correct position



125
126
127
128
129
130
131
# File 'lib/origen/registers/container.rb', line 125

def data
  d = 0
  regs.each do |reg|
    d += (reg.data << bit_shift_for_reg(reg))
  end
  d
end

#data_bObject

Data bar, the ones complement of the current data value of the container



137
138
139
# File 'lib/origen/registers/container.rb', line 137

def data_b
  ~data & ((1 << size) - 1)
end

#dummy_bitObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



278
279
280
# File 'lib/origen/registers/container.rb', line 278

def dummy_bit
  @dummy_bit ||= Bit.new(self, 0, writable: false)
end

#emptyObject

Remove all registers from the container



142
143
144
145
146
# File 'lib/origen/registers/container.rb', line 142

def empty
  @regs = []
  @addresses = {}
  self
end

#little_endian?Boolean

Returns:

  • (Boolean)


215
216
217
# File 'lib/origen/registers/container.rb', line 215

def little_endian?
  !big_endian?
end

#local_addr_for_reg(reg) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



193
194
195
# File 'lib/origen/registers/container.rb', line 193

def local_addr_for_reg(reg)
  address_of_reg(reg) & 0.ones_comp(Math.log(size / bits_per_address, 2))
end

#owned_by?(name) ⇒ Boolean

Proxies to the Reg#owned_by? method

Returns:

  • (Boolean)


157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/origen/registers/container.rb', line 157

def owned_by?(name)
  if owned_by
    [owned_by].flatten.any? do |al|
      al.to_s =~ /#{name}/i
    end
  else
    if @regs.empty?
      false
    else
      @regs.first.owned_by?(name)
    end
  end
end

#ownerObject

Returns the owner of the contained registers (assumed to be the same for all)



150
151
152
153
154
# File 'lib/origen/registers/container.rb', line 150

def owner
  unless @regs.empty?
    @regs.first.owner
  end
end

#reg_contains_position?(reg, position) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


265
266
267
268
269
# File 'lib/origen/registers/container.rb', line 265

def reg_contains_position?(reg, position)
  start = bit_shift_for_reg(reg)
  stop = start + reg.size - 1
  position >= start && position <= stop
end

#shift_for_reg(reg) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



198
199
200
201
202
203
204
# File 'lib/origen/registers/container.rb', line 198

def shift_for_reg(reg)
  if big_endian?
    local_addr_for_reg(reg)
  else
    (size / bits_per_address) - (local_addr_for_reg(reg) + (reg.size / bits_per_address))
  end
end

#shift_out_leftObject Also known as: shift_out_left_with_index

Shifts out a stream of bit objects corresponding to the size of the container. i.e. calling this on a 32-bit container this will pass back 32 bit objects. If there are holes then a dummy bit object will be returned that is not writable and which will always read as 0.

The index is also returned as a second argument. Note that the position property of the bit is not updated to reflect its position within the container (it will return its position with its parent register), therefore the index should be used if the calling code needs to work out the bit position within the container.



229
230
231
232
233
# File 'lib/origen/registers/container.rb', line 229

def shift_out_left
  size.times do |i|
    yield(bit_at_position(size - i - 1), i)
  end
end

#shift_out_rightObject Also known as: shift_out_right_with_index

Shifts out a stream of bit objects corresponding to the size of the container. i.e. calling this on a 32-bit container this will pass back 32 bit objects. If there are holes then a dummy bit object will be returned that is not writable and which will always read as 0.

The index is also returned as a second argument. Note that the position property of the bit is not updated to reflect its position within the container (it will return its position with its parent register), therefore the index should be used if the calling code needs to work out the bit position within the container.



246
247
248
249
250
# File 'lib/origen/registers/container.rb', line 246

def shift_out_right
  size.times do |i|
    yield(bit_at_position(i), i)
  end
end