Hi Suraj,

Sorry to have dropped off the radar for so long.. Anyway, I notice you have 
done two major releases since we last communicated!
I have just installed 19.0.0, and been playing around with it.

I am starting to refine my system level testing needs, using a RISC system as 
the target.

So here's what I want to do:

BACKGROUND:
-----------

I ignore the RISC itself to start with - this is usually obtained as a 
simulator model or somesuch later on.
The majority of the work involves describing the system around the RISC, and 
the interactions with it.
So to start with, I will be building a system using a bus functional model, 
that just initiates bus transactions, And checks the required results. This is 
not sufficient for all system testing, but it is usually the first step When 
developing bus-based peripherals, or memory systems.

Furthermore, I will develop two bus models: a low speed peripheral bus, and a 
high-speed bus that connects the RISC to The memories and to the bridge that 
translates between the high speed bus and the peripheral bus.

For a concrete example, I will use an ARM AHB bus as the high speed bus 
connected to the ARM core and memories, and an APB bridge connecting the AHB 
bus to a set of peripherals on an APB bus.

This is a very typical situation - in the Open Source world, there are also the 
Wishbone buses, which are similar (and probably better; AHB has some glaring 
faults)

Typically, a verilog hierarchy would look something like this:

AHBSys------|
                (AHB bus)
                |
                |---- ARM7TDMI
                |
                |---- Memory
                |
                |---- AHB2APB
                                |
                                |---- Peripheral1
                                |
                                |---- Peripheral2

Etc.

Also, in a traditional HDL verification setup, outside of all this would be 
a 'testbench' - this contains models of the environment With which the design 
has to interact. This may contain very little, if the device is standalone, or 
it may contain many models:
        memory
        external serial port model
        USB slave or host
        JTAG host
        
        etc.. I have had to develop all these at one time or another

More on the external harness later.

BUS MODELLING 1
---------------

First of all, let's develop some of the peripheral blocks. This corresponds 
more to the way you have looked at Ruby-VPI so far.

We can develop a simple register based peripheral, that is attached to the APB 
(peripheral) bus. This will have an interface that Connects to the bus, and a 
few simple internals.

Now we can use Ruby-VPI pretty much as described in the tutorial; we can 
develop individual tests that manipulate the bus signals In the correct order, 
read back values, check to see whether they are correct, etc.

But, notice that this will become tedious, if we have a lot of tests; we are 
performing the same actions over and over again.
What we want is a set of routines that emulate the bus transactions, so that we 
can write tests like

describe "A peripheral register" do
        setup do        
                Peripheral.reset!
        end

        it "should be reset to zero" do
                # use a bus read function to obtain the value of reg
                Peripheral.read(ADDR).should == 0
        end

        it "should be writeable" do
                # write a value, then read it back
                # use a bus write and bus read function
                # not exhaustive..
                Peripheral.write(ADDR, TEST_VAL)
                Peripheral.read(ADDR).should == TEST_VAL
        end
end

When implementing in Ruby-VPI, there are some questions we need to ask:

1.      Where should we implement the bus functions?
        At the moment, I have implemented them (successfully) in the _design.rb 
file, alongside the cycle!, reset! Functions

        But there are possible issues with this, as we will see later.

2.      We need to make sure that we can re-use the tests later on. This means 
that the address used to access the peripheral
        and the scope will need to change.

        So the question is, how should we refer to the DUT? Here we have 
referenced it explicitly (as Peripheral), but this will
        not be sufficient later on.

BUS MODELLING 2
---------------

The next step is to place several peripherals on a peripheral bus. We will want 
to develop peripherals in separate verilog files, plus a System level file that 
connects them together.

The first query is just one about the mechanics of Ruby-VPI. How do we include 
multiple files in the build?

One way is to add the files into the filelist generated in the _runner.rake 
file; this works. However, I don't find it satisfactory.
An easier way normally is to use the -y switch, together with diectory names. 
Only the top level file needs to be given, and the rest are Found by inspection 
on the directory paths. The problem is that it works for CVER on the command 
line, but I can't seem to get the arguments Passed in correctly by changing 
things in the _runner.rake file. That is, it seems to generate a correct 
command line, but it doesn't work.
However, that's a minor (if frustrating) detail.

Secondly, we now have several blocks to test, and also, the top level that we 
are testing has changed. Hence, how do we refer to the different Peripherals?

One way is to recognise that we are not performing tests on the peripherals, 
but on the bus - which is our top level. So the tests should Include statements 
like:

        PeriphBus.read(ADDR).should == TEST_VAL

But this means we have to edit all our tests now, which is annoying. And later 
on, we may have to do it again. One way might be to always call Our top level 
System; a further refinement would be to have the very top level just a wrapper 
around the actual top level, so that we never Need to change the name of the 
module in the verilog code. The wrapper will always be called System.

BUS MODELLING 3
---------------

Now we want to instantiate our peripheral bus system within the main bus.

To start with, this is just an extension of our previous technique. We build 
the verilog of the complete system, and wrap it with a new System wrapper.

We now have to build bus functions that perform main bus functions, and put 
them in the _design.rb file.
This should work, because our tests just use 
        System.read(ADDR).should == TEST_VAL

The translation between the main bus and the peripheral bus transactions is now 
performed by the HDL itself - as it will be in real life.

