HDLのRuby実装であるRHDLをさわってみた

FPGAの話を聞いてHDLに興味をもったので、いろいろ調べてレポート書くついでにゴニョゴニョしました。RHDLというのがあったので、本に載ってたサンプルコード(VHDL)をもとにテストを書いて、コードの内容をRubyで書き換えてみました。コードの内容は、16ビットデータのパリティエンコードです。参考にしたのは以下の本です。

まあ、いろいろコケまくりました。レジスタに変更を加えるときはVHDLの表現と変わらないんだけど、レジスタに入ってるビット列のレベルで変更するときはVHDLとは違ってBit#assignで書かないと通りません。それから、本当の意味での同時代入がうまく実現されないみたいです。なので、同時代入的に書けるはずの部分でも、Bit#assignとは別の余計な代入を書いている部分があります。
あと、テストの内容説明は手抜きです。走るのがわかれば良かった。今は反省している。でも面倒なので気にしない。気が向いたら直します。

require 'hardware/RHDL'

include RHDL
CRC_Enc = model {
  # 入出力端子の宣言
  inputs data_in, start_in, valid_in, reset_n, clk
  outputs data_out, start_out, valid_out
  init {
    # レジスタやノードの宣言
    parity_reg = Signal(BitVector(0, 16))
    datacnt_reg = Signal(0)
    STATE_TYPE = EnumType(:waitdata, :calcparity, :outparity)
    state_reg = Signal(STATE_TYPE)
    # レジスタ、ノード、出力端子への代入
    define_behavior {
      # 1クロックごとの処理
      process(clk) {
        behavior {
          if clk.event && clk == 1
            if reset_n == 0
              state_reg.assign(:waitdata)
            else
              case state_reg.inspect
              when :waitdata
                if start_in == 1 && valid_in == 1
                  parity_reg[15].assign(0)
                  parity_reg[14].assign(0)
                  parity_reg[13].assign(0)
                  parity_reg[12].assign(data_in)
                  parity_reg[11].assign(0)
                  parity_reg[10].assign(0)
                  parity_reg[9].assign(0)
                  parity_reg[8].assign(0)
                  parity_reg[7].assign(0)
                  parity_reg[6].assign(0)
                  parity_reg[5].assign(data_in)
                  parity_reg[4].assign(0)
                  parity_reg[3].assign(0)
                  parity_reg[2].assign(0)
                  parity_reg[1].assign(0)
                  parity_reg[0].assign(data_in)
                  datacnt_reg <= 0
                  state_reg.assign(:calcparity)
                end
              when :calcparity
                if valid_in == 1
                  comp = parity_reg[15] ^ data_in
                  parity_reg[15].assign(parity_reg[14])
                  parity_reg[14].assign(parity_reg[13])
                  parity_reg[13].assign(parity_reg[12])
                  parity_reg[12].assign(parity_reg[11] ^ comp)
                  parity_reg[11].assign(parity_reg[10])
                  parity_reg[10].assign(parity_reg[9])
                  parity_reg[9].assign(parity_reg[8])
                  parity_reg[8].assign(parity_reg[7])
                  parity_reg[7].assign(parity_reg[6])
                  parity_reg[6].assign(parity_reg[5])
                  parity_reg[5].assign(parity_reg[4] ^ comp)
                  parity_reg[4].assign(parity_reg[3])
                  parity_reg[3].assign(parity_reg[2])
                  parity_reg[2].assign(parity_reg[1])
                  parity_reg[1].assign(parity_reg[0])
                  parity_reg[0].assign(comp)
                end

                if datacnt_reg == 15
                  datacnt_reg <= 0
                  state_reg.assign(:outparity)
                else
                  datacnt_reg <= datacnt_reg + 1
                end
              when :outparity
                if datacnt_reg == 15
                  state_reg.assign(:waitdata)
                else
                  parity_reg[15].assign(parity_reg[14])
                  parity_reg[14].assign(parity_reg[13])
                  parity_reg[13].assign(parity_reg[12])
                  parity_reg[12].assign(parity_reg[11])
                  parity_reg[11].assign(parity_reg[10])
                  parity_reg[10].assign(parity_reg[9])
                  parity_reg[9].assign(parity_reg[8])
                  parity_reg[8].assign(parity_reg[7])
                  parity_reg[7].assign(parity_reg[6])
                  parity_reg[6].assign(parity_reg[5])
                  parity_reg[5].assign(parity_reg[4])
                  parity_reg[4].assign(parity_reg[3])
                  parity_reg[3].assign(parity_reg[2])
                  parity_reg[2].assign(parity_reg[1])
                  parity_reg[1].assign(parity_reg[0])
                  datacnt_reg <= datacnt_reg + 1
                end
              else
                state_reg.assign(:waitdata)
              end
            end
          end
        }
      }

      # レジスタ、入力端子が変化したときに行われる処理
      process(state_reg, parity_reg, data_in, start_in, valid_in) {
        behavior {
          data_out  <= (state_reg == :outparity ? parity_reg[15] : data_in)
          start_out <= (start_in * valid_in)
          valid_out <= (((state_reg == :waitdata && start_in == 1 && valid_in == 1) || (state_reg == :calcparity && valid_in == 1) || state_reg == :outparity) ? 1 : 0)
        }
      }
    }
  }
}

