Jump to content

Using sc_uint_base directly?


Roman Popov

Recommended Posts

Is it ok to use sc_uint_base directly, instead of template sc_uint<W> ?

In particular I need to model a vector of buses of variable length.  I'm thinking of modeling it like this:

static const int VEC_SIZE = 3;
static const int WIDTHS[VEC_SIZE] = {10, 20, 40};

SC_MODULE(top) {

    sc_vector<sc_signal<sc_uint_base>> sig_vec{"sig_vec", VEC_SIZE,
                                               [&](const char *name, size_t i)
                                               { return new sc_signal<sc_uint_base>(name, {0, WIDTHS[i]}); }};

    sc_vector<sc_out<sc_uint_base>> out_port_vec{"out_port_vec", VEC_SIZE};

    SC_CTOR(top) {
        out_port_vec.bind(sig_vec);
        SC_THREAD(test_thread);
    }

    void test_thread() {
        for (auto & p : out_port_vec)
            cout <<"width: " <<  p.read().length() << " value: " << p << endl;

        unsigned i = 10;

        for (auto & p : out_port_vec)
            p.write({i++,  p.read().length()});

        wait(1, SC_NS);

        for (auto & p : out_port_vec)
            cout <<"width: " <<  p.read().length() << " value: " << p << endl;
    }

};

Was it supposed to be used like this by SystemC authors?

Link to comment
Share on other sites

I don't see any issue from the point of the LRM. From a methodology perspective, I think it depends on the abstraction level and use case you are targeting. On the side of modules representing actual hardware, I would probably prefer to have a port type stating clearly the width to have the compiler statically check for me the correct binding of ports and interfaces. For stimuli generators and monitors, a more generic base type is OK for me. Where it then interfaces with HW modules, I would insert a small adaptor. You may also consider to have a channel, which can bind to sc_int<W> and sc_int_base, and which then checks at elaboration time that the widths on all bound ports match.

Link to comment
Share on other sites

Well, I want to use it for RTL bus fabric model. Number of bus slaves is parameterizable, and each slave have different address space size.

So the other option I have here is to pick some "one size fits all" value for address bus width, like

  typedef sc_uint<64> bus_addr_t

In that case I rely on synthesis tool to optimize bus width based on address decoding logic in slaves. 

I can help synthesis tool to by explicitly reducing bus address width in slave like this:

<template unsigned WIDTH>
struct bus_slave : sc_module {
    sc_in<sc_uint<64>>         bus_addr{"bus_addr"};
    sc_signal<sc_uint<WIDTH>>  effective_bus_addr{"effective_bus_addr"};

    SC_METHOD_INST(shrink_addr, sensitive << bus_addr;) {
        effective_bus_addr = bus_addr;
    }
}

But when cross-boundary optimizations are disabled, it will still have to emit 64 wires for address to gate-level netlist.  If fabric slave port has a register buffer for address, it will also be 64-bits, instead of some slave-specific width.

So sc_uint_base seem to me as the only solution now (if supported by tools).  

 

Quote

You may also consider to have a channel, which can bind to sc_int<W> and sc_int_base, and which then checks at elaboration time that the widths on all bound ports match.

Unfortunately I don't know how to support user-defined channel classes for synthesis. Most tools support only sc_signal<T>.  And it is not possible to bind sc_out<sc_uint_base> to sc_signal<sc_uint<W>>, despite sc_uint is derived from sc_uint_base.

 

Link to comment
Share on other sites

I'm not an expert, but my understanding is that other HDLs built on top of general-purpose programming languages like MyHDL (Python) and Chisel (Scala) support this idea of runtime-defined bitwidths. It is very powerful when you need to create a configurable modules like fabrics,routers, caches etc. Chisel authors even advertise it as "Hardware construction language" instead of "Hardware description language", to emphasize that structure of hardware is created at runtime, instead of being hardcoded manually.

SystemC also has hidden hardware construction capabilities like sc_uint_base (bitwidth defined at elaboration time), sc_vector (size can be defined during elaboration time, instead of compile-time). But this approach is not marketed widely yet (and not supported by most synthesis tools). 

Link to comment
Share on other sites

First of all, we need to distinguish between SystemC in general and its synthesizable subset.  Whenever something is not explicitly covered by the current synthesizable subset, it's probably best to include the HLS vendors in the discussion.  This almost certainly includes sc_signal<sc_uint_base>.  Vendors may support this as signal type, if they can derive the width of the signal (statically) during elaboration.  But in a fully generic fabric model, this might be difficult.

IIRC, the main difference between SystemC and MyHDL or Chisel is that the latter basically create an explicit "netlist" in memory during elaboration.  Basically, the synthesis rules/tool is already embedded in the language (kernel) itself, knowing how to generate the elaborated model to Verilog from memory.  In SystemC, a synthesis tool is implemented more like a compiler, i.e. working at the language level directly.  These are very different approaches with different benefits and limitations.

