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
10import renode_pkg::renode_runtime, renode_pkg::bus_connection, renode_pkg::renode_connection, renode_pkg::no_peripheral_index;
11import renode_pkg::message_t, renode_pkg::address_t, renode_pkg::data_t, renode_pkg::valid_bits_e;
12
13module renode #(
14    int unsigned RenodeToCosimCount = 0,
15    int unsigned CosimToRenodeCount = 0,
16    int unsigned RenodeInputsCount = 1,
17    int unsigned RenodeOutputsCount = 1
18) (
19    ref renode_runtime runtime,
20    input logic clk,
21    input logic [RenodeInputsCount-1:0] renode_inputs,
22    output logic [RenodeOutputsCount-1:0] renode_outputs
23);
24  time renode_time = 0;
25
26  event reset_assert_all;
27  int reset_assert_done_count;
28  event reset_assert_done;
29
30  event reset_deassert_all;
31  int reset_deassert_done_count;
32  event reset_deassert_done;
33
34  renode_inputs #(
35      .InputsCount(RenodeInputsCount)
36  ) gpio (
37      .runtime(runtime),
38      .clk(clk),
39      .inputs(renode_inputs)
40  );
41
42  initial begin
43    runtime.controllers = new[RenodeToCosimCount];
44    foreach(runtime.controllers[i]) begin
45      runtime.controllers[i] = new();
46    end
47
48    runtime.peripherals = new[CosimToRenodeCount];
49    foreach(runtime.peripherals[i]) begin
50      runtime.peripherals[i] = new();
51    end
52  end
53
54  if(CosimToRenodeCount > 0) begin
55    genvar i;
56    for(i = 0; i < CosimToRenodeCount; i += 1) begin
57      always @(runtime.peripherals[i].read_transaction_request) read_transaction(i);
58      always @(runtime.peripherals[i].write_transaction_request) write_transaction(i);
59      always @(reset_assert_all) runtime.peripherals[i].reset_assert();
60      always @(runtime.peripherals[i].reset_assert_response) begin
61          reset_assert_done_count++;
62          if(reset_assert_done_count == (CosimToRenodeCount + RenodeToCosimCount)) begin
63              ->reset_assert_done;
64          end
65      end
66      always @(runtime.peripherals[i].reset_deassert_response) begin
67          reset_deassert_done_count++;
68          if(reset_deassert_done_count == (CosimToRenodeCount + RenodeToCosimCount)) begin
69              ->reset_deassert_done;
70          end
71      end
72      always @(reset_deassert_all) runtime.peripherals[i].reset_deassert();
73    end
74  end
75
76  if(RenodeToCosimCount > 0) begin
77    genvar i;
78    for(i = 0; i < RenodeToCosimCount; i += 1) begin
79      always @(reset_assert_all) runtime.controllers[i].reset_assert();
80      always @(reset_deassert_all) runtime.controllers[i].reset_deassert();
81      always @(runtime.controllers[i].reset_assert_response) begin
82          reset_assert_done_count++;
83          if(reset_assert_done_count == (CosimToRenodeCount + RenodeToCosimCount)) begin
84              ->reset_assert_done;
85          end
86      end
87      always @(runtime.controllers[i].reset_deassert_response) begin
88          reset_deassert_done_count++;
89          if(reset_deassert_done_count == (CosimToRenodeCount + RenodeToCosimCount)) begin
90              ->reset_deassert_done;
91          end
92      end
93    end
94  end
95
96  task static receive_and_handle_message();
97    message_t message;
98    bit did_receive;
99
100    // This task doesn't block elapse of a simulation time, when messages are being received and handled in an other place.
101    if (runtime.connection.exclusive_receive.try_get() != 0) begin
102      did_receive = runtime.connection.try_receive(message);
103      runtime.connection.exclusive_receive.put();
104      if (did_receive) handle_message(message);
105    end
106  endtask
107
108  task static handle_message(message_t message);
109    bit is_handled;
110
111    is_handled = 1;
112    case (message.action)
113      renode_pkg::resetPeripheral: reset();
114      renode_pkg::tickClock: sync_time(time'(message.data));
115      renode_pkg::interrupt: handle_renode_output(message.address, message.data[0]);
116      renode_pkg::writeRequestQuadWord: write_to_bus(message.address, renode_pkg::QuadWord, message.data, message.peripheral_index);
117      renode_pkg::writeRequestDoubleWord: write_to_bus(message.address, renode_pkg::DoubleWord, message.data, message.peripheral_index);
118      renode_pkg::writeRequestWord: write_to_bus(message.address, renode_pkg::Word, message.data, message.peripheral_index);
119      renode_pkg::writeRequestByte: write_to_bus(message.address, renode_pkg::Byte, message.data, message.peripheral_index);
120      renode_pkg::readRequestQuadWord: read_from_bus(message.address, renode_pkg::QuadWord, message.peripheral_index);
121      renode_pkg::readRequestDoubleWord: read_from_bus(message.address, renode_pkg::DoubleWord, message.peripheral_index);
122      renode_pkg::readRequestWord: read_from_bus(message.address, renode_pkg::Word, message.peripheral_index);
123      renode_pkg::readRequestByte: read_from_bus(message.address, renode_pkg::Byte, message.peripheral_index);
124      default: is_handled = 0;
125    endcase
126
127    if (!is_handled) runtime.connection.handle_message(message, is_handled);
128    if (!is_handled) runtime.connection.log(renode_pkg::LogWarning, $sformatf("Trying to handle the unsupported action (%0s)", message.action.name()));
129  endtask
130
131  task static reset();
132    // Nothing to reset, return immediately.
133    if(RenodeToCosimCount + CosimToRenodeCount == 0) return;
134
135    // The reset just locks the connection without using it to avoid an unexpected behaviour.
136    // It also prevents from a message handling in the receive_and_handle_message until a reset deassertion.
137    runtime.connection.exclusive_receive.get();
138
139    reset_assert_done_count = 0;
140    #1 fork
141        ->reset_assert_all;
142        gpio.reset_assert();
143    join
144
145    @(reset_assert_done);
146
147    // It's required to make values of all signals known (different than `x`) before a deassertion of resets.
148    // The assignment to renode_outputs is an equivalent of a reset assertion.
149    renode_outputs = 0;
150
151    reset_deassert_done_count = 0;
152    fork
153      ->reset_deassert_all;
154      gpio.reset_deassert();
155    join
156
157    @(reset_deassert_done);
158
159    runtime.connection.exclusive_receive.put();
160  endtask
161
162  task static sync_time(time time_to_elapse);
163    renode_time = renode_time + time_to_elapse;
164    while ($time < renode_time) @(clk);
165
166    runtime.connection.send_to_async_receiver(message_t'{renode_pkg::tickClock, 0, 0, renode_pkg::no_peripheral_index});
167    runtime.connection.log(renode_pkg::LogNoisy, $sformatf("Simulation time synced to %t", $realtime));
168  endtask
169
170  task automatic read_from_bus(address_t address, valid_bits_e data_bits, int peripheral_index);
171    data_t data = 0;
172    bit is_error = 0;
173    runtime.controllers[peripheral_index].read(address, data_bits, data, is_error);
174
175    if (is_error) runtime.connection.send(message_t'{renode_pkg::error, 0, 0, peripheral_index});
176    else runtime.connection.send(message_t'{renode_pkg::readRequest, address, data, peripheral_index});
177  endtask
178
179  task automatic write_to_bus(address_t address, valid_bits_e data_bits, data_t data, int peripheral_index);
180    bit is_error = 0;
181    runtime.controllers[peripheral_index].write(address, data_bits, data, is_error);
182
183    if (is_error) runtime.connection.send(message_t'{renode_pkg::error, 0, 0, peripheral_index});
184    else runtime.connection.send(message_t'{renode_pkg::ok, 0, 0, peripheral_index});
185  endtask
186
187  task automatic read_transaction(int peripheral_index);
188    message_t message;
189
190    case (runtime.peripherals[peripheral_index].read_transaction_data_bits)
191      renode_pkg::Byte: message.action = renode_pkg::getByte;
192      renode_pkg::Word: message.action = renode_pkg::getWord;
193      renode_pkg::DoubleWord: message.action = renode_pkg::getDoubleWord;
194      renode_pkg::QuadWord: message.action = renode_pkg::getQuadWord;
195      default: begin
196        runtime.connection.fatal_error($sformatf("Renode doesn't support access with the 'b%b mask from a bus controller.", runtime.peripherals[peripheral_index].read_transaction_data_bits));
197        runtime.peripherals[peripheral_index].read_respond(0, 1);
198        return;
199      end
200    endcase
201    message.address = runtime.peripherals[peripheral_index].read_transaction_address;
202    message.data = 0;
203
204    runtime.connection.exclusive_receive.get();
205    if(!runtime.connection.is_connected()) begin
206        runtime.connection.exclusive_receive.put();
207        return;
208    end
209
210    runtime.connection.send_to_async_receiver(message);
211
212    runtime.connection.receive(message);
213    while (message.action != renode_pkg::writeRequest) begin
214      handle_message(message);
215      if(message.action == renode_pkg::disconnect) break;
216      runtime.connection.receive(message);
217    end
218
219    runtime.connection.exclusive_receive.put();
220    runtime.peripherals[peripheral_index].read_respond(message.data, 0);
221  endtask
222
223  task automatic write_transaction(int peripheral_index);
224    message_t message;
225
226    case (runtime.peripherals[peripheral_index].write_transaction_data_bits)
227      renode_pkg::Byte: message.action = renode_pkg::pushByte;
228      renode_pkg::Word: message.action = renode_pkg::pushWord;
229      renode_pkg::DoubleWord: message.action = renode_pkg::pushDoubleWord;
230      renode_pkg::QuadWord: message.action = renode_pkg::pushQuadWord;
231      default: begin
232        runtime.connection.fatal_error($sformatf("Renode doesn't support access with the 'b%b mask from a bus controller.", runtime.peripherals[peripheral_index].read_transaction_data_bits));
233        runtime.peripherals[peripheral_index].write_respond(1);
234        return;
235      end
236    endcase
237    message.address = runtime.peripherals[peripheral_index].write_transaction_address;
238    message.data = runtime.peripherals[peripheral_index].write_transaction_data;
239
240    runtime.connection.exclusive_receive.get();
241    if(!runtime.connection.is_connected()) begin
242        runtime.connection.exclusive_receive.put();
243        return;
244    end
245
246    runtime.connection.send_to_async_receiver(message);
247    runtime.connection.receive(message);
248    while (message.action != renode_pkg::pushConfirmation) begin
249      handle_message(message);
250      if(message.action == renode_pkg::disconnect) break;
251      runtime.connection.receive(message);
252    end
253
254    runtime.connection.exclusive_receive.put();
255
256    runtime.peripherals[peripheral_index].write_respond(0);
257  endtask
258
259  // calculate number of bits needed to hold the output number
260  `define max(a,b) (a > b) ? a : b
261  localparam RenodeOutputsCountWidth = `max($clog2(RenodeOutputsCount), 1);
262
263  task automatic handle_renode_output(address_t number, bit value);
264    if (number >= 64'(RenodeOutputsCount)) begin
265      runtime.connection.log(renode_pkg::LogWarning, $sformatf("Output %0d is out of range of [0;%0d]", number, RenodeOutputsCount - 1));
266      runtime.connection.send(message_t'{renode_pkg::error, 0, 0, renode_pkg::no_peripheral_index});
267    end
268
269    @(posedge clk);
270    renode_outputs[number[RenodeOutputsCountWidth-1:0]] <= value;
271
272    runtime.connection.send(message_t'{renode_pkg::ok, 0, 0, renode_pkg::no_peripheral_index});
273  endtask
274endmodule
275