require 'rubygems'
require 'spec'
require 'Simulator'

describe "simulate crcenc" do
  include RHDL
  include Simulator
  before(:all) do
    @data_in   = Signal(Bit(0))
    @start_in  = Signal(Bit(0))
    @valid_in  = Signal(Bit(0))
    @reset_n   = Signal(Bit(0))
    @clk       = Signal(Bit(0))
    @data_out  = Signal(Bit())
    @start_out = Signal(Bit())
    @valid_out = Signal(Bit())
    @crcenc = CRC_Enc.new(:data_in => @data_in, :start_in => @start_in, :valid_in => @valid_in, :reset_n => @reset_n, :clk => @clk, :data_out => @data_out, :start_out => @start_out, :valid_out => @valid_out)
#    step { puts "@reset_n = #{@reset_n} : @start_in = #{@start_in} : @valid_in = #{@valid_in} : @data_in = #{@data_in}"; @clk <= @clk.inv }
    step { @clk <= @clk.inv }
    step
    puts ''
  end

  it "initial clock check" do
    @clk.should == 0
    @reset_n <= 1
  end

  it "00(reset_n should be 1)" do
    @reset_n.should == 1
    @valid_in <= 1
    @start_in <= 1
    @data_in  <= 1
  end

  it "01" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '111'
    @start_in <= 0
  end

  it "02" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
    @data_in <= 0
  end

  it "03" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '100'
    @data_in <= 1
  end

  it "04" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
  end

  it "05" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
    @data_in <= 0
  end

  it "06" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '100'
    @data_in <= 1
  end

  it "07" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
  end

  it "08" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
  end

  it "09" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
    @data_in <= 0
  end

  it "10" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '100'
    @data_in <= 1
  end

  it "11" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
  end

  it "12" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
    @data_in <= 0
  end

  it "13" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '100'
  end

  it "14" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '100'
    @data_in <= 1
  end

  it "15" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
  end

  it "16" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
    @valid_in <= 0
    @data_in  <= 0
  end

  it "17" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '100'
  end

  it "18" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
  end

  it "19" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '100'
  end

  it "20" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
  end

  it "21" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
  end

  it "22" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
  end

  it "23" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
  end

  it "24" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '100'
  end

  it "25" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '100'
  end

  it "26" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
  end

  it "27" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
  end

  it "28" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '100'
  end

  it "29" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '100'
  end

  it "30" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
  end

  it "31" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '100'
  end

  it "32" do
    "#{@valid_out}#{@start_out}#{@data_out}".should == '101'
  end

  after do
    step
    step
    puts ''
  end
end