1//
2// Copyright (c) 2010-2024 Antmicro
3//
4// This file is licensed under the MIT License.
5// Full license text is available in 'licenses/MIT.txt'.
6//
7
8`timescale 1ns / 1ps
9
10package renode_pkg;
11  typedef longint address_t;
12  typedef longint data_t;
13
14  typedef enum int {
15    // The included file contains enumerators of the action type used by the Renode protocol.
16    // The values must be in sync with ActionType from Renode (defined in C#).
17    `include "renode_action_enumerators.svh"
18  } action_e;
19
20  const int no_peripheral_index = -1;
21
22  typedef struct {
23    action_e action;
24    address_t address;
25    data_t data;
26    int peripheral_index;
27  } message_t;
28
29  typedef enum data_t {
30    QuadWord = {64{1'b1}},
31    DoubleWord = {32'b0, {32{1'b1}}},
32    Word = {48'b0, {16{1'b1}}},
33    Byte = {56'b0, {8{1'b1}}}
34  } valid_bits_e;
35
36  typedef enum int {
37    LogNoisy = -1,
38    LogDebug = 0,
39    LogInfo = 1,
40    LogWarning = 2,
41    LogError = 3
42  } log_level_e;
43
44  import "DPI-C" function void renodeDPIConnect(
45    int receiverPort,
46    int senderPort,
47    string address
48  );
49
50  import "DPI-C" function void renodeDPIDisconnect();
51
52  import "DPI-C" function bit renodeDPIIsConnected();
53
54  import "DPI-C" function bit renodeDPILog(
55    int logLevel,
56    string data
57  );
58
59  import "DPI-C" function bit renodeDPIReceive(
60    output action_e action,
61    output address_t address,
62    output data_t data,
63    output int peripheralIndex
64  );
65
66  import "DPI-C" function bit renodeDPISend(
67    action_e action,
68    address_t address,
69    data_t data,
70    int peripheralIndex
71  );
72
73  import "DPI-C" function bit renodeDPISendToAsync(
74    action_e action,
75    address_t address,
76    data_t data,
77    int peripheralIndex
78  );
79
80  function static bit is_access_aligned(address_t address, valid_bits_e valid_bits);
81    case(valid_bits)
82      Byte: return 1;
83      Word: return address % 2 == 0;
84      DoubleWord: return address % 4 == 0;
85      QuadWord: return address % 8 == 0;
86      default: return 0;
87    endcase
88  endfunction
89
90  function static integer valid_bits_to_transaction_width(valid_bits_e valid_bits);
91    case (valid_bits)
92      QuadWord: return 64;
93      DoubleWord: return 32;
94      Word: return 16;
95      Byte: return 8;
96      default: begin
97          $error($sformatf("Cannot determine transaction width for valid_bits %d", valid_bits));
98          return 0;
99      end
100    endcase
101  endfunction
102
103  class renode_connection;
104    semaphore exclusive_receive = new(1);
105
106    function new();
107      $timeformat(0, 9, "s", 0);
108    endfunction
109
110    function void connect(int receiver_port, int sender_port, string address);
111      renodeDPIConnect(receiver_port, sender_port, address);
112      if(is_connected())
113        $display("Renode at %t: Connected using the socket based interface", $realtime);
114      else
115        $error("Renode at %t: Connection error", $realtime);
116    endfunction
117
118    function bit is_connected();
119      return renodeDPIIsConnected();
120    endfunction
121
122    function void fatal_error(string message);
123      string error_msg;
124      error_msg = $sformatf("Renode at %t: Error! %s", $realtime, message);
125      log(LogError, error_msg);
126      disconnect();
127      $error(error_msg);
128      $finish;
129    endfunction
130
131    function void handle_message(message_t message, output bit is_handled);
132      is_handled = 1;
133      case (message.action)
134        renode_pkg::invalidAction: ;  // Intentionally left blank
135        renode_pkg::disconnect: handle_disconnect();
136        default: is_handled = 0;
137      endcase
138    endfunction
139
140    function void log(log_level_e log_level, string message);
141`ifdef RENODE_DEBUG
142      $display("Renode at %t logs: %s", $realtime, message);
143`endif
144      if(!renodeDPILog(log_level, message)) begin
145        $display("Renode at %t: Unable to send the log: %s", $realtime, message);
146      end
147    endfunction
148
149    function void receive(output message_t message);
150      bit is_received = try_receive(message);
151      if (!is_received) fatal_error("Unable to receive a message.");
152    endfunction
153
154    function bit try_receive(output message_t message);
155      bit is_received = renodeDPIReceive(message.action, message.address, message.data, message.peripheral_index);
156      if(is_received) begin
157`ifdef RENODE_DEBUG
158      log(LogDebug, $sformatf("Renode at %t: Received action %0s, address = 'h%h, data = 'h%h, peripheral index: %d", $realtime, message.action.name(), message.address, message.data, message.peripheral_index));
159`endif
160      end
161      return is_received;
162    endfunction
163
164    function void send(message_t message);
165`ifdef RENODE_DEBUG
166      log(LogDebug, $sformatf("Renode at %t: Sent action %0s, address = 'h%h, data = 'h%h, peripheral index: %d", $realtime, message.action.name(), message.address, message.data, message.peripheral_index));
167`endif
168      if (!renodeDPISend(message.action, message.address, message.data, message.peripheral_index)) fatal_error("Unexpected channel disconnection");
169    endfunction
170
171    function void send_to_async_receiver(message_t message);
172`ifdef RENODE_DEBUG
173      log(LogDebug, $sformatf("Renode at %t: Sent async action %0s, address = 'h%h, data = 'h%h, peripheral index: %d", $realtime, message.action.name(), message.address, message.data, message.peripheral_index));
174`endif
175      if (!renodeDPISendToAsync(message.action, message.address, message.data, message.peripheral_index)) fatal_error("Unexpected channel disconnection");
176    endfunction
177
178    local function void disconnect();
179      renodeDPIDisconnect();
180    endfunction
181
182    local function void handle_disconnect();
183      send(message_t'{ok, 0, 0, renode_pkg::no_peripheral_index});
184      disconnect();
185`ifdef RENODE_DEBUG
186      $display("Renode at %t: disconnected", $realtime);
187`endif
188    endfunction
189  endclass
190
191  class bus_connection;
192    event reset_assert_request;
193    event reset_assert_response;
194    event reset_deassert_request;
195    event reset_deassert_response;
196
197    event read_transaction_request;
198    event read_transaction_response;
199    address_t read_transaction_address;
200    data_t read_transaction_data;
201    valid_bits_e read_transaction_data_bits;
202    bit read_transaction_is_error;
203
204    event write_transaction_request;
205    event write_transaction_response;
206    address_t write_transaction_address;
207    data_t write_transaction_data;
208    valid_bits_e write_transaction_data_bits;
209    bit write_transaction_is_error;
210
211    // Passing a class by a reference isn't supported by Verilator.
212    // Events are indirectly triggered by tasks.
213    task reset_assert();
214      ->reset_assert_request;
215      @(reset_assert_response);
216    endtask
217
218    task reset_assert_respond();
219      ->reset_assert_response;
220    endtask
221
222    task reset_deassert();
223      ->reset_deassert_request;
224      @(reset_deassert_response);
225    endtask
226
227    task reset_deassert_respond();
228      ->reset_deassert_response;
229    endtask
230
231    task read(address_t address, valid_bits_e data_bits, output data_t data, output bit is_error);
232      read_transaction_address = address;
233      read_transaction_data_bits = data_bits;
234      ->read_transaction_request;
235      @(read_transaction_response) begin
236        data = read_transaction_data;
237        is_error = read_transaction_is_error;
238      end
239    endtask
240
241    task read_respond(data_t data, bit is_error);
242      read_transaction_data = data;
243      read_transaction_is_error = is_error;
244      ->read_transaction_response;
245    endtask
246
247    task write(address_t address, valid_bits_e data_bits, data_t data, output bit is_error);
248      write_transaction_address = address;
249      write_transaction_data_bits = data_bits;
250      write_transaction_data = data;
251      ->write_transaction_request;
252      @(write_transaction_response) begin
253        is_error = write_transaction_is_error;
254      end
255    endtask
256
257    task write_respond(bit is_error);
258      write_transaction_is_error = is_error;
259      ->write_transaction_response;
260    endtask
261  endclass
262
263  // It's required to pass the whole instance to modules.
264  // Passing single property triggers a null pointer dereference in Verilator.
265  class renode_runtime;
266    const string ReceiverPortArgName = "RENODE_RECEIVER_PORT";
267    const string SenderPortArgName = "RENODE_SENDER_PORT";
268    const string AddressArgName = "RENODE_ADDRESS";
269
270    renode_connection connection = new();
271
272    // Initialized by the Renode module
273    bus_connection controllers[];
274    bus_connection peripherals[];
275
276    function void connect_plus_args();
277      int receiver_port, sender_port;
278      string address;
279      if(!$value$plusargs({ReceiverPortArgName, "=%d"}, receiver_port)
280        || !$value$plusargs({SenderPortArgName, "=%d"}, sender_port)
281        || !$value$plusargs({AddressArgName, "=%s"}, address))
282      begin
283          $error("Please specify the +%s, +%s and +%s arguments in the command that invokes the simulation", ReceiverPortArgName, SenderPortArgName, AddressArgName);
284      end
285      else begin
286        connection.connect(receiver_port, sender_port, address);
287      end
288    endfunction
289
290    function bit is_connected();
291      return connection.is_connected();
292    endfunction
293  endclass
294endpackage
295