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