• No results found

Hierarchical Logic Simulator using SystemC

N/A
N/A
Protected

Academic year: 2021

Share "Hierarchical Logic Simulator using SystemC"

Copied!
32
0
0

Bezig met laden.... (Bekijk nu de volledige tekst)

Hele tekst

(1)

Bachelor Informatica

Hierarchical Logic Simulator

using SystemC

Sijmen Schoon

March 2, 2020

Supervisor(s): drs. A. van Inge

Inf

orma

tica

Universitei

t

v

an

Amsterd

am

(2)
(3)

Abstract

Describing systems using Hardware Description Languages and simulating them is a pop-ular tool in designing hardware systems. This study aims to create a hierarchical, multi-abstraction hardware description tool with an interactive symbolic layer for digital logic components, with the possibility to be run and updated live and independently from each other.

To allow for this, C++ syntax analysis was used to parse SystemC programs, which is a hardware description language allowing for abstraction levels from Register Transfer Level to Transaction-Level Modeling, and to allow for live warnings and errors in the program editor. Components were then compiled and ran as independent processes. For this, code generation was employed to create inter-process communication code using nanomsg and Cap’n Proto to allow the separate components to communicate. Finally, the option to add projects to other projects was added to allow for a hierarchy, to allow for reducing cognitive load in bigger projects.

To support this, a Django backend with a RESTful API was created, supported by an SQLite database for storing the configuration. This backend manages the compilation and running of the component instances. A React frontend for in the browser, aided by the Next.JS framework, was made for interacting with the system, which hooked into both the RESTful API, for displaying the system configuration, and the IPC, for displaying live updates. On this basis, it can be concluded that while it is possible to create the tool as asked by the research question, it is a big task which, due to time constraints, limited it to a proof of concept which can be expanded upon in the future.

(4)
(5)

Contents

1 Introduction 7

2 Research and Theory 9

2.1 Workings of SystemC . . . 9

2.1.1 Library Architecture . . . 9

2.1.2 Compilation. . . 10

2.1.3 Levels of Abstraction. . . 11

2.2 SystemC Module Analysis . . . 11

2.2.1 Specialised SystemC Parsers . . . 11

2.2.2 Generic C++ Parsers . . . 11

2.3 Inter-process Communication . . . 11

2.3.1 Transport . . . 12

2.3.2 Serialization and deserialization. . . 13

3 Implementation 15 3.1 C++ Syntax Analysis . . . 15

3.1.1 Loading Source Files . . . 15

3.1.2 Compiler Diagnostics. . . 16

3.1.3 SystemC Module Analysis . . . 16

3.2 Inter-Process Communication . . . 16

3.2.1 Cap’n Proto Schemas . . . 16

3.3 Code Generation . . . 17 3.4 Module Hierarchy. . . 18 3.4.1 Internal Representation . . . 18 3.4.2 Code Generation . . . 19 3.5 Application Architecture . . . 19 3.6 Database Structure . . . 20 3.7 Back-end API . . . 21 3.7.1 Data Endpoints. . . 21 3.7.2 Action Endpoints. . . 21 3.8 User Interface . . . 22 3.8.1 Project Overview . . . 22 3.8.2 Project Page . . . 22 3.8.3 Instance Details . . . 24 3.8.4 Module Overview. . . 25 3.8.5 Module Details . . . 25

4 Conclusion and Future Work 27 4.1 Abstraction Levels . . . 27

4.2 Independent Component Simulation . . . 27

4.3 Hierarchy of Components . . . 28

4.4 Schematic Layer . . . 28

(6)

4.5.1 Researching Alternatives to nanomsg. . . 28 4.5.2 User Interface . . . 29 4.5.3 Performance . . . 29 4.5.4 Features . . . 29 4.5.5 FPGA Synthesis . . . 29 Bibliography 30

(7)

CHAPTER 1

Introduction

Hardware Description Languages (HDLs) have taken hardware design by storm since languages such as Verilog and VHDL were designed in, respectively, 1985 and 1987. These languages allow hardware designers to accurately model the hardware they are developing at a more behavioral level at the Register-Transfer Level (RTL), instead of the transistor or gate level that was common before [3].

Modeling hardware allows the designer to first simulate the hardware before producing a physical design (in the shape of an integrated circuit or IC). Since no physical hardware has to be produced to validate a design, first simulating the design is more efficient in both time and costs. In addition, multiple combinations of design decisions can be combined and tried with relatively little effort, to see what works best in practice [16].

Another advantage of hardware modeling is allowing certain levels of abstraction when designing the system. HDLs allow for certain levels of abstraction, such as RTL. This allows for, for example, cleanly distinguishing between boolean operations on signals and clock signals, reducing the amount of information (the cognitive load) that needs to be processed by the hardware designer.

Additionally, simulation allows for recording the state of the simulation at any moment, as often as desired. The system can later then be restored to this state at any time, even after events that would normally destroy the hardware like short-circuits and transient voltages. This allows the designer to reproduce and examine faulty states in the design, which can then be quickly fixed. Since RTL has been showing its limits, several researchers have been looking for the next level of abstraction. Since version 2, SystemC started to focus on Transaction-Level Modeling (TLM) earlier explored in SpecC (an earlier effort of creating a modeling language backed by a generic programming language [10]), a successor in abstraction level to RTL, while still being flexible enough to be used for lower levels of abstraction [1].

For this thesis, research will be done on a system to allow for a more graphical and user-friendly symbolic layer for SystemC with support for nesting parts of a schema hierarchically, while al-lowing the user to interact with the simulation by adding, removing, connecting or disconnecting modules to the layer while it’s running, with a live visualization of the schema, exploiting the flexibility of SystemC as to allow for combining multiple layers of abstractions in a single project.

(8)

This leads to the research question “How can SystemC be used to make a hierarchical, multi-abstraction hardware description tool with an interactive symbolic layer for digital logic compo-nents, which can run and updated live and independently of each other?”.

The research question can be split up into the following components:

• How can multiple levels of abstraction, such as gate-level and RTL be combined, allowing for using them together in one system?

• How can a hierarchy of functional blocks be implemented, e.g. a block implementing a NAND port, which can be used to create an XOR port?

• How can these functional blocks be run independently of each other, to allow for creating, deleting and making connections between them without recompiling and restarting the simulation?

