Ruby-vpi를 테스트 하는 과정을 정리하였다.
여기서 사용하는 예제는 아래 링크에서 참조 하였다.
http://snk.tuxfamily.org/lib/ruby-vpi/#usage.tutorial.declare-design
디자인은 counter.v 이다.
일단 Top module부터 만든다.
module counter #(parameter Size = 5) (
input clock,
input reset,
output reg [Size-1 : 0] count
);
endmodule
일단 폴더를 만들고
mkdir xUnit
디자인을 옮겨 둔다.
cp counter.v ./xUnit
그리고 해당 폴더로 가서 xUnit용 파일을 만든다.
GUNDAM-NT:xUnit kevinim$ ruby-vpi generate counter.v --xUnit
module counter
create counter_runner.rake
create counter_design.rb
create counter_proto.rb
create counter_spec.rb
create counter_loader.rb
create Rakefile
위와 같이 커맨드를 넣으면 그에 맞게 파일이 만들어진다.
각각 앞에서 설명하였던 *.rake *_design.rb _proto.rb *_spec.rb *_loader.rb Rakefile 등이다.
(2) Spec 생성
카운터라는 것의 특성상 기본적인 내용은 아래와 같이 정의할 수 있겠다.
- 초기 값은 0이어야 한다.
- 상승 클럭에서 카운트 값이 1씩 증가한다.
- 최대 값에 도달하면 overflow가 발생하지만 하여튼 0부터 시작한다.
이것을 스펙으로 정의하면 아래와 같다.
require 'test/unit'
# lowest upper bound of counter's value
LIMIT = 2 ** DUT.Size.intVal
# maximum allowed value for a counter
MAX = LIMIT - 1
class A_counter_when_reset < Test::Unit::TestCase
def setup
DUT.reset! # reset the counter
end
def test_should_be_zero
assert_equal( 0, DUT.count.intVal )
end
def test_should_increment_upon_each_subsequent_posedge
LIMIT.times do |i|
assert_equal( i, DUT.count.intVal )
DUT.cycle! # increment the counter
end
end
end
class A_counter_with_the_maximum_value < Test::Unit::TestCase
def setup
DUT.reset! # reset the counter
# increment the counter to maximum value
MAX.times { DUT.cycle! }
assert_equal( MAX, DUT.count.intVal )
end
def test_should_overflow_upon_increment
DUT.cycle! # increment the counter
assert_equal( 0, DUT.count.intVal )
end
end
크게 2개의 클래스를 만들었는데
하나가 A_counter_after_being_reset 이고
나머지 하나가 A_conter_with_the_maximum_value 이다.
각각 다음과 같은 동작을 수행한다.
A_counter_after_being_reset
- setup :: Reset을 건다.
- test_should_be_zero :: 0인지 확인한다.
- test_should_increment_upon_each_subsequent_posedge :: 계속 증가하면서 하나씩 커지는 것을 확인한다.
A_counter_with_the_maximum_value
- setup :: 리셋을 건다.
- test_should_overflow_upon_increment :: overflow가 되면 0이 된다.
이다.
(3) Prototype을 만든다.
counter_proto.rb 파일에서 만든다.
if RubyVPI::USE_PROTOTYPE
always do
wait until DUT.clock.posedge?
if DUT.reset.t ?
DUT.count.intVal = 0
else
DUT.count.intVal += 1
end
end
end
카운터의 값의 프로토 타입을 의미한다.
(4) Prototype을 검증하기
검증은 다음과 같이 한다.
GUNDAM-NT:xUnit kevinim$ rake ivl PROTOTYPE=1
(in /Users/kevinim/Documents/Verilog_toos/ruby-vpi-test/xUnit)
rake aborted!
no such file to load -- ruby-vpi/runner_proxy
옵션으로 ivl은 icarus verilog simulator를 의미한다.
위와 같은 오류가 발생한다.
ruby-vpi/runner_proxy
를 찾을 수 없다는 오류이다.
이 파일은 라이브러리에 있으므로 환경 변수를 설정한다.
export RUBYLIB=/Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/
이후에 다시 실행한다.
GUNDAM-NT:xUnit kevinim$ rake ivl PROTOTYPE=1
(in /Users/kevinim/Documents/Verilog_toos/ruby-vpi-test/xUnit)
rake -f counter_runner.rake ivl PROTOTYPE=1
(in /Users/kevinim/Documents/Verilog_toos/ruby-vpi-test/xUnit)
cp /Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/obj/ivl.so ruby-vpi.vpi
["iverilog", "-mruby-vpi", "counter.v", {:verbose=>:default, :noop=>false}]
iverilog -mruby-vpi counter.v
["vvp -M. a.out", {:verbose=>:default, :noop=>false}]
vvp -M. a.out
ruby-vpi: prototype is enabled
SyntaxError: compile error
counter_proto.rb:6: syntax error, unexpected '\n'
counter_proto.rb:12: syntax error, unexpected kEND, expecting $end
from counter_proto.rb:12:in `load_test'
from /Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi.rb:68:in `each'
from /Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi.rb:68:in `load_test'
from ./counter_loader.rb:1
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `require'
from /Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi/boot/loader.rb:146
from /Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi/core/scheduler.rb:121:in `call'
from /Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi/core/scheduler.rb:121:in `initialize'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/generator.rb:83:in `call'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/generator.rb:83:in `initialize'
from /Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi/core/scheduler.rb:119:in `new'
from /Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi/core/scheduler.rb:119:in `initialize'
from /Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi/core/scheduler.rb:53:in `new'
from /Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi/core/scheduler.rb:53:in `run'
from /Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi/boot/loader.rb:144
몇개의 오류가 보인다.
이것을 먼저 수정하고 진행한다.
GUNDAM-NT:xUnit kevinim$ vi counter_proto.rb
GUNDAM-NT:xUnit kevinim$ rake ivl PROTOTYPE=1
(in /Users/kevinim/Documents/Verilog_toos/ruby-vpi-test/xUnit)
rake -f counter_runner.rake ivl PROTOTYPE=1
(in /Users/kevinim/Documents/Verilog_toos/ruby-vpi-test/xUnit)
cp /Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/obj/ivl.so ruby-vpi.vpi
["iverilog", "-mruby-vpi", "counter.v", {:verbose=>:default, :noop=>false}]
iverilog -mruby-vpi counter.v
["vvp -M. a.out", {:verbose=>:default, :noop=>false}]
vvp -M. a.out
ruby-vpi: prototype is enabled
Loaded suite ruby-vpi
Started
EEE
Finished in 0.00128 seconds.
1) Error:
test_should_be_zero(#<Module:0x10252d340>::A_counter_when_reset):
ArgumentError: "VpiHigh" is not a valid VPI property
/Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi/core/handle.rb:364:in `initialize'
/Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi/core/handle.rb:288:in `new'
/Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi/core/handle.rb:288
....
2) Error:
test_should_increment_upon_each_subsequent_posedge(#<Module:0x10252d340>::A_counter_when_reset):
ArgumentError: "VpiHigh" is not a valid VPI property
/Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi/core/handle.rb:364:in `initialize'
/Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi/core/handle.rb:288:in `new'
....
3) Error:
test_should_overflow_upon_increment(#<Module:0x10252d340>::A_counter_with_the_maximum_value):
ArgumentError: "VpiHigh" is not a valid VPI property
/Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi/core/handle.rb:364:in `initialize'
/Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi/core/handle.rb:288:in `new'
/Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/lib/ruby-vpi/core/handle.rb:288
...
3 tests, 0 assertions, 0 failures, 3 errors
위의 에러는 뜻밖인데,
Ruby-VPI에서 버그가 있는것이다.
Test Code용으로 만들어낸 디자인에서 code가 틀리게 되어 있다.
counter_design.rb 를 아래와 같이 수정한다.
# Simulates the design under test for one clock cycle.
def DUT.cycle!
clock.t!
advance_time
clock.f!
advance_time
end
# Brings the design under test into a blank state.
def DUT.reset!
reset.t!
cycle!
reset.f!
end
그리고 시뮬레이션을 수행하면 다음과 같은 오류가 발생한다.
GUNDAM-NT:xUnit kevinim$ rake ivl PROTOTYPE=1
(in /Users/kevinim/Documents/Verilog_toos/ruby-vpi-test/xUnit)
rake -f counter_runner.rake ivl PROTOTYPE=1
(in /Users/kevinim/Documents/Verilog_toos/ruby-vpi-test/xUnit)
cp /Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/obj/ivl.so ruby-vpi.vpi
["iverilog", "-mruby-vpi", "counter.v", {:verbose=>:default, :noop=>false}]
iverilog -mruby-vpi counter.v
["vvp -M. a.out", {:verbose=>:default, :noop=>false}]
vvp -M. a.out
ruby-vpi: prototype is enabled
MAX is 31
SIZE is 5
Limit is 32
Loaded suite ruby-vpi
Started
.FF
Finished in 0.059358 seconds.
1) Failure:
test_should_increment_upon_each_subsequent_posedge(#<Module:0x10232d3d8>::A_counter_when_reset)
[counter_spec.rb:27:in `test_should_increment_upon_each_subsequent_posedge'
counter_spec.rb:26:in `times'
counter_spec.rb:26:in `test_should_increment_upon_each_subsequent_posedge']:
<1> expected but was
<0>.
2) Failure:
test_should_overflow_upon_increment(#<Module:0x10232d3d8>::A_counter_with_the_maximum_value) [counter_spec.rb:39]:
<31> expected but was
<0>.
3 tests, 4 assertions, 2 failures, 0 errors
이번의 오류는 RUBY와 icaurs가 연결이 되지 않는다는 의미이다.
그래서 이번에는 CVER로 바꾸어서 컴파일을 해본다.
GPLCver에 대한 설치는 이 페이지를 참조하고 여기서는 그냥 진행한다
GUNDAM-NT:xUnit kevinim$ rake cver PROTOTYPE=1
(in /Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/examples/counter/xUnit)
rake -f counter_runner.rake cver PROTOTYPE=1
(in /Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/examples/counter/xUnit)
["cver", "+loadvpi=/Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/obj/cver.so:vlog_startup_routines_bootstrap", "+incdir+..", "../counter.v", {:verbose=>:default, :noop=>false}]
cver +loadvpi=/Users/kevinim/Documents/Verilog_toos/Ruby-VPI/ruby-vpi-21.1.0/obj/cver.so:vlog_startup_routines_bootstrap +incdir+.. ../counter.v
GPLCVER_2.12a of 05/16/07 (Mac OSX).
Copyright (c) 1991-2007 Pragmatic C Software Corp.
All Rights reserved. Licensed under the GNU General Public License (GPL).
See the 'COPYING' file for details. NO WARRANTY provided.
Today is Wed Nov 2 19:37:38 2011.
Compiling source file "../counter.v"
Highest level modules:
counter
ruby-vpi: prototype is enabled
Loaded suite ruby-vpi
Started
...
Finished in 0.103285 seconds.
3 tests, 35 assertions, 0 failures, 0 errors
0 simulation events and 0 declarative immediate assigns processed.
1 behavioral statements executed (1 procedural suspends).
Times (in sec.): Translate 0.0, load/optimize 0.1, simulation 0.3.
There were 3 error(s), 136 warning(s), and 4 inform(s).
End of GPLCVER_2.12a at Wed Nov 2 19:37:38 2011 (elapsed 0.3 seconds).
결과는 이상없이 잘 진행된다.
아마도 icarus와 ruby-vpi가 잘 안맞는 문제인듯..
Osx에서만 안맞는것인지 linux에서도 그런지는 확인을 하지 못하였다.
이 방법은 불안정 해서 더이상 진행하지 않습니다.
나중에 Linux + ModelSim이나 VCS로 테스트 해보고 되면 진행 예정입니다.