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