| // |
| // Copyright (c) 2010-2024 Antmicro |
| // |
| // This file is licensed under the MIT License. |
| // Full license text is available in 'licenses/MIT.txt'. |
| // |
| |
| `timescale 1ns / 1ps |
| |
| import renode_pkg::renode_runtime, renode_pkg::bus_connection, renode_pkg::renode_connection, renode_pkg::no_peripheral_index; |
| import renode_pkg::message_t, renode_pkg::address_t, renode_pkg::data_t, renode_pkg::valid_bits_e; |
| |
| module renode #( |
| int unsigned RenodeToCosimCount = 0, |
| int unsigned CosimToRenodeCount = 0, |
| int unsigned RenodeInputsCount = 1, |
| int unsigned RenodeOutputsCount = 1 |
| ) ( |
| ref renode_runtime runtime, |
| input logic clk, |
| input logic [RenodeInputsCount-1:0] renode_inputs, |
| output logic [RenodeOutputsCount-1:0] renode_outputs |
| ); |
| time renode_time = 0; |
| |
| event reset_assert_all; |
| int reset_assert_done_count; |
| event reset_assert_done; |
| |
| event reset_deassert_all; |
| int reset_deassert_done_count; |
| event reset_deassert_done; |
| |
| renode_inputs #( |
| .InputsCount(RenodeInputsCount) |
| ) gpio ( |
| .runtime(runtime), |
| .clk(clk), |
| .inputs(renode_inputs) |
| ); |
| |
| initial begin |
| runtime.controllers = new[RenodeToCosimCount]; |
| foreach(runtime.controllers[i]) begin |
| runtime.controllers[i] = new(); |
| end |
| |
| runtime.peripherals = new[CosimToRenodeCount]; |
| foreach(runtime.peripherals[i]) begin |
| runtime.peripherals[i] = new(); |
| end |
| end |
| |
| if(CosimToRenodeCount > 0) begin |
| genvar i; |
| for(i = 0; i < CosimToRenodeCount; i += 1) begin |
| always @(runtime.peripherals[i].read_transaction_request) read_transaction(i); |
| always @(runtime.peripherals[i].write_transaction_request) write_transaction(i); |
| always @(reset_assert_all) runtime.peripherals[i].reset_assert(); |
| always @(runtime.peripherals[i].reset_assert_response) begin |
| reset_assert_done_count++; |
| if(reset_assert_done_count == (CosimToRenodeCount + RenodeToCosimCount)) begin |
| ->reset_assert_done; |
| end |
| end |
| always @(runtime.peripherals[i].reset_deassert_response) begin |
| reset_deassert_done_count++; |
| if(reset_deassert_done_count == (CosimToRenodeCount + RenodeToCosimCount)) begin |
| ->reset_deassert_done; |
| end |
| end |
| always @(reset_deassert_all) runtime.peripherals[i].reset_deassert(); |
| end |
| end |
| |
| if(RenodeToCosimCount > 0) begin |
| genvar i; |
| for(i = 0; i < RenodeToCosimCount; i += 1) begin |
| always @(reset_assert_all) runtime.controllers[i].reset_assert(); |
| always @(reset_deassert_all) runtime.controllers[i].reset_deassert(); |
| always @(runtime.controllers[i].reset_assert_response) begin |
| reset_assert_done_count++; |
| if(reset_assert_done_count == (CosimToRenodeCount + RenodeToCosimCount)) begin |
| ->reset_assert_done; |
| end |
| end |
| always @(runtime.controllers[i].reset_deassert_response) begin |
| reset_deassert_done_count++; |
| if(reset_deassert_done_count == (CosimToRenodeCount + RenodeToCosimCount)) begin |
| ->reset_deassert_done; |
| end |
| end |
| end |
| end |
| |
| task static receive_and_handle_message(); |
| message_t message; |
| bit did_receive; |
| |
| // This task doesn't block elapse of a simulation time, when messages are being received and handled in an other place. |
| if (runtime.connection.exclusive_receive.try_get() != 0) begin |
| did_receive = runtime.connection.try_receive(message); |
| runtime.connection.exclusive_receive.put(); |
| if (did_receive) handle_message(message); |
| end |
| endtask |
| |
| task static handle_message(message_t message); |
| bit is_handled; |
| |
| is_handled = 1; |
| case (message.action) |
| renode_pkg::resetPeripheral: reset(); |
| renode_pkg::tickClock: sync_time(time'(message.data)); |
| renode_pkg::interrupt: handle_renode_output(message.address, message.data[0]); |
| renode_pkg::writeRequestQuadWord: write_to_bus(message.address, renode_pkg::QuadWord, message.data, message.peripheral_index); |
| renode_pkg::writeRequestDoubleWord: write_to_bus(message.address, renode_pkg::DoubleWord, message.data, message.peripheral_index); |
| renode_pkg::writeRequestWord: write_to_bus(message.address, renode_pkg::Word, message.data, message.peripheral_index); |
| renode_pkg::writeRequestByte: write_to_bus(message.address, renode_pkg::Byte, message.data, message.peripheral_index); |
| renode_pkg::readRequestQuadWord: read_from_bus(message.address, renode_pkg::QuadWord, message.peripheral_index); |
| renode_pkg::readRequestDoubleWord: read_from_bus(message.address, renode_pkg::DoubleWord, message.peripheral_index); |
| renode_pkg::readRequestWord: read_from_bus(message.address, renode_pkg::Word, message.peripheral_index); |
| renode_pkg::readRequestByte: read_from_bus(message.address, renode_pkg::Byte, message.peripheral_index); |
| default: is_handled = 0; |
| endcase |
| |
| if (!is_handled) runtime.connection.handle_message(message, is_handled); |
| if (!is_handled) runtime.connection.log(renode_pkg::LogWarning, $sformatf("Trying to handle the unsupported action (%0s)", message.action.name())); |
| endtask |
| |
| task static reset(); |
| // Nothing to reset, return immediately. |
| if(RenodeToCosimCount + CosimToRenodeCount == 0) return; |
| |
| // The reset just locks the connection without using it to avoid an unexpected behaviour. |
| // It also prevents from a message handling in the receive_and_handle_message until a reset deassertion. |
| runtime.connection.exclusive_receive.get(); |
| |
| reset_assert_done_count = 0; |
| #1 fork |
| ->reset_assert_all; |
| gpio.reset_assert(); |
| join |
| |
| @(reset_assert_done); |
| |
| // It's required to make values of all signals known (different than `x`) before a deassertion of resets. |
| // The assignment to renode_outputs is an equivalent of a reset assertion. |
| renode_outputs = 0; |
| |
| reset_deassert_done_count = 0; |
| fork |
| ->reset_deassert_all; |
| gpio.reset_deassert(); |
| join |
| |
| @(reset_deassert_done); |
| |
| runtime.connection.exclusive_receive.put(); |
| endtask |
| |
| task static sync_time(time time_to_elapse); |
| renode_time = renode_time + time_to_elapse; |
| while ($time < renode_time) @(clk); |
| |
| runtime.connection.send_to_async_receiver(message_t'{renode_pkg::tickClock, 0, 0, renode_pkg::no_peripheral_index}); |
| runtime.connection.log(renode_pkg::LogNoisy, $sformatf("Simulation time synced to %t", $realtime)); |
| endtask |
| |
| task automatic read_from_bus(address_t address, valid_bits_e data_bits, int peripheral_index); |
| data_t data = 0; |
| bit is_error = 0; |
| runtime.controllers[peripheral_index].read(address, data_bits, data, is_error); |
| |
| if (is_error) runtime.connection.send(message_t'{renode_pkg::error, 0, 0, peripheral_index}); |
| else runtime.connection.send(message_t'{renode_pkg::readRequest, address, data, peripheral_index}); |
| endtask |
| |
| task automatic write_to_bus(address_t address, valid_bits_e data_bits, data_t data, int peripheral_index); |
| bit is_error = 0; |
| runtime.controllers[peripheral_index].write(address, data_bits, data, is_error); |
| |
| if (is_error) runtime.connection.send(message_t'{renode_pkg::error, 0, 0, peripheral_index}); |
| else runtime.connection.send(message_t'{renode_pkg::ok, 0, 0, peripheral_index}); |
| endtask |
| |
| task automatic read_transaction(int peripheral_index); |
| message_t message; |
| |
| case (runtime.peripherals[peripheral_index].read_transaction_data_bits) |
| renode_pkg::Byte: message.action = renode_pkg::getByte; |
| renode_pkg::Word: message.action = renode_pkg::getWord; |
| renode_pkg::DoubleWord: message.action = renode_pkg::getDoubleWord; |
| renode_pkg::QuadWord: message.action = renode_pkg::getQuadWord; |
| default: begin |
| 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)); |
| runtime.peripherals[peripheral_index].read_respond(0, 1); |
| return; |
| end |
| endcase |
| message.address = runtime.peripherals[peripheral_index].read_transaction_address; |
| message.data = 0; |
| |
| runtime.connection.exclusive_receive.get(); |
| if(!runtime.connection.is_connected()) begin |
| runtime.connection.exclusive_receive.put(); |
| return; |
| end |
| |
| runtime.connection.send_to_async_receiver(message); |
| |
| runtime.connection.receive(message); |
| while (message.action != renode_pkg::writeRequest) begin |
| handle_message(message); |
| if(message.action == renode_pkg::disconnect) break; |
| runtime.connection.receive(message); |
| end |
| |
| runtime.connection.exclusive_receive.put(); |
| runtime.peripherals[peripheral_index].read_respond(message.data, 0); |
| endtask |
| |
| task automatic write_transaction(int peripheral_index); |
| message_t message; |
| |
| case (runtime.peripherals[peripheral_index].write_transaction_data_bits) |
| renode_pkg::Byte: message.action = renode_pkg::pushByte; |
| renode_pkg::Word: message.action = renode_pkg::pushWord; |
| renode_pkg::DoubleWord: message.action = renode_pkg::pushDoubleWord; |
| renode_pkg::QuadWord: message.action = renode_pkg::pushQuadWord; |
| default: begin |
| 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)); |
| runtime.peripherals[peripheral_index].write_respond(1); |
| return; |
| end |
| endcase |
| message.address = runtime.peripherals[peripheral_index].write_transaction_address; |
| message.data = runtime.peripherals[peripheral_index].write_transaction_data; |
| |
| runtime.connection.exclusive_receive.get(); |
| if(!runtime.connection.is_connected()) begin |
| runtime.connection.exclusive_receive.put(); |
| return; |
| end |
| |
| runtime.connection.send_to_async_receiver(message); |
| runtime.connection.receive(message); |
| while (message.action != renode_pkg::pushConfirmation) begin |
| handle_message(message); |
| if(message.action == renode_pkg::disconnect) break; |
| runtime.connection.receive(message); |
| end |
| |
| runtime.connection.exclusive_receive.put(); |
| |
| runtime.peripherals[peripheral_index].write_respond(0); |
| endtask |
| |
| // calculate number of bits needed to hold the output number |
| `define max(a,b) (a > b) ? a : b |
| localparam RenodeOutputsCountWidth = `max($clog2(RenodeOutputsCount), 1); |
| |
| task automatic handle_renode_output(address_t number, bit value); |
| if (number >= 64'(RenodeOutputsCount)) begin |
| runtime.connection.log(renode_pkg::LogWarning, $sformatf("Output %0d is out of range of [0;%0d]", number, RenodeOutputsCount - 1)); |
| runtime.connection.send(message_t'{renode_pkg::error, 0, 0, renode_pkg::no_peripheral_index}); |
| end |
| |
| @(posedge clk); |
| renode_outputs[number[RenodeOutputsCountWidth-1:0]] <= value; |
| |
| runtime.connection.send(message_t'{renode_pkg::ok, 0, 0, renode_pkg::no_peripheral_index}); |
| endtask |
| endmodule |