When you refer to "runtime-defined bitwidths", you certainly mean "elaboration-defined", because the widths cannot change once the simulation has started.  But sc_uint_base doesn't know about elaboration/simulation. In order to reduce the risk of width mismatches, you would need a custom channel as suggested by Torsten.  But as you said, such custom channels are not supported well by HLS tools.

Long story short: Having an "elaboration-sized" signal could definitely help for HLS use cases like yours.  The simulation model could be implemented quite easily based on a signal specialization for sc_(u)int_base/sc_(u)nsigned.  But as long as HLS tools doesn't support it, its usage would be fairly limited. Have you talked to HLS vendors about this already?

Last, but not least, I think that C++17 constexpr, especially in combination with if constexpr and auto return type deduction, will provide most of the required features to write generic "hardware construction code" efficiently.  Such models would then automatically be synthesizable by an C++17 capable HLS tool, as the code is evaluated during compile time already.

Link to comment
Share on other sites

6 hours ago, Philipp A Hartmann said:

First of all, we need to distinguish between SystemC in general and its synthesizable subset.  Whenever something is not explicitly covered by the current synthesizable subset, it's probably best to include the HLS vendors in the discussion.  This almost certainly includes sc_signal<sc_uint_base>.  Vendors may support this as signal type, if they can derive the width of the signal (statically) during elaboration.  But in a fully generic fabric model, this might be difficult.

IIRC, the main difference between SystemC and MyHDL or Chisel is that the latter basically create an explicit "netlist" in memory during elaboration.  Basically, the synthesis rules/tool is already embedded in the language (kernel) itself, knowing how to generate the elaborated model to Verilog from memory.  In SystemC, a synthesis tool is implemented more like a compiler, i.e. working at the language level directly.  These are very different approaches with different benefits and limitations.

I think this is true for Chisel, but not for MyHDL. 

Chisel is indeed uses Scala syntax overload magic to construct netlist out of library predefined primitives. But this makes Chisel somewhat inferior to Verilog/SystemC/MyHDL, because it does not support synthesizable behavioral blocks (AFAIK). 

MyHDL is essentially the same idea as SystemC, but implemented in dynamic interpreted language. To generate Verilog it uses both Python reflection capabilities, but also has a "compiler" for behavioral blocks.

SystemC synthesis tools are not just a compilers. They also have built-in rudimentary C++ interpreters, to comprehend module constructor code, where binding and process creation happens.

Quote

Whenever something is not explicitly covered by the current synthesizable subset, it's probably best to include the HLS vendors in the discussion.  

Well, I think they read this forum periodically. But there is no button "Include HLS vendors". 

The problem is that synthesizable SystemC subset is essentially a common denominator of what HLS tools already support.  So any real HLS tool is more powerful than syn subset.

To support "hardware construction" in synthesizable subset SystemC synthesis should draw inspiration from MyHDL. I can reformulate syn subset like this:

  1. During elaboration-time everything is allowed. I.e. constructor code and phase callbacks code can be arbitrary C++.
  2. All SystemC integer datatypes are supported. This gives elaboration-time defined bit-width.
  3. sc_vector is supported.   This gives elaboration-time sized container.  But only for sc_objects.  What if I want elaboration-time defined ROM?
  4. Support elaboration time constants and constant vectors.  Currently missing in SystemC library
  5. SC_METHOD/SC_THREADs are restricted the same way as they are today.

To support this subset real C++ interpreter like Cling should be integrated into HLS tool. Another option would be using introspection information like MyHDL does. C++ does not have a standardized one, but Clang and G++ can dump DWARF debug information that is sufficient to extract netlist of SystemC design together with values of all data fields (including constants, sizes of containers, bit-widths ...) as demonstrated by simulator tools based on GDB.

Quote

Last, but not least, I think that C++17 constexpr, especially in combination with if constexpr and auto return type deduction, will provide most of the required features to write generic "hardware construction code" efficiently.  Such models would then automatically be synthesizable by an C++17 capable HLS tool, as the code is evaluated during compile time already.

I don't see how if constexpr helps in this particular case.  Can you please elaborate on a small example?

I agree that compile-time programming is powerful and solves most of hardware parameterization cases. We use it everywhere indeed.

Originally I also thought that compile-time meta-programming is a solution for this case too. It is indeed possible to construct modules recursively the way like std::tuple is implemented.

But there are two issues:

  • TMP code is hard to comprehend.
  • It's not possible to iterate compile-time data structures at runtime ( inside THREADS/METHODS).

Behavioral code that uses sc_vector of elaboration-time defined buses is much easier to write and comprehend. 

 

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...