Skip to content

Cpp Model Outputs

Scott Nellenbach edited this page Oct 11, 2017 · 12 revisions

C++ models

Ordt supports generation of two different C++ model types. The first (using the -cppmod command line option) is a representation of the device register storage that can be accessed externally via an read/write address-data api and internally by direct access of field variables. The second (using the -cppdrvmod command line option) models the external view of the register storage as seen from a driver and provides an api to convert a register path string into an address.

Note: this page reflects code in version 170405.01



C++ Device Model

Specifying the -cppmod option on the command line will generate a C++ register model representing the device and write hpp and cpp files to the specified directory. Register information is accessible via a processor-like read/write api specifying an address and data as appropriate, as well as by directly accessing register field variables. A register-level mutex is supported to allow management of concurrent access from the write api (automatic lock/unlock on write) and field modifications (manual register lock required to modify).

An example Makefile for building a basic test (main.cpp) using clang is shown below...

 objects = main.o ordt_pio.o ordt_pio_common.o
 
 ccomp = clang++ -c -Wall -std=c++11 -stdlib=libc++
 
 runtest : $(objects) 
 	clang++ -stdlib=libc++ -o runtest $(objects) 
 
 main.o : main.cpp ordt_pio.hpp ordt_pio_common.hpp
 	$(ccomp) main.cpp
 
 ordt_pio.o : ordt_pio.cpp ordt_pio.hpp ordt_pio_common.hpp
 	$(ccomp) -D ORDT_PIO_VERBOSE ordt_pio.cpp
 
 ordt_pio_common.o : ordt_pio_common.cpp ordt_pio_common.hpp
 	$(ccomp) ordt_pio_common.cpp
 
 clean :
 	rm runtest $(objects) 

The main.cpp file showing simple model access...

 // main.cpp
 #include <iostream>
 #include "ordt_pio_common.hpp"  // common classes
 #include "ordt_pio.hpp"  // defines model classes
 
 int main()
 {
      
   // --- accessing model using read/write api
   ordt_root root; // root node of model
   ordt_data rdata; // read data return type
   int rc = 0;
      
   std::cout << "----------- read register at address 0x4:" << '\n';
   rc=root.read(0x4, rdata);
   std::cout << "rc=" << rc << ", size=" << rdata.size() << ", rdata=" << rdata.to_string() << '\n';
 
   std::cout << "----------- directly access a field in this register:" << '\n';
   std::cout << "foo.first_rf.base_reg.fld1=" << std::hex << root.first_rf.base_reg2.fld1.data << '\n';

   std::cout << "----------- write 0xffffffff to register at address 0x4:" << '\n';
   ordt_data wdata(1, 0xffffffff);
   rc=root.write(0x4, wdata);
   std::cout << "rc=" << rc << '\n';
 
   std::cout << "----------- read register at address 0x4:" << '\n';
   rc=root.read(0x4, rdata);
   std::cout << "rc=" << rc << ", size=" << rdata.size() << ", rdata=" << rdata.to_string() << '\n';
 
   std::cout << "----------- directly access field post write:" << '\n';
   std::cout << "foo.first_rf.base_reg.fld1=" << std::hex << root.first_rf.base_reg2.fld1.data << '\n';
   
   std::cout << "----------- lock register and directly modify field:" << '\n';
   {
     std::lock_guard<std::mutex> lock(root.first_rf.base_reg2.m_mutex);
     root.first_rf.base_reg2.fld1.data = 0x3;
   }
 
   std::cout << "----------- read register at address 0x4:" << '\n';
   rc=root.read(0x4, rdata);
   std::cout << "rc=" << rc << ", size=" << rdata.size() << ", rdata=" << rdata.to_string() << '\n';
 
   return 0;
 }

