Build a simple circuit from scratch — your first complete RHDL design from source to simulation to Verilog export.
A Clocked Register
We will build an 8-bit register with enable and reset, then simulate it and export to Verilog.
class MyRegister < RHDL::Sim::SequentialComponent
input :d, width: 8
input :clk
input :rst
input :en
output :q, width: 8
sequential clock: :clk, reset: :rst, reset_values: { q: 0 } do
q <= mux(en, d, q) # Load when enabled, hold otherwise
end
endThis component:
- Samples
don the rising edge ofclkwhenenis high - Holds its current value when
enis low - Resets
qto 0 whenrstis asserted
Simulate It
Test the register interactively:
reg = MyRegister.new("my_reg")
# Reset
reg.set_input(:rst, 1)
reg.set_input(:en, 0)
reg.set_input(:clk, 0); reg.propagate
reg.set_input(:clk, 1); reg.propagate
puts reg.get_output(:q) # => 0
# Load a value
reg.set_input(:rst, 0)
reg.set_input(:en, 1)
reg.set_input(:d, 0x42)
reg.set_input(:clk, 0); reg.propagate
reg.set_input(:clk, 1); reg.propagate
puts reg.get_output(:q) # => 66 (0x42)
# Hold (en=0, value should stay)
reg.set_input(:en, 0)
reg.set_input(:d, 0xFF)
reg.set_input(:clk, 0); reg.propagate
reg.set_input(:clk, 1); reg.propagate
puts reg.get_output(:q) # => 66 (still 0x42)Test with RSpec
Write a proper test:
# spec/my_register_spec.rb
RSpec.describe MyRegister do
let(:reg) { MyRegister.new('test') }
def clock!
reg.set_input(:clk, 0); reg.propagate
reg.set_input(:clk, 1); reg.propagate
end
it "loads data when enabled" do
reg.set_input(:rst, 0)
reg.set_input(:en, 1)
reg.set_input(:d, 0x42)
clock!
expect(reg.get_output(:q)).to eq(0x42)
end
it "holds value when disabled" do
reg.set_input(:rst, 0)
reg.set_input(:en, 1)
reg.set_input(:d, 0x42)
clock!
reg.set_input(:en, 0)
reg.set_input(:d, 0xFF)
clock!
expect(reg.get_output(:q)).to eq(0x42)
end
it "resets to zero" do
reg.set_input(:en, 1)
reg.set_input(:d, 0x42)
clock!
reg.set_input(:rst, 1)
clock!
expect(reg.get_output(:q)).to eq(0)
end
endRun it:
rspec spec/my_register_spec.rbDebug with the TUI
Launch the interactive debugger to step through clock cycles and watch signals:
rhdl tui MyRegister --signals d,clk,rst,en,q --format hexThe TUI shows signal values and waveforms. Press Space to step one cycle, r to run continuously, and q to quit. See the RTL Simulation guide for full debugger documentation.
Export to Verilog
rhdl export --lang verilog --out ./output MyRegisterThis generates clean, synthesizable Verilog:
module my_register(
input clk,
input rst,
input en,
input [7:0] d,
output [7:0] q
);
reg [7:0] q_reg;
always @(posedge clk) begin
if (rst)
q_reg <= 8'h00;
else if (en)
q_reg <= d;
end
assign q = q_reg;
endmoduleA Hierarchical Design
Combine components using the Structure DSL:
class TopLevel < RHDL::Sim::Component
input :clk, :rst
input :data, width: 8
input :load, :count_en, :count_up
output :reg_out, width: 8
output :count_out, width: 8
instance :reg, Register, width: 8
instance :ctr, Counter, width: 8
# Clock and reset to both sub-components
port :clk => [[:reg, :clk], [:ctr, :clk]]
port :rst => [[:reg, :rst], [:ctr, :rst]]
# Register connections
port :data => [:reg, :d]
port :load => [:reg, :en]
port [:reg, :q] => :reg_out
# Counter connections
port :count_en => [:ctr, :en]
port :count_up => [:ctr, :up]
port [:ctr, :q] => :count_out
endNext Steps
- Ruby DSL Fundamentals — complete DSL reference
- Signals and Types — signal values, bit slicing, concatenation
- Component Library — explore the built-in components