-
Notifications
You must be signed in to change notification settings - Fork 71
Cpp Model Outputs
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
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';
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
Home
Running Ordt
Building Ordt from source
Running Ordt Tests
Source contributions
Ordt Inputs
Ordt Outputs