As shown in the example above, the model read/write api uses the ordt_data type to represent registers of arbitrary width. Some manipulations of the ordt_data class are shown below:

   // --- use of ordt_data for storing wide fields (vector of uint32_t) 
   ordt_data a;    // new empty data object  
   std::cout << "size=" << std::hex << a.size() << ", is empty=" << a.empty() << '\n';
   a.push_back(20);  // update high order word 
   std::cout << "size=" << a.size() << ", is empty=" << a.empty() << '\n';
 
   ordt_data b(4, 0x22); // define data using word count and value
   std::cout << "size=" << b.size() << ", is empty=" << b.empty() << ", b=" << b.to_string() << '\n';
   b[2] = 0xff; // update a word
   std::cout << "size=" << b.size() << ", is empty=" << b.empty() << ", b[3]=" << b[3]  << ", b[2]=" << b[2] << ", b[1]=" << b[1]  << ", b[0]=" << b[0] << '\n';
   int newdat = 0xf;
   b.set_slice(20,64,newdat);  // example of setting a slice value at bit location/width
   std::cout << "size=" << b.size() << ", is empty=" << b.empty() << ", b[3]=" << b[3]  << ", b[2]=" << b[2] << ", b[1]=" << b[1]  << ", b[0]=" << b[0] << '\n';
 
   // -- get slices of various types
   uint_fast8_t small_field = 0;
   b.get_slice(96, 4, small_field);
   int outp = 0;
   outp = outp + small_field;
   std::cout << "b.get_slice(96, 4, small_field):" << small_field << ", outp=" << outp << '\n';
 
   uint32_t uint32_field = 0;
   b.get_slice(96, 4, uint32_field);
   std::cout << "b.get_slice(96, 4, uint32_field):" << uint32_field << '\n';
 
   uint64_t med_field = 0;
   b.get_slice(20, 64, med_field);
   std::cout << "b.get_slice(20, 64, med_field):" << med_field << '\n';
 
   // --- copy data to c
   ordt_data c(b);
   std::cout << "size=" << c.size() << ", is empty=" << c.empty() << ", c[3]=" << c[3]  << ", c[2]=" << c[2] << ", c[1]=" << c[1]  << ", c[0]=" << c[0] << '\n';


C++ Driver Model

Specifying the -cppdrvmod option on the command line creates a C++ register model that can convert a specified path string into an address as well as return basic field info.

The -overlay command line option can be used to overlay multiple input files in a single driver model with each accessible by tag/id. The overlay option is intended to allow multiple versions of a register space in a single model while reusing common structures (the overlay option is currently only active when -cppdrvmod output is specified).

As an example, consider the following two rdl files with minor differences:

Running the following ordt command...

 ordt -cppdrvmod output_cppdrv -overlay v2 file2.rdl file1.rdl

Results in the following console output

 Open Register Design Tool, version=170405.01, input=...file1.rdl
 Ordt: building C++ driver model...
 *** INFO ***: Overlay 0 total processed instances=9, unique instances=6, duplicate instances=3
 Ordt: processing overlay file ...file2.rdl...
 *** INFO ***: Overlay 1 total processed instances=10, unique instances=3, duplicate instances=7
 Ordt: writing C++ driver model file output_cppdrv/ordt_pio_common.hpp...
 Ordt: writing C++ driver model file output_cppdrv/ordt_pio_common.cpp...
 Ordt: writing C++ driver model file output_cppdrv/ordt_pio_drv.hpp...
 Ordt: writing C++ driver model file output_cppdrv/ordt_pio_drv.cpp...
 Ordt complete Wed Apr 05 12:40:25 EDT 2017

Note that the second file processes one additional instance (new_reg1) and adds only 3 unique instances to the model (new_reg1 and its parent register sets).

An example Makefile for building a basic test (main.cpp) using clang is shown below...

 objects = main.o ordt_pio_drv.o ordt_pio_common.o
 
 ccomp = clang++ -c -Wall -std=c++11 -stdlib=libc++
 
 runtest : $(objects) 
 	clang++ -stdlib=libc++ -o runtest $(objects) 
 
 main.o : main.cpp ordt_pio_drv.hpp ordt_pio_common.hpp
 	$(ccomp) main.cpp
 
 ordt_pio_drv.o : ordt_pio_drv.cpp ordt_pio_drv.hpp ordt_pio_common.hpp
 	$(ccomp) -D ORDT_PIO_DRV_VERBOSE ordt_pio_drv.cpp
 
 ordt_pio_common.o : ordt_pio_common.cpp ordt_pio_common.hpp
 	$(ccomp) ordt_pio_common.cpp
 
 clean :
 	rm runtest $(objects) 