• How can an interactive schematic layer be built, allowing for live interaction with these functional blocks?

The source code for this project can be found on https://gitlab-fnwi.uva.nl/11055987/ thesis.

(9)

CHAPTER 2

Research and Theory

2.1 Workings of SystemC

2.1.1

Library Architecture

SystemC provides an abstraction over the C++ programming language, unlike similar concepts, such as VHDL and Verilog, which are a language of their own. It supports the functional modeling of a system by providing the following abstractions [7]:

• Modules to decompose a system into modules. • Ports to provide connectivity between those modules.

• Synchronizing and triggering concurrent modules using events and sensitivity. • Simulated time, that could pass at a different rate than wall time.

• Separating computation in modules (processes) from communication (channels). • Hardware-oriented data types, such assc_logicto model digital logic.

• Fixed-point arithmetic, for a more absolute representation of the fractional part.

One of the main features of SystemC is its process scheduler. This non-preemptive scheduler executes processes in response to the notification of events, like when an input is toggled or when a clock signal reaches a positive edge. For events that happen in different ticks, this is deterministic, but when multiple events happen in a single tick, the order in which the events are executed is non-deterministic [8].

Figure 2.1 on the following page shows a visual overview of the architecture of a SystemC application, in which the features that SystemC are represented by shaded blocks.

A module is the primary building block of a SystemC application. A system is represented by a hierarchy of modules (see figure2.2 on the next page for a graphical overview), each of which can contain the following:

• Ports, which are bound to interfaces and can be used for internal communication, i.e. communication between modules. These represent input/output pins on modules.

• Channels, which implement interfaces for use with ports.

• Events, which are mutations of the state of a port, such as a changed value or the high edge of a clock signal.

• Module instances, which allow for a hierarchy by composition.

(10)

Figure 2.1: The SystemC language architecture [9]. Module Instance

Module

Instance

Module Instance Process Process Process channel event

sc_port sc_export interface

Figure 2.2: A graphical overview of the architecture of a hierarchical SystemC module [13].

2.1.2

Compilation

As SystemC applications are normal C++ programs, they can be compiled by any C++ com-piler. To compile a source file calledclock.cpp, the command listed in listing 2.1 (or its GCC equivalent) can be run.

clang++ \

-I/opt/systemc/include -L/opt/systemc/lib \ # Specify SystemC location.

-lsystemc -pthread \ # Link in SystemC and PThread.

-o clock \ # Set output name to clock.

clock.cpp # Use clock.cpp as source file.

Listing 2.1: Compiling a SystemC source file using clang.

This will link the SystemC library (located at/opt/systemc/), enable PThreads (as required by SystemC) and name the output binaryclock. The simulation can then be started by running theclock executable.

(11)

2.1.3

Levels of Abstraction

Being based on a “normal” programming language makes SystemC an extremely flexible tool for multiple abstraction level modeling. While SystemC mostly is associated with Transaction-Level Modeling, it also supports any abstraction level up from Register Transfer Level [5].

SystemC not supporting an abstraction level, such as transistor level or gate-level, does however not mean it is impossible to use this level. By modeling, in the case of transistor level, transistors, resistors, and capacitors, or in the case of gate-level, basic logic gates, it is possible to model lower abstraction level systems using SystemC. This research will however not focus on this, and will instead focus on Register Transfer Level abstraction levels and higher.

2.2 SystemC Module Analysis

To obtain an understanding of the structure of user-provided SystemC modules, the module source code must be parsed. There are multiple strategies of doing this: using a parser specialized for SystemC applications, or, since SystemC modules are implemented in standard C++, using a more general parser. These two methods are described in this section.

2.2.1

Specialised SystemC Parsers

One method of parsing modules is to use an existing SystemC parser. Four such parsers are ParSyC [2], a nameless parser created at the Istituto Politecnico Milano [29], PINAPA [4], and PinaVM [6], which all focus on creating an abstraction layer on top of SystemC for easier analysis, visualisation or synthesis.

ParSyC and the parser created at the Istituto Politecnico Milano directly parse the C++ syntax without using existing GCC or LLVM front-ends. While this approach appears to be very perfor-mant, it can parse only a subset of the C++ standard and is not very tolerant of future changes to the C++ standard. PINAPA and PinaVM, however, use a different approach and could be seen as more of a SystemC front-end than a parser, since these systems gain an understanding of the module structure by actually running the elaboration phases of the modules and considering the state of memory at the end of the phase. This makes them extremely flexible and worth considering for analyzing the user-provided SystemC code.

2.2.2

Generic C++ Parsers

A different approach is to use a library to directly parse the C++ code and build an Abstract Syntax Tree (AST) from the source code. This is similar to ParSyC’s approach, but by using an existing, established tool, full compatibility with the C++ standard can be maintained and therefore establishing a middle ground between the parsers and front-ends. Of these libraries, LibClang was preferred due to the stability of its interface, its simple but effective representation of the AST [41], and its official Python bindings [42].

While LibClang does not offer full control over the AST, it provides plenty of detail to gain enough insight into the name and types of a module’s ports, which is what the library will initially be used for.

All things considered, LibClang will be used for analysing SystemC modules due to its lower complexity compared to the SystemC parsers, its association with the Clang compiler and the stable interface that is promised by the Clang Team.

2.3 Inter-process Communication

Since the modules will be compiled as separate applications, to allow the modules to be edited and, more importantly, be connected and disconnected from each other without first recompil-ing or re-runnrecompil-ing the entire project to allow for experimentation in the application front-end, communication between these applications should be facilitated.

(12)

The concept of connecting processes to allow them to communicate is called Inter-Process Com-munication or IPC. In this section, possible tools for this will be discussed.

2.3.1

Transport

To allow the now separate SystemC applications and the back-end server to communicate with each other, we need a system to dynamically connect and disconnect inter-process communication sockets. To allow this, nanomsg was chosen as a high-performance messaging library. This library was chosen due to its simplicity and higher performance compared to non-standalone libraries such as RabbitMQ [37] and Apache ActiveMQ [28] (which require dedicated broker software; see figures2.3 and 2.4 for an illustration) and was preferred over ZeroMQ (the predecessor of nanomsg by the same author) due to a more advanced feature set [18].

Broker Module A Module B Module C Module D input output

Figure 2.3: The topology of a pipelined broker-based messaging system involving four chained messages. input Module A Module B Module C Module D output

