RHDL signals carry digital values between components. This guide covers signal values, bit selection, concatenation, and reduction operators.
Signal Values
Signals can represent:
- Binary values —
0or1for single-bit, multi-bit integers for buses - Unknown (X) — uninitialized or conflicting values
- High-impedance (Z) — disconnected or tri-state outputs
Wires
Wires connect components and propagate signal changes:
wire = RHDL::HDL::Wire.new("my_signal", width: 8)
wire.set(0x42)
wire.get # => 66
wire.bit(0) # => 0 (LSB)
wire.bit(6) # => 1Internal wires within a component are declared with the wire macro:
wire :intermediate, width: 8
wire :carry
wire :alu_out, width: :width # Parameterized widthBit Selection and Slicing
Extract individual bits or ranges within a behavior block:
behavior do
# Single bit
lsb <= a[0] # Least significant bit
msb <= a[7] # Most significant bit (8-bit signal)
sign <= a[7] # Sign bit for 8-bit signed
# Range slicing
low_nibble <= a[3..0] # Bits 0-3
high_nibble <= a[7..4] # Bits 4-7
byte <= word[15..8] # Upper byte of 16-bit word
endConcatenation
Join signals together — the first argument becomes the high bits:
behavior do
# Combine two bytes into a 16-bit word
combined <= high_byte.concat(low_byte)
# Multiple concatenation
word <= a.concat(b).concat(c).concat(d)
# Shift left by 1 with zero fill
shifted_left <= a[6..0].concat(lit(0, width: 1))
# Shift right by 1 with zero fill
shifted_right <= lit(0, width: 1).concat(a[7..1])
endReplication
Repeat a signal multiple times:
behavior do
# Sign extension: replicate sign bit
sign_ext <= sign_bit.replicate(8)
# Arithmetic shift right (preserve sign)
sign = a[7]
asr1 <= sign.concat(a[7..1])
asr2 <= sign.replicate(2).concat(a[7..2])
endReduction Operators
Reduce a multi-bit signal to a single bit:
behavior do
# OR reduction — is any bit set?
non_zero <= reduce_or(error_flags)
# AND reduction — are all bits set?
all_ready <= reduce_and(ready_signals)
# XOR reduction — parity
parity <= reduce_xor(data)
endLiterals
Always specify explicit widths for synthesis correctness:
behavior do
zero <= lit(0, width: 8)
max <= lit(0xFF, width: 8)
one_bit <= lit(1, width: 1)
masked <= a & lit(0x0F, width: 8)
endPort Width Query
Get the width of a port at elaboration time:
behavior do
w = port_width(:result)
default_val <= lit(0, width: w)
endComplete Example: Bit Manipulation
class BitManipulator < RHDL::Sim::Component
input :data, width: 8
input :op, width: 3
output :result, width: 8
output :flag
behavior do
reversed = data[0].concat(data[1]).concat(data[2]).concat(data[3])
.concat(data[4]).concat(data[5]).concat(data[6]).concat(data[7])
swapped = data[3..0].concat(data[7..4])
parity = reduce_xor(data)
result <= case_select(op, {
0 => data[7..1].concat(lit(0, width: 1)), # Shift right
1 => data[6..0].concat(lit(0, width: 1)), # Shift left
2 => reversed, # Bit reverse
3 => swapped, # Nibble swap
4 => ~data # Invert
}, default: data)
flag <= parity
end
endNext Steps
- Ports and Interfaces — Vec and Bundle for structured types
- Ruby DSL Fundamentals — complete operator reference