The main.cpp file showing simple model access...

// main.cpp
#include <iostream>
#include <string>
#include "ordt_pio_common.hpp"  // common classes
#include "ordt_pio_drv.hpp"  // defines model classes

int main()
{

  // --- access model using get_address call

  ordt_drv_root root; // root node of model
  int rc = 0;
  uint64_t address = 0;
  std::list<ordt_drv_field> fields;

  std::string path = "bad_path.bad_path2[2].reg";
  std::cout << "----------- get register address using an invalid tag, path: " << path << '\n';
  rc = root.get_address("badtag", path, address, fields);
  std::cout << "rc=" << rc << ", addr=0x" << std::hex << address << '\n';

  std::cout << "----------- read tags defined in this model" << '\n';
  std::vector<std::string> tags = root.get_tags();
  for (int i=0; i<tags.size(); i++) std::cout << "tag idx=" << i << ", tag=" << tags.at(i) << '\n';

  std::cout << "----------- get register address using root tag, path: " << path << '\n';
  rc = root.get_address("foo", path, address, fields);
  std::cout << "rc=" << rc << ", addr=0x" << std::hex << address << '\n';

  std::cout << "----------- get register address using second tag, path: " << path << '\n';
  rc = root.get_address("v2", path, address, fields);
  std::cout << "rc=" << rc << ", addr=0x" << std::hex << address << '\n';

  path = "foo.first_rf.base_reg3";
  std::cout << "----------- get register address using root tag, path: " << path << '\n';
  rc = root.get_address("foo", path, address, fields);
  std::cout << "rc=" << rc << ", addr=0x" << std::hex << address << '\n';

  std::cout << "----------- get register address using second tag, path: " << path << '\n';
  rc = root.get_address("v2", path, address, fields);
  std::cout << "rc=" << rc << ", addr=0x" << std::hex << address << '\n';

  std::cout << "----------- show fields in this register" << '\n';
  for (auto &fld: fields) std::cout << "name=" << fld.m_name << ", idx=" << fld.m_loidx << ", width=" << fld.m_width << ", read=" << fld.m_readable << ", write=" << fld.m_writeable << '\n';

  path = "foo.first_rf.new_reg1";
  std::cout << "----------- get register address using root tag, path: " << path << '\n';
  rc = root.get_address("foo", path, address, fields);
  std::cout << "rc=" << rc << ", addr=0x" << std::hex << address << '\n';

  std::cout << "----------- get register address using second tag, path: " << path << '\n';
  rc = root.get_address("v2", path, address, fields);
  std::cout << "rc=" << rc << ", addr=0x" << std::hex << address << '\n';
  return 0;
}

Results in the following output...

----------- get register address using an invalid tag, path: bad_path.bad_path2[2].reg
--> invalid tag: badtag
rc=2, addr=0x0
----------- read tags defined in this model
tag idx=0, tag=foo
tag idx=1, tag=v2
----------- get register address using root tag, path: bad_path.bad_path2[2].reg
--> unable to find child bad_path2 in regset foo
rc=8, addr=0x0
----------- get register address using second tag, path: bad_path.bad_path2[2].reg
--> unable to find child bad_path2 in regset foo
rc=8, addr=0x0
----------- get register address using root tag, path: foo.first_rf.base_reg3
rc=0, addr=0x8
----------- get register address using second tag, path: foo.first_rf.base_reg3
rc=0, addr=0x14
----------- show fields in this register
name=fld1, idx=0, width=a, read=1, write=1
name=fld2, idx=f, width=1, read=1, write=1
name=fld3, idx=19, width=1, read=1, write=1
----------- get register address using root tag, path: foo.first_rf.new_reg1
--> unable to find child new_reg1 in regset first_rf
rc=8, addr=0x0
----------- get register address using second tag, path: foo.first_rf.new_reg1
rc=0, addr=0x8