Figure 2.4: The topology of a brokerless messaging system involving four chained mes-sages.

Another option that was considered was using JSON to push events to the Django backend. This, however, will likely not scale properly, considering that (de)serializing JSON is inefficient and that module instances (which could run up to hundreds per project) would have to poll the server for updates to their inputs, which would flood the backend server with requests, reducing the performance of the entire application significantly.

There are two implementations of the nanomsg protocol; libnanomsg, the initial implementation, and NNG (or nanomsg-next-generation), which is the result of a rewrite of libnanomsg and has some significant advantages over libnanomsg, importantly reliability and scalability [20] and will, therefore, be used for this research. To communicate with other processes, nanomsg supports a range of transports, including TCP, IPC (implemented using UNIX domain sockets for UNIX systems and named pipes for Windows systems) and WebSockets — which could prove to be useful for displaying live updates in the web interface [21]. For communicating between module instances, the IPC transport will be used due to its lack of TCP overhead and not needing to bind to TCP ports.

Nanomsg supports, among others, the pub/sub and pipeline patterns. The pipeline pattern [24,

23] will be used for the modules to push messages to the backend, and the pub/sub pattern [22,

25] will be used by the backend to push messages back to all modules. Note that while the pub/sub pattern supports subscribing to specific subjects, this happens on the subscriber side anyway [25]. Therefore, this feature will not be used and replaced by more flexible filtering based on the deserialized data.

A nice feature of this set-up that the pipeline pattern supports multiple clients on the pull side, allowing for load distribution across multiple back-end instances [23]. This could improve performance for bigger projects.

(13)

Django (backend) SystemC module SystemC module SystemC module

Figure 2.5: Visualization of a possible BUS-based topology.

For example code to connect to an IPC socket at/tmp/socket.ipc, see listings2.2and2.3. # Create and listen on an IPC socket at /tmp/nanomsg.ipc.

bus = Bus0(listen="ipc:///tmp/nanomsg.ipc") # Wait for other nodes to connect.

time.sleep(0.5)

# Send a message to all other nodes. bus.send(b"Hello world!")

Listing 2.2: Listening on a nanomsg BUS IPC socket in Python, and sending a message.

nng::socket bus = nng::bus::open();

// Connect to the socket opened in listing 2.2. bus.dial("ipc:///tmp/socket.ipc");

// Receive the message and print it. nng::msg message = bus.recv_msg();

std::cout << "Received '" << message.body().get() << "'" << std::endl;

Listing 2.3: Connecting to a nanomsg BUS IPC socket in C++, and receiving a message. The example in listing2.3 can be used as many times as needed, and every message that gets sent to the bus (by both listening and dialing nodes) will be delivered to every node, except the one that sent it.

2.3.2

Serialization and deserialization

Before a message can be sent in the queue, it first needs to be serialized to a common format. Since the backend server is written in Python, it is impractical to avoid serialization altogether and just send the raw struct from memory. While Python does support parsing C structs [19], this does not support importing C/C++ header files to share a single version of the data format, and it is not practical and highly error-prone to maintain the same struct format in multiple places.

To solve this problem, Google’s Protocol Buffers and its successor Cap’n Proto were consid-ered [34,38]. Text-based formats like JSON or XML were not considered due to binary formats being more efficient in both storage space (so fewer bytes have to be transferred) and encoding

(14)

performance [12,11,14], and simpler binary formats like BSON, while performant [17] were not considered due to lack of a well-defined schema format. Of the binary formats, Cap’n Proto was used for this project due to both better performance and personal preference for its schema format and API.

Cap’n Proto uses a schema language to define the message structure in a strongly typed fash-ion [40]. This language is then either compiled using the capnp tool for C++ or imported in the Python library. For an example of such a schema, see listing2.4.

@0xf329d4e30b29e3e9; # Unique file ID. struct Module { id @0 :UInt64; name @1 :Text; ports @2 :List(Port); struct Port { id @0 :UInt64; name @1 :Text; type @2 :Text; } }

Listing 2.4: An example of the Cap’n Proto schema language.

After compiling and/or importing this schema, it will be possible to serialize and deserialize data to a byte array, which can then be sent to other processes using nanomsg. For an example of serialization in C++ and deserialization in Python, see listings2.5and2.6respectively.

capnp::MallocMessageBuilder message;

Module::Builder builder = message.initRoot<Module>(); builder.setId(1);

builder.setName("And");

kj::Array array = capnp::messageToFlatArray(message); kj::ArrayPtr buf = array.asChars();

send_buffer(buf);

Listing 2.5: Serializing into Cap’n Proto-encoded data in C++.

buf = receive_buffer()

module = module_capnp.Module.from_bytes(buf) print("ID:", module.id)

print("Name:", module.name)

(15)

CHAPTER 3

Implementation

This chapter discusses how the elements researched in chapter2were implemented.

The first half of this chapter reports on the foremost features of the project: first, in section3.1, source analysis, to analyze the user-provided SystemC code, is discussed. Then, in section 3.3

code generation is examined. Finally, an overview of how a hierarchy is implemented is provided in section3.4.

In the second half of this chapter, the more general elements are discussed: section3.5outlines the overall architecture of the application, discussing the main technologies used and how they were fitted together to create the application. Of these technologies, the database structure is described in section3.6and the back-end API is described in section3.7and the user interface is shown and explained in section3.7.

3.1 C++ Syntax Analysis

This section describes how LibClang was employed for analysing the module source files for both correctness and to obtain insight into the structure of these modules. For more information on LibClang, refer to section2.2on page11.

3.1.1

Loading Source Files

To load and analyze a source file, LibClang’sTranslationUnit.from_sourcefunction was used, specifying the file name, compiler arguments (the same as when compiling the file normally — see section2.1.2on page 10— but adding the system header files, which LibClang does not find automatically) and thePARSE_PRECOMPILED_PREAMBLEargument (which caches the SystemC headers for improved performance). Additionally, theunsaved_filesargument can be specified to allow analyzing source code from a string.

See listing 3.1 for an example of loading and analyzing source code from memory. Note how

main.cppwas used as a temporary filename — this is required by LibClang.

