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 
Clone this wiki locally