However, we have had to perform major edits to our System_design.rb file, which 
is not good. What is the best way of abstracting this out?
Obviously, we should encapsulate the bus modelling functions somewhere, in say, 
an AHBBus and APBBus class or module?. However, what would be the best way of 
getting these functions in scope? Use an 'include' in the System_design.rb 
file. I'm a bit hazy about the namespace issues here.

DISTRIBUTED DESIGN
------------------

But actually, there will probably be several teams developing modules. Ideally 
we would want to be able to develop the verilog, and more importantly, the 
tests, and then import these into the top level.

So I'm not sure how to do set this up. The top level System would need to 
import the verilog (which could be done by using library directives), but it 
would be good to be able to, say, get Ruby-VPI to recursively pull all the 
required modules from a directory.

It is also worth pointing out that one tends to have both project specific 
libraries, and shared libraries. For example there might be a Library of AHB 
modules, APB modules, and then some project-specific peripherals.

So a project layout might be something like:

        |
        |----AHBLib
        |       |
        |       |----verilog
        |       |
        |       |----System
        |       |       |
        |       |       |---- System_design.rb
        |       |       |       AHBmodule1.v
        |       |       |       AHBmodule1_spec.rb
        |       |       |       AHBmodule2.v
        |       |       |       AHBmodule2_spec.rb      //
        |       |       |       AHB_helper.rb           //bus models
        |       |       |       System_spec.rb          //refers to specs
        |
        |----APBLib
        |       |
        |       |----verilog
        |       |
        |       |----System
        |       |       |
        |       |       |---- System_design.rb
        |       |       |       APBmodule1.v
        |       |       |       APBmodule1_spec.rb
        |       |       |       APBmodule2.v
        |       |       |       APBmodule2_spec.rb      //
        |       |       |       APB_helper.rb           //bus models
        |       |       |       System_spec.rb          //refers to specs       
        |
        |----MyProject
        |       |
        |       |----verilog
        |       |
        |       |----System
        |       |       |
        |       |       |---- System_design.rb
        |       |       |       MyAPBmodule1.v
        |       |       |       MyAPBmodule1_spec.rb    //refers to APBLib
        |       |       |       MyAHBmodule2.v
        |       |       |       MyAHBmodule2_spec.rb    //refers to AHBLib
        |       |       |       MyProject_helper.rb     //test harness models
        |       |       |       System_spec.rb          //refers to specs
        here, in AHBLib, APBLib 

Note that we will also want to integrate revision control into the main project 
rake files.

As pointed out before, the tests either specific to the project, OR referred to 
from other libs, Will use the bus model referenced in the project, NOT the one 
used locally in their library test System.

TESTING
-------

As pointed out above, it should be possible to get hierarchical builds for the 
verilog (after sorting out those pesky rake problems).
However, I am less sure about the Rspec specifications.

Ideally, I would want to have the master System_spec.rb file look something 
like:

        describe "APBSubstem" do
                
                it_should_behave_like "APBModule1" (ADDR1)
                it_should_behave_like "APBModule2" (AADR2)
                it_should_behave_like "MyAPBModule1" (AADR3)

                ..etc

Where we refer to 'shared' Rspec behaviour

Now there are two issues here: 
        1. how to set up the namespaces correctly (doable, except I don't know 
enough about it yet..)
        2. we need to parametrise the specs with the actual address / size / 
etc. of the module
        
           This is particularly true of library modules, since they will in 
general, be parametrisable.

Any ideas about point (2)?  At the moment it looks impossible, but its 
difficult to get any hard info About Rspec, the documentation is terrible. I'll 
have a poke round.

Note also, that we would like to have a standard means of assigning addresses, 
etc. In practice this means a Header file with verilog definitions (enter your 
verilog to ruby translator). There is one small verilog point Here - it is 
probably better to use 'parameters' to set addresses, sizes, etc., rather than 
rely on macro Substitutions (`defines) as these can be set from anywhere within 
the hierarchy. However, one can use macros In the parameter instantiations, if 
necessary.

BUS MODELS vs CODE
------------------

All the above use the bus functions to perform testing. This works well will 
the Rspec framework.
Eventually, though, we would like to run code.

It is possible to modify the bus functions, so that, as well as performing the 
transaction, they emit Code (possibly assembler code) to execute the same 
action. So 
        write (ADDR,DATA)

        gets turned into (pseudo-assembler):

        load    r4, #DATA       // load immediate       
        store @#ADDR, r4        // store absolute

One point, though, is that the read needs to have an expected value; at the 
moment the bus function read returns The value so that it can be checked by the 
Rspec framework. The assember code will need to know the expected value, So 
would probably need something like

        expect(ADDR, DATA)      // returns value as well, so can use expect
(ADDR,DATA).should == DATA
        
        becomes:

        load    r4,#DATA
        load  r5,@#ADDR
        cmp     r4,r5
        bnz     FAIL_EXPECT

This approach does mean that the code version can't be checked using the Rspec 
framework; it gets emitted, assembled Then loaded into a memory model for 
execution by a harness that uses the real core (or a C model thereof).
Again, I can't work out whether Rspec could be made to do anything more clever.

STATUS
------

I have got as far as using simple bus models, but these are still at the early 
stage, i.e. just shoved into the Top_level_design.rb file.

I haven't tackled the hierarchy problems yet.

If you are interested, I can send you the files (such as they are!)




Best Regards (and keep up the good work!)

Rob MacAulay




Reply via email to