TEMPORARY_FILENAME = "main.cpp" return TranslationUnit.from_source( TEMPORARY_FILENAME, args=settings.CXXFLAGS, unsaved_files=[(TEMPORARY_FILENAME, program)], options=TranslationUnit.PARSE_PRECOMPILED_PREAMBLE, )

(16)

3.1.2

Compiler Diagnostics

LibClang also provides a list of compiler warnings and errors, or diagnostics, when loading a source file [43]. These are provided as a list of Diagnosticobjects, which provide, among others, a level of severity, the exact row and column location where the diagnostic occurred, and related information (for example, to suggest corrections for misspellings).

These diagnostics were then converted to the format expected by the text editor component used and could be retrieved using thecheckendpoints mentioned in section 3.7.2on page21.

3.1.3

SystemC Module Analysis

To be able to generate code for IPC and for generating a schematic view of the module, the program entered by the user was analyzed. This was done by iterating over the AST provided by LibClang, which allowed for the following:

• Retrieving all the classes and structs in the program source. A restriction of only one struct or class per file was added, to prevent confusion which struct or class was the main SystemC module. The database representation of the module was then renamed to the name of the struct if it was changed.

• Extracting the ports from the SystemC module and updating the database entries for them, which could then be used for the visualization of the module and for allowing the ports to be connected on instances of the module. This can then be used for generating the necessary IPC code.

3.2 Inter-Process Communication

As described in section2.3on page11, nanomsg and Cap’n Proto were used to allow the separate SystemC modules to communicate. Three different sockets were used:

• A pub-sub IPC socket for sending messages from the back-end to the SystemC modules. • A pub-sub WebSocket that mirrors the pub-sub IPC socket for sending these messages to

the front-end for visualization

• A pipeline IPC socket for sending messages from the SystemC modules to the back-end. Messages are first serialized using the Cap’n Proto schemas described in subsection section3.2.1, after which they are sent using nanomsg. The messages are then received by the receiver, which then deserializes it and handles its contents.

3.2.1

Cap’n Proto Schemas

Cap’n Proto requires specifying the contents of the messages in advance in a schema, as explained in section2.3.2on page 13. This schema is then compiled to C++ headers and source files, or imported using the Python library. All three sockets share the same schema.

The schema used for this project is based on the Event struct, which then includes the specific data.

(17)

Event struct

The Event struct has two fields, of which one is an enum with which can contain one of several value types:

• instance: An integer specifying the idea of the instance this message is meant for or originates from.

• child: An union containing one and only one of the following values:

– signal: An instance of the Signal struct, sent when an instance has had a signal event, or sent to an instance to update one of its inputs.

– join: A void field (containing no information) sent by an instance when it starts and joins the network of modules.

– wait: A floating-point field representing the number of picoseconds an instance needs to wait to synchronize up with the other instances. Note that this is what makes the simulation cycle-accurate, as SystemC’s simulation timesteps do not need to match clock time.

– connect: An instance of the Connect struct, sent by the back-end when an instance needs to start listening for signal changes on a net.

– disconnect: The same as the connect field, but for disconnecting an instance from a net.

Signal struct

The Signal struct is sent by either a SystemC module,when the module’s output has changed, or by the back-end when the value of a net has changed in a signal event. It consists of the following fields:

• timestamp: The timestamp in picoseconds at which the signal value has changed. Used by other modules to synchronize their internal timestamps.

• port: The ID of the port that has changed. Used when a module updates its output, which is then resolved to a net by the back.

• net: The ID of the net that has changed. Used when the back has an update for an input, which is then resolved to a port by the modules.

• value: The new signal value. Currently a union of a boolean, logic value or logic vector, but only boolean values are currently used by the back-end and modules.

Config struct

The Config struct is sent by the back-end to the SystemC modules when a net is to be connected or disconnected to a module’s port in a connect or disconnect event. It has two fields, port and net, which are used as pointed out in the aforementioned Event struct.

3.3 Code Generation

To interconnect the modules, code is necessary to, among other steps, set up the message queue and send and receive these messages. To reduce the amount of boilerplate code required to interconnect the modules, code will have to be generated automatically.

Initially, the code was generated using Python’s literal string interpolation [15]. These, however, did not scale well and became hard to maintain very quickly, not only due to limitations of the string interpolation (it does not support backslashes, so for example newlines were inconvenient) but also due to the confusing mix of programming languages in one file (see listing 3.2 on the following page).

(18)

NL = "\n"

project.source = f""" SC_MODULE(Logger) {{

{NL.join(f" sc_in<{net.type}> {net.name};" for net in nets)} {NL.join(

f" HANDLER({net.name}, {net.id}, {types.get(net.type)},)" for net in nets

)} }} """

# Further Python code…

Listing 3.2: Code generation using Python string interpolation

Code generation can also be done using templates. Using a templating language allows for putting the C++ template code in separate, allowing for editor syntax highlighting and significantly less cluttered Python code as is visible in listing3.3.

While Django includes a templating language (Django Template Language or DTL) which can be used for both HTML (its original intention) and plaintext [44], it is not very extensive — there, for example, is no way to trim the added newlines it generates without removing all indentation whitespace as well, compromising on the readability of the generated code. Since Jinja2, a newer and more advanced templating language modeled after DTL, does offer this feature [36], it was preferred over DTL for this purpose.

