{"id":22975806,"url":"https://github.com/jc-ll/ruby_rtl","last_synced_at":"2025-10-11T05:07:21.653Z","repository":{"id":41144752,"uuid":"220856319","full_name":"JC-LL/ruby_rtl","owner":"JC-LL","description":"Describing RTL circuit in Ruby","archived":false,"fork":false,"pushed_at":"2022-03-17T10:33:27.000Z","size":502,"stargazers_count":11,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-08T05:35:08.053Z","etag":null,"topics":["digital-circuits","dsl","hdl","migen","vhdl"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JC-LL.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-11-10T21:53:45.000Z","updated_at":"2025-04-25T08:12:10.000Z","dependencies_parsed_at":"2022-09-09T05:41:24.482Z","dependency_job_id":null,"html_url":"https://github.com/JC-LL/ruby_rtl","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/JC-LL/ruby_rtl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JC-LL%2Fruby_rtl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JC-LL%2Fruby_rtl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JC-LL%2Fruby_rtl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JC-LL%2Fruby_rtl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JC-LL","download_url":"https://codeload.github.com/JC-LL/ruby_rtl/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JC-LL%2Fruby_rtl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279006331,"owners_count":26084083,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-11T02:00:06.511Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["digital-circuits","dsl","hdl","migen","vhdl"],"created_at":"2024-12-15T00:39:26.412Z","updated_at":"2025-10-11T05:07:21.635Z","avatar_url":"https://github.com/JC-LL.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg src=\"./doc/logo.png\" alt=\"drawing\" width=\"200\"/\u003e\n\n# RubyRTL : Ruby-*on-gates* !\n\nWARNING : Work-in-progess !\n\nRubyRTL is an experimental Ruby DSL that aims at :\n- Describing Digital circuits in Ruby, at the RTL level\n- Generating synthesizable VHDL for FPGAs or ASICs\n\nThis approach is quite similar to [Python Migen](https://github.com/m-labs/migen). We recommand having a look at the very successful [Litex IP library](http://github.com/enjoy-digital/litex) approach, relying on Migen. Our [paper](http://www.ensta-bretagne.fr/lelann/papers/litex.pdf) talks about Litex and Migen.\n\nWe believe however that Ruby metaprogramming capabilities probably best suits the idea of creating such DSL.\n\n## How to install ?\nThe recommanded version of RubyRTL is uploaded on RubyGems, so that can simply be installed on a Linux box, by typing (use of rvm recommended):\n- gem install ruby_rtl\n\n## How does it look ?\n\nLet's build a introductory-level digital system : a ripple-carry adder. Using RubyRTL, we can elaborate much more complex circuits than this simple adders. At the register-transfer level, much more complex functions operating on complex data structures, are possible : imagine a video macroblocks on which several filters are applied, in a single clock cycle, or a processor pipline etc.\n\nFor the moment, let's build this adder, in a progessive manner !\n\n### Basic signal assignments : example of a 1-bit half adder circuit\n\nWe start by the \"Hello World\" of Digital Design : the Half adder. We recall that it built from  2 basic gates. That \"block\" can be then used in a hierarchical manner to build a 1-bit full-adder and then a classical adder operating on integers. See [wikipedia](https://en.wikipedia.org/wiki/Adder_(electronics))  if needed. This bottom-up approach is representative of Digital System Design : we can elaborate complex functions with a clever composition of such components, either hierarchically or using the intrinsic parallelism of digital circuit, or both.\n\n~~~ruby\n  require_relative 'ruby_rtl'\n\n  include RubyRTL\n\n  class HalfAdder \u003c Circuit\n    def initialize\n      input  :a,:b\n      output :sum\n      output :cout\n\n      assign(sum  \u003c= a ^ b) #xor\n      assign(cout \u003c= a \u0026 b) #cout\n    end\n  end\n\n  ha=HalfAdder.new\n\n  compiler=Compiler.new\n  compiler.compile ha # VHDL generated !\n~~~\n\nThe generated code is then :\n\n~~~vhdl\n-- automatically generated by RubyRTL\nlibrary ieee;\nuse ieee.std_logic_1164.all;\nuse ieee.numeric_std.all;\n\nlibrary ruby_rtl;\nuse ruby_rtl.ruby_rtl_package.all;\n\nlibrary halfadder_lib;\nuse halfadder_lib.halfadder_package.all;\n\nentity halfadder_c is\n  port (\n    a : in  std_logic;\n    b : in  std_logic;\n    sum : out std_logic;\n    cout : out std_logic);\nend halfadder_c;\n\narchitecture rtl of halfadder_c is\nbegin\n\n  sum \u003c= (a xor b);\n  cout \u003c= (a and b);\n\nend rtl;\n~~~\n## Hierarchical composition : example of 1-bit full adder\n\nHere, we *reuse* 1-but half adder, to elaborate a 1-bit full adder. It now has an input carry and an output carry. The example show how to call such hierarchal components and glue them together.\n\n~~~ruby\nrequire 'ruby_rtl'\n\ninclude RubyRTL # module now visible\n\nrequire_relative 'half_adder' # preceding circuit\n\nclass FullAdder \u003c Circuit\n  def initialize\n    input  :a,:b,:cin\n    output :sum,:cout\n\n    component :ha1 =\u003e HalfAdder     # class...\n    component :ha2 =\u003e HalfAdder.new # or ...obj\n\n    assign(ha1.a \u003c= a )\n    assign(ha1.b \u003c= b )\n    assign(ha2.a \u003c= cin)\n    assign(ha2.b \u003c= ha1.sum)\n    assign(sum   \u003c= ha1.sum)\n    assign(cout  \u003c= ha1.cout | ha1.cout)\n\n  end\nend\n~~~\n\n## Genericity : example of a word-level adder\nHere comes the most exiting parts of RubyRTL. We can rely on Ruby host itself, to describe the glue between components. This glue may rely on many *parameters* (or \"generics\" in the VHDL world). Ruby host also allows you to make regular computations required for the configuration of your design, which can be cumbersome in classical HDLs.\n\nLet's build a *n-bits* adder using previous components !\n\u003cimg src=\"./doc/adder.png\" alt=\"generic adder\" width=\"300\"/\u003e\n\n~~~ruby\nrequire_relative 'ruby_rtl'\nrequire_relative 'full_adder' #preceding circuit\n\ninclude RubyRTL\n\nclass Adder \u003c Circuit\n\n  def initialize nbits\n    input  :a    =\u003e nbits\n    input  :b    =\u003e nbits\n    output :sum  =\u003e nbits\n    output :cout\n\n    # create  components\n    adders=[]\n    for i in 0..nbits-1\n      adders \u003c\u003c component(\"fa_#{i}\" =\u003e FullAdder)\n    end\n\n    # connect everything\n    for i in 0..nbits-1\n      assign(adders[i].a \u003c= a[i])\n      assign(adders[i].b \u003c= b[i])\n      if i==0\n        #assign(adders[0].cin \u003c= Bit(0)) # no carry in for FA_0\n        assign(adders[0].cin \u003c= 0)       # even better.\n      else\n        assign(adders[i].cin \u003c= adders[i-1].cout)\n      end\n      # final sum\n      assign(sum[i]        \u003c= adders[i].sum)\n    end\n  end\nend\n~~~\n\n## Behavioral statements : counter\n\nAll previous examples were *structural*. Hardware descriptions languages such as VHDL and Verilog also allows for so called *behavioral* descriptions (please note that this naming is historical, and still found in course books. This is not to be cofounded with modern \"High-level synthesis\" also called *behavioral* synthesis, that is at a higher abstraction layer than RTL). Here our DSL allows more basically to resort to statements like :\n- **If..Elsif...Else**\n- **Case...When ...**\n\nRubyRTL introduces these DSL keywords, that **require upcase** (in order to avoid collision with regular Ruby host keywords).\n\nAs for VHDL or Verilog, such RubyRTL statements are also synthesizable on hardware, if used correctly.\n\nThe **important remark** is about *clocks* and *resets*. RubyRTL recognizes (via **sequential** keyword) that your design requires D (edge-triggered) flip-flops : their clocking is considered *implicit*. By default, RubyRTL works on a *single clock* and generates synchronous and asynchronous reset. This may be modified in future versions.\n\n~~~ruby\nclass Counter \u003c Circuit\n  def initialize\n    input  :do_count\n    output :count =\u003e :byte\n\n    sequential(:strange_counting){\n      If(do_count==1){\n        If(count==255){\n          assign(count \u003c= 0)\n        }\n        Elsif(count==42){\n          assign(count \u003c= count + 42)\n        }\n        Else{\n          assign(count \u003c= count + 1)\n        }\n      }\n    }\n  end\nend\n~~~\n\n## Finite state machines (FSM)\n\nFinite state machines are essential in Digital System Design. However, VHDL and Verilog do not provide instrinsic keywords for them. Here, RubyRTL simplified the coding by providing such keywords.\n\n~~~ruby\nclass FSM1 \u003c Circuit\n  def initialize\n    input :go,:b\n    output :f =\u003e :bv2\n\n    fsm(:simple){\n\n      assign(f \u003c= 0)\n\n      state(:s0){\n        assign(f \u003c= 1)\n        If(go==1){\n          next_state :s1\n        }\n      }\n\n      state(:s1){\n        assign(f \u003c= 2)\n        next_state :s2\n      }\n\n      state(:s2){\n        assign(f \u003c= 3)\n        next_state :s0\n      }\n    }\n  end\nend\n~~~\n\n## How does this DSL works ?\nRubyRTL is an *internal DSL*. We can see it as a new language, embedded in Ruby syntax. It benefits from Ruby directly. However, such embedding needs a cautious resort to metaprogramming and introspection.\n\nMore to come here. Stay tuned !\n\n## Please cite RubyRTL !\n\n* RubyRTL was published at OSDA DATE'21 satellite workshop.\n* The pdf paper is [here](https://www.jcll.fr/papers/osda_2020.pdf).\n* A poster is also provided [here](https://www.jcll.fr/papers/osda_2020_poster.pdf).\n* The bibtex entry is [here](https://www.jcll.fr/papers/bib_rubyrtl_20.bib) --\u003e\n\n## Contact\nDon't hesitate to drop me a mail if you like RubyRTL, or found a bug etc.\nI will try to do my best to consolidate, maintain and enhance RubyRTL.\n\njean-christophe.le_lann at ensta-bretagne.fr\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjc-ll%2Fruby_rtl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjc-ll%2Fruby_rtl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjc-ll%2Fruby_rtl/lists"}