https://community.intel.com/t5/Blogs/Products-and-Solutions/Software/Opening-up-the-Device-Modeling-Language/post/1417739 Search[ ][Search] Browse * Community * About Community * Private Forums + Private Forums + Intel oneAPI Toolkits Private Forums + All other private forums and groups + Intel AI Software - Private Forums + GEH Pilot Community Sandbox + Intel(r) Connectivity Research Program (Private) * Developer Software Forums + Developer Software Forums + Toolkits & SDKs + Software Development Tools + Software Development Topics + Software Development Technologies + Intel(r) DevCloud + oneAPI Registration, Download, Licensing and Installation + GPU Compute Software + Software Archive * Product Support Forums + Product Support Forums + Intel(r) NUCs + Memory & Storage + Embedded Products + Visual Computing + FPGA + Graphics + Processors + Wireless + Ethernet Products + Server Products + Intel(r) Enpirion(r) Power Solutions + Intel Unite(r) App + Intel vPro(r) Platform + Intel(r) Trusted Execution Technology (Intel(r) TXT) + Intel(r) Unison(tm) App + Intel(r) QuickAssist Technology (Intel(r) QAT) * Gaming Forums + Gaming Forums + Intel(r) ARC(tm) Graphics + Gaming on Intel(r) Processors with Intel(r) Graphics + Developing Games on Intel Graphics * Blogs + Blogs + @Intel + Products and Solutions + Tech Innovation + Thought Leadership Software Communicate with software experts on product changes and improvements Announcements The Intel sign-in experience has changed to support enhanced security controls. If you sign in, click here for more information. Success! Subscription added. Success! Subscription removed. Sorry, you must verify to complete this action. Please click the verification link in your email. You may re-send via your profile. * Intel Community * * Blogs * * Products and Solutions * * Software * * Opening up the Device Modeling Language 13 Discussions Opening up the Device Modeling Language Subscribe Article Options * Subscribe to RSS Feed * * Mark as New * Mark as Read * * Bookmark * Subscribe * * Printer Friendly Page * Report Inappropriate Content [large] Jakob_Engblom Jakob_Engblom Employee 09-27-2022 12:03 PM 0 0 23.2K The Device Modeling Language (DML) is a domain-specific language for creating fast functional transaction-level virtual platform models. The first version of DML was launched in 2005, and it has been the standard way to build device models for the Simics(r) simulator ever since. The Simics use of DML as the primary modeling tool is an interesting example for the virtual platform community. The most common approach for virtual platform modeling is to use a general-purpose language (like C++ or C#) along with a modeling library and simulator API. Designing a domain-specific language is philosophically different, and in our experience DML has provided benefits for programmer productivity and model quality that provide a clear return-on-investment. Some particular observations about the benefits of DML: * DML code is smaller and clearer than general-purpose languages (which is to be expected from a domain-specific language). * Maintaining code is easier, thanks to the higher level of abstraction. * It is easy to follow good transaction-level modeling practice. * Event-driven execution is built in. * It encourages the use of metadata at all interface points. * Virtual-platform-specific actions such as scheduling event callbacks and issuing log message are built-in keywords in the language, not function calls. * The language definition is not limited to what is provided by the underlying language, making it possible to add useful constructs like multiple return values from methods. * It automatically generates checkpoint-capable models (unless the programmer explicitly breaks checkpointing). * Compile-time resolution is used wherever possible, to minimize run-time and storage overhead from generic and common code libraries. * DML can support new versions of the Simics simulator framework with a simple recompile of the code - since the simulator API is handled by the compiler. * DML code is portable across host types, as the language and Simics simulator runtime system isolate the model code from the specifics of the host. Previously, the user base and audience for DML has been fairly limited, as it has only been available as part of the Simics simulator. That has changed. The Simics simulator is available as a public release, and the DML language and compiler have been made available as open-source on github. Taken together, these releases make very easy for anyone interested to try DML and do some device modeling with it, in a virtual platform context. dml-random-source-code-in-editor.png Some example DML source code being edited in Emacs* The Background of DML Before DML existed, device modeling for the Simics simulator was done using C or C++ and the Simics API. This resulted in large amounts of boilerplate code for mandatory tasks such as registering classes with the simulator core. Whenever such unnecessary repetition is seen, the natural reaction of a programmer is to design a little language to distill the interesting parts of the problem. DML grew from an initial C-code generator into a language. This resulted in DML 1.0, which was released in 2005. The second version, DML 1.2, was released in 2007. It worked very well, but over time the need for a major overhaul became clear. The current version, DML 1.4, had its first release in 2019. It marked a distinct change compared for DML 1.2, overhauling much of the syntax and refining and defining the language semantics. Since then, there has been a steady stream of additions to the language, with more in the pipe. The DML compiler generates C code with simulator API calls. Models written in DML can be mixed in the same Simics simulator session as models written in plain C, C++, Python, and SystemC. Device Modeling The Device Modeling Language is a language used to model devices, i.e., individual hardware blocks. For example, a timer, serial port, interrupt controller, accelerator block, or Ethernet interface. DML assumes an event-driven transaction-level simulator where the device model is driven by a series of function calls from the outside, and in turn operate by calling other device models. Device models are typically leaves in the system hierarchy (alongside instruction-set simulators, memory maps, interconnects, and memories), and are created by a separate simulator configuration system. The main interfaces of a device model are illustrated below. DML provides dedicated language constructs for each of those interfaces. * Programming registers (register banks) are used by software drivers * Connects let a device call out to other devices * Ports receive calls from other devices * Events are used to get callbacks at future points in time * Attributes are used to configure the device and provide back-door access to the device state for tools and users dml-device-model-sketch.png Example Device Declaration Every DML device starts with a device declaration, followed by parameters (param keywords) that provide model metadata and control aspects of the device setup. The behaviors of device-level templates are also controlled and specialized using param. Standard libraries, interface declarations, and framework code used in a model are pulled in using import statements. // DML version dml 1.4; // Declaring the name of the device class device m_control; // Metadata, as device parameters param desc = "mandelbrot control unit"; param documentation = "Control unit for synchronizing compute units."; // Importing general library files import "utility.dml"; // Importing particular interfaces used in the device import "simics/devs/signal.dml"; import "simics/devs/memory-space.dml"; import "simics/simulator-api.dml"; import "m-compute-control-interface.dml"; // Settings to use in this device model only // Enable PCIe setup param use_pcie = true; // Enable use of stall performance optimization param stall_on_status_read = false; // Compute units: created outside this device and connected at runtime // up to a fixed maximum param max_compute_units = 8; The example code used here can be found in the m-control device that is part of workshop-02 in the training package in the public release of the Intel Simics simulator. Example Register Bank Register banks are declared using the bank keyword. Inside each bank, there are registers and fields. For readability, it is recommended to declare the layout of a register bank separate from the implementation. The DML compiler combines all declarations related to the same register into a single implementation, allowing arbitrary code structuring. An example register bank: // Declaring a register bank - typically separate from implementation bank ctrl { param register_size = 8; register compute_units @ 0x00 is (read_only) "Available compute units"; register start @ 0x08 is (write) "Start operation"; register status @ 0x10 "Operation status" { field done @ [63] is (write) "Operation completed" ; field processing @ [62] is (read_only) "Operation in progress" ; field unused @ [61:0] is (reserved) "unused" ; } register _reserved @ 0x18 is (reserved) "Reserved"; // Managing the compute units // as bit masks in registers register present @ 0x20 is (compute_unit_bitmask_register) "compute units present bitmask"; register used @ 0x28 is (compute_unit_bitmask_register) "units used in current operation"; register done @ 0x30 is (compute_unit_bitmask_register) "units used & completed operation"; } Register layouts generated from specification files (such as IP-XACT* ) are typically saved to their own source file, which is then imported into the device model. Templates (such as read_only or reserved in the code above) are used to specify common behaviors. When more complex and specific behaviors are needed, they are added in a second bank declaration that provides implementations for methods like read() and write() on registers and fields. For example: // Specifying the behavior in a section of code // All partial definitions are "merged" in compilation bank ctrl { ... register start is write { method write(uint64 value) { start_compute(value); // Call method defined elsewhere this.val = value; } } ... register status { field done is write { method write(uint64 v) { if (v == 1) { if (this.val == 1) { // Wrote a 1 bit to clear the done flag // - Take all side-effect actions needed. do_clear_done(); } else { log spec_viol, 1, control: "Attempt to clear already clear done flag"; } } else { log spec_viol, 1, control: "Writing zero to done has no effect"; } } } Note the use of log spec_viol to report bad actions from the software. A virtual platform can check and report on what the software does, and DML makes it very easy to add usage warnings like these. Example Connect and Port Another core function of a device is to communicate with other devices. In DML, this is accomplished using unidirectional connections from one device to another. A device receives incoming transactions as function calls in named ports. For example, this port is called control_in and implements the m_compute_control interface: port control_in { param desc = "control input from the control unit"; implement m_compute_control { // Start an operation method start_operation() { log info, 2, control: "Received request to start compute job"; if (ctrl.status.processing.val == 1) { // Getting to this error state requires that the // external world calls start_operation() twice without // completing the operation in the meantime. log spec_viol, 1, control : "Operation start request while operation in progress"; return; } // Note that it is OK to start a new operation if the device // is in state "done". start_compute_job(); } method clear_done() { log info, 2, control: "Received request to clear done flag"; // Sanity check if (ctrl.status.done.val == 0) { log spec_viol, 1, control : "Clear done signal received when done flag is not set."; return; } log info, 2, control: "Clearing done flag from signal %s", this.qname; do_clear_done(); } } } Interfaces are defined outside of the device model, since they are by nature shared between multiple devices. On the sending side of the interface, there is a connect. For example, the following declaration creates an array of connections from a device to a set of devices implementing the m_compute_control interface: connect compute_unit_control[i