SC_MODULE(Ipc) {

{% for port in ports %}

{{port.direction.systemc()}}<{{port.type}}> port_{{port.id}}; {% endfor %}

{% for port in ports if port.direction.is_out() %}

void handler_{{port.id}}() {

// ... }

{% endfor %} }

Listing 3.3: Code generation using the Django template language

3.4 Module Hierarchy

Allowing to nest modules hierarchically comes with three main challenges: how to store the nested modules in the database, how to allow the nested modules to communicate with their parents in the hierarchy and, considering nesting modules make it easy to gain a very high module count, how to assure that the process count and IPC traffic do not exceed acceptable levels?

3.4.1

Internal Representation

The first challenge in the hierarchy of modules is creating a representation to store the design in the database and adding REST endpoints for it.

The simplest solution was to allow entire projects to be compiled as one module, eliminating the inter-process communication within one hierarchical block. Additionally, to allow for the project to communicate with its parent, any free port could be marked as exported. Projects can then be instantiated like any other module.

(19)

In the example in figure 3.1, the NAND gate, XOR gate, and Half Adder would be stored as three separate projects. In the NAND and XOR projects, their inputs and outputs are exported. These ports are then accessible in the Half Adder project when the child projects are added as modules. NAND NAND NAND NAND NAND XOR AND Half Adder

Figure 3.1: An example of a hierarchical system, showing a half adder from NAND gates with three projects: the parent project (Half Adder) and its two child projects (XOR and NAND).

3.4.2

Code Generation

For generating the code necessary for including a child project into a parent project, the same techniques were applied as for single modules — see section 3.3 on page 17, except that all modules in the child project were combined into one source file and all connections between these modules were generated as SystemC signals, with no involvement of IPC. The exported ports, however, were exposed over IPC, to allow the parent project to communicate with the project as if it were a normal module.

3.5 Application Architecture

The user interface was implemented as a web application, using Django as the backend frame-work [30] with the Django REST framework library to provide RESTful endpoints for the front-end [32]. To store the state of a project, Django’s ORM feature (Object-relational Mapping) was used to facilitate storing the data in an SQLite database, which could, if necessary, be eas-ily interchanged for another supported relational database (such as PostgreSQL) thanks to the abstractions Django’s ORM provides [31].

For the browser front-end, React was used in the front-end [33] with the Next.js front-end frame-work [47] to provide server-side rendering and some quality of life improvements for development. For the frontend to retrieve data from the back-end, the JSON endpoints were accessed using the Axios library [46].

The Django backend also manages the modules, running and terminating them as necessary and setting up connections between them using IPC as described in section 2.3.1 on page 12. Additionally, live signal updates from the modules were sent to the front-end using a nanomsg

(20)

over WebSockets for transport, passing the Cap’n Proto-encoded IPC messages through from the modules.

For a visualization of the architecture, see figure3.2.

Modules SystemC Modules SystemC Modules SystemC SQL JSON HTTP Cap’n Proto Websockets Cap’n Proto nanomsg Cap’n Proto nanomsg Back-end Django Modules SystemC Database SQLite Front-end React

Figure 3.2: Visualization of the application architecture, with arrows indicating how the separate components are connected.

3.6 Database Structure

To store the state of the application, an SQL database was used, consisting of the following tables:

• Module: Describes a single module, defining its name and its implementation in the form of a SystemC module. A module’s ports and instances are defined in a many-to-one fashion — see Port and Instance.

These are not bound to a project but are available globally. This was done this way to encourage code reuse between projects. It is possible however to later add aproject_id field if separating this turns out to be desirable.

• Project: Describes a project, defining its name. A project’s instances of modules and nets that connect these instances are defined in a many-to-one fashion — see Instance and Net.

• Port: Describes a Module’s port, defining its name, the direction of the port (in/out/in-out), its C++ type name and the foreign key of the Module it belongs to.

• Instance: Describes a Module or Project instance, defining its x and y position, a foreign key of either the Module or the Project it is an instance of, and a foreign key of the Project it was placed in.

• InstanceLog: Describes a line of output from an Instance, defining the text, which stream it is from (stdout or stderr), its timestamp and the process ID that created the output.

• Net: Describes a net of connections, defining their common type and a foreign key of the Project it is placed in. Connections that are part of this net are defined in a many-to-one fashion — see Connection.

• Connection: Describes a single connection to a port, defining foreign keys of the Instance and its Port it is connecting a Net to.

• Export: Describes a Port being exported in a project, allowing it to be accessed from parent Projects in the hierarchy, defining the Instance and its Port it is exporting. See figure3.3on the facing page for an entity-relationship diagram (ERD) of the database set-up used in this project.

(21)

Instance PK id int x int y int FK module_id int FK subproject_id int FK project_id int Module PK id int name varchar program text InstanceLog PK id int text varchar stream enum timestamp datetime pid int FK instance_id int Project PK id int name varchar Port PK id int name varchar direction enum type varchar FK module_id int Net PK id int type varchar FK project_id int Connection PK id int FK port_id int FK instance_id int FK net_id int Export PK id int FK port_id int FK instance_id int

Figure 3.3: An entity-relationship diagram of the database model and its relations.

3.7 Back-end API

To allow the Django back-end and the React front-end to communicate, an HTTP API has to be defined. For this project, a RESTful JSON API was used, which was built to represent the database models in section3.6 on the preceding page, and several endpoints to perform actions on them.

For a full schema of all endpoints, see the git repository for this project onhttps://gitlab-fnwi. uva.nl/11055987/thesis.

3.7.1

Data Endpoints

There are five main URL groups:/api/modules/,/api/projects/,/api/ports/,/api/instances/ and /api/nets/. For the first three groups, GETand POSTendpoints were defined to retrieve a list of resources and to create a new resource, respectively. Additionally, all groups (including the last two) have detail endpoints to retrieve a single element of a group, e.g./api/projects/1/. On these endpoints, GET requests read a resource, PUTrequests fully update a resource, PATCH requests partially update a resource andDELETErequests delete a resource.

In addition to the five main endpoint groups, some endpoints have child endpoints, namely

/api/projects/[id]/instances/ and /api/projects/[id]/nets/ for accessing instances and

nets that belong to a project; and/api/nets/[id]/connections/for accessing connections that belong to a net. Note that these routes also have detail routes, but those are identical to the main routes — e.g./api/projects/[id]/instances/5/is equivalent to/api/instances/5/, as all IDs are unique and refer to a single resource.

3.7.2

Action Endpoints

To address actions done in the front-end, such as code being edited in the text editor, requiring a syntax check, but also certain buttons being pressed, the following action endpoints are defined:

(22)

Modules

POST /api/modules/[id]/check/: Expects a module program in the request body, for which it will generate the necessary IPC code as described in section3.3on page17, and then use LibClang to check for warnings and errors, as described in section3.1on page15. Used by the program editor on the Module page to display live warnings and errors. • POST /api/modules/[id]/compile/: Generates the necessary IPC code as described in

section3.3on page17, and then compiles the module code as described in section2.1.2on page10.

POST /api/modules/compile/: Shortcut to compile all modules; see the previous item for more detail.

Instances

POST /api/instances/[id]/run/: Starts running the compiled SystemC module program for this instance. Stops the process first if it was already running.

POST /api/instances/[id]/terminate/: Terminates the process associated with this mod-ule instance.

POST /api/instances/[instanceId]/ports/[portId]/export/: Adds port portId to the list of exported ports of instance instanceId.

POST /api/instances/[instanceId]/ports/[portId]/unexport/: Removes port portId from the list of exported ports of instance instanceId.

Projects

POST /api/projects/[id]/run/: Runs all instances in the project. Stops processes first if they were already running.

POST /api/projects/[id]/terminate/: Terminates all processes associated with instances in this project.

POST /api/projects/[id]/check/: Checks the project for unbound ports and ports bound to more than one net, which would generate a runtime SystemC error when using the project as a subproject in another project.

POST /api/projects/[id]/compile/: Generates the necessary IPC code as described in section3.3 on page17 for using this project as a subproject in another project, and then compiles the project code as described in section2.1.2on page10.

3.8 User Interface

3.8.1

Project Overview

The project overview page contains an overview of all the projects stored in the database. Here, projects can be opened and new projects can be created — see figure 3.4 on the facing page. Clicking a project or creating a new one will lead to the corresponding project page, described in the next section.

3.8.2

Project Page

The project page is the most important page of the user interface. On this page, the schematic view of the components, their input/output ports, their nets and the state of the connections are visible — see figure3.6 on page24.

(23)

Figure 3.4: The project overview interface. Instances

Module instances are visualized using a rectangle with the name and ID of the instance. In the interest of time, they were not styled to look like real components. The visualization is however done using JavaScript-generated SVG, which makes it trivial to replace the rectangles with arbitrary symbols and images in future development.

Instances can be right-clicked to open a context menu, in which the detail page can be opened (see section 3.8.3 on the following page), the instance can be deleted and its simulation of this

instance can be started/stopped. For an example of an instance, see figure3.5.

Figure 3.5: An example of an instance, with a non-exported input, an exported input and a non-exported output.

Ports

Ports on the components are visualized using smaller, color-coded rectangles: • Blue: Non-exported input (light blue) or output (darker blue).

• Light orange: Exported input (light orange) or output (darker orange).

Ports can be right-clicked to open a context menu, in which their exported status can be toggled. Ports are also visible on the aforementioned example in figure3.5.

Nets

Nets, which connect instance ports, are visualized using lines between instances, and are color-coded to indicate their state — grey to indicate an unknown state, very dark green to represent a low logic value and bright green to represent a high logic value.

Nets can be right-clicked to open a context menu, in which a net can be deleted. Menu

On top of the page, there is a menu composed of several buttons, which will trigger the following actions:

• Run All: This button will send a signal to all instances to start their simulations, first terminating them if already running.

(24)

• Terminate All: This button will send a signal to all instances to terminate their simula-tions.

• Compile All: This button will recompile all instances, including the projects hierarchically included in this project.

• Check: This button will run a test for possible design errors when this project is to be included in another project, most notably unbound ports or doubly connected ports. • Compile Project: This will compile the project for inclusion in other projects. Note

that this button is different from the Compile All button, as that button will compile all instances separately, and this button will bundle all instances in a single executable for inclusion in other projects.

Figure 3.6: The project page, with a simple example simulation with a clock, a D-Flip Flop gate, and a Not gate.

3.8.3

Instance Details

In the context menu of an instance on the Project page, the detail page of an instance can be opened (see figure3.7). On this page, the name and ID of the instance are visible, as well as a link of the module the instance instantiates, a source code preview of this module, a list of the module’s ports and a Run/Terminate button, to start or stop the simulation.

Figure 3.7: The instance detail page, showing an instance of a Not module. Not visible in this screenshot is the Logs section, which shows the console output of the simulation.

(25)

3.8.4

Module Overview

The module overview page contains an overview of all the modules stored in the database. Here, modules can be opened and new modules can be created. Clicking a module or creating a new one will lead to the corresponding module details page, described in the next section. For an example of how the module overview looks, see figure3.8.

Figure 3.8: The module overview page, showing a list of projects.

3.8.5

Module Details

The most important feature of the module details page is editing the source code of a module. It consists of a source code editor, an overview of the module’s ports and buttons to check the source code, compile the source code and delete the module.

To allow for editing the source code, an Ace Editor React component was used [27,35]. This editor supports syntax highlighting and autocompletion. Additionally, the editor constantly sends the source code to the back-end to be checked, so the editor can show up to date information about warnings and errors in the source code — see section3.1.2on page16for the implementation in the backend.

Refer to figure3.9 for an example of what the module details page for a clock module may look like.

(26)
(27)

CHAPTER 4

Conclusion and Future Work

The research question “How can SystemC be used to make a hierarchical, multi-abstraction hardware description tool with an interactive symbolic layer for digital logic components, which can run and updated live and independently of each other?” was split up in the following components in the introduction in chapter1:

• How can multiple levels of abstraction, such as gate-level and RTL be combined, allowing for using them together in one system?

• How can a hierarchy of functional components be implemented, e.g. a component imple-menting a NAND port, which can be used to create an XOR port?

• How can these functional blocks be run independently of each other, to allow for creating, deleting and making connections between them without recompiling and restarting the simulation?

• How can an interactive schematic layer be built, allowing for live interaction with these functional blocks?

To answer this question, the separate components will first be discussed, which will then be used to answer the research question.

The source code for this project can be found on https://gitlab-fnwi.uva.nl/11055987/ thesis.

4.1 Abstraction Levels

SystemC was used as the foundation for simulation, which already has a very flexible choice of abstraction levels. While SystemC 2.0 and up are heavily focused on transaction-level modeling and can be used from register-transfer level and up, it can be used for about any level of abstrac-tion wanted by modeling the basic building blocks those abstracabstrac-tion levels are based on, such as transistors for a transistor-level abstraction, and basic logic gates for gate-level abstraction. Due to the flexibility of SystemC being based on a normal programming language, there is no limitation of combining multiple levels abstractions, be it in the same level of hierarchy or in different ones.

4.2 Independent Component Simulation

To allow for independent creation, deletion and reconnecting of components, SystemC modules were all compiled independently, which can then be started for every instance of this module.

(28)

For communication between the module instances, code was generated and added to the module source code. This generated code used a combination of Cap’n Proto (for serialization) and nanomsg (as a message transport layer) to communicate with the Django back-end, which would then publish the message to all other instances, which then process the messages relevant to them.

An important aspect of this communication that, while communication over sockets is — as a general rule — non-synchronized, SystemC allows for timesteps independent of wall time [8], still allowing for synchronizing the modules themselves.

This turned out to be a very flexible system, which was then also used for the hierarchy of components and the schematic layer.

4.3 Hierarchy of Components

To implement a hierarchy of components, the concept used for independent component simulation was reused, replacing separate instances with entire projects.

For a project to be included as an instance in another project, the instances in the project were compiled as a single instance and code was generated to send and receive signals from ports that were marked as exported. This project then behaved like any other instance.

Some problems did, however, arise when nesting projects more than once. Project instances can have ports that occur more than once. This can happen when two of the same gates get the same ports exported (for example, the result ports on two AND gates). One possible way of solving this is to have a special case for subprojects, addressing their exported ports by their export ID, rather than by their port ID. Due to time constraints, this could not be implemented.

4.4 Schematic Layer

The schematic layer was built as a web app, so it could be run in every web browser. The frontend was built in React, using Next.JS to reduce boilerplate.

To display the schematic layer, an SVG image was generated in the frontend, based on the information requested from the backend when the page is loaded. In addition to this, the frontend also subscribes to the message queue, which is used by the instances to communicate their signals, using nanomsg over WebSockets, so live updates can be displayed.

4.5 Future Work

4.5.1

Researching Alternatives to nanomsg

For communication between modules, nanomsg and Cap’n Proto were used over the nanomsg implemented IPC protocol. While nanomsg is extremely flexible, alternatives could be researched. For example, Cap’n Proto, in addition to its serialization and deserialization functionality, also has a communication protocol (specifically, a remote procedure call (RPC) layer), with, as they name it, three-way interactions:

“A network of Cap’n Proto vats (nodes) can pass object references to each other and automatically form direct connections as needed. For instance, if Alice (on machine A) sends Bob (on machine B) a reference to Carol (on machine C), then machine B will form a new connection to machine C so that Bob can call Carol directly without proxying through machine A.” [39]

Notably the automatic connection management, and the protocol being an RPC layer, should make it possible for the modules to call procedures that set the inputs of the modules directly. This will likely be a much cleaner solution than handling the routing ourselves.

(29)

4.5.2

User Interface

More future work could also be focused on the visualization of instances. Currently, there is no way of visually routing the connections (instead of straight lines) and manipulating the view (zooming and panning), there is no way to interact with the simulation in the form of buttons, switches or other inputs — all interaction has to be done using SystemC code, or by manually connecting and disconnecting instances. Additionally, all instances are visualized using plain rectangles. To make the visualization look more like an actual schematic layer, the possibility to display custom symbols must be added.

Other than the schematic layer, the user interface also needs more thought put into its usability. Currently, the project and module overviews are very simple and only allow for selecting and creating projects or modules. The interface also does not look very attractive — work could be done on a better-looking and more user-friendly design as well.

4.5.3

Performance

While performance was kept in mind at the beginning of the project, the focus on performance was lost as the project progressed. While the current choices do their job for the small-scale applications the project was tested with, they were not tested with bigger scale applications. Further optimization is likely required in future development, especially in the synchronization of instance timestamps.

4.5.4

Features

The tool currently is very basic. This was mostly in the interest of time — in this project, the focus was on both laying down a solid foundation for future work, while providing a proof-of-concept-worthy tool to answer the research question.

One of the current problems, as mentioned earlier in the conclusion, is that projects can’t be nested more than once due to a problem with the database design. This is, however, a relatively simple thing to fix.

Another important focus in the future will be expanding the tool with more functionality, such as the aforementioned user interface improvements, but also support for more signal types, timing diagrams and more.

SystemC also has an extension for modeling analog systems [26]. For this application, this could be a very interesting addition, since it would allow for simulating rise and fall times and glitches more accurately.

4.5.5

FPGA Synthesis

SystemC lends itself to high-level synthesis, which allows for configuring an FPGA to the hard-ware model defined in the SystemC modules. Since the hierarchy functionality generates source code for an entire project as if it were a single SystemC specification, this source code can then be fed into a high-level synthesis tool such as Xilinx Vivado [45].

Research can be done for exporting a project as a Vivado project, or even as a plug-in for Vivado of some kind, creating an ergonomic way of synthesizing a project to an FPGA.

(30)
(31)

Bibliography

[1] Ney Calazans et al. “From VHDL register transfer level to SystemC transaction level modeling: a comparative case study.” In: Oct. 2003, pp. 355–360. isbn: 0-7695-2009-X. doi: 10.1109/SBCCI.2003.1232853.

[2] Görschwin Fey et al. “ParSyC: an efficient SystemC parser.” In: In Workshop on Synthesis

And System Integration of Mixed Information technologies (SASIMI. Citeseer. 2004.

[3] Frank Ghenassia et al. Transaction-level modeling with SystemC. Vol. 2. Springer, 2005, pp. vii–xiii.

[4] Matthieu Moy, Florence Maraninchi, and Laurent Maillet-Contoz. “Pinapa: an extraction tool for SystemC descriptions of systems-on-a-chip.” In: Proceedings of the 5th ACM

inter-national conference on Embedded software. 2005, pp. 317–324.

[5] European Space Agency. System-Level Modeling in SystemC. Apr. 2010. url: https:// www.esa.int/Enabling_Support/Space_Engineering_Technology/Microelectronics/ System-Level_Modeling_in_SystemC.

[6] Kevin Marquet and Matthieu Moy. “PinaVM: a SystemC front-end based on an executable intermediate representation.” In: Proceedings of the tenth ACM international conference on

Embedded software. 2010, pp. 79–88.

[7] IEEE Standards Association et al. IEEE Std. 1666–2011, Open SystemC Language

Refer-ence Manual. IEEE Press, 2011, pp. 562–565.

[8] IEEE Standards Association et al. IEEE Std. 1666–2011, Open SystemC Language

Refer-ence Manual. IEEE Press, 2011, pp. 15–20.

[9] IEEE Standards Association et al. IEEE Std. 1666–2011, Open SystemC Language

Refer-ence Manual. IEEE Press, 2011.

[10] Daniel D Gajski et al. SpecC: Specification language and methodology. Springer Science & Business Media, 2012.

[11] Kazuaki Maeda. “Performance evaluation of object serialization libraries in XML, JSON and binary formats.” In: 2012 Second International Conference on Digital Information and

Communication Technology and it’s Applications (DICTAP). IEEE. 2012, pp. 177–182.

[12] Audie Sumaray and S Kami Makki. “A comparison of data serialization formats for optimal efficiency on a mobile platform.” In: Proceedings of the 6th international conference on

ubiquitous information management and communication. ACM. 2012, p. 48.

[13] Alexander de Graaf. “SystemC: an overview.” University lecture. Sept. 2014. url: http: //bd.eduweb.hhs.nl/es/systemc/SystemC-14v1.pdf.

[14] George Xie. Improving Facebook’s performance on Android with FlatBuffers. July 2015. url: https://engineering.fb.com/android/improving-facebook-s-performance-on-android-with-flatbuffers/.

[15] Python Software Foundation. PEP 498 – Literal String Interpolation. Nov. 2016. url: https://www.python.org/dev/peps/pep-0498/.

[16] Johannes Kohl et al. “A SystemC Based Framework for Cycle Accurate Processor Simula-tion and Parameter Analysis.” In: IFAC-PapersOnLine 49.25 (2016), pp. 92–97.

[17] Maxim Ya Afanasev et al. “Performance evaluation of the message queue protocols to trans-fer binary json in a distributed cnc system.” In: 2017 IEEE 15th International Contrans-ference

(32)

[18] Martin Sústrik. Differences between nanomsg and ZeroMQ. Feb. 2018. url: https : / / nanomsg.org/documentation-zeromq.html.

[19] Python Software Foundation. struct - Interpret bytes as packed binary data. Aug. 2019. url:https://docs.python.org/3/library/struct.html.

[20] Staysail Systems, Inc. NNG readme. Dec. 2019. url:https://github.com/nanomsg/nng/ blob/v1.2.4/README.adoc.

[21] Staysail Systems, Inc. nng(7) Manual Page. July 2019. url: https : / / github . com / nanomsg/nng/blob/v1.2.4/docs/man/nng.7.adoc#transports.

[22] Staysail Systems, Inc. nng_pub(7) Manual Page. July 2019. url:https://nng.nanomsg. org/man/v1.2.2/nng_pub.7.html.

[23] Staysail Systems, Inc. nng_pull(7) Manual Page. July 2019. url:https://nng.nanomsg. org/man/v1.2.2/nng_pull.7.html.

[24] Staysail Systems, Inc. nng_push(7) Manual Page. July 2019. url:https://nng.nanomsg. org/man/v1.2.2/nng_push.7.html.

[25] Staysail Systems, Inc. nng_sub(7) Manual Page. July 2019. url:https://nng.nanomsg. org/man/v1.2.2/nng_sub.7.html.

[26] Accellera Systems Initiative. About the SystemC AMS extensions. url:https://accellera. org/community/systemc/about-systemc-ams.

[27] Amazon Web Services. Cloud9 ACE Editor. url:https://ace.c9.io/.

[28] Apache Software Foundation. ActiveMQ website. url:https://activemq.apache.org/. [29] Carlo Brandolese et al. “Parsing SystemC: an open-source, easy-to-extend parser.” In: [30] Django Software Foundation. Django. url:https://www.djangoproject.com/.

[31] Django Software Foundation. Models and databases. url:https://docs.djangoproject. com/en/3.0/topics/db/.

[32] Encode OSS Ltd. Django REST framework. url:https://www.django-rest-framework. org/.

[33] Facebook Open Source. React. url:https://reactjs.org/.

[34] Google. Protocol Buffers. url:https://developers.google.com/protocol-buffers. [35] James Hrisho. React-Ace. url:https://github.com/securingsincity/react-ace. [36] Pallets. Template Designer Documentation — Whitespace control. url: https://jinja.

palletsprojects.com/en/2.11.x/templates/#whitespace-control. [37] Pivotal Software, Inc. RabbitMQ website. url:https://www.rabbitmq.com/. [38] Sandstorm.io. Cap’n Proto. url:https://capnproto.org/.

[39] Sandstorm.io. Cap’n Proto: RPC protocol. url:https://capnproto.org/rpc.html. [40] Sandstorm.io. Cap’n Proto: Schema language. url:https://capnproto.org/language.

html.

[41] The Clang Team. Choosing the Right Interface for Your Application. url:https://clang. llvm.org/docs/Tooling.html.

[42] The Clang Team. Clang Python bindings. url:https://github.com/llvm/llvm-project/ tree/master/clang/bindings/python.

[43] The Clang Team. clang: Diagnostic reporting. url: https://clang.llvm.org/doxygen/ group__CINDEX__DIAG.html.

[44] The Django template language. url: https://docs.djangoproject.com/en/3.0/ref/ templates/language/.

[45] Xilinx. Xilinx Vivado Design Suite: Project page. url: https : / / www . xilinx . com / products/design-tools/vivado.html.

[46] Matt Zabriskie. Axios. url:https://github.com/axios/axios. [47] ZEIT, Inc. Next.js. url:https://nextjs.org/.

Referenties

GERELATEERDE DOCUMENTEN

Scale, Assen project. Q: Would MaaS be more successful on a regional scale? Instead of just a city scale for somewhere like Assen? So that you include the rural areas.. Table

Worden in Groot-Brittannie en in Frankrijk rond 1947 de eerste ideeen omtrent nationale ontwikkelingsprogramma's voor kern- elektrische centrales geformuleerd, in de

Een tweede voorwaarde is dat, indien het voertuig niet op het knooppunt moet laden of lossen, het direct verder kan reizen naar een volgend segment zodat het

The method used for localization in this project, is based on the assump- tion that it should be possible to compare images from the real RGB- camera on the robot with images taken

V(A) est une valeur de vérité désignée. Dans L 3 il y a seulement une valeur de vérité désignée, à savoir ‘1’, mais dans d’autres systèmes de

1) Dialogical Logic is an ideal framework for the combination of different logics. 2) Concerning the formulation of non-classical logics dialogical logic offers a fruitful

In other hands, the following makes TEXT 2 invisible to everybody: \begin{shownto}{execs} TEXT 1 \begin{shownto}{devs} TEXT 2 \end{shownto} \end{shownto}2. 2.3 Commands

Our expectations have been heightened by the occasional references in Byzantine and Prankish texts to locatable sites hi our survey region, but chiefly by the remarkable detail