Jump to content
Roman Popov

Converting sc_signal<T1> to sc_signal<T2>

Recommended Posts

Hello,

I need to connect Verilated model into SystemC testbench .

  • Testbench was written for bit-accurate types like ( sc_in<sc_int<exact_bus_width> > ).
  • Verilator uses sc_in<uint32_t>  and sc_in<uint64_t>  for all buses that are under 64 bits in width.

How can I efficiently convert between two?

I know there is a solution with additional SC_METHOD that converts from T1 to T2,  but it is unfortunately affecting simulation performance, since I'm introducing another method and signal to design. 

 

Instead I've implemented converter from  sc_signal_inout_if<T1> to  sc_signal_inout_if<T2>  and it worked fine for my simulation.  But there is a potential problem with a read() method, since it returns a reference:
 

template <typename Treal, typename Tcasted>

class signal_interface_converter : public sc_signal_inout_if<Tcasted> {

  const Tcasted &read() const override {
    temp_val = host_ptr->read();
    return temp_val;
  }

  private:
  Tcasted temp_val;
  sc_signal_inout_if<Treal> * host_ptr;
}

In Verilator integration context it looks safe:  Verilator converts whole design into a single SC_METHOD so it should not reuse reference between different delta cycles.

However in general I can write a code like that:

auto & address_ref = addr_signal.read();

wait();
memory[address] = 42;
// address_ref may have changed after wait.

So basically returning value by reference in read() disables a potential optimization for sc_signal converters :(

And the benefits of returning a reference are not clear to me.

 

Share this post


Link to post
Share on other sites

Hi Roman,

Why reference? Because handing over variabels as const reference should be default whenever you don't have a good reason to do something else. When a signal contains a complex data type and not only an int, you do not want to copy it with every read.

From your example, I am not sure if I got your scenario well.

So, returning a const reference is normally not a problem. It is the same as normal sc_signal does.

If your problem is that '*host_ptr' changes without temp_val being updated, you need to get grip on its write or update method and implement a write-through to temp_val. And this leads you to implementing a channel.

Actually you did implement something like a primitive channel. So, implementing a full primitive channel that allows to be bound to different types could help. But this requires some thoughts and effort. Or you do it the easy, classical way with an hierarchical conversion channel.  I would not expect the performance impact of the additional method to be very critical. In some cases, the additional delta cycle can be more problematic, mainly when this affects the clock edge.

In both cases, you can benefit from knowing the direction of data transfer and conversion and reduce effort in comparison to using normal signals.

Greetings

Ralph

 

Share this post


Link to post
Share on other sites

Hi Ralph,

Quote

Why reference? Because handing over variabels as const reference should be default whenever you don't have a good reason to do something else. When a signal contains a complex data type and not only an int, you do not want to copy it with every read.

Compiler should optimize a copy when possible. So it should not be a performance bottleneck.  

Quote

If your problem is that '*host_ptr' changes without temp_val being updated, you need to get grip on its write or update method and implement a write-through to temp_val. And this leads you to implementing a channel.

Its not my problem, but it is a problem in general.  A write-through to temp_val will be a SC_METHOD sensitive to value_change_event().  This will be a performance problem when a lot of traffic goes through such a converter

Quote

 In some cases, the additional delta cycle can be more problematic, mainly when this affects the clock edge.

Yes, this is another issue.

 

Share this post


Link to post
Share on other sites

Any eventual copy returned from read() by value will never be optimized away, as the source location continues to exist in the signal.

The simplest solution is to change your signal converter as follows (untested):

template <typename Treal, typename Tcasted>
class signal_interface_converter
   : public sc_core::sc_signal<Treal>
   , public sc_core::sc_signal_in_if<Tcasted> // only read allowed
{
  typedef sc_core::sc_signal<Treal> real_type;
public:
  explicit signal_interface_converter(const char* nm)
    : real_type(nm), casted_val() {}

  const Tcasted &read() const override {
    return casted_val;
  }

private:
  void update() override {
    real_type::update();
    casted_val = static_cast<Tcasted>(real_type::read());
  ]

  Tcasted casted_val;
};

So the idea is basically to leverage the update phase to update the casted value. If the above doesn't work due to read() overloads based on the return type only, you may need to wrap it differently to separate the two conflicting interfaces (e.g. by using an internal signal with an overridden update() function, triggering the update of the casted value in the converter).

Hope that helps,
  Philipp

Share this post


Link to post
Share on other sites
Quote

Any eventual copy returned from read() by value will never be optimized away, as the source location continues to exist in the signal.

Yes, but in most cases copy is not created , but used as rvalue in computations.

Most likely end user can do something like:

auto val = some_signal.read();  that also creates a copy anyway.

Quote

So the idea is basically to leverage the update phase to update the casted value.

Great idea. Unfortunately it is not possible to apply it for hierarchical bindings, that are also quite common case where type conversion happes, i.e. when you bind sc_in<T1> to  sc_